为了了解FFmpeg解码的细节,使用avcodec_send_packet和avcodec_receive_frame组合,写了一个简单的例子,解码video生成yuv文件,比起用FFmpeg跟踪代码要简单很多。
但是问题是在FFmpeg下编译的ffmpeg/ffplay都可以直接跟踪调试,自己编译的因为连接的系统的动态库,所以不能跟踪到代码里面。
所以参考tools/enum_options程序,在FFmpeg源码目录新增一个目录,修改Makefile相关代码,就可以编译生成类似ffmpeg/ffplay的可执行文件,放在vscode里面非常方便的展台调试。
STEP 1:增加decode目录
bash
decode/
├── decode3.c
└── Makefile
如果是直接编译,链接系统库,没法gdb调试FFmpeg源码,试了下make tools/enum_options
可以生成enum_options程序,并且gdb可以调试FFmpeg源码,所以参考tools/Makefile,decode/Makefile如下:
STEP 2:Makefile
bash
DECODE = decode3
OUTDIRS += decode
clean::
$(RM) $(CLEANSUFFIXES:%=decode/%)
STEP 3:添加到FFmpeg/Makefile
将这个Makefile还要加入到FFmpeg/Makefile里面,下面是diff文件,为了知道每个变量的内容,增加了info打印:
shell
--- a/Makefile
+++ b/Makefile
@@ -39,13 +39,29 @@ SKIPHEADERS = compat/w32pthreads.h
# first so "all" becomes default target
all: all-yes
+# decode3
+
include $(SRC_PATH)/tools/Makefile
+include $(SRC_PATH)/decode/Makefile
include $(SRC_PATH)/ffbuild/common.mak
FF_EXTRALIBS := $(FFEXTRALIBS)
FF_DEP_LIBS := $(DEP_LIBS)
FF_STATIC_DEP_LIBS := $(STATIC_DEP_LIBS)
+$(info debug - info FF_EXTRALIBS: $(FF_EXTRALIBS))
+$(info debug - info FF_DEP_LIBS: $(FF_DEP_LIBS))
+$(info debug - info DEP_LIBS: $(DEP_LIBS))
+$(info debug - info LD_O: $(LD_O))
+$(info debug - info LDFLAGS: $(LDFLAGS))
+$(info debug - info LDEXEFLAGS: $(LDEXEFLAGS))
+$(info debug - info EXTRALIBS: $(EXTRALIBS))
+$(info debug - info ELIBS: $(ELIBS))
+$(info debug - info LD: $(LD))
+$(info debug - info EXESUF: $(EXESUF))
+$(info debug - info DECODE: $(DECODE))
+$(info debug - info LIBFUZZER_PATH: $(LIBFUZZER_PATH))
+
$(TOOLS): %$(EXESUF): %.o
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(EXTRALIBS-$(*F)) $(EXTRALIBS) $(ELIBS)
@@ -64,6 +80,8 @@ tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS)
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
+decode/decode3$(EXESUF): decode/decode3.o $(FF_DEP_LIBS)
+ $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
STEP 4:编译
这样编译就可以生成decode3,奇怪的是得先make decode
,然后再make decode3
bash
$ make decode
debug - info FF_EXTRALIBS: -lavdevice -lavfilter -lavformat -lavcodec -lpostproc -lswresample -lswscale -lavutil -lm -latomic -lxcb -lxcb-shm -lxcb-shape -lxcb-xfixes -lasound -lSDL2 -lsndio -lXv -lX11 -lXext -pthread -lm -latomic -lva -lm -latomic -lz -pthread -lm -latomic -llzma -L/usr/local/lib/x86_64-linux-gnu -llc3 -L/usr/local/lib/x86_64-linux-gnu -lopus -L/usr/local/lib -lx264 -L/usr/local/lib/x86_64-linux-gnu -lopenh264 -lz -lva -lm -latomic -lm -latomic -lm -latomic -pthread -lva-drm -lva -lva-x11 -lva -lvdpau -lX11 -lm -lva -latomic -lX11
debug - info FF_DEP_LIBS: libavdevice/libavdevice.a libavfilter/libavfilter.a libavformat/libavformat.a libavcodec/libavcodec.a libpostproc/libpostproc.a libswresample/libswresample.a libswscale/libswscale.a libavutil/libavutil.a
debug - info DEP_LIBS: libavdevice/libavdevice.a libavfilter/libavfilter.a libavformat/libavformat.a libavcodec/libavcodec.a libpostproc/libpostproc.a libswresample/libswresample.a libswscale/libswscale.a libavutil/libavutil.a
debug - info LD_O: -o
debug - info LDFLAGS: -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Wl,--as-needed -Wl,-z,noexecstack -Wl,--warn-common -Wl,-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil
debug - info LDEXEFLAGS:
debug - info EXTRALIBS:
debug - info ELIBS:
debug - info LD: @printf "LD\t%s\n" ; gcc
debug - info EXESUF:
debug - info DECODE: decode3
debug - info LIBFUZZER_PATH:
Current directory: ffmpeg ffplay ffprobe
$ make decode/decode3
debug - info FF_EXTRALIBS: -lavdevice -lavfilter -lavformat -lavcodec -lpostproc -lswresample -lswscale -lavutil -lm -latomic -lxcb -lxcb-shm -lxcb-shape -lxcb-xfixes -lasound -lSDL2 -lsndio -lXv -lX11 -lXext -pthread -lm -latomic -lva -lm -latomic -lz -pthread -lm -latomic -llzma -L/usr/local/lib/x86_64-linux-gnu -llc3 -L/usr/local/lib/x86_64-linux-gnu -lopus -L/usr/local/lib -lx264 -L/usr/local/lib/x86_64-linux-gnu -lopenh264 -lz -lva -lm -latomic -lm -latomic -lm -latomic -pthread -lva-drm -lva -lva-x11 -lva -lvdpau -lX11 -lm -lva -latomic -lX11
debug - info FF_DEP_LIBS: libavdevice/libavdevice.a libavfilter/libavfilter.a libavformat/libavformat.a libavcodec/libavcodec.a libpostproc/libpostproc.a libswresample/libswresample.a libswscale/libswscale.a libavutil/libavutil.a
debug - info DEP_LIBS: libavdevice/libavdevice.a libavfilter/libavfilter.a libavformat/libavformat.a libavcodec/libavcodec.a libpostproc/libpostproc.a libswresample/libswresample.a libswscale/libswscale.a libavutil/libavutil.a
debug - info LD_O: -o
debug - info LDFLAGS: -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Wl,--as-needed -Wl,-z,noexecstack -Wl,--warn-common -Wl,-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil
debug - info LDEXEFLAGS:
debug - info EXTRALIBS:
debug - info ELIBS:
debug - info LD: @printf "LD\t%s\n" ; gcc
debug - info EXESUF:
debug - info DECODE: decode3
debug - info LIBFUZZER_PATH:
Current directory: ffmpeg ffplay ffprobe
CC decode/decode3.o
LD decode/decode3
没有decode目录,make decode/decode3
会报错,手动创建decode目录也是可以的。
不然会报错:
bash
src/decode/decode3.c: At top level:
src/decode/decode3.c:165:1: fatal error: opening dependency file decode/decode3.d: No such file or directory
165 | }
| ^
compilation terminated.
make: *** [src/ffbuild/common.mak:81: decode/decode3.o] Error 1
decode/Makefile中已经定义了OUTDIRS += decode
,检查FFmpeg/Makefile
中的OUTDIRS
有:
bash
$(sort $(OUTDIRS)):
$(Q)mkdir -p $@
所以从这里判断make clean
,重新make
后应该可以直接make decode/decode3
,试了下果然可以了。
STEP 5:运行调试
查找decode3的符号可以发现,这个是包含了所有FFmpeg的代码,不需要加载FFmpeg的动态库,所以FFmpeg中的源码更新后直接make decode/decode3即可
。
bash
$ nm decode3 | grep -E "avcodec_send_packet|avcodec_receive_frame|av_read_frame"
000000000031dd95 T avcodec_receive_frame
000000000041a5b6 T avcodec_send_packet
000000000009ea7c T av_read_frame
ldd也可以确定这一点:
bash
$ ldd decode3
linux-vdso.so.1 (0x00007ffe27536000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f04eadf4000)
libdrm.so.2 => /usr/lib/x86_64-linux-gnu/libdrm.so.2 (0x00007f04eabe3000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f04ea9c6000)
libopus.so.0 => /usr/local/lib/x86_64-linux-gnu/libopus.so.0 (0x00007f04ea76f000)
libx264.so.152 => /usr/lib/x86_64-linux-gnu/libx264.so.152 (0x00007f04ea3ca000)
libopenh264.so.7 => /usr/local/lib/x86_64-linux-gnu/libopenh264.so.7 (0x00007f04ea0b4000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f04e9e95000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f04e9aa4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f04edef6000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f04e98a0000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f04e9440000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f04e921d000)
vscode launch.json
vscode中的 launch.json中修改program和args即可以运行调试。
dart
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/decode/decode3",
"args": [
"${workspaceFolder}/decode/256x144.mp4", "${workspaceFolder}/decode/256x144-yuv420p.yuv"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/",
"environment": [ ],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}