FFmpeg开发之旅(一)---视频解码

【写在前面】

本篇只单讲 FFmpeg 解码视频,即使含有音频。

本篇主要内容:

1、多媒体基础概念

2、视频基础概念

3、FFmpeg解码基本流程

【正文开始】

首先,稍微解释一下,什么是多媒体。

从字面意思看:多媒体 (Multimedia) 是多种媒体的综合,一般包括文本,声音和图像等多种媒体形式。

实际上,容纳了多种媒体的集合,也可以称之为多媒体容器。

当然,整个多媒体的格式非常多,比如我们常见的视频格式有:mp4、avi、wmv、mkv,音频格式有:mp3、wav等等。

可能你还听说过h.264、mpeg4格式,然而那些都是编码格式,与多媒体的格式不同。

要注意,这里的容器和编码是两个不同的概念:

对于一个多媒体容器,它可以包含视频,音频,字幕等等,而编码则是压缩视频、音频的某个算法。

例如,有一个MP4文件(容器),它里面有使用 A 编码的视频和使用 B 编码的音频,而且 A 和 B 并不是固定的,可以是任意。

因此,想要播放一部视频的第一步,就是要知道 编码 A 和 编码 B。

这一步一般称之为解复用 Demux ( 或者叫解封装 )。

  • 现在进入第一步:解复用。

我们先不管各种AV*、AV*Context等等各种数据结构,这里先讲流程:

    avformat_open_input(&formatContext, m_filename.toStdString().c_str(), nullptr, nullptr);
    avformat_find_stream_info(formatContext, nullptr);
 
    videoIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
 
    if (videoIndex >= 0)
        videoStream = formatContext->streams[videoIndex];
 
    if (videoStream)
        videoDecoder = avcodec_find_decoder(videoStream->codecpar->codec_id);
 
    if (videoDecoder)
        codecContext = avcodec_alloc_context3(videoDecoder);
 
    if (codecContext)
        avcodec_parameters_to_context(codecContext, videoStream->codecpar);
 
    if (codecContext)
        avcodec_open2(codecContext, videoDecoder, nullptr);

1、使用 avformat_open_input() 来打开一个媒体文件。

2、使用 avformat_find_stream_info() 读取媒体文件的数据以获取流信息。

所谓的流是码流,即经过编码后的数据流。

3、使用 av_find_best_stream() 找到匹配(最佳)的流并返回其索引,记住,所有的流存在于 formatContext->streams 数组中。实际上,到这里解复用就算是结束了,不过,在解码之前,还需要一些额外的工作。

4、使用 avcodec_find_decoder() 找到匹配的编解码器。

5、使用 avcodec_alloc_context3() 分配一个 AVCodecContext *,后面需要使用 avcodec_free_context() 来释放它。

6、使用 avcodec_parameters_to_context() 使用编解码器中相关值来填充编解码器上下文 ( AVCodecContext )。

7、最后,打开编解码上下文:avcodec_open2() 。

  • 一切准备就绪,现在进入第二步,开始解码。

前面提到过,编码就是使用某种算法把视频( 或者音频 ) 给压缩了 视频 -> 码流。

那么解码就是把码流还原成视频, 码流 -> 视频。

    AVPacket *packet = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();
    packet->data = nullptr;
    packet->size = 0;
 
    while (m_runnable && av_read_frame(formatContext, packet) >= 0) {
 
        if (packet->stream_index == videoIndex) {
            //发送给解码器
            int ret = avcodec_send_packet(codecContext, packet);
 
            while (ret >= 0) {
                //从解码器接收解码后的帧
                ret = avcodec_receive_frame(codecContext, frame);
 
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
                else if (ret < 0) return;
 
                int dst_linesize[4];
                uint8_t *dst_data[4];
                av_image_alloc(dst_data, dst_linesize, m_width, m_height, AV_PIX_FMT_RGB24, 1);
                sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, dst_data, dst_linesize);
                QImage image = QImage(dst_data[0], m_width, m_height, QImage::Format_RGB888).copy();
                av_freep(&dst_data[0]);
 
                m_frameQueue.enqueue(image);
 
                av_frame_unref(frame);
            }
        }
 
        av_packet_unref(packet);
    }

1、初始化一个 AVPacket 和 AVFrame。

注意:FFmpeg很多时候都要求传入的指针被初始化为nullptr,例如packet->data。

2、使用 av_read_frame() 读取一帧数据包。

3、使用 avcodec_send_packet() 将数据包发送给解码器。

4、使用 avcodec_receive_frame() 接收一帧解码器解码后的数据( 这里是图像 ),然后我用 sws_scale() 将其转换成 QImage ,接着定时( 根据 fps )显示 image 即可播放。

5、av_frame_unref(frame),av_packet_unref(packet) 和 C++中的引用计数类似,不过需要手动取消计数,注意,如果不取消引用计数,就不会释放内存。

至此,我们就完成了使用 FFmpeg 解码视频的基本流程。

这里,我画了个图,大概就是这样:

【结语】

本篇主要就讲了视频的一些基本的概念,因为我也还在学习中。

然后这里只有一些关键部分的代码,完整代码见:https://github.com/mengps/FFmpeg-Learn,编译运行就可以看到效果了。

还有关于图像转换部分的也没有讲,毕竟这里主要讲解码流程,所以可能后面会进行讲解吧。

原文链接:FFmpeg开发之旅(一)---视频解码_纯软件编码解码 ffmpeg 压缩视频mpeg_梦起丶的博客-CSDN博客

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值