上一篇阐述了调研结果,而我们常用的应用场景就是录制屏幕内容,然后将内容分享给他人(直播或录播)。流程如下:

1.被录制端host app需引入 ReplayKit,以便可以使用其api选择一个app的extension来启动录制;

2.广播端宿主app需要集成 Broadcast UI 和 Broadcast Upload 两个 Extension,以便出现在被录制端可选的 App 列表中;

3.host app选定宿主app 后,将启动宿主app的extension,开始录制和广播相关逻辑。

上文已经提到,从iOS9系统开始,苹果推出了replaykit 这个sdk来支持屏幕录制,通过extension形式实现屏幕录制。本文将对屏幕录制使用replaykit的技术细节进行描述, 下一篇将对录制内容的推送(广播)进行描述。通过本文你将对以下几方面得到信息:

1. extension是什么?

2. extension跟app什么关系?

3. 在iOS10 11上集成extension注意哪些,区别有哪些?

4. 调试时注意哪些?

5. 调试时涉及到的原理和通信方式

extension是什么?

逻辑形式:

extension必须寄生在宿主app中,会随着宿主 app的安装而安装,同时随着宿主 app的卸载而卸载,但是extension却可以独立生存,即使宿主app没有启动,extension也可以为其他app提供相关服务。(能够调起extension的app被称为host app)

物理形式:

iOS系统提供屏幕录制和直播功能都需要通过Extensions的形式来支持,通过在Xcode的已有工程中新建target,选择broadcast upload extension,这样工程中将自动添加broadcast upload extension和broadcast setup UI extension两个extensions。extension并不是一个独立的app,它有一个包含在app bundle中的独立bundle,extension的bundle后缀名是.appex。

集成extension

集成方式很简单,新建target,选择upload相关两个extension。集成之后将在工程的列表中看到两个新增的目录。

需要注意的是,ios10 系统在upload的extension中的info.plist中NSExtensionPointIdentifier对应的value必须使用NSExtensionPointIdentifierkey对应ios10才兼容的com.apple.broadcast-services,不应该使用com.apple.broadcast-services-upload ,在iOS10系统中使用com.apple.broadcast-services-upload将无法通过编译,Xcode会报错。

通信

iOS10系统和iOS11系统的屏幕录制和直播,涉及到extensions和host app、containing app之间的通信,其中host app一端需要集成ReplayKit2,从而可以发起录制和直播请求,而containing app需要集成extensions,实现对其他可以录制的app的直播功能的支持。extension和host app之间可以通过extensionContext属性直接通信,extension和宿主containing app之间是通过IPC或基于group的文件共享来实现的。

对于iOS10和iOS11,屏幕录制区别较大,前者只能录制app内的内容,后者可以录制整个系统的内容,而且前者可以通过代码控制录制的启动,而后者只能通过用户的操作(控制中心,点击圆点,选择app)启动录制。

iOS 10

在iOS10系统中,想要录制当前app内的内容,必须通过其他app的extension,而启动这个extension必须通过集成replaykit的api。

@interface 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);

@end

@protocol RPBroadcastActivityViewControllerDelegate

- (void)broadcastActivityViewController:(RPBroadcastActivityViewController *)broadcastActivityViewController didFinishWithBroadcastController:(nullable RPBroadcastController *)broadcastController error:(nullable NSError *)error API_AVAILABLE(ios(10.0), tvos(10.0));

@end

按照前文流程,当host app一端想要将app或系统内容广播给他人观看时,需要首先选择一个app的extension来帮他广播,就是需要展示出支持广播的app列表。这点通过调用ReplayKit2的RPBroadcastActivityViewController类的load相关api来实现。可以看到上面有两个api可供使用。

(void)loadBroadcastActivityViewControllerWithHandler:(void(^)(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error))handler;

这时iOS系统将会寻找系统内已经集成了屏幕录制和直播extensions的containing app,并将这些app列表展示出来,用户可以在列表中选择containing app,点击选择之后,将通过containing app的extension中的UI-extension来展示相关的界面(可以自定义),让用户输入信息,一般用来鉴权或者保存用户信息,用户点击ok按钮之后,可以通过相关方法来调用[self.extensionContext completeRequestWithBroadcastURL:broadcastURL broadcastConfiguration:broadcastConfig setupInfo:setupInfo];,这个方法中将传递一些信息给host app,RPBroadcastActivityViewControllerDelegate的代理方法didFinishWithBroadcastController将会回调调用,这时我们可以获取到用于广播的controller,相当于与containing app已经建立起了通信链路,然后调用broadcastController 的startBroadcastWithHandler接口即可启动录制。

![

image.png

(void)loadBroadcastActivityViewControllerWithPreferredExtension:(NSString * _Nullable)preferredExtension handler:(nonnull void(^)(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error))handler

第二个api是ios11新增的。可以通过参数preferredExtension,直接打开指定使用的app,只需要preferredExtension传递相应app extension的bundle id。RPBroadcastActivityViewControllerDelegate的代理方法didFinishWithBroadcastController将会回调调用,这时我们可以获取到用于广播的controller,相当于与containing app已经建立起了通信链路,然后调用broadcastController 的startBroadcastWithHandler接口即可启动录制。

image.png

iOS11

在iOS10系统中,只能用户自己手动启动录制,并且无法通过代码控制录制进程的启动,所以被录制端host app其实无需集成replaykit,而只需要宿主app集成两个extension。

与iOS10不同的是,用户手动选择录制app后,宿主app的extension相关方法将自动开始回调。

录制进程

通过上面的形式,启动录制后,我们可以在extension中自建出来的SampleHandler文件中相关代理方法中获取到屏幕采集的进度,具体使用方式见注释:

@interface RPBroadcastSampleHandler : RPBroadcastHandler

/*! @abstract Method is called when the RPBroadcastController startBroadcast method is called from the broadcasting application.

@param setupInfo Dictionary that can be supplied by the UI extension to the sample handler.

屏幕采集工作已经开始启动,在此方法中一般进行初始化工作

*/

- (void)broadcastStartedWithSetupInfo:(nullable NSDictionary *)setupInfo;

/*! @abstract Method is called when the RPBroadcastController pauseBroadcast method is called from the broadcasting application. */

- (void)broadcastPaused;

/*! @abstract Method is called when the RPBroadcastController resumeBroadcast method is called from the broadcasting application. */

- (void)broadcastResumed;

/*! @abstract Method is called when the RPBroadcastController finishBroadcast method is called from the broadcasting application. */

- (void)broadcastFinished;

/*! @abstract Method is called when broadcast is started from Control Center and provides extension information about the first application opened or used during the broadcast.

@param applicationInfo Dictionary that contains information about the first application opened or used buring the broadcast.

*/

- (void)broadcastAnnotatedWithApplicationInfo:(NSDictionary *)applicationInfo API_AVAILABLE(ios(11.2)) API_UNAVAILABLE(tvos);

/*! @abstract Method is called as video and audio data become available during a broadcast session and is delivered as CMSampleBuffer objects.

@param sampleBuffer CMSampleBuffer object which contains either video or audio data.

@param sampleBufferType Determine's the type of the sample buffer defined by the RPSampleBufferType enum.

采集到数据的实时回调,此方法中的sampleBuffer数据结构中有视频和音频数据,我们通过相关推流方法将数据推送给服务器,即实现了录制和推流。

*/

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType;

/*! @abstract Method that should be called when broadcasting can not proceed due to an error. Calling this method will stop the broadcast and deliver the error back to the broadcasting app through RPBroadcastController's delegate.

@param error NSError object that will be passed back to the broadcasting app through RPBroadcastControllerDelegate's broadcastController:didFinishWithError: method.

*/

- (void)finishBroadcastWithError:(NSError *)error;

@end

文件读写

尽管extension的bundle是放在containing app的bundle中,但是他们是两个完全独立的进程,之间不能直接通信。不过extension可以通过openURL的方式启动containing app(当然也能启动其它app),不过extension中是无法直接使用openURL的,必须通过extensionContext借助host app来实现。extension和containing app可以共同读写一个被称为Shared resources的存储区域,这是通过App Groups实现的,用于同一group下的app共享同一份读写空间,以实现数据共享。

• 首先需要在apple开发网站上对profile文件进行配置,将group数据共享配置,并设置group id(dns域名反写),用户app和extension之间;

• 然后app中配置这个profile,并设置app的group,通过TARGETS-->App-->Capabilities-->App Groups,选择正确的group id;

• 同时,在extension中也要通过TARGETS-->App-->Capabilities-->App Groups,选择同样的group id;

• 通过NSUserDefaults共享数据,通过下面的形式:

- (void)saveTextByNSUserDefaults

{

NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.cmcc.ShareScreen"];

[shared setObject:_textField.text forKey:@"cmcc"];

[shared synchronize];

}

• 读写文件时,也需要通过指定group id的形式,才能将文件写入共享的数据区,或者从共享数据区读出来

- (NSString *)readTextByNSFileManager

{

NSError *err = nil;

NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.cmcc.ShareScreen "];

containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/good"];

NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&err];

return value;

}

- (bool)writeTextByNSFileManager

{

NSError *err = nil;

NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.cmcc.ShareScreen "];

containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/good"];

NSString *value = @"just test";

BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err];

return result;

}

注意:containing app需要配置带有group配置的profile, extension可以配置自动,但是bundle id不能和containing app相同

调试

由于涉及到extensions作为独立target,所以调试时,需要单独编译运行,即我们想要调试containing app那就需要将xcode切换到containing app,然后重新运行,如果需要调试upload 或 setupUI的extension,那就需要需要切换到extension的target,在重新运行,这样才能在sampleHandler相关的方法中断点调试;

image.png

userDidFinishSetup(通过extensionContext与host app通信的方法)必须在viewDidAppear后,而不能放在viewDidLoad之后,否则导致无法将事件传递给SampleHandler,它的代理方法不会回调。

- (void)viewDidAppear:(BOOL)animated

{

[super viewDidAppear:animated];

[self userDidFinishSetup];

}

app内录屏开发 ios_iOS端屏幕录制Replaykit项目实践相关推荐

  1. app内录屏开发 ios_iOS 一行代码实现手机录屏功能

    iOS9以后苹果推出ReplayKit框架,专门用来实现手机录屏功能,所以想要实现录屏功能再也不需要配置第三方了,下面是我实现的效果图,敬请欣赏!

  2. app内录屏开发 ios_【原创】苹果内置录屏SDK-ReplayKit库的使用说明

    1 #import "ViewController.h" 2 #import 3 4 static NSString *StartRecord = @"开始"; ...

  3. app内录屏开发 ios_盘点APP开发者喜爱iOS企业签名的原因

    相信很多APP开发者在做iOS签名的时候,最先接触的都会是iOS企业签名,因为App Store上架实在不容易,通过企业签名来完成APP分发这样的事情,可以说是一件美事.并且企业签名也是苹果官方默许的 ...

  4. app内录屏开发 ios_蓝七科技一款APP开发与ios企业签名过程中的雷区你知多少

    一款APP开发途中中的雷区你知多少? 有需要的客户请联系(vchat:co10101co) 一款APP从开发到上线中要经历几个阶段,而开发途中中的雷区你知道几个呢?这里蓝七科技同伴们一齐来扫雷,看看这 ...

  5. iOS端屏幕录制ReplayKit

    ReplayKit是苹果在iOS9上面提供的一个框架. 库的特性说明 目前这个库只支持真机允许,不支持模拟器. 系统版本必须高于iOS9. 不支持录avplayer播放的视频,这个可能是为了保护视频的 ...

  6. c语言录屏软件wps,WPS屏幕录制怎么操作?最详细的屏幕录制介绍

    屏幕录制可以随意录制电脑桌面屏幕上的任何内容,支持全屏录制.区域录制以及摄像头录制等多种视频录制模式.还支持锁定窗口录制.计划任务录制.录制视频编辑.切换录制格式等复杂功能.WPS录屏是一款功能强大的 ...

  7. Macbook怎么录屏?macbook怎么屏幕录制

    因为之前使用的是windows系统,工作时有专门的录屏工具,现如今换了苹果系统,那么,Macbook怎么录屏? 软件一:macOS自带录屏工具 QuickTime Player是Mac系统自带的录屏工 ...

  8. 苹果11怎么录屏_小米、苹果手机怎么录屏,这些手机屏幕录制秘方,快来收好...

    怎么在手机上录制屏幕内容?每天我们都会在手机上浏览一些内容或者玩游戏,也会经常有把手机上有趣的东西录制下来分享给他人的需求,那么手机屏幕录制有哪些方法呢?今天这篇文章给大家讲一讲小米和苹果手机的屏幕录 ...

  9. 直播录屏神器Camtasia 2023屏幕录制和视频编辑的软件

    如果你需要制作视频教程.游戏直播或其他视频内容,那么一个好的录屏软件就是必不可少的.Camtasia Studio是非常好用的录屏软件,它们可以记录计算机屏幕上发生的所有活动,并可捕捉声音.这些软件还 ...

最新文章

  1. 【Java小工匠聊密码学】--base58编码
  2. PTP4L命令手册(谷歌翻译)
  3. magento 加速(.htaccess)
  4. u大侠pe系统桌面计算机,详解各种PE启动的过程
  5. Python中is和==的区别
  6. 数据结构之查找算法:B树
  7. ubuntu18.04安装opencv4.3.0
  8. 三范式理解 数据库原理
  9. Collections.sort()和Arrays.sort()排序算法选择
  10. c语言编写点餐系统的图形界面,「分享」C语言如何编写图形界面
  11. 在web项目启动时,使用监听器来执行某个方法
  12. python常见图形代码可视化大全整理(包括动图)
  13. JDK安装包和Mysql安装包整理
  14. 反爬虫破解——裁判文书网
  15. 鼠标划过切换div显示
  16. python组合的语法_在Python中使用语法sugar-to-function组合是个好主意吗?
  17. YY/T 0316风险管理对医疗器械得应用学习分享(一)
  18. 斯特林数 java实现_斯特林数 - BILL666 - 博客园
  19. 三分钟教会你用Python爬取到喜欢的小姐姐图片
  20. 电脑之间利用串口传文件

热门文章

  1. 《转》Vmware vSphere常见问题汇总
  2. Cannot mix different versions of joi schemas
  3. 使用python manage.py startapp myapp未报错,但是没有创建出myapp
  4. 【C语言】PTA-查找书籍
  5. SysML 第一讲:SysML简介
  6. python远程聊天_Python实现多人在线匿名聊天的小程序
  7. revit 二次开发之创建图纸和放置视图
  8. [足式机器人]Part1 运动控制的替代方法Ch06——【Legged Robots that Balance 读书笔记】
  9. 微信影视站域名防封、微信影视站公众号域名防封
  10. circos 中堆积柱状图的画法