音视频项目—基于FFmpeg和SDL的音视频播放器解析(八)

介绍

在本系列,我打算花大篇幅讲解我的 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。

欲知后事如何,请听下回分解。

你可能感兴趣的:(音视频,ffmpeg)