前言:刚来公司时,就接手了直播功能版块的开发。推拉流走通了,逻辑框架和UI界面也都搭好了,但是因为资源问题,老板决定放弃这个版块。
当时用的是网易云直播的sdk,没有集成IJKPlay,最近空闲时间比较多,就集成了一下IJKPlayer,使用很方便,集成起来会麻烦一点
简述下直播原理:
一个完整的直播程序,包括音视频采集/处理,视频转码,解码拉取等。概括来说,要有下面几个环节进行配合:
推流端:采集主播的音视频信息,进行美颜、变声、信息编码,将媒体流推流至服务器。
服务器端:对媒体流进行转码,录制等处理,分发数据。
拉流端:从服务器拉取媒体流,进行解码、渲染、提供音视频播放环境。
互动系统:聊天室、弹幕、点赞。
其中核心环节无外乎就是 推/拉流 过程,集成IJKPlayer,可以方便我们在这一步骤上所做的处理。
一. 什么是IJKPlayer?
ijkplayer是B站的一款开源框架,专门用来做视频直播,基于ffmpeg,同时支持 Android 和 iOS 平台。
对于 App 中的直播功能,如果我们成功集成ijkplayer ,那么只要得到一个拉流 URL,就能实现简单的音视频直播功能了。
二. 下载IJKPlayer
IJKPlayer下载地址
三. 运行demo,编译IJKPlayer
下载IJKPlayer时,在README里面已经告诉了我们应该如何集成IJKPlayer,所以在编译demo前,应当在终端中按照图中步骤进行相应操作,如果没有按照上面的提示步骤,而是直接运行demo,因为缺少文件,编译器就会报错,
readme中提示步骤:
-
打开终端,cd到IJKPlayer文件夹:
-
执行./init-ios.sh命令(等待下载···):
进程结束后,会发现extra文件夹下会多出这两个文件:
-
cd到ios文件夹下:
-
依次执行:./compile-ffmpeg.sh clean 和 ./compile-ffmpeg.sh all命令(等待编译ffmpeg···),过程比较久,要耐心等待一会
-
进程结束后,查看是否编译成功:
-
打开IJKPlayerDemo,进行编译:
四. 打包IJKPlayer进行集成
集成IJKPlayer有两种方式:
- 按照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)
#
- 将IJKPlayer打包成framework,导入到项目中:
-
打开IJKMediaPlayer.xcodeproj
注:这里有两个target,IJKMediaFramework 和 IJKMediaFrameworkWithSSL,在支持https的情况下,我们选择IJKMediaFrameworkWithSSL进行编译:
- 将编译环境调成release模式:
-
分别在真机和模拟器上进行编译,机型不限。
如果编译报错“library not found for -lcrypto”,说明缺少文件
解决方法:我的解决方法是下载libcrypto和libssl文件拷贝到当前项目下。
我在电脑上全局搜这两个文件,发现之前的第三方里有这两个文件,就直接拿来用了:
拷贝到文件夹下
添加文件:
再次编译。
-
编译成功后,进入Finder中,查看编译结果:
-
将真机和模拟器上编译生成的文件进行合并,要合并的文件在图中进行了标记:
在终端执行合成命令:
lipo -create 真机版本编译后文件路径 模拟器版本编译后文件路径 -output 合成后路径(这里我放在Products文件夹下)
注: 合成后路径指的是:合成后所放的文件夹/合成后的文件名,这里我把合成后文件命名为:“IJKMediaFrameWithSSL”,合成后路径为:/Build/Products/IJKMediaFrameWithSSL
执行完成后,在Products文件夹下看到新的合成文件:
使用新的合成文件,替换掉真机版本编译后文件路径下的文件:
五. 将IJKPlayer集成到项目中
-
将打包好的IJKPlayer静态库和一些依赖库添加到项目中:
编译查看是否报错:
这里暂时发现的有两个错误:
1、xxx does not contain bitcode:
xcode默认开启了bitcode,如果我们集成的库没有bitcode编译的包,则有可能出现报错。
解决方法:target-->build setting-->enable bitcode-->NO
- 运行时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,里面写的很详细。