ffplay的FrameQueue
先简单介绍以下FrameQueue, FrameQueue是一个队列,它是用来存储解码后的数据, 像视频, 就是一帧的YUV或者RGB数据, 音频,就是pcm数据,还有字幕。每一种媒体类型都会有FrameQueue这样一个队列, 一般视频有最多三种媒体类型,视频、音频还有字幕,所以
ffplay
会创建三个FrameQueue,分别用来存储视频、音频和字幕。
下面贴下FrameQueue的结构体
typedef struct FrameQueue {
Frame queue[FRAME_QUEUE_SIZE];
int rindex;
int windex;
int size;
int max_size;
int keep_last;
int rindex_shown;
SDL_mutex *mutex;
SDL_cond *cond;
PacketQueue *pktq;
} FrameQueue;
-
queue
是存储Frame
的数组 -
rindex
是读帧数据索引, 相当于是队列的队首 -
windex
是写帧数据索引, 相当于是队列的队尾 -
size
是存储在这个队列的Frame
的数量 -
max_size
是可以存储Frame
的最大数量 -
keep_last
,这个变量的含义,据我分析, 是用来判断队列是否保留正在显示的帧(Frame
) -
rindex_shown
表示当前是否有帧在显示 -
pktq
指向各自数据包(ES包)
的队列
下面分析以下与FrameQueue的操作函数
FrameQueue的初始化函数frame_queue_init
:
static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
{
int i;
memset(f, 0, sizeof(FrameQueue));
// 创建互斥量
if (!(f->mutex = SDL_CreateMutex())) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
// 创建条件变量
if (!(f->cond = SDL_CreateCond())) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
// 使pktq指向相应的ES流队列
f->pktq = pktq;
f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
f->keep_last = !!keep_last;
// 为帧队列中的各个Frame创建AvFrame
for (i = 0; i < f->max_size; i++)
if (!(f->queue[i].frame = av_frame_alloc()))
return AVERROR(ENOMEM);
return 0;
}
frame_queue_peek_last
:获取当前播放器显示的帧
static Frame *frame_queue_peek_last(FrameQueue *f)
{
return &f->queue[f->rindex];
}
frame_queue_peek
:获取待显示的第一个帧
static Frame *frame_queue_peek(FrameQueue *f)
{
return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
}
frame_queue_peek_next
:获取待显示的第二个帧
static Frame *frame_queue_peek_next(FrameQueue *f)
{
return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
}
frame_queue_peek_writable
:获取queue
中一块Frame
大小的可写内存
static Frame *frame_queue_peek_writable(FrameQueue *f)
{
/* wait until we have space to put a new frame */
SDL_LockMutex(f->mutex);
while (f->size >= f->max_size &&
!f->pktq->abort_request) {
SDL_CondWait(f->cond, f->mutex);
}
SDL_UnlockMutex(f->mutex);
if (f->pktq->abort_request)
return NULL;
return &f->queue[f->windex];
}
frame_queue_peek_readable
:这方法和frame_queue_peek
的作用一样, 都是获取待显示的第一帧
static Frame *frame_queue_peek_readable(FrameQueue *f)
{
/* wait until we have a readable a new frame */
SDL_LockMutex(f->mutex);
while (f->size - f->rindex_shown <= 0 &&
!f->pktq->abort_request) {
SDL_CondWait(f->cond, f->mutex);
}
SDL_UnlockMutex(f->mutex);
if (f->pktq->abort_request)
return NULL;
return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
}
frame_queue_push
:推入一帧数据, 其实数据已经在调用这个方法前填充进去了, 这个方法的作用是将队列的写索引(也就是队尾)向后移, 还有将这个队列中的Frame
的数量加一。
static void frame_queue_push(FrameQueue *f)
{
if (++f->windex == f->max_size)
f->windex = 0;
SDL_LockMutex(f->mutex);
f->size++;
SDL_CondSignal(f->cond);
SDL_UnlockMutex(f->mutex);
}
frame_queue_next
:将读索引(队头)后移一位, 还有将这个队列中的Frame
的数量减一
static void frame_queue_next(FrameQueue *f)
{
if (f->keep_last && !f->rindex_shown) {
f->rindex_shown = 1;
return;
}
frame_queue_unref_item(&f->queue[f->rindex]);
if (++f->rindex == f->max_size)
f->rindex = 0;
SDL_LockMutex(f->mutex);
f->size--;
SDL_CondSignal(f->cond);
SDL_UnlockMutex(f->mutex);
}
frame_queue_nb_remaining
:返回队列中待显示帧的数目
/* return the number of undisplayed frames in the queue */
static int frame_queue_nb_remaining(FrameQueue *f)
{
return f->size - f->rindex_shown;
}
frame_queue_last_pos
:返回正在显示的帧的position
/* return last shown position */
static int64_t frame_queue_last_pos(FrameQueue *f)
{
Frame *fp = &f->queue[f->rindex];
if (f->rindex_shown && fp->serial == f->pktq->serial)
return fp->pos;
else
return -1;
}
frame_queue_destory
:释放Frame
,释放互斥锁和互斥量
static void frame_queue_destory(FrameQueue *f)
{
int i;
for (i = 0; i < f->max_size; i++) {
Frame *vp = &f->queue[i];
frame_queue_unref_item(vp);
av_frame_free(&vp->frame);
}
SDL_DestroyMutex(f->mutex);
SDL_DestroyCond(f->cond);
}
frame_queue_unref_item
:取消引用帧引用的所有缓冲区并重置帧字段,释放给定字幕结构中的所有已分配数据。
static void frame_queue_unref_item(Frame *vp)
{
av_frame_unref(vp->frame);
avsubtitle_free(&vp->sub);
}