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

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

  1. MySDK,是一个快速的框架,用于分析和处理CMSampleBuffer,以避免广播应用扩展消耗过多的内存。这个SDK有一个单例,一个变量isReady,函数func initializefunc analyzeSampleBuffer(_ sampleBuffer: CMSampleBuffer)
  2. 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 我们不能使用同一个单例。应用扩展和主应用是分开的。我们唯一能做的就是通过应用组共享数据进行通信。 :)
  • 感谢您澄清这一点。非常感谢:)

标签: ios swift frameworks ios-app-extension replaykit


【解决方案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 else{return false}
    
    guard assetWriter != nil else{return 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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多