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

🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:理解
avcodec_parameters_from_context()如何把AVCodecContext中的编解码参数复制到AVCodecParameters,明确复制范围、目标对象所有权和它在封装流程中的位置。
编码器Context包含编码配置和运行状态,封装器需要的是描述码流的参数对象。avcodec_parameters_from_context()把Context中的编解码类型、格式、尺寸、采样信息和额外数据复制到AVCodecParameters。它不会打开编码器,也不会写入容器文件。
#mermaid-svg-hngiZ1uUTzz01dzZ{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-hngiZ1uUTzz01dzZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hngiZ1uUTzz01dzZ .error-icon{fill:#552222;}#mermaid-svg-hngiZ1uUTzz01dzZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hngiZ1uUTzz01dzZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hngiZ1uUTzz01dzZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hngiZ1uUTzz01dzZ .marker.cross{stroke:#333333;}#mermaid-svg-hngiZ1uUTzz01dzZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hngiZ1uUTzz01dzZ p{margin:0;}#mermaid-svg-hngiZ1uUTzz01dzZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ .cluster-label text{fill:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ .cluster-label span{color:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ .cluster-label span p{background-color:transparent;}#mermaid-svg-hngiZ1uUTzz01dzZ .label text,#mermaid-svg-hngiZ1uUTzz01dzZ span{fill:#333;color:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ .node rect,#mermaid-svg-hngiZ1uUTzz01dzZ .node circle,#mermaid-svg-hngiZ1uUTzz01dzZ .node ellipse,#mermaid-svg-hngiZ1uUTzz01dzZ .node polygon,#mermaid-svg-hngiZ1uUTzz01dzZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hngiZ1uUTzz01dzZ .rough-node .label text,#mermaid-svg-hngiZ1uUTzz01dzZ .node .label text,#mermaid-svg-hngiZ1uUTzz01dzZ .image-shape .label,#mermaid-svg-hngiZ1uUTzz01dzZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-hngiZ1uUTzz01dzZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hngiZ1uUTzz01dzZ .rough-node .label,#mermaid-svg-hngiZ1uUTzz01dzZ .node .label,#mermaid-svg-hngiZ1uUTzz01dzZ .image-shape .label,#mermaid-svg-hngiZ1uUTzz01dzZ .icon-shape .label{text-align:center;}#mermaid-svg-hngiZ1uUTzz01dzZ .node.clickable{cursor:pointer;}#mermaid-svg-hngiZ1uUTzz01dzZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hngiZ1uUTzz01dzZ .arrowheadPath{fill:#333333;}#mermaid-svg-hngiZ1uUTzz01dzZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hngiZ1uUTzz01dzZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hngiZ1uUTzz01dzZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hngiZ1uUTzz01dzZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hngiZ1uUTzz01dzZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hngiZ1uUTzz01dzZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hngiZ1uUTzz01dzZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hngiZ1uUTzz01dzZ .cluster text{fill:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ .cluster span{color:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ 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-hngiZ1uUTzz01dzZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hngiZ1uUTzz01dzZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-hngiZ1uUTzz01dzZ .icon-shape,#mermaid-svg-hngiZ1uUTzz01dzZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hngiZ1uUTzz01dzZ .icon-shape p,#mermaid-svg-hngiZ1uUTzz01dzZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hngiZ1uUTzz01dzZ .icon-shape .label rect,#mermaid-svg-hngiZ1uUTzz01dzZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hngiZ1uUTzz01dzZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hngiZ1uUTzz01dzZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hngiZ1uUTzz01dzZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 已配置或打开的AVCodecContext
avcodec_parameters_from_context
目标AVCodecParameters
复制通用编解码字段
复制媒体类型专属字段
复制额外数据和侧数据
供封装器使用的参数对象
🌻2.应用场景和用法
2.1把编码器参数交给封装器
典型封装流程会为输出文件创建AVFormatContext和AVStream,编码器则使用独立的AVCodecContext。编码参数确定后,程序需要把参数复制到AVStream->codecpar,供封装器写文件头和处理码流。
对于编码器,常见做法是在avcodec_open2()成功后调用avcodec_parameters_from_context(),这样编码器初始化得到的参数和额外数据也有机会被复制。解码流程也可以把已打开解码器Context的参数复制到参数对象,但如果最初数据已经在AVStream->codecpar中,通常不需要绕道复制。
| 使用场景 | 源对象 | 目标对象 |
|---|---|---|
| 编码后写入容器 | 编码器AVCodecContext |
输出流的AVCodecParameters |
| 重建或检查码流描述 | 已配置的AVCodecContext |
独立分配的AVCodecParameters |
| 缓存编解码信息 | Context中的参数字段 | 调用者管理的参数对象 |
| 拷贝失败 | 任意有效Context | 根据负错误码检查内存分配或侧数据复制问题 |
2.2函数原型与复制语义
函数原型如下:
c
int
avcodec_parameters_from_context (AVCodecParameters *par,
const AVCodecContext *codec);
par是目标对象,codec是只读源Context。函数会先重置目标参数对象,再复制公共字段以及视频、音频或字幕对应的专属字段。若Context含有extradata,函数会为目标分配独立内存并复制数据;侧数据和声道布局也通过各自的复制逻辑处理。
成功返回0,失败返回负错误码。该函数不会修改源Context。目标对象中原有的动态字段会被释放并替换,因此调用者应传入已正确初始化或由avcodec_parameters_alloc()分配的对象。对象最终由avcodec_parameters_free()释放。
🌻3.调用流程剖析
3.1重置目标并复制通用字段
函数首先重置目标AVCodecParameters,清理它先前持有的参数和动态数据,然后复制媒体类型、Codec ID、Codec Tag、码率、Profile和Level等通用字段。
这一步决定目标对象以源Context当前的编解码信息为准。它属于一次覆盖式同步,不是把新字段追加到目标对象,也不是两个对象之间建立共享引用。
3.2按媒体类型复制字段和动态数据
视频分支复制像素格式、宽高、场序、色彩信息、Sample Aspect Ratio和帧率等字段。音频分支复制采样格式、声道布局、采样率、Block Align、Frame Size和Padding信息。字幕分支复制宽高。
完成类型字段后,函数会为extradata分配新内存并复制内容,同时复制编码侧数据和声道布局。由于目标持有自己的副本,复制后释放Context不会直接释放参数对象中的这些动态数据。
#mermaid-svg-wqyp7sa4DnUsTaPW{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-wqyp7sa4DnUsTaPW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wqyp7sa4DnUsTaPW .error-icon{fill:#552222;}#mermaid-svg-wqyp7sa4DnUsTaPW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wqyp7sa4DnUsTaPW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wqyp7sa4DnUsTaPW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wqyp7sa4DnUsTaPW .marker.cross{stroke:#333333;}#mermaid-svg-wqyp7sa4DnUsTaPW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wqyp7sa4DnUsTaPW p{margin:0;}#mermaid-svg-wqyp7sa4DnUsTaPW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW .cluster-label text{fill:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW .cluster-label span{color:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW .cluster-label span p{background-color:transparent;}#mermaid-svg-wqyp7sa4DnUsTaPW .label text,#mermaid-svg-wqyp7sa4DnUsTaPW span{fill:#333;color:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW .node rect,#mermaid-svg-wqyp7sa4DnUsTaPW .node circle,#mermaid-svg-wqyp7sa4DnUsTaPW .node ellipse,#mermaid-svg-wqyp7sa4DnUsTaPW .node polygon,#mermaid-svg-wqyp7sa4DnUsTaPW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wqyp7sa4DnUsTaPW .rough-node .label text,#mermaid-svg-wqyp7sa4DnUsTaPW .node .label text,#mermaid-svg-wqyp7sa4DnUsTaPW .image-shape .label,#mermaid-svg-wqyp7sa4DnUsTaPW .icon-shape .label{text-anchor:middle;}#mermaid-svg-wqyp7sa4DnUsTaPW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wqyp7sa4DnUsTaPW .rough-node .label,#mermaid-svg-wqyp7sa4DnUsTaPW .node .label,#mermaid-svg-wqyp7sa4DnUsTaPW .image-shape .label,#mermaid-svg-wqyp7sa4DnUsTaPW .icon-shape .label{text-align:center;}#mermaid-svg-wqyp7sa4DnUsTaPW .node.clickable{cursor:pointer;}#mermaid-svg-wqyp7sa4DnUsTaPW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wqyp7sa4DnUsTaPW .arrowheadPath{fill:#333333;}#mermaid-svg-wqyp7sa4DnUsTaPW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wqyp7sa4DnUsTaPW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wqyp7sa4DnUsTaPW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wqyp7sa4DnUsTaPW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wqyp7sa4DnUsTaPW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wqyp7sa4DnUsTaPW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wqyp7sa4DnUsTaPW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wqyp7sa4DnUsTaPW .cluster text{fill:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW .cluster span{color:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW 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-wqyp7sa4DnUsTaPW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wqyp7sa4DnUsTaPW rect.text{fill:none;stroke-width:0;}#mermaid-svg-wqyp7sa4DnUsTaPW .icon-shape,#mermaid-svg-wqyp7sa4DnUsTaPW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wqyp7sa4DnUsTaPW .icon-shape p,#mermaid-svg-wqyp7sa4DnUsTaPW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wqyp7sa4DnUsTaPW .icon-shape .label rect,#mermaid-svg-wqyp7sa4DnUsTaPW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wqyp7sa4DnUsTaPW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wqyp7sa4DnUsTaPW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wqyp7sa4DnUsTaPW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 视频
音频
字幕
源AVCodecContext
重置目标参数
复制通用字段
媒体类型
复制视频格式和尺寸
复制采样格式和声道布局
复制字幕尺寸
复制额外数据和侧数据
返回结果码
封装器仍需由调用者创建和配置。参数复制完成后,常见后续步骤是调用avformat_write_header()写容器头;avcodec_parameters_from_context()本身既不创建AVStream,也不写文件。
🌻4.实战案例
4.1配置MPEG-4编码器
示例使用MPEG-4视频编码器,配置320×240、YUV420P、25帧每秒和目标码率。编码器打开成功后,再准备一个独立的AVCodecParameters作为封装参数目标。
4.2复制编码参数并检查结果
调用avcodec_parameters_from_context()后,检查返回值以及目标对象中的Codec ID、宽高和像素格式。目标参数对象是副本,可以在释放Context后继续使用,直到调用avcodec_parameters_free()。
4.3完整代码
c
#include <libavcodec/avcodec.h>
#include <libavutil/pixfmt.h>
int
main (void)
{
const AVCodec *encoder;
AVCodecContext *context = NULL;
AVCodecParameters *parameters = NULL;
int ret;
/* 查找MPEG-4视频编码器 */
encoder = avcodec_find_encoder (AV_CODEC_ID_MPEG4);
if (encoder == NULL)
return 1;
/* 分配编码器Context并设置编码参数 */
context = avcodec_alloc_context3 (encoder);
if (context == NULL)
return 1;
context->width = 320;
context->height = 240;
context->pix_fmt = AV_PIX_FMT_YUV420P;
context->time_base = (AVRational) { 1, 25 };
context->framerate = (AVRational) { 25, 1 };
context->bit_rate = 400000;
context->gop_size = 12;
context->max_b_frames = 2;
/* 打开编码器,使最终编码参数就绪 */
ret = avcodec_open2 (context, encoder, NULL);
if (ret < 0)
goto done;
/* 创建独立的封装参数对象 */
parameters = avcodec_parameters_alloc ();
if (parameters == NULL) {
ret = AVERROR (ENOMEM);
goto done;
}
/* 把Context中的编码参数复制到参数对象 */
ret = avcodec_parameters_from_context (parameters, context);
if (ret < 0)
goto done;
/* 确认复制后的基本视频参数 */
if (parameters->codec_id != AV_CODEC_ID_MPEG4 ||
parameters->width != 320 ||
parameters->height != 240 ||
parameters->format != AV_PIX_FMT_YUV420P) {
ret = AVERROR_INVALIDDATA;
goto done;
}
ret = 0;
done:
/* 两个对象独立释放,参数副本不依赖Context生命周期 */
avcodec_parameters_free (¶meters);
avcodec_free_context (&context);
return ret < 0 ? 1 : 0;
}
🌻5.总结
avcodec_parameters_from_context()把AVCodecContext中的编解码描述复制到独立的AVCodecParameters对象,供封装器或其他组件使用。