前言:刚来公司时,就接手了直播功能版块的开发。推拉流走通了,逻辑框架和UI界面也都搭好了,但是因为资源问题,老板决定放弃这个版块。? 当时用的是网易云直播的sdk,没有集成IJKPlay,最近空闲时间比较多,就集成了一下IJKPlayer,使用很方便,集成起来会麻烦一点


简述下直播原理: 一个完整的直播程序,包括音视频采集/处理,视频转码,解码拉取等。概括来说,要有下面几个环节进行配合: 推流端:采集主播的音视频信息,进行美颜、变声、信息编码,将媒体流推流至服务器。 服务器端:对媒体流进行转码,录制等处理,分发数据。 拉流端:从服务器拉取媒体流,进行解码、渲染、提供音视频播放环境。 互动系统:聊天室、弹幕、点赞。

其中核心环节无外乎就是 推/拉流 过程,集成IJKPlayer,可以方便我们在这一步骤上所做的处理。

一. 什么是IJKPlayer?  ijkplayer是B站的一款开源框架,专门用来做视频直播,基于ffmpeg,同时支持 Android 和 iOS 平台。   对于 App 中的直播功能,如果我们成功集成ijkplayer ,那么只要得到一个拉流 URL,就能实现简单的音视频直播功能了。

二. 下载IJKPlayer IJKPlayer下载地址

三. 运行demo,编译IJKPlayer 下载IJKPlayer时,在README里面已经告诉了我们应该如何集成IJKPlayer,所以在编译demo前,应当在终端中按照图中步骤进行相应操作,如果没有按照上面的提示步骤,而是直接运行demo,因为缺少文件,编译器就会报错, readme中提示步骤:



  1. 打开终端,cd到IJKPlayer文件夹:
  2. 执行./init-ios.sh命令(等待下载···):

进程结束后,会发现extra文件夹下会多出这两个文件:



  1. cd到ios文件夹下:
  2. 依次执行:./compile-ffmpeg.sh clean 和 ./compile-ffmpeg.sh all命令(等待编译ffmpeg···),过程比较久,要耐心等待一会
  3. 进程结束后,查看是否编译成功:
  4. 打开IJKPlayerDemo,进行编译:

四. 打包IJKPlayer进行集成 集成IJKPlayer有两种方式:

  1. 按照README中的方法,和demo中一样,将IJKMediaPlayer.xcodeproj导入到我们自己的项目中,直接按照README中的提示进行操作,这里不做多余赘述。
# Demo
#     open ios/IJKMediaDemo/IJKMediaDemo.xcodeproj with Xcode
# 
# Import into Your own Application
#     Select your project in Xcode.
#     File -> Add Files to ... -> Select ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj
#     Select your Application's target.
#     Build Phases -> Target Dependencies -> Select IJKMediaFramework
#     Build Phases -> Link Binary with Libraries -> Add:
#         IJKMediaFramework.framework
#
#         AudioToolbox.framework
#         AVFoundation.framework
#         CoreGraphics.framework
#         CoreMedia.framework
#         CoreVideo.framework
#         libbz2.tbd
#         libz.tbd
#         MediaPlayer.framework
#         MobileCoreServices.framework
#         OpenGLES.framework
#         QuartzCore.framework
#         UIKit.framework
#         VideoToolbox.framework
#
#         ... (Maybe something else, if you get any link error)
# 
复制代码
  1. 将IJKPlayer打包成framework,导入到项目中:
  2. 打开IJKMediaPlayer.xcodeproj

注:这里有两个target,IJKMediaFramework 和 IJKMediaFrameworkWithSSL,在支持https的情况下,我们选择IJKMediaFrameworkWithSSL进行编译:


2. 将编译环境调成release模式:



  1. 分别在真机和模拟器上进行编译,机型不限。 如果编译报错“library not found for -lcrypto”,说明缺少文件



解决方法:我的解决方法是下载libcrypto和libssl文件拷贝到当前项目下。 我在电脑上全局搜这两个文件,发现之前的第三方里有这两个文件,就直接拿来用了: 拷贝到文件夹下


添加文件:


再次编译。

  1. 编译成功后,进入Finder中,查看编译结果:
  2. 将真机和模拟器上编译生成的文件进行合并,要合并的文件在图中进行了标记:



在终端执行合成命令:

lipo -create 真机版本编译后文件路径 模拟器版本编译后文件路径 -output 合成后路径(这里我放在Products文件夹下)
复制代码

注: 合成后路径指的是:合成后所放的文件夹/合成后的文件名,这里我把合成后文件命名为:“IJKMediaFrameWithSSL”,合成后路径为:/Build/Products/IJKMediaFrameWithSSL

执行完成后,在Products文件夹下看到新的合成文件:



使用新的合成文件,替换掉真机版本编译后文件路径下的文件:



五. 将IJKPlayer集成到项目中

  1. 将打包好的IJKPlayer静态库和一些依赖库添加到项目中:
  2. 编译查看是否报错:

这里暂时发现的有两个错误:

1、xxx does not contain bitcode: xcode默认开启了bitcode,如果我们集成的库没有bitcode编译的包,则有可能出现报错。

解决方法:target-->build setting-->enable bitcode-->NO



  1. 运行时crash,提示:dyld: Library not loaded: @rpath xxx reason: image not found 出现这种错误,是因为在运行时没有找到framewok对应的包,如果运行到这个包时,就会崩溃。

解决方法: 手动添加framework到项目中:





六. 模仿demo,进行测试:

这里我写了一个tableView,链接到不同的拉流地址:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSURL *url = [NSURL URLWithString:_dataArray[indexPath.row]];
    
    NSString *scheme = [[url scheme]lowercaseString];
    
    if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"rtmp"]) {
        
        [XSIJKMediaPlayerViewController presentFromViewController:self withTitle:@"Livestream" URL:_dataArray[indexPath.row] completion:nil];
    }
    
}
复制代码

放入测试地址:

- (void)requestData{
    
    NSArray *urls = @[@"rtmp://live.hkstv.hk.lxdns.com/live/hks",@"http://wzfree.10043.doftp.com/tvtest/182tv.php/live/id/suntv.m3u8",@"invalidUrl"];
    _dataArray = [[NSMutableArray alloc]initWithArray:urls];
    
    [_tableView reloadData];
    
}
复制代码

在直播页面的ViewController中进行配置:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    
    self.view.backgroundColor = [UIColor grayColor];
#ifdef DEBUG
    //设置是否打印信息
    [IJKFFMoviePlayerController setLogReport:YES];
    
    [IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
    
#else
    [IJKFFMoviePlayerController setLogReport:NO];
    [IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_INFO];
#endif
    
    [IJKFFMoviePlayerController checkIfFFmpegVersionMatch:YES];
    
    //配置参数:数据处理、videotoolbox解码、设置音视频属性参数设置
    IJKFFOptions *options = [IJKFFOptions optionsByDefault];
    
    self.player = [[IJKFFMoviePlayerController alloc]initWithContentURL:self.url withOptions:options];
    self.player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
    self.player.view.frame = self.view.bounds;
    self.player.scalingMode = IJKMPMovieScalingModeAspectFit;
    self.player.shouldAutoplay = YES;
    
    self.view.autoresizesSubviews = YES;
    [self.view addSubview:self.player.view];
    
    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 40, 40)];
    [btn setTitle:@"返回" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(leftClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}
复制代码

运行效果:




这里只写了简单的拉流,代码很简单,就不上demo了,其他相关代码可以看IJKPlayer的demo,里面写的很详细。