# ReadSourceCode-FFmpeg **Repository Path**: shunzi20220/ReadSourceCode-FFmpeg ## Basic Information - **Project Name**: ReadSourceCode-FFmpeg - **Description**: ffmpeg源码学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-01-01 - **Last Updated**: 2025-01-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 目录结构 ```shell ├── Changelog ├── compat:兼容性代码,用于支持不同平台和系统 ├── config.asm ├── config.h ├── configure ├── CONTRIBUTING.md ├── COPYING.GPLv2 ├── COPYING.GPLv3 ├── COPYING.LGPLv2.1 ├── COPYING.LGPLv3 ├── CREDITS ├── doc: 包含ffmpeg的开发文档,API文档,示例 ├── ffbuild:构建 FFmpeg 的脚本和相关文件 ├── fftools:这是一些用于测试和辅助工具的目录,如 ffmpeg,ffplay,ffprobe命令行工具的源代码 ├── INSTALL.md ├── libavcodec:AVCodec中实现了目前多媒体领域绝大多数常用的编解码格式,既支持编码,也支持解码。AVCodec除了支持MPEG4、AAC、MJPEG等自带的媒体编解码格式之外,还支持第三方的编解码器,如H.264(AVC)编码,需要使用x264编码器;H.265(HEVC)编码,需要使用x265编码器;MP3(mp3lame)编码,需要使用libmp3lame编码器 ├── libavdevice: 提供一层采集设备和播放设备的抽象,包括一些特定设备相关的接口实现,比如sdl、opengl等 ├── libavfilter:提供了一个通用的音频、视频、字幕等滤镜处理框架 ├── libavformat:实现了目前多媒体领域中的绝大多数媒体封装格式,包括封装和解封装,如MP4、FLV、KV、TS等文件封装格式,RTMP、RTSP、MMS、HLS等网络协议封装格式。 ├── libavresample: 用于音频混音、重采样的库 ├── libavutil: 这个目录包含了一些通用的实用工具和功能,它们被不同部分的 FFmpeg 使用 ├── libpostproc: 这个库包含一些后处理滤镜,用于改善视频质量 ├── libswresample:音频转换计算模块,比如操作音频采样、音频通道布局转换与布局调整 ├── libswscale:视频图像转换计算模块,比如图像缩放和像素格式转换 ├── LICENSE.md ├── MAINTAINERS ├── Makefile ├── presets:存储了一些编码器的预设配置文件,可以用于不同的编码任务。 ├── README.md ├── RELEASE ├── RELEASE_NOTES ├── tests:包含各种自动化测试脚本和测试数据,用于确保 FFmpeg 的正常运作 └── tools: ``` #### 命令 ffmpeg:编解码工具 ffplay:提供了音视频显示和播放相关的图像信息、音频的波形信息。播放可进行快进快退,逐帧播放,滤镜 ffprobe:媒体信息分析工具 ### 源码学习 #### 示例代码: doc\examples * muxing.c: 音视频流封装 * demuxing_decoding.c:音视频文件解封装:视频解码 * remuxing.c:音视频文件转封装 * avio_reading.c:avio内存数据操作 * demuxing_decoding.c:视频解码 * decode_audio.c: 解码音频 * decode_video.c: 解码视频 * filtering_video.c:视频加滤镜 其他看代码 #### 播放器:fftools\ffplay.c ```shell ffplay --help ffplay -i video.mp4 #以视频时钟为主时钟同步 ffplay -sync video -i video.mp4 #以外部时钟为主时钟同步 ffplay -sync ext -i video.mp4 ``` #### 转换器:fftools\ffmpeg.c ffmpeg参数定义见:`ffmpeg_opt.c OptionDef` ```shell #打开debug日志 ffmpeg -hide_banner -re -i video.mp4 -t 5 video-5s.mp4 -loglevel debug #简单滤镜 ffmpeg -i video.mp4 -vf "null" video.flv #复杂滤镜 ffmpeg -i video.mp4 -i image.jpg -filter_complex "[1:v]scale=176:144[logo];[0:v][logo]overlay=x=0:y=0" output.mp4 -y # 打开 debug_ts,显示pts信息 ffmpeg -hide_banner -an -i video.mp4 -t 10 video.flv -debug_ts -y # 禁用多线程读取输入文件 ffmpeg -thread_queue_size 0 -i audio.aac -thread_queue_size 0 -i video.h264 -f mp4 video.mp4 # 多个输出文件 ts,flv ffmpeg -an -i video.mp4 video.ts video.flv #调用 av_pkt_dump_log2() 打印AVPacket信息 ffmpeg -an -i video.mp4 -vframes 1 video.flv -dump -hex #丢弃音频流 ffmpeg -i video.mp4 -t 10 -pix_fmt yuv420p video-10s.yuv #速率模拟,推直播流用 re:rate emulator ffmpeg -re -i video.mp4 video.flv #pcm裸流 采样率,声道数,声道布局 ac:audio channels ffmpeg -ar 48000 -ac 2 -f s16le -i audio-5s.pcm -f mp4 audio-5s.mp4 #YUV pix_fmt:pixel foramt ffmpeg -s 1920x1080 -pix_fmt yuv420p -i video.yuv video.mp4 #设置视频码率 b:bitrate ffmpeg -i video.mp4 -b:v 200k output.flv # 限制输出源的时间 t:recording_time ffmpeg -i video.mp4 -t 10 video.flv # 限制输入源的时间 ffmpeg -t 10 -i video.mp4 video.flv # 限制输出源的帧数 vframes: video frame ffmpeg -i video.mp4 -an -vframes 10 video.flv # 设置起始时间 ss:set the start time ffmpeg -an -ss 10 -i video.mp4 video.flv # 丢弃视频 vn:video no ffmpeg -i video.mp4 -vn video.flv # 丢弃音频 vn:audio no ffmpeg -i video.mp4 -an video.flv # output_ts_offset:设置输出文件起始时间 ffmpeg -i video.mp4 -t 5 -output_ts_offset 5 video.flv ``` ```c reap_filters(...) //编码封装模块 process_input(...) //解码模块 ``` #### MP4解复用 ```sh ffmpeg -i video.mp4 out.flv -y ``` 流程, 可参考示例:`doc\examples\demuxing_decoding.c` 1. avformat_alloc_context() :申请容器上下文的实例 2. avformat_open_input() :打开输入源 3. avformat_find_stream_info() :提取流的信息。 4. av_read_frame() :读取 `AVPacket`,这个 `AVPacket` 后面会送给解码器 ##### 备忘录 * FFmpeg-release-4.3/libavutil/intreadwrite.h:定义了大小端函数。命名规则:R/W 代表 读/写, B 的全称是 big(大端)L 是 little(小端),N 是 Native(本机字节序) * FFmpeg-release-4.3/libavformat/mov.c: `mov_probe()` 探测MP4格式 * FFmpeg-release-4.3/libavformat/isom.h:`MOVContext` MP4数据结构 * FFmpeg-release-4.3/libavformat/isom.h:`MOVStreamContext` MP4 流管理器。 mp4有多个音视频流,即track box * FFmpeg-release-4.3/libavformat/mov.c: `mov_read_header()` 解析MP4头部 * FFmpeg-release-4.3/libavformat/mov.c: `mov_read_trak()` 读取MP4轨道数据 * FFmpeg-release-4.3/libavformat/mov.c:`ff_mov_demuxer` mp4 read_header * FFmpeg-release-4.3/libavformat/flvdec.c: `ff_kux_demuxer` flv read_header #### FLV解复用 ```shell ffmpeg -i video.flv out.flv -y #添加关键帧表 ffmpeg -i video.flv -c copy -flvflags add_keyframe_index video-key.flv #flv默认的音视频编码格式 ffmpeg -hide_banner -h muxer=flv ``` ##### 备忘录 * FFmpeg-release-4.3/libavformat/flvdec.c: 3种解复用 `ff_kux_demuxer`, `ff_live_flv_demuxer`, `ff_flv_demuxer`. 结构里关联了解复用有关函数 * FFmpeg-release-4.3/libavformat/flvdec.c:`FLVContext` flv的数据结构 * FFmpeg-release-4.3/libavformat/flvenc.c:`ff_flv_muxer` 定义了flv默认的编码格式 * FFmpeg-release-4.3/libavformat/flvenc.c:`flv_video_codec_ids`, `flv_audio_codec_ids` flv支持的编码格式 * [在ffmpeg中支持hevc/vp8/vp9/opus的flv格式](https://github.com/runner365/ffmpeg_rtmp_h265) #### h264解复用 ```shell #原始数据pcm,yuv,裸流h264作为输入解码用 ffmpeg -s 640x480 -pix_fmt yuv420p -f rawvideo -i video.yuv -c:v h264 -f mp4 video.mp4 ffmpeg -ar 48000 -ac 2 -f s16le -i audio.pcm -c:a aac -f mp4 audio.mp4 ffmpeg -f h264 -i video.h264 -c:v copy -f mp4 video.mp4 ffmpeg -f mp4 -i video.mp4 -c:v copy -f flv video.flv #获取Annex B格式h264 ffmpeg -i video.mp4 -an -c:v copy -bsf:h264_mp4toannexb -f h264 video.h264 #查看NUAL ffmpeg -hide_banner -i video.h264 -c copy -bsf:v trace_headers -f null - # 把一个 Frame 拆成多个分片 ffmpeg -i video.h264 -x264opts slices=4 video-slice.h264 #debug ffmpeg -i video.h264 out.flv -y #设置GOP ffmpeg -i video.h264 -g 10 video-gop.h264 -y #转成低分辨率 ffmpeg -i video.h264 -vf "scale=w=320:h=180" video-scale.h264 -y #删除SEI ffmpeg -i video.h264 -c copy -bsf:v 'filter_units=remove_types=6' video-nosei.h264 ``` ##### 备忘录 * FFmpeg-release-4.3/libavcodec/rawdec.c:`RawVideoContext` 伪解码器 * FFmpeg-release-4.3/libavformat/h264dec.c: h264解复用宏 `FF_DEF_RAWVIDEO_DEMUXER` * ffmpeg解复用3步骤 1. 先调 `probe()` 函数确定输入文件格式(`iformat`),再调 `iformat` 里面的 `read_header()` 函数来读取头部。 2. 调 `avformat_find_stream_info()` 解析出来更多的媒体信息。 3. 调 `av_read_frame()` 读取 `AVPacket` * FFmpeg-release-4.3/libavformat/rawdec.c: `ff_raw_video_read_header` 读取头部信息 * FFmpeg-release-4.3/libavformat/rawdec.c: `ff_data_demuxer` * FFmpeg-release-4.3/libavcodec/h264_parser.c: h264解析模块 * FFmpeg-release-4.3/libavcodec/h264_parser.c:`h264_find_frame_end()` 找帧结束位置 * FFmpeg-release-4.3/libavcodec/h264_parser.c: `ff_h264_parser` 关联解析调用的函数 * FFmpeg-release-4.3/libavcodec/avcodec.h:`AVCodecParserContext` 解析用到的通用数据结构 * FFmpeg-release-4.3/libavcodec/h264_parser.c:`H264ParseContext` h264解析器数据结构/上下文 * FFmpeg-release-4.3/libavcodec/parser.h: `ParseContext` 解析器数据结构/上下文 * FFmpeg-release-4.3/libavcodec/h264_parser.c: `parse_nal_units()`解析NALU * FFmpeg-release-4.3/libavcodec/h2645_parse.c:`ff_h2645_extract_rbsp()`提取rbsp数据 * FFmpeg-release-4.3/libavcodec/h264_ps.c:`ff_h264_decode_seq_parameter_set()` 解析 SPS NALU * FFmpeg-release-4.3/libavcodec/h264_ps.c:`ff_h264_decode_picture_parameter_set()` 解析 PPS NALU * FFmpeg-release-4.3/libavcodec/h264dsp.c:`ff_h264dsp_init() `里面有跟数字信号处理相关的函数,例如环路滤波,DCT反变换 * FFmpeg-release-4.3/libavcodec/arm/startcode_armv6.S:汇编优化代码 #### aac解复用 ```shell #debug ffmpeg -i audio.aac out.flv -y ``` ##### 备忘录 * FFmpeg-release-4.3/libavformat/aacdec.c:`ff_aac_demuxer` aac解复用数据结构,指明了会调用哪些函数 #### concat解复用 ```sh #多个文件拼接 ffmpeg -f concat -safe 0 -i mylist.txt -c copy -f mp4 all_in_one.mp4 -y #mylist.txt内容如下: file './video0.mp4' file './video1.mp4' file './video2.mp4' #不同格式的拼接 ffmpeg -i input1.mp4 -i input2.webm -i input3.mov \ -filter_complex "[0:v:0][0:a:0][1:v:0][1:a:0][2:v:0][2:a:0]concat=n=3:v=1:a=1[outv][outa]" \ -map "[outv]" -map "[outa]" output.mkv ``` ##### 备忘录 * FFmpeg-release-4.3/libavformat/concatdec.c:`ff_concat_demuxer` concat解复用数据结构 #### MP4复用/封装 ```sh #debug ffmpeg -re -stream_loop 10 -i video.mp4 -c copy -f mp4 out.mp4 ``` ##### 备忘录 * FFmpeg-release-4.3/libavformat/movenc.c: `ff_mp4_muxer` mp4封装的数据结构 * FFmpeg-release-4.3/doc/examples/muxing.c : 封装流程示例 1. `avformat_alloc_output_context2()` 申请 `Muxer`上下文实例的内存,并且对 `Muxer`上下文的实例进行初始化,就是把一些字段设置一下默认值 2. `avformat_new_stream()` 往 `Muxer` 里面添加一个新的数据流,例如 MP4 支持多个音频流或者视频流,其他格式也类似。 3. `avio_open2()` 正式打开输出文件,或者打开网络流,建立 TCP 链接,建立 RTMP 链接等等。 4. `avformat_write_header()` 写入头部信息。 5. `av_interleaved_write_frame()`对编码后的 `AVPacket` 进行封装,然后写入文件,或者推送到网络上。 6. `av_write_trailer()` 写入尾部信息 #### tee复用多路输出 ```sh # 设置多路输出 ffmpeg -re -i video.mp4 -map 0 -c:v h264 ^ -f tee "[f=flv]rtmp://192.168.1.102/live/livestream|[f=flv]rtmp://192.168.1.103/live/livestream" ffmpeg -re -i video.mp4 -map 0 -c:v h264 ^ -f tee "[f=mpegts]tcp://192.168.1.102:1234|[f=mpegts]udp://192.168.1.103:1234" ffmpeg -re -i video.mp4 -map 0 -c:v h264 ^ -f tee "[f=mp4]./out.mp4|[f=flv]rtmp://192.168.1.102/live/livestream" ffmpeg -re -i video.mp4 -map 0 -c:v h264 ^ -f tee "[f=mp4]./out.mp4|[f=mpegts]./out.ts" #视频截图 ffmpeg -i video.mp4 -frames 10 ./%04d.jpeg ``` ##### 备忘录 * FFmpeg-release-4.3/libavformat/tee.c: `ff_tee_muxer ` tee复用的数据结构 #### Filter滤镜 ```shell # 把video-scale.mp4叠加在video.mp4的左上角 ffmpeg -an -i video.mp4 -an -i video-scale.mp4 -filter_complex "[0:v][1:v]overlay=18:20" out.mp4 # 第5秒开始叠加 ffmpeg -an -i video.mp4 -an -i video-scale.mp4 -filter_complex "[1:v]setpts=5/TB+PTS[second];[0:v][second]overlay=18:20" out.mp4 ``` ### 参考资料 《FFmpeg从入门到精通》-刘歧 赵文杰著 https://blog.csdn.net/wushakun/category_12588909.html [vscode远程debug调试ffmpeg](https://blog.csdn.net/qq_22655153/article/details/142622524) ffmpeg examples解读: https://juejin.cn/user/2418581311593917/posts https://juejin.cn/user/1222312662671934/posts [ffplay 音视频同步的源码分析](https://blog.csdn.net/lyy901135/article/details/95307111) [**FFMPEG**原理](https://ffmpeg.xianwaizhiyin.net/) 《深入理解FFMPEG》 https://zhuanlan.zhihu.com/p/562692508