Qt与FFmpeg联合开发指南(三)-- 解码播放本地音视频

FFmpeg音视频解码流程

  • AAC视频格式解码为PCM编码格式
  • h264视频格式解码为YUV编码格式

音频解码流程与视频解码流程大同小异(视频解码流程详细见:Qt与FFmpeg联合开发指南(二)-- 解码播放本地视频), 使用不同的解码器对音频视频流进行解码操作。初始化操作两者一致。

核心代码:

先添加音频采样数据格式库:

//音频采样数据格式库
​​​​​​​#include <libswresample/swresample.h>
第一步:获取当前解码器是属于什么类型解码器
    qDebug()<<"第四步:查找解码器";
    int av_stream_index = -1;
    int audio_index = -1;
    for (int i = 0; i < avformat_context->nb_streams; ++i) {
        //循环遍历每一流
        //视频流、音频流、字幕流等等...
        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            //找到了视频流
            av_stream_index = i;
        }
        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            //找到了音频
            audio_index = i;
        }
    }
    if (av_stream_index == -1 )
    {
        qDebug()<<QString("没有找到视频流");
        return false;
    }
    if (audio_index == -1 )
    {
        qDebug()<<QString("没有找到音频流");
        return false;
    }

 第二步:根据流信息打开对应解码器

    //第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
    AVCodecContext* avcodec_context = avformat_context->streams[av_stream_index]->codec;

    //第三点:根据解码器上下文->获取解码器ID
    AVCodec* avcodec = avcodec_find_decoder(avcodec_context->codec_id);
    if (avcodec == NULL)
    {
        qDebug()<<QString("没有找到视频解码器");
        return false;
    }

    qDebug()<<"第五步:打开视频解码器";
    //第五步:打开解码器
    int avcodec_open2_result = avcodec_open2(avcodec_context,avcodec,NULL);
    if (avcodec_open2_result != 0)
    {
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
        return false;
    }
    ////////音频

    //第二点:根据音频流->查找到音频解码器上下文->音频压缩数据
    AVCodecContext* avcodec_context_audio = avformat_context->streams[audio_index]->codec;

    //第三点:根据解码器上下文->获取解码器ID
    AVCodec* avcodec_audio = avcodec_find_decoder(avcodec_context_audio->codec_id);
    if (avcodec_audio == NULL)
    {
        qDebug()<<QString("没有找到音频解码器");
        return false;
    }

    qDebug()<<"      打开音频解码器";
    //第五步:打开音频解码器
    int avcodec_open2_result1 = avcodec_open2(avcodec_context_audio,avcodec_audio,NULL);
    if (avcodec_open2_result1 != 0)
    {
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
        return false;
    }

输出解码器名称

  qDebug()<<QString("视频解码器名称: %1").arg(avcodec->name);
  qDebug()<<QString("音频解码器 %1").arg(avcodec_audio->name);

第三步:创建音视频压缩数据帧

  //循环读取视频帧,进行循环解码->输出YUV420P视频->格式:yuv格式
    qDebug()<<"第六步:循环读取视频帧,进行循环解码";
    //读取帧数据换成到哪里->缓存到packet里面
    AVPacket* av_packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    //输入->环境一帧数据->缓冲区->类似于一张图
    AVFrame* av_frame_in = av_frame_alloc();
    //输出->帧数据->视频像素数据格式->yuv420p
    AVFrame* av_frame_out_yuv420p = av_frame_alloc();
    //缓冲区分配内存
    uint8_t *out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height));
    //初始化缓冲区
    avpicture_fill((AVPicture *)av_frame_out_yuv420p, out_buffer, AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);
    //解码的状态类型(0:表示解码完毕,非0:表示正在解码)
    int  av_decode_result, y_size, u_size, v_size, current_frame_index = 0,current_audio_index = 0;
    //准备一个视频像素数据格式上下文
    //参数一:输入帧数据宽
    //参数二:输入帧数据高
    //参数三:输入帧数据格式
    //参数四:输出帧数据宽
    //参数五:输出帧数据高
    //参数六:输出帧数据格式->AV_PIX_FMT_YUV420P
    //参数七:视频像素数据格式转换算法类型
    //参数八:字节对齐类型(C/C++里面)->提高读取效率
    SwsContext* sws_context = sws_getContext(avcodec_context->width,
                                             avcodec_context->height,
                                             avcodec_context->pix_fmt,
                                             avcodec_context->width,
                                             avcodec_context->height,
                                             AV_PIX_FMT_YUV420P,
                                             SWS_BICUBIC,NULL,NULL,NULL);
    /////////////////////////////////////////////////////////////视频处理
 
    //创建音频压缩数据帧->acc格式、mp3格式
    AVPacket* avpacket_audio = (AVPacket*)av_malloc(sizeof(AVPacket));
    //创建音频采样数据帧
    AVFrame* avframe_audio = av_frame_alloc();
    // 确保解码为pcm我们需要转换为pcm格式
    SwrContext*swr_context_audio = swr_alloc();
    swr_alloc_set_opts(swr_context_audio,
                       AV_CH_LAYOUT_STEREO,//立体声 输出声道布局类型
                       AV_SAMPLE_FMT_S16, //输出采样精度
                       avcodec_context_audio->sample_rate, //输出采样率
                       avcodec_context_audio->channel_layout, //输入声道布局类型
                       avcodec_context_audio->sample_fmt, //输入采样精度
                       avcodec_context_audio->sample_rate, //输入采样率
                       NULL, NULL);
    //初始化音频采样数据上下文
    swr_init(swr_context_audio);
    //缓冲区大小 = 采样率(44100HZ) * 采样精度(16位 = 2字节)
    int MAX_AUDIO_SIZE = 44100 * 2;
    uint8_t *out_audio= (uint8_t *) av_malloc(MAX_AUDIO_SIZE);;
    //获取输出的声道个数
    int out_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

    int audio_decode_result = 0;

第四步:循环读取音频压缩数据

  FILE* out_file_pcm = fopen("diao.pcm","wb+");
    if (out_file_pcm == NULL){
        qDebug()<<QString("文件不存在 ");
        return false;
    }   
     while (av_read_frame(avformat_context,av_packet) >= 0)
    {
         //解码什么类型流(视频流、音频流、字幕流等等...)
        if (av_packet->stream_index == av_stream_index)
        {
           //略 (参照上一篇)
        }
        //解码什么类型流(视频流、音频流、字幕流等等...)
        if (av_packet->stream_index == audio_index)
        {
            //音频解码
            //1、发送一帧音频压缩数据包->音频压缩数据帧
            avcodec_send_packet(avcodec_context_audio, av_packet);

            //2、解码一帧音频压缩数据包->得到->一帧音频采样数据->音频采样数据帧
            av_decode_result = avcodec_receive_frame(avcodec_context_audio, avframe_audio);
            // 解码成功
            if ( av_decode_result == 0 )
            {
                //3、类型转换(音频采样数据格式有很多种类型)
                //我希望我们的音频采样数据格式->pcm格式->保证格式统一->输出PCM格式文件
                //swr_convert:表示音频采样数据类型格式转换器
                //参数一:音频采样数据上下文
                //参数二:输出音频采样数据
                //参数三:输出音频采样数据->大小
                //参数四:输入音频采样数据
                //参数五:输入音频采样数据->大小
                swr_convert(swr_context_audio,
                            &out_audio,
                            MAX_AUDIO_SIZE,
                            (const uint8_t **)avframe_audio->data,
                            avframe_audio->nb_samples);
                //4、获取缓冲区实际存储大小
                //参数一:行大小
                //参数二:输出声道数量
                //参数三:输入大小
                int nb_samples = avframe_audio->nb_samples;
                //参数四:输出音频采样数据格式
                //参数五:字节对齐方式
                int out_buffer_size = av_samples_get_buffer_size(NULL,
                                                                 out_nb_channels,
                                                                 nb_samples,
                                                                 AV_SAMPLE_FMT_S16,
                                                                 1);

                //5、写入文件
                fwrite(out_audio, 1, out_buffer_size, out_file_pcm);
                current_audio_index++;
                qDebug()<<QString("音频当前遍历第 %1 帧").arg(current_audio_index);

            }
        }
    }

第五步:关闭音频解码器

   av_packet_free(&avpacket_audio);
    swr_free(&swr_context_audio);
    av_free(out_buffer);
    avcodec_close(avcodec_context_audio);
    av_frame_free(&avframe_audio);

解码后存储地pcm音频文件使用:Softe Audio Converter软件进行查看

猜你喜欢

转载自blog.csdn.net/qq_34623621/article/details/106298038