快速实现低延时直播
更新时间: 2022/07/18 18:04:47
本文介绍网易云信低延时直播主播端推流以及播放器的实现方法。
视频讲解
前期准备
- 开发工具:Xcode
- 开发语言:Objective-C
- 准备 iOS 运行环境:iOS 9.0 及以上系统,armv7、arm64、x86_64 的 CPU 架构(目前暂不支持 i386 架构)
- 创建应用并获取App Key
- 开通低延时直播
- 获取低延时直播的推拉流地址
注意事项
- 低延时直播的视频源必须不包含 B 帧,否则会出现画面跳跃等现象。
- 建议 GOP 关键帧间隔设置为 1~2 秒(否则会增加延时)。
集成推流 SDK 和播放器 SDK
步骤1 通过 CocoaPods 集成 SDK
请确保您的 Mac 已经安装 Ruby 环境。
-
安装 CocoaPods。
在终端窗口中输入如下命令:
sudo gem install cocoapods
-
创建 Podfile 文件。
从 Terminal 中进入您所创建项目所在路径,运行以下命令创建
Podfile
文件。pod init
-
编辑 Podfile 文件,在 Podfile 文件中增加类似如下代码:
# platform :ios, '9.0' target '{YourApp}' do pod 'NMCLiveStreaming', '~> {version}' pod 'NELivePlayer', '~> {version}' end
- YourApp:您的 Target 名称。
- version:待集成的播放器和推流 SDK 版本号。建议使用最新版本,请从 SDK 下载中心查看最新版本的版本号。
-
执行以下命令查询本地库版本。
pod search NELivePlayer
-
若不是最新版本,可以执行以下命令更新本地库版本。
pod repo update
-
执行以下命令安装安装 SDK。
pod install
步骤2 设置签名并添加媒体设备权限
-
设置签名。
- 在 Xcode 中,选择目标 TARGETS 和 Project Name,单击 Signing & Capabilities 页签,选中 Automatically manage signing。
- 在 Team 中选择您的开发团队。
-
在 Signing & Capabilities 页面,打开后台音频权限。
打开后台音频权限之后,应用在手机中后台运行时,SDK 默认在后台也可以继续处理音频流,维持通话。
-
编辑
info.plist
文件,授权麦克风、摄像头和 WiFi 的使用权限。使用 SDK 的音视频功能,需要授权麦克风和摄像头的使用权限。在 App 的
Info.plist
中添加以下三项。- Privacy - Microphone Usage Description,并填入麦克风使用目的提示语。
- Privacy - Camera Usage Description,并填入摄像头使用目的提示语。
- Application uses Wi-Fi,设置为 YES。
实现主播端推流
-
创建推流实例。
传入获取到的推流地址和自定义的推流配置。
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); }
-
设置画布开启推流的视频预览。
//开启预览 [self.mediaCapture startVideoPreview:self.localPreview];
-
开始推流直播。
推流之前需要在项目
info.plist
文件中配置请求允许 App 使用摄像头和麦克风。<key>NSCameraUsageDescription</key> <string>app需要使用摄像头进行直播录制</string> <key>NSMicrophoneUsageDescription</key> <string>app需要使用麦克风进行直播录制</string>
//开始推流 [_mediaCapture startLiveStream:^(NSError *error) { if (error) { NSLog(@"---%@", error.localizedDescription); } }];
这样本地摄像头采集的画面和声音就可以通过预览画布看到,并且推流直播出去了。其他用户就可以通过对应的拉流地址和低延时拉流地址播放观看了。
-
结束推流直播,释放推流实例。
//结束预览 [self.mediaCapture pauseVideoPreview]; //结束推流 [_mediaCapture stopLiveStream:^(NSError *error) { if (error) { NSLog(@"---%@", error.localizedDescription); } }]; //释放 [_mediaCapture unInitLiveStream]; _mediaCapture = nil;
进阶
-
在直播过程中 ,可以进行视频推流的相关操作,例如中断和恢复音频,中断和恢复视频。
[_mediaCapture resumeVideoLiveStream]; //恢复视频推流 [_mediaCapture pauseVideoLiveStream];//中断视频推流 [_mediaCapture pauseAudioLiveStream];//中断音频推流 [_mediaCapture resumeAudioLiveStream];//恢复音频推流
-
直播过程中还可以进行 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; } } }
-
直播过程中也可以加入伴音,播放本地音频文件,例如在直播中加入播放背景音乐。
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; };
实现播放器拉流播放
您可以分别创建普通直播播放器和低延时直播播放器,对比两者的时延差异。
步骤1 创建低延时直播的播放器
-
传入拉流地址,创建低延时直播的播放器实例。
NSURL *url = [NSURL URLWithString:@"低延时拉流地址"]; self.liveplayer = [[NELivePlayerController alloc] init]; NSError *error = [self.liveplayer setPlayUrl:url]; NSLog(@"---%@", error.localizedDescription);
传入的拉流地址为网易云信控制台上获取的拉流地址(NERTC),获取方法请参见获取低延时直播的推拉流地址。示例如下:
nertc://xxx.live.126.net/live/xxxxxxx?xxx=xxx
-
添加播放视图。
self.liveplayer.view.frame = _liveplayerView.bounds; [_liveplayerView addSubview:self.liveplayer.view];
-
播放器播放配置。
[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];
步骤2 创建普通直播的播放器
-
传入拉流地址,创建播放器实例。
NSURL *commonUrl = [NSURL URLWithString:@"拉流地址"]; self.commonliveplayer = [[NELivePlayerController alloc] init]; NSError *error1 = [self.commonliveplayer setPlayUrl:commonUrl]; NSLog(@"---%@", error1.localizedDescription);
-
添加播放视图。
self.commonliveplayer.view.frame = _liveplayerView.bounds; [_commonliveplayerView addSubview:self.liveplayer.view];
-
播放器播放配置。
[self.commonliveplayer setShouldAutoplay:YES]; // 设置prepareToPlay完成后是否自动播放 [self.commonliveplayer setPauseInBackground:NO]; // 设置切入后台时的状态,暂停还是继续播放 [self.commonliveplayer setPlaybackTimeout:15 *1000]; // 设置拉流超时时间
-
初始化视频文件为播放做准备。
[self.commonliveplayer prepareToPlay];
步骤3 添加通知回调
添加播放器初始化完成的通知回调
// 播放器媒体流初始化完成后触发,收到该通知表示可以开始播放
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(NELivePlayerDidPreparedToPlay:)
name: NELivePlayerDidPreparedToPlayNotification
object: nil];
步骤4 开始播放
在播放器初始化完成的通知回调里面,调用 play
方法开始播放。
- (void)NELivePlayerDidPreparedToPlay:(NSNotification*)notification {
[self.liveplayer play];
[self.commonliveplayer play];
}
步骤5 销毁播放器实例
停止播放,销毁播放器实例。
[self.liveplayer shutdown];
self.liveplayer = nil;
[self.commonliveplayer shutdown];
self.commonliveplayer = nil;
进阶
- 播放器相关监听,在播放前、播放中、播放后的一些时间添加监听通知 ,第一时间拿到通知回调 ,做一些业务上的处理 。
// 播放器媒体流初始化完成后触发,收到该通知表示可以开始播放
[[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
}
- 视频本地缓存处理,可以快速切换播放视频 ,提高播放体验。
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];
- 切换播放源,切换播放不同的地址。
NELPUrlConfig *urlConfig = [[NELPUrlConfig alloc] init];
//配置缓存(非必须)
urlConfig.cacheConfig = [[NELPUrlCacheConfig alloc] init];
urlConfig.cacheConfig.isCache = YES;
urlConfig.cacheConfig.cacheRootPath = nil;
//切换
[self.liveplayer switchContentUrl:url config:urlConfig];
- 截图操作,播放的过程也支持截图操作,在需要截图的地方调用一下截图的方法就可以。
UIImage *image = [self.liveplayer getSnapshot];
- 设置音量,播放过程中可以设置音量大小,开关静音。
//设置静音
[self.liveplayer setMute:NO];
//设置音量
[self.liveplayer setVolume:1.0];
- 获取播放信息。
NELivePlayerRealTimeInfo *info = [playView2 addSubview:_commonliveplayerView];
返回 | 参数 | 说明 |
---|---|---|
videoReceiveBitrate | NSTimeInterval | 视频接收的码率 |
videoReceiveFramerate | 视频接收的帧率 | |
videoPlayFramerate | 视频播放的帧率 | |
videoCacheDuration | 视频缓存的时长 | |
videoCacheBytes | 视频缓存的大小 | |
audioReceiveBitrate | 音频接收的码率 | |
audioCacheDuration | 音频缓存的时长 | |
audioCacheBytes | 音频缓存的大小 | |
AVPlayTimeDifference | 音频和视频的播放时间差 |
查询播放的实时信息相关参数,需要收到 preparedToPlay
通知之后再查询。