最近更新时间:2023.09.27 15:15:22
首次发布时间:2022.01.21 18:42:59
本章节为您介绍推流 SDK 进阶功能的接入方式,您可以根据实际业务需求,借助推流 SDK 实现更复杂的功能。
本文档适用于 V1.39.1 版本的客户端 SDK,其他版本请参考历史版本文档。
真机调试:由于 SDK 使用了大量 iOS 系统的音视频接口,这些接口在仿真模拟器下可能会出现异常,推荐您使用真机进行代码调试。
视频直播推流 SDK 支持 RTM 协议推流,详细信息可参考超低延时直播介绍。
使用视频直播控制台的地址生成器,生成 RTM 推流地址。
开始推流时,将 SDK 的推流地址设置为 RTM 地址。代码示例如下所示:
[self.livePusher startPush:@"http://example.push/stream.sdp"];
(可选)配置自动降级。代码示例如下所示:
说明
配置自动降级后,RTM 推流失败时,推流 SDK 将自动降级到 RTMP 推流。使用此功能需同时设置 RTM 和 RTMP 推流地址。
[self.livePusher startPushWithUrls:@[@"http://example.push/stream.sdp", // 添加 RTM 协议推流地址 @"rtmp://example.push/stream"]]; // 添加 RTMP 协议推流降级地址
视频直播推流 SDK 支持 QUIC 协议推流。
使用视频直播控制台的地址生成器,生成 RTMP 推流地址。
修改推流地址协议,将 RTMP 推流地址中的 rtmp 协议修改为 rtmpq。例如:
rtmp://example.push/stream
rtmpq://example.push/stream
[self.livePusher startPush:@"rtmpq://example.push/stream"];//添加 QUIC 协议推流地址
说明
推流 SDK 默认开启 QUIC 协议推流的自动降级策略。无需额外配置,QUIC 协议推流失败后,将自动降级为 RTMP 协议推流。
使用多 URL 推流,可以在推流失败后自动切换到下一个推流地址继续推流,提高推流的稳定性和可靠性。
说明
多 URL 推流的地址数量无限制,推流引擎会按顺序遍历所有的推流地址,直至最后无可用地址后报错。
代码示例如下所示:
[self.livePusher startPushWithUrls:@[@"rtmp://example.push/stream_1", @"rtmp://example.push/stream_2"]];
使用 H.265 硬件编码,可在相同画质下有效降低带宽开销;也可在带宽开销相同时,提供更优异的画质体验。
说明
配置 H.265 编码的代码示例如下所示:
// 视频编码配置 VeLiveVideoEncoderConfiguration *videoEncodeCfg = [[VeLiveVideoEncoderConfiguration alloc] initWithResolution:(VeLiveVideoResolution720P)]; // 设置编码类型为 H.265 videoEncodeCfg.codec = VeLiveVideoCodecByteVC1; // 配置编码 [self.livePusher setVideoEncoderConfiguration:videoEncodeCfg];
如您的 App 已经实现了音视频的采集和处理,希望将推流 SDK 作为推流引擎使用,可参考以下外部采集源推流功能的使用方式。
推流引擎支持传入 OpenGL 纹理 、NSData 二进制数据、CVPixelBuffer 和 CMSampleBufferRef 格式的视频数据。
说明
推流引擎开启外部视频采集。代码示例如下所示:
// 开启外部视频采集 [self.livePusher startVideoCapture:(VeLiveVideoCaptureExternal)];
送入视频帧数据。代码示例如下所示:
VeLiveVideoFrame *videoFrame = [[VeLiveVideoFrame alloc] init]; videoFrame.bufferType = VeLiveVideoBufferTypeTexture; videoFrame.width = 720; videoFrame.height = 1280; videoFrame.pts = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000000000); videoFrame.textureId = 0; videoFrame.pixelFormat = VeLivePixelFormat2DTexture; [videoFrame setReleaseCallback:^{ // 当videoFrame释放的时候调用,释放内存 }]; [self.livePusher pushExternalVideoFrame:videoFrame]; ```
推流引擎支持传入 NSData 二进制数据和 CMSampleBufferRef 格式的音频数据。
说明
二进制数据支持视音频采样率为 44100 Hz,单/双声道音频数据。
推流引擎开启外部音频采集。代码示例如下所示:
// 开启外部音频采集 [self.livePusher startAudioCapture:(VeLiveAudioCaptureExternal)];
送入音频数据。代码示例如下所示:
VeLiveAudioFrame *audioFrame = [[VeLiveAudioFrame alloc] init]; audioFrame.bufferType = VeLiveAudioBufferTypeNSData; // 此处示例音频采样率为 44100 Hz,位深 16 bit,双通道 audioFrame.data = [[NSData alloc] initWithBytes:nil length:(44100 * 2 * 2)]; audioFrame.sampleRate = VeLiveAudioSampleRate44100; audioFrame.channels = VeLiveAudioChannelStereo; // VeLiveAudioChannelMono audioFrame.pts = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000000000); [self.livePusher pushExternalAudioFrame:audioFrame]; ```
除主要视频输入源 Main Input,推流引擎支持添加多个视频输入源,推流引擎内部会自动将所有输入源合并成同一视图后传送给传输模块。
添加视频源输入流。代码示例如下所示:
VeLiveMixerManager *mixerManager = [self.livePusher getMixerManager]; self.mixerVideoId = [mixerManager addVideoStream];
更新视频源输入信息。代码示例如下所示:
streamId(图层 ID):用于识别输入的数据流,即要混流的视频流的标识。通常在创建混流配置时使用。
视频画面展示位置及大小:通过设置 x、y、width、height 来控制视频图层在画布中的位置和大小,例如 (0.0, 0.0, 1.0, 1.0) 表示平铺整个屏幕。
videoLayout.x = 0;
这表示视频图层在水平方向(x轴)上的位置,取值范围为 [0.0, 1.0],其中 0.0 表示位于左边缘,1.0 表示位于右边缘。videoLayout.y = 0;
这表示视频图层在垂直方向(y轴)上的位置,取值范围也为 [0.0, 1.0],其中 0.0 表示位于顶部,1.0 表示位于底部。videoLayout.width = 0.5;
这表示视频图层的宽度,取值范围为 [0.0, 1.0],其中 1.0 表示等于整个画布宽度。videoLayout.height = 0.3;
这表示视频图层的高度,取值范围同样为 [0.0, 1.0],其中 1.0 表示等于整个画布高度。zOrder(图层位置):图层的层级位置,值越大,图层越高,显示在其他图层之上。
renderMode(填充模式):枚举值,用于指定视频的填充模式,决定了如何在画布中展示视频。分为三种模式:
VeLivePusherRenderModeHidden
视窗填满优先:视频帧等比缩放,填满视窗,多余部分被裁剪。VeLivePusherRenderModeFit
视频帧内容全部显示优先:将视频尺寸等比缩放,保证视频内容全部显示。未填满的区域会被背景颜色填充。VeLivePusherRenderModeFill
视频帧自适应画布:视频帧非等比缩放,填满画布,可能会导致视频帧的长宽比例变化。VeLiveStreamMixDescription *description = [[VeLiveStreamMixDescription alloc] init]; VeLiveMixVideoLayout *videoLayout = [[VeLiveMixVideoLayout alloc] init]; videoLayout.streamId = self.mixerVideoId; videoLayout.x = 0; videoLayout.y = 0; videoLayout.width = 0.5; videoLayout.height = 0.3; videoLayout.zOrder = 1; videoLayout.renderMode = VeLivePusherRenderModeHidden; description.mixVideoStreams = @[videoLayout]; [self.livePusher.getMixerManager updateStreamMixDescription:description];
输入视频数据。代码示例如下所示:
说明
VeLiveVideoFrame *videoFrame = [[VeLiveVideoFrame alloc] init]; videoFrame.bufferType = VeLiveVideoBufferTypePixelBuffer; videoFrame.pts = CMTimeMakeWithSeconds(CACurrentMediaTime(), 1000000000); videoFrame.pixelBuffer = pixelBuffer; [self.livePusher.getMixerManager sendCustomVideoFrame:videoFrame streamId:self.mixerVideoId];
移除视频源输入流。代码示例如下所示:
[self.livePusher.getMixerManager removeVideoStream:self.mixerVideoId];
推流 SDK 除了支持手机摄像头采集推流外,还支持手机屏幕采集推流,通常用于游戏直播场景。您可通过 iOS 官方的原生工具 ReplayKit,录制 App 的屏幕画面。
录屏推流的实现方式为:通过 ReplayKit 的回调后,使用推流引擎的外部采集源方式,将 ReplayKit 采集到的画面视频/音频帧传入推流引擎。下文为您介绍使用 ReplayKit2 录屏后,使用推流 SDK 推流的方法。
横屏推流。
说明
横屏推流,如游戏横屏显示,请先参见基础功能接入横竖屏推流,完成横屏推流配置。
音视频数据回调。
ReplayKit 会将音频和视频都以回调的方式传给协议函数 processSampleBuffer,代码示例如下所示:
- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { switch (sampleBufferType) { case RPSampleBufferTypeVideo: // 传入视频帧 VeLiveVideoFrame *videoFrame = [[VeLiveVideoFrame alloc] init]; videoFrame.bufferType = VeLiveVideoBufferTypeSampleBuffer; videoFrame.sampleBuffer = sampleBuffer; [self.livePusher pushExternalVideoFrame:videoFrame]; break; } } - (void)quit { // 屏幕共享被停止了 }
参考 客户端混流控制 ,完成外部采集源推流配置,将音视频帧传入推流引擎。
推流 SDK 除支持手机摄像头采集推流外,还支持使用单张图片推流。代码示例如下所示:
// 更新推流图片 [self.livePusher updateCustomImage:UIImage.new]; // 开启视频采集类型为自定义图片 [self.livePusher startVideoCapture:(VeLiveVideoCaptureCustomImage)];
推流 SDK 在设置编码配置时,提供了和清晰度对应的基础配置,包括分辨率、码率、帧率等,您可以通过调用高级接口更改分辨率配置。代码示例如下所示:
[self.livePusher setProperty:@"VeLiveKeyVideoQualityEx" value:@{@"width" : @(720), @"height" : @(1280)}];
在直播业务中,有些主播会在直播间 K 歌或播放背景音乐,下面为您介绍相关能力的接口。
获取媒体音乐播放器。代码示例如下所示:
VeLiveMediaPlayer *mediaPlayer = [self.livePusher createPlayer];
设置音乐文件路径,支持的文件格式包括 MP3、AAC、M4A、WAV。代码示例如下所示:
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"music.m4a" withExtension:nil]; // 设置文件路径 [mediaPlayer prepare:fileUrl];
设置是否混音到直播流。代码示例如下所示:
// 是否混音到直播流 [mediaPlayer enableMixer:YES];
设置是否循环播放。代码示例如下所示:
// 设置是否循环播放。 [mediaPlayer enableBGMLoop:YES];
开始播放。代码示例如下所示:
// 开始播放 [mediaPlayer start];
调整背景音乐音量和主播音量。代码示例如下所示:
// 设置背景音乐音量大小 0.0 - 1.0 [mediaPlayer setBGMVolume:0.8]; // 设置主播声音音量大小 0.0 - 1.0 [mediaPlayer setVoiceVolume:1.0];
快进播放。代码示例如下所示:
// 快进到某个时间点,可以通过 getDuration 接口获取文件总时长 [mediaPlayer seek:3000];
暂停播放、继续播放。代码示例如下所示:
// 暂停 [mediaPlayer pause]; // 继续 [mediaPlayer resume];
停止播放。代码示例如下所示:
// 停止播放 [mediaPlayer stop];
直播过程中,支持主播将自己的精彩直播瞬间截图并保存在手机上,推流 SDK 提供了相应的截图能力。代码示例如下所示:
调用截图方法。
// 调用截图方法 [self.livePusher snapshot:self]
实现代理回调 VeLiveSnapshotListener
。
// 实现 `VeLiveSnapshotListener` 代理方法 - (void)onSnapshotComplete:(UIImage *)image { // 保存到相册 UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); }
在直播过程中,有些信息需要跟随视频帧到达每一个观众,推流引擎提供了发送 SEI 的接口,可以编码自定义信息到关键帧或者每个视频帧里。代码示例如下所示:
// 发送 SEI 信息 [self.livePusher sendSeiMessage:@"key" value:@"value" repeat:-1 isKeyFrame:YES allowsCovered:YES]; // 强制请求 IDR 帧 [self.livePusher requestIDRFrame];
在使用推流引擎过程中,如果需要自行处理 SDK 采集的视频帧,可以使用下面的方法实现。
设置自定义视频帧处理回调监听。代码示例如下所示:
[self.livePusher setVideoFrameFilter:self]
实现代理方法 VeLiveVideoFrameFilter
。代码示例如下所示:
- (int)onVideoProcess:(VeLiveVideoFrame *)srcFrame dstFrame:(VeLiveVideoFrame *)dstFrame { CVPixelBufferRef pixelBuffer = srcFrame.pixelBuffer; if (srcFrame.bufferType == VeLiveVideoBufferTypeSampleBuffer) { pixelBuffer = CMSampleBufferGetImageBuffer(srcFrame.sampleBuffer); } dstFrame.width = srcFrame.width; dstFrame.height = srcFrame.height; dstFrame.bufferType = VeLiveVideoBufferTypePixelBuffer; // 此处是默认写 BGRA32,请根据实际类型填写,否则会推流失败 dstFrame.pixelFormat = VeLivePixelFormatBGRA32; // 自行实现自处理方法 processPixelBuffer pixelBuffer = [self processPixelBuffer:pixelBuffer]; dstFrame.pixelBuffer = pixelBuffer; dstFrame.pts = srcFrame.pts; [dstFrame setReleaseCallback:^{ CVPixelBufferRelease(pixelBuffer); }]; if (pixelBuffer == NULL) { return -1; } return 0; }
移除自定义视频帧处理回调监听。代码示例如下所示:
[self.livePusher setVideoFrameFilter:nil];
在使用推流引擎过程中,如果需要自行处理 SDK 采集的音频数据,可以使用下面的方法实现。
设置自定义音频处理回调。代码示例如下所示:
[self.livePusher setAudioFrameFilter:self];
实现代理方法 VeLiveAudioFrameFilter
。代码示例如下所示:
- (int)onAudioProcess:(VeLiveAudioFrame *)srcFrame dstFrame:(VeLiveAudioFrame *)dstFrame { dstFrame.bufferType = srcFrame.bufferType; dstFrame.sampleRate = srcFrame.sampleRate; dstFrame.channels = srcFrame.channels; dstFrame.pts = srcFrame.pts; dstFrame.sampleBuffer = srcFrame.sampleBuffer; // 这里是清空了所有音频数据,效果和静音一致 if (srcFrame.bufferType == VeLiveAudioBufferTypeNSData && srcFrame.data != nil) { void *data = malloc(srcFrame.data.length); memset(data, 0, srcFrame.data.length); dstFrame.data = [[NSData alloc] initWithBytesNoCopy:data length:srcFrame.data.length]; return 0; } return -1; }
移除自定义音频处理回调。代码示例如下所示:
[self.livePusher setAudioFrameFilter:nil];
在推流的过程中,保存采集或编码前的视频数据可以通过以下 API 实现。
添加视频数据订阅回调监听。代码示例如下所示:
[self.livePusher addVideoFrameListener:self];
实现监听代理接口 VeLiveVideoFrameListener
。代码示例如下所示:
/** * @brief 获取当前 Listener 需要的视频帧来源。 * @return 订阅视频数据的位置信息,详情请参见 VeLiveVideoFrameSource{@link #VeLiveVideoFrameSource} */ - (VeLiveVideoFrameSource)getObservedVideoFrameSource { return VeLiveVideoFrameSourceCapture | VeLiveVideoFrameSourcePreEncode; } /** * @brief 采集视频帧回调。 * @param frame 视频帧数据 */ - (void)onCaptureVideoFrame:(VeLiveVideoFrame *)frame { } /** * @brief 编码前视频帧回调。 * @param frame 视频帧数据 */ - (void)onPreEncodeVideoFrame:(VeLiveVideoFrame *)frame { }
移除视频数据订阅回调监听。代码示例如下所示:
[self.livePusher removeVideoFrameListener:self];
在推流的过程中,保存采集或编码前的音频数据可以通过以下 API 实现。
添加音频数据订阅回调监听。代码示例如下所示:
[self.livePusher addAudioFrameListener:self];
实现监听代理接口 VeLiveAudioFrameListener
。代码示例如下所示:
/** * @brief 获取当前 Listener 需要的音频帧来源。 * @return 订阅的音频帧位置信息,详情请参见 VeLiveAudioFrameSource{@link #VeLiveAudioFrameSource}。 */ - (VeLiveAudioFrameSource)getObservedAudioFrameSource { return VeLiveAudioFrameSourceCapture | VeLiveAudioFrameSourcePreEncode; } /** * @brief 采集音频帧回调。 * @param frame 音频帧数据 */ - (void)onCaptureAudioFrame:(VeLiveAudioFrame *)frame { } /** * @brief 编码前音频帧回调。 * @param frame 音频帧数据 */ - (void)onPreEncodeAudioFrame:(VeLiveAudioFrame *)frame { }
移除音频数据订阅回调监听。代码示例如下所示:
[self.livePusher removeAudioFrameListener:self];
直播过程中,支持主播将自己的精彩直播录制并保存在手机上,推流 SDK 提供了相应的录制能力。
配置录制参数,开始录制。代码示例如下所示:
// 设置录制参数 VeLiveFileRecorderConfiguration *recordConfig = [[VeLiveFileRecorderConfiguration alloc] init]; // 录制视频的宽度 recordConfig.width = 720; // 录制视频的高度 recordConfig.height = 1280; // 录制码率 recordConfig.bitrate = 2000; // 如果是横屏模式,需要调整宽高 if (UIDeviceOrientationIsLandscape(UIDevice.currentDevice.orientation)) { recordConfig.width = 1280; recordConfig.height = 720; } // 创建录制文件保存路径 NSString *videoPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; videoPath = [videoPath stringByAppendingPathComponent:@"record_video.mp4"]; // 开始录制 [self.livePusher startFileRecording:videoPath config:recordConfig listener:self];
实现录制代理回调方法 VeLiveFileRecordingListener
。代码示例如下所示:
/** * @brief 直播录制开始回调。 */ - (void)onFileRecordingStarted { } /** * @brief 直播录制停止回调。 */ - (void)onFileRecordingStopped { } /** * @brief 直播录制错误回调。 * @param path 录制文件保存路径 * @param errorCode 错误码,详情请参见 VeLivePusherCode{@link #VeLivePusherCode} * @param msg 错误信息 */ - (void)onFileRecordingError:(int)errorCode message:(nullable NSString *)msg { }
停止录制。代码示例如下所示:
[self.livePusher stopFileRecording];
直播过程中,支持主播在 App 进入到后台时持续推流,推流 SDK 提供了相应的后台推流能力。
说明
监听 App 进入前后台通知。代码示例如下所示:
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(applicationWillResignActive) name:UIApplicationWillResignActiveNotification object:nil]; [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
根据不同的模式,切换后台视频采集类型。代码示例如下所示:
- (void)applicationWillResignActive { // 尾帧推流 [self.livePusher switchVideoCapture:(VeLiveVideoCaptureLastFrame)]; // 黑帧推流 // [self.livePusher switchVideoCapture:(VeLiveVideoCaptureDummyFrame)]; // 后台图片推流 // [self.livePusher updateCustomImage:self.config.backgroundImage]; // [self.livePusher switchVideoCapture:(VeLiveVideoCaptureCustomImage)]; } - (void)applicationDidBecomeActive { // 这里业务上可以记录上次开启的视频采集类型,在进入前台后切换为上次的采集类型 [self.livePusher switchVideoCapture:(VeLiveVideoCaptureFrontCamera)] }