Fplay: h264 hardware decoding and output.
git-svn-id: svn://kolibrios.org@6133 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
49e6d69320
commit
fc65c2518c
@ -417,4 +417,3 @@ exit_whith_error:
|
||||
return -1;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,26 +111,31 @@ 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 */
|
||||
AVCodecContext *vCtx; /* video decoder context */
|
||||
AVCodecContext *aCtx; /* audio decoder context */
|
||||
AVCodec *vCodec; /* video codec */
|
||||
AVCodec *aCodec; /* audio codec */
|
||||
int vStream; /* video stream index */
|
||||
int aStream; /* audio stream index */
|
||||
AVFormatContext *fCtx; /* format context */
|
||||
AVCodecContext *vCtx; /* video decoder context */
|
||||
AVCodecContext *aCtx; /* audio decoder context */
|
||||
AVCodec *vCodec; /* video codec */
|
||||
AVCodec *aCodec; /* audio codec */
|
||||
int vStream; /* video stream index */
|
||||
int aStream; /* audio stream index */
|
||||
|
||||
queue_t q_video; /* video packets queue */
|
||||
queue_t q_audio; /* audio packets queue */
|
||||
queue_t q_video; /* video packets queue */
|
||||
queue_t q_audio; /* audio packets queue */
|
||||
|
||||
mutex_t gpu_lock; /* gpu access lock. libdrm not yet thread safe :( */
|
||||
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 */
|
||||
void *hwCtx; /* hardware context */
|
||||
int hwdec; /* hardware decoder */
|
||||
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
579
contrib/media/fplay/list.h
Normal 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
|
@ -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;
|
||||
@ -219,9 +219,9 @@ void *vaapi_init(VADisplay display)
|
||||
|
||||
if ((vaapi = calloc(1, sizeof(*vaapi))) == NULL)
|
||||
goto error;
|
||||
vaapi->display = display;
|
||||
vaapi->config_id = VA_INVALID_ID;
|
||||
vaapi->context_id = VA_INVALID_ID;
|
||||
vaapi->display = display;
|
||||
vaapi->config_id = VA_INVALID_ID;
|
||||
vaapi->context_id = VA_INVALID_ID;
|
||||
|
||||
v_context = vaapi;
|
||||
|
||||
@ -281,7 +281,7 @@ static int has_entrypoint(struct vaapi_context *vaapi, VAProfile profile, VAEntr
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vaapi_init_decoder(VAProfile profile,
|
||||
static int vaapi_init_decoder(VAProfile profile,
|
||||
VAEntrypoint entrypoint,
|
||||
unsigned int picture_width,
|
||||
unsigned int picture_height)
|
||||
@ -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()"))
|
||||
{
|
||||
@ -387,8 +386,8 @@ ENTER();
|
||||
return -1;
|
||||
};
|
||||
|
||||
vaapi->config_id = config_id;
|
||||
vaapi->context_id = context_id;
|
||||
vaapi->config_id = config_id;
|
||||
vaapi->context_id = context_id;
|
||||
LEAVE();
|
||||
return 0;
|
||||
}
|
||||
@ -397,48 +396,38 @@ 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 (vaapi_init_decoder(profile, VAEntrypointVLD, avctx->width, avctx->height) == 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)
|
||||
{
|
||||
avctx->hwaccel_context = v_context;
|
||||
LEAVE();
|
||||
return fmt[i]; ;
|
||||
profile = hw_profiles[n].va_profile;
|
||||
if (vaapi_init_decoder(profile, VAEntrypointVLD, avctx->width, avctx->height) == 0)
|
||||
{
|
||||
avctx->hwaccel_context = v_context;
|
||||
LEAVE();
|
||||
return fmt[i]; ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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,107 +504,154 @@ 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"
|
||||
"offset2: %d pitch2: %d\n",
|
||||
vaimage.buf, vaimage.format.fourcc,
|
||||
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()"))
|
||||
/*
|
||||
printf("vaDeriveImage: %x fourcc: %x\n"
|
||||
"offset0: %d pitch0: %d\n"
|
||||
"offset1: %d pitch1: %d\n"
|
||||
"offset2: %d pitch2: %d\n",
|
||||
vaimage.buf, vaimage.format.fourcc,
|
||||
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()"))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
@ -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,81 +67,113 @@ 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 )
|
||||
return -1;
|
||||
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;
|
||||
|
||||
vst->vCtx->reordered_opaque = pkt.pts;
|
||||
|
||||
mutex_lock(&vst->gpu_lock);
|
||||
|
||||
if(avcodec_decode_video2(vst->vCtx, Frame, &frameFinished, &pkt) <= 0)
|
||||
printf("video decoder error\n");
|
||||
|
||||
if(frameFinished)
|
||||
{
|
||||
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_q2d(video_time_base);
|
||||
|
||||
dst_pic = &vst->vframe[vst->dfx].picture;
|
||||
|
||||
if(vst->hwdec == 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);
|
||||
|
||||
vst->vframe[vst->dfx].pts = pts*1000.0;
|
||||
vst->vframe[vst->dfx].ready = 1;
|
||||
vst->dfx = (vst->dfx + 1) & 3;
|
||||
frames_count++;
|
||||
};
|
||||
mutex_unlock(&vst->gpu_lock);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
frameFinished = 0;
|
||||
if(dts == 0)
|
||||
dts = pkt.pts;
|
||||
|
||||
mutex_lock(&vst->gpu_lock);
|
||||
|
||||
if(avcodec_decode_video2(vst->vCtx, Frame, &frameFinished, &pkt) <= 0)
|
||||
printf("video decoder error\n");
|
||||
|
||||
if(frameFinished)
|
||||
{
|
||||
vframe_t *vframe;
|
||||
AVPicture *dst_pic;
|
||||
|
||||
pts = av_frame_get_best_effort_timestamp(Frame);
|
||||
pts *= av_q2d(video_time_base);
|
||||
|
||||
vframe = vst->decoder_frame;
|
||||
dst_pic = &vframe->picture;
|
||||
|
||||
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_create_planar(vst, vframe);
|
||||
|
||||
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,11 +793,18 @@ 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,
|
||||
dst_width, dst_height, AV_PIX_FMT_BGRA,
|
||||
SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
||||
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)
|
||||
{
|
||||
printf("Cannot initialize the conversion context!\n");
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user