🎬 FFmpeg 核心 API 系列:avcodec_find_decoder / avcodec_alloc_context3 / avcodec_open2 全解析
📅 更新时间:2025年10月2日
🏷️ 标签:FFmpeg | 多媒体处理 | 音视频编程 | C/C++ | 流媒体
文章目录
- [📖 前言](#📖 前言)
- [🎯 三个核心API详解](#🎯 三个核心API详解)
-
- [API 1️⃣:`avcodec_find_decoder` - 查找解码器](#API 1️⃣:
avcodec_find_decoder
- 查找解码器) - [API 2️⃣:`avcodec_alloc_context3` - 分配解码器上下文](#API 2️⃣:
avcodec_alloc_context3
- 分配解码器上下文) - [🔧 辅助API:`avcodec_parameters_to_context` - 复制参数](#🔧 辅助API:
avcodec_parameters_to_context
- 复制参数) - [API 3️⃣:`avcodec_open2` - 打开解码器](#API 3️⃣:
avcodec_open2
- 打开解码器)
- [API 1️⃣:`avcodec_find_decoder` - 查找解码器](#API 1️⃣:
- [🔑 关键数据结构](#🔑 关键数据结构)
- [💻 完整小Demo](#💻 完整小Demo)
- [📋 总结](#📋 总结)
📖 前言
回顾上一篇文章,我们已经能:
- 打开文件 → 得到
AVFormatContext
- 分析流信息 → 得到
AVStream
(包含编码参数codecpar
)
但是!这些信息只是"参数",并不能解码。就像你知道一个文件是H.264编码的,但还需要一个"H.264解码器"才能把压缩数据变成图像。
解码器的作用:将压缩的数据包(AVPacket
)→ 解码成原始的帧(AVFrame
)
🎯 三个核心API详解
API 1️⃣:avcodec_find_decoder
- 查找解码器
函数原型
cpp
const AVCodec *avcodec_find_decoder(enum AVCodecID id);
参数说明
参数 | 说明 |
---|---|
id |
编码ID(从 stream->codecpar->codec_id 获取) |
返回值
- 成功:返回解码器指针(
AVCodec*
) - 失败:返回
NULL
作用
根据编码ID找到对应的解码器
用法一:通过 codec_id
查找对应的解码器
cpp
AVCodecID cid=stream->codecpar->codec_id;
//根据编码ID查找对应解码器
const AVCodec * decoder=avcodec_find_decoder(cid);
用法二:指定解码器名称查找
注意事项:要判断一下这个解码器是否能解码对应的流
cpp
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
cpp
#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
- 分配解码器上下文
函数原型
cpp
AVCodecContext* avcodec_alloc_context3(const AVCodec* codec);
参数说明
参数 | 说明 |
---|---|
codec |
解码器指针(可以传 NULL ,但通常传第一步找到的解码器) |
返回值
- 成功:返回解码器上下文指针(
AVCodecContext*
) - 失败:返回
NULL
作用
为解码器分配一个工作环境(上下文)
示例
cpp
// 分配解码器上下文
AVCodecContext* codec_ctx = avcodec_alloc_context3(decoder); // decoder是通过avcodec_find_decoder找到的解码器
if (!codec_ctx) {
qDebug() << "分配解码器上下文失败!";
}
🔧 辅助API:avcodec_parameters_to_context
- 复制参数
为什么需要这一步?
AVStream
中的codecpar
包含了编码参数(分辨率、帧率等)- 但
AVCodecContext
刚分配时是空的 - 需要把参数复制过去
函数原型
cpp
int avcodec_parameters_to_context(AVCodecContext* codec_ctx,
const AVCodecParameters* par);
返回值
0
→ 成功< 0
→ 失败(通常是负数错误码,比如AVERROR(EINVAL)
)
示例
cpp
// 将流的参数复制到解码器上下文
int ret = avcodec_parameters_to_context(codec_ctx, video_stream->codecpar);
if (ret < 0) {
qDebug() << "复制参数失败!";
}
API 3️⃣:avcodec_open2
- 打开解码器
函数原型
cpp
int avcodec_open2(AVCodecContext* avctx,
const AVCodec* codec,
AVDictionary** options);
参数说明
参数 | 说明 |
---|---|
avctx |
解码器上下文 |
codec |
解码器(传第一步找到的解码器) |
options |
可选参数字典(通常传 NULL ) |
返回值
0
:成功< 0
:失败
作用
初始化并打开解码器,打开后才能真正使用
示例
cpp
// 打开解码器
int ret = avcodec_open2(codec_ctx, decoder, nullptr);
if (ret < 0) {
qDebug() << "打开解码器失败!";
}
🔑 关键数据结构
AVCodec(解码器)
cpp
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(解码器上下文)
cpp
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
目标:打开视频文件,为视频流和音频流找到并打开解码器,打印解码器信息
cpp
#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 系列教程将持续更新 🔥!