qt+ffmpeg开发经验技巧

科技飞扬不打烊 2024-03-20 17:44:42

1. ffmpeg的库有链接顺序要求,如果不按照顺序来,也许在windows上没有问题,但是到了unix系统很可能有问题,报错提示云里雾里的找不到原因。顺序参照ffmpeg自带示例中的编译链接顺序即可。正确顺序是 LIBS += -L/ -lavformat -lavfilter -lavcodec -lswresample -lswscale -lavutil -lavdevice 。不是所有的库都是必须的,比如avdevice库,如果代码中没用上也没引用,可以不需要。

2. ffmpeg解码中,av_find_best_stream第五个参数传入AVCodec的话,就直接获取到了值,而不用avcodec_find_decoder来处理。

3. ffmpeg在解码的时候,avcodec_alloc_context3的参数AVCodec不是必须的,如果这里是NULL,则下面avcodec_open2的时候就必须传入。编码的时候在avcodec_alloc_context3的时候必须传入,否则下面打开失败。

4. 解码阶段,每次av_read_frame后,使用完对应的packet数据,必须调用av_packet_unref,否则内存泄漏。编码阶段,每次av_write_frame后,里面会自动调用av_packet_unref。

5. avpacket表示压缩的视音频数据(解码前和编码后),avframe表示未压缩的视音频数据(解码后和编码前),视音频文件以及传输都是使用压缩的数据,收到数据解码后才是未压缩的数据,才能直接绘制和播放。

6. 解码对应av_read_frame/avcodec_send_packet/avcodec_receive_frame,编码对应avcodec_send_frame/avcodec_receive_packet/av_write_frame。可以看到命名非常规整,编码刚好和解码顺序相反。

7. avcodec_send_packet和avcodec_receive_frame并不是一一对应的调用关系,而是一个avcodec_send_packet的调用,可能会对应多个avcodec_receive_frame函数的调用。因为解码器内部是有缓存和参考帧的,并不是每送进去一个数据包就能解码出一帧数据,可能出现送进去几个数据包,但是暂时没有数据帧解码输出的情况,也可能会出现某个时间点送进去一个数据包,然后会输出多个数据帧的情况。但是实际使用过程中你会发现你遇到过的99.9%的视音频文件或者流都是一对一关系,一个avcodec_send_packet就对应一次avcodec_receive_frame。

8. 在ffmpeg函数接口中,有不少带数字结尾的函数,比如avcodec_alloc_context3、avcodec_decode_video2、avcodec_decode_audio4,这种一般就是不断迭代的结果。比如早期版本很可能有个函数avcodec_alloc_context,但是后面又新增了更完善的函数,又希望用户能够快速找到该函数,所以直接后面加个数字用以区分。这基本上都是程序员的惯例。如果是大版本的更新,可能旧的函数会主键废弃,甚至移除。一般建议用新的函数接口。

9. 众多的音视频格式,是为了各种应用场景需求产生的,也是随着时代的发展需要应运而生的,主要就是为了方便压缩和传输,经过各种各样的算法标准。在ffmpeg中,通用的解码做法都是将任意视频格式解码转换成yuv(软解码)或者nv12(硬解码)数据,任意音频格式转换成pcm数据,yuv/nv12/pcm因为是原始的数据,所以体积非常大,优点就是非常通用,可以直接显示和播放。

10. 要做音视频格式的转换,通用做法就是视频解码成yuv,音频解码成pcm,然后再由yuv编码成其他视频格式(对应转换对象SwsContext),pcm编码成其他音频格式(对应转换对象SwrContext),万变不离其中,唯一区别就是不同格式对应的各种参数不一样。音频转换这块可能还需要重采样。

11. 无论是ffmpeg源码本身还是提供的示例demo,会发现里面大部分的代码都是在做判断,比如对象是否分配ok,大部分都提供返回值,负数表示失败,每一步执行后都需要判断是否ok再继续。其实这种写法是非常规范而且有必要的,可以避免程序意外崩溃段错误,而且就算出错也可以非常清晰的知道具体在哪一步,况且还增加了代码行数,何乐而不为。那有些人又要担心这会减慢程序运行速度,毕竟绝大部分的判断都是不会出现的,这里可以很负责任的告诉你,这个if判断,占用时间忽略不计,至少在纳秒级别。而且为了提高程序的健壮性稳定性,很有必要,尤其是C/C++这类程序。

12. 很多初学者觉得ffmpeg很复杂,函数接口太多,或者流程繁琐,为什么不提供一个简易的接口呢?其实完全可以提供,只是在耦合性这块为了将各个组件独立,所以才会拆分出来各种库和接口。其实就是抽象出来为了各种场景需求,比如解码的时候,需要先通过avformat_find_stream_info获取流信息,然后将流信息中的codecpar复制给AVCodecContext,在av_read_frame读取到数据帧后,在解码调用avcodec_send_packet/avcodec_receive_frame的时候就需要这个AVCodecContext,这个流程永远如此,为什么ffmpeg不内部处理到复制呢?以为AVCodecContext还可以用于编码,而且各种参数信息可调,同时也是为了和AVStream以及其他独立开来,因为没有AVStream他也可以完成编解码,只要给到他需要的信息就行。随着你使用的深入,会发现这种设计才是最棒的。

欢迎关注公众号:Qt实战 (专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发)

0 阅读:1

科技飞扬不打烊

简介:感谢大家的关注