ffmpeg configure 研究2:分析屏幕输出及文件输出的具体过程

发布于:2025-02-21 ⋅ 阅读:(22) ⋅ 点赞:(0)

author: hjjdebug
date: 2025年 02月 17日 星期一 16:57:55 CST
description: ffmpeg configure 研究2
分析屏幕输出及文件输出的具体过程


0. 执行./configure 命令


在家目录下创建目录t1, 把ffmpeg目录下的 configure 文件copy进去.
在 t1 目录下执行:
cd ~/t1
./configure

sed: can’t read ./libavfilter/allfilters.c: No such file or directory
sed: can’t read ./libavdevice/alldevices.c: No such file or directory
sed: can’t read ./libavdevice/alldevices.c: No such file or directory
sed: can’t read ./libavformat/allformats.c: No such file or directory
sed: can’t read ./libavformat/allformats.c: No such file or directory
sed: can’t read ./libavcodec/allcodecs.c: No such file or directory
sed: can’t read ./libavcodec/allcodecs.c: No such file or directory
sed: can’t read ./libavcodec/parsers.c: No such file or directory
sed: can’t read ./libavcodec/bitstream_filters.c: No such file or directory
sed: can’t read ./libavcodec/hwaccels.h: No such file or directory
sed: can’t read ./libavformat/protocols.c: No such file or directory

install prefix /usr/local
source path .
C compiler gcc
C library glibc
ARCH x86 (generic)
big-endian no
runtime cpu detection yes
standalone assembly yes
x86 assembler nasm
MMX enabled yes

运行./configure 后
会惊奇的发现在t1目录下生成了很多文件及目录.
$ tree
.
├── config.asm
├── config.h
├── configure # sh文件本身
├── doc
│ └── config.texi
├── ffbuild
│ ├── config.fate
│ ├── config.log
│ ├── config.mak
│ └── config.sh
├── libavcodec
│ ├── bsf_list.c
│ ├── codec_list.c
│ └── parser_list.c
├── libavdevice
│ ├── indev_list.c
│ └── outdev_list.c
├── libavfilter
│ └── filter_list.c
├── libavformat
│ ├── demuxer_list.c
│ ├── muxer_list.c
│ └── protocol_list.c
├── libavutil
│ └── avconfig.h
├── Makefile
└── tests
└── api

下面我们要阐明3个问题, 出错信息是从哪里来的? 屏幕打印信息从哪里来的? 输出文件是怎样生成的?

1. sed: can’t read 信息是从哪里来的 ?


对应 ./configure 3912 处代码

FILTER_LIST=$(find_filters_extern libavfilter/allfilters.c)
OUTDEV_LIST=$(find_things_extern muxer AVOutputFormat libavdevice/alldevices.c outdev)
INDEV_LIST=$(find_things_extern demuxer AVInputFormat libavdevice/alldevices.c indev)
MUXER_LIST=$(find_things_extern muxer AVOutputFormat libavformat/allformats.c)
DEMUXER_LIST=$(find_things_extern demuxer AVInputFormat libavformat/allformats.c)
ENCODER_LIST=$(find_things_extern encoder AVCodec libavcodec/allcodecs.c)
DECODER_LIST=$(find_things_extern decoder AVCodec libavcodec/allcodecs.c)

这里我们看到是调用了2个函数:

1.1 find_filters_extern()

{
    file=$source_path/$1
    sed -n 's/^extern AVFilter ff_[avfsinkrc]\{2,5\}_\([[:alnum:]_]\{1,\}\);/\1_filter/p' $file
}

1.2 find_things_extern()

{
    thing=$1
    pattern=$2
    file=$source_path/$3
    out=${4:-$thing}
    sed -n "s/^[^#]*extern.*$pattern *ff_\([^ ]*\)_$thing;/\1_$out/p" "$file"
}

很简单,在控制台上执行一下sed命令就知道这些list 是什么内容了.
就是在指定的文件中查找pattern, 满足条件的行被替换内容后输出.保存到不同的变量中
由于t1目录只有一个configure 文件, 没有添加诸如 libavfilter/allfilters.c 文件,所以sed 报错了.

2. 屏幕输出信息的来源


对应 ./configure 7285 处代码. 嗯,bashdb 调试执行太慢了,不能用bashdb 调试了,
在3912设断点等一会能到达,在7285处设断点就慢的没法忍受了.只能放弃,你可以用bash -x -v ./configure 来记录执行的过程

echo “install prefix $prefix”
echo “source path $source_path”
echo “C compiler $cc”
echo “C library $libc_type”

2.1,查找 prefix 变量在哪里定义的?

有的变量定义是不太好找的. prefix 变量定义就是这样的.
在bash 中,变量的赋值是通过 a=b 方式进行的,如果是直接定义,可以通过字符串来查找.
但如果变量在函数中定义, 传进去的只是参数, 对参数的引用通过$i 或 $var 进行,则赋值就不能通过字符串直接查找了.
prefix在configure 代码中没有发现直接定义, 即没有prefix=xxx 形式的行.
在3755行 有 prefix_default=“/usr/local”, 与prefix 很接近.
在PATH_LIST 中有 prefix 字符串
由set_default P A T H L I S T , s e t d e f a u l t 函数引用了 PATH_LIST, set_default函数引用了 PATHLIST,setdefault函数引用了PATH_LIST变量,它设置了prefix变量, 具体过程参见函数

2.2 函数名称: set_default

输入参数: 列表
输出: 把列表作为变量名称,为其赋值.
描述: 把xxx_default变量的值送给xxx变量


set_default(){
for opt; do # 对传来的参数列表, 把其default值作为其值, 参数列表都是输出值
eval : ${$opt:=$${opt}_default}
done
}
当枚举到 $opt=prefix
则进行变量扩展后为

  • eval : ‘KaTeX parse error: Expected '}', got 'EOF' at end of input: {prefix:=prefix_default}’
    再执行一次 eval 命令完成赋值
    赋值语句 x x x : = y y y , 当 {xxx:=yyy}, 当 xxx:=yyy,xxx没有值或为空时,赋值给yyy, 2次扩展.

sh 脚本的难点是函数, 函数也等价于一个命令
configure 有7000多行代码, 包含180个函数, 知道了这180个函数的意思, 也就不怕configure 文件了.
关于具体的函数,以后会慢慢介绍几个关键函数.

2.3, 查找其它变量的定义.

先用grep 查找直接定义,
若找不到则可能是在函数中赋值的,如果代码不长运行较快可以用bashdb辅助查找,也可用bash -x -v输出log记录信息帮助查找


3 configure 中创建的文件具体创建过程是怎样的?


关注config.h 是怎样生成的, 顺便关注一下其它的文件
config.h
代码在7581-7628
向缓冲文件$TMPH 中打印头部信息

cat > $TMPH <<EOF
/* Automatically generated by configure - do not modify! */
#ifndef FFMPEG_CONFIG_H
#define FFMPEG_CONFIG_H
#define FFMPEG_CONFIGURATION "$(c_escape $FFMPEG_CONFIGURATION)"
#省略部分代码....
#define HAVE_MMX2 HAVE_MMXEXT
#define SWS_MAX_FILTER_SIZE $sws_max_filter_size
EOF
 
#省略部分代码....
print_config ARCH_   "$config_files" $ARCH_LIST
print_config HAVE_   "$config_files" $HAVE_LIST
print_config CONFIG_ "$config_files" $CONFIG_LIST       \
                                     $CONFIG_EXTRA      \
                                     $ALL_COMPONENTS    \
echo "#endif /* FFMPEG_CONFIG_H */" >> $TMPH
echo "endif # FFMPEG_CONFIG_MAK" >> ffbuild/config.mak

cp_if_changed $TMPH config.h

3.1 第一步,要找到变量定义.

config_files=“$TMPH ffbuild/config.mak doc/config.texi” 3个文件
ARCH_LIST=“aarch64
alpha
arm
avr32
avr32_ap
avr32_uc
bfin
ia64
m68k
mips
mips64
parisc
ppc
ppc64
s390
sh4
sparc
sparc64
tilegx
tilepro
tomi
x86
x86_32
x86_64”

ARCH_LIST 是24种cpu

3.2. 读懂函数代码.


3.3 函数名称: print_config

输入参数: $1, 前缀
          $2, 输出的文件名称列表
		  $v, 是$@的枚举值, 则\${$v:-no}是其值,默认为no
输出参数: 无
描述.  对$@ 执行显示其配置值信息并通过awk 向文件列表中输出信息


print_config(){
    pfx=$1
    files=$2        #文件列表
    shift 2
    map 'eval echo "$v \${$v:-no}"' "$@" |
    awk "BEGIN { split(\"$files\", files) }   #把文件列表分割为文件
        {
            c = \"$pfx\" toupper(\$1);       #c变量是前缀和第一列值(即枚举字符串)
            v = \$2;                         #v变量是 第2列值,它是yes 或者 no 
            sub(/yes/, 1, v);                # 将yes 或no 变成1和0
            sub(/no/,  0, v);
            for (f in files) {               # 向文件列表中输出内容
                file = files[f];
                if (file ~ /\\.h\$/) {                     # .h 文件, 打印 #define xxx 1 形式
                    printf(\"#define %s %d\\n\", c, v) >>file;
                } else if (file ~ /\\.asm\$/) {            # .asm 文件, 打印 %%define xxx 1形式
                    printf(\"%%define %s %d\\n\", c, v) >>file;
                } else if (file ~ /\\.mak\$/) {            # .mak 文件, 打印xxx 或者!xxx
                    n = -v ? \"\" : \"!\";                 # 赋值n 为空或 ‘!'
                    printf(\"%s%s=yes\\n\", n, c) >>file;
                } else if (file ~ /\\.texi\$/) {           # .texi 文件, 打印pre@set xxx yes的形式
                    pre = -v ? \"\" : \"@c \";             # 赋值pre为空或@c 
                    yesno = \$2;
                    c2 = tolower(c);
                    gsub(/_/, \"-\", c2);
                    printf(\"%s@set %s %s\\n\", pre, c2, yesno) >>file;
                }
            }
        }"
}

3.4 函数名称: cp_if_changed

输入参数:$1,$2 两个文件名称
输出参数:无
函数功能: 比较2个文件$1,$2是否相同,相同给出提示 "$2 is nuchanged",返回, 不同则进行copy,不提示

cp_if_changed(){ 
    cmp -s "$1" "$2" && { test "$quiet" != "yes" && echo "$2 is unchanged"; } && return
    mkdir -p "$(dirname $2)"
    cp -f "$1" "$2"
}

其中9个list 都是由如下命令生成
print_enabled_components libavfilter/filter_list.c AVFilter filter_list $FILTER_LIST
print_enabled_components libavcodec/codec_list.c AVCodec codec_list $CODEC_LIST
print_enabled_components libavcodec/parser_list.c AVCodecParser parser_list $PARSER_LIST
print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
print_enabled_components libavformat/demuxer_list.c AVInputFormat demuxer_list $DEMUXER_LIST
print_enabled_components libavformat/muxer_list.c AVOutputFormat muxer_list $MUXER_LIST
print_enabled_components libavdevice/indev_list.c AVInputFormat indev_list $INDEV_LIST
print_enabled_components libavdevice/outdev_list.c AVOutputFormat outdev_list $OUTDEV_LIST
print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST

望文生义也可以猜到,例如它从$FILTER_LIST 参数中生成libavfilter/filter_list.c 文件,
使用AVFilter,filter_list 两个关键字

3.5 函数名称: print_enabled_components

输入参数: 第一参数输出文件名, 第二参数结构名称,
          第三参数一个格式参考关键字,其它为模块名称作为输出文件内容
输出参数: 无
描述: 从一堆信息中,合成一个输出文件, 用printf 打印的

print_enabled_components(){
    file=$1            #输出文件
    struct_name=$2     #结构名称
    name=$3            #名称
    shift 3
    echo "static const $struct_name * const $name[] = {" > $TMPH   # 定义结构名称,向临时文件copy
    for c in $*; do
        if enabled $c; then              # 如果变量是enabled , 对输出打印
            case $name in                # 判断输出要求什么格式, 可以进行修饰, 重新赋值给c
                filter_list)
                    eval c=\$full_filter_name_${c%_filter}
                ;;
                indev_list)
                    c=${c%_indev}_demuxer
                ;;
                outdev_list)
                    c=${c%_outdev}_muxer
                ;;
            esac
            printf "    &ff_%s,\n" $c >> $TMPH           # 打印变量
        fi
    done
    if [ "$name" = "filter_list" ]; then
        for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do
            printf "    &ff_%s,\n" $c >> $TMPH
        done
    fi
    echo "    NULL };" >> $TMPH
    cp_if_changed $TMPH $file       #跟输出文件不同则copy
}

4 configure 对 Makefile 的影响

Makefile 其时是没有改变的, 但Makefile 有
include ffbuild/config.mak
参考 print_config() 函数,
它同时向3个文件

config_files="$TMPH ffbuild/config.mak doc/config.texi" 打印信息. 
其中$TMPH 就是config.h的缓冲文件,

网站公告

今日签到

点亮在社区的每一天
去签到