编译FFmpeg4.1

2021-03-10  本文已影响0人  iOS小孟和小梦

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的编译和静态库的相关配置

然后就可以使用了!!!

上一篇下一篇

猜你喜欢

热点阅读