快速实现低延时直播

更新时间: 2022/07/18 10:04:47

本文介绍网易云信低延时直播主播端推流以及播放器的实现方法。

视频讲解

前期准备

注意事项

  • 低延时直播的视频源必须不包含 B 帧,否则会出现画面跳跃等现象。
  • 建议 GOP 关键帧间隔设置为 1~2 秒(否则会增加延时)。

集成推流 SDK 和播放器 SDK

步骤1 通过 CocoaPods 集成 SDK

请确保您的 Mac 已经安装 Ruby 环境。

  1. 安装 CocoaPods。

    在终端窗口中输入如下命令:

    sudo gem install cocoapods
    
  2. 创建 Podfile 文件。

    从 Terminal 中进入您所创建项目所在路径,运行以下命令创建 Podfile 文件。

    pod init
    
  3. 编辑 Podfile 文件,在 Podfile 文件中增加类似如下代码:

    # platform :ios, '9.0' 
    target '{YourApp}' do
        pod 'NMCLiveStreaming', '~> {version}'
        pod 'NELivePlayer', '~> {version}'
    end
    
    • YourApp:您的 Target 名称。
    • version:待集成的播放器和推流 SDK 版本号。建议使用最新版本,请从 SDK 下载中心查看最新版本的版本号。
  4. 执行以下命令查询本地库版本。

    pod search NELivePlayer         
    
  5. 若不是最新版本,可以执行以下命令更新本地库版本。

    pod repo update
    
  6. 执行以下命令安装安装 SDK。

    pod install        
    

步骤2 设置签名并添加媒体设备权限

  1. 设置签名。

    1. 在 Xcode 中,选择目标 TARGETS 和 Project Name,单击 Signing & Capabilities 页签,选中 Automatically manage signing

    Xcode_Signing.png

    1. 在 Team 中选择您的开发团队。
  2. Signing & Capabilities 页面,打开后台音频权限。

    打开后台音频权限之后,应用在手机中后台运行时,SDK 默认在后台也可以继续处理音频流,维持通话。

  3. 编辑 info.plist 文件,授权麦克风、摄像头和 WiFi 的使用权限。

    使用 SDK 的音视频功能,需要授权麦克风和摄像头的使用权限。在 App 的 Info.plist 中添加以下三项。

    • Privacy - Microphone Usage Description,并填入麦克风使用目的提示语。
    • Privacy - Camera Usage Description,并填入摄像头使用目的提示语。
    • Application uses Wi-Fi,设置为 YES

实现主播端推流

  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 使用摄像头和麦克风。

    <key>NSCameraUsageDescription</key>
    <string>app需要使用摄像头进行直播录制</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>app需要使用麦克风进行直播录制</string>
    
    //开始推流
        [_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 录制,录制直播内容之后保存在本地。

    1. 先创建一个本地路径 ,传入相关方法就可以了 。
    2. 也可以进行录制的配置 ,示例代码如下 :
    - (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;
                        };
    

实现播放器拉流播放

您可以分别创建普通直播播放器和低延时直播播放器,对比两者的时延差异。

步骤1 创建低延时直播的播放器

  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

  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]; // 设置拉流超时时间
    
  3. 初始化视频文件为播放做准备。

    [self.liveplayer prepareToPlay];
    

步骤2 创建普通直播的播放器

  1. 传入拉流地址,创建播放器实例。

    NSURL *commonUrl = [NSURL URLWithString:@"拉流地址"];
    self.commonliveplayer = [[NELivePlayerController alloc] init];
    NSError *error1 = [self.commonliveplayer setPlayUrl:commonUrl];
    NSLog(@"---%@", error1.localizedDescription);
    
  2. 添加播放视图。

    self.commonliveplayer.view.frame = _liveplayerView.bounds;
    [_commonliveplayerView addSubview:self.liveplayer.view];
    
  3. 播放器播放配置。

    [self.commonliveplayer setShouldAutoplay:YES]; // 设置prepareToPlay完成后是否自动播放
    [self.commonliveplayer setPauseInBackground:NO]; // 设置切入后台时的状态,暂停还是继续播放
    [self.commonliveplayer setPlaybackTimeout:15 *1000]; // 设置拉流超时时间
    
  4. 初始化视频文件为播放做准备。

    [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;

进阶

  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
}
  1. 视频本地缓存处理,可以快速切换播放视频 ,提高播放体验。
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];

  1. 切换播放源,切换播放不同的地址。
NELPUrlConfig *urlConfig = [[NELPUrlConfig alloc] init];
        
   //配置缓存(非必须)
   urlConfig.cacheConfig = [[NELPUrlCacheConfig alloc] init];
   urlConfig.cacheConfig.isCache = YES;
   urlConfig.cacheConfig.cacheRootPath = nil;
    //切换
    [self.liveplayer switchContentUrl:url config:urlConfig];
  1. 截图操作,播放的过程也支持截图操作,在需要截图的地方调用一下截图的方法就可以。
UIImage *image = [self.liveplayer getSnapshot];

  1. 设置音量,播放过程中可以设置音量大小,开关静音。
//设置静音
[self.liveplayer setMute:NO];
//设置音量
[self.liveplayer setVolume:1.0];

  1. 获取播放信息。
NELivePlayerRealTimeInfo *info = [playView2 addSubview:_commonliveplayerView];
返回 参数 说明
videoReceiveBitrate NSTimeInterval 视频接收的码率
videoReceiveFramerate 视频接收的帧率
videoPlayFramerate 视频播放的帧率
videoCacheDuration 视频缓存的时长
videoCacheBytes 视频缓存的大小
audioReceiveBitrate 音频接收的码率
audioCacheDuration 音频缓存的时长
audioCacheBytes 音频缓存的大小
AVPlayTimeDifference 音频和视频的播放时间差

查询播放的实时信息相关参数,需要收到 preparedToPlay 通知之后再查询。

此文档是否对你有帮助?
有帮助
去反馈
  • 视频讲解
  • 前期准备
  • 注意事项
  • 集成推流 SDK 和播放器 SDK
  • 步骤1 通过 CocoaPods 集成 SDK
  • 步骤2 设置签名并添加媒体设备权限
  • 实现主播端推流
  • 进阶
  • 实现播放器拉流播放
  • 步骤1 创建低延时直播的播放器
  • 步骤2 创建普通直播的播放器
  • 步骤3 添加通知回调
  • 步骤4 开始播放
  • 步骤5 销毁播放器实例
  • 进阶