iOS 广播上传扩展、框架和应用程序之间的通信
Posted
技术标签:
【中文标题】iOS 广播上传扩展、框架和应用程序之间的通信【英文标题】:Communication between iOS Broadcast Upload Extension, Frameworks and App 【发布时间】:2020-02-18 23:09:32 【问题描述】:我有一个带有ViewController.swift
和Broadcast Upload app Extension
的示例应用程序。在我的项目中,我创建了两个框架:
MySDK
,是一个快速的框架,用于分析和处理CMSampleBuffer
,以避免广播应用扩展消耗过多的内存。这个SDK有一个单例,一个变量isReady
,函数func initialize
和func analyzeSampleBuffer(_ sampleBuffer: CMSampleBuffer)
Broadcaster
,是一个 swift 框架,用于调用 MySDK
调用分析和处理 CMSampleBuffer
(是的,听起来像是与 MySDK 的副本,但我不能这样做,我需要这两个框架)。此 SDK 具有单例和以下功能:func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?)
、func processSampleBuffer(_ sampleBuffer: CMSampleBuffer)
在这个项目中,我添加了一个名为 BroadcastExtension
的 Broadcast Upload App Extension
。
在他的主文件SampleHandler.swift
中,在processSampleBuffer
函数中,我使用我的Broadcaster SDK
将应用程序扩展的责任交给Broadcaster SDK
,然后再到MySDK
流式传输CMSampleBuffer
我从应用程序扩展收到的Broadcaster.shared.processSampleBuffer(sampleBuffer)
.最后MySDK
成功解析处理CMSampleBuffer
但是我的广播上传应用扩展占用了太多内存,并且在屏幕共享后X分钟后崩溃(应用扩展最大50Mb)。如何在应用扩展上使用更少的内存?
这是我的文件:
MySDK.swift:
import Foundation
import ReplayKit
@objcMembers public class MySDK
public static let shared = MySDK()
public var isReady = false
public func initialize()
// Init SDK
public func analyzeSampleBuffer(_ sampleBuffer: CMSampleBuffer)
// Analyze
Broadcaster.swift :
import Foundation
import ReplayKit
import MySDK
@objcMembers public class Broadcaster: NSObject, Codable
public static let shared = Broadcaster()
public func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?)
MySDK.shared.isReady = true
public func processSampleBuffer(_ sampleBuffer: CMSampleBuffer)
if MySDK.shared.isReady
MySDK.shared.analyzeSampleBuffer(sampleBuffer)
SampleHandler.swift :
import ReplayKit
import Broadcaster
class SampleHandler: RPBroadcastSampleHandler
override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?)
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
Broadcaster.shared.broadcastStarted(withSetupInfo: setupInfo)
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType)
switch sampleBufferType
case RPSampleBufferType.video:
// Handle video sample buffer
Broadcaster.shared.processSampleBuffer(sampleBuffer)
break
case RPSampleBufferType.audioApp:
break
case RPSampleBufferType.audioMic:
break
@unknown default:
fatalError("Unknown type of sample buffer")
我想在应用程序中使用相同的共享实例,但在应用程序扩展中。我尝试将组应用程序放在应用程序和应用程序扩展之间,并使用Userdefaults
和对应于组ID的suiteName
来发送共享实例,但是当我在应用程序扩展中接收时,地址内存不一样,创建对象的另一个实例(我想要应用程序和应用程序扩展之间的真正单例)。我不知道如何在应用扩展上节省内存,以及如何在 2 个框架、应用扩展和应用之间进行通信以在项目的每个部分使用相同的单例。
这是我的项目的层次结构:
【问题讨论】:
你有没有设法解决这个问题? @ChrisTomAlx 我们不能使用同一个单例。应用扩展和主应用是分开的。我们唯一能做的就是通过应用组共享数据进行通信。 :) 感谢您澄清这一点。非常感谢:) 【参考方案1】:但是我的广播上传应用扩展占用了太多内存,并且崩溃了 屏幕共享后 X 分钟后(应用扩展最大 50Mb)。 如何在应用扩展上使用更少的内存?
在广播上传扩展中避免繁重的计算和 50Mb 内存限制的一种方法是在 SampleHandler 中使用AVAssetWriter
,当它完成后你只需关闭它。之后,您可以使用共享文件夹从 AVAssetWriter 复制(视频/音频)。
NSURL* url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"your group name"];
NSString* sharedVideoPath = [NSString stringWithFormat:@"%@/video.mp4", url.path];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSError* error;
if([fileManager moveItemAtPath:PATH_TO_FILE_FROM_AVASSETWRITER toPath:sharedVideoPath error:&error])
NSLog(@"Successfully moved to shared folder!");
之后,您只需使用NSUserDefaults
告诉主应用程序sharedVideoPath
,主应用程序就可以访问它。
【讨论】:
正如my other comment 中提到的,看起来 AVAssetWriter 不能在广播扩展中使用,因为它需要前台权限。 hmm 我有点困惑,因为我能够在我的广播扩展中使用AVAssetWriter
,上次我测试它是使用 ios 13.3 版本,但我没有测试过更新版本。如果我有更新,我会在这里发布
我确认可以在广播扩展中使用 AVAssetWritter; github.com/romiroma/BroadcastWriter - 帮助程序包和示例项目【参考方案2】:
如果您只需要将屏幕共享视频保存到文件中,这很容易。 您应该必须在应用程序扩展和使用 AVAssetWriter 将缓冲区保存到共享容器路径(应用程序组)的框架之间进行通信。当广播开始并在 Sample Handler 上发送缓冲区时,只需将缓冲区传递到您的 AVAssetWriter 之后,当广播停止时,使用 finishWriting AVAssetWriter 完成写入。如果您想了解 BroadCastExtension 中的视频状态或过程,您可以使用将日志写入共享容器的日志管理器。例如:
func append(_ sample: CMSampleBuffer, with bufferType: RPSampleBufferType) -> Bool
guard self.state == .recording elsereturn false
guard assetWriter != nil elsereturn false
guard sample.isReady else
LogManager.shared.e(self,"Buffer Data Is not Ready")
return true
LogManager.shared.i(self,"Assets Writer Status : \(assetWriter.status.description)")
switch assetWriter.status
case .failed:
LogManager.shared.e(self,"Error occured, status = \(assetWriter.status), \(assetWriter.error!.localizedDescription) \(String(describing: assetWriter.error))")
return false
default:
break
switch bufferType
case .video:
self.lastVideoTime = sample.time
if let lastSampleBuffer = self.lastSampleBuffer
videoInput.appendIfPossible(lastSampleBuffer.with(updated: sample.time))
case .audioMic:
if self.audioEnabled
micInput.appendIfPossible(lastVideoTime != nil ? sample.with(updated: lastVideoTime) : sample)
case .audioApp:
if self.audioEnabled
audioInput.appendIfPossible(lastVideoTime != nil ? sample.with(updated: lastVideoTime) : sample)
@unknown default:
LogManager.shared.e(self,"Unkown buffer type")
return true
【讨论】:
以上是关于iOS 广播上传扩展、框架和应用程序之间的通信的主要内容,如果未能解决你的问题,请参考以下文章