介绍
在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器
如果您不理解本文,可参考我的前一篇文章音视频项目—基于FFmpeg和SDL的音视频播放器解析(七)
解析
上文还有个函数没有解析完,这篇文章和大家接着将 decodethread 的 Run 函数,这个函数负责解码线程的运行,因此非常重要,我才放在最后讲。
void DecodeThread::Run(){
AVFrame* frame = av_frame_alloc();
while (abort != 1)
{
if(frame_queue->Size() > 10){
this_thread::sleep_for(chrono::milliseconds(10));
continue;
}
AVPacket* pack = packet_queue->Pop(10);
if(pack){
int ret = avcodec_send_packet(codec_ctx, pack);
av_packet_free(&pack);
if(ret < 0){
break;
}
while (true) {
ret = avcodec_receive_frame(codec_ctx, frame);
if(ret == 0){
frame_queue->Push(frame);
continue;
}else if (AVERROR(EAGAIN) == ret) {
break;
}else {
abort = 1;
break;
}
}
}else {
perror("not got packet");
}
}
}
首先,我们要弄懂解码的目的,就是讲包(Packet)中的帧(Frame)数据提取出来,放入相应的队列当中。
于是,我们首先声明一个局部变量 AVFrame,并用 av_frame_alloc 分配内存,不多解释。
然后,有一个 while 循环,当标识位 abort 不等于 1 时,就不断循环运行代码。
然后我们继续看这段代码:
if(frame_queue->Size() > 10){
this_thread::sleep_for(chrono::milliseconds(10));
continue;
}
这段代码意思是如果帧队列的长度超过 10 的话,线程停止 10 毫秒,至于为什么这样,我还不太清楚,希望有高手指点迷津哈。
然后抽取包数据:
AVPacket* pack = packet_queue->Pop(10);
这个 Pop 是自定义的,功能是将队列的头部删除并返回,于是 AVPacket 变量 pack 获取了队列头部,队列也删除了头部。
然后开始有一个条件判断,如果 pack 有数据不为空时继续执行,否则停止程序。
这是 pack 不为空时的逻辑
int ret = avcodec_send_packet(codec_ctx, pack);
av_packet_free(&pack);
if(ret < 0){
break;
}
通过 avcodec_send_packet 将 pack 发送给上下文,如果出错,则中止。
然后有一个死循环:
while (true) {
ret = avcodec_receive_frame(codec_ctx, frame);
if(ret == 0){
frame_queue->Push(frame);
continue;
}else if (AVERROR(EAGAIN) == ret) {
break;
}else {
abort = 1;
break;
}
}
通过刚才接收了包数据的解码器上下文,用 avcodec_receive_frame,接收解码的帧(Frame)。
然后开始条件判断,如果接收帧成功就放入帧队列,继续运行。否则终止死循环。
好了,decodethread 的最后一个函数 Run 我们讲完了,这个比较重要,内容也比较多,大家可以慢慢理解。
下一篇文章,我们会讲解队列 Queue。
欲知后事如何,请听下回分解。