本章节为您介绍推流 SDK 进阶功能的接入方式,您可以根据实际业务需求,借助推流 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 支持 RTMPS 协议推流。
使用视频直播控制台的地址生成器,生成 RTMP 推流地址。
修改推流地址协议,将 RTMP 推流地址中的 rtmp 协议修改为 rtmps。例如:
rtmp://example.push/stream
rtmps://example.push/stream
[self.livePusher startPush:@"rtmps://example.push/stream"];//添加 RTMPS 协议推流地址
(可选)配置自动降级。代码示例如下所示:
说明
配置自动降级后,RTMPS 推流失败时,推流 SDK 将自动降级到 RTMP 推流。使用此功能需同时设置 RTMPS 和 RTMP 推流地址。
[self.livePusher startPushWithUrls:@[@"rtmps://example.push/stream", // 添加 RTMPS 协议推流地址 @"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];
除主路音频流之外,推流引擎支持添加多个音频输入源,推流引擎内部会自动将所有音频流混流后传送给传输模块,同时支持在本地播放混流音频。
int audioMixID = [[self.livePusher getMixerManager] addAudioStream:VeLiveAudioMixPush];
(可选)您可以通过调用 updateStreamMixDescription: 调节指定混流音频的音量。代码示例如下所示:
VeLiveStreamMixDescription *desc = [VeLiveStreamMixDescription new]; VeLiveMixAudioLayout *audioLayout = [VeLiveMixAudioLayout new]; audioLayout.volume = volume;//音量大小 audioLayout.streamId = audioMixID;//调用addAudioStream: API获取的mixid desc.mixAudioStreams = @[audioLayout]; [[self.livePusher getMixerManager] updateStreamMixDescription:desc];
您也可以调用简化方法 setAudioStream:volume: 进行混流音频的音量调节。
[[self.livePusher getMixerManager] setAudioStream:audioMixID volume:volume];
输入音频帧。以下示例代码以 NSData 类型的 PCM 裸数据为例,音频帧数据为 float32 格式小端字节序存储。更多详情请参考 VeLiveAudioFrame。
VeLiveAudioFrame *audioFrame = [[VeLiveAudioFrame alloc] init]; audioFrame.bufferType = VeLiveAudioBufferTypeNSData; audioFrame.data = data; audioFrame.sampleRate = sampleRate; audioFrame.channels = channels; [self.livePusher.getMixerManager sendCustomAudioFrame:audioFrame streamId:audioMixID];
[[self.livePusher getMixerManager] removeAudioStream:audioMixID];
推流 SDK 除支持手机摄像头采集推流外,还支持使用单张图片推流。代码示例如下所示:
// 更新推流图片 [self.livePusher updateCustomImage:UIImage.new]; // 开启视频采集类型为自定义图片 [self.livePusher startVideoCapture:(VeLiveVideoCaptureCustomImage)];
在直播业务中,有些主播会在直播间 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 的后台音频采集权限。
在 Xcode 的项目设置中,找到 Signing & Capabilities 选项卡。
添加 Background Modes 功能。
勾选 Audio, AirPlay, and Picture in Picture 选项,该配置可保证应用在后台时,也可以进行音频采集。
监听 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)] }
推流 SDK 除支持手机摄像头采集推流外,还支持手机录屏采集推流。录屏采集推流适用于游戏直播、移动端 app 演示等场景。
实现录屏推流,需使用 iOS 12 及以上系统,推流 SDK 1.40.2 及以上版本。
iOS 端基于苹果提供的 Replaykit 框架实现屏幕录制,可以分享整个系统的屏幕内容。但由于苹果的隐私设置,不同 app 之间数据无法互通,因此需要当前 app(主 app 进程)额外提供一个 extension 扩展组件(extension 进程),并且把 app 和 extension 配置为同一 App Group,让 extension 录屏进程可以同主 app 进程进行跨进程通信,实现屏幕内容分享。
使用 Xcode 打开您的 app 工程。
点击 File > New > Target。
选择 Broadcast Upload Extension,并点击 Next。
填写必要信息,并取消勾选 Include UI Extension。
点击 Finish。
创建 App Group 并配置给 app 和录屏 extension,以实现两者间的数据共享。具体步骤可参考如何创建和配置 App Group。需要保证 app 和录屏 extension 的 App Group ID 是一致的。
在您的 Podfile 文件中添加录屏采集的相关依赖。
如您集成的是推流 SDK 动态库,则需要为 extension 添加录屏采集依赖。您需要将 YOUR_EXTENSION_TARGET
替换为 extension target 的名称。
target 'YOUR_EXTENSION_TARGET' do pod 'TTSDKFramwork/ScreenCaptureExtension', 'x.x.x' end
如您集成的是推流 SDK 静态库,则需要完成如下操作:
YOUR_EXTENSION_TARGET
替换为 extension 组件的名称。target 'YOUR_EXTENSION_TARGET' do pod 'TTSDK/ScreenCaptureExtension', 'x.x.x' end
pod 'TTSDK', 'x.x.x.', :subspecs => [...,'ScreenCapture']
在创建录屏 extension 时,Xcode 会自动生成 SampleHandler.m
文件。在该文件中,调用 VeLiveReplayKitExtension
类的相关接口来实现采集功能。具体代码参考如下。
#import "SampleHandler.h" #import <VeLiveReplayKitExtension/VeLiveReplayKitExtension.h> @interface SampleHandler()<VeLiveReplayKitExtensionDelegate> @end @implementation SampleHandler - (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo { // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. // APP_GROUP_ID 即为您在创建 App Group 时获得的 App Group ID [[VeLiveReplayKitExtension sharedInstance] startWithAppGroup:APP_GROUP_ID delegate:self]; } - (void)broadcastPaused { // User has requested to pause the broadcast. Samples will stop being delivered. [[VeLiveReplayKitExtension sharedInstance] broadcastPaused]; } - (void)broadcastResumed { // User has requested to resume the broadcast. Samples delivery will resume. [[VeLiveReplayKitExtension sharedInstance] broadcastResumed]; } - (void)broadcastFinished { // User has requested to finish the broadcast. [[VeLiveReplayKitExtension sharedInstance] broadcastFinished]; } // iOS 系统采集到录屏数据的回调 - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { [[VeLiveReplayKitExtension sharedInstance] processSampleBuffer:sampleBuffer withType:sampleBufferType]; } // 主 app 请求停止录屏的回调 - (void)broadcastFinished:(VeLiveReplayKitExtension *)broadcast reason:(VeLiveReplayKitExtensionReason)reason { NSString *tip = @""; switch (reason) { case VeLiveReplayKitExtensionReasonMainStop: tip = @"main app stops screen capture"; break; } NSError *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:0 userInfo:@{ NSLocalizedFailureReasonErrorKey:tip }]; [self finishBroadcastWithError:error]; } @end
在 app 代码中调用推流 SDK 接口,实现录屏推流功能。
前置准备
引入 VeLivePusher+ScreenCapture.h
。
#import <TTSDKFramework/VeLivePusher.h> #import <TTSDKFramework/VeLivePusher+ScreenCapture.h>
#import <TTSDK/VeLivePusher.h> #import <TTSDK/VeLivePusher+ScreenCapture.h>
初始化推流器。
VeLivePusherConfiguration *pusherConfig = [[VeLivePusherConfiguration alloc] init]; self.pusher = [[VeLivePusher alloc]initWithConfig:pusherConfig];
nil
。[self.pusher setRenderView:nil];
VeLiveScreenCaptureStatusObserver
。[self.pusher setScreenCaptureObserver:self];
VeLiveVideoEncoderConfiguration *video = [[VeLiveVideoEncoderConfiguration alloc] initWithResolution:VeLiveVideoResolutionScreen]; [self.pusher setVideoEncoderConfiguration:video];
[self.pusher setOrientation:UIInterfaceOrientationLandscapeRight];
VeLiveScreenCaptureStatusObserver
中,处理推流器的开启和停止。在采集开始时,同时开始麦克风采集和开启推流。在采集停止时,同时停止麦克风采集和推流。#pragma -- mark VeLiveScreenCaptureStatusObserver delegate - (void)broadcastStarted { // 推流器开始音频采集 [self.pusher startAudioCapture:VeLiveAudioCaptureMicrophone]; // 推流器开始推流 [self.pusher startPush:@"PUSH_STREAM_ADDRESS"]; } - (void)broadcastPaused { } - (void)broadcastResumed { } - (void)broadcastFinished { // 推流器停止音频采集 [self.pusher stopAudioCapture]; // 推流器停止推流 [self.pusher stopPush]; }
开始录屏采集
调用 iOS 系统的 RPSystemBroadcastPickerView
来调起屏幕录制功能。您需要将 EXTENSION_BUNDLE_ID
替换为 extension 的 Bundle ID。
RPSystemBroadcastPickerView* picker = [[RPSystemBroadcastPickerView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; picker.showsMicrophoneButton = false; picker.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin; picker.preferredExtension = @"EXTENSION_BUNDLE_ID";
当用户点击 RPSystemBroadcastPickerView 区域时,系统将开始屏幕录制。您也可以通过如下代码发送点击事件:
for (UIView* view in picker.subviews) { UIButton* button = (UIButton*)view; [button sendActionsForControlEvents:UIControlEventAllTouchEvents]; }
采集中
开始录屏后,您可以修改录屏采集到的 app 音量。调用 getAppAudioStream 获取录屏采集音频流的 ID 后,调用 setAudioStream:volume: 修改音量。
int appAudioID = [self.pusher getMixerManager] getAppAudioStream]; [[self.pusher getMixerManager] setAudioStream:appAudioID volume:appAudioVolumn];
停止采集
当用户点击 iOS 系统自带的停止录屏采集图标后,系统将停止录屏采集。此外,您也可以调用 stopScreenCapture 停止录屏采集。
[self.pusher stopScreenCapture];
如果在编译录屏 extension 时,出现不支持低版本手机的情况,可以修改 extension 支持的最低版本。
屏幕推流过程中,iOS 15 及以上的系统会自动屏蔽系统通知消息,即通知消息不会出现在录屏画面中。iOS 16 及以上的系统,如果需要取消屏蔽系统通知消息,可在设备的系统设置中手动调整通知设置。