iOS 直播推流 SDK 开发指南

1 开发准备

2 快速集成

3 直播接口说明

SDK 所有功能都封装在 LSMediaCapture 类中,下面将详细介绍如何调用SDK接口实现推流以及一些定制化的特色功能:水印,美颜等。

3.1 初始化推流

SDK在初始化推流阶段,设置推流地址,配置推流参数,创建推流session

3.1.1 提供的接口有三种,您可以根据是否需要定制直播参数而选择

-(instancetype)initLiveStream:(NSString *)liveStreamingURL
-(instancetype)initLiveStream:(NSString *)liveStreamingURL 
  withVideoParaCtx:(LSVideoParaCtx)videoParaCtx
-(instancetype)initLiveStream:(NSString *)liveStreamingURL
  withLivestreamParaCtx:(LSLiveStreamingParaCtx)lsParaCtx
-(void)unInitLiveStream
建议在调用unInitLiveStream之后,将mediaCapture实例对象置为空,用于完全释放资源

3.1.2 直播推流地址

如上文开发准备所述,直播推流地址由网易云视频注册用户通过调用服务端api产生,或者从官网管理平台手动创建频道获得,格式如:rtmp://pxxxx.live.126.net/live/channelID

3.1.3 直播推流参数

    lsParaCtx.sLSVideoParaCtx.fps = 24;                   // 帧率
    lsParaCtx.sLSVideoParaCtx.bitrate = 640000;        // 码率
    lsParaCtx.sLSVideoParaCtx.videoStreamingQuality = LS_VIDEO_QUALITY_HIGH  //分辨率

其中视频分辨率参数为 lsParaCtx.sLSVideoParaCtx.videoStreamingQuality

    LS_VIDEO_QUALITY_LOW,           //!< 视频分辨率: 352*288.
    LS_VIDEO_QUALITY_MEDIUM,        //!< 视频分辨率: 480*360.
    LS_VIDEO_QUALITY_HIGH,          //!< 视频分辨率: 640*480.
    LS_VIDEO_QUALITY_SUPER,         //!< 视频分辨率: 960*540.
    LS_VIDEO_QUALITY_SUPER_HIGH,    //!< 视频分辨率: 1280*720.

视频参数中其它与具体功能相关的参数将在后续用到时具体介绍,先简单列出,供您参考

    //此字段用来设置LSMediaCapture初始化的摄像头为前置或后置,当然在初始化之后,任意时间都可以通过switchCamera接口去切换摄像头
    lsParaCtx.sLSVideoParaCtx.cameraPosition = LS_CAMERA_POSITION_Back;
    //此字段用来设置LSMediaCapture初始化的摄像头方向,目前不支持直播过程中切换摄像头方向
    lsParaCtx.sLSVideoParaCtx.interfaceOrientation =LS_CAMERA_ORIENTATION_RIGHT;
    lsParaCtx.sLSVideoParaCtx.videoRenderMode = LS_VIDEO_RENDER_MODE_SCALE_NONE;//视频显示是否为16:9 模式
    lsParaCtx.sLSVideoParaCtx.filterType = LS_GPUIMAGE_ZIRAN;//打开自然这款滤镜模式

    lsParaCtx.sLSVideoParaCtx.isCameraFlashEnabled = NO;//是否打开摄像头flash功能
    lsParaCtx.sLSVideoParaCtx.isCameraZoomPinchGestureOn = NO;//是否打开摄像头zoom功能
    lsParaCtx.sLSVideoParaCtx.isVideoWaterMarkEnabled = NO;//是否支持视频水印功能
    lsParaCtx.sLSVideoParaCtx.isVideoFilterOn = NO;//是否支持视频滤镜功能
    lsParaCtx.sLSVideoParaCtx.isQosOn = YES;//是否支持动态自适应码率调整
    lsParaCtx.sLSAudioParaCtx.frameSize = 2048;//此字段用来设置音频采集的帧大小
    lsParaCtx.sLSAudioParaCtx.bitrate = 64000;//此字段用来设置音频编码码率
    lsParaCtx.sLSAudioParaCtx.samplerate = 44100;//此字段用来设置音频采样率
    lsParaCtx.sLSAudioParaCtx.numOfChannels = 1;//此字段用来设置音频采集编码声道
    lsParaCtx.sLSAudioParaCtx.codec = LS_AUDIO_CODEC_AAC;//此字段用来设置  音频的编码器类型,目前仅支持AAC
 //设置推流地址
@property(nonatomic,copy)NSString* pushUrl;
 //设置视频关键参数
 - (void)setVideoParameters:(LSVideoStreamingQuality)videoResolution
                   bitrate:(int)bitrate
                       fps:(int)fps
         cameraOrientation:(LSCameraOrientation) cameraOrientation;

3.2 开始推流

3.3 结束推流

3.4 视频预览

SDK提供视频预览功能,上层需要创建 UIView 对象,作为视频预览的窗口

3.5 美颜

如果您已经通过 lsParaCtx.sLSVideoParaCtx.isVideoFilterOn 开启了美颜功能,初始化推流阶段我们可以选择一种滤镜类型通过lsParaCtx.sLSVideoParaCtx.setVideoFilterType 目前sdk提供以下几种滤镜类型,分别为黑白,自然,怀旧,粉嫩,建议用户选择自然这款滤镜,您还可以在任何时刻随时改变您的滤镜类型,通过下列接口实现。

- (void)setFilterType:(LSGpuImageFilterType)filterType;

3.6 设置摄像头

SDK提供丰富的摄像头设置功能,以满足您定制化的需求

3.7 水印

如果已经通过 lsParaCtx.sLSVideoParaCtx.isVideoWaterMarkEnabled 开启了水印功能,你可以在任意时刻设置水印:

/**
 *  设置水印,
 *  @param  image 水印图片
 *  @param  rect 水印图片位置
 *  @param  rect 水印图片标准位置,左右上下,中心店,当第一种位置模式时,具体位置由rect origin点决定
 *  @return 当前摄像头的位置,前或者后
 */

- (void) addWaterMark: (UIImage*) image
                 rect: (CGRect) rect
             location: (LSWaterMarkLocation) location;


/**
 *  设置动态水印,随着时间变化水印图片变化
 *  @param  imageArray 水印图片数组
 *  @param  fps 动态水印的变化频率 fps 次/s,建议和摄像头采集帧率匹配,最大帧率为摄像头采集的频率
 *  @param  interval 水印滚动动态间隔
 *  @param  rect 水印图片位置
 *  @param  rect 水印图片标准位置,左右上下,中心店,当第一种位置模式时,具体位置由rect origin点决定
 *  @return 当前摄像头的位置,前或者后
 */

- (void) addDynamicWaterMarks: (NSArray*) imageArray
                          fps: (unsigned int) fps
                         loop: (BOOL)looped
                         rect: (CGRect) rect
                     location: (LSWaterMarkLocation) location;

3.8 直播统计信息

当直播开启后,您只要实现了如下接口,则可以实时获取每秒视频的发送帧率、发送码率,音频的发送码率的信息,以此来监听直播流的健康状况

@property (nonatomic,copy) void (^onStatisticInfoGot)(LSStatistics* statistics)

3.9 动态码率调整

如果已经通过 lsParaCtx.sLSVideoParaCtx.isQosOn == YES 开启了动态码率支适应调整功能,sdk将根据自身音视频流发送状况,相应的调节发送的码率,频率等,以满足您在当前可用网络带宽下的最大吞吐率。

3.10 视频截图

SDK提供推流过程中任意时刻视频图像的uiimage回调,如果您开启了美颜功能,则uiimage是经过滤镜处理的图像,您可以基于uiimage做内容分析(鉴黄)或实时封面等

typedef void(^LSFrameCaptureCompletionBlock)(UIImage *latestFrameImage);

3.11 音视频采集的裸流回调

SDK提供摄像头采集的rgba流回调和采集声音的PCM流回调,便于开发者可以自定义的对推流前的裸流数据进行加工,加工后的数据我们会推流出去。 setCaptureRawDataCB在startlivestream之前调用。

// 摄像头裸流的回调,开发者可以修改该数据,美颜增加滤镜等,推出的流便随之发生变化
typedef void(^LSCameraCaptureDataCB)( uint8_t *uiRgbaData, int iWideth, int iHeight);

//声音采集的原始pcm数据回调,开发者可以修改该pcm,推出的声音便随之发生变化
typedef void(^LSAudioCaptureDataCB)( uint8_t *uiPcmData, int iLen);

//  设置音视频采集的原始裸流回调,在startlivestream之前调用
- (void)setCaptureRawDataCB:(LSCameraCaptureDataCB)cameraCaptureDataCB audioRawDataCB:(LSAudioCaptureDataCB)audioRawDataCB;

4 代码示例

    - (BOOL)requestMediaCapturerAccessWithCompletionHandler:(void (^)(BOOL, NSError*))handler {
    AVAuthorizationStatus videoAuthorStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    AVAuthorizationStatus audioAuthorStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];

    if (AVAuthorizationStatusAuthorized == videoAuthorStatus && AVAuthorizationStatusAuthorized == audioAuthorStatus) {
        handler(YES,nil);
    }else{
        if (AVAuthorizationStatusRestricted == videoAuthorStatus || AVAuthorizationStatusDenied == videoAuthorStatus) {
            NSString *errMsg = NSLocalizedString(@"此应用需要访问摄像头,请设置", @"此应用需要访问摄像头,请设置");
            NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errMsg};
            NSError *error = [NSError errorWithDomain:@"访问权限" code:0 userInfo:userInfo];
            handler(NO,error);

            return NO;
        }

        if (AVAuthorizationStatusRestricted == audioAuthorStatus || AVAuthorizationStatusDenied == audioAuthorStatus) {
            NSString *errMsg = NSLocalizedString(@"此应用需要访问麦克风,请设置", @"此应用需要访问麦克风,请设置");
            NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errMsg};
            NSError *error = [NSError errorWithDomain:@"访问权限" code:0 userInfo:userInfo];
            handler(NO,error);

            return NO;
        }

        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            if (granted) {
                [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
                    if (granted) {
                        handler(YES,nil);
                    }else{
                        NSString *errMsg = NSLocalizedString(@"不允许访问麦克风", @"不允许访问麦克风");
                        NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errMsg};
                        NSError *error = [NSError errorWithDomain:@"访问权限" code:0 userInfo:userInfo];
                        handler(NO,error);
                    }
                }];
            }else{
                NSString *errMsg = NSLocalizedString(@"不允许访问摄像头", @"不允许访问摄像头");
                NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errMsg};
                NSError *error = [NSError errorWithDomain:@"访问权限" code:0 userInfo:userInfo];
                handler(NO,error);
            }
        }];

    }
    return YES;
    }
    LSLiveStreamingParaCtx paraCtx;//直播推流参数

    paraCtx.eHaraWareEncType             = LS_HRD_NO;
    paraCtx.eOutFormatType               = LS_OUT_FMT_RTMP;
    paraCtx.eOutStreamType               = LS_HAVE_AV; //这里可以设置推音视频流/音频流/视频流,如果只推送视频流,则不支持伴奏播放音乐

    //视频相关参数
    paraCtx.sLSVideoParaCtx.interfaceOrientation       = LS_CAMERA_ORIENTATION_PORTRAIT;//摄像头的方向,可以选择横屏或者竖屏
    paraCtx.sLSVideoParaCtx.cameraPosition             = LS_CAMERA_POSITION_FRONT;//前后摄像头
    paraCtx.sLSVideoParaCtx.bitrate                    = 640000; //码率
    paraCtx.sLSVideoParaCtx.fps                        = 24;     //帧率
    paraCtx.sLSVideoParaCtx.videoStreamingQuality      = LS_VIDEO_QUALITY_HIGH; //分辨率

    paraCtx.sLSVideoParaCtx.isCameraZoomPinchGestureOn = YES; //打开摄像头zoom功能
    paraCtx.sLSVideoParaCtx.isCameraFlashEnabled       = YES; //打开摄像头flash功能
    paraCtx.sLSVideoParaCtx.isVideoWaterMarkEnabled    = YES; //开启水印
    paraCtx.sLSVideoParaCtx.videoRenderMode            = LS_VIDEO_RENDER_MODE_SCALE_16x9;//设置为16:9模式 //对端接收的图像将以16:9比例绘制
    paraCtx.sLSVideoParaCtx.isVideoFilterOn            = YES;  //开启美颜
    paraCtx.sLSVideoParaCtx.filterType                 = LS_GPUIMAGE_ZIRAN;//默认使用这种滤镜
    paraCtx.sLSVideoParaCtx.isQosOn                    = YES;  // 开启码率自适应调整功能


    //音频相关参数
    paraCtx.sLSAudioParaCtx.bitrate       = 64000;
    paraCtx.sLSAudioParaCtx.codec         = LS_AUDIO_CODEC_AAC;
    paraCtx.sLSAudioParaCtx.frameSize     = 2048;
    paraCtx.sLSAudioParaCtx.numOfChannels = 1;
    paraCtx.sLSAudioParaCtx.samplerate    = 44100;


    NSString* _streamUrl = @"rtmp:pxxxxx" ;//初始化阶段允许 此字段为nil

    LSMediaCapture* _mediaCapture;

    _mediaCapture = [[LSMediaCapture alloc]initLiveStream:_streamUrl withLivestreamParaCtx:paraCtx]; //初始化推流
    [_mediaCapture startVideoPreview:self.localPreview];
     _mediaCapture.pushUrl = @"rtmp://pxxx";
     [_mediaCapture setVideoParameters:LS_VIDEO_QUALITY_LOW bitrate:150000 fps:30 cameraOrientation:LS_CAMERA_ORIENTATION_PORTRAIT];//视频的分辨率,码率,帧率,以及视频的方向横屏或者竖屏

     //如果摄像头方向发生变化了,想要camera的预览画面跟着旋转,则在调用一次开启预览
     [_mediaCapture startVideoPreview:self.localPreview];
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onStartLiveStream:) name:LS_LiveStreaming_Started object:nil]; //直播开始通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onFinishedLiveStream:) name:LS_LiveStreaming_Finished object:nil]; // 直播结束通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onBadNetworking:) name:LS_LiveStreaming_Bad object:nil]; //直播过程中网络差通知
     __weak MediaCaptureViewController *weakSelf = self;
     _mediaCapture.onLiveStreamError = ^(NSError* error)
     {
         if (error != nil) {
            [weakSelf LiveStreamErrorInterrup];

          }
     };
     //调用统计数据回调
     _mediaCapture.onStatisticInfoGot = ^(LSStatistics* statistics)
     {
        if (statistics != nil) {
            LSStatistics statis;
            memcpy(&statis, statistics, sizeof(LSStatistics));

            dispatch_async(dispatch_get_main_queue(),^(void) {[weakSelf showStatInfo:statis];});
        }
     };
     UIImage* image = [UIImage imageNamed:[[[NSBundle mainBundle] bundlePath]stringByAppendingPathComponent:@"logo.png"]];
     [_mediaCapture addWaterMark:image rect:CGRectMake(20, 20, 60, 34) location:LS_WATERMARK_LOCATION_RIGHTUP];
     NSError* error =nil;
     [_mediaCapture startLiveStreamWithError:&error];
     if (error) {
        [self showErrorInfo:error ];//直播出错可以将出错信息直接显示给用户,也可以封装成统一格式,告诉用户检查网络等情况,尝试重新开启直播
        return;
     }
    -(void)onStartLiveStream:(NSNotification*)notification
    {
        NSLog(@"on start live stream");//只有收到直播开始的 信号,才可以关闭直播

        __weak MediaCaptureViewController *weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^(void){
            _isLiving = YES;
            weakSelf.showStatButton.enabled = YES;


            UIImage *startBtnImage = [UIImage imageNamed:@"pause0.png"];
            [weakSelf.startButton setImage:startBtnImage  forState:UIControlStateNormal];

            //当直播开始时,获取当前最新的一张图片
            [ weakSelf.mediaCapture snapShotWithCompletionBlock:^(UIImage *latestFrameImage) {

                UIImageWriteToSavedPhotosAlbum(latestFrameImage, weakSelf, nil, nil);

            }];

        });
    }
    -(void)LiveStreamErrorInterrup{

    [_mediaCapture stopLiveStream:^(NSError *error) {

        if (error == nil) {
            NSLog(@"直播结束了");
                     }
        else{
            NSLog(@"结束直播发生错误");//需要等待一会儿,譬如sleep(2)后,再次调用 stopLiveStream()
        }

    }];
    __weak MediaCaptureViewController *weakSelf1 = self;
    [_mediaCapture stopLiveStream:^(NSError *error) {

           if (error == nil) {
            NSLog(@"直播结束了");
                     }
        else{
            NSLog(@"结束直播发生错误");//需要等待一会儿,譬如sleep(2)后,再次调用 stopLiveStream()



            }

        }];

5 API说明

有关API的详细说明,可参见SDK包中docs,打开index.html查看,或者打开下面的在线文档。

网易云视频直播推流LiveStreaming iOS SDK API详细文档