知行合一,知是行之始,行是知之成,知而不行非真知,行而不知非真行!凡事应理论+实践!打破思维惯性和过度思考可能会带来的决策风险!
ijkPlayer iOS编译(支持多种编码格式)
ijkPlayer iOS编译(支持多种编码格式)

ijkPlayer iOS编译(支持多种编码格式)

  • 最近项目中用到ijkPlayer播放器,因为有些格式不支持比如rtsp,rtmp,flv等格式,所以把ijkplayer重新编译了一次,在这个过程中或多或少遇到了几个坑吧,记录一下方便以后自己查询,那些前赴后继踩坑的童鞋可能会有此类似的问题,少走些弯路吧。

1.ijkPlayer

  • ijkplayer 是Bilibili团队小伙伴基于ffmpeg二次封装的一个很棒开源直播框架(免费), 支持Android 和 iOS, 网上也有很多集成说明,笔者也是看了很多大大分享的集成资料,才得以修成正果😁。

2.准备工作(Homebrew、git、yasm)

检查自己电脑是不是安装了homebrew、git、yasm可以打开终端依次输入:

  • brew:brew 是 Mac 下的一个包管理工具,类似于 centos 下的 yum,可以很方便地进行安装/卸载/更新各种软件包,例如:nodejs, elasticsearch, kibana, mysql, mongodb 等等,可以用来快速搭建各种本地环境,程序员必备工具
  • git:是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理
  • yasm:汇编编译器,yasm是一个完全重写的NASM汇编。目前,它支持x86和AMD64指令集。
  • pkg-config:pkg-config是一个在源代码编译时查询已安装的库的使用接口的计算机工具软件,pkg-config可用与列举出某个库的相关信息,比如此库的路径、相关头文件路径等,这在程序编译时将非常有用
brew -v
git --version
yasm --version
pkg-config --version

我的已经安装了下面是我的各个版本截图
-w556

brew安装步骤:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)

brew卸载步骤:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)

homebrew的用法:

安装软件,如:brew install oclint
卸载软件,如:brew uninstall oclint
搜索软件,如:brew search oclint
更新软件,如:brew upgrade oclint
查看安装列表, 如:brew list
更新Homebrew,如:brew update

brew安装git

brew install git

brew安装yasm

brew install yasm

brew安装pkg-config

brew install pkg-config

3.确定项目需求项目是否需要支持armv7和armv7s架构的设备

-w620

如果需要支持armv7和armv7s架构的设备

  • 必须安装Xcode9.1以下版本,因为小面编译会用到

如果不需要支持armv7和armv7s架构的设备

  • 什么都不用做直接return

4.下载ijkPlayer和一些配置

下载ijkplayer

ijkPlayer github地址:

https://github.com/Bilibili/ijkplayer.git ijkplayer-ios

本地创建一个空文件夹cd到该文件目录下

-w569

克隆ijkPlayer项目到该文件中

git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-ios

cd到ijkplayer-ios目录下

-w570

创建分支并切换到该分支进行后续的操作

创建分支命令:git checkout -B AnmoTest
查看当前分支:git branch
-w568

支持armv7和armv7s(如果不需要支持直接看下一步)

1.修改init-ios.sh(目录/ijkplayer-ios/init-ios.sh)

 将ios8环境 因为 FF_ALL_ARCHS=$FF_ALL_ARCHS_IOS8_SDK 因为xcode为9.1也是所有环境 增加armv7s
 FF_ALL_ARCHS_IOS8_SDK=”armv7 armv7s arm64 i386 x86_64″
 -w631

2. 运行下载ffmpeg
 ./init-ios.sh

目的下载 ffmpeg–armv7–armv7s-arm64- i386 x86_64
-w568
-w565

备注:ffmpeg比较大,国外服务器,此过程可能耗时比较常,如果失败了多试几次,或者开个小飞机,笔者开了小飞机比较快😁(这个过程大概2-3分钟完成)!

3.修改 compile-ffmpeg.sh脚本(目录/ijkplayer-ios/ios/compile-ffmpeg.sh)

将下面2个地方 加上 armv7s
FF_ALL_ARCHS_IOS8_SDK=”armv7 armv7s arm64 i386 x86_64″
echo ” compile-ffmpeg.sh armv7|armv7s|arm64|i386|x86_64″

-w653
-w801

不需要支持armv7和armv7s

在 /ijkplayer-ios/ios/compile-ffmpeg.sh 中删除 armv7 , 修改如:
-w760
-w665
-w549

因为最新的 Xcode 已经弱化了对 32 位的支持, 在编译的时候可能会遇到如下问题,所以对armv7提前做处理
AS libavcodec/arm/aacpsdsp_neon.o
./libavutil/arm/asm.S:50:9: error: unknown directive
.arch armv7-a
^
make: *** [libavcodec/arm/aacpsdsp_neon.o] Error 1
make: *** Waiting for unfinished jobs….

更换Xcode编译环境编译支持armv7和armv7s(必须用Xcode9.1以下)

sudo /usr/bin/xcode-select -switch /Applications/Xcode9.0.app/Contents/Developer

更换Xcode编译环境编译不需要支持armv7和armv7s

sudo /usr/bin/xcode-select -switch /Applications/Xcode.app/Contents/Developer
-w574

配置编码格式支持

  • cd 到当前目录下的config文件下
    -w578
  • ls查看文件
    -w575
module-default.sh 更多的编解码器/格式
module-lite-hevc.sh 较少的编解码器/格式(包括hevc)
module-lite.sh 较少的编解码器/格式(默认情况)
偏好更多的解码器/视频格式支持, 则链接module-default.sh
偏好打包出来的库体积更小 (默认格式支持), 则链接module-lite.sh
偏好打包出来的库体积更小 (包含HEVC支持), 则链接module-lite-hevc.sh

删除当前的 module.sh 文件

rm module.sh

可根据需要替换为`module-default.sh`, `module-lite-hevc.sh`, `module-lite.sh`

创建软链接 module.sh 指向 module-lite-hevc.sh

ln -s module-lite-hevc.sh module.sh
-w567

这一步支持RTSP(目录/ijkplayer-ios/ijkmedia/ijkplayer/ff_ffplay.c)

  • open module.sh
    -w571
  • 修改module.sh内容 然后保存
将 export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtp"

修改为 export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"

并添加 export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"

export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mjpeg"

export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mjpeg"
-w752
  • 修改ff_ffplay.c文件
    目录:~/ijkmedia/ijkplayer/ff_ffplay.c 将这一方法:

修改前:

static int packet_queue_get_or_buffering(FFPlayer *ffp, PacketQueue *q, AVPacket *pkt, int *serial, int *finished)
{
    assert(finished);
    if (!ffp->packet_buffering)
        return packet_queue_get(q, pkt, 1, serial);
    while (1) {
        int new_packet = packet_queue_get(q, pkt, 0, serial);
&nbsp; &nbsp; &nbsp; &nbsp; if (new_packet < 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -1;
&nbsp; &nbsp; &nbsp; &nbsp; else if (new_packet == 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (q->is_buffer_indicator && !*finished)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ffp_toggle_buffering(ffp, 1);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_packet = packet_queue_get(q, pkt, 1, serial);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (new_packet < 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; if (*finished == *serial) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; av_packet_unref(pkt);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; else
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;
&nbsp; &nbsp; }
&nbsp; &nbsp; return 1;
}

修改后:

static int packet_queue_get_or_buffering(FFPlayer *ffp, PacketQueue *q, AVPacket *pkt, int *serial, int *finished){
&nbsp; &nbsp; if (!ffp->packet_buffering)
&nbsp; &nbsp; &nbsp; &nbsp; return packet_queue_get(q, pkt, 1, serial);
&nbsp; &nbsp; while (1) {
&nbsp; &nbsp; &nbsp; &nbsp; int new_packet = packet_queue_get(q, pkt, 1, serial);
&nbsp; &nbsp; &nbsp; &nbsp; if (new_packet < 0){
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_packet = packet_queue_get(q, pkt, 0, serial);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(new_packet < 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -1;
&nbsp; &nbsp; &nbsp; &nbsp; }else if (new_packet == 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (q->is_buffer_indicator && !*finished)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ffp_toggle_buffering(ffp, 1);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_packet = packet_queue_get(q, pkt, 1, serial);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (new_packet < 0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -1;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; if (*finished == *serial) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; av_packet_unref(pkt);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; else
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;
&nbsp; &nbsp; }
&nbsp; &nbsp; return 1;
}

播放RTSP流卡死问题(目录/ijkplayer-ios/ijkmedia/ijkplayer/ff_ffplay.c)

  • packet_queue_get_or_buffering 这个方法中做相应修改
    -w804

添加https支持(最后会生成支持 https 的静态文件 libcrypto.a 和 libssl.a, 如果不需要可以跳过这一步)

获取 openssl 并初始化
./init-ios-openssl.sh

在模块文件中添加一行配置 以启用 openssl 组件
echo 'export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-openssl"' >> ../config/module.sh
-w550
-w642
-w798

5.编译ffmpeg

编译openssl, 如果不需要https可以跳过这一步

支持https

cd ios
./compile-openssl.sh all
./compile-ffmpeg.sh all

需要等待一段时间
-w636

不支持https

cd ios
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all //编译

这个编译过程有点漫长笔者等了差不多十几分钟编译完成!
-w726

编译完成那么看看能不能起飞吧(跑一下官方Demo)

-w770

现在Demo能正常运行

但是要支持RTSP格式在Demo的IJKDemoInputURLViewController.m 文件中做相应的修改就能跳转过去播放了!
-w1249

RTSP正常播放(但是笔者在播放的时候遇到的问题是,可能部分网络下播放不了)
-w1291

打包framwork

那么在项目中需要用到,就需要打包成framwork了

-w770
-w903

大家会发现IJKMediaFramework,IJKMediaFrameworkWithSSL, 不推荐使用第一个, 大部分基于 ijkplayer 的第三方框架都是使用的IJKMediaFramework!

-w891
Edit Scheme

-w894

分别选择真机和模拟器编译一次(Command + B)
-w1090
-w1058

编译的时候可能会遇到问题按提示注释掉就可能正常编译了
-w851
-w831

两种情况都编译成功接下来合并真机和模拟器的framework

-w866
-w770
合并: lipo -create 真机framework路径 模拟器framework路径 -output 合并的文件路径
-w1279
-w978

替换掉Release-iphoneos中的IJKMediaFramework

-w905

接下来这个就是可以集成在我们项目中的ijkplayr的framework了
-w770

6.在项目中集成

项目中并导入IJKMediaFramework.framework,以及其他相关依赖库!
-w833

7.ijkplayer的参数配置说明(按需求设置)

IJKFFOptions * options = [IJKFFOptions optionsByDefault];
// 帧速率(fps)可以改,确认非标准帧率会导致音画不同步,所以只能设定为15或者29.97)
[options setPlayerOptionIntValue:29.97 forKey:@"r"]; 
// 设置音量大小,256为标准音量。(要设置成两倍音量时则输入512,依此类推)
[options setPlayerOptionIntValue:512 forKey:@"vol"]; 
//  关闭播放器缓冲 (如果频繁卡顿,可以保留缓冲区,不设置默认为1)
[options setPlayerOptionIntValue:0 forKey:@"packet-buffering"]; 
//静音设置
[option setPlayerOptionValue:@"1" forKey:@"an"];
// 最大fps
[options setPlayerOptionIntValue:30 forKey:@"max-fps"]; 
// 跳帧开关
[options setPlayerOptionIntValue:0 forKey:@"framedrop"]; 
// 开启硬编码 (默认是 0 :软解)
[options setPlayerOptionIntValue:1 forKey:@"videotoolbox"];
// 指定最大宽度
[options setPlayerOptionIntValue:960 forKey:@"videotoolbox-max-frame-width"]; 
 // 自动转屏开关
[options setFormatOptionIntValue:0 forKey:@"auto_convert"];
// 重连开启 BOOL
[options setFormatOptionIntValue:1 forKey:@"reconnect"]; 
// 超时时间,timeout参数只对http设置有效,若果你用rtmp设置timeout,ijkplayer内部会忽略timeout参数。rtmp的timeout参数含义和http的不一样。
[options setFormatOptionIntValue:30 * 1000 * 1000 forKey:@"timeout"]; 
// 如果使用rtsp协议,可以优先用tcp(默认udp)
[options setFormatOptionValue:@"tcp" forKey:@"rtsp_transport"];
//播放前的探测Size,默认是1M, 改小一点会出画面更快
[options setFormatOptionIntValue:1024 * 16 forKey:@"probesize"];
//开启环路滤波(0比48清楚,但解码开销大,48基本没有开启环路滤波,清晰度低,解码开销小)
[options setCodecOptionIntValue:IJK_AVDISCARD_DEFAULT forKey:@"skip_loop_filter"];
[options setCodecOptionIntValue:IJK_AVDISCARD_DEFAULT forKey:@"skip_frame"];

// param for living
 // 最大缓存大小是3秒,可以依据自己的需求修改
[options setPlayerOptionIntValue:3000 forKey:@"max_cached_duration"];  
 // 无限读
[options setPlayerOptionIntValue:1 forKey:@"infbuf"]; 
 //  关闭播放器缓冲
[options setPlayerOptionIntValue:0 forKey:@"packet-buffering"]; 
//param for playback
[options setPlayerOptionIntValue:0 forKey:@"max_cached_duration"];
[options setPlayerOptionIntValue:0 forKey:@"infbuf"];
[options setPlayerOptionIntValue:1 forKey:@"packet-buffering"];

_player = [[IJKFFMoviePlayerController alloc] initWithContentURL:self.url withOptions:options];

至于最后一点怎么用,其实官方Demo已经有了大概,自己看看Demo播放界面集成就成可以了,笔者也是看的官方Demo,有什么问题,可以留下大家一起交流😁!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注