Выборочное считывание выборочных буферов из определенных временных диапазонов, а затем запись их в средство записи ресурсов — почему AVPlayer застрял при загрузке?

#ios #swift #avfoundation

#ios #swift #avfoundation

Вопрос:

Учитывая AVAsset, я выполняю запрос траектории видения на нем и хотел бы записать видеоресурс, содержащий только кадры с траекториями (отфильтруйте время простоя в спортивных кадрах, где мяч не движется).

Я не уверен, что было бы хорошим подходом, но в качестве отправной точки я попробовал следующий конвейер:

  1. Скопируйте буфер образцов из источника AVAssetReaderOutput .
  2. Выполните запрос траектории для обработчика видения, параметризованного буфером выборки.
  3. Для каждого результата VNTrajectoryObservation (обнаружена траектория) используйте его значение CMTimeRange , чтобы настроить новый AVAssetReader набор для этого временного диапазона.
  4. Добавьте буфер выборки с ограниченным временным диапазоном к одному AVAssetWriterInput , пока forEach он не будет завершен.

В коде:

 private func transferSamplesAsynchronously(from readerOutput: AVAssetReaderOutput,  to writerInput: AVAssetWriterInput,  onQueue queue: DispatchQueue,  sampleBufferProcessor: SampleBufferProcessor,  completionHandler: @escaping () -gt; Void) { /*  The writerInput continously invokes this closure until finished or  cancelled. It throws an NSInternalInconsistencyException if called more  than once for the same writer. */ writerInput.requestMediaDataWhenReady(on: queue) {  var isDone = false   /*  While the writerInput accepts more data, process the sampleBuffer  and then transfer the processed sample to the writerInput.  */  while writerInput.isReadyForMoreMediaData {  if self.isCancelled {  isDone = true  break  }   // Get the next sample from the asset reader output.  guard let sampleBuffer = readerOutput.copyNextSampleBuffer() else {  // The asset reader output has no more samples to vend.  isDone = true  break  }   let visionHandler = VNImageRequestHandler(cmSampleBuffer: sampleBuffer, orientation: self.orientation, options: [:])    do {  try visionHandler.perform([self.detectTrajectoryRequest])  if let results = self.detectTrajectoryRequest.results {  try results.forEach { result in  let assetReader = try AVAssetReader(asset: self.asset)  assetReader.timeRange = result.timeRange    let trackOutput = AVTrackOutputs.firstTrackOutput(ofType: .video, fromTracks: self.asset.tracks,  withOutputSettings: nil)      assetReader.add(trackOutput)    assetReader.startReading()    guard let sampleBuffer = trackOutput.copyNextSampleBuffer() else {  // The asset reader output has no more samples to vend.  isDone = true  return  }    // Append the sample to the asset writer input.  guard writerInput.append(sampleBuffer) else {  /*  The writer could not append the sample buffer.  The `readingAndWritingDidFinish()` function handles any  error information from the asset writer.  */  isDone = true  return  }  }  }  } catch {  print(error)  }  }   if isDone {  /*  Calling `markAsFinished()` on the asset writer input does the  following:  1. Unblocks any other inputs needing more samples.  2. Cancels further invocations of this "request media data"  callback block.  */  writerInput.markAsFinished()   /*  Tell the caller the reader output and writer input finished  transferring samples.  */  completionHandler()  } } }  private func readingAndWritingDidFinish(assetReaderWriter: AVAssetReaderWriter,  completionHandler: @escaping FinishHandler) { if isCancelled {  completionHandler(.success(.cancelled))  return }  // Handle any error during processing of the video. guard sampleTransferError == nil else {  assetReaderWriter.cancel()  completionHandler(.failure(sampleTransferError!))  return }  // Evaluate the result reading the samples. let result = assetReaderWriter.readingCompleted() if case .failure = result {  completionHandler(result)  return }  /*  Finish writing, and asynchronously evaluate the results from writing  the samples. */ assetReaderWriter.writingCompleted { result in  completionHandler(result)  return } }  

При запуске я получаю следующее:

введите описание изображения здесь

No error is caught in the first catch clause, and none are caught in private func readingAndWritingDidFinish(assetReaderWriter: AVAssetReaderWriter, completionHandler: @escaping FinishHandler) , the completion handler is called.

Help with any of the following questions would be appreciated:

  • What is causing what appears to be indefinite loading?
  • How might I isolate the problem further?
  • Am I misusing or misunderstanding how to selectively read from time ranges of AVAssetReader objects?
  • Should I forego the AVAssetReader / AVAsssetWriter route entirely, and use the time ranges with AVAssetExportSession instead? I don’t know how the two approaches compare, or what to consider when choosing between the two.