Linux FFmpeg深度解析之avcodec_find_decoder调用流程与实战(十二)

简介: 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遍历已注册编解码器并返回匹配的解码器描述对象,不创建上下文,也不执行解码。

相关推荐
1-1=01 小时前
.NET Core 文件系统
.net·.netcore·1024程序员节
Hommy881 小时前
【直播切片工作台】第23章:媒体探测与转码:ffprobe 与 ffmpeg 封装
人工智能·ffmpeg·直播切片
小白的码BUG之路1 小时前
K8s -- 启动一主二从节点
linux·运维·服务器
Escalating_xu1 小时前
【Linux 网络编程】IP 分片与重组:3000 字节的数据报,怎样拆开又拼回来?
linux·网络·tcp/ip
FPGA小迷弟1 小时前
FPGA 与卫星:从星载硬件到在轨智能的完整技术解析
fpga开发·架构·fpga·1024程序员节·无线电
小相会自律1 小时前
全平台远程控制软件推荐2026:Win/Mac/Linux/手机/鸿蒙互通哪款好
linux·macos·智能手机·todesk·teamviewer·向日葵
码匠许师傅1 小时前
【C++三方组件】FFmpeg:音视频处理的瑞士军刀
c++·ffmpeg·音视频
yyzc111111 小时前
合规稳定资金结算收款系统助力门店发展
大数据·运维·人工智能
AI_Lab011 小时前
服务器管理工具要在服务器上装 Agent,安全吗?
服务器·安全·github