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;
}
九、总结
-
废弃状态:
av_init_packet在新版本FFmpeg中已废弃 -
替代函数:使用
av_packet_alloc()和av_packet_free() -
内存管理:新API自动管理内存,支持引用计数
-
兼容性:如果需要支持旧版本,使用条件编译
-
错误处理:总是检查返回值
-
最佳实践:
-
使用新API
-
检查内存分配失败
-
及时释放资源
-
正确处理引用计数
-
强烈建议在新项目中使用新API,并对现有代码进行迁移。
4830

被折叠的 条评论
为什么被折叠?



