ffmpeg 音视频解码

音视频播放的流程

根据我之前写的文章,基于ffmpeg来对视频进行解复用,提取音视频压缩数据pkt。现在我们来看看如何进行将pkt进行解码得到原始数据

音视频频解码过程

⾳频解码过程如下图所示:

基于FFmpeg解码流程
cpp 复制代码
主要api
// 1. 查找解码器
codec = avcodec_find_decoder(audio_codec_id);  // AV_CODEC_ID_AAC
//2.获取裸流的解析器 AVCodecParserContext(数据)  +  AVCodecParser(方法)
parser = av_parser_init(codec->id);  根据指定的解码器ID初始化相应裸流的解析器
// 3.分配codec上下文 坑点:使用新版api
codec_ctx = avcodec_alloc_context3(codec);	分配解码器上下文
// 4.将解码器和解码器上下文进行关联
avcodec_open2(codec_ctx, codec, NULL) 		打开解码器和关联解码器上下文


//5.开始解析,解析获得⼀个Packet
@param s 解析器上下文。
@param avctx 编解码器上下文。
@param poutbuf 设置为指向已解析的缓冲区的指针,或者如果还没有完成则设置为 NULL。
@param poutbuf_size 设置为已解析缓冲区的大小,或者如果还没有完成则设置为 0。
@param buf 输入缓冲区。
@param buf_size 缓冲区大小(以字节为单位),不包括填充。即整个缓冲区大小假定为 buf_size + AV_INPUT_BUFFER_PADDING_SIZE。
要标记文件结束(EOF),应该设置为 0(以便输出最后一帧)。
@param pts 输入的presentation timestamp。
@param dts 输入的解码timestamp。
@param pos 输入流中的字节位置。
@return 返回使用的输入比特流的字节数。
//解析一个数据包。
ret = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size,
                               data, data_size,
                               AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);


//6.将压缩后的数据包发送给解码器  注意:pkt可以使用引用技术版本 即new free  unref相关api 没使用即copy 以及注意返回值
ret = avcodec_send_packet(dec_ctx, pkt);
if(ret == AVERROR(EAGAIN))//队满
{
    cout<<"avcodec_send_frame AVERROR(EAGAIN) error"<<endl;
}
else if(ret == AVERROR_EOF){ //读完
    cout<<"avcodec_send_frame AVERROR_EOF error"<<endl;
}else if(ret == AVERROR(EINVAL)) //编码器未打开
{
    cout<<"avcodec_send_frame AVERROR(EINVAL) error"<<endl;
    return -2;
}
else if(ret < 0 ){
    fprintf(stderr, "Error sending the frame to the encoder\n");
    return -1;
}


// 7.从解码器接收一帧数据  对于frame, avcodec_receive_frame内部每次都先调用unref
ret = avcodec_receive_frame(dec_ctx, frame);
if(ret == AVERROR(EAGAIN))//队空 编码器或解码器的内部缓冲区已空,无法立即处理新的数据包。等待发送
...
else if(ret == AVERROR_EOF)//读完 表示已经处理完所有的输入数据包,已经达到了数据流的末尾。
else if(ret == AVERROR(EINVAL)) //编码器未打开
{
    cout<<"avcodec_send_frame AVERROR(EINVAL) error"<<endl;
    return -2;
}
else if(ret < 0 ){
    fprintf(stderr, "Error sending the frame to the encoder\n");
    return -1;
}


//获取给定音频采样格式对应的每个样本所占用的字节数。
data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);

视频解码过程如下:

⼀般解出来的是420p

基于FFmpeg解码流程

api与音频相关类似

总结

1.AVCodecParser:⽤于解析输⼊的数据流并把它分成⼀帧⼀帧的压缩编码数据。⽐较形象 的说法就是把⻓⻓的⼀段连续的数据"切割"成⼀段段的数据。

2.FFmpeg提供了两组函数,分别⽤于编码和解码:

解码:调⽤avcodec_receive_frame(),如果成功会返回⼀个包含未压缩数据的 AVFrame。 编码:调⽤avcodec_receive_packet(),如果成功会返回⼀个包含压缩数据的 AVPacket。

3.在⼀个循环体内去接收codec的输出,即周期性地调⽤avcodec_receive_*()来接收codec 输出的数据。

反复地调⽤avcodec_receive_packet()直到返回 AVERROR(EAGAIN)或其他错误。返回 AVERROR(EAGAIN)错误表示codec需要新的输⼊来输出更多的数据。对于每个输⼊的 packet或frame,codec⼀般会输出⼀个frame或packet,但是也有可能输出0个或者多 于1个。

  1. 流处理结束的时候需要flush(冲刷) codec。因为codec可能在内部缓冲多个frame或 packet,如果继续使用可能造成下次使用数据不匹配。

处理流程如下: 调⽤avcodec_send_*()传⼊的AVFrame或AVPacket指针设置为NULL。 这将进⼊ draining mode(排⽔模式)。 反复地调⽤avcodec_receive_*()直到返回AVERROR_EOF ,该⽅法在draining mode 时不会返回AVERROR(EAGAIN)的错误,除⾮你没有进⼊draining mode。 当重新开启codec时,需要先调⽤ avcodec_flush_buffers()来重置codec。

/* 冲刷解码器 */

pkt->data = NULL; // 让其进入drain mode

pkt->size = 0;

decode(codec_ctx, pkt, decoded_frame, outfile);

相关推荐
音视频牛哥1 小时前
大牛直播SDK(SmartMediaKit)Windows平台RTSP/RTMP直播播放SDK集成说明(C++版)
windows·音视频·实时音视频·windows rtsp播放器·windows rtmp播放器·超低延迟rtsp播放器·超低延迟rtmp播放器
EasyGBS3 小时前
1分钟讲清楚选EasyNVR还是国标GB28181视频平台EasyGBS:路线不同,别选错
音视频
日光明媚4 小时前
深度解析 SGLang 框架 Wan2.1 视频生成加速技术:从 49 分钟到 1 分钟的极致优化
人工智能·计算机视觉·aigc·音视频·sglang
小猿君4 小时前
谷歌I/O前夜Veo 4遭泄露,AI视频底层逻辑浮出水面
人工智能·音视频
南山有乔木7894 小时前
音频怎么转换MP3格式?M4A、WAV、FLAC转mp3实测有效的格式转换方法
音视频
不昀4 小时前
音频变压器Bourns SM-LP-5001国产替代选型指南
网络·音视频·以太网·网络通信·电子元器件
REDcker4 小时前
RGB与YUV像素格式详解
音视频·实时音视频·视频编解码·yuv·rgb
水上冰石4 小时前
v1-5-pruned-emaonly.safetensors 搭配mm_sd_v15_v2.ckpt 生成视频,具体操作步骤
stable diffusion·音视频·文生视频
searchforAI4 小时前
我用这款本土NotebookLM平替重构了知识库
人工智能·笔记·gpt·ai·音视频·知识图谱
美狐美颜SDK开放平台5 小时前
美颜SDK开发详解:如何优化美颜SDK在低端安卓机上的性能?
android·ios·音视频·直播美颜sdk·视频美颜sdk