Vol. 12
低延时直播是网易云信推出的低延时、强同步、高质量的直播产品。低延时直播产品基于云信全球智能路由网络,为开发者提供毫秒级延时、多平台同步、高可靠高并发的直播服务。
接下同大家一起,用 15 分钟的时间快速实现低延时直播的功能。
视频讲解
15分钟视频详解,手把手教你接入
前期准备
开发工具:Xcode
开发语言:Objective-C
使用到的 SDK
#直播推流
pod 'NMCLiveStreaming', '3.3.0'
#播放器拉流
pod 'NELivePlayer', '3.1.3'
https://doc.yunxin.163.com/docs/jEyODA1OTE/DcxNjY0OTI?platformId=110002
参考官网低延时直播使用指南在云信后台直播模块-域名管理界面,开通低延时直播,获取推流地址以及对应的拉流地址和低延时拉流地址。
具体步骤
第一步:首先实现推流
1.传入获取到的“推流地址”和自定义的“推流配置”创建推流实例
NSString *url = @"推流地址";
//----推流配置----
LSLiveStreamingParaCtxConfiguration *config = [LSLiveStreamingParaCtxConfiguration defaultLiveStreamingConfiguration];
config.sLSVideoParaCtx.interfaceOrientation = LS_CAMERA_ORIENTATION_PORTRAIT;//摄像头的方向,可以选择横屏或者竖屏
config.sLSVideoParaCtx.cameraPosition = LS_CAMERA_POSITION_BACK;//前后摄像头
...
//----创建推流实例----
_mediaCapture = [[LSMediaCapture alloc]initLiveStream:url withLivestreamParaCtxConfiguration:config];
if (_mediaCapture == nil) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"初始化失败" forKey:NSLocalizedDescriptionKey];
NSError *error = [NSError errorWithDomain:@"LSMediaCaptureErrorDomain" code:0 userInfo:userInfo];
NSLog(@"---%@", error.localizedDescription);
}
2.设置画布开启推流的视频预览
//开启预览
[self.mediaCapture startVideoPreview:self.localPreview];
3.开始推流直播
推流之前需要在项目 info.plist 文件中配置请求允许 App 使用摄像头和麦克风。
NSCameraUsageDescription
app需要使用摄像头进行直播录制
NSMicrophoneUsageDescription
app需要使用麦克风进行直播录制
//开始推流
[_mediaCapture startLiveStream:^(NSError *error) {
if (error) {
NSLog(@"---%@", error.localizedDescription);
}
}];
这样本地摄像头采集的画面和声音就可以通过预览画布看到,并且推流直播出去了。其他用户就可以通过对应的拉流地址和低延时拉流地址播放观看了。
4.结束推流直播释放推流实例
//结束预览
[self.mediaCapture pauseVideoPreview];
//结束推流
[_mediaCapture stopLiveStream:^(NSError *error) {
if (error) {
NSLog(@"---%@", error.localizedDescription);
}
}];
//释放
[_mediaCapture unInitLiveStream];
_mediaCapture = nil;
进阶
1.在直播过程中 ,可以进行视频推流的相关操作,如中断恢复音频,中断恢复视频
[_mediaCapture resumeVideoLiveStream]; //恢复视频推流
[_mediaCapture pauseVideoLiveStream];//中断视频推流
[_mediaCapture pauseAudioLiveStream];//中断音频推流
[_mediaCapture resumeAudioLiveStream];//恢复音频推流
2.直播过程中还可以进行 MP4 录制,录制直播内容之后保存在本地,可以先 创建一个本地路径 ,传入相关方法就可以了 ,也可以进行录制的配置 ,如下
- (void)recordBtnTapped:(UIButton *)sender {
if (sender.isSelected) {
//以开始录制的时间作为时间戳,作为文件名后缀
NSString *fileName = @"/vcloud_";
NSDate *date = [NSDate date];
NSTimeInterval sec = [date timeIntervalSinceNow];
NSDate *currDate = [[NSDate alloc] initWithTimeIntervalSinceNow:sec];
NSDateFormatter *df = [NSDateFormatter new];
[df setDateFormat:@"yyyyMMddHHmmss"];
NSString *dfStr = [df stringFromDate:currDate];
fileName = [fileName stringByAppendingString:dfStr];
fileName = [fileName stringByAppendingString:@".mp4"];
//存储在Documents路径里
NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documntsPath = arr[0];
NSString *savePath = [documentsPath stringByAppendingString:fileName];
BOOL isStrated = [_mediaCapture startRecord:savePath videoStreamingQuality:paraCtx.videoStreamingQuality];
if (isStrated) {
_isRecording = YES;
}
}else {
BOOL isStoped = [_mediaCapture stopRecord];
if (isStoped) {
_isRecording = NO;
}
}
}
3.直播过程中也可以加入伴音,播放本地音频文件,比如直播中加入播放背景音乐,获取本地文件路径传入播放方法就可以了
NSString* musicFileURL = [[NSBundle mainBundle]pathForResource:@"lovest" ofType:@"mp3"];
if (musicFileURL == nil) {
NSLog(@"have not found music file");
return;
}
if (![_mediaCapture startPlayMusic:musicFileURL withEnableSignleFileLooped:YES]) {
NSLog(@"播放音乐文件失败");
return;
};
第二步:实现播放器拉流播放
作为对比,拉流实现创建两个播放器实例,一个进行普通直播拉流播放,用到普通的拉流地址,另一个进行低延时直播拉流播放,用到低延时拉流地址。低延时拉流地址以 nertc:// 格式开头。
传入拉流地址,创建播放器实例
第一个播放器播放低延时直播
NSURL *url = [NSURL URLWithString:@"低延时拉流地址"];
self.liveplayer = [[NELivePlayerController alloc] init];
NSError *error = [self.liveplayer setPlayUrl:url];
NSLog(@"---%@", error.localizedDescription);
1.添加播放视图
self.liveplayer.view.frame = _liveplayerView.bounds;
[_liveplayerView addSubview:self.liveplayer.view];
2.播放器播放配置
[self.liveplayer setBufferStrategy:NELPLowDelay]; // 直播低延时模式
[self.liveplayer setScalingMode:NELPMovieScalingModeNone]; // 设置画面显示模式,默认原始大小
[self.liveplayer setShouldAutoplay:YES]; // 设置prepareToPlay完成后是否自动播放
[self.liveplayer setPauseInBackground:NO]; // 设置切入后台时的状态,暂停还是继续播放
[self.liveplayer setPlaybackTimeout:15 *1000]; // 设置拉流超时时间
初始化视频文件为播放做准备
[self.liveplayer prepareToPlay];
第二个播放器播放普通直播
NSURL *commonUrl = [NSURL URLWithString:@"拉流地址"];
self.commonliveplayer = [[NELivePlayerController alloc] init];
NSError *error1 = [self.commonliveplayer setPlayUrl:commonUrl];
NSLog(@"---%@", error1.localizedDescription);
1.播放器播放配置
[self.liveplayer setShouldAutoplay:YES]; // 设置prepareToPlay完成后是否自动播放
[self.liveplayer setPauseInBackground:NO]; // 设置切入后台时的状态,暂停还是继续播放
[self.liveplayer setPlaybackTimeout:15 *1000]; // 设置拉流超时时间
初始化视频文件为播放做准备
[self.liveplayer prepareToPlay];
2.添加播放器初始化完成的通知回调
// 播放器媒体流初始化完成后触发,收到该通知表示可以开始播放
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerDidPreparedToPlay:)
name: NELivePlayerDidPreparedToPlayNotification
object: nil];
3.在播放器初始化完成的通知回调里面调用播放方法开始播放
- (void)NELivePlayerDidPreparedToPlay:(NSNotification*)notification {
[self.liveplayer play];
[self.commonliveplayer play];
}
4.停止播放销毁播放器实例
[self.liveplayer shutdown];
self.liveplayer = nil;
[self.commonliveplayer shutdown];
self.commonliveplayer = nil;
这样一个简单的直播推流到拉流播放的功能就完成了。更多有意思的配置和功能实现可以参考SDK的API说明一一探索。
进阶:
1.播放器相关监听,在播放前播放中播放后的一些时间添加监听通知 ,第一时间拿到通知回调 ,做一些业务上的处理
// 播放器媒体流初始化完成后触发,收到该通知表示可以开始播放
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerDidPreparedToPlay:)
name: NELivePlayerDidPreparedToPlayNotification
object: nil];
// 播放器加载状态发生变化时触发,如开始缓冲,缓冲结束
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NeLivePlayerloadStateChanged:)
name: NELivePlayerLoadStateChangedNotification
object: nil];
// 正常播放结束或播放过程中发生错误导致播放结束时触发的通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerPlayBackFinished:)
name: NELivePlayerPlaybackFinishedNotification
object: nil];
// 第一帧视频图像显示时触发的通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerFirstVideoDisplayed:)
name: NELivePlayerFirstVideoDisplayedNotification
object: nil];
// 第一帧音频播放时触发的通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerFirstAudioDisplayed:)
name: NELivePlayerFirstAudioDisplayedNotification
object: nil];
// 资源释放成功后触发的通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerReleaseSuccess:)
name: NELivePlayerReleaseSueecssNotification
object: nil];
// 视频码流解析失败时触发的通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerVideoParseError:)
name: NELivePlayerVideoParseErrorNotification
object: nil];
// seek完成通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerSeekComplete:)
name: NELivePlayerMoviePlayerSeekCompletedNotification
object: nil];
// HTTP状态通知
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerHttpCodeResponse:)
name: NELivePlayerHttpCodeResponseNotification
object: nil];
收到通知可以在通知方法里面做一些业务处理
// 初始化完成通知响应
- (void)NELivePlayerDidPreparedToPlay:(NSNotification*)notification {
[self.livepalyer play]; //如果设置shouldAutoplay为YES,此处可以不用调用play
}
// 播放完成通知响应
- (void)NELivePlayerPlayBackFinished:(NSNotification*)notification {
NSDictionary *userInfo = [notification userInfo];
NELPMovieFinishReason reason = [userInfo[NELivePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
switch (reason) {
case NELPMovieFinishReasonPlaybackEnded: //结束
break;
case NELPMovieFinishReasonPlaybackError: //失败
{
//获取错误码
NSInteger code = [userInfo[NELivePlayerPlaybackDidFinishErrorKey] integerValue];
break;
}
}
}
//HTTP状态响应
- (void)NELivePlayerHttpCodeResponse:(NSNotification *)notification {
NSDictionary * userInfo = [notification userInfo];
NELivePlayerHttpCodeModel *codeModel = userInfo[NELivePlayerHttpCodeResponseInfoKey];
// http code: codeModel.code
// http header: codeModel.header
}
2.视频本地缓存处理,可以快速切换播放视频 ,提高播放体验
NELPUrlConfig *urlConfig = [[NELPUrlConfig alloc] init];
urlConfig.cacheConfig = [[NELPUrlCacheConfig alloc] init];
urlConfig.cacheConfig.isCache = YES;
urlConfig.cacheConfig.cacheRootPath = nil;
//初始化方式一
self.liveplayer = [[NELivePlayerController alloc] initWithContentURL:url
config:urlConfig
error:&error];
//初始化方式二
self.liveplayer = [[NELivePlayerController alloc] init];
[self.liveplayer setPlayUrl:url config:urlConfig];
//切换方式
[self.liveplayer switchContentUrl:url config:urlConfig];
3.切换播放源,切换播放不同的地址 。
NELPUrlConfig *urlConfig = [[NELPUrlConfig alloc] init];
//配置缓存(非必须)
urlConfig.cacheConfig = [[NELPUrlCacheConfig alloc] init];
urlConfig.cacheConfig.isCache = YES;
urlConfig.cacheConfig.cacheRootPath = nil;
//切换
[self.liveplayer switchContentUrl:url config:urlConfig];
4.截图操作,播放的过程也支持截图操作 ,在需要截图的地方调用一下截图的方法就可以
UIImage *image = [self.liveplayer getSnapshot];
5.设置音量,播放过程中可以设置音量大小,开关静音
//设置静音
[self.liveplayer setMute:NO];
//设置音量
[self.liveplayer setVolume:1.0];
6.获取播放信息
NELivePlayerRealTimeInfo *info = [playView2 addSubview:_commonliveplayerView];
返回 参数 说明
videoReceiveBitrate
NSTimeInterval
视频接收的码率
videoReceiveFramerate
NSTimeInterval
视频接收的楨率
videoPlayFramerate
NSTimeInterval
视频播放的楨率
videoCacheDuration
NSTimeInterval
视频缓存的时长
videoCacheBytes
NSTimeInterval
视频缓存的大小
audioReceiveBitrate
NSTimeInterval
音频接收的码率
audioCacheDuration
NSTimeInterval
音频缓存的时长
audioCacheBytes
NSTimeInterval
音频缓存的大小
AVPlayTimeDifference
NSTimeInterval
音频和视频的播放时间差
注意 :
需要收到preparedToPlay通知之后再查询
以上为低延时直播功能的快速实现流程。更多接入指南请访问云信官网。
网易智企“易+”开源计划已正式发布低延时直播开源代码,点击链接立刻体验:https://github.com/GrowthEase/LLS-Player
「云信小课堂」推荐阅读
快速接入指南
(点击即可快速跳转)
音视频通话 | 构建本土「Clubhouse」
安卓端 PK 连麦 | iOS 端 PK 连麦
在线聊天室 | 聊天室内容审核
利用 UI 组件实现应用级别在线聊天室
协同办公系统 | 音视频安全检测