ffplay---源码分析(二):自定义队列---FrameQueue

本文深入分析ffplay的FrameQueue源码,讲解其初始化、销毁、写入和读取过程,特别是环形缓冲区的设计以防止读写速度不匹配的问题。重点阐述了条件变量在同步中的作用,以及如何通过减小锁的范围提高效率。此外,还介绍了FrameQueue的独特特性——保留上一读取的frame。
摘要由CSDN通过智能技术生成
FrameQuene的关键函数:
1、初始化static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
2、写入	
{
   
	1、获取可写节点	static Frame *frame_queue_peek_writable(FrameQueue *f)
	2、写入			av_frame_move_ref(vp->frame, src_frame);
	3、更新quene		frame_queue_push(q); //期间SDL_CondSignal通知读线程有数据了 + SDL_UnlockMutex
}
3、读【新特性:保留上一个节点FrameQueue.keep_last】
{
   
	1static Frame *frame_queue_peek_readable(FrameQueue *f)  	
									没数据就SDL_CondWait(f->cond, f->mutex);	
									等frame_queue_push通过通知
	2static void frame_queue_next(FrameQueue *f)SDL_CondSignal(f->cond);通知写线程有新空位了 +SDL_UnlockMutex】
										※类似pthread_cond_signal:释放互斥锁mutex,并发出信号signal
}	
4、销毁		static void frame_queue_destory(FrameQueue *f)

为了防止竞争,使用条件变量cond进行同步时常常和互斥锁mutex结合使用
一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)
pthread _mutex_lock(&mutex)
while或if(线程执行的条件不成立)
  pthread_cond_wait(&cond, &mutex);//阻塞自己并释放mutex(让其他线程能够访问公有资源)
线程执行
pthread_mutex_unlock(&mutex);
如果while或者if判断的时候,不满足线程的执行条件,那么线程便会调用pthread_cond_wait阻塞自己,但是它持有的锁怎么办呢,如果他不归还操作系统,那么其他线程将会无法访问公有资源。这就要追究一下pthread_cond_wait的内部实现机制,当pthread_cond_wait被调用线程阻塞的时候,pthread_cond_wait会自动释放互斥锁。释放互斥锁的时机是什么呢:是线程从调用pthread_cond_wait到操作系统把他放在线程等待队列之后,这样做有一个很重要的原因,就是mutex的第二个作用,保护条件。想一想,线程是并发执行的,如果在没有把被阻塞的线程A放在等待队列之前,就释放了互斥锁,这就意味着其他线程比如线程B可以获得互斥锁去访问公有资源,这时候线程A所等待的条件改变了,但是它没有被放在等待队列上,导致A忽略了等待条件被满足的信号。倘若在线程A调用pthread_cond_wait开始,到把A放在等待队列的过程中,都持有互斥锁,其他线程无法得到互斥锁,就不能改变公有资源。这就保证了线程A被放在等待队列上之后才会有公有资源被改变的信号传递给等待队列
下面来讲一下:pthread_cond_wait和pthread_cond_singal是怎样配对使用的:
1、等待线程:
 pthread_cond_wait前要先加锁
 pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
 pthread_cond_wait被激活后会再自动加锁
2、激活线程:
 加锁(和等待线程用同一个锁)
 pthread_cond_signal发送信号(阶跃信号前最好判断有无等待线程)
 等待线程解锁
激活线程的上面三个操作在运行时间上都在等待线程的pthread_cond_wait函数内部。

不同于PacketQueue的链表实现队列,而是用数组实现队列(环形缓冲区)且更复杂。
FrameQueue设计理念:

1、高效率的读写模型(回顾PacketQueue的设计,每次访问都需要加锁整个队列,锁范围很大)
2、高效的内存模型(节点内存以数组形式预分配,无需动态分配)
3、环形缓冲区设计,同时可以访问上一读节点

typedef struct FrameQueue {
   
    Frame queue[FRAME_QUEUE_SIZE];				队列节点
    int rindex;									读指针
    int windex;									(待写节点)写指针
    int size;									有效的节点数
    int max_size;								= FFMIN(max_size, FRAME_QUEUE_SIZE);
    int keep_last;								是否要保留读取了的最后一个节点(不被覆写,保留最后显示的一帧)
    int rindex_shown;							当前节点是否已经显示
    SDL_mutex *mutex;
    SDL_cond *cond;
    PacketQueue *pktq;							关联至PacketQueue  是为了使用其中的终止标识abort_request
} FrameQueue;

typedef struct Frame {
   							即解码帧队列节点
    AVFrame *frame;  //音频或视频的解码数据
    AVSubtitle sub;  //解码的字幕数据
    int serial;
    double pts;           /* presentation timestamp for the frame */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值