草庐IT

ffmpeg解复用编解码 常用API大全给出详细中文解释

杀神李 2023-06-15 原文

int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags);

 将你给出的条目设置进入你给到的 pm 中 如果条目存在 则覆盖他 

小提示:如果AV_DICT_DONT_STRDUP_KEY宏和AV_DICT_DONT_STRDUP_VAL被设置了 这些参数会在出错时释放 

警告:添加一个全新的条目到pm会使所有已存在的条目失效 可以使用av_dict_get得到

参数 pm:一个指向AVDictionary结构体的二重指针 如果*pm为空 那么一个AVDictionay结构体会被分配然后使*pm等于他

参数 key:添加进入*pm的key值 (类似于词典 key-value的形式去设置一些参数)到底是会发生覆盖还是添加一个新key取决于你设置的第三个参数 flags

参数 value 添加进入*pm的value值 (类似于词典 key-value的形式去设置一些参数)到底是会发生覆盖还是添加一个新value 取决于你设置的第三个参数 flags 如果传递一个空值将会导致已经存在的条目被删除 

返回值: >=0表示成功 否则表示失败

首先讲这个的原因是许多ffmpeg 函数最后都能传入一个AVDictionary来做一些设置

tips:如何通过AVDictionary 设置ffmpeg各种参数 详见另一篇文章 FFMPEG Tips (5) 如何利用 AVDictionary 配置参数_Jhuster的专栏的技术博客_51CTO博客

 void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));

设置超时回调 特别是网络流 经常是会超时的 这很常见 所以基本是需要设置的 这个东西

回调函数必须是线程安全的 即使你的程序运行在单线程环境下也必须是线程安全的 因为一些解码器内部工作时是多线程的 

 AVFormatContext *avformat_alloc_context(void);

 分配一个AVFormatContext结构体

avformat_free_context()可以用来释放这个结构体和任何一切AVFormatContext内分配的东西比如AVIOContext

 int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);

打开输入流并且读他的头部 编解码器此时并未打开 打开的流必须通过avformat_close_input()来关闭 通过读头的过程 解复用器就能正常工作了 以后就可以通过av_read_frame来读取AVPacket

参数 ps:指向用户刚刚通过avformat_alloc_context函数分配得来的AVFormatContext 如果用户还没通过avformat_alloc_context也没事内部会分配内存并写入ps

参数 url:打开流的URL

参数fmt:如果非空 则强制指定特定的输入格式 否则输入格式由ffmpeg内部确定

参数 options:包含了AVFormatContext和解复用器私有参数的dictionary 在返回时 这个参数会被销毁并且被替换为包含未找到选项的dictionary 也就是说如果你乱填一些ffmpeg不支持的选项 ffmpeg会直接给你返回回来  也许是空的

返回值:返回0表示成功 返回AVERROR表示失败

tips:如果你想使用自定义io输入 提前分配avio结构体 并且设置他的pb成员 关于如何自定义io输入模式 请看

ffmpeg avio 自定义读取AVPacket(aac数据)并写入文件_杀神李的博客-CSDN博客

 int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

函数内部会把整个解复用解码流程走一遍 读取数个AVPacket来拿到复用格式中流的信息 比如到底有几条流 音频流还是视频流还是字幕流 这些流是用的什么格式 比如h.264 h.265 aac 这些流适合用什么编码解码解码 

这个函数对于没有头的文件格式是非常有效的 比如MPEG

这个方法即使在MPEG-2重复帧模式下也会计算真实帧率

文件指针位置并不会因为这个函数读取了数个AVPacket而发生变化

读取过后的数个AVPacket可能会被缓冲用作之后的处理 比如av_read_frame

参数 ic:你刚刚打开的AVFormatContext

参数 options:如果非null,则为指向dictionary的指针的ic.nb_streams长数组,其中第i个成员包含对应于第i个流的编解码器选项。

返回值:>=0表示成功 AVERROR_xxx表示失败

这个方法不会保证打开所有的编解码器去读取数个AVPacket 所以在返回时 options非空是正常的

接下来 让用户以某种方式决定需要什么信息,这样ffmpeg内部就不会浪费时间去获取用户不需要的东西

 int av_find_best_stream(AVFormatContext *ic,
                        enum AVMediaType type,
                        int wanted_stream_nb,
                        int related_stream,
                        AVCodec **decoder_ret,
                        int flags);

 找到用户最希望的流的索引 因为没有索引的话 你后面通过av_read_frame读出来的AVPaket你就无法分辨到底是音频的Packet还是视频的Packet 所以必须通过这个函数去找到流索引 AVPacket里有一个字段来表明他是属于哪一个索引的

参数 ic:上文的AVFormatContext

参数 AVMediaType type:你希望找到的流的类型 比如音频视频字幕等等

参数 wanted_stream_nb:用户希望的流的索引 ffmpeg尽量满足 实在满足不了 也只能返回你不希望的 设置-1自动选择            

参数 related_stream:尝试去找到与你指定type相关的流 如果没有的话 设置-1

参数 decoder_ret:如果非空 返回你指定的流的编解码器 后面就可以直接打开了 不然的话你需要手动根据流信息去查找 通过调用avcodec_find_decoder这个函数

参数 flags:一些选项 

返回值:你指定的type的流的索引 返回AVERROR_STREAM_NOT_FOUND表示压根没找到你指定的类型 如果找到了流但是没有对应的解码器 一样是会返回AVERROR_STREAM_NOT_FOUND

tips:如果返回成功 并且decoder_ret非空 那么ffmpeg保证给你返回的decoder是有效的 是可以直接打开的

 AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

分配编解码器上下文结构体内存 然后设置为默认值 这个编解码器上下文必须通过avcodec_free_context()来释放

参数 codec:如果非空 特定于你传入的编解码器的内存被分配并且初始化 如果你传了codec 但是后面又使用avcodec_open2来打开其他编解码器是非法的 

如果空 然后特定于编解码器的默认值将不会被初始化,这可能会导致次优的默认设置,这可能会导致默认设置不理想(这主要对编码器来说很重要,例如libx264)。

返回值:充满默认值的编解码器上下文 或者空表示失败

 int avcodec_parameters_to_context(AVCodecContext *codec,
                                  const AVCodecParameters *par);

根据提供的编解码器参数的值填充编解码器上下文。在编解码器中,任何在par中有对应字段的已分配字段将被释放并替换为par中对应字段的副本。在编解码器中没有对应字段的字段将不会被修改。通过这个函数后

参数 codec:编解码器上下文

参数 par: 流中关于编解码器的信息 在上文 find_stream_info时候就已经填充了AVStream里的AVCodecParametes 所以这里可以直接传入 

返回值:>=0表示成功 或者返回AVERROR表示失败

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

初始化编解码器上下文来使用编解码器 在使用这个方法之前 确保你已经使用avcodec_alloc_context3()来分配内存了 通过以下四个函数来找到编解码器是非常简单的

avcodec_find_decoder_by_name()

avcodec_find_encoder_by_name()
 avcodec_find_decoder() 

avcodec_find_encoder()

警告:这个方法不是线程安全的

在调用这个方法之前请不要使用解码流程 如avcodec_send_packet和av_receive_frame

参数 avctx:需要初始化的编解码器上下文

参数 codec:需要打开的编解码器 如果之前你通过avcodec_alloc_context3这个函数来指定了需要打开什么编解码器 那么你就不能传入其他的编解码器

参数 options:你想要对上下文和编解码器所做的一些设置 在返回时没有找到的选项会给你返回出来 比如你乱填一些ffmpeg不支持的选项

返回值: 0表示成功 负数表示发生错误

AVFrame *av_frame_alloc(void);

 分配一个AVFrame的内存并且初始化为默认值 通过这个函数分配的内存必须通过av_frame_free()来释放掉

返回值:AVFrame

注意这个函数只会分配AVFrame本身的内存而不会去分配实际的data buffer的内存 缓冲区内存必须通过av_frame_get_buffer函数分配 或者手动分配 上一张图就懂我在说什么了 

具体实现类似于智能指针 内部会有一个引用计数去维护他 ffmpeg内存管理详见其他文章

 


AVPacket *av_packet_alloc(void);

分配一个AVPacket的内存然后初始化为默认值 分配后的内存必须通过 av_packet_free来释放掉

返回值:AVPacket

注意这个方法和 av_frame_alloc一样只会初始化他本身的字段 而不会去初始化他的data buffer 如果你想同时初始化data buffer你应该选择 av_new_packet

 void av_init_packet(AVPacket *pkt);

初始化AVPacket的可选字段 

注意这个不会去改变 data和size字段 这两个字段需要分别初始化

int av_read_frame(AVFormatContext *s, AVPacket *pkt);

返回流中的下一个packet 他并不会保证这个AVPacket对于解码器是有效的 也就是说可能这个AVPacket你打开的编解码器根本解码不了 他会把流中的信息变成一个一个的AVPacket 你每调用一次返回一个AVPacket 并且他不会省略AVPacket中没用的信息(比如h.264中的sps pps sei等信息)来给编解码器提供最多的信息 

如果传入的pkt的buf是NULL的 那么在你下一次调用av_read_frame和直到你调用avformat_close_input的时候他都是有效的 并且是永远有效 buf不会被释放也不会被更改 

所以在你下一次调用av_read_frame的时候确保你已经使用av_packet_unref来释放掉了他的buf 

对于视频来说 一个AVPacket恰好就包含了完整的一帧数据(并且可能夹杂了一些其他的信息)关于这个还挺重要的 以后再写文章描述吧 包括AVPacket和NALU之间的关系

对于音频来说 如果他的帧长是固定的 那么有可能一个AVPacket包含了几个帧 如果他的帧长是可变的 那么一个AVPacket只包含一个音频帧 

tips:音频帧这个概念其实挺模糊的 他更像是一种规定 在发送方与接收方之间的一种约定 以后一起写个文章描述

其中 pkt的pts(显示时间戳) dts(解码时间戳) duration(建议的持续时间)这三个字段是基于AVStream中的time_base字段来计算的 如果你的视频中含有B帧 那么pkt中的pts是无效的 所以你最好是依赖dts来进行解码负荷

参数 s:上下文

参数pkt:经过你av_packet_alloc之后的pkt

返回值:0表示ok 负数表示失败

 void av_packet_free(AVPacket **pkt);

 释放packet内存 如果他拥有buf的引用计数 那么会首先释放掉他的引用计数 

AVPacket *av_packet_clone(const AVPacket *src); 

直接使用=是发生的浅拷贝 内部引用计数并不会加1 如果使用这个函数则是深拷贝 内部引用计数加1  

 int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

 给解码器提供原始的packet数据 就是把packet数据送去解码器解码 在内部,这个调用将复制相关的AVCodecContext字段,这可能会影响每个包的解码,并在包实际解码时应用它们 为什么可能会影响 举一个例子 比如

AVCodecContext.skip_frame字段 他复制过后就会去填充这个字段 这个字段指示解码器丢弃使用此函数发送的包所包含的帧。比如一个包可能包含多个音频帧 其中一些可能会丢弃

注意:在你传入的pkt的data buf字段必须是字节对齐的 不然的话就有可能会访问越界 因为一些比特流读取器是按照每一次4字节或者每一次8字节来读取的 如果字节不对齐 则有可能导致访问越界 

注意:在使用这个API的时候请不要和一些老的API混用同一个AVCodecContext 不然有可能会造成一些不可预料的结果 建议老API就不要再使用了

tips:在使用本API之前确保你已经使用avcodec_open2来打开了编解码器

参数 avctx:上下文

参数 avpkt:通常是一个视频帧或者是几个音频帧 这个函数并不会去改变你pkt的buf数据 但是他会创造一个引用计数去指向你的data buf 如果你传入的buf没有引用计数 就是说你传入了一个没有数据的packet 那么他会创造一个引用计数 不像老一代的API 这个data buf中的数据是会被完全读取的 如果AVPacket中包含了多个帧 那么在你下一次send packet之前 你是需要去调用多次avcodec_receive_frame去读取frame的而不是一次 这个参数可以是NULL 如果你传入NULL 那么就认为你传入了一个flush packet 代表已经读到了流的结尾 第一次发flush packet会成功 以后发的都返回AVERROR_EOF,如果你发送flush packet时 解码器中还有frame没有读取完成 那么这些东西会在下一次av_receive_frame的时候返回 

返回值:0表示成功 

 AVERROR(EAGAIN):现在解码器的状态不可以发送AVPacket 也就是说你还没有调用av_receive_frame去获取输出 那么你就不能再传入AVPacket 

 AVERROR_EOF:解码器已经被flush了 没有新的包可以被发送进去了 如果你发送了多个flush packet 那么也是返回这个错误

AVERROR(EINVAL):编解码器未打开 需要打开

AVERROR(ENOMEM):未知原因导致packet无法加入内部的队列 或者是一些解码错误导致的

 int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

返回解码后的视频帧或音频帧 

参数 avctx:编解码器上下文

参数 frame:内部会给你申请一个data buf并添加引用计数去装解码后的音频帧或者视频帧 如果你传入的frame拥有data buf的引用计数 那么在操作之前 会首先抹去你的引用计数

返回值:0表示成功

 AVERROR(EAGAIN):现在解码器的状态不可以接受AVFrame 也就是说你还没有调用av_send_packet去输入 那么你就不能再获取AVFrame 

 AVERROR_EOF:解码器已经被flush了 没有新的包可以被接收了 

AVERROR(EINVAL):编解码器未打开 需要打开

AVERROR_INPUT_CHANGED:当设置flag AV_CODEC_FLAG_DROPCHANGED时适用 表示当前frame的一些属性发生了变化

 

有关ffmpeg解复用编解码 常用API大全给出详细中文解释的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  3. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  4. 亚特兰蒂斯的回声(中文版): chatGPT 的杰作 - 2

    英文版英文链接关注公众号在“亚特兰蒂斯的回声”中踏上一段难忘的冒险之旅,深入未知的海洋深处。足智多谋的考古学家AriaSeaborne偶然发现了一件古代神器,揭示了一张通往失落之城亚特兰蒂斯的隐藏地图。在她神秘的导师内森·兰登教授的指导和勇敢的冒险家亚历克斯·默瑟的帮助下,阿丽亚开始了一段危险的旅程,以揭开这座传说中城市的真相。他们的冒险之旅带领他们穿越险恶的大海、神秘的岛屿和充满陷阱和谜语的致命迷宫。随着Aria潜在的魔法能力的觉醒,她被睿智勇敢的QueenNeria的幻象所指引,她让她为即将到来的挑战做好准备。三人组揭开亚特兰蒂斯令人惊叹的隐藏文明,并了解到邪恶的巫师马拉卡勋爵试图利用其古

  5. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  6. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  7. ruby-on-rails - Mandrill API 模板 - 2

    我正在使用Mandrill的RubyAPIGem并使用以下简单的测试模板:testastic按照Heroku指南中的示例,我有以下Ruby代码:require'mandrill'm=Mandrill::API.newrendered=m.templates.render'test-template',[{:header=>'someheadertext',:main_section=>'Themaincontentblock',:footer=>'asdf'}]mail(:to=>"JaysonLane",:subject=>"TestEmail")do|format|format.h

  8. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  9. ruby-on-rails - 在 Ruby (on Rails) 中使用 imgur API 获取图像 - 2

    我正在尝试使用Ruby2.0.0和Rails4.0.0提供的API从imgur中提取图像。我已尝试按照Ruby2.0.0文档中列出的各种方式构建http请求,但均无济于事。代码如下:require'net/http'require'net/https'defimgurheaders={"Authorization"=>"Client-ID"+my_client_id}path="/3/gallery/image/#{img_id}.json"uri=URI("https://api.imgur.com"+path)request,data=Net::HTTP::Get.new(path

  10. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

随机推荐