Fplay: h264 hardware decoding and output.

git-svn-id: svn://kolibrios.org@6133 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
Sergey Semyonov (Serge) 2016-02-03 23:55:46 +00:00
parent 49e6d69320
commit fc65c2518c
8 changed files with 966 additions and 350 deletions

View File

@ -417,4 +417,3 @@ exit_whith_error:
return -1;
};

View File

@ -26,9 +26,6 @@ volatile enum player_state sound_state = STOP;
uint32_t win_width, win_height;
AVFrame *pFrame;
int have_sound = 0;
uint8_t *decoder_buffer;
@ -147,6 +144,15 @@ int main( int argc, char *argv[])
printf("codec id %x name %s\n",vst.vCtx->codec_id, vst.vCodec->name);
printf("ctx->pix_fmt %d\n", vst.vCtx->pix_fmt);
INIT_LIST_HEAD(&vst.input_list);
INIT_LIST_HEAD(&vst.output_list);
mutex_init(&vst.q_video.lock);
mutex_init(&vst.q_audio.lock);
mutex_init(&vst.gpu_lock);
mutex_init(&vst.decoder_lock);
mutex_init(&vst.input_lock);
mutex_init(&vst.output_lock);
if(vst.vCodec == NULL)
{
printf("Unsupported codec with id %d for input stream %d\n",
@ -166,10 +172,6 @@ int main( int argc, char *argv[])
printf("ctx->pix_fmt %d\n", vst.vCtx->pix_fmt);
mutex_init(&vst.q_video.lock);
mutex_init(&vst.q_audio.lock);
mutex_init(&vst.gpu_lock);
if (vst.aCtx->channels > 0)
vst.aCtx->request_channels = FFMIN(2, vst.aCtx->channels);
else
@ -224,10 +226,9 @@ int main( int argc, char *argv[])
if(!init_video(&vst))
return 0;
decoder(&vst);
mutex_lock_timeout(&vst.decoder_lock, 3000);
// Free the YUV frame
av_free(pFrame);
decoder(&vst);
//__asm__ __volatile__("int3");
@ -241,7 +242,7 @@ int main( int argc, char *argv[])
mutex_destroy(&vst.q_video.lock);
mutex_destroy(&vst.q_audio.lock);
mutex_destroy(&vst.decoder_lock);
return 0;
}
@ -334,8 +335,7 @@ void decoder(vst_t* vst)
}
decode_video(vst);
ret = decode_audio(vst->aCtx, &vst->q_audio);
}while(astream.count < resampler_size*2 &&
ret == 1);
}while(astream.count < resampler_size*2 && ret == 1);
sound_state = PREPARE;
decoder_state = PLAY;

View File

@ -1,6 +1,7 @@
#include <libsync.h>
#include "pixlib3.h"
#include <pixlib3.h>
#include "list.h"
#define BLACK_MAGIC_SOUND
#define BLACK_MAGIC_VIDEO
@ -18,10 +19,15 @@ typedef struct vstate vst_t;
typedef struct
{
struct list_head list;
enum AVPixelFormat format;
AVPicture picture;
int planar;
planar_t* planar;
int is_hw_pic;
int index;
double pts;
double pkt_pts;
double pkt_dts;
volatile int ready;
}vframe_t;
@ -53,7 +59,7 @@ struct render
EMPTY, INIT }state;
enum win_state win_state;
void (*draw)(render_t *render, AVPicture *picture);
void (*draw)(render_t *render, vframe_t *vframe);
};
enum player_state
@ -105,6 +111,7 @@ typedef struct {
int put_packet(queue_t *q, AVPacket *pkt);
int get_packet(queue_t *q, AVPacket *pkt);
#define HWDEC_NUM_SURFACES 16
struct vstate
{
AVFormatContext *fCtx; /* format context */
@ -119,10 +126,14 @@ struct vstate
queue_t q_audio; /* audio packets queue */
mutex_t gpu_lock; /* gpu access lock. libdrm not yet thread safe :( */
mutex_t decoder_lock;
vframe_t vframe[4]; /* decoder workset */
int vfx; /* index of decoded frame */
int dfx; /* index of renderd frame */
mutex_t input_lock;
mutex_t output_lock;
struct list_head input_list;
struct list_head output_list;
vframe_t *decoder_frame;
void *hwCtx; /* hardware context */
int hwdec; /* hardware decoder */
};
@ -165,7 +176,7 @@ static inline void GetNotify(void *event)
::"a"(68),"b"(14),"c"(event));
}
void va_convert_picture(vst_t *vst, int width, int height, AVPicture *pic);
void va_create_planar(vst_t *vst, vframe_t *vframe);
int init_fontlib();
char *get_moviefile();

579
contrib/media/fplay/list.h Normal file
View File

@ -0,0 +1,579 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#define LIST_POISON1 ((void *) 0x80000000)
#define LIST_POISON2 ((void *) 0x80001000)
#define container_of(ptr, type, member) ({ \
const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - __builtin_offsetof(type,member) );})
struct list_head
{
struct list_head *next;
struct list_head *prev;
};
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) \
(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, __typeof__(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, __typeof__(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, __typeof__(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_head within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, __typeof__(*pos), member))
/**
* list_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_next_entry(pos, member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; &pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, __typeof__(*pos), member), \
n = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_prev_entry(n, member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_head within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_next_entry(pos, member)
#endif

View File

@ -17,7 +17,7 @@ struct hw_profile
{
enum AVCodecID av_codec;
int ff_profile;
uint64_t va_profile;
VAProfile va_profile;
};
@ -39,7 +39,7 @@ struct hw_profile
static int drm_fd = 0;
static struct vaapi_context *v_context;
static VASurfaceID v_surface_id[4];
static VASurfaceID v_surface_id[HWDEC_NUM_SURFACES];
#define HAS_HEVC VA_CHECK_VERSION(0, 38, 0)
#define HAS_VP9 (VA_CHECK_VERSION(0, 38, 1) && defined(FF_PROFILE_VP9_0))
@ -48,7 +48,7 @@ static VASurfaceID v_surface_id[4];
{AV_CODEC_ID_ ## av_codec_id, FF_PROFILE_ ## ff_profile, \
VAProfile ## vdp_profile}
static const struct hw_profile profiles[] = {
static const struct hw_profile hw_profiles[] = {
PE(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main),
PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple),
PE(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4AdvancedSimple),
@ -75,8 +75,8 @@ static const struct hw_profile profiles[] = {
int va_check_codec_support(enum AVCodecID id)
{
for (int n = 0; profiles[n].av_codec; n++) {
if (profiles[n].av_codec == id)
for (int n = 0; hw_profiles[n].av_codec; n++) {
if (hw_profiles[n].av_codec == id)
return 1;
}
return 0;
@ -343,8 +343,7 @@ ENTER();
printf("vaCreateSurfaces %dx%d\n",picture_width,picture_height);
status = vaCreateSurfaces(vaapi->display, VA_RT_FORMAT_YUV420, picture_width, picture_height,
v_surface_id,4,NULL,0);
printf("v_surface_id_3 %x\n", v_surface_id[3]);
v_surface_id,HWDEC_NUM_SURFACES,NULL,0);
if (!vaapi_check_status(status, "vaCreateSurfaces()"))
{
FAIL();
@ -379,7 +378,7 @@ ENTER();
status = vaCreateContext(vaapi->display, config_id,
picture_width, picture_height,
VA_PROGRESSIVE,
v_surface_id, 4,
v_surface_id, HWDEC_NUM_SURFACES,
&context_id);
if (!vaapi_check_status(status, "vaCreateContext()"))
{
@ -397,41 +396,29 @@ ENTER();
static enum PixelFormat get_format(struct AVCodecContext *avctx,
const enum AVPixelFormat *fmt)
{
int i, profile;
VAProfile profile = VAProfileNone;
ENTER();
// for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++)
// printf(" %s", av_get_pix_fmt_name(fmt[i]));
for (int i = 0; fmt[i] != PIX_FMT_NONE; i++)
{
enum AVCodecID codec = avctx->codec_id;
for (i = 0; fmt[i] != PIX_FMT_NONE; i++) {
printf("pixformat %x\n", fmt[i]);
if (fmt[i] != AV_PIX_FMT_VAAPI_VLD)
continue;
switch (avctx->codec_id)
if (codec == AV_CODEC_ID_H264)
{
case CODEC_ID_MPEG2VIDEO:
profile = VAProfileMPEG2Main;
break;
case CODEC_ID_MPEG4:
case CODEC_ID_H263:
profile = VAProfileMPEG4AdvancedSimple;
break;
case CODEC_ID_H264:
profile = VAProfileH264High;
break;
case CODEC_ID_WMV3:
profile = VAProfileVC1Main;
break;
case CODEC_ID_VC1:
profile = VAProfileVC1Advanced;
break;
default:
profile = -1;
break;
}
if (profile >= 0) {
if (profile == FF_PROFILE_H264_CONSTRAINED_BASELINE)
profile = FF_PROFILE_H264_MAIN;
};
for (int n = 0; hw_profiles[n].av_codec; n++)
{
if (hw_profiles[n].av_codec == codec &&
hw_profiles[n].ff_profile == avctx->profile)
{
profile = hw_profiles[n].va_profile;
if (vaapi_init_decoder(profile, VAEntrypointVLD, avctx->width, avctx->height) == 0)
{
avctx->hwaccel_context = v_context;
@ -440,6 +427,8 @@ static enum PixelFormat get_format(struct AVCodecContext *avctx,
}
}
}
}
FAIL();
return PIX_FMT_NONE;
}
@ -454,16 +443,15 @@ struct av_surface
static void av_release_buffer(void *opaque, uint8_t *data)
{
struct av_surface surface = *(struct av_surface*)data;
// VDPAUContext *ctx = opaque;
// ctx->video_surface_destroy(surface);
av_freep(&data);
}
static int get_buffer2(AVCodecContext *avctx, AVFrame *pic, int flags)
{
vst_t *vst = (vst_t*)avctx->opaque;
void *surface = (void *)(uintptr_t)v_surface_id[vst->dfx];
void *surface;
surface = (void *)(uintptr_t)v_surface_id[vst->decoder_frame->index];
pic->data[3] = surface;
@ -493,20 +481,16 @@ int fplay_init_context(vst_t *vst)
if(vst->hwCtx != NULL)
{
for(int i = 0; i < 4; i++)
for(int i = 0; i < HWDEC_NUM_SURFACES; i++)
{
int ret;
vframe_t *vframe = calloc(1, sizeof(*vframe));
ret = avpicture_alloc(&vst->vframe[i].picture, AV_PIX_FMT_BGRA,
vst->vCtx->width, vst->vCtx->height);
if ( ret != 0 )
{
printf("Cannot alloc video buffer\n\r");
return ret;
};
vst->vframe[i].format = AV_PIX_FMT_BGRA;
vst->vframe[i].pts = 0;
vst->vframe[i].ready = 0;
vframe->format = AV_PIX_FMT_NONE;
vframe->is_hw_pic = 1;
vframe->index = i;
vframe->pts = 0;
vframe->ready = 0;
list_add_tail(&vframe->list, &vst->input_list);
};
vst->hwdec = 1;
@ -520,52 +504,119 @@ int fplay_init_context(vst_t *vst)
vst->hwdec = 0;
for(int i = 0; i < 4; i++)
for(int i = 0; i < HWDEC_NUM_SURFACES; i++)
{
vframe_t *vframe;
int ret;
ret = avpicture_alloc(&vst->vframe[i].picture, vst->vCtx->pix_fmt,
vframe = calloc(1, sizeof(*vframe));
ret = avpicture_alloc(&vframe->picture, vst->vCtx->pix_fmt,
vst->vCtx->width, vst->vCtx->height);
if ( ret != 0 )
{
printf("Cannot alloc video buffer\n\r");
return ret;
};
vst->vframe[i].format = vst->vCtx->pix_fmt;
vst->vframe[i].pts = 0;
vst->vframe[i].ready = 0;
vframe->format = vst->vCtx->pix_fmt;
vframe->index = i;
vframe->pts = 0;
vframe->ready = 0;
list_add_tail(&vframe->list, &vst->input_list);
};
return 0;
}
struct SwsContext *vacvt_ctx;
#define EGL_TEXTURE_Y_U_V_WL 0x31D7
#define EGL_TEXTURE_Y_UV_WL 0x31D8
#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
void va_convert_picture(vst_t *vst, int width, int height, AVPicture *pic)
enum wl_drm_format {
WL_DRM_FORMAT_C8 = 0x20203843,
WL_DRM_FORMAT_RGB332 = 0x38424752,
WL_DRM_FORMAT_BGR233 = 0x38524742,
WL_DRM_FORMAT_XRGB4444 = 0x32315258,
WL_DRM_FORMAT_XBGR4444 = 0x32314258,
WL_DRM_FORMAT_RGBX4444 = 0x32315852,
WL_DRM_FORMAT_BGRX4444 = 0x32315842,
WL_DRM_FORMAT_ARGB4444 = 0x32315241,
WL_DRM_FORMAT_ABGR4444 = 0x32314241,
WL_DRM_FORMAT_RGBA4444 = 0x32314152,
WL_DRM_FORMAT_BGRA4444 = 0x32314142,
WL_DRM_FORMAT_XRGB1555 = 0x35315258,
WL_DRM_FORMAT_XBGR1555 = 0x35314258,
WL_DRM_FORMAT_RGBX5551 = 0x35315852,
WL_DRM_FORMAT_BGRX5551 = 0x35315842,
WL_DRM_FORMAT_ARGB1555 = 0x35315241,
WL_DRM_FORMAT_ABGR1555 = 0x35314241,
WL_DRM_FORMAT_RGBA5551 = 0x35314152,
WL_DRM_FORMAT_BGRA5551 = 0x35314142,
WL_DRM_FORMAT_RGB565 = 0x36314752,
WL_DRM_FORMAT_BGR565 = 0x36314742,
WL_DRM_FORMAT_RGB888 = 0x34324752,
WL_DRM_FORMAT_BGR888 = 0x34324742,
WL_DRM_FORMAT_XRGB8888 = 0x34325258,
WL_DRM_FORMAT_XBGR8888 = 0x34324258,
WL_DRM_FORMAT_RGBX8888 = 0x34325852,
WL_DRM_FORMAT_BGRX8888 = 0x34325842,
WL_DRM_FORMAT_ARGB8888 = 0x34325241,
WL_DRM_FORMAT_ABGR8888 = 0x34324241,
WL_DRM_FORMAT_RGBA8888 = 0x34324152,
WL_DRM_FORMAT_BGRA8888 = 0x34324142,
WL_DRM_FORMAT_XRGB2101010 = 0x30335258,
WL_DRM_FORMAT_XBGR2101010 = 0x30334258,
WL_DRM_FORMAT_RGBX1010102 = 0x30335852,
WL_DRM_FORMAT_BGRX1010102 = 0x30335842,
WL_DRM_FORMAT_ARGB2101010 = 0x30335241,
WL_DRM_FORMAT_ABGR2101010 = 0x30334241,
WL_DRM_FORMAT_RGBA1010102 = 0x30334152,
WL_DRM_FORMAT_BGRA1010102 = 0x30334142,
WL_DRM_FORMAT_YUYV = 0x56595559,
WL_DRM_FORMAT_YVYU = 0x55595659,
WL_DRM_FORMAT_UYVY = 0x59565955,
WL_DRM_FORMAT_VYUY = 0x59555956,
WL_DRM_FORMAT_AYUV = 0x56555941,
WL_DRM_FORMAT_NV12 = 0x3231564e,
WL_DRM_FORMAT_NV21 = 0x3132564e,
WL_DRM_FORMAT_NV16 = 0x3631564e,
WL_DRM_FORMAT_NV61 = 0x3136564e,
WL_DRM_FORMAT_YUV410 = 0x39565559,
WL_DRM_FORMAT_YVU410 = 0x39555659,
WL_DRM_FORMAT_YUV411 = 0x31315559,
WL_DRM_FORMAT_YVU411 = 0x31315659,
WL_DRM_FORMAT_YUV420 = 0x32315559,
WL_DRM_FORMAT_YVU420 = 0x32315659,
WL_DRM_FORMAT_YUV422 = 0x36315559,
WL_DRM_FORMAT_YVU422 = 0x36315659,
WL_DRM_FORMAT_YUV444 = 0x34325559,
WL_DRM_FORMAT_YVU444 = 0x34325659,
};
void va_create_planar(vst_t *vst, vframe_t *vframe)
{
uint8_t *src_data[4];
int src_linesize[4];
struct vaapi_context* const vaapi = v_context;
VABufferInfo info = {0};
VAImage vaimage;
VAStatus status;
uint8_t *vdata;
struct vaapi_context* const vaapi = v_context;
planar_t *planar;
vaSyncSurface(vaapi->display,v_surface_id[vst->dfx]);
vaSyncSurface(vaapi->display,v_surface_id[vframe->index]);
status = vaDeriveImage(vaapi->display,v_surface_id[vst->dfx],&vaimage);
if(vframe->format != AV_PIX_FMT_NONE)
return;
ENTER();
status = vaDeriveImage(vaapi->display,v_surface_id[vframe->index],&vaimage);
if (!vaapi_check_status(status, "vaDeriveImage()"))
{
FAIL();
return;
};
static int once = 2;
if(once && vst->dfx == 0)
{
VABufferInfo info = {0};
/*
printf("vaDeriveImage: %x fourcc: %x\n"
"offset0: %d pitch0: %d\n"
"offset1: %d pitch1: %d\n"
@ -574,53 +625,33 @@ void va_convert_picture(vst_t *vst, int width, int height, AVPicture *pic)
vaimage.offsets[0],vaimage.pitches[0],
vaimage.offsets[1],vaimage.pitches[1],
vaimage.offsets[2],vaimage.pitches[2]);
*/
info.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM;
status = vaAcquireBufferHandle(vaapi->display, vaimage.buf, &info);
if (vaapi_check_status(status, "vaAcquireBufferHandle()"))
{
printf("vaAcquireBufferHandle: %x type: %x\n"
"mem type: %x mem size: %d\n",
info.handle, info.type, info.mem_type, info.mem_size);
vaReleaseBufferHandle(vaapi->display, vaimage.buf);
}
once--;
};
src_linesize[0] = vaimage.pitches[0];
src_linesize[1] = vaimage.pitches[1];
src_linesize[2] = vaimage.pitches[2];
src_linesize[3] = 0;
status = vaMapBuffer(vaapi->display,vaimage.buf,(void **)&vdata);
if (!vaapi_check_status(status, "vaMapBuffer()"))
if (!vaapi_check_status(status, "vaAcquireBufferHandle()"))
{
vaDestroyImage(vaapi->display, vaimage.image_id);
FAIL();
return;
};
// printf("vdata: %x offset0: %d offset1: %d offset2: %d\n", vdata,
// vaimage.offsets[0],
// vaimage.offsets[1],
// vaimage.offsets[2]);
src_data[0] = vdata + vaimage.offsets[0];
src_data[1] = vdata + vaimage.offsets[1];
src_data[2] = vdata + vaimage.offsets[2];
src_data[3] = 0;
vacvt_ctx = sws_getCachedContext(vacvt_ctx, width, height, AV_PIX_FMT_NV12,
width, height, AV_PIX_FMT_BGRA,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
if(vacvt_ctx == NULL)
/*
printf("vaAcquireBufferHandle: %x type: %x\n"
"mem type: %x mem size: %d\n",
info.handle, info.type, info.mem_type, info.mem_size);
*/
planar = pxCreatePlanar(info.handle, WL_DRM_FORMAT_NV12,
vaimage.width, vaimage.height,
vaimage.offsets[0],vaimage.pitches[0],
vaimage.offsets[1],vaimage.pitches[1],
vaimage.offsets[2],vaimage.pitches[2]);
if(planar != NULL)
{
printf("Cannot initialize the conversion context!\n");
return ;
printf("create planar image\n",planar);
vframe->planar = planar;
vframe->format = AV_PIX_FMT_NV12;
};
sws_scale(vacvt_ctx, (const uint8_t* const *)src_data, src_linesize, 0, height, pic->data, pic->linesize);
vaUnmapBuffer (vaapi->display, vaimage.buf);
vaReleaseBufferHandle(vaapi->display, vaimage.buf);
vaDestroyImage(vaapi->display, vaimage.image_id);
LEAVE();
}

View File

@ -28,35 +28,38 @@ struct SwsContext *cvt_ctx = NULL;
render_t *main_render;
int width;
int height;
AVRational video_time_base;
AVFrame *Frame;
void get_client_rect(rect_t *rc);
void run_render(window_t *win, void *render);
void window_update_layout(window_t *win);
int fini_winlib();
void flush_video(vst_t *vst)
{
int i;
vframe_t *vframe, *tmp;
for(i = 0; i < 4; i++)
mutex_lock(&vst->output_lock);
mutex_lock(&vst->input_lock);
list_for_each_entry_safe(vframe, tmp, &vst->output_list, list)
list_move_tail(&vframe->list, &vst->input_list);
list_for_each_entry(vframe, &vst->output_list, list)
{
vst->vframe[i].pts = 0;
vst->vframe[i].ready = 0;
};
vst->vfx = 0;
vst->dfx = 0;
vframe->pts = 0;
vframe->ready = 0;
}
mutex_unlock(&vst->input_lock);
mutex_unlock(&vst->output_lock);
frames_count = 0;
};
int init_video(vst_t *vst)
{
int i;
width = vst->vCtx->width;
height = vst->vCtx->height;
Frame = av_frame_alloc();
if ( Frame == NULL )
{
@ -64,44 +67,44 @@ int init_video(vst_t *vst)
return 0;
};
mutex_lock(&vst->decoder_lock);
create_thread(video_thread, vst, 1024*1024);
delay(50);
return 1;
};
static double dts = 0.0;
int decode_video(vst_t* vst)
{
AVPacket pkt;
double pts;
int frameFinished;
double current_clock;
if(vst->vframe[vst->dfx].ready != 0 )
if(vst->decoder_frame == NULL)
{
mutex_lock(&vst->input_lock);
if(list_empty(&vst->input_list))
{
mutex_unlock(&vst->input_lock);
return -1;
}
vst->decoder_frame = list_first_entry(&vst->input_list, vframe_t, list);
list_del(&vst->decoder_frame->list);
mutex_unlock(&vst->input_lock);
vframe_t *vframe = vst->decoder_frame;
};
if( get_packet(&vst->q_video, &pkt) == 0 )
return 0;
/*
current_clock = -90.0 + get_master_clock();
if( pkt.dts == AV_NOPTS_VALUE &&
Frame->reordered_opaque != AV_NOPTS_VALUE)
pts = Frame->reordered_opaque;
else if(pkt.dts != AV_NOPTS_VALUE)
pts= pkt.dts;
else
pts= 0;
pts *= av_q2d(video_time_base)*1000.0;
*/
if( 1 /*pts > current_clock*/)
{
frameFinished = 0;
return 0;
};
vst->vCtx->reordered_opaque = pkt.pts;
frameFinished = 0;
if(dts == 0)
dts = pkt.pts;
mutex_lock(&vst->gpu_lock);
@ -110,35 +113,67 @@ int decode_video(vst_t* vst)
if(frameFinished)
{
vframe_t *vframe;
AVPicture *dst_pic;
if( pkt.dts == AV_NOPTS_VALUE &&
Frame->reordered_opaque != AV_NOPTS_VALUE)
pts = Frame->reordered_opaque;
else if(pkt.dts != AV_NOPTS_VALUE)
pts = pkt.dts;
else
pts = 0;
pts = av_frame_get_best_effort_timestamp(Frame);
pts *= av_q2d(video_time_base);
dst_pic = &vst->vframe[vst->dfx].picture;
vframe = vst->decoder_frame;
dst_pic = &vframe->picture;
if(vst->hwdec == 0)
if(vframe->is_hw_pic == 0)
av_image_copy(dst_pic->data, dst_pic->linesize,
(const uint8_t**)Frame->data,
Frame->linesize, vst->vCtx->pix_fmt, vst->vCtx->width, vst->vCtx->height);
else
va_convert_picture(vst, vst->vCtx->width, vst->vCtx->height, dst_pic);
va_create_planar(vst, vframe);
vst->vframe[vst->dfx].pts = pts*1000.0;
vst->vframe[vst->dfx].ready = 1;
vst->dfx = (vst->dfx + 1) & 3;
frames_count++;
vframe->pts = pts*1000.0;
vframe->pkt_pts = pkt.pts*av_q2d(video_time_base)*1000.0;
vframe->pkt_dts = dts*av_q2d(video_time_base)*1000.0;
vframe->ready = 1;
mutex_lock(&vst->output_lock);
if(list_empty(&vst->output_list))
list_add_tail(&vframe->list, &vst->output_list);
else
{
vframe_t *cur;
cur = list_first_entry(&vst->output_list,vframe_t,list);
if(vframe->pkt_pts < cur->pkt_pts)
{
list_add_tail(&vframe->list, &vst->output_list);
}
else
{
list_for_each_entry_reverse(cur,&vst->output_list,list)
{
if(vframe->pkt_pts > cur->pkt_pts)
{
list_add(&vframe->list, &cur->list);
break;
};
};
};
};
mutex_unlock(&vst->output_lock);
// printf("decoded index: %d pts: %f pkt_pts %f pkt_dts %f\n",
// vst->dfx, vst->vframe[vst->dfx].pts,
// vst->vframe[vst->dfx].pkt_pts, vst->vframe[vst->dfx].pkt_dts);
vst->decoder_frame = NULL;
frames_count++;
dts = 0;
};
av_frame_unref(Frame);
mutex_unlock(&vst->gpu_lock);
};
av_free_packet(&pkt);
return 1;
@ -357,8 +392,6 @@ int MainWindowProc(ctrl_t *ctrl, uint32_t msg, uint32_t arg1, uint32_t arg2)
return 0;
};
#define VERSION_A 1
void render_time(render_t *render)
{
progress_t *prg = main_render->win->panel.prg;
@ -367,8 +400,6 @@ void render_time(render_t *render)
double ctime; /* milliseconds */
double fdelay; /* milliseconds */
//again:
if(player_state == CLOSED)
{
render->win->win_command = WIN_CLOSED;
@ -390,51 +421,38 @@ void render_time(render_t *render)
return;
};
#ifdef VERSION_A
if(vst->vframe[vst->vfx].ready == 1 )
mutex_lock(&vst->output_lock);
if(list_empty(&vst->output_list))
{
mutex_unlock(&vst->output_lock);
delay(1);
}
else
{
vframe_t *vframe;
int sys_time;
ctime = get_master_clock();
fdelay = (vst->vframe[vst->vfx].pts - ctime);
vframe = list_first_entry(&vst->output_list, vframe_t, list);
list_del(&vframe->list);
mutex_unlock(&vst->output_lock);
// printf("pts %f time %f delay %f\n",
// frames[vfx].pts, ctime, fdelay);
ctime = get_master_clock();
fdelay = (vframe->pkt_pts - ctime);
if(fdelay > 15.0)
{
delay((int)fdelay/10);
// return;
};
#if 0
ctime = get_master_clock();
fdelay = (vst->vframe[vst->vfx].pts - ctime);
// while(fdelay > 0)
// {
// yield();
// ctime = get_master_clock();
// fdelay = (frames[vfx].pts - ctime);
// }
// printf("output index: %d pts: %f pkt_pts %f pkt_dts %f\n",
// vframe->index,vframe->pts,vframe->pkt_pts,vframe->pkt_dts);
// sys_time = get_tick_count();
main_render->draw(main_render, vframe);
// if(fdelay < 0)
// printf("systime %d pts %f time %f delay %f\n",
// sys_time*10, frames[vfx].pts, ctime, fdelay);
printf("pts %f time %f delay %f\n",
vst->vframe[vst->vfx].pts, ctime, fdelay);
printf("video cache %d audio cache %d\n", q_video.size/1024, q_audio.size/1024);
#endif
main_render->draw(main_render, &vst->vframe[vst->vfx].picture);
if(main_render->win->win_state != FULLSCREEN)
{
prg->current = vst->vframe[vst->vfx].pts*1000;
// printf("current %f\n", prg->current);
lvl->current = vst->vfx & 1 ? sound_level_1 : sound_level_0;
prg->current = vframe->pkt_pts * 1000;
lvl->current = vframe->index & 1 ? sound_level_1 : sound_level_0;
send_message(&prg->ctrl, PRG_PROGRESS, 0, 0);
@ -443,69 +461,12 @@ void render_time(render_t *render)
}
frames_count--;
vst->vframe[vst->vfx].ready = 0;
vst->vfx = (vst->vfx + 1) & 3;
vframe->ready = 0;
mutex_lock(&vst->input_lock);
list_add_tail(&vframe->list, &vst->input_list);
mutex_unlock(&vst->input_lock);
}
else delay(1);
#else
if(vst->vframe[vfx].ready == 1 )
{
ctime = get_master_clock();
fdelay = (vst->vrame[vst->vfx].pts - ctime);
// printf("pts %f time %f delay %f\n",
// frames[vfx].pts, ctime, fdelay);
if(fdelay < 0.0 )
{
int next_vfx;
fdelay = 0;
next_vfx = (vst->vfx+1) & 3;
if( vst->vrame[next_vfx].ready == 1 )
{
if(vst->vrame[next_vfx].pts <= ctime)
{
vst->vrame[vst->vfx].ready = 0; // skip this frame
vst->vfx = (vst->vfx + 1) & 3;
}
else
{
if( (vst->vrame[next_vfx].pts - ctime) <
( ctime - frames[vst->vfx].pts) )
{
vst->vrame[vst->vfx].ready = 0; // skip this frame
vst->vfx = (vst->vfx + 1) & 3;
fdelay = (vst->vrame[next_vfx].pts - ctime);
}
}
};
};
if(fdelay > 10.0)
{
int val = fdelay;
printf("pts %f time %f delay %d\n",
vst->vrame[vst->vfx].pts, ctime, val);
delay(val/10);
};
ctime = get_master_clock();
fdelay = (vst->vrame[vst->vfx].pts - ctime);
printf("pts %f time %f delay %f\n",
vst->vrame[vst->vfx].pts, ctime, fdelay);
main_render->draw(main_render, &vst->vrame[vfx].picture);
main_render->win->panel.prg->current = vst->vrame[vfx].pts;
// send_message(&render->win->panel.prg->ctrl, MSG_PAINT, 0, 0);
vst->vrame[vst->vfx].ready = 0;
vst->vfx = (vst->vfx + 1) & 3;
}
else yield();
#endif
}
@ -519,7 +480,7 @@ int video_thread(void *param)
init_winlib();
MainWindow = create_window(movie_file,0,
10,10,width,height+CAPTION_HEIGHT+PANEL_HEIGHT,MainWindowProc);
10,10,vst->vCtx->width,vst->vCtx->height+CAPTION_HEIGHT+PANEL_HEIGHT,MainWindowProc);
MainWindow->panel.prg->max = stream_duration;
@ -528,6 +489,7 @@ int video_thread(void *param)
main_render = create_render(vst, MainWindow, HW_TEX_BLIT|HW_BIT_BLIT);
if( main_render == NULL)
{
mutex_unlock(&vst->decoder_lock);
printf("Cannot create render\n\r");
return 0;
};
@ -537,6 +499,8 @@ int video_thread(void *param)
render_draw_client(main_render);
player_state = PLAY;
mutex_unlock(&vst->decoder_lock);
run_render(MainWindow, main_render);
__sync_and_and_fetch(&threads_running,~VIDEO_THREAD);
@ -548,8 +512,8 @@ int video_thread(void *param)
};
void draw_hw_picture(render_t *render, AVPicture *picture);
void draw_sw_picture(render_t *render, AVPicture *picture);
void draw_hw_picture(render_t *render, vframe_t *vframe);
void draw_sw_picture(render_t *render, vframe_t *vframe);
render_t *create_render(vst_t *vst, window_t *win, uint32_t flags)
{
@ -783,8 +747,26 @@ void render_adjust_size(render_t *render, window_t *win)
return;
};
void draw_hw_picture(render_t *render, AVPicture *picture)
static void render_hw_planar(render_t *render, vframe_t *vframe)
{
vst_t *vst = render->vst;
planar_t *planar = vframe->planar;
if(vframe->is_hw_pic != 0 && vframe->format != AV_PIX_FMT_NONE)
{
mutex_lock(&render->vst->gpu_lock);
pxBlitPlanar(planar, render->rcvideo.l,
CAPTION_HEIGHT+render->rcvideo.t,
render->rcvideo.r, render->rcvideo.b,0,0);
mutex_unlock(&render->vst->gpu_lock);
}
};
void draw_hw_picture(render_t *render, vframe_t *vframe)
{
AVPicture *picture;
int dst_width;
int dst_height;
bitmap_t *bitmap;
@ -794,6 +776,8 @@ void draw_hw_picture(render_t *render, AVPicture *picture)
int linesize[4];
enum AVPixelFormat format;
vst_t *vst = render->vst;
if(render->win->win_state == MINIMIZED ||
render->win->win_state == ROLLED)
return;
@ -809,9 +793,16 @@ void draw_hw_picture(render_t *render, AVPicture *picture)
dst_height = render->rcvideo.b;
};
format = render->vst->hwdec == 0 ? render->ctx_format : AV_PIX_FMT_BGRA
cvt_ctx = sws_getCachedContext(cvt_ctx,
render->ctx_width, render->ctx_height, format,
if(vst->hwdec)
{
render_hw_planar(render, vframe);
return;
};
picture = &vframe->picture;
format = render->vst->hwdec == 0 ? render->ctx_format : AV_PIX_FMT_BGRA;
cvt_ctx = sws_getCachedContext(cvt_ctx, render->ctx_width, render->ctx_height, format,
dst_width, dst_height, AV_PIX_FMT_BGRA,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
if(cvt_ctx == NULL)
@ -829,7 +820,6 @@ void draw_hw_picture(render_t *render, AVPicture *picture)
return ;
}
// printf("sws_getCachedContext\n");
data[0] = bitmap_data;
data[1] = bitmap_data+1;
data[2] = bitmap_data+2;
@ -874,8 +864,9 @@ void draw_hw_picture(render_t *render, AVPicture *picture)
render->target&= 1;
}
void draw_sw_picture(render_t *render, AVPicture *picture)
void draw_sw_picture(render_t *render, vframe_t *vframe)
{
AVPicture *picture;
uint8_t *bitmap_data;
uint32_t bitmap_pitch;
uint8_t *data[4];
@ -885,6 +876,8 @@ void draw_sw_picture(render_t *render, AVPicture *picture)
render->win->win_state == ROLLED)
return;
picture = &vframe->picture;
cvt_ctx = sws_getCachedContext(cvt_ctx,
render->ctx_width, render->ctx_height,
render->ctx_format,
@ -937,9 +930,9 @@ void render_draw_client(render_t *render)
if(player_state == PAUSE)
{
if(vst->vframe[vst->vfx].ready == 1 )
main_render->draw(main_render, &vst->vframe[vst->vfx].picture);
else
// if(vst->vframe[vst->vfx].ready == 1 )
// main_render->draw(main_render, &vst->vframe[vst->vfx].picture);
// else
draw_bar(0, y, render->win_width,
render->rcvideo.b, 0);
}

View File

@ -38,7 +38,7 @@ static inline void list_remove(link_t *link)
link_initialize(link);
}
static inline int list_empty(link_t *head)
static inline int llist_empty(link_t *head)
{
return head->next == head ? 1 : 0;
}

View File

@ -35,6 +35,9 @@ static int need_update;
void adjust_frame(window_t *win);
void blit_panel(panel_t *panel);
void update_panel_size(window_t *win);
void update_caption_size(window_t *win);
//#include "timer.h"
ctrl_t *win_get_child(window_t *win, int x, int y)