美文网首页
编译FFmpeg4.1

编译FFmpeg4.1

作者: iOS小孟和小梦 | 来源:发表于2021-03-10 16:51 被阅读0次

    8月26日更新: 由于之前编译后得到的包体积过大(增加了40M)左右 , 整个包有很多暂时使用不到的组件 , 因此需要对FFmpeg的编译库进行轻量化配置 , 经过选项配置 , 编译后库的大小减少了30M左右!~

    配置CONFIGURE_FLAGS参数 , 把ffprobe、ffplay、avresample、sdl2都给关掉 , 这样在编译时就不会把这些组件增加进去

    CONFIGURE_FLAGS="--enable-cross-compile --disable-debug --disable-programs --disable-doc --enable-pic --disable-shared --disable-ffprobe --disable-ffplay --disable-avresample --disable-sdl2"

    关于Configure_flags的配置参数可以通过命令行ffmpeg -help查看支持的组件命令 , 再选择裁剪 , 这里有篇较新的参考文档,比较全面而且分类清晰

    前言

    之所以要采用FFmpeg , 是因为原本使用系统原生api进行压缩转码时 , 发现某部分视频会转码失败或者转码出来的格式阿里云不支持 , 导致无法正常播放

    安装步骤

    0.去https://github.com/bigsen/gas-preprocessor.git 下载gas-preprocessor, 编译时需要用到, 然后把它放到/usr/local/bin/ 下面 , 并且执行命令给与权限
    sudo chmod 777 /usr/local/bin/gas-preprocessor.pl

    1. 首先安装下yasm
      brew install yasm
    2. 下载ffmpeg的编译脚本(如果自己编译的话难度很大 , 所幸已经有脚本帮助我们走流程)

    (3-5 是编译 x264和 fdk-aac 插件步骤, 为了让FFmpeg支持更多的指令)

    1. ①从[官网] https://www.videolan.org/developers/x264.html] 直接下载最新版源码。
      ②从 https://github.com/kewlbear/x264-ios 下载编译脚本build-x264.sh
      ③把源码解压到x264目录,把编译脚本build-x264.sh放到x264同级目录,根据需要编译的archs修改脚本。(注意脚本里面SOURCE变量的目录名称对不对)
      ④然后执行 ./build-x264.sh
    2. ①从[官网http://www.linuxfromscratch.org/blfs/view/svn/multimedia/fdk-aac.html] 直接下载最新版源码。
      ②从https://github.com/kewlbear/fdk-aac-build-script-for-iOS下载编译脚本build-fdk-aac.sh
      ③把源码解压,把编译脚本 build-fdk-aac.sh 放到解压出来的fdk-aac-2.0.0同级目录根据需要编译的archs 及fdk-aac源码目录修改脚本。(注意脚本里面SOURCE变量的目录名称对不对)
      ④然后执行 ./build-x264.sh
      !!! 编译x264和fdk-aac的时候, 如果需要编译模拟器架构的话, 有可能会报错
        Minimum version is NASM version 2.13
    

    这时候是因为系统自带的nasm汇编编译器太旧了, 直接用brew reinstall nasm 重装, 升级到最新的2.14版本

    1. 把编译好的x264和fdk-aac 库和头文件放到build-ffmpeg.sh脚本同级目录下, 打开修改 build-ffmpeg.sh 脚本的配置
    # 把变量修改成对应的目录名称
    X264=`pwd`/x264-iOS  
    FDK_AAC=`pwd`/fdk-aac-ios
    
    1. 编译前可以打开build-ffmpeg.sh脚本, 配置一下裁剪参数CONFIGURE_FLAGS 和支持平台ARCHS 打开文件在根目录下打开终端运行脚本 build-ffmpeg.sh , 脚本会自动下载ffmpeg4.x(反正会下载最新的) , 然后编译生成各个平台的静态库和对应平台的相关代码
      image.png
    2. 为了能真机和模拟器都能使用 , 执行完脚本后再执行一遍 , 会把所有平台的.a文件合并成fat包
      ./build-ffmpeg.sh lipo
    3. 将FFmpeg-iOS整个文件夹拖进项目中 , 在build setting 中搜索search path , 配置Library Search Paths和 Header Search Paths , 为对应的lib和include路径(一般拖进项目时lib路径会自动填充好)
    4. 添加依赖库
    libz.tbd
    libbz2.tbd
    libiconv.tbd
    videoToolbox.framework
    
    1. 然后随便找个.m文件导入 , 写几行代码编译看看会不会报错(后面删掉!)
    #import <libavformat/avformat.h>
    #import <libavcodec/avcodec.h>
    #import <libswscale/swscale.h>
    #import <libavutil/avutil.h>
    #import <libswresample/swresample.h>
    #import <libavdevice/avdevice.h>
    #import <libavfilter/avfilter.h>
    {
        av_register_all();
        avcodec_register_all();
        avformat_network_init();
    }
    

    到这里已经可以直接通过c++api使用库了 , 但是如果要用命令则需要添加其他依赖文件

    1. 打开FFmpeg-4.1(源码目录),在fftools中找到除了config.h的其他8个文件(网上较旧的文章都不需要 ffmpeg_hw.c文件, 但是在4.1中需要 , 不然会报错)
      image.png
    1. 打开ffmpeg.c , 把main函数修改一下 , 因为一个程序不能有两个main函数 , 还有在.h把这个方法声明一下
    ffmpeg.h ----
        int ffmpeg_main(int argc, char **argv);
    ffmpeg.m ----
        int ffmpeg_main(int argc, char **argv){
    }
    
    1. 打开cmdutils.c文件 , 把exit_program函数改一下 , 因为这个函数会退出进程 , 我们先直接啥都不干 , 后面改成其他操作
        void exit_program(int ret)
        {
            //if (program_exit)
            //    program_exit(ret);
            //exit(ret);
    }
    
    1. 接下就是编译看看报什么错误 , 然后去源码目录中找到相应的头文件,放到include目录下相应的文件夹中
      处理完错误之后会发现在真机上已经可以运行了 , 但是在模拟器上还是会报SDL相关错误 , 但是我们已经合并了多平台静态库 , 为什么还会报错呢 ?

    既然报SDL错误, 那么我们就给他SDL库

    1. 去SDL官网下载2.0的源码包

    2. 下载好后解压 , 在根目录中找到Xcode-iOS-SDL-SDL.xcodeproj , 打开项目 , 在Public Headers中找到SDL_main.h , 添加一行宏定义 , 因为SDL本身有main函数 , 如果定义了SDL_MAIN_HANDLED , 就说明自己有main函数, 不需要使用SDL的
      #define SDL_MAIN_HANDLED

    3. 分别选择模拟器和真机编译一下 , 在products目录就有对应的.a文件 , 右键打开Finder , 用lipo命令把他们合并起来
      lipo -create xxx/.a xxx/.a -output xxx/libSDL2.a

      image.png
    4. 在项目中新建SDL文件夹 , 再新建lib文件夹 , 把SDL2.a丢进去 , 再从源码目录中找到include头文件拖入SDL目录 , 配置对应的Header Search 路径 , 添加两个需要的库

    GameController
    CoreMotion
    

    到这里已经可以开始使用了 , 但是有一些地方的代码还需要修改下

    1. ffmpeg.c中的 ffmpeg_cleanup方法 , 需要把一些计数变量清空 , 否则会导致下一次转码崩溃 !!!!后面发现, 把这一部分代码移到exit_program 中更加适合
        avformat_network_deinit();
        //在👆这行代码之前的逻辑所有内存后  添加清空计数代码
        //这里不清空会导致下一个视频转码崩溃  
        nb_filtergraphs = 0;
        nb_input_files = 0;
        nb_input_streams = 0;
        nb_output_files = 0;
        nb_output_streams = 0;
        //    nb_frames_dup = 0;
        //    nb_frames_drop = 0;
        //    run_as_daemon = 0;
    
    1. 想获取转码进度的话 , 需要些一个桥接类 , 创建任意一个cocoa touch类,把.h文件中所有东西都删除掉,.m中只留下头文件导入的代码 。 这样在.h 中就可以声明c函数 在点m中调用 就能完成C和OC之间的通信
    //声明这几个方法
    // 转换停止回调
    void stopRuning(void);
    // 获取总时间长度
    void setDuration(long long int time);
    // 获取当前时间
    void setCurrentTime(char info[1024]);
    
    1. 再新建一个用来管理转码的类
    ➕(VSFFmpegManager *)sharedManager;
    /**
     转换视频
     @param inputPath 输入视频路径
     @param outpath 输出视频路径
     @param processBlock 进度回调
     @param completionBlock 结束回调
     */
    ➖ (void)converWithInputPath:(NSString *)inputPath
                     outputPath:(NSString *)outpath
                   processBlock:(void (^)(long long spendTime , long long totalTime))processBlock
                completionBlock:(void (^)(NSError *error))completionBlock;
    // 设置总时长
    ➕(void)setDuration:(long long)time;
    // 设置当前时间
    ➕(void)setCurrentTime:(long long)time;
    // 转换停止
    ➕(void)stopRuning;
    
    1. 监控转码的开始和完成状态 , 在C++函数中调用OC方法,来传递状态信息 , cmdutils.c 中的 exit_program 结束线程前调用stopRuning()结束方法 , 并且把原本的结束进程方法改为退出线程
    //需要导入
    #include <pthread.h>
    #include “VSFFmpegConverOC.h"
    void exit_program(int ret){
        //标记为转换完成
        stopRuning();
        if (program_exit)
            program_exit(ret);
        //这个是结束进程的方法,让ffmpeg进行完转码以后不至于退出程序
        //exit(ret);
        //所以将退出进程的方法改造为退出线程
        pthread_exit(NULL);
    }
    
    1. 要获取视频文件总时间长度 , ffmpeg_opt.copen_input_file方法中会有时长信息 ic->duration , 为long long int类型数据
    #include “VSFFmpegConverOC.h"
        //获取文件总时长信息
        setDuration(ic->duration);
        //👇在这行代码前 , 其实也就是尽量在加载完文件之后去获取就行了
        input_stream_potentially_available = 1;
    
    1. 定时获取当前进度时间 , ffmpeg.c的print_report方法中会输出Log,从log中获取当前的进度信息,为char info[1024]类型数据
    #include “VSFFmpegConverOC.h”
    //4.1中的buf改成了一个结构体 , 里面的str是char *类型
    av_log(NULL, AV_LOG_INFO, "%s    %c", buf.str, end);
    //在打印之后获取转码的当前时间
     setCurrentTime(buf.str);
    
    1. 转换百分比为当前进度除以总时长。注意事项:更改进度条的时候,是在非主线程,所以无法更改UI,需要在主线程执行更改UI操作

    到这里就完成了ffmpeg的编译和静态库的相关配置

    然后就可以使用了!!!

    相关文章

      网友评论

          本文标题:编译FFmpeg4.1

          本文链接:https://www.haomeiwen.com/subject/tgwwqltx.html