• 重新配置chrome中ffmpeg插件


    FFmpegDemuxer

    我们从demux开始说起,Chrome中非MSE源都是走的ffmpeg_demuxer,具体实现是在src/media/filters/ffmpeg_demuxer.cc里面进行的。

    ffmpeg_demuxer先借助buffer数据初始化一个format_context,记录视频格式信息,然后调avformat_find_stream_info得到所有的streams,一个stream包含一个track,循环streams,根据codec_id区分audio、video、text三种track,记录每种track的数量,设置播放时长duration,用fist_pts初始化播放开始时间start_time。

    同时实例化一个DemuxerStream对象,这个对象会记录视频宽高、是否有旋转角度等,初始化audio_config和video_config,给解码的时候使用。

    这里面的每一步几乎都是通过PostTask进行的,即把函数当作一个任务抛给media线程处理,同时传递一个处理完成的回调函数。如果其中有一步挂了就不会进行下一步,例如遇到不支持的容器格式,在第一步初始化就会失败,就不会调回调函数往下走了。

    FFmpegVideoDecoder

    FFmpegVideoDecoder在编译的时候配置了下面几个参数后,编译出来的chrome就支持ffmpeg解码,默认是软解。

    在这里插入图片描述

    如图,FFmpegVideoDecoder的结构很简单,在Chrome的框架下,实现了Decode和onNewFrame函数,Decode是将DecodeBuffer类型的数据送给FFmpegDecodingLoop解码,FFmpegDecodingLoop中会创建AVPacket,获取解码后的AVFrame并且完成AVFrame到chrome的VideoFrame的数据转移,最后通过frame_ready_cb(onnewFrame)回调,将解码的数据送给render。

    下面是编译ffmpeg的参数:

    is_component_ffmpeg = true
    media_use_ffmpeg = true
    ffmpeg_branding = "Chrome"
    
    • 1
    • 2
    • 3

    ffmpeg v4l2m2m

    开始这个探索的背景是,在我们的平台上ffmpeg v4l2m2m是可以直接使用的,所以就想在Chrome中也能用v4l2m2m插件走硬解码,这里先忽略硬显示问题以及render和GPU线程在使用hardware decoder的限制,单纯从技术角度探讨下可行性。

    add configure

    通过阅读代码,发现ffmpeg中V4l2m2m默认是没有启用的,在Chrome中ffmpeg的源码是在third_party/ffmpeg/目录下,但是ffmpeg的编译是由third_party/ffmpeg/BUILD.gn组织的,在这个文件的开头,首先import了一个ffmpeg_generated.gni,这个文件也才是ffmpeg编译真正的代码组织文件。

    third_party/ffmpeg/ffmpeg_generated.gni中,首先会看到NOTE: this file is autogenerated by ffmpeg/chromium/scripts/generate_gn.py这一行注释,看来这个文件是自动生成的。从生成的代码中发现不同平台编译引用的文件是不完全相同的。

    这个以codec_list.c文件来看就可以很清楚。

    在third_party/ffmpeg/libavcodec/allcodecs.c中codec_list.c是被include进来的,同时在代码树中可以看到多个codec_list.c文件:

    #if CONFIG_OSSFUZZ
    const FFCodec * codec_list[] = {
        NULL,
        NULL,
        NULL
    };
    #else
    #include "libavcodec/codec_list.c"
    #endif
    
    static AVOnce av_codec_static_init = AV_ONCE_INIT;
    static void av_codec_init_static(void)
    {
        for (int i = 0; codec_list[i]; i++) {
            if (codec_list[i]->init_static_data)
                codec_list[i]->init_static_data((FFCodec*)codec_list[i]);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里列出来linux平台不同ABI对应的文件:

    third_party/ffmpeg/chromium/config/Chrome/linux/x64/libavcodec/codec_list.c
    third_party/ffmpeg/chromium/config/Chrome/linux/arm/libavcodec/codec_list.c
    third_party/ffmpeg/chromium/config/Chrome/linux/ia32/libavcodec/codec_list.c
    third_party/ffmpeg/chromium/config/Chrome/linux/arm64/libavcodec/codec_list.c
    
    • 1
    • 2
    • 3
    • 4

    实际上还有很多,如下,以arm/libavcodec/codec_list.c为例,默认只有这么几个codec列表:

    static const FFCodec * const codec_list[] = {
        &ff_h264_decoder,
        &ff_theora_decoder,
        &ff_vp3_decoder,
        &ff_vp8_decoder,
        &ff_aac_decoder,
        &ff_flac_decoder,
        &ff_mp3_decoder,
        &ff_vorbis_decoder,
        &ff_pcm_alaw_decoder,
        &ff_pcm_f32le_decoder,
        &ff_pcm_mulaw_decoder,
        &ff_pcm_s16be_decoder,
        &ff_pcm_s16le_decoder,
        &ff_pcm_s24be_decoder,
        &ff_pcm_s24le_decoder,
        &ff_pcm_s32le_decoder,
        &ff_pcm_u8_decoder,
        &ff_libopus_decoder,
        NULL };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如果只是单纯的调试,只要找对了平台和ABI对应的文件,直接修改这个也是可以的,但是如果是整体编译,这样改肯定不行,交叉编译需要arm和ia32对应的代码一致才可以,这就是后面要说的怎么自动生成这个文件。

    代码生成

    回到third_party/ffmpeg/chromium/scripts/build_ffmpeg.py,如果要重新配置ffmpeg的组件,直接修改build_ffmpeg.py是可以的,当然可以不该,直接传递参数给build_ffmpeg.py是可以的。

    先把需要传递的参数写出来:

    --enable-v4l2-m2m --enable-swscale \
    --enable-decoder=h263_v4l2m2m,h264_v4l2m2m,hevc_v4l2m2m,mpeg1_v4l2m2m,mpeg2_v4l2m2m,vp8_v4l2m2m,vp9_v4l2m2m \
    --enable-filter=h264_mp4toannexb,hevc_mp4toannexb \
    --enable-demuxer=mpegts,flv,matroska
    
    • 1
    • 2
    • 3
    • 4

    下面是使用build_ffmpeg.py脚本的过程:

    调用ffmpeg的代码如果使用到没有启用的部分,就需要重新生成ffmpeg_generated.gni文件,才能完成编译,最后要生产arm和

    86的libffmpeg.so

    更新、生成arm-neon的相关的文件

    
    export PATH=`pwd`/third_party/llvm-build/Release+Asserts/bin:$PATH
    
    
    ./build/linux/sysroot_scripts/install-sysroot.py --arch=arm
    
    
    cd ./third_party/ffmpeg/
    
    ./chromium/scripts/build_ffmpeg.py linux arm-neon --branding Chrome \
    -- --enable-v4l2-m2m --enable-swscale \
    --enable-decoder=h263_v4l2m2m,h264_v4l2m2m,hevc_v4l2m2m,mpeg1_v4l2m2m,mpeg2_v4l2m2m,vp8_v4l2m2m,vp9_v4l2m2m \
    --enable-filter=h264_mp4toannexb,hevc_mp4toannexb \
    --enable-demuxer=mpegts,flv,matroska
    
    ./chromium/scripts/copy_config.sh
    ./chromium/scripts/generate_gn.py
    cd ../../
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    更新、生成x86的相关的文件

    export PATH=`pwd`/third_party/llvm-build/Release+Asserts/bin:$PATH
    
    ./build/linux/sysroot_scripts/install-sysroot.py --arch=i386
    
    cd ./third_party/ffmpeg/
    
    ./chromium/scripts/build_ffmpeg.py linux ia32 --branding Chrome \
    -- --enable-v4l2-m2m --enable-swscale \
    --enable-decoder=h263_v4l2m2m,h264_v4l2m2m,hevc_v4l2m2m,mpeg1_v4l2m2m,mpeg2_v4l2m2m,vp8_v4l2m2m,vp9_v4l2m2m \
    --enable-filter=h264_mp4toannexb,hevc_mp4toannexb \
    --enable-demuxer=mpegts,flv,matroska
    
    ./chromium/scripts/copy_config.sh
    ./chromium/scripts/generate_gn.py
    cd ../../
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    编译

    
    export PATH="$PATH:/home/hui/chrome/depot_tools"
    
    gn gen out/default/
    autoninja -C out/default/ chrome -v
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    编译错误记录

    lld: error: undefined symbol问题处理

    ld.lld: error: undefined symbol: avcodec_free_context
    >>> referenced by ffmpeg_audio_decoder.cc
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-49CB027A656D6EFA991374D541CC87AA4FC1D312:(media::FFmpegAudioDecoder::~FFmpegAudioDecoder())
    >>> referenced by ffmpeg_audio_decoder.cc
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-49CB027A656D6EFA991374D541CC87AA4FC1D312:(media::FFmpegAudioDecoder::~FFmpegAudioDecoder())
    >>> referenced by ffmpeg_audio_decoder.cc
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-49CB027A656D6EFA991374D541CC87AA4FC1D312:(media::FFmpegAudioDecoder::ConfigureDecoder(media::AudioDecoderConfig const&))
    >>> referenced 11 more times
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    先检查ffmpeg.sigs文件中是否有定义,如果有,删除out/default/clang_x86_v8_arm/obj/third_party/ffmpeg/ffmpeg_internal/目录下文件,重新编译(autoninja -C out/default/ chrome -v)。

    rm out/default/clang_x86_v8_arm/obj/third_party/ffmpeg/ffmpeg_internal/*
    
    • 1

    比如添加了swscale库之后:

    ld.lld: error: undefined symbol: sws_getContext
    >>> referenced by ffmpeg_decoding_loop.cc
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-601B86BFA7603203EE89754C3E35196FBCC0AE0E:(media::FFmpegDecodingLoop::ConvertToI420(AVFrame*, AVFrame*))
    
    ld.lld: error: undefined symbol: sws_scale
    >>> referenced by ffmpeg_decoding_loop.cc
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-601B86BFA7603203EE89754C3E35196FBCC0AE0E:(media::FFmpegDecodingLoop::ConvertToI420(AVFrame*, AVFrame*))
    
    ld.lld: error: undefined symbol: sws_freeContext
    >>> referenced by ffmpeg_decoding_loop.cc
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-601B86BFA7603203EE89754C3E35196FBCC0AE0E:(media::FFmpegDecodingLoop::ConvertToI420(AVFrame*, AVFrame*))
    clang++: error: linker command failed with exit code 1 (use -v to see invocation)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    需要更新./third_party/ffmpeg/chromium/ffmpeg.sigs文件,添加:

    SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,int dstW, int dstH, enum AVPixelFormat dstFormat,int flags, SwsFilter *srcFilter,SwsFilter *dstFilter, const double *param);
    int attribute_align_arg sws_scale(struct SwsContext *c,const uint8_t * const srcSlice[],const int srcStride[], int srcSliceY,int srcSliceH, uint8_t *const dst[],const int dstStride[]);
    void sws_freeContext(SwsContext *c);
    
    • 1
    • 2
    • 3

    recompile with -fPIC

    ld.lld: error: relocation R_386_32 cannot be used against local symbol; recompile with -fPIC
    >>> defined in clang_x86_v8_arm/thinlto-cache/llvmcache-FE528CDEC76E847EC1A72B262C147253194FC80C
    >>> referenced by hscale_fast_bilinear_simd.c
    >>>               clang_x86_v8_arm/thinlto-cache/llvmcache-FE528CDEC76E847EC1A72B262C147253194FC80C:(ff_init_hscaler_mmxext)
    
    • 1
    • 2
    • 3
    • 4

    增加–enable-pic重新生成ffmpeg_generated.gni:

    # --enable-pic
    
    ./build/linux/sysroot_scripts/install-sysroot.py --arch=i386
    
    cd ./third_party/ffmpeg/
    
    ##--disable-x86asm
    
    ./chromium/scripts/build_ffmpeg.py linux ia32 --branding Chrome \
    -- --enable-v4l2-m2m --enable-swscale --disable-x86asm --enable-pic \
    --enable-decoder=h263_v4l2m2m,h264_v4l2m2m,hevc_v4l2m2m,mpeg1_v4l2m2m,mpeg2_v4l2m2m,vp8_v4l2m2m,vp9_v4l2m2m \
    --enable-filter=h264_mp4toannexb,hevc_mp4toannexb \
    --enable-demuxer=mpegts,flv,matroska
    
    ./chromium/scripts/copy_config.sh
    ./chromium/scripts/generate_gn.py
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    再次编译,验证还是解决不了这个错误。

    ./chromium/scripts/build_ffmpeg.py中增加参数--disable-x86asm解决不了output.asm找不到的问题,看了下output.asm里面直接是一句include,所以拷贝third_party/ffmpeg/libswscale/x86/autorename_libswscale_x86_output.asm到output.asm编译通过。

    # ../../third_party/ffmpeg/libswscale/x86/autorename_libswscale_x86_output.asm:2: \
    error: unable to open include file `output.asm': No such file or directory
    
    • 1
    • 2

    看了下output.asm文件存在,autorename_libswscale_x86_output.asm直接include了output.asm,不知道为什么会报错,但是直接复制为autorename_libswscale_x86_output.asm,这个文件就解决了:

    cp third_party/ffmpeg/libswscale/x86/output.asm \
    third_party/ffmpeg/libswscale/x86/autorename_libswscale_x86_output.asm
    
    • 1
    • 2

    –disable-x86asm是不行的,前面的fpic可能就是这个引起的,最后总结下,前面这部分是试过无数次之后才得以编译通过,就是要保证这个build_ffmpeg.py后面的参数在不同arch一定要一样,不然碰到的问题是没完没了的。


    参考链接

    从Chrome源码看audio/video流媒体实现二

    Chromium FFmpeg Roll Instructions

    Chromium 定制之 FFmpeg 裁剪

  • 相关阅读:
    MySQL | MySQL不区分大小写配置
    HTTP与HTTPS 对比,区别详解(2024-04-25)
    日志报错 fork:Cannot allocate memory
    HTML3天快速入门教程(详细整理附案例)
    Redis 分布式锁
    openssl从证书中提取公钥
    JAVA的异常处理
    刷爆力扣之构建乘积数组
    【汇编】[bx+idata]的寻址方式、SI和DI寄存器
    Kaggle竞赛 Real or Not? NLP with Disaster Tweets 文本分类
  • 原文地址:https://blog.csdn.net/hongszh/article/details/126167387