AVAssetWriterInput 和 readyForMoreMediaData

发布于 2024-11-05 02:16:31 字数 633 浏览 1 评论 0原文

AVAssetWriterInput 的 readyForMoreMediaData 是否在后台线程中更新?如果readyForMoreMediaData为NO,我可以在主线程中阻塞并等待直到该值更改为YES吗?

我通过将数据推送到 AVAssetWriterInput 来使用它(即不使用 requestMediaDataWhenReadyOnQueue),并且我设置了 ExpectsMediaDataInRealTime,并且 99.9% 的时间我可以在其上调用appendSampleBuffer(或appendPixelBuffer),速度与我的应用程序生成帧的速度一样快。

除非您在 AVAssetWriter 会话中将设备 (iPhone 3GS) 置于睡眠状态 15 分钟左右,否则这种方法可以正常工作。唤醒设备后,appendPixelBuffer 有时会收到错误消息:“当readyForMoreMediaData 为NO 时,无法附加像素缓冲区”。因此我的问题是如何最好地响应readyForMoreMediaData=NO以及我是否可以在主线程中稍等一下,如下所示:

while ( ![assetWriterInput readyForMoreMediaData] )
{
    Sleep for a few milliseconds
}

Is AVAssetWriterInput's readyForMoreMediaData updated in a background thread? If readyForMoreMediaData is NO, can I block in the main thread and wait until the value changes to YES?

I'm using an AVAssetWriterInput by pushing data to it (i.e. without using requestMediaDataWhenReadyOnQueue) and I've set expectsMediaDataInRealTime, and 99.9% of the time I can just call appendSampleBuffer (or appendPixelBuffer) on it as fast as my app can generate frames.

This works fine unless you put the device (iPhone 3GS) to sleep for 15 minutes or so in the middle of an AVAssetWriter session. After waking up the device, appendPixelBuffer sometimes gets an error saying, "A pixel buffer cannot be appended when readyForMoreMediaData is NO". Hence my question - how best to respond to readyForMoreMediaData=NO and if I can just wait a bit in the main thread like so:

while ( ![assetWriterInput readyForMoreMediaData] )
{
    Sleep for a few milliseconds
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

洋洋洒洒 2024-11-12 02:16:32

小心不要只是阻塞线程,这是我之前所做的,但不起作用:

while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) {
  [NSThread sleepForTimeInterval:0.1];
}

上述方法有时会在我的 iPad2 上失败。这样做反而解决了问题:

while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) {
  NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:0.1];
  [[NSRunLoop currentRunLoop] runUntilDate:maxDate];
}

Be careful not to just block the thread, here is what I was doing before that was not working:

while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) {
  [NSThread sleepForTimeInterval:0.1];
}

The above approach would fail sometimes on my iPad2. Doing this instead fixed the problem:

while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) {
  NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:0.1];
  [[NSRunLoop currentRunLoop] runUntilDate:maxDate];
}
无可置疑 2024-11-12 02:16:32

找不到类似的东西,所以我把它留在这里。 Swift 4 解决方案。
最好使用精确的技术来解决这个问题。 Fe 使用 NSCondition:

func startRecording() {
        // start recording code goes here 

    readyForMediaCondition = NSCondition()
    readyForMediaObservation = pixelBufferInput?.assetWriterInput.observe(\.isReadyForMoreMediaData, options: .new, changeHandler: { [weak self](_, change) in
        guard let isReady = change.newValue else {
            return
        }

        if isReady {
            self?.readyForMediaCondition?.lock()
            self?.readyForMediaCondition?.signal()
            self?.readyForMediaCondition?.unlock()
        }
    })
}

下一步:

func grabFrame(time: CMTime? = nil) {
    readyForMediaCondition?.lock()
    while !pixelBufferInput!.assetWriterInput.isReadyForMoreMediaData {
        readyForMediaCondition?.wait()
    }
    readyForMediaCondition?.unlock()

    // append your framebuffer here
}

不要忘记最后使观察者无效

readyForMediaObservation?.invalidate()

Don't find something similar, so I left it here. Swift 4 solution.
Would better using precise technique to solve that problem. F.e. using NSCondition:

func startRecording() {
        // start recording code goes here 

    readyForMediaCondition = NSCondition()
    readyForMediaObservation = pixelBufferInput?.assetWriterInput.observe(\.isReadyForMoreMediaData, options: .new, changeHandler: { [weak self](_, change) in
        guard let isReady = change.newValue else {
            return
        }

        if isReady {
            self?.readyForMediaCondition?.lock()
            self?.readyForMediaCondition?.signal()
            self?.readyForMediaCondition?.unlock()
        }
    })
}

Next:

func grabFrame(time: CMTime? = nil) {
    readyForMediaCondition?.lock()
    while !pixelBufferInput!.assetWriterInput.isReadyForMoreMediaData {
        readyForMediaCondition?.wait()
    }
    readyForMediaCondition?.unlock()

    // append your framebuffer here
}

Don't forget to invalidate observer in the end

readyForMediaObservation?.invalidate()
把回忆走一遍 2024-11-12 02:16:32

轮询不是明智的解决方案。

您应该使用 requestMediaDataWhenReadyOnQueue

assetWriterInput.requestMediaDataWhenReady(on: serialQueue) { [weak self] in
    guard let self = self else { return }
    while self.assetWriterInput.isReadyForMoreMediaData {
        // Copy the next sample buffer from source media.
        guard let nextSampleBuffer = copyNextSampleBufferToWrite() else {
            // Mark the input as finished.
            self.assetWriterInput.markAsFinished()
            break
        }
        // Append the sample buffer to the input.
        self.assetWriterInput.append(nextSampleBuffer)
    }
}

Polling is not the advisable solution.

You should be using requestMediaDataWhenReadyOnQueue

assetWriterInput.requestMediaDataWhenReady(on: serialQueue) { [weak self] in
    guard let self = self else { return }
    while self.assetWriterInput.isReadyForMoreMediaData {
        // Copy the next sample buffer from source media.
        guard let nextSampleBuffer = copyNextSampleBufferToWrite() else {
            // Mark the input as finished.
            self.assetWriterInput.markAsFinished()
            break
        }
        // Append the sample buffer to the input.
        self.assetWriterInput.append(nextSampleBuffer)
    }
}
深空失忆 2024-11-12 02:16:32
        int waitTime = 300;
        while (weakSelf.input.readyForMoreMediaData == NO) {
            NSLog(@"readyForMoreMediaData is NO");
            NSTimeInterval waitIntervale = 0.001 * waitTime;
            NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:waitIntervale];
            [[NSRunLoop currentRunLoop] runUntilDate:maxDate];
            waitTime += 200; // add 200ms every time
        }
        int waitTime = 300;
        while (weakSelf.input.readyForMoreMediaData == NO) {
            NSLog(@"readyForMoreMediaData is NO");
            NSTimeInterval waitIntervale = 0.001 * waitTime;
            NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:waitIntervale];
            [[NSRunLoop currentRunLoop] runUntilDate:maxDate];
            waitTime += 200; // add 200ms every time
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文