在音视频处理流程中,ffplay的有两种队列,包缓存队列(Packet Buffer Queue)和帧缓存队列(Frame Buffer Queue)。这两个队列的存在,是为了适应音视频数据处理过程中的多线程架构——包括收包线程、解码线程和渲染线程。具体来说,收包线程负责从网络或文件中读取数据并将其放入包缓存队列中;解码线程从包缓存队列中取出数据进行解码,然后将解码后的数据放入帧缓存队列中;最后,渲染线程从帧缓存队列中取出数据进行渲染。由于每个线程的处理速度不同,缓存队列在这一过程中起到了平衡各线程工作负荷和避免数据丢失的关键作用。
音频、视频和字幕都经历了类似的处理流程,因此设计出高效且适应音视频特性的缓存队列显得尤为重要。ffplay中对于包缓存队列和帧缓存队列的设计不仅确保了音视频数据的流畅处理,还有效地提升了播放体验。这种设计通过合理的缓存策略和线程同步机制,成功地解决了音视频处理中的各种挑战。
一、包缓存队列
包缓存队列的设计需要考虑多个因素,以确保其高效性和稳定性。因为数据包本身通常较小,因此没有必要将缓存队列设计为循环队列,采用常规的入队申请内存和出队释放内存的方式即可。
ffplay中的包缓存队列设计适配了音视频的特性,和普通的队列相比有如下差异
- 序列号处理功能
使用serial字段来追踪数据包的顺序,在某些多路流(如音视频同步)场景中非常有用。每次队列重启或刷新时,serial都会递增,有助于区分不同的播放段。
比如说发生跳转时,又解码到了跳转之前的数据,可能会有回跳的现象,
ffplay会在发生跳转的时候,更新包的序列号,当解码到老的序列号,就把数据给丢弃掉,直到解码到新的数据。
- 自动增长的fifo队列
使用av_fifo_alloc2创建自动增长的FIFO队列,避免了频繁内存分配,提高了性能。
- 阻塞和非阻塞设置
阻塞与非阻塞模式:packet_queue_get函数通过block参数实现了阻塞和非阻塞模式的灵活切换,使得队列在不同的使用场景下能够适应需求。
1.1 PacketQueue结构体
//MyAVPacketList结构体的作用就是给包加上序列号
typedef struct MyAVPacketList {
AVPacket *pkt;
int serial;
} MyAVPacketList;
typedef struct PacketQueue {
AVFifo *pkt_list;//fifo队列,
int nb_packets;//packet数量
int size;//packet大小(字节)
int64_t duration;//持续时长
int abort_request;//中断请求
int serial;//序列号
SDL_mutex *mutex;//锁
SDL_cond *cond;//条件变量
} PacketQueue;
1.2 初始化
static int packet_queue_init(PacketQueue *q)
{
memset(q, 0, sizeof(PacketQueue));
//
q->pkt_list = av_fifo_alloc2(1, sizeof(MyAVPacketList), AV_FIFO_FLAG_AUTO_GROW);
if (!q->pkt_list)
return AVERROR(ENOMEM);
q->mutex = SDL_CreateMutex();
if (!q->mutex) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
q->cond = SDL_CreateCond();
if (!q->cond) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
q->abort_request = 1;
return 0;
}
1.3 开始运行
static void packet_queue_start(PacketQueue *q)
{
SDL_LockMutex(q->mutex);
q->abort_request = 0;
q->serial++;
SDL_UnlockMutex(q->mutex);
}
1.4 放入packet
//内部调用
static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
{
MyAVPacketList pkt1;
int ret;
if (q->abort_request)
return -1;
//给音视频包加上序列号
pkt1.pkt = pkt;
pkt1.serial = q->serial;
//把包添加进队列
ret = av_fifo_write(q->pkt_list, &pkt1, 1);
if

最低0.47元/天 解锁文章
6万+

被折叠的 条评论
为什么被折叠?



