1、ffplay PacketQueue源码分析

ffplay PacketQueue源码分析

一、数据结构

typedef struct PacketQueue {
    MyAVPacketList *first_pkt, *last_pkt;//串联AVPacket链表,分别指向队头和队尾
    int nb_packets;//队列中元素的个数
    int size;//队列持有数据的字节大小
    int64_t duration;//队列AVPacket总时长
    int abort_request;
    int serial;
    SDL_mutex *mutex;//队列被两个线程同时操作,添加AVPacket和移除AVPacket,采用锁机制保证线程安全,防止数据错乱
    SDL_cond *cond;
} PacketQueue;


typedef struct MyAVPacketList {
    AVPacket pkt;
    struct MyAVPacketList *next;
    int serial; //序列号与PacketQueue中的serial对应,作为是否丢弃数据帧的依据
} MyAVPacketList;

二、操作函数

包含如图所示的方法
PacketQueue方法

主要分析packet_queue_put和packet_queue_get

1、添加数据

由read_thread调用,从网络或磁盘读取数据并封装为AVPacket,加入PacketQueue

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

    SDL_LockMutex(q->mutex);//操作前加锁
    ret = packet_queue_put_private(q, pkt);//若添加pkt到队列失败,返回值<0。在packet_queue_put_private中将完成pkt挂载到链表上以及PacketQueue其他数据结构的填充
    SDL_UnlockMutex(q->mutex);//解锁

    if (pkt != &flush_pkt && ret < 0)
        av_packet_unref(pkt);//添加数据失败时,减小pkt的引用;ffmpeg内部使用引用计数机制,当AVPackt引用计数变为0时,释放关联的相关数据

    return ret;
}

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

    if (q->abort_request)//播放终止,返回-1。确保pkt内存释放
       return -1;

    pkt1 = av_malloc(sizeof(MyAVPacketList));
    if (!pkt1)
        return -1;
    pkt1->pkt = *pkt;
    pkt1->next = NULL;
    if (pkt == &flush_pkt)
        q->serial++;//flush_pkt是全局变量,主要是标识作用。如果pkt是flush_pkt说明是新的一轮开始了,将PacktQueue的数列好+1。用于解码线程判断,若MyAVPacketList节点的serial小于当前队列的serial,该帧数据将不再参与解码,直接丢弃
    pkt1->serial = q->serial;//新添加的节点serial设置为当前队列的serial

    if (!q->last_pkt)
        q->first_pkt = pkt1;//若队列为空,添加到队列头部
    else
        q->last_pkt->next = pkt1;//否则将pkt添加到队列的尾部
    q->last_pkt = pkt1;//更新队尾指针
    q->nb_packets++;//队列中元素的个数++
    q->size += pkt1->pkt.size + sizeof(*pkt1);//队列持有数据的字节大小
    q->duration += pkt1->pkt.duration;
    /* XXX: should duplicate packet data in DV case */
    SDL_CondSignal(q->cond);//唤醒因队列为空,而阻塞的解码线程
    return 0;
}

2、从队列中获取数据

/* return < 0 if aborted, 0 if no packet and > 0 if packet.  */

//1、参数block表示当对了为空时函数如何处理,0表示不阻塞返回0,否则阻塞,直到被唤醒
//2、参数serial 获取当前AVPacket的serial
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);
            q->duration -= pkt1->pkt.duration;
            *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;
}
       SDL_CondWait(q->cond, q->mutex);
    }
}
SDL_UnlockMutex(q->mutex);//释放队列的锁
return ret;

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值