ffplay的FrameQueue分析

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);
}

你可能感兴趣的:(ffplay的FrameQueue分析)