iOS 广播上传扩展、框架和应用程序之间的通信

Posted

技术标签:

【中文标题】iOS 广播上传扩展、框架和应用程序之间的通信【英文标题】:Communication between iOS Broadcast Upload Extension, Frameworks and App 【发布时间】:2020-02-18 23:09:32 【问题描述】:

我有一个带有ViewController.swiftBroadcast Upload app Extension 的示例应用程序。在我的项目中,我创建了两个框架:

    MySDK,是一个快速的框架,用于分析和处理CMSampleBuffer,以避免广播应用扩展消耗过多的内存。这个SDK有一个单例,一个变量isReady,函数func initializefunc analyzeSampleBuffer(_ sampleBuffer: CMSampleBuffer) Broadcaster,是一个 swift 框架,用于调用 MySDK 调用分析和处理 CMSampleBuffer(是的,听起来像是与 MySDK 的副本,但我不能这样做,我需要这两个框架)。此 SDK 具有单例和以下功能:func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?)func processSampleBuffer(_ sampleBuffer: CMSampleBuffer)

在这个项目中,我添加了一个名为 BroadcastExtensionBroadcast 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 广播上传扩展、框架和应用程序之间的通信的主要内容,如果未能解决你的问题,请参考以下文章

详解Android广播机制

跨应用之间的广播安全-示例

iOS 广播扩展(屏幕广播)不起作用

iOS 8 beta - Today 扩展无法识别嵌入式框架

vlan通讯问题?

iOS通知中心