音频解码流程和视频解码流程差不多,理解了一个另一个应该也很好掌握;
解码视频请移步【Qt+FFmpeg】FFmpeg解码本地视频流程_logani的博客-CSDN博客
一、视频播放器的实现框架
解码音频流程图:
二、音频解码具体流程
1.注册所有组件
void video::openAudioFile(QString filename)
{
av_register_all();
2.打开音频输入文件
AVFormatContext *formatContentAudio=avformat_alloc_context();
//打开输入视频文件,filename是传参进来的
int avformat_open_result=avformat_open_input(&formatContentAudio,filename.toStdString().c_str(),nullptr,nullptr);
if(avformat_open_result!=0)
{
qDebug()<<"无法打开输入音频文件";
return;
}
3.查找对应音频流
- 判断是否有流媒体数据-获取视频文件信息,第二个参数结构体用 null,返回一个 int 类型的数据,
- 然后遍历所有类型的流(音频流、视频流、字幕流),找到音频流 (信息都是在封装视频格式formatContent 里面);
- 结构体数组 formatContent->streams, 存放着多股流,每一股流都有一个该流对应的AVCodecContext
int avformat_find_stream_info_result=avformat_find_stream_info(formatContentAudio,nullptr);
if(avformat_find_stream_info_result<0)
{
qDebug()<<"无法获取视频文件信息";
return;
}
//获取音频流的索引位置
int audio_stream_idx =-1;
//nb_streams 代表封装格式里面的结构体信息有几个,正常就两个:一个音频信息,一个视频信息
for (int i=0;i<formatContentAudio->nb_streams;i++)
{
//流的类型
if(formatContentAudio->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{
audio_stream_idx=i;//标识类型
break;
}
}
//判断是否有音频流信息
if(audio_stream_idx==-1)
{
qDebug()<<"没找到音频流";
return;
}
4.查找音频解码器
查找解码器-根据音频流信息中的编码器类型 id 去查找合适的解码器, 返回的是一个解码器指针
AVCodecContext *codec=formatContentAudio->streams[audio_stream_idx]->codec;
AVCodec *decoder=avcodec_find_decoder(codec->codec_id);
if(decoder==nullptr)
{
qDebug()<<"找不到解码器";
return;
}
5.打开解码器
int avcodec_open2_result=avcodec_open2(codec,decoder,nullptr);
if(avcodec_open2_result!=0)
{
qDebug()<<"打开解码器失败";
return;
}
为准备读取帧数据做准备
AVPacket 用于存储一帧一帧的压缩音频数据
//缓冲区,开辟空间
AVPacket *pkt=(AVPacket *)malloc(sizeof(AVPacket));
int size=codec->width*codec->height;// save picture size
av_new_packet(pkt,size);
//AVFrame 用于存储解码后的音频数据
AVFrame *picture;
//内存分配
picture=av_frame_alloc();
//frame->16bit 44100 PCM 统一音频采样格式与采样率
SwrContext *swrCtx = swr_alloc();
//重采样设置选项------start
//输入的采样格式
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);
6.循环读取压缩音频数据
//存储 pcm 数据
uint8_t *out_buffer = (uint8_t *) av_malloc(2 * 44100);
FILE *fp_pcm = fopen("out.pcm", "wb+");
FILE *fp_aac = fopen("out.aac", "wb+");
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.解码一帧压缩音频数据
int av_decode_result=avcodec_decode_audio4(codec,picture,&got_picture_ptr,pkt);
if (av_decode_result < 0)
{
qDebug()<<"解码完成";
}
//为 0 说明解码完成,非 0 正在解码
if(got_picture_ptr!=0)
{
/*参数 1:音频重采样的上下文
参数 2:输出的指针。传递的输出的数组
参数 3:输出的样本数量,不是字节数。单通道的样本数量。
参数 4:输入的数组,AVFrame 解码出来的 DATA
参数 5:输入的单通道的样本数量。
*/
swr_convert(swrCtx,&out_buffer,2*44100, (const uint8_t**)(picture->data), picture->nb_samples);
//获取 sample 的 size
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);
}
}
//释放资源
av_packet_unref(pkt);
av_frame_unref(picture);
}
7.关闭释放资源
//关闭 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);
工程目录底下生成的音频数据文件
acc文件用腾讯视频可打开,pcm可用Softe Audio Converter软件进行查看;
感谢观看!!!!
以上就是全部内容,如果对您有帮助,欢迎点赞评论,或者发现有哪里写错的,欢迎指正!