FFmpeg 核心 API 系列:avcodec_find_decoder / avcodec_alloc_context3 / avcodec_open2
🎬 FFmpeg 核心 API 系列:avcodec_find_decoder / avcodec_alloc_context3 / avcodec_open2 全解析
📅 更新时间:2025年10月2日
🏷️ 标签:FFmpeg | 多媒体处理 | 音视频编程 | C/C++ | 流媒体
文章目录
- 📖 前言
- 🎯 三个核心API详解
- API 1️⃣:`avcodec_find_decoder` - 查找解码器
- 函数原型
- 参数说明
- 返回值
- 作用
- 用法一:通过 `codec_id` 查找对应的解码器
- 用法二:指定解码器名称查找
- 练习小Demo
- API 2️⃣:`avcodec_alloc_context3` - 分配解码器上下文
- 函数原型
- 参数说明
- 返回值
- 作用
- 示例
- 🔧 辅助API:`avcodec_parameters_to_context` - 复制参数
- 为什么需要这一步?
- 函数原型
- 返回值
- 示例
- API 3️⃣:`avcodec_open2` - 打开解码器
- 函数原型
- 参数说明
- 返回值
- 作用
- 示例
- 🔑 关键数据结构
- AVCodec(解码器)
- AVCodecContext(解码器上下文)
- 💻 完整小Demo
- 📋 总结
📖 前言
回顾上一篇文章,我们已经能:
- 打开文件 → 得到
AVFormatContext
- 分析流信息 → 得到
AVStream
(包含编码参数codecpar
)
但是!这些信息只是"参数",并不能解码。就像你知道一个文件是H.264编码的,但还需要一个"H.264解码器"才能把压缩数据变成图像。
解码器的作用:将压缩的数据包(AVPacket
)→ 解码成原始的帧(AVFrame
)
🎯 三个核心API详解
API 1️⃣:avcodec_find_decoder
- 查找解码器
函数原型
const AVCodec *avcodec_find_decoder(enum AVCodecID id);
参数说明
参数 | 说明 |
---|---|
id | 编码ID(从 stream->codecpar->codec_id 获取) |
返回值
- 成功:返回解码器指针(
AVCodec*
) - 失败:返回
NULL
作用
根据编码ID找到对应的解码器
用法一:通过 codec_id
查找对应的解码器
AVCodecID cid=stream->codecpar->codec_id;
//根据编码ID查找对应解码器
const AVCodec * decoder=avcodec_find_decoder(cid);
用法二:指定解码器名称查找
注意事项:要判断一下这个解码器是否能解码对应的流
const AVCodec *codec = avcodec_find_decoder_by_name("h264_qsv");
if (!codec) {qDebug() << "未找到 h264_qsv 解码器";
} else {if (codec->id == stream->codecpar->codec_id) {qDebug() << "此解码器可用于该流";} else {qDebug() << "此解码器和流的编码ID不匹配";}
}
练习小Demo
#include "mainwindow.h"
#include<QDebug>
#include <QApplication>extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
}int main(int argc, char *argv[])
{QApplication a(argc, argv);//MainWindow w;//w.show();AVFormatContext* con=nullptr;QString path="E:/BaiduNetdiskDownload/音视频资料/1、C++实战手把手教您用ffmpeg和QT开发播放器实战视频课程/1-02、音视频解封装和解码原理分析_ev.mp4";int r=avformat_open_input(&con,path.toUtf8().data(),nullptr,nullptr);if(r<0){qDebug()<<"avformat_open_input is error";}else{qDebug()<<"avformat_open_input is success";}r=avformat_find_stream_info(con,nullptr);if(r<0){qDebug()<<"avformat_find_stream_info is error";}else{qDebug()<<"avformat_find_stream_info is success";for(unsigned i=0;i<con->nb_streams;i++){AVStream* stream=con->streams[i];if(stream->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){qDebug()<<"找到视频流";AVCodecID cid=stream->codecpar->codec_id;//根据编码ID查找对应解码器const AVCodec * decoder=avcodec_find_decoder(cid);if(!decoder){qDebug()<<"此视频流找不到对应解码器";}else{qDebug()<<"此视频流的编码ID为:"<<cid;qDebug()<<"此视频流成功找到对应解码器";}}else if(stream->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){qDebug()<<"找到音频流";AVCodecID cid=stream->codecpar->codec_id;//根据编码ID查找对应解码器const AVCodec * decoder=avcodec_find_decoder(cid);if(!decoder){qDebug()<<"此音频流找不到对应解码器";}else{qDebug()<<"此音频流的编码ID为:"<<cid;qDebug()<<"此音频流成功找到对应解码器";}}else{qDebug()<<"找到其它未知流";}}}avformat_close_input(&con);return a.exec();
}
输出结果:
avformat_open_input is success
avformat_find_stream_info is success
找到视频流
此视频流的编码ID为: 27
此视频流成功找到对应解码器
找到音频流
此音频流的编码ID为: 86018
此音频流成功找到对应解码器
API 2️⃣:avcodec_alloc_context3
- 分配解码器上下文
函数原型
AVCodecContext* avcodec_alloc_context3(const AVCodec* codec);
参数说明
参数 | 说明 |
---|---|
codec | 解码器指针(可以传 NULL ,但通常传第一步找到的解码器) |
返回值
- 成功:返回解码器上下文指针(
AVCodecContext*
) - 失败:返回
NULL
作用
为解码器分配一个工作环境(上下文)
示例
// 分配解码器上下文
AVCodecContext* codec_ctx = avcodec_alloc_context3(decoder); // decoder是通过avcodec_find_decoder找到的解码器
if (!codec_ctx) {qDebug() << "分配解码器上下文失败!";
}
🔧 辅助API:avcodec_parameters_to_context
- 复制参数
为什么需要这一步?
AVStream
中的codecpar
包含了编码参数(分辨率、帧率等)- 但
AVCodecContext
刚分配时是空的 - 需要把参数复制过去
函数原型
int avcodec_parameters_to_context(AVCodecContext* codec_ctx, const AVCodecParameters* par);
返回值
0
→ 成功< 0
→ 失败(通常是负数错误码,比如AVERROR(EINVAL)
)
示例
// 将流的参数复制到解码器上下文
int ret = avcodec_parameters_to_context(codec_ctx, video_stream->codecpar);
if (ret < 0) {qDebug() << "复制参数失败!";
}
API 3️⃣:avcodec_open2
- 打开解码器
函数原型
int avcodec_open2(AVCodecContext* avctx, const AVCodec* codec, AVDictionary** options);
参数说明
参数 | 说明 |
---|---|
avctx | 解码器上下文 |
codec | 解码器(传第一步找到的解码器) |
options | 可选参数字典(通常传 NULL ) |
返回值
0
:成功< 0
:失败
作用
初始化并打开解码器,打开后才能真正使用
示例
// 打开解码器
int ret = avcodec_open2(codec_ctx, decoder, nullptr);
if (ret < 0) {qDebug() << "打开解码器失败!";
}
🔑 关键数据结构
AVCodec(解码器)
const AVCodec* decoder;
decoder->name; // 解码器名称,如"h264"
decoder->long_name; // 完整名称,如"H.264 / AVC / MPEG-4 AVC"
decoder->type; // 类型:AVMEDIA_TYPE_VIDEO 或 AVMEDIA_TYPE_AUDIO
decoder->id; // 编码ID
AVCodecContext(解码器上下文)
AVCodecContext* codec_ctx;// 视频相关字段
codec_ctx->width; // 视频宽度
codec_ctx->height; // 视频高度
codec_ctx->pix_fmt; // 像素格式(如AV_PIX_FMT_YUV420P)
codec_ctx->framerate; // 帧率// 音频相关字段
codec_ctx->sample_rate; // 采样率(如44100)
codec_ctx->channel_layout; // 声道布局
codec_ctx->sample_fmt; // 采样格式(如AV_SAMPLE_FMT_FLTP)
💻 完整小Demo
目标:打开视频文件,为视频流和音频流找到并打开解码器,打印解码器信息
#include <QCoreApplication>
#include <QDebug>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);AVFormatContext* fmt_ctx = nullptr;AVCodecContext* video_codec_ctx = nullptr;AVCodecContext* audio_codec_ctx = nullptr;// ===== 阶段一:打开文件和分析流信息 =====QString path = "E:/BaiduNetdiskDownload/音视频资料/1、C++实战手把手教您用ffmpeg和QT开发播放器实战视频课程/1-02、音视频解封装和解码原理分析_ev.mp4";// 1. 打开文件if (avformat_open_input(&fmt_ctx, path.toUtf8().data(), nullptr, nullptr) < 0) {qDebug() << "打开文件失败!";return -1;}// 2. 分析流信息if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {qDebug() << "分析流信息失败!";avformat_close_input(&fmt_ctx);return -1;}// 3. 找到视频流和音频流int video_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);int audio_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);qDebug() << "========== 文件信息 ==========";qDebug() << "视频流索引:" << video_index;qDebug() << "音频流索引:" << audio_index;qDebug() << "";// ===== 阶段二:查找并打开解码器 =====// 处理视频流if (video_index >= 0) {AVStream* video_stream = fmt_ctx->streams[video_index];// 1. 查找解码器const AVCodec* video_decoder = avcodec_find_decoder(video_stream->codecpar->codec_id);if (!video_decoder) {qDebug() << "找不到视频解码器!";} else {qDebug() << "========== 视频解码器 ==========";qDebug() << "解码器名称:" << video_decoder->name;qDebug() << "解码器完整名称:" << video_decoder->long_name;// 2. 分配解码器上下文video_codec_ctx = avcodec_alloc_context3(video_decoder);if (!video_codec_ctx) {qDebug() << "分配视频解码器上下文失败!";} else {// 3. 复制参数到上下文if (avcodec_parameters_to_context(video_codec_ctx, video_stream->codecpar) < 0) {qDebug() << "复制视频参数失败!";} else {// 4. 打开解码器if (avcodec_open2(video_codec_ctx, video_decoder, nullptr) < 0) {qDebug() << "打开视频解码器失败!";} else {qDebug() << "视频解码器打开成功!";qDebug() << "分辨率:" << video_codec_ctx->width << "x" << video_codec_ctx->height;qDebug() << "像素格式:" << video_codec_ctx->pix_fmt;qDebug() << "帧率:" << av_q2d(video_stream->r_frame_rate) << "fps";}}}qDebug() << "";}}// 处理音频流if (audio_index >= 0) {AVStream* audio_stream = fmt_ctx->streams[audio_index];// 1. 查找解码器const AVCodec* audio_decoder = avcodec_find_decoder(audio_stream->codecpar->codec_id);if (!audio_decoder) {qDebug() << "找不到音频解码器!";} else {qDebug() << "========== 音频解码器 ==========";qDebug() << "解码器名称:" << audio_decoder->name;qDebug() << "解码器完整名称:" << audio_decoder->long_name;// 2. 分配解码器上下文audio_codec_ctx = avcodec_alloc_context3(audio_decoder);if (!audio_codec_ctx) {qDebug() << "分配音频解码器上下文失败!";} else {// 3. 复制参数到上下文if (avcodec_parameters_to_context(audio_codec_ctx, audio_stream->codecpar) < 0) {qDebug() << "复制音频参数失败!";} else {// 4. 打开解码器if (avcodec_open2(audio_codec_ctx, audio_decoder, nullptr) < 0) {qDebug() << "打开音频解码器失败!";} else {qDebug() << "音频解码器打开成功!";qDebug() << "采样率:" << audio_codec_ctx->sample_rate << "Hz";qDebug() << "采样格式:" << audio_codec_ctx->sample_fmt;}}}qDebug() << "";}}// ===== 清理资源 =====if (video_codec_ctx) {avcodec_free_context(&video_codec_ctx);}if (audio_codec_ctx) {avcodec_free_context(&audio_codec_ctx);}avformat_close_input(&fmt_ctx);qDebug() << "程序结束";return 0;
}
输出结果:
========== 文件信息 ==========
视频流索引: 0
音频流索引: 1========== 视频解码器 ==========
解码器名称: h264
解码器完整名称: H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
视频解码器打开成功!
分辨率: 1280 x 720
像素格式: 0
帧率: 60 fps========== 音频解码器 ==========
解码器名称: aac
解码器完整名称: AAC (Advanced Audio Coding)
音频解码器打开成功!
采样率: 44100 Hz
采样格式: 8程序结束
📋 总结
核心流程:
查找解码器 → 分配上下文 → 复制参数 → 打开解码器 → 开始使用
如果您觉得这篇文章对您有帮助,不妨点赞 + 收藏 + 关注,更多 FFmpeg 系列教程将持续更新 🔥!