Qt与FFmpeg开发指南(三)-- 音频解码

一、前期准备

头文件位于#include <libswresample/swresample.h>

二、流程图

三、代码


void fdecode_audio::registerFFmpeg()
{
    av_register_all();//1、注册所有组件

}

void fdecode_audio::openAudioStream(QString filename)
{
    //2、打开输入音频文件
    formatContentAudio=avformat_alloc_context(); 
    int res=avformat_open_input(&formatContentAudio,filename.toStdString().c_str(),nullptr,nullptr);
    if(res!=0)
    {
        qDebug()<<"打开输入音频文件失败";
        return;
    }
    //3、获取音频文件信息
    //是否有流媒体数据-》获取视频文件信息
    res=avformat_find_stream_info(formatContentAudio,nullptr);
    if(res<0)
    {
        qDebug()<<"获取视频文件信息失败";
        return;
    }
    //获取音频流的索引位置
    //遍历所有类型的流(音频流、视频流、字幕流),找到音频流 (信息都是在封装格式 formatContent 里面)
    //int audio_stream_idx =-1;
    //nb_streams 代表封装格式里面的结构体信息有几个,正常就两个:一个音频信息,一个视频信息
    for (int i=0;i<formatContentAudio->nb_streams;i++)
    {
        /*这里有一个结构体数组 formatContent->streams, 存放着多股流,每一股流都有一个该流对应的 AVCodecContext, */ 
        if(formatContentAudio->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
        {
            audio_stream_idx=i;//标识类型
            break;
        }
    }
    //判断是否有音频流信息
    if(audio_stream_idx==-1)
    {
        qDebug()<<"没找到音频流";
        return;
    }
    //⒋根据编解码上下文中的编码id查找对应的解码
    /*查找解码器-根据音频流信息中的编码器类型 id 去查找合适的解码器, 返回的是一个解码器指针 */
    AVCodecContext *codec=formatContentAudio->streams[audio_stream_idx]->codec;
    AVCodec *decoder=avcodec_find_decoder(codec->codec_id);
    if(decoder==nullptr)
    {
        qDebug()<<"未找到相应的解码器";
        return;
    }

    //5.打开解码器
    res=avcodec_open2(codec,decoder,nullptr);
    if(res!=0)
    {
        qDebug()<<"打开解码器失败";
        return;
    }
    //存储 pcm 数据
    out_buffer = (uint8_t *) av_malloc(2 * 44100);
    FILE *fp_aac = fopen("./output/audio_out.aac","wb+");
    FILE *fp_pcm = fopen("./output/audio_out.pcm","wb+");
    //⒍读取准备:为准备读取帧数据做准备
    //AVPacket 用于存储一帧一帧的压缩音频数据
    //缓冲区,开辟空间
    pkt=(AVPacket *)malloc(sizeof(AVPacket));
    int size=codec->width*codec->height;// save picture size
    av_new_packet(pkt,size);

    //内存分配
    picture=av_frame_alloc();
    //frame->16bit 44100 PCM 统一音频采样格式与采样率

    SwrContext *swrCtx = swr_alloc();

    //开始重采样设置选项
    //输入的采样格式
    enum AVSampleFormat in_sample_fmt = codec->sample_fmt;
    //输出的采样格式 16bit PCM
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //输入的采样率
    int in_sample_rate = codec->sample_rate;
    //输出的采样率
    int out_sample_rate = 44100;
    //输入的声道布局
    uint64_t in_ch_layout = codec->channel_layout;
    //输出的声道布局
    uint64_t out_ch_layout = AV_CH_LAYOUT_MONO;
    //获取输出的声道个数
    int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);

    swr_alloc_set_opts(swrCtx, out_ch_layout, out_sample_fmt, out_sample_rate,
                       in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr);
    swr_init(swrCtx);
    //重采样设置选项结束

    //⒎一帧一帧的读取压缩音频数据--保存音频码流数据 aac 和 pcm 音频采样数据
    while(av_read_frame(formatContentAudio,pkt)>=0)
    {
        //只要有音频压缩数据(根据流的索引位置判断)
        if(pkt->stream_index==audio_stream_idx)
        {
            //将码流数据写入 aac 文件中
            fwrite(pkt->data,pkt->size,1,fp_aac);
            int got_picture_ptr=-1;
            //7.1解码一帧压缩音频数据
            res=avcodec_decode_audio4(codec,picture,&got_picture_ptr,pkt);
            if (res < 0)
            {

                qDebug()<<"解码完成";
            }
            //为 0 说明解码完成,非 0 正在解码
            if(got_picture_ptr!=0)
            {
                //音频重采样
                swr_convert(swrCtx,&out_buffer,2*44100,
                            (const uint8_t**)(picture->data), picture->nb_samples);

                //获取给定音频参数所需的缓冲区大小               
                int out_buffer_size = av_samples_get_buffer_size(nullptr, out_channel_nb,
                                                                 picture->nb_samples,
                                                                 out_sample_fmt, 1); //写入文件
                fwrite(out_buffer, 1, out_buffer_size, fp_pcm);
            }
        }
        //8、释放资源
        av_packet_unref(pkt);
        av_frame_unref(picture);
    }

    qDebug()<<"写入文件成功";
    //关闭 out.pcm 文件
    fclose(fp_pcm);
    fclose(fp_aac);
    //释放AVFrame
    av_frame_free(&picture);
    av_free(out_buffer);
    swr_free(&swrCtx);
    //关闭解码器
    avcodec_close(codec);
    //释放视频信息结构体
    avformat_free_context(formatContentAudio);
}

void fdecode_audio::run()
{
    this->fileName="Warcraft3_End.avi";//要打开是视频文件名
    this->registerFFmpeg();//注册
    this->openAudioStream(this->fileName);
}

main中测试:

 

 

四、实现效果

 aac 的音频文件可以直接播放器播放;pcm 格式的音频可以通过Audacity工具播放 

 关于本地视频的解码:

Qt与FFmpeg开发指南(二)-- 本地视频的解码_hml111666的博客-CSDN博客

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,针对您的问题,我来简单回答一下。 首先,要明确的是,FFmpeg 是一个开源的跨平台音视频处理库,支持包括解码、编码、封装、解封装等功能。而解封装(Demuxing)是指将音视频封装格式中的音视频流解析出来,以便后续对音视频流进行解码、编码等操作。 在 Android 基于 FFmpeg 开发简易播放器中,我们可以使用 FFmpeg 提供的 API 来进行解封装。具体步骤如下: 1. 打开输入文件 使用 FFmpeg 的 avformat_open_input() 函数打开要解封装的音视频文件,该函数返回一个 AVFormatContext 结构体指针,该指针包含了输入文件的相关信息。 2. 寻找音视频流 使用 FFmpeg 的 avformat_find_stream_info() 函数读取输入文件的文件头信息,并寻找其中包含的音视频流。该函数会将每个音视频流的信息保存在 AVStream 结构体中。 3. 选择音视频流 根据需要播放的音视频流类型,在所有寻找到的音视频流中选择对应的流。可以通过判断 AVStream 结构体中的 codecpar->codec_type 来确定该流是音频流还是视频流。 4. 获取解码器 使用 FFmpeg 的 avcodec_find_decoder() 函数获取对应的解码器,并使用 avcodec_open2() 函数打开解码器。 5. 循环读取数据包 使用 FFmpeg 的 av_read_frame() 函数循环读取音视频数据包(AVPacket 结构体),并将数据包送到解码器进行解码。 6. 关闭解码器和输入文件 在播放完成后,需要使用 avcodec_free_context() 函数释放解码器占用的资源,并使用 avformat_close_input() 函数关闭输入文件。 以上就是基于 FFmpeg 进行解封装的大致步骤。当然,在实际开发中还有很多细节需要注意,比如错误处理、内存管理等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ze言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值