您的位置:首页 > 科技 > IT业 > 一个小网站一般多少钱_东莞画册设计_发布软文广告_上海百网优seo优化公司

一个小网站一般多少钱_东莞画册设计_发布软文广告_上海百网优seo优化公司

2025/3/24 12:46:59 来源:https://blog.csdn.net/weixin_43063398/article/details/146435760  浏览:    关键词:一个小网站一般多少钱_东莞画册设计_发布软文广告_上海百网优seo优化公司
一个小网站一般多少钱_东莞画册设计_发布软文广告_上海百网优seo优化公司

解封装

常用函数

1. avformat_open_input()

作用
  • 打开媒体文件或网络资源:解析文件路径或 URL,识别媒体格式(如 MP4、AVI、RTSP 等)。
  • 初始化 AVFormatContext:分配并初始化 AVFormatContext 结构体,用于存储媒体文件的元数据和流信息。
  • 准备后续操作:为后续的解封装(demuxing)和解码操作做好准备。
典型用法
AVFormatContext* fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, input_file, NULL, NULL) < 0) {fprintf(stderr, "Could not open input file: %s\n", input_file);return -1;
}
关键点
  • 输入参数
    • AVFormatContext** fmt_ctx:指向 AVFormatContext 指针的指针,用于存储媒体文件的上下文。
    • const char* url:文件路径或 URL。
    • AVInputFormat* fmt:指定输入格式(通常为 NULL,自动检测)。
    • AVDictionary** options:额外的选项(如超时、缓冲区大小等)。
  • 输出
    • 成功时返回 0,失败时返回负值。
    • 初始化后的 AVFormatContext 包含媒体文件的元数据和流信息。

2. avformat_find_stream_info()

作用
  • 解析流信息:分析媒体文件中的视频、音频流,提取编码器类型、帧率、时长、分辨率等关键信息。
  • 填充 AVFormatContext:将解析到的流信息填充到 AVFormatContextstreams 数组中。
  • 准备解码:为后续的解码操作分配必要的缓冲区和数据结构。
典型用法
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");avformat_close_input(&fmt_ctx);return -1;
}
关键点
  • 输入参数
    • AVFormatContext* fmt_ctx:已打开的 AVFormatContext
    • AVDictionary** options:额外的选项(如最大读取时长、最大帧数等)。
  • 输出
    • 成功时返回 0,失败时返回负值。
    • fmt_ctx->streams 数组包含所有流的信息(如视频、音频、字幕等)。

3. av_find_best_stream()

作用
  • 查找最佳流:根据指定的流类型(如视频、音频)在 AVFormatContext 中查找最佳匹配的流。
  • 简化流选择:避免手动遍历所有流,自动选择最合适的流。
典型用法
int video_stream_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_idx < 0) {fprintf(stderr, "Could not find video stream\n");avformat_close_input(&fmt_ctx);return -1;
}
关键点
  • 输入参数
    • AVFormatContext* fmt_ctx:已打开的 AVFormatContext
    • enum AVMediaType type:流类型(如 AVMEDIA_TYPE_VIDEOAVMEDIA_TYPE_AUDIO)。
    • int wanted_stream_nb:期望的流索引(通常为 -1,自动选择)。
    • int related_stream:相关流索引(通常为 -1)。
    • AVCodec** decoder_ret:返回解码器(通常为 NULL)。
    • int flags:标志位(通常为 0)。
  • 输出
    • 成功时返回流索引,失败时返回负值。

4. av_read_frame()

av_read_frame 是 FFmpeg 中一个非常重要的函数,用于从媒体文件(如 MP4、MKV 等)中读取一帧数据(可以是视频帧、音频帧或其他类型的包)。它的作用是从 AVFormatContext 中读取下一个数据包(AVPacket),并将其存储到指定的 AVPacket 结构中。


1. 函数原型
int av_read_frame(AVFormatContext *fmt_ctx, AVPacket *pkt);
  • 参数

    • fmt_ctxAVFormatContext 指针,表示媒体文件的上下文。
    • pktAVPacket 指针,用于存储读取到的数据包。
  • 返回值

    • 成功时返回 0
    • 如果到达文件末尾,返回 AVERROR_EOF
    • 如果发生错误,返回负的错误代码。

2. 功能说明

av_read_frame 的作用是从媒体文件中读取下一个数据包(AVPacket),并将其存储到 pkt 中。数据包可以是:

  • 视频帧(如 H.264 帧)。
  • 音频帧(如 AAC 帧)。
  • 其他类型的包(如字幕或元数据)。

每次调用 av_read_frame 时,它会从文件中读取一个完整的数据包,并将其填充到 pkt 中。读取的数据包需要后续通过解码器(AVCodecContext)进行解码。


3. 使用步骤

以下是使用 av_read_frame 的典型步骤:

1. 打开媒体文件并初始化 AVFormatContext
AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);
avformat_find_stream_info(fmt_ctx, NULL);
2. 准备 AVPacket
AVPacket pkt;
av_init_packet(&pkt);
3. 循环读取数据包
while (av_read_frame(fmt_ctx, &pkt) >= 0) {// 检查数据包属于哪个流if (pkt.stream_index == video_stream_index) {// 处理视频帧} else if (pkt.stream_index == audio_stream_index) {// 处理音频帧}// 释放数据包av_packet_unref(&pkt);
}
4. 释放资源
avformat_close_input(&fmt_ctx);

4. 关键点
1. AVPacket 的生命周期
  • av_read_frame 会为 pkt 分配内存并填充数据。
  • 使用完 pkt 后,必须调用 av_packet_unref 释放其内存,否则会导致内存泄漏。
2. 流索引(stream_index
  • 每个数据包都属于某个流(视频流、音频流等),通过 pkt.stream_index 可以确定数据包所属的流。
  • 流的索引可以通过 AVFormatContext 中的 streams 数组获取。
3. 数据包的时间戳
  • 数据包中包含时间戳(PTS 和 DTS),用于同步音视频。
  • 时间戳的单位是流的时间基(AVStream->time_base),需要通过 av_q2d 转换为秒。

5. av_seek_frame()

在 FFmpeg 中,av_seek_frame() 是用于在输入流中定位到指定时间戳或帧索引的核心函数。它允许你在处理音视频流时跳转到特定位置,广泛应用于播放器、编辑器等场景。以下是详细解析:


1. 函数原型
int av_seek_frame(AVFormatContext *fmt_ctx, int stream_idx, int64_t timestamp, int flags);
参数
  • fmt_ctx: 输入流的上下文(AVFormatContext*),表示要操作的媒体文件或流。
  • stream_idx: 需要操作的流的索引(如视频流为 0,音频流为 1)。若为 -1,表示操作所有流。
  • timestamp: 目标时间戳(单位由流的 time_base 定义,如微秒)。
  • flags: 控制 seek 行为的标志位,例如:
    • AVSEEK_FLAG_BACKWARD: 向后搜索(最近的匹配位置)。
    • AVSEEK_FLAG_FORWARD: 向前搜索(第一个匹配位置)。
    • AVSEEK_FLAG_FRAME精确: 精确匹配帧边界。
    • AVSEEK_FLAG_ANY: 允许任何近似值。
返回值

≥0: 成功,返回新的时间戳位置。
<0: 失败,返回错误码(如 AVERROR_EOF)。


2. 核心功能
  • 时间戳定位:将播放头移动到指定的时间戳(如 10秒)。
  • 帧索引定位:直接跳转到指定帧(如第 100 帧)。
  • 流同步:确保多个流(视频+音频)同步到同一时间点。

3. 使用示例
场景 1:跳转到指定时间(秒)
AVFormatContext *fmt_ctx = ...; // 初始化的输入流上下文
int video_stream_idx = ...;     // 视频流索引// 跳转到第 5 秒(需转换为时间戳)AVRational time_base = fmt_ctx->streams[video_stream_idx]->time_base;int64_t target_ts = 5 * av_q2d(time_base); // 5秒 = 5 / 1 (假设 time_base=1/1)int ret = av_seek_frame(fmt_ctx, video_stream_idx, target_ts, AVSEEK_FLAG_BACKWARD);
if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Seek failed\n");
} else {av_log(NULL, AV_LOG_INFO, "Seeked to %lld microseconds\n", ret);
}
场景 2:跳转到指定帧
// 跳转到第 100 帧(仅视频流支持)
int frame = 100;
ret = av_seek_frame(fmt_ctx, video_stream_idx, frame, AVSEEK_FLAG_FRAME精确);

4. 关键注意事项
1. 时间基转换
  • 时间戳单位timestamp 的单位由流的 time_base 决定(如 AV_TIME_BASE 表示微秒)。
  • 转换公式
int64_t timestamp = seconds * av_q2d(time_base); 
// 或 av_rescale_q(seconds, AV_TIME_BASE, time_base)
2. 流索引处理
  • 单一流操作:明确指定 stream_idx(如视频或音频流)。
  • 多流同步:若需同步多个流,需分别对每个流调用 av_seek_frame()
3. 错误处理
  • 检查返回值:失败时可能返回 AVERROR_EOF(未找到位置)或 AVERROR_IO(I/O 错误)。
  • 流状态:确保流未被关闭,且处于可seek状态(如 AVFS_SEEKABLE)。
4. 性能优化
  • 批量 seek:避免频繁调用,可结合 av_read_frame()AVFRAME_FLAG Sebastian 标志读取多帧。
  • 硬件加速:某些解码器(如 NVIDIA NVDEC)可能不支持随机访问,需特殊处理。

5. 高级场景
多流同步
// 同步视频和音频流到同一时间戳
int videoStream = ...;
int audioStream = ...;
int64_t target_ts = ...;av_seek_frame(fmt_ctx, videoStream, target_ts, 0);
av_seek_frame(fmt_ctx, audioStream, target_ts, 0);
动态调整播放速度
// 加速播放(2倍速)
int64_t new_ts = av_rescale_q(current_ts, fmt_ctx->streams[0]->time_base, av_make_q(1, 2)); // 时间缩放因子为 0.5
av_seek_frame(fmt_ctx, 0, new_ts, 0);

6. 常见问题
  1. 为什么seek后无法读取到数据?

    • 缓冲区未刷新:调用 av_flush_packets(fmt_ctx) 清空输入缓冲区。
    • 流未seekable:某些流(如直播流)不支持随机访问。
  2. 如何实现逐帧播放?

    int frame = 0;
    while (frame < total_frames) {av_seek_frame(fmt_ctx, videoStream, frame, AVSEEK_FLAG_FRAME精确);AVPacket pkt;av_init_packet(&pkt);avcodec_decode_video2(...); // 读取当前帧frame++;
    }
    
  3. seek到帧边界的问题

    • 使用 AVSEEK_FLAG_FRAME精确 确保定位到帧起始位置。

常用数据结构

AVFormatContext:媒体格式的全局管理者

作用
  • 管理容器格式:存储媒体文件的容器信息(如MP4、MKV、FLV等)。
  • 封装流信息:包含文件中所有流(AVStream)的元数据。
  • 控制输入/输出:用于解封装(demuxing)或封装(muxing)操作。
关键字段
typedef struct AVFormatContext {const AVClass *av_class;          // 类信息(用于日志和回调)AVInputFormat *iformat;           // 输入格式(解封装时使用)AVOutputFormat *oformat;          // 输出格式(封装时使用)AVIOContext *pb;                  // I/O上下文(文件或网络读写)unsigned int nb_streams;          // 流的数量AVStream **streams;               // 流数组(每个元素对应一个AVStream)char filename[1024];              // 文件名或URLint64_t duration;                 // 文件总时长(微秒)int64_t bit_rate;                 // 全局比特率(bps)AVDictionary *metadata;           // 元数据(标题、作者等)
} AVFormatContext;

典型用法
// 打开输入文件
AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL);// 读取流信息
avformat_find_stream_info(fmt_ctx, NULL);// 遍历所有流,找到视频流索引
int video_stream_idx = -1;
for (int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_idx = i;break;}
}// 关闭并释放资源
avformat_close_input(&fmt_ctx);

AVCodecParameters

AVCodecParameters 是 FFmpeg 中用于描述编解码器参数的结构体。它在 FFmpeg 的较新版本中取代了旧的 AVCodecContext,用于存储音视频流的编解码信息。AVCodecParameters 是一个更轻量级的结构体,专注于存储编解码器的参数,而不包含编解码器的状态或运行时数据。
AVCodecParameters 用于描述音视频流的编解码属性,例如:

  • 编码格式(如 H.264、AAC)。
  • 视频的分辨率、帧率。
  • 音频的采样率、声道数。
  • 其他编解码相关的参数。

它的主要目的是在解复用(Demuxing)时提取流的编解码信息,而不需要初始化完整的编解码器上下文(AVCodecContext)。


1. AVCodecParameters 的主要字段

以下是 AVCodecParameters 中一些重要的字段:

字段名类型描述
codec_typeAVMediaType媒体类型(视频、音频、字幕等)。例如:AVMEDIA_TYPE_VIDEO 表示视频。
codec_idAVCodecID编解码器 ID(如 AV_CODEC_ID_H264 表示 H.264 编码)。
formatint像素格式(视频)或采样格式(音频)。例如:AV_PIX_FMT_YUV420P
width / heightint视频的宽度和高度(以像素为单位)。
sample_rateint音频的采样率(如 44100 Hz)。
channelsint音频的声道数(如 2 表示立体声)。
channel_layoutuint64_t音频的声道布局(如 AV_CH_LAYOUT_STEREO 表示立体声)。
bit_rateint64_t流的比特率(单位:比特/秒)。
extradatauint8_t*编解码器特定的额外数据(如 H.264 的 SPS/PPS)。
extradata_sizeint额外数据的大小。

2. AVCodecParameters 的使用场景

AVCodecParameters 通常在以下场景中使用:

  1. 解复用(Demuxing)

    • 当从容器(如 MP4、MKV)中读取音视频流时,AVFormatContext 会为每个流分配一个 AVStream,而 AVStream 中的 codecpar 字段就是 AVCodecParameters
    • 通过 AVCodecParameters,可以获取流的编解码信息,而不需要初始化编解码器。
  2. 编码/解码前的准备

    • 在初始化编解码器(AVCodecContext)之前,可以使用 AVCodecParameters 中的信息来配置编解码器。
  3. 流的复制或转封装

    • 在转封装(Remuxing)时,可以直接将 AVCodecParameters 从一个流复制到另一个流,而不需要重新解析编解码信息。

3. AVCodecParametersAVCodecContext 的区别
  • AVCodecParameters

    • 仅存储编解码器的参数(如格式、分辨率、采样率等)。
    • 不包含编解码器的状态或运行时数据。
    • 更轻量级,适合在解复用或转封装时使用。
  • AVCodecContext

    • 存储编解码器的参数和状态。
    • 包含编解码器的运行时数据(如帧缓冲区、编码延迟等)。
    • 需要在编解码时使用。

AVStream:媒体流的详细信息

作用
  • 描述单个流:每个 AVStream 对应一个媒体流(如视频、音频、字幕)。
  • 存储流参数:包含流的编解码参数(如分辨率、采样率)、时间基(time_base)等。
关键字段
typedef struct AVStream {int index;                        // 流索引(唯一标识)AVCodecParameters *codecpar;      // 编解码参数(已过时)AVRational time_base;             // 时间基(理解成分数就行了)int64_t duration;                 // 流的总时长(单位:time_base)AVRational avg_frame_rate;        // 平均帧率(视频流)
} AVStream;

AVRational time_base 是 FFmpeg 中用于表示时间基的结构体。时间基是一个分数,形式为 num/den,其中 num 是分子,den 是分母。它定义了时间的基本单位,用于将时间值转换为秒或其他时间单位。

具体解释:
时间基的定义

时间基是一个分数,形式为 AVRational {num, den},表示每个时间戳的单位是 num/den 秒。

例如:

  • 如果 time_base = {1, 1000},那么每个时间戳的单位是 1/1000 秒(即 1 毫秒)。

  • 如果 time_base = {1, 90000},那么每个时间戳的单位是 1/90000 秒(常见于 MPEG-TS 流)。
    那么该帧的实际时间可以通过公式计算:

    double seconds = timestamp * av_q2d(time_base);
    

    其中 av_q2d 是 FFmpeg 提供的函数,用于将 AVRational 转换为浮点数。

  • 示例
    假设 time_base = {1, 1000},即 1/1000,表示时间单位是毫秒。如果某个帧的时间戳是 5000,那么该帧的实际时间是:

    double seconds = 5000 * (1.0 / 1000) = 5.0
  • AVStream 中的意义

    • time_base 是流的时间基准,用于解释该流中的时间戳。
    • 例如,视频流的时间基可能是 1/90000(常见于 MPEG-TS 流),而音频流的时间基可能是 1/44100(CD 音质)。
  • 与其他字段的关系

    • duration 字段表示流的总时长,单位是 time_base。例如,如果 duration = 90000time_base = {1, 1000},那么流的总时长是 90 秒。
    • avg_frame_rate 是视频流的平均帧率,也是一个 AVRational,表示每秒的帧数。

AVPacket:编码后的数据包

作用
  • 存储压缩数据:保存从媒体文件读取的编码后的数据(如一个视频帧或音频帧)。
  • 携带时间信息:包含解码时间戳(DTS)和显示时间戳(PTS)。
关键字段
typedef struct AVPacket {AVBufferRef *buf;uint8_t *data;                    // 数据指针(压缩数据)int size;                         // 数据大小int64_t pts;                      // 表示数据应被显示的时间点 (num/den)int64_t dts;                      // 表示数据应被解码的时间点(num/den)int stream_index;                 // 所属流的索引int flags;                        // 标志位(关键帧等)
} AVPacket;
av_packet 的生命周期管理
函数作用内存操作
av_packet_alloc()分配新包分配内存,引用计数初始化为 0
av_packet_clone()克隆包(共享缓冲区)引用计数不变
av_buffer_ref()增加缓冲区引用引用计数 +1
av_packet_unref()释放包引用计数 -1,释放内存
av_packet_free()强制释放包直接释放内存(不依赖引用计数)

三者的协作流程
  1. 初始化容器

    • 通过 AVFormatContext 打开输入文件,获取全局信息。
    • 遍历 AVFormatContext->streams 获取各个 AVStream
  2. 处理数据包

    • 使用 av_read_frame 读取 AVPacket
    • 根据 AVPacket->stream_index 找到对应的 AVStream
    • AVPacket 送入解码器(需结合 AVCodecContext)。
  3. 时间戳转换

    • AVPacketptsdts 转换为实际时间:
      double timestamp_sec = pkt.pts * av_q2d(stream->time_base);
      
  4. 资源释放

    • 使用 avformat_close_input 释放 AVFormatContext
    • 使用 av_packet_unref 释放 AVPacket

总结
  • AVFormatContext:媒体文件的全局管理器,负责解封装和流信息存储。
  • AVStream:单个流的详细信息,包含编解码参数和时间基。
  • AVPacket:编码后的数据包,携带压缩数据和时间戳。

三者协作实现媒体文件的读取、处理和写入,是FFmpeg处理流程的核心结构体。

AVPacket的关键函数

1. av_packet_alloc()

作用

动态分配一个空的 AVPacket,初始化 buf 数组和元数据。

函数原型
AVPacket *av_packet_alloc(int buf_count);
参数

buf_count: 预分配的 buf 数组长度(需 ≥ 数据平面数)。

返回值

• 成功返回指向新分配的 AVPacket,失败返回 NULL

示例
// 分配一个支持 3 数据平面的包(如视频 YUV420P)
AVPacket *pkt = av_packet_alloc(3);
if (!pkt) {av_log(NULL, AV_LOG_ERROR, "Allocation failed\n");exit(1);
}// 使用后释放
av_packet_unref(pkt); // 自动释放内存

2. av_packet_clone()

作用

深度克隆现有 AVPacket,包括 buf 引用、时间戳、流索引等所有字段。

函数原型
int av_packet_clone(AVPacket *src, AVPacket *dst, int buf_count);
参数

src: 源数据包。
dst: 目标数据包(需已通过 av_packet_alloc() 分配)。
buf_count: 目标 buf 数组容量(需 ≥ src->buf_count)。

返回值

• 成功返回 0,失败返回错误码(如 AVERROR(ENOMEM))。

示例
AVPacket *src_pkt, *dst_pkt;
av_packet_alloc(&dst_pkt, src_pkt->buf_count); // 预分配缓冲区int ret = av_packet_clone(src_pkt, dst_pkt, src_pkt->buf_count);
if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Clone failed\n");
}// 增加引用计数(若需长期保留)
for (int i = 0; i < dst_pkt->buf_count; i++) {av_buffer_ref(dst_pkt->buf[i]);
}av_packet_unref(dst_pkt); // 自动释放

3. av_packet_ref()

作用

增加 AVBufferRef 的引用计数,确保缓冲区不会被意外释放。

函数原型
void av_buffer_ref(AVBufferRef *buf);
参数

buf: 需要增加引用的 AVBufferRef

使用场景

克隆后保留数据:克隆 AVPacket 后,若需长期使用其缓冲区,需手动调用 av_buffer_ref()
多线程共享:在多线程环境中,确保每个线程对缓冲区的引用合法。

1. 为什么克隆后需要手动调用 av_buffer_ref()

引用计数的作用

  • 共享缓冲区AVPacket 克隆后,缓冲区引用是共享的(即克隆后的 buf 数组直接指向源包的 AVBufferRef)。
  • 引用计数规则
    • 引用计数 (refcount):表示当前有多少个 AVBufferRef 指向同一块内存。
    • 释放条件:当 refcount 降为 0 时,FFmpeg 会自动释放缓冲区内存。

克隆后的风险

  • 示例场景
    AVPacket *src_pkt = ...; // 原始数据包,buf->refcount=1
    AVPacket *clone_pkt = av_packet_clone(src_pkt, ...); // 克隆后,clone_pkt->buf 的 refcount=1
    
  • 问题
    如果此时源包 src_pkt 被释放(av_packet_unref(src_pkt)),其 bufrefcount 会减到 0,导致缓冲区被销毁。此时 clone_pkt 仍然指向已释放的内存,引发未定义行为(如崩溃或数据错误)。

解决方案

  • 手动增加引用
    通过 av_buffer_ref(clone_pkt->buf[i]) 显式增加引用计数,确保缓冲区不会被意外释放:
    for (int i = 0; i < clone_pkt->buf_count; i++) {av_buffer_ref(clone_pkt->buf[i]); // refcount +=1
    }
    
  • 引用计数变化
    • 克隆后:buf->refcount=1(共享)。
    • 增加引用后:buf->refcount=2,即使源包被释放,缓冲区仍保留。

2. 为什么多线程环境需要确保引用合法?

线程安全问题

  • 竞态条件
    多个线程可能同时操作同一缓冲区的引用计数(如一个线程释放内存,另一个线程正在读取数据)。
  • 原子操作
    FFmpeg 的 refcount 使用原子操作(如 AV_ATOMIC_INCAV_ATOMIC_DEC)确保增减操作的原子性,但用户代码仍需遵守规则

关键规则

  1. 每个线程必须独立管理引用
    • 如果线程 A 持有缓冲区的引用,线程 B 不能直接释放它。
  2. 克隆后需显式增加引用
    • 即使缓冲区由 FFmpeg 内部管理(owner=1),多线程环境下仍需调用 av_buffer_ref(),避免其他线程误释放。
  3. 使用 av_packet_unref() 而非直接 free
    • 始终通过 av_packet_unref() 释放 AVPacket,由其自动处理引用计数递减。

示例场景

// 线程 1:克隆数据包并处理
AVPacket *clone_pkt = av_packet_clone(src_pkt, ...);
av_buffer_ref(clone_pkt->buf[0]); // 增加引用// 线程 2:释放源包(可能导致问题!)
av_packet_unref(src_pkt); // 如果 clone_pkt 未增加引用,此处会释放缓冲区

解决方案

  • 线程内独立引用
    每个线程在克隆后必须自行增加引用,并在结束时释放:
    // 线程 1
    AVPacket *clone_pkt = av_packet_clone(src_pkt, ...);
    av_buffer_ref(clone_pkt->buf[0]); // 线程 1 的引用
    process(clone_pkt);
    av_buffer_unref(clone_pkt->buf[0]); // 线程 1 释放引用
    av_packet_unref(clone_pkt);// 线程 2
    av_packet_unref(src_pkt); // 安全释放(假设 src_pkt 无其他引用)
    

3. 深层原理:FFmpeg 的内存管理策略

AVBufferRef 的设计

  • 引用计数 (refcount)
    • 初始值为 1(由分配者持有)。
    • 每次 av_buffer_ref() 调用,refcount 增加;每次 av_buffer_unref() 调用,refcount 减少。
  • 所有者标志 (owner)
    • owner=1:缓冲区由 FFmpeg 管理,av_buffer_unref() 会释放内存。
    • owner=0:用户管理内存,av_buffer_unref() 仅减少引用计数,不释放内存。

克隆操作的副作用

  • 浅拷贝av_packet_clone() 是浅拷贝,buf 数组直接引用源包的 AVBufferRef
  • 引用计数共享:克隆后的 buf 引用计数与源包一致,不自动增加

4. 最佳实践总结
场景正确操作错误操作结果
克隆后长期使用av_buffer_ref(clone_pkt->buf[i])直接使用,不增加引用缓冲区被源包释放,导致崩溃或数据错误
多线程共享数据包每个线程独立调用 av_buffer_ref()av_buffer_unref()所有线程共享同一个引用竞态条件,内存泄漏或崩溃
释放数据包av_packet_unref(pkt)直接 free(pkt)av_free(pkt)引用计数未正确递减,内存泄漏

4. av_packet_free()

作用

释放 AVPacket 及其关联的 AVBufferRef,自动递减引用计数。

函数原型
void av_packet_free(AVPacket *pkt);
注意事项

引用计数规则
• 若 buf 由 FFmpeg 内部管理(buf->owner=1),调用 av_packet_free() 会自动释放。
• 若 buf 由用户管理(如硬件解码器返回的 GPU 缓冲区),需手动释放。
替代函数:推荐使用 av_packet_unref(),它会自动处理引用计数。

示例
AVPacket *pkt = av_packet_alloc(3);
// ... 使用 pkt ...
av_packet_free(pkt); // 释放内存

5. av_init_packet()

作用

初始化 AVPacket 结构体,设置默认值(如 size=0pts=dts=0)。

函数原型
void av_init_packet(AVPacket *pkt);
av_packet_alloc() 的区别

无需分配内存:仅初始化现有结构体的字段。
典型用法:在复用已分配的 AVPacket 时调用(如循环处理数据包)。

示例
AVPacket pkt;
av_init_packet(&pkt); // 初始化
pkt.buf_count = 3;    // 设置 buf 数组长度
// ... 填充数据 ...
av_packet_unref(&pkt); // 释放

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com