avcodec_parameters_copy详解

函数原型

复制代码
int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src);

功能描述

avcodec_parameters_copy是 FFmpeg 中用于深度复制 一个 AVCodecParameters结构体到另一个的函数。它会:

  1. 复制所有基本字段

  2. 深度复制extradata(编解码器私有数据)

  3. 确保目标结构体完全独立于源结构体

参数详解

复制代码
/**
 * 复制编解码器参数
 * 
 * @param dst 目标AVCodecParameters,必须是已分配的结构体
 * @param src 源AVCodecParameters
 * @return 成功返回0,失败返回负的错误码
 */
int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src);

函数行为

复制代码
// 伪代码演示函数行为
int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src) {
    if (!dst || !src)
        return AVERROR(EINVAL);
    
    // 1. 保存目标原来的extradata指针
    uint8_t *old_extradata = dst->extradata;
    int old_extradata_size = dst->extradata_size;
    
    // 2. 重置目标extradata,避免重复释放
    dst->extradata = NULL;
    dst->extradata_size = 0;
    
    // 3. 复制所有基本字段
    memcpy(dst, src, sizeof(*dst));
    
    // 4. 深度复制extradata
    if (src->extradata && src->extradata_size > 0) {
        dst->extradata = av_malloc(src->extradata_size);
        if (!dst->extradata) {
            // 恢复原来的extradata
            dst->extradata = old_extradata;
            dst->extradata_size = old_extradata_size;
            return AVERROR(ENOMEM);
        }
        memcpy(dst->extradata, src->extradata, src->extradata_size);
        dst->extradata_size = src->extradata_size;
    }
    
    // 5. 释放原来的extradata
    av_freep(&old_extradata);
    
    return 0;
}

使用示例

基本复制

复制代码
#include <libavcodec/avcodec.h>

int main() {
    // 源参数(例如从流中获取)
    AVCodecParameters *src_par = /* 从某个地方获取 */;
    
    // 分配目标参数
    AVCodecParameters *dst_par = avcodec_parameters_alloc();
    if (!dst_par) {
        return AVERROR(ENOMEM);
    }
    
    // 复制参数
    int ret = avcodec_parameters_copy(dst_par, src_par);
    if (ret < 0) {
        fprintf(stderr, "复制参数失败: %s\n", av_err2str(ret));
        avcodec_parameters_free(&dst_par);
        return ret;
    }
    
    // 使用复制的参数...
    
    // 清理
    avcodec_parameters_free(&dst_par);
    
    return 0;
}

完整示例:复制视频流参数

复制代码
int copy_video_stream_params(AVFormatContext *in_fmt_ctx, 
                             AVFormatContext *out_fmt_ctx, 
                             int stream_index) {
    // 获取输入流参数
    AVCodecParameters *in_par = in_fmt_ctx->streams[stream_index]->codecpar;
    
    // 创建输出流
    AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
    if (!out_stream) {
        return AVERROR(ENOMEM);
    }
    
    // 复制参数到输出流
    int ret = avcodec_parameters_copy(out_stream->codecpar, in_par);
    if (ret < 0) {
        fprintf(stderr, "无法复制编解码器参数: %s\n", av_err2str(ret));
        return ret;
    }
    
    // 可能需要调整某些参数
    out_stream->codecpar->codec_tag = 0;  // 让FFmpeg自动选择codec_tag
    
    return 0;
}

批量复制参数

复制代码
int copy_all_streams_params(AVFormatContext *in_ctx, AVFormatContext *out_ctx) {
    for (int i = 0; i < in_ctx->nb_streams; i++) {
        // 创建输出流
        AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
        if (!out_stream) {
            return AVERROR(ENOMEM);
        }
        
        // 复制编解码器参数
        int ret = avcodec_parameters_copy(out_stream->codecpar, 
                                          in_ctx->streams[i]->codecpar);
        if (ret < 0) {
            fprintf(stderr, "复制流 %d 的参数失败: %s\n", i, av_err2str(ret));
            return ret;
        }
        
        // 复制其他流信息
        out_stream->time_base = in_ctx->streams[i]->time_base;
    }
    
    return 0;
}

内存管理细节

深度复制 extradata

复制代码
// 演示avcodec_parameters_copy如何处理extradata
void demonstrate_extradata_copy() {
    // 创建源参数
    AVCodecParameters *src = avcodec_parameters_alloc();
    src->extradata_size = 100;
    src->extradata = av_malloc(src->extradata_size);
    memset(src->extradata, 0xAA, src->extradata_size);
    
    // 创建目标参数
    AVCodecParameters *dst = avcodec_parameters_alloc();
    
    // 复制
    avcodec_parameters_copy(dst, src);
    
    // 验证
    printf("源extradata地址: %p\n", src->extradata);
    printf("目标extradata地址: %p\n", dst->extradata);
    printf("是否相同地址: %s\n", src->extradata == dst->extradata ? "是" : "否");
    
    // 修改源extradata
    src->extradata[0] = 0xBB;
    printf("目标受影响: %s\n", dst->extradata[0] == 0xBB ? "是" : "否");
    // 输出:否,因为复制是深复制
    
    // 清理
    avcodec_parameters_free(&src);
    avcodec_parameters_free(&dst);
}

重复复制时的内存管理

复制代码
int safe_copy_with_reuse(AVCodecParameters *dst, AVCodecParameters *src1, 
                         AVCodecParameters *src2) {
    // 第一次复制
    int ret = avcodec_parameters_copy(dst, src1);
    if (ret < 0) {
        return ret;
    }
    
    // 第二次复制到同一个目标
    ret = avcodec_parameters_copy(dst, src2);
    if (ret < 0) {
        return ret;
    }
    
    // 注意:第二次复制会自动释放第一次复制的extradata
    // 然后分配新的extradata
    
    return 0;
}

实际应用场景

场景1:流映射

复制代码
typedef struct {
    int src_stream_index;
    int dst_stream_index;
} StreamMapping;

int map_and_copy_streams(AVFormatContext *in_ctx, AVFormatContext *out_ctx,
                         StreamMapping *mappings, int nb_mappings) {
    for (int i = 0; i < nb_mappings; i++) {
        int src_idx = mappings[i].src_stream_index;
        int dst_idx = mappings[i].dst_stream_index;
        
        if (src_idx >= in_ctx->nb_streams) {
            return AVERROR(EINVAL);
        }
        
        // 创建输出流
        AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
        if (!out_stream) {
            return AVERROR(ENOMEM);
        }
        
        // 复制参数
        int ret = avcodec_parameters_copy(out_stream->codecpar,
                                         in_ctx->streams[src_idx]->codecpar);
        if (ret < 0) {
            fprintf(stderr, "无法复制流 %d 的参数\n", src_idx);
            return ret;
        }
        
        // 记录映射关系
        mappings[i].dst_stream_index = out_ctx->nb_streams - 1;
    }
    
    return 0;
}

场景2:参数修改

复制代码
AVCodecParameters* create_modified_params(const AVCodecParameters *src,
                                          int64_t new_bitrate,
                                          int new_width, int new_height) {
    // 分配新参数
    AVCodecParameters *dst = avcodec_parameters_alloc();
    if (!dst) {
        return NULL;
    }
    
    // 复制原始参数
    int ret = avcodec_parameters_copy(dst, src);
    if (ret < 0) {
        avcodec_parameters_free(&dst);
        return NULL;
    }
    
    // 修改参数
    if (new_bitrate > 0) {
        dst->bit_rate = new_bitrate;
    }
    
    if (new_width > 0 && new_height > 0) {
        dst->width = new_width;
        dst->height = new_height;
    }
    
    return dst;
}

错误处理

检查返回值

复制代码
int process_codec_params(const AVCodecParameters *src) {
    // 分配目标参数
    AVCodecParameters *dst = avcodec_parameters_alloc();
    if (!dst) {
        fprintf(stderr, "内存不足\n");
        return AVERROR(ENOMEM);
    }
    
    // 复制参数
    int ret = avcodec_parameters_copy(dst, src);
    if (ret < 0) {
        // 具体错误处理
        switch (ret) {
            case AVERROR(ENOMEM):
                fprintf(stderr, "复制extradata时内存不足\n");
                break;
            case AVERROR(EINVAL):
                fprintf(stderr, "无效参数\n");
                break;
            default:
                fprintf(stderr, "未知错误: %s\n", av_err2str(ret));
                break;
        }
        
        avcodec_parameters_free(&dst);
        return ret;
    }
    
    // 使用复制的参数...
    
    avcodec_parameters_free(&dst);
    return 0;
}

空指针检查

复制代码
int safe_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src) {
    if (!dst || !src) {
        fprintf(stderr, "参数指针为空\n");
        return AVERROR(EINVAL);
    }
    
    // 确保dst是已分配的结构体
    if (dst->codec_type == 0 && dst->codec_id == 0) {
        // 看起来是未初始化的结构体,但可能只是巧合
        // 最好在调用前确保dst是通过avcodec_parameters_alloc分配的
    }
    
    return avcodec_parameters_copy(dst, src);
}

与相关函数的对比

函数 深度复制 extradata 需要目标已分配 是否创建新结构体 使用场景
avcodec_parameters_copy() 复制参数到已分配的结构体
avcodec_parameters_dup() 创建参数的副本(FFmpeg 4.0+)
memcpy() 浅复制,危险!不推荐
手动复制字段 可选 复杂,容易出错

注意事项

1. 必须预先分配目标

复制代码
// 错误:目标未分配
AVCodecParameters *dst;  // 未初始化
avcodec_parameters_copy(dst, src);  // 崩溃!

// 正确
AVCodecParameters *dst = avcodec_parameters_alloc();
avcodec_parameters_copy(dst, src);

2. 避免自复制

复制代码
// 危险:自复制
avcodec_parameters_copy(par, par);  // 可能导致内存问题

// 安全:检查是否是同一个指针
if (dst != src) {
    avcodec_parameters_copy(dst, src);
}

3. 处理嵌套调用

复制代码
int complex_operation(AVCodecParameters *dst, AVCodecParameters *src) {
    // 先保存原始状态
    AVCodecParameters *temp = avcodec_parameters_alloc();
    if (!temp) {
        return AVERROR(ENOMEM);
    }
    
    // 复制到临时变量
    int ret = avcodec_parameters_copy(temp, dst);
    if (ret < 0) {
        avcodec_parameters_free(&temp);
        return ret;
    }
    
    // 修改目标
    ret = avcodec_parameters_copy(dst, src);
    if (ret < 0) {
        // 恢复原始状态
        avcodec_parameters_copy(dst, temp);
        avcodec_parameters_free(&temp);
        return ret;
    }
    
    avcodec_parameters_free(&temp);
    return 0;
}

4. 线程安全考虑

复制代码
// 注意:avcodec_parameters_copy本身是线程安全的
// 但需要确保在复制过程中源参数不被修改

// 不安全:多线程同时修改和复制
// 线程1: 修改src->extradata
// 线程2: avcodec_parameters_copy(dst, src)  // 可能导致不一致

// 安全:使用锁保护
pthread_mutex_lock(&mutex);
int ret = avcodec_parameters_copy(dst, src);
pthread_mutex_unlock(&mutex);

性能优化

避免不必要的复制

复制代码
// 检查是否需要复制
int copy_if_needed(AVCodecParameters *dst, const AVCodecParameters *src) {
    // 快速检查:如果已经相同,跳过复制
    if (are_parameters_equal(dst, src)) {
        return 0;  // 无需复制
    }
    
    return avcodec_parameters_copy(dst, src);
}

int are_parameters_equal(const AVCodecParameters *a, const AVCodecParameters *b) {
    // 比较关键字段
    if (a->codec_id != b->codec_id ||
        a->width != b->width ||
        a->height != b->height ||
        a->format != b->format) {
        return 0;
    }
    
    // 比较extradata
    if (a->extradata_size != b->extradata_size) {
        return 0;
    }
    
    if (memcmp(a->extradata, b->extradata, a->extradata_size) != 0) {
        return 0;
    }
    
    return 1;
}

总结

avcodec_parameters_copy是 FFmpeg 中关键且安全的参数复制函数:

  1. 完全深度复制:包括 extradata 的独立内存分配

  2. 内存安全:自动管理目标结构体的原有内存

  3. 线程安全:函数本身是线程安全的

  4. 错误处理:提供详细的错误码

使用时需注意:

  • 目标参数必须已分配

  • 检查返回值处理错误

  • 避免自复制

  • 在多线程环境中注意源参数的同步

正确使用此函数可以避免常见的指针和内存管理错误,是 FFmpeg 编程中的基础但重要的操作。

相关推荐
石去皿2 小时前
AI命名实体识别常见面试篇
人工智能·面试·职场和发展
沪漂阿龙2 小时前
从Chatbot到Agent:AI如何从“能说会道”进化为“能干实事”
人工智能
头发那是一根不剩了2 小时前
Linux 常用服务器命令
linux·运维·服务器
sanshanjianke2 小时前
AI辅助网文创作理论研究笔记(一):叙事模型的构建
人工智能·语言模型·ai写作
TImCheng06092 小时前
能力模型构建:AI产品经理所需技术、伦理与商业知识的比例与深度
人工智能
锅包一切2 小时前
二、几种安装类型
linux·运维·后端·操作系统
vortex52 小时前
详解Linux磁盘相关命令:从基础查看到底层运维
linux·运维
敲代码的哈吉蜂2 小时前
Haproxy
linux·运维·服务器
甲枫叶2 小时前
【claude产品经理系列11】实现后端接口——数据在背后如何流动
java·数据库·人工智能·产品经理·ai编程·visual studio code