av_init_packet详解

av_init_packet是FFmpeg中用于初始化AVPacket结构的函数,但在新版本中已废弃,推荐使用av_packet_alloc()av_packet_free()替代。

一、函数原型

/**
 * 初始化可选的AVPacket字段
 * 注意:此函数不分配数据缓冲区
 * 
 * @param pkt 要初始化的AVPacket
 */
void av_init_packet(AVPacket *pkt);

二、新旧API对比

旧方法(已废弃)

#include <libavcodec/avcodec.h>

void old_method() {
    AVPacket pkt;
    
    // 初始化packet
    av_init_packet(&pkt);
    
    // 设置数据
    pkt.data = NULL;
    pkt.size = 0;
    pkt.stream_index = 0;
    pkt.pts = AV_NOPTS_VALUE;
    pkt.dts = AV_NOPTS_VALUE;
    
    // 使用packet...
    
    // 注意:需要手动释放数据
    if (pkt.data) {
        av_free(pkt.data);
    }
}

新方法(推荐)

#include <libavcodec/avcodec.h>

void new_method() {
    AVPacket *pkt = NULL;
    
    // 分配并初始化packet
    pkt = av_packet_alloc();
    if (!pkt) {
        // 错误处理
        return;
    }
    
    // 使用packet...
    // 注意:av_packet_alloc()已经正确初始化了所有字段
    
    // 释放packet
    av_packet_free(&pkt);
}

三、历史用法详解

3.1 基本初始化

void init_packet_basic() {
    AVPacket pkt;
    
    // 初始化packet
    av_init_packet(&pkt);
    
    // 现在packet的字段被设置为默认值:
    // pkt.data = NULL
    // pkt.size = 0
    // pkt.stream_index = 0
    // pkt.flags = 0
    // pkt.pts = AV_NOPTS_VALUE
    // pkt.dts = AV_NOPTS_VALUE
    // pkt.pos = -1
    // pkt.duration = 0
    // pkt.buf = NULL
    // pkt.side_data = NULL
    // pkt.side_data_elems = 0
    
    printf("初始化后的packet: data=%p, size=%d\n", pkt.data, pkt.size);
}

3.2 创建数据包并填充数据

int create_and_fill_packet_old() {
    AVPacket pkt;
    uint8_t *data = NULL;
    int data_size = 1024;  // 假设需要1024字节
    
    // 初始化packet
    av_init_packet(&pkt);
    
    // 分配数据缓冲区
    data = (uint8_t*)av_malloc(data_size);
    if (!data) {
        fprintf(stderr, "无法分配内存\n");
        return AVERROR(ENOMEM);
    }
    
    // 填充测试数据
    for (int i = 0; i < data_size; i++) {
        data[i] = i & 0xFF;
    }
    
    // 设置packet
    pkt.data = data;
    pkt.size = data_size;
    pkt.pts = 1000;  // 示例时间戳
    pkt.dts = 900;   // 示例解码时间戳
    pkt.flags = 0;
    
    // 标记为关键帧
    pkt.flags |= AV_PKT_FLAG_KEY;
    
    printf("创建packet成功:\n");
    printf("  数据地址: %p\n", pkt.data);
    printf("  数据大小: %d\n", pkt.size);
    printf("  PTS: %ld\n", pkt.pts);
    printf("  DTS: %ld\n", pkt.dts);
    printf("  关键帧: %s\n", (pkt.flags & AV_PKT_FLAG_KEY) ? "是" : "否");
    
    // 使用packet...
    
    // 清理
    av_free(data);
    
    return 0;
}

四、迁移到新API

4.1 迁移示例

// 旧代码
void old_way() {
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = (uint8_t*)av_malloc(1024);
    pkt.size = 1024;
    
    // ... 使用pkt
    
    av_free(pkt.data);
}

// 新代码
void new_way() {
    AVPacket *pkt = av_packet_alloc();
    if (!pkt) {
        return;
    }
    
    // 为新包分配缓冲区
    int ret = av_new_packet(pkt, 1024);
    if (ret < 0) {
        av_packet_free(&pkt);
        return;
    }
    
    // ... 使用pkt
    
    av_packet_free(&pkt);
}

4.2 完整迁移示例

// 旧代码示例
int process_packets_old(const char *filename) {
    AVFormatContext *fmt_ctx = NULL;
    AVPacket pkt;
    int ret = 0;
    
    if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {
        return ret;
    }
    
    // 读取packet循环
    while (1) {
        // 初始化packet
        av_init_packet(&pkt);
        
        ret = av_read_frame(fmt_ctx, &pkt);
        if (ret < 0) {
            break;
        }
        
        // 处理packet...
        printf("读取packet: 流%d, 大小%d\n", pkt.stream_index, pkt.size);
        
        // 释放数据
        av_packet_unref(&pkt);
    }
    
    avformat_close_input(&fmt_ctx);
    return 0;
}

// 新代码示例
int process_packets_new(const char *filename) {
    AVFormatContext *fmt_ctx = NULL;
    AVPacket *pkt = NULL;
    int ret = 0;
    
    if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {
        return ret;
    }
    
    pkt = av_packet_alloc();
    if (!pkt) {
        avformat_close_input(&fmt_ctx);
        return AVERROR(ENOMEM);
    }
    
    // 读取packet循环
    while (1) {
        ret = av_read_frame(fmt_ctx, pkt);
        if (ret < 0) {
            break;
        }
        
        // 处理packet...
        printf("读取packet: 流%d, 大小%d\n", pkt->stream_index, pkt->size);
        
        // 清除packet内容以便重用
        av_packet_unref(pkt);
    }
    
    av_packet_free(&pkt);
    avformat_close_input(&fmt_ctx);
    return 0;
}

五、理解引用计数

5.1 新旧API的内存管理差异

void compare_memory_management() {
    printf("=== 新旧API内存管理对比 ===\n\n");
    
    // 1. 旧API - 手动内存管理
    printf("1. 旧API (av_init_packet):\n");
    printf("   - 需要在栈或堆上分配AVPacket结构体\n");
    printf("   - 需要手动调用av_init_packet初始化\n");
    printf("   - 需要手动分配和释放数据缓冲区\n");
    printf("   - 没有引用计数机制\n");
    printf("   - 容易出现内存泄漏和野指针\n\n");
    
    // 2. 新API - 自动内存管理
    printf("2. 新API (av_packet_alloc):\n");
    printf("   - 在堆上分配AVPacket结构体\n");
    printf("   - 自动初始化所有字段\n");
    printf("   - 通过av_new_packet自动管理数据缓冲区\n");
    printf("   - 内置引用计数(通过AVBufferRef)\n");
    printf("   - 自动处理内存释放\n");
    printf("   - 支持浅拷贝(av_packet_ref/av_packet_unref)\n");
}

5.2 引用计数示例

void reference_counting_example() {
    printf("引用计数示例:\n");
    
    // 创建原始packet
    AVPacket *original = av_packet_alloc();
    av_new_packet(original, 1024);
    printf("1. 创建original packet:\n");
    printf("   引用计数: %d (如果使用引用计数)\n", original->buf ? original->buf->size : 0);
    
    // 创建引用
    AVPacket *reference = av_packet_alloc();
    av_packet_ref(reference, original);
    printf("2. 创建reference packet:\n");
    printf("   引用计数增加\n");
    
    // 使用两个packet
    printf("3. 使用original和reference:\n");
    printf("   original数据地址: %p\n", original->data);
    printf("   reference数据地址: %p\n", reference->data);
    printf("   两个packet共享同一个数据缓冲区\n");
    
    // 释放reference
    av_packet_unref(reference);
    av_packet_free(&reference);
    printf("4. 释放reference:\n");
    printf("   引用计数减少,但数据仍存在\n");
    
    // 释放original
    av_packet_unref(original);
    av_packet_free(&original);
    printf("5. 释放original:\n");
    printf("   引用计数归零,数据被释放\n");
}

六、兼容性处理

6.1 支持多版本FFmpeg

#if LIBAVCODEC_VERSION_MAJOR < 57
// FFmpeg 2.x 及更早版本
#define USE_OLD_PACKET_API 1
#else
// FFmpeg 3.0 及更新版本
#define USE_OLD_PACKET_API 0
#endif

void compatible_packet_creation() {
#if USE_OLD_PACKET_API
    printf("使用旧API (FFmpeg < 3.0)\n");
    AVPacket pkt;
    av_init_packet(&pkt);
    
    // 设置数据
    pkt.data = (uint8_t*)av_malloc(1024);
    pkt.size = 1024;
    
    // 使用pkt...
    
    // 释放
    if (pkt.data) {
        av_free(pkt.data);
    }
#else
    printf("使用新API (FFmpeg >= 3.0)\n");
    AVPacket *pkt = av_packet_alloc();
    if (pkt) {
        av_new_packet(pkt, 1024);
        
        // 使用pkt...
        
        av_packet_free(&pkt);
    }
#endif
}

6.2 封装兼容性函数

#include <stddef.h>

/**
 * 兼容性函数:创建并初始化AVPacket
 */
int compat_packet_create(AVPacket **packet, int size) {
    if (!packet) {
        return AVERROR(EINVAL);
    }
    
#if USE_OLD_PACKET_API
    *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    if (!*packet) {
        return AVERROR(ENOMEM);
    }
    
    av_init_packet(*packet);
    (*packet)->data = (uint8_t*)av_malloc(size);
    if (!(*packet)->data) {
        av_free(*packet);
        *packet = NULL;
        return AVERROR(ENOMEM);
    }
    (*packet)->size = size;
    
    return 0;
#else
    *packet = av_packet_alloc();
    if (!*packet) {
        return AVERROR(ENOMEM);
    }
    
    return av_new_packet(*packet, size);
#endif
}

/**
 * 兼容性函数:释放AVPacket
 */
void compat_packet_free(AVPacket **packet) {
    if (!packet || !*packet) {
        return;
    }
    
#if USE_OLD_PACKET_API
    if ((*packet)->data) {
        av_free((*packet)->data);
    }
    av_free(*packet);
#else
    av_packet_free(packet);
#endif
    
    *packet = NULL;
}

七、常见错误

7.1 忘记初始化

void common_mistake_1() {
    AVPacket pkt;  // 未初始化!
    
    // 错误:直接使用未初始化的packet
    pkt.data = NULL;  // 可能没问题
    pkt.size = 0;     // 可能没问题
    // 但其他字段包含未定义的值!
    
    // 正确做法:
    // av_init_packet(&pkt);  // 旧API
    // 或
    // AVPacket *pkt = av_packet_alloc();  // 新API
}

7.2 重复初始化

void common_mistake_2() {
    AVPacket *pkt = av_packet_alloc();  // 已自动初始化
    
    // 错误:重复初始化
    // av_init_packet(pkt);  // 不应该调用!
    
    // 这可能导致引用计数等问题
}

7.3 内存泄漏

void common_mistake_3() {
    // 旧API - 容易泄漏
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = (uint8_t*)av_malloc(1024);
    pkt.size = 1024;
    
    // 使用pkt...
    
    // 错误:忘记释放数据!
    // av_free(pkt.data);  // 必须手动释放
    
    // 新API - 自动管理
    AVPacket *pkt2 = av_packet_alloc();
    av_new_packet(pkt2, 1024);
    
    // 使用pkt2...
    
    av_packet_free(&pkt2);  // 自动释放所有资源
}

八、最佳实践

8.1 使用新API的完整流程

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

int modern_packet_processing(const char *input_file, const char *output_file) {
    AVFormatContext *input_ctx = NULL;
    AVFormatContext *output_ctx = NULL;
    AVPacket *pkt = NULL;
    int ret = 0;
    
    // 1. 打开输入文件
    if ((ret = avformat_open_input(&input_ctx, input_file, NULL, NULL)) < 0) {
        fprintf(stderr, "无法打开输入文件: %s\n", av_err2str(ret));
        return ret;
    }
    
    // 2. 创建输出上下文
    if ((ret = avformat_alloc_output_context2(&output_ctx, NULL, NULL, output_file)) < 0) {
        fprintf(stderr, "无法创建输出上下文: %s\n", av_err2str(ret));
        avformat_close_input(&input_ctx);
        return ret;
    }
    
    // 3. 创建packet
    pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "无法分配packet\n");
        avformat_close_input(&input_ctx);
        avformat_free_context(output_ctx);
        return AVERROR(ENOMEM);
    }
    
    // 4. 处理每个packet
    while (1) {
        // 清除之前的内容
        av_packet_unref(pkt);
        
        // 读取packet
        ret = av_read_frame(input_ctx, pkt);
        if (ret < 0) {
            if (ret == AVERROR_EOF) {
                printf("处理完成\n");
                ret = 0;
            } else {
                fprintf(stderr, "读取错误: %s\n", av_err2str(ret));
            }
            break;
        }
        
        // 处理packet(这里可以根据需要修改)
        printf("处理packet: 流%d, 大小%d, 时间%.3fs\n",
               pkt->stream_index,
               pkt->size,
               pkt->pts * av_q2d(input_ctx->streams[pkt->stream_index]->time_base));
        
        // 写入输出(示例,需要实际实现)
        // ret = av_interleaved_write_frame(output_ctx, pkt);
        // if (ret < 0) {
        //     fprintf(stderr, "写入错误: %s\n", av_err2str(ret));
        //     break;
        // }
    }
    
    // 5. 清理
    av_packet_free(&pkt);
    avformat_close_input(&input_ctx);
    if (output_ctx) {
        avformat_free_context(output_ctx);
    }
    
    return ret;
}

九、总结

  1. 废弃状态av_init_packet在新版本FFmpeg中已废弃

  2. 替代函数:使用 av_packet_alloc()av_packet_free()

  3. 内存管理:新API自动管理内存,支持引用计数

  4. 兼容性:如果需要支持旧版本,使用条件编译

  5. 错误处理:总是检查返回值

  6. 最佳实践

    • 使用新API

    • 检查内存分配失败

    • 及时释放资源

    • 正确处理引用计数

强烈建议在新项目中使用新API,并对现有代码进行迁移。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浩瀚之水_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值