iOS录屏直播(一)初识ReplayKit
Morris_2019.05.08
本篇主要功能:
- 认识ReplayKit框架
- RPScreenRecorder实现在应用内录屏功能
- RPPreviewViewController查看录屏内容
- RPBroadcastActivityViewController获取可录屏直播的App列表
- 初识Broadcast Setup UI Extension
ReplayKit是苹果在iOS9.0,tvos10.0提功能一个支持录屏功能的库,在目前感觉仍然不是很成熟,但是对于录屏和录屏直播的支持在功能上是完整的。
在iOS9只是提供了RPScreenRecorder、RPPreviewViewController、RPError这几个类,RPScreenRecorder可以实现应用内录屏功能,包括语音的录制。RPPreviewViewController则可以实现对录制内容的展示、保存等操作。
在iOS10又新增了RPBroadcast和RPBroadcastExtension,来达到应用外录屏的功能支持。在之后的每个版本ReplayKit这个库都有更新,废弃了些Api,替换了新的Api。
以下是该库下的所有类:
#import <ReplayKit/RPScreenRecorder.h>
#import <ReplayKit/RPPreviewViewController.h>
#import <ReplayKit/RPBroadcast.h>
#import <ReplayKit/RPBroadcastExtension.h>
#import <ReplayKit/RPError.h>
一、RPError
此框架下定义的录屏错误类,定义了一个RPRecordingErrorCode的枚举,列举了录屏出现的错误码。
二、RPScreenRecorder
这个类是一个单例对象,是苹果提供的可以在应用内对App进行屏幕数据和银屏数据进行录制的类。
看完这个类,大体上应该知道在应用内机型屏幕录制、采集语音、停止录制这些功能。
1、Api
iOS9
iOS9提供了如下Api开始录制,录制时设置是否同时录制音频。
- (void)startRecordingWithMicrophoneEnabled:(BOOL)microphoneEnabled handler:(nullable void(^)(NSError * _Nullable error))handler API_DEPRECATED("Use microphoneEnabaled property", ios(9.0, 10.0));
iOS10
iOS10将microphoneEnabled设置成了一个属性,也就是说在iOS9的时候,一旦录制开始是不支持设置语音的,在iOS10的时候可以录制的同时,通过set方法设置microphoneEnabled。
- (void)startRecordingWithHandler:(nullable void(^)(NSError * _Nullable error))handler API_AVAILABLE(ios(10.0), tvos(10.0));
iOS11
iOS11又提供了另一个开始录制的方法:
- (void)startCaptureWithHandler:(nullable void(^)(CMSampleBufferRef sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error))captureHandler completionHandler:(nullable void(^)(NSError * _Nullable error))completionHandler API_AVAILABLE(ios(11.0), tvos(11.0));
这个方法在录制过程中,会通过回调将录制的数据返回来。这里的sampleBuffer就是音频/视频数据,sampleBuffer是苹果对音视频数据的包装。
- 在iOS11以前,可以在应用内录屏,但不能实时获取录制的数据,在iOS11才可以。
- iOS10还提供了cameraEnabled、cameraPosition、cameraPreviewView这些属性。
- iOS9和iOS10的停止录制的方法一样,在iOS11的时候,开始和录制的方法都是新提供的。
2、RPScreenRecorderDelegate
这里先提前申请一下,这两个代理都是在录制出问题的情况下才会回调。
iOS9和iOS10
- (void)screenRecorder:(RPScreenRecorder *)screenRecorder didStopRecordingWithError:(NSError *)error previewViewController:(nullable RPPreviewViewController *)previewViewController API_DEPRECATED("No longer supported", ios(9.0, 10.0), tvos(10.0,10.0));
iOS11是另外一个方法:
- (void)screenRecorder:(RPScreenRecorder *)screenRecorder didStopRecordingWithPreviewViewController:(nullable RPPreviewViewController *)previewViewController error:(nullable NSError *)error API_AVAILABLE(ios(11.0), tvos(11.0));
表面上看好像没什么分别。
还有一个代理方式是当录制不可用时会回调:
- (void)screenRecorderDidChangeAvailability:(RPScreenRecorder *)screenRecorder;
注意一下第一个代理什么情况下会回调,起初我以为停止录制即会回调,后来我试了我错了,后来我以为调用stopRecording才会回调,再次打脸。这个方法从代码名上来看还真就以为didStop后才会回调。再次看下文档描述:Called when recording has stopped due to an error.
,恍然大悟有么有。
为什么在iOS11改成了didStopRecordingWithPreviewViewController呢?didStopRecordingWithError不是很合理吗?这个方法本来就是录屏出错停止的时候回调的。不出错是不会回调的。
那这么看来这个RPScreenRecorderDelegate基本上没有什么卵用,毕竟我们录屏的时候都不希望它出错。
3、总结
应用内录制功能答题步骤:
1、获取屏幕录制单例对象,检测设备是否支持屏幕录制;
2、如果支持,则开始录制,设置是否同时录制音频;
3、处理开启录制结果
4、结束录制
要做的最多的可能就是兼容的问题,在iOS9和iOS10或者iOS11的兼容性问题。
4、代码
到这里,应该大概知道App内录制功能了,使用RPScreenRecorder这个类进行start,开始录制,stop结束录制,在应用内录屏,这个就可以实现了。
这里用一个按钮实现iOS11录制和结束录制,功能代码如下:
- (void)statrButtonClick:(UIButton *)sender {//停止录屏if ([[RPScreenRecorder sharedRecorder] isRecording]) {if (@available(iOS 11.0, *)) {[[RPScreenRecorder sharedRecorder] stopCaptureWithHandler:^(NSError * _Nullable error) {if (error) {NSLog(@"stopCaptureWithHandler:%@", error.localizedDescription);} else {NSLog(@"CaptureWithHandlerStoped");}}];} else {// Fallback on earlier versions}}//开始录屏else {if (@available(iOS 11.0, *)){//打开录制音频[[RPScreenRecorder sharedRecorder] setMicrophoneEnabled:YES];//开始录屏[[RPScreenRecorder sharedRecorder] startCaptureWithHandler:^(CMSampleBufferRef _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {if (error) {NSLog(@"startCaptureWithHandler:error:%@", error.localizedDescription);}else {//这个block会实时回调录制数据,录制数据是以sampleBuffer的形式被回调。bufferType是数据类型。}} completionHandler:^(NSError * _Nullable error) {if (error) {NSLog(@"completionHandler:error:%@", error.localizedDescription);}}];} else {// Fallback on earlier versions}}
}
使用startCapture和stopCapture或者startRecording和stopRecording都可以的,配对使用。
在开始录屏后,可以开始推流,回调拿到录屏数据后,用推流的SDK直接将数据进行推流。这样就实现了应用内录屏直播的功能了。
这个类是不能实现应用外录屏的,即不能录制整个手机屏幕,录制整个App的话还需要应用扩展程序,Broadcast Upload Extension和Broadcast Setup UI Extension。
三、RPPreviewViewController
在用RPScreenRecorder录屏结束后,在RPScreenRecorderDelegate的回调方法中,会回调一个RPPreviewViewController的对象。
这个类是继承自UIViewController的,是用来展示/分享RPScreenRecorder录制结束后的视频的。看看文档的描述:View controller that allows the user to preview/edit a movie recorded with ReplayKit. Passed into the completion handler supplied to [RPScreenRecorder stopRecordingWithHandler:].
这个描述直接告诉我们:1、这个类是用来让用户preview/edit录屏文件的。2、在stopRecording这个方法中使用该类,实现对录制结束后的视频进行preview或edit,那在stopCapture这个方法里行不行,当然也是不可以的,因为stopCapture这个方法不会回调previewViewController这个对象。
这么看来,startRecording/stopRecording和startCapture/stopCapture之间的关系和区别了。
相同点:都可以用来实现应用内录屏功能
不同点:startRecording/stopRecording这个对停止录屏后提供了数据可编辑的previewViewController,startCapture/stopCapture没有该功能;startCapture/stopCapture提供了录制时实时获取录屏数据的回调,startRecording/stopRecording只能是在结束后通过previewViewController来访问录屏数据。
[[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController * _Nullable previewViewController, NSError * _Nullable error) {NSLog(@"stop error : %@",error);if (error) {} else {previewViewController.previewControllerDelegate = self;[self presentViewController:previewViewController animated:YES completion:^{}];}}];
在stopRecording后设置previewViewController.previewControllerDelegate = self;,然后将previewViewController显示出来,点击cancel按钮回调第一个代理方法,点击save回掉第二个代理方法。
#pragma mark - RPPreviewViewControllerDelegate
- (void)previewControllerDidFinish:(RPPreviewViewController *)previewController {//在这里需要将previewController dismiss掉[previewController dismissViewControllerAnimated:YES completion:^{}];
}
- (void)previewController:(RPPreviewViewController *)previewController didFinishWithActivityTypes:(NSSet <NSString *> *)activityTypes {//在这里需要将previewController dismiss掉[previewController dismissViewControllerAnimated:YES completion:^{}];if ([activityTypes containsObject:@"com.apple.UIKit.activity.SaveToCameraRoll"]) {NSLog(@"保存到相册成功");}
}
这个activityTypes是做什么用的?这里牵扯到了UIActivity这个类。
四、RPBroadcast
RPBroadcastExtension和RPBroadcast初看好像完全不知道做什么用的。RPBroadcastExtension看来是对RPBroadcast的一个扩展类。看看里面都有哪些东西吧先。
RPBroadcast是iOS10推出的类。里面定义了一个RPBroadcastActivityViewController、RPBroadcastActivityViewControllerDelegate、RPBroadcastController、RPBroadcastControllerDelegate。看起来RPBroadcastActivityViewController是继承自RPBroadcastController,其实不然。
1、RPBroadcastActivityViewController
继承自UIViewController。里面有两个方法:
+ (void)loadBroadcastActivityViewControllerWithHandler:(void(^)(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error))handler;
+ (void)loadBroadcastActivityViewControllerWithPreferredExtension:(NSString * _Nullable)preferredExtension handler:(nonnull void(^)(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error))handler API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos);
这个怎么玩?这样?
- (void)loadBroadcastActivityViewControllerAction:(UIButton *)sender {RPBroadcastActivityViewController *vc = [[RPBroadcastActivityViewController alloc] init];vc.view.backgroundColor = [UIColor whiteColor];vc.delegate = self;[self presentViewController:vc animated:YES completion:^{}];
}
这样什么都实现不了啊?。看了下文档:Loads a RPBroadcastActivityViewController instance and returns it in the handler block.
Loads RPBroadcastActivityViewController这个实例,并返回该对象,果然不是上面那么玩的。
- (void)loadBroadcastActivityViewControllerAction:(UIButton *)sender {[RPBroadcastActivityViewController loadBroadcastActivityViewControllerWithHandler:^(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error) {if (!error) {broadcastActivityViewController.delegate = self;broadcastActivityViewController.view.backgroundColor = [UIColor whiteColor];[self presentViewController:broadcastActivityViewController animated:YES completion:^{}];}}];
}
调用这个方法,会弹出一个窗,xxx正在直播,”此操作需要直播应用。请在App Store中查找相关应用。点击“查找直播应用”,跳到了App Store里。
然鹅,接下来怎么玩?下载个直播应用吧。我没下载直接返回了,结果这个broadcastActivityViewController还在,无法操作界面了…
这个我后来才知道原来是这么玩的,再看了下文档
view controller will present the user with a list of broadcast services available on the device and allow the user to set up a broadcast with the service through the service's UI.
这个broadcastActivityViewController将会把在手机上实现了broadcast UI的App搜罗出来,显示在broadcastActivityViewController上,提供用户直播用。也就是手机上哪个App实现了Broadcast Setup UI Extension,这个App就会出现在broadcastActivityViewController的列表里。
如果手机里没有应用实现Broadcast Setup UI Extension,则会弹出来前面我们看到的弹窗xxx正在直播,”此操作需要直播应用。请在App Store中查找相关应用。点击“查找直播应用”。
Broadcast Setup UI Extension是做什么用的,这里先不说了。
注意:这里有个问题需要说明一下,如果跳转App Store,再返回来,或者是打开了Broadcast Setup UI Extension App的列表,点击取消,返回到当前界面时,broadcastActivityViewController没法消失了,这个怎么处理。
看了一下下面的文档:
The RPBroadcastActivityViewController can be presented using [UIViewController presentViewController:animated:completion:] and should be dismissed when the delegate's broadcastActivityViewController:didFinishWithBroadcastController:error: method is called. Note that on large screen devices such as iPad, the default presentation style for view controllers is a popover. For an instance of RPBroadcastActivityViewController to present properly on iPad, it needs to have its popoverPresentationController's sourceRect and sourceView configured.
里面说了broadcastActivityViewController的用法。被present出来展示App类表用的,需要在broadcastActivityViewController:didFinishWithBroadcastController:error:代理方法中dismiss掉。
我试了试,这个代理方法不会被执行这个为什么?
这里需要这样处理,才可以:
- (void)loadBroadcastActivityViewControllerAction:(UIButton *)sender {[RPBroadcastActivityViewController loadBroadcastActivityViewControllerWithHandler:^(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error) {if (!error) {broadcastActivityViewController.delegate = self;broadcastActivityViewController.view.backgroundColor = [UIColor whiteColor];self.broadcastActivityViewController = broadcastActivityViewController;[self presentViewController:broadcastActivityViewController animated:YES completion:^{}];}}];
}
将broadcastActivityViewController保存成全局变量,
@property (nonatomic, weak) RPBroadcastActivityViewController *broadcastActivityViewController;
这样broadcastActivityViewController:didFinishWithBroadcastController:error:这个代理方法才会被执行,在代理方法中做dismiss操作。
2、RPBroadcastActivityViewControllerDelegate
#pragma mark - RPBroadcastActivityViewControllerDelegate
- (void)broadcastActivityViewController:(RPBroadcastActivityViewController *)broadcastActivityViewController didFinishWithBroadcastController:(nullable RPBroadcastController *)broadcastController error:(nullable NSError *)error API_AVAILABLE(ios(10.0), tvos(10.0)) {NSLog(@"%s %@",__FUNCTION__,error);
}
这个代理方法中又牵扯出来了另一个类broadcastController。
broadcastController An RPBroadcastController instance that can be used to start and stop broadcasts to a user selected service.
看了看present出来的broadcastActivityViewController,它的上面还有一个controller,显示集成Broadcast Setup UI Extension了的App列表,这个controller就是回调方法中的broadcastController,它随着broadcastActivityViewController而来。
#pragma mark - RPBroadcastActivityViewControllerDelegate
- (void)broadcastActivityViewController:(RPBroadcastActivityViewController *)broadcastActivityViewController didFinishWithBroadcastController:(nullable RPBroadcastController *)broadcastController error:(nullable NSError *)error API_AVAILABLE(ios(10.0), tvos(10.0)) {__weak typeof(self)weakSelf = self;[broadcastActivityViewController dismissViewControllerAnimated:YES completion:^{weakSelf.broadcastActivityViewController = nil;}];NSLog(@"%s %@",__FUNCTION__,error);
}
这样处理了一下,又发现,dismiss的时候,broadcastController小时的很慢,感觉主线程被卡主了一样。正是坑,原来这个方法的回调回调到了子线程中。
我们需要将dismiss放在主线程处理就好了:
#pragma mark - RPBroadcastActivityViewControllerDelegate
- (void)broadcastActivityViewController:(RPBroadcastActivityViewController *)broadcastActivityViewController didFinishWithBroadcastController:(nullable RPBroadcastController *)broadcastController error:(nullable NSError *)error API_AVAILABLE(ios(10.0), tvos(10.0)) {NSLog(@"%s %@",__FUNCTION__,error);dispatch_async(dispatch_get_main_queue(), ^{__weak typeof(self)weakSelf = self;[broadcastActivityViewController dismissViewControllerAnimated:YES completion:^{weakSelf.broadcastActivityViewController = nil;}];});
}
3、RPBroadcastController
继承自NSObject。
里面提供了一些方法,startBroadcastWithHandler、pauseBroadcast、resumeBroadcast、finishBroadcastWithHandler
是不是很像录屏开始、暂停、恢复、结束。做什么用的,这里先不用管了。
…略
4、RPBroadcastControllerDelegate
…略
五、RPBroadcastExtension
这个Extension中扩展了几个方法,这个注意一下:- (void)completeRequestWithBroadcastURL:(NSURL *)broadcastURL setupInfo:(nullable NSDictionary <NSString *, NSObject <NSCoding> *> *)setupInfo
定义了一个类RPBroadcastHandler。
定义了一下继承自RPBroadcastHandler的RPBroadcastMP4ClipHandler类。
定义了RPBroadcastSampleHandler继承自RPBroadcastHandler。
这里不知道这个是做什么的,先看看行了。
iOS录屏直播(一)初识ReplayKit相关推荐
- iOS录屏直播(二)Broadcast Upload Extension和Broadcast Setup UI Extension
Morris_2019.06.13 上一篇总结了ReplayKit相关的知识点,实现了应用内的录屏功能,同时涉及到了很少一部分Broadcast Upload Extension和Broadcast ...
- iOS录屏直播(四)主App和宿主App数据共享,通信功能实现
文章目录 CFNotificationCenterGetDarwinNotifyCenter 发送通知 接收通知 注意事项 遗留问题 补充 Morris_ 2019.06.17 上一篇总结了一下App ...
- iOS录屏直播(三)AppGroup
Morris_2019.06.14 AppGroup是什么 App Groups Entitlement AppGroup是一个App组,里面可以有若干个App,AppGroup组是个虚无的存在,若干 ...
- iOS rtmp 摄像头/录屏直播以及观看
之前讲过如何在centos上使用nginx搭建rtmp服务器(链接),本文介绍一下iOS 端如何通过rtmp录屏直播以及观看,完整的工程代码地址(https://github.com/zxm006/R ...
- 手游录屏直播技术详解 | 直播 SDK 性能优化实践
直播无疑是 2016 年的大热话题,七牛云在 6 月底发布了实时流网络 LiveNet 和直播云解决方案后,我们用<直播技术详解>系列文章系统地介绍了直播各个环节的关键技术,帮助视频直播创 ...
- 如何做电脑游戏桌面录屏直播实现手机直接观看
原创教程 ( 转载请注明出处 ) 2017-6-26,今天来做一下是电脑游戏桌面录屏直播的教程,就是把桌面的游戏直播出去,加上话筒做讲解.最终实现在电脑.手机.微信中都可以观看到游戏的直播和讲解画面. ...
- Android PC投屏简单尝试(录屏直播)2—硬解章(MediaCodec+RMTP)
代码地址 :https://github.com/deepsadness/MediaProjectionDemo 想法来源 上一边文章的最后说使用录制的Api进行录屏直播.本来这边文章是预计在5月份完 ...
- android实现录像功能吗,Android实现录屏直播(一)ScreenRecorder的简单分析
应项目需求瞄准了Bilibili的录屏直播功能,基本就仿着做一个吧.研究后发现Bilibili是使用的MediaProjection 与 VirtualDisplay结合实现的,需要 Android ...
- iOS自动化-iOS录屏xrecord及解决iPhone设备不显示的问题
iOS自动化-iOS录屏xrecord及解决iPhone设备不显示的问题 参考文章: (1)iOS自动化-iOS录屏xrecord及解决iPhone设备不显示的问题 (2)https://www.cn ...
最新文章
- UICollectionView
- 报表应用系列——图表JFreeChart: 第 4 章 折线图
- 给你一个团队怎么带?抓住3要点,别做13件事,没人敢不服你
- python3 ssl.CertificateError: hostname manifest.googlevideo.com doesn t match either
- linux /proc/net/arp
- 苹果笔记本怎么找文件夹_如何在苹果笔记本中找出 “~/Library/Preferences/” 文件夹?...
- mysql中的函数有哪些?(1.数字函数)
- 通过Chocolatey软件包管理器安装.NET Core
- C语言中sizeof详解——面试C/C++
- 搞事 | 5分钟部署一个机器人帮你告别 “信息焦虑”
- 04-树6 Complete Binary Search Tree
- jQuery点击图片弹出大图遮罩层
- SpringMVC工作原理的介绍
- (STM32笔记2)基于hc05的蓝牙实验
- 各种滤镜算法C语言,Photoshop入门学习之PS 滤镜算法原理——染色玻璃
- 【小知识】光的偏振态及镜头前加偏振片的去噪原理
- 使用opencv制作人脸识别小软件
- IIS应用程序池自动回收
- c语言无符号数最大值和最小值,微机原理中设计一个程序,求10个无符号数的最大值...
- 写代码应该用什么字体?