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相关推荐

  1. iOS录屏直播(二)Broadcast Upload Extension和Broadcast Setup UI Extension

    Morris_2019.06.13 上一篇总结了ReplayKit相关的知识点,实现了应用内的录屏功能,同时涉及到了很少一部分Broadcast Upload Extension和Broadcast ...

  2. iOS录屏直播(四)主App和宿主App数据共享,通信功能实现

    文章目录 CFNotificationCenterGetDarwinNotifyCenter 发送通知 接收通知 注意事项 遗留问题 补充 Morris_ 2019.06.17 上一篇总结了一下App ...

  3. iOS录屏直播(三)AppGroup

    Morris_2019.06.14 AppGroup是什么 App Groups Entitlement AppGroup是一个App组,里面可以有若干个App,AppGroup组是个虚无的存在,若干 ...

  4. iOS rtmp 摄像头/录屏直播以及观看

    之前讲过如何在centos上使用nginx搭建rtmp服务器(链接),本文介绍一下iOS 端如何通过rtmp录屏直播以及观看,完整的工程代码地址(https://github.com/zxm006/R ...

  5. 手游录屏直播技术详解 | 直播 SDK 性能优化实践

    直播无疑是 2016 年的大热话题,七牛云在 6 月底发布了实时流网络 LiveNet 和直播云解决方案后,我们用<直播技术详解>系列文章系统地介绍了直播各个环节的关键技术,帮助视频直播创 ...

  6. 如何做电脑游戏桌面录屏直播实现手机直接观看

    原创教程 ( 转载请注明出处 ) 2017-6-26,今天来做一下是电脑游戏桌面录屏直播的教程,就是把桌面的游戏直播出去,加上话筒做讲解.最终实现在电脑.手机.微信中都可以观看到游戏的直播和讲解画面. ...

  7. Android PC投屏简单尝试(录屏直播)2—硬解章(MediaCodec+RMTP)

    代码地址 :https://github.com/deepsadness/MediaProjectionDemo 想法来源 上一边文章的最后说使用录制的Api进行录屏直播.本来这边文章是预计在5月份完 ...

  8. android实现录像功能吗,Android实现录屏直播(一)ScreenRecorder的简单分析

    应项目需求瞄准了Bilibili的录屏直播功能,基本就仿着做一个吧.研究后发现Bilibili是使用的MediaProjection 与 VirtualDisplay结合实现的,需要 Android ...

  9. iOS自动化-iOS录屏xrecord及解决iPhone设备不显示的问题

    iOS自动化-iOS录屏xrecord及解决iPhone设备不显示的问题 参考文章: (1)iOS自动化-iOS录屏xrecord及解决iPhone设备不显示的问题 (2)https://www.cn ...

最新文章

  1. UICollectionView
  2. 报表应用系列——图表JFreeChart: 第 4 章 折线图
  3. 给你一个团队怎么带?抓住3要点,别做13件事,没人敢不服你
  4. python3 ssl.CertificateError: hostname manifest.googlevideo.com doesn t match either
  5. linux /proc/net/arp
  6. 苹果笔记本怎么找文件夹_如何在苹果笔记本中找出 “~/Library/Preferences/” 文件夹?...
  7. mysql中的函数有哪些?(1.数字函数)
  8. 通过Chocolatey软件包管理器安装.NET Core
  9. C语言中sizeof详解——面试C/C++
  10. 搞事 | 5分钟部署一个机器人帮你告别 “信息焦虑”
  11. 04-树6 Complete Binary Search Tree
  12. jQuery点击图片弹出大图遮罩层
  13. SpringMVC工作原理的介绍
  14. (STM32笔记2)基于hc05的蓝牙实验
  15. 各种滤镜算法C语言,Photoshop入门学习之PS 滤镜算法原理——染色玻璃
  16. 【小知识】光的偏振态及镜头前加偏振片的去噪原理
  17. 使用opencv制作人脸识别小软件
  18. IIS应用程序池自动回收
  19. c语言无符号数最大值和最小值,微机原理中设计一个程序,求10个无符号数的最大值...
  20. 写代码应该用什么字体?

热门文章

  1. python中的numpy标准正态分布_Numpy创建正态分布和均匀分布
  2. python 绘制椭圆
  3. Mac下使用Eclipse读java源码
  4. pdf文件删除空白页技巧介绍
  5. mysql jail_FreeNAS:如何在Jail里面安装软件?
  6. OpenGL显示枫叶蝴蝶,无聊作品
  7. 用pip安装django
  8. 瑞典皇家理工学院计算机学什么,瑞典皇家理工大学学科设置是怎样的?
  9. 《向着光亮那方》刘同 读书笔记
  10. 浏览器原理-持续汇总