ffplay.c数据结构分析

本文深入剖析了流媒体播放器的优化方案,重点介绍了ffplay.c中的数据存储结构及解码播放流程,包括15M大小的队列设计、PacketQueue与FrameQueue的数据结构,以及读线程和解码线程的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做流媒体播放器的优化,参考了ffplay.c的代码,主要对存储和流程上做下解剖。


数据存储
15M的QUEUE
#define MAX_QUEUE_SIZE (15 * 1024 * 1024)

typedef struct MyAVPacketList {
    AVPacket pkt;
    struct MyAVPacketList *next;
    int serial;
} MyAVPacketList;

typedef struct PacketQueue {
    MyAVPacketList *first_pkt, *last_pkt;
    int nb_packets;
    int size;
    int abort_request;
    int serial;
    SDL_mutex *mutex;
    SDL_cond *cond;

} PacketQueue;


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;


解码播放流程:

读线程:
is->read_tid     = SDL_CreateThread(read_thread, is);/* this thread gets the stream from the disk or the network */
主要作用是读取一帧编码后的数据,然后入队


ret = av_read_frame(ic, pkt);
packet_queue_put(&is->videoq, pkt);

入队代码:

static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
{
    MyAVPacketList *pkt1;

    if (q->abort_request)
       return -1;

    pkt1 = av_malloc(sizeof(MyAVPacketList));
    if (!pkt1)
        return -1;
    pkt1->pkt = *pkt;
    pkt1->next = NULL;
    if (pkt == &flush_pkt)
        q->serial++;
    pkt1->serial = q->serial;

    if (!q->last_pkt)
        q->first_pkt = pkt1;
    else
        q->last_pkt->next = pkt1;
    q->last_pkt = pkt1;
    q->nb_packets++;
    q->size += pkt1->pkt.size + sizeof(*pkt1);
    /* XXX: should duplicate packet data in DV case */
    SDL_CondSignal(q->cond);
    return 0;
}

static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
{
    int ret;

    /* duplicate the packet */
    if (pkt != &flush_pkt && av_dup_packet(pkt) < 0)
        return -1;

    SDL_LockMutex(q->mutex);
    ret = packet_queue_put_private(q, pkt);
    SDL_UnlockMutex(q->mutex);

    if (pkt != &flush_pkt && ret < 0)
        av_free_packet(pkt);

    return ret;
}




解码线程:

decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);
decoder_start(&is->auddec, audio_thread, is);


decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
decoder_start(&is->viddec, video_thread, is);
video_thread的流程:
get_video_frame 然后去刷新界面
for (;;) {
ret = get_video_frame(is, frame);
}

get_video_frame是解码后得到一帧:
if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
decoder_decode_frame是得到从packet_queue_get中得到一个packet并扔给解码器解码:
if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)
return -1;  

packet_queue_get是等待信号量直到得到一个pkt:
/* return < 0 if aborted, 0 if no packet and > 0 if packet.  */
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial)
{
    MyAVPacketList *pkt1;
    int ret;

    SDL_LockMutex(q->mutex);

    for (;;) {
        if (q->abort_request) {
            ret = -1;
            break;
        }

        pkt1 = q->first_pkt;
        if (pkt1) {
            q->first_pkt = pkt1->next;
            if (!q->first_pkt)
                q->last_pkt = NULL;
            q->nb_packets--;
            q->size -= pkt1->pkt.size + sizeof(*pkt1);
            *pkt = pkt1->pkt;
            if (serial)
                *serial = pkt1->serial;
            av_free(pkt1);
            ret = 1;
            break;
        } else if (!block) {
            ret = 0;
            break;
        } else {
            SDL_CondWait(q->cond, q->mutex);
        }
    }
    SDL_UnlockMutex(q->mutex);
    return ret;
}



分析完了。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值