简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:理解
avcodec_find_decoder()如何根据AVCodecID在FFmpeg已注册的编解码器中查找解码器,并通过查找H.264解码器的示例确认返回对象的用途。
解复用器解析出媒体流后,AVCodecParameters会提供该流对应的编码ID。应用需要找到匹配的解码器,才能继续创建解码上下文并打开解码器。avcodec_find_decoder()负责按编码ID查找解码器对象;它不创建AVCodecContext,也不启动解码。
#mermaid-svg-sWd8WCaNOmbgthGK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sWd8WCaNOmbgthGK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sWd8WCaNOmbgthGK .error-icon{fill:#552222;}#mermaid-svg-sWd8WCaNOmbgthGK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sWd8WCaNOmbgthGK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sWd8WCaNOmbgthGK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sWd8WCaNOmbgthGK .marker.cross{stroke:#333333;}#mermaid-svg-sWd8WCaNOmbgthGK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sWd8WCaNOmbgthGK p{margin:0;}#mermaid-svg-sWd8WCaNOmbgthGK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sWd8WCaNOmbgthGK .cluster-label text{fill:#333;}#mermaid-svg-sWd8WCaNOmbgthGK .cluster-label span{color:#333;}#mermaid-svg-sWd8WCaNOmbgthGK .cluster-label span p{background-color:transparent;}#mermaid-svg-sWd8WCaNOmbgthGK .label text,#mermaid-svg-sWd8WCaNOmbgthGK span{fill:#333;color:#333;}#mermaid-svg-sWd8WCaNOmbgthGK .node rect,#mermaid-svg-sWd8WCaNOmbgthGK .node circle,#mermaid-svg-sWd8WCaNOmbgthGK .node ellipse,#mermaid-svg-sWd8WCaNOmbgthGK .node polygon,#mermaid-svg-sWd8WCaNOmbgthGK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sWd8WCaNOmbgthGK .rough-node .label text,#mermaid-svg-sWd8WCaNOmbgthGK .node .label text,#mermaid-svg-sWd8WCaNOmbgthGK .image-shape .label,#mermaid-svg-sWd8WCaNOmbgthGK .icon-shape .label{text-anchor:middle;}#mermaid-svg-sWd8WCaNOmbgthGK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sWd8WCaNOmbgthGK .rough-node .label,#mermaid-svg-sWd8WCaNOmbgthGK .node .label,#mermaid-svg-sWd8WCaNOmbgthGK .image-shape .label,#mermaid-svg-sWd8WCaNOmbgthGK .icon-shape .label{text-align:center;}#mermaid-svg-sWd8WCaNOmbgthGK .node.clickable{cursor:pointer;}#mermaid-svg-sWd8WCaNOmbgthGK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sWd8WCaNOmbgthGK .arrowheadPath{fill:#333333;}#mermaid-svg-sWd8WCaNOmbgthGK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sWd8WCaNOmbgthGK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sWd8WCaNOmbgthGK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sWd8WCaNOmbgthGK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sWd8WCaNOmbgthGK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sWd8WCaNOmbgthGK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sWd8WCaNOmbgthGK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sWd8WCaNOmbgthGK .cluster text{fill:#333;}#mermaid-svg-sWd8WCaNOmbgthGK .cluster span{color:#333;}#mermaid-svg-sWd8WCaNOmbgthGK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sWd8WCaNOmbgthGK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sWd8WCaNOmbgthGK rect.text{fill:none;stroke-width:0;}#mermaid-svg-sWd8WCaNOmbgthGK .icon-shape,#mermaid-svg-sWd8WCaNOmbgthGK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sWd8WCaNOmbgthGK .icon-shape p,#mermaid-svg-sWd8WCaNOmbgthGK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sWd8WCaNOmbgthGK .icon-shape .label rect,#mermaid-svg-sWd8WCaNOmbgthGK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sWd8WCaNOmbgthGK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sWd8WCaNOmbgthGK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sWd8WCaNOmbgthGK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 媒体流的AVCodecID
avcodec_find_decoder
遍历已注册编解码器
筛选解码器并匹配ID
返回AVCodec或NULL
后续创建并打开解码上下文
🌻2.应用场景和用法
2.1根据流编码ID选择解码器
这个接口常用于解码流程初始化。调用者从AVStream的codecpar->codec_id取得编码ID,再查找对应解码器。
| 场景 | 输入 | 用途 |
|---|---|---|
| 解复用后创建解码器 | AVCodecParameters.codec_id |
为每一路编码流选择解码器 |
| 已知编码格式 | AV_CODEC_ID_H264等枚举 |
查找对应的默认解码器 |
| 检查构建能力 | 目标编码ID | 判断当前FFmpeg构建中是否注册了匹配解码器 |
| 区分编解码方向 | 编码ID | 只返回满足解码器判定的对象 |
如果返回NULL,表示当前可见的已注册解码器中没有匹配对象。常见原因包括FFmpeg构建时未启用相关解码器,或者调用者传入了错误的编码ID。
2.2函数原型与返回语义
c
const AVCodec *
avcodec_find_decoder (enum AVCodecID id);
| 参数或返回值 | 含义 |
|---|---|
id |
要查找的编码ID,通常来自流参数 |
| 返回值 | 匹配的解码器描述对象;未找到时返回NULL |
返回的AVCodec是FFmpeg注册的编解码器描述对象,不是每路流独有的运行状态对象。调用者可以用它创建上下文:
c
const AVCodec *decoder;
AVCodecContext *decoder_context;
decoder = avcodec_find_decoder (codec_id);
if (decoder == NULL) {
/* 当前构建中没有匹配的解码器 */
}
decoder_context = avcodec_alloc_context3 (decoder);
avcodec_find_decoder()只负责查找。随后由avcodec_alloc_context3()创建解码上下文,将流参数复制到上下文后,再调用avcodec_open2()打开解码器。真正的数据解码由avcodec_send_packet()和avcodec_receive_frame()完成。
🌻3.调用流程剖析
3.1按方向和ID遍历注册表
FFmpeg源码中,avcodec_find_decoder()把查找工作交给内部find_codec(),并传入av_codec_is_decoder作为方向筛选条件。
find_codec()先处理可能需要映射的旧编码ID,再通过av_codec_iterate()遍历当前已注册的编解码器。每个候选对象先经过解码器方向判断,再比较其id。匹配后返回对应的AVCodec。
#mermaid-svg-nF9oLsHuXpuLKEKc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nF9oLsHuXpuLKEKc .error-icon{fill:#552222;}#mermaid-svg-nF9oLsHuXpuLKEKc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nF9oLsHuXpuLKEKc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nF9oLsHuXpuLKEKc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nF9oLsHuXpuLKEKc .marker.cross{stroke:#333333;}#mermaid-svg-nF9oLsHuXpuLKEKc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nF9oLsHuXpuLKEKc p{margin:0;}#mermaid-svg-nF9oLsHuXpuLKEKc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc .cluster-label text{fill:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc .cluster-label span{color:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc .cluster-label span p{background-color:transparent;}#mermaid-svg-nF9oLsHuXpuLKEKc .label text,#mermaid-svg-nF9oLsHuXpuLKEKc span{fill:#333;color:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc .node rect,#mermaid-svg-nF9oLsHuXpuLKEKc .node circle,#mermaid-svg-nF9oLsHuXpuLKEKc .node ellipse,#mermaid-svg-nF9oLsHuXpuLKEKc .node polygon,#mermaid-svg-nF9oLsHuXpuLKEKc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nF9oLsHuXpuLKEKc .rough-node .label text,#mermaid-svg-nF9oLsHuXpuLKEKc .node .label text,#mermaid-svg-nF9oLsHuXpuLKEKc .image-shape .label,#mermaid-svg-nF9oLsHuXpuLKEKc .icon-shape .label{text-anchor:middle;}#mermaid-svg-nF9oLsHuXpuLKEKc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nF9oLsHuXpuLKEKc .rough-node .label,#mermaid-svg-nF9oLsHuXpuLKEKc .node .label,#mermaid-svg-nF9oLsHuXpuLKEKc .image-shape .label,#mermaid-svg-nF9oLsHuXpuLKEKc .icon-shape .label{text-align:center;}#mermaid-svg-nF9oLsHuXpuLKEKc .node.clickable{cursor:pointer;}#mermaid-svg-nF9oLsHuXpuLKEKc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nF9oLsHuXpuLKEKc .arrowheadPath{fill:#333333;}#mermaid-svg-nF9oLsHuXpuLKEKc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nF9oLsHuXpuLKEKc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nF9oLsHuXpuLKEKc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nF9oLsHuXpuLKEKc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nF9oLsHuXpuLKEKc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nF9oLsHuXpuLKEKc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nF9oLsHuXpuLKEKc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nF9oLsHuXpuLKEKc .cluster text{fill:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc .cluster span{color:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nF9oLsHuXpuLKEKc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nF9oLsHuXpuLKEKc rect.text{fill:none;stroke-width:0;}#mermaid-svg-nF9oLsHuXpuLKEKc .icon-shape,#mermaid-svg-nF9oLsHuXpuLKEKc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nF9oLsHuXpuLKEKc .icon-shape p,#mermaid-svg-nF9oLsHuXpuLKEKc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nF9oLsHuXpuLKEKc .icon-shape .label rect,#mermaid-svg-nF9oLsHuXpuLKEKc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nF9oLsHuXpuLKEKc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nF9oLsHuXpuLKEKc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nF9oLsHuXpuLKEKc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
avcodec_find_decoder收到ID
内部查找函数
映射兼容的旧ID
遍历注册的AVCodec
是解码器且ID匹配
返回AVCodec
继续遍历
无匹配项时返回NULL
如果多个候选解码器关联同一ID,普通候选会被优先返回;实验性候选会作为备用结果保留,只有遍历结束仍没有普通匹配项时才返回实验性解码器。
3.2从查找结果进入解码器初始化
查找到AVCodec后,应用还需要创建与当前流关联的AVCodecContext。此时应使用流的AVCodecParameters填充上下文,再打开解码器。
查找成功只说明注册表中存在可用候选,不代表媒体数据一定能被成功解码。参数复制、解码器打开、输入包格式和后续数据都可能产生独立错误。
🌻4.实战案例
4.1准备编码ID
示例直接使用AV_CODEC_ID_H264演示按编码类型查找。处理实际媒体时,应优先从选中的AVStream读取codecpar->codec_id,避免硬编码编码类型。
4.2查找解码器并创建上下文
找到解码器后,使用返回的AVCodec创建AVCodecContext。这个案例展示查找和上下文创建的边界,不打开解码器或提交压缩数据。
4.3完整代码
c
#include <libavcodec/avcodec.h>
#include <stdio.h>
int
main (void)
{
const AVCodec *decoder;
AVCodecContext *decoder_context;
/* 按H.264编码ID查找已注册的解码器 */
decoder = avcodec_find_decoder (AV_CODEC_ID_H264);
if (decoder == NULL) {
fprintf (stderr, "当前FFmpeg构建中没有匹配的H.264解码器\n");
return 1;
}
/* AVCodec描述解码器,AVCodecContext保存本次解码状态 */
decoder_context = avcodec_alloc_context3 (decoder);
if (decoder_context == NULL) {
fprintf (stderr, "无法创建解码上下文\n");
return 1;
}
/* 在真实解码流程中,随后还需填充流参数并调用avcodec_open2 */
avcodec_free_context (&decoder_context);
return 0;
}
🌻5.总结
avcodec_find_decoder()根据AVCodecID遍历已注册编解码器并返回匹配的解码器描述对象,不创建上下文,也不执行解码。