找回密码
 立即注册
首页 业界区 业界 FFmpeg 关键的结构体

FFmpeg 关键的结构体

乙荒 2 小时前
[[N_FFmpeg]]
一些重要的结构体

FFMPEG中结构体很多; 最关键的结构体可以分成以下几类:

  • 解协议(http,rtsp,rtmp,mms)
AVIOContext, URLProtocol, URLContext 主要存储视音频使用的协议的类型以及状态; URLProtocol存储输入视音频使用的封装格式; 每种协议都对应一个 URLProtocol 结构; (注意: FFMPEG中文件也被当做一种协议"file")

  • 解封装(flv,avi,rmvb,mp4)
AVFormatContext 主要存储视音频封装格式中包含的信息; AVInputFormat存储输入视音频使用的封装格式; 每种视音频封装格式都对应一个AVInputFormat 结构;

  • 解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext, 存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec, 包含该视频/音频对应的解码器; 每种解码器都对应一个AVCodec结构;

  • 存数据
视频的话, 每个结构一般是存一帧; 音频可能有好几帧
解码前数据: AVPacket
解码后数据: AVFrame
参考雷神博客-FFMPEG中最关键的结构体之间的关系
ffmpeg库函数介绍
FFmpeg 学习整理总结
AVFormatContext

可以理解为整个统领上下文的大结构体, 包含: 多个AVStream(streams), AVCodec, AVCodecContext
对应封装格式例如 (mp3/mp4/avi...)
格式转换过程中实现输入和输出功能, 保存相关数据的主要结构, 描述了一个媒体文件或媒体流的构成和基本信息
nb_streams/streams: AVStream结构指针数组, 包含了所有内嵌媒体流的描述, 其内部有 AVInputFormat + AVOutputFormat 结构体, 来表示输入输出的文件格式
avformat_open_input: 创建并初始化部分值, 但其他一些值(如 mux_rate, key 等)需要手工设置初始值, 否则可能出现异常
avformat_alloc_output_context2: 根据文件的输出格式, 扩展名或文件名等分配合适的 AVFormatContext 结构
获取
  1. AVFormatContext fmt_ctx = nullptr;
  2. avformat_open_input(&fmt_ctx, "filename", nullptr, nullptr);//打开一个输入
  3. //然后使用 读取
  4. if((avformat_find_stream_info(avFormatCtx,nullptr))!=0){
  5.     //reaad error
  6. }
  7. //或者
  8. AVFormatContext *avformat_alloc_context(void);
复制代码
每一个 AVFormatContext 都有AVStream **streams;属性; 而nb_streams属性对应着索引
销毁

avformat_free_context()
API doc
pd 属性 (内存读写数据)

参考下 #ffmpeg内存编解码
AVStream

可理解为 "流通道",  例如视频一般会有两个, 音频流和视频流, 保存着解码方式的相关数据
AVStream -- 描述一个媒体流, 其大部分信息可通过 avformat_open_input 根据文件头信息确定, 其他信息可通过 avformat_find_stream_info 获取, 典型的有 视频流, 中英文音频流, 中英文字幕流(Subtitle), 可通过 av_new_stream, avformat_new_stream 等创建;
index: 在AVFormatContext中流的索引, 其值自动生成(AVFormatContext::streams[index])
nb_frames: 流内的帧数目
time_base: 流的时间基准, 是一个实数, 该流中媒体数据的pts和dts都将以这个时间基准为粒度; 通常, 使用av_rescale/av_rescale_q可以实现不同时间基准的转换
avformat_find_stream_info: 获取必要的编解码器参数(如 AVMediaType, CodecID ), 设置到 AVFormatContext::streams::codec 中
av_read_frame: 从多媒体文件或多媒体流中读取媒体数据, 获取的数据由 AVPacket 来存放
av_seek_frame: 改变媒体文件的读写指针来实现对媒体文件的随机访问, 通常支持基于时间, 文件偏移, 帧号(AVSEEK_FLAG_FRAME)的随机访问方式
每个 AVStream, 下面都至少有一个可用的 AVCodec; 用属性名, video_codec,audio_codec,
获取

依赖于 AVCodecContext 或  AVCodecParameters 获取
而AVStream 对应一个 AVCodecContext(新版建议使用 AVCodecParameters), 存储该视频/音频流使用解码方式的相关数据
  1. //必须先读取AVFormatContext
  2. if((avformat_find_stream_info(avFormatCtx,nullptr))!=0){
  3.     //reaad error
  4. }
  5. //流通道 一般两个; 音频/视频
  6. for(unsigned int i=0; inb_streams; i++) {
  7.     if(avFormatCtx->streams[i]->codecpar->codec_type==AVMediaType::AVMEDIA_TYPE_VIDEO){
  8.         this->videoIndex =i;
  9.     }else if(avFormatCtx->streams[i]->codecpar->codec_type==AVMediaType::AVMEDIA_TYPE_AUDIO){
  10.         this->audioIndex =i
  11.     }
  12. }
  13. //或者 avformat_new_stream 函数直接分配
  14. AVStream *out_stream = avformat_new_stream(avFormatCtx, coder);
复制代码
销毁

AVStream的初始化函数是avformat_new_stream(), 销毁函数使用销毁 AVFormatContext 的 avformat_free_context() 就可以了;
建议使用 AVCodecParameters 代替 AVCodecContext

AVCodecContext 结构体在AVStream被声明为已过时, 使用另一个 AVCodecParameters 结构体代替;
注意只是在AVStream被声明为已过时! 但是某些地方该用还是得用..
解码例子
  1. const char *filename = "a.mp4";
  2. AVFormatContext fmt_ctx = nullptr;
  3. avformat_open_input(&fmt_ctx, filename, nullptr, nullptr);//打开一个输入
  4. avformat_find_stream_info(fmt_ctx, nullptr);//读取封装格式
  5. for(size_t i = 0; i < fmt_ctx->nb_streams; ++i)//流通道
  6. {
  7.     AVStream *stream = fmt_ctx->streams[i];
  8.     AVCodec *codec =  avcodec_find_decoder(stream->codecpar->codec_id);//依据ID找的解编码器
  9.     AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);//需ctx中间过渡;  使用avcodec_free_context释放
  10.     //包含了大部分解码器相关的信息, 这里是直接从AVCodecParameters复制到AVCodecContext
  11.     avcodec_parameters_to_context(codec_ctx, stream->codecpar);
  12.     av_codec_set_pkt_timebase(codec_ctx, stream->time_base);
  13.     avcodec_open2(codec_ctx, codec, nullptr);
  14. }
复制代码
编码例子
  1. const char *filename = "b.mp4";
  2. AVFormatContext *fmt_ctx = nullptr;
  3. avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, filename); //需要调用avformat_free_context释放
  4. //new一个流并挂到fmt_ctx名下, 调用avformat_free_context时会释放该流
  5. AVStream *stream = avformat_new_stream(fmt_ctx, nullptr);
  6. AVCodec *codec = avcodec_find_encoder(fmt_ctx->oformat->video_codec);//音频为audio_codec
  7. AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
  8. codec_ctx->video_type = AVMEDIA_TYPE_VIDEO;
  9. codec_ctx->codec_id = m_fmt_ctx->oformat->video_codec;
  10. codec_ctx->width = 1280;//你想要的宽度
  11. codec_ctx->height = 720;//你想要的高度
  12. codec_ctx->format = AV_PIX_FMT_YUV420P;//受codec->pix_fmts数组限制
  13. codec_ctx->gop_size = 12;
  14. codec_ctx->time_base = AVRational{1, 25};//应该根据帧率设置
  15. codec_ctx->bit_rate = 1400 * 1000;
  16. avcodec_open2(codec_ctx, codec, nullptr);
  17. //将AVCodecContext的成员复制到AVCodecParameters结构体; 必须先open 再 copy
  18. avcodec_parameters_from_context(stream->codecpar, codec_ctx);
  19. av_stream_set_r_frame_rate(stream, {1, 25});
复制代码
用 AVCodecParameters 代替AVCodecContext
AVCodecContext

可以理解为 编解码器的(上下文或参数) 在AVStream中, 新API建议使用 AVCodecParameters 代替它!
解码器上下文,统领编码器基本结构体
AVFormatContext -- 格式转换过程中实现输入和输出功能, 保存相关数据的主要结构, 描述了一个媒体文件或媒体流的构成和基本信息
nb_streams/streams: AVStream结构指针数组, 包含了所有内嵌媒体流的描述, 其内部有 AVInputFormat + AVOutputFormat 结构体, 来表示输入输出的文件格式
avformat_open_input: 创建并初始化部分值, 但其他一些值(如 mux_rate, key 等)需要手工设置初始值, 否则可能出现异常
avformat_alloc_output_context2: 根据文件的输出格式, 扩展名或文件名等分配合适的 AVFormatContext 结构
  1. avCodecContext.width(1080);
  2. avCodecContext.height(1920);
  3. avCodecContext.bit_rate(90000);//平均码率
  4. //                avCodecContext.profile(FF_PROFILE_H264_HIGH_422);//profile-level-id=000042
  5. avCodecContext.pix_fmt( AV_PIX_FMT_BGR24);//设置颜色格式
  6. //下面挑一些关键的变量来看看(这里只考虑解码);
  7. enum AVMediaType codec_type: 编解码器的类型(视频, 音频...)
  8. struct AVCodec  *codec: 采用的解码器AVCodec(H.264,MPEG2...)
  9. int bit_rate: 平均比特率
  10. uint8_t *extradata; int extradata_size: 针对特定编码器包含的附加信息(例如对于H.264解码器来说, 存储SPS, PPS等)
  11. AVRational time_base: 根据该参数, 可以把PTS转化为实际的时间(单位为秒s)
  12. int width, height: 如果是视频的话, 代表宽和高
  13. int refs: 运动估计参考帧的个数(H.264的话会有多帧, MPEG2这类的一般就没有了)
  14. int sample_rate: 采样率(音频)
  15. int channels: 声道数(音频)
  16. enum AVSampleFormat sample_fmt: 采样格式
  17. int profile: 型(H.264里面就有, 其他编码标准应该也有)
  18. int level: 级(和profile差不太多)
复制代码
参考 - 雷神博客
获取
  1. //依据编码器获得
  2. avCodecContext = avcodec_alloc_context3(avCodec);
  3. //或者
  4. avCodecContext = avcodec_alloc_context3(nullptr); //传nullptr 先分配一个空的
  5. avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoIndex]->codecpar);// 在通过AVStream 解析获得
  6. //或者 如果情况允许 最好通过AVStream复制
  7. AVStream *in_stream = ifmt_ctx->streams[i];
  8. AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);//根据输入流创建输出流
  9. avcodec_copy_context(out_stream->codec, in_stream->codec);//复制 ctx
复制代码
AVCodecParamteres

可以理解为 编解码器的(上下文或参数)
AVCodecParamteres 结构体是将AVCodecContext中编解码器参数抽取出而形成的新的结构体, 在新版本中的FFMPEG中, 有些结构体中的AVCodecContext已经被弃用, 取而代之的就是AVCodecParameters这个参数, 该结构体定义在libavcodec/codec_par.h文件中
  1. enum AVMediaType codec_type;// 编解码器的类型
  2. const struct AVCodec *codec;// 编解码器,初始化后不可更改
  3. enum AVCodecID codec_id;// 编解码器的id
  4. int64_t bit_rate;// 平均比特率
  5. uint8_t *extradata;// int extradata_size;// 针对特定编码器包含的附加信息
  6. AVRational time_base;// 根据该参数可以将pts转化为实践
  7. int width, height;// 每一帧的宽和高
  8. int gop_size;// 一组图片的数量, 编码时用户设置, 解码时不使用
  9. enum AVPixelFormat pix_fmt;// 像素格式, 编码时用户设置, 解码时可由用户指定, 但是在分析数据会被覆盖用户的设置
  10. int refs;// 参考帧的数量
  11. enum AVColorSpace colorspace;// YUV色彩空间类型
  12. enum AVColorRange color_range;// MPEG JPEG YUV范围
  13. int sample_rate;// 采样率 仅音频
  14. int channels;// 声道数(音频)
  15. enum AVSampleFormat sample_fmt;// //采样格式
  16. int frame_size;// 每个音频帧中每个声道的采样数量
  17. int profile;//配置类型
  18. int level;//级别
复制代码
分配
avcodec_parameters_alloc()
AVCodec

对应编码方式
每一个编码方式对应一个该结构体, 例如(H264, H265 ..等)
获取

codec_id 有哪些可以参考: AVCodecID 枚举
avCodec = avcodec_find_decoder(codec_id);
AVPacket

对应未解码的原始数据, 对视频(Video)来说, AVPacket通常包含一个压缩的Frame; 而音频(Audio)则有可能包含多个压缩的Frame;
AVPacket -- 暂存解码之前的媒体数据(一个音/视频帧, 一个字幕包等)及附加信息(解码时间戳, 显示时间戳, 时长等), 主要用于建立缓冲区并装载数据;
在 AVPacket 结构体中, 重要的变量:
data/size/pos: 数据缓冲区指针, 长度和媒体流中的字节偏移量
flags: 标志域的组合, 1(AV_PKT_FLAG_KEY)表示该数据是一个关键帧, 2(AV_PKT_FLAG_CORRUPT)表示该数据已经损坏
destruct: 释放数据缓冲区的函数指针, 其值可为 [av_destruct_packet]/av_destruct_packet_nofree, 会被 av_free_packet 调用
  1. uint8_t *data;//压缩编码的数据;
  2. //例如对于H.264来说; 1个AVPacket的data通常对应一个NAL;
  3. //注意: 在这里只是对应, 而不是一模一样; 他们之间有微小的差别;//使用FFMPEG类库分离出多媒体文件中的H.264码流
  4. //因此在使用FFMPEG进行视音频处理的时候, 常常可以将得到的AVPacket的data数据直接写成文件, 从而得到视音频的码流文件;
  5. int size;//data的大小
  6. int64_t pts;//显示时间戳
  7. int64_t dts;//解码时间戳
  8. int stream_index;//标识该AVPacket所属的视频/音频流;
  9. flats// 标志, 其中最低为1表示该数据是一个关键帧// 判断是否I帧 if (pkt->flags & AV_PKT_FLAG_KEY) // is keyframe
  10. duration//时长, 以所属媒体流的时间基准为单位
复制代码
https://blog.csdn.net/leixiaohua1020/article/details/14215755
FFmpeg 5.0+ 开始,av_init_packet() 已经被 弃用(deprecated),在 FFmpeg 6.0 及以上版本 甚至直接 移除 了。
官方推荐改用新的 av_packet_alloc() / av_packet_unref() / av_packet_free() 接口。
  1. AVPacket *pkt = av_packet_alloc();
  2. if (!pkt) {
  3.     LOGE("Could not allocate AVPacket");
  4.     return;
  5. }
  6. while (ctx->running && av_read_frame(fmt_ctx, pkt) >= 0) {
  7.     if (pkt->stream_index == videoIndex) {
  8.     }
  9.     av_packet_unref(pkt);
  10. }
  11. av_packet_free(&pkt);
复制代码
获取
  1. //分配
  2. av_packet_alloc();
  3. //擦除数据
  4. void av_packet_unref (AVPacket *pkt)
  5. //释放结构体
  6. void av_packet_free (AVPacket**pkt)
复制代码
API 文档

参考官方文档:
AVFrame


  • This structure describes decoded (raw) audio or video data.

  • AVFrame must be allocated using av_frame_alloc(). Note that this only
  • allocates the AVFrame itself, the buffers for the data must be managed
  • through other means (see below).
  • AVFrame must be freed with av_frame_free().
struct AVFrame

  • 存放从AVPacket中解码后的原始数据, 其必须通过av_frame_alloc()来创建, 通过av_frame_free()来释放;

  • 和AVPacket类似, AVFrame中也有一块数据缓存空间, 在调用av_frame_alloc的时候并不会为这块缓存区域分配空间,
  • 需要使用其他的方法;
  • 在解码的过程使用了两个AVFrame, 这两个AVFrame分配缓存空间的方法也不相同

  • AVFrame 通常分配一次,然后重复使用多次, 不同的数据(如一个AVFrame持有来自解码器的frames; )
  • 在再次使用时,av_frame_unref()将自由持有的任何之前的帧引用并重置它变成初始态;

  • 使用 av_frame_unref(packet);重置数据 和 av_frame_free(packet);来释放
data/linesize: FFMpeg内部以平面的方式存储原始图像数据, 即将图像像素分为多个平面(R/G/B或Y/U/V)数组
data 数组: 其中的指针指向各个像素平面的起始位置, 编码时需要用户设置数据
linesize数组: 存放各个存贮各个平面的缓冲区的行宽, 编码时需要用户设置数据
key_frame: 该图像是否是关键帧, 由 libavcodec 设置
pict_type: 该图像的编码类型: Intra(1)/Predicted(2)/Bi-dir(3) 等 (就是I帧/P帧/B帧), 默认值是 NONE(0), 其值由libavcodec设置
pts: 呈现时间, 编码时由用户设置
quality: 从1(最好)到FF_LAMBDA_MAX(256*128-1,最差), 编码时用户设置, 默认值是0
nterlaced_frame: 表明是否是隔行扫描的,编码时用户指定, 默认0
//分配
av_frame_alloc();
//擦除数据
av_frame_unref(pFrame);
//释放结构体
av_frame_free()
图像处理 行宽(linesize/步长(stride)/间距(pitch)

自定义编码时,AVFrame::data需要手动初始化分配内存, 这些参数很关键
cpu都是32位或者64位的cpu, 他们一次最少读取4, 8个字节, 如果少于这些, 反而要做一些额外的工作, 会花更长的时间; 所有会有一个概念叫做内存对齐, 将结构体的长度设为4, 8的倍数;
间距就是指图像中的一行图像数据所占的存储空间的长度, 它是一个大于等于图像宽度的内存对齐的长度; 这样每次以行为基准读取数据的时候就能内存对齐, 虽然可能会有一点内存浪费, 但是在内存充裕的今天已经无所谓了;
所以如果图像的宽度如果是内存对齐长度的整数倍, 那么间距就会等于宽度, 而现在的cpu通常一次读取都是4个字节, 而我们通常见到的分辨率都是4的整数倍, 所以我们通常发现间距和图像的宽度一样(通常rgb32格式或者以通道表示的yuv420p格式的y通道);
在用ffmpeg进行图像格式转换的时候, 需要传入一个参数 stride, 其实也是间距; 只不过这次不需要复杂的处理, 只需要知道传入ffmpeg进行转换的图像数据使用的间距, 然后传入就行, ffmpeg会自动根据这个值进行相应的处理;
图像处理, 显示中的行宽
给 AVFrame data分配内存

新APIavpicture_get_size->av_image_get_buffer_size, avpicture_fill>av_image_fill_arrays 就是增加间距以内存对齐?
  1. //旧api
  2. numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height,4);
  3. //新api
  4. av_image_fill_arrays
  5. //更简单的
  6. AVFrame * convert_frame= av_frame_alloc();
  7. av_image_alloc(convert_frame->data, convert_frame->linesize, t_ctx->codecCtx->width, t_ctx->codecCtx->height,AV_PIX_FMT_YUV420P, 4);
  8. /**
  9. The following fields must be set on frame before calling this function:
  10. (也行, 需设置)
  11. format (pixel format for video, sample format for audio)
  12. width and height for video
  13. nb_samples and channel_layout for audio
  14. */
  15. int av_frame_get_buffer        (        AVFrame *         frame, int         align  )       
复制代码
注意: av_free() 并没有释放AVFrame中data[x]指向的 数据, 仅仅是把data本身指向的数据释放了, 但其作为二级指针指向的数据跳过了, 需要手动释放, 添加 av_free(AVFrame->data[0])后问题解决;
AVDictionary

封装的一个类似Map的结构体, 主要在配置选项是使用.
注意一下, 这个使用完也要释放?
av_dict_free(&dict);
关于有哪些配置项可以参考:官方文档 Options, 以及各编解码的 Options
关于怎么用可以参考: ffmpeg.exe源码
主要结构体的分配与销毁 表

结构体初始化销毁AVFormatContextavformat_alloc_context()avformat_free_context()AVIOContextavio_alloc_context()AVStreamavformat_new_stream()AVCodecContextavcodec_alloc_context3()avcodec_close(vCodecCtx);AVFrameav_frame_alloc(); 填充数据: av_image_fill_arrays();av_frame_free()AVPacketav_init_packet(); av_new_packet()av_free_packet已过时, av_packet_unref取代之小坑指南

AVDictionary

AVDictionary 不管成功失败都要释放av_dict_free(&options);
  1. AVDictionary* options = nullptr;//一些 option
  2. av_dict_set(&options, "rtsp_transport", "tcp", 0);//over tcp
  3. // av_dict_set(&options, "buffer_size", "102400", 0); //设置缓存大小, 1080p可将值调大
  4. av_dict_set(&options, "stimeout", "3000000", 0); //设置socket 超时断开连接时间, 单位微秒
  5. av_dict_free(&options);//坑!
  6. if((ret=avformat_open_input(&inAvFormatCtx,rtspUrl,nullptr,&options)) != 0){
  7. }
复制代码
av_write_trailer

//注意即使无限直播, 结束时也要写尾, 不然还有私有数据没有被释放, 有文件则会占用文件锁!
ret = av_write_trailer(outAvFormatCtx);
内存的释放

总之有调用 alloc 相关函数名的, 就一定要调用free 释放之, 参考 FFmpeg.exe 工具的源码:
  1. static void ffmpeg_cleanup(int ret)
  2. {
  3.     int i, j;
  4.     if (do_benchmark) {
  5.         int maxrss = getmaxrss() / 1024;
  6.         av_log(NULL, AV_LOG_INFO, "bench: maxrss=%ikB\n", maxrss);
  7.     }
  8.     for (i = 0; i < nb_filtergraphs; i++) {
  9.         FilterGraph *fg = filtergraphs[i];
  10.         avfilter_graph_free(&fg->graph);
  11.         for (j = 0; j < fg->nb_inputs; j++) {
  12.             InputFilter *ifilter = fg->inputs[j];
  13.             struct InputStream *ist = ifilter->ist;
  14.             while (av_fifo_size(ifilter->frame_queue)) {
  15.                 AVFrame *frame;
  16.                 av_fifo_generic_read(ifilter->frame_queue, &frame,
  17.                                      sizeof(frame), NULL);
  18.                 av_frame_free(&frame);
  19.             }
  20.             av_fifo_freep(&ifilter->frame_queue);
  21.             if (ist->sub2video.sub_queue) {
  22.                 while (av_fifo_size(ist->sub2video.sub_queue)) {
  23.                     AVSubtitle sub;
  24.                     av_fifo_generic_read(ist->sub2video.sub_queue,
  25.                                          &sub, sizeof(sub), NULL);
  26.                     avsubtitle_free(&sub);
  27.                 }
  28.                 av_fifo_freep(&ist->sub2video.sub_queue);
  29.             }
  30.             av_buffer_unref(&ifilter->hw_frames_ctx);
  31.             av_freep(&ifilter->name);
  32.             av_freep(&fg->inputs[j]);
  33.         }
  34.         av_freep(&fg->inputs);
  35.         for (j = 0; j < fg->nb_outputs; j++) {
  36.             OutputFilter *ofilter = fg->outputs[j];
  37.             avfilter_inout_free(&ofilter->out_tmp);
  38.             av_freep(&ofilter->name);
  39.             av_freep(&ofilter->formats);
  40.             av_freep(&ofilter->channel_layouts);
  41.             av_freep(&ofilter->sample_rates);
  42.             av_freep(&fg->outputs[j]);
  43.         }
  44.         av_freep(&fg->outputs);
  45.         av_freep(&fg->graph_desc);
  46.         av_freep(&filtergraphs[i]);
  47.     }
  48.     av_freep(&filtergraphs);
  49.     av_freep(&subtitle_out);
  50.     /* close files */
  51.     for (i = 0; i < nb_output_files; i++) {
  52.         OutputFile *of = output_files[i];
  53.         AVFormatContext *s;
  54.         if (!of)
  55.             continue;
  56.         s = of->ctx;
  57.         if (s && s->oformat && !(s->oformat->flags & AVFMT_NOFILE))
  58.             avio_closep(&s->pb);
  59.         avformat_free_context(s);
  60.         av_dict_free(&of->opts);
  61.         av_freep(&output_files[i]);
  62.     }
  63.     for (i = 0; i < nb_output_streams; i++) {
  64.         OutputStream *ost = output_streams[i];
  65.         if (!ost)
  66.             continue;
  67.         av_bsf_free(&ost->bsf_ctx);
  68.         av_frame_free(&ost->filtered_frame);
  69.         av_frame_free(&ost->last_frame);
  70.         av_dict_free(&ost->encoder_opts);
  71.         av_freep(&ost->forced_keyframes);
  72.         av_expr_free(ost->forced_keyframes_pexpr);
  73.         av_freep(&ost->avfilter);
  74.         av_freep(&ost->logfile_prefix);
  75.         av_freep(&ost->audio_channels_map);
  76.         ost->audio_channels_mapped = 0;
  77.         av_dict_free(&ost->sws_dict);
  78.         av_dict_free(&ost->swr_opts);
  79.         avcodec_free_context(&ost->enc_ctx);
  80.         avcodec_parameters_free(&ost->ref_par);
  81.         if (ost->muxing_queue) {
  82.             while (av_fifo_size(ost->muxing_queue)) {
  83.                 AVPacket pkt;
  84.                 av_fifo_generic_read(ost->muxing_queue, &pkt, sizeof(pkt), NULL);
  85.                 av_packet_unref(&pkt);
  86.             }
  87.             av_fifo_freep(&ost->muxing_queue);
  88.         }
  89.         av_freep(&output_streams[i]);
  90.     }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册