我有一個avassetwriter使用應用過濾器錄制視頻,然后通過avqueueplayer.
我的問題是,在播放時,錄制的視頻在第一幀顯示黑/白屏。據我了解,這是由于作者在捕獲第一個實際視頻幀之前捕獲了音頻。
為了解決這個問題,我在附加到音頻撰寫器輸入時放置了一個布爾檢查,第一個視頻幀是否附加到配接器。也就是說,盡管列印了時間戳,但我仍然在播放時看到一個黑框,這表明視頻在音頻之前......我還嘗試檢查以在輸出 == 視頻時開始寫入會話,但最終得到同樣的結果。
任何指導或其他解決方法將不勝感激。
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer).seconds
if output == _videoOutput {
if connection.isVideoOrientationSupported { connection.videoOrientation = .portrait }
guard let cvImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let ciImage = CIImage(cvImageBuffer: cvImageBuffer)
guard let filteredCIImage = applyFilters(inputImage: ciImage) else { return }
self.ciImage = filteredCIImage
guard let cvPixelBuffer = getCVPixelBuffer(from: filteredCIImage) else { return }
self.cvPixelBuffer = cvPixelBuffer
self.ciContext.render(filteredCIImage, to: cvPixelBuffer, bounds: filteredCIImage.extent, colorSpace: CGColorSpaceCreateDeviceRGB())
metalView.draw()
}
switch _captureState {
case .start:
guard let outputUrl = tempURL else { return }
let writer = try! AVAssetWriter(outputURL: outputUrl, fileType: .mp4)
let videoSettings = _videoOutput!.recommendedVideoSettingsForAssetWriter(writingTo: .mp4)
let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
videoInput.mediaTimeScale = CMTimeScale(bitPattern: 600)
videoInput.expectsMediaDataInRealTime = true
let pixelBufferAttributes = [
kCVPixelBufferCGImageCompatibilityKey: NSNumber(value: true),
kCVPixelBufferCGBitmapContextCompatibilityKey: NSNumber(value: true),
kCVPixelBufferPixelFormatTypeKey: NSNumber(value: Int32(kCVPixelFormatType_32ARGB))
] as [String:Any]
let adapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoInput, sourcePixelBufferAttributes: pixelBufferAttributes)
if writer.canAdd(videoInput) { writer.add(videoInput) }
let audioSettings = _audioOutput!.recommendedAudioSettingsForAssetWriter(writingTo: .mp4) as? [String:Any]
let audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
audioInput.expectsMediaDataInRealTime = true
if writer.canAdd(audioInput) { writer.add(audioInput) }
_filename = outputUrl.absoluteString
_assetWriter = writer
_assetWriterVideoInput = videoInput
_assetWriterAudioInput = audioInput
_adapter = adapter
_captureState = .capturing
_time = timestamp
writer.startWriting()
writer.startSession(atSourceTime: CMTime(seconds: timestamp, preferredTimescale: CMTimeScale(600)))
case .capturing:
if output == _videoOutput {
if _assetWriterVideoInput?.isReadyForMoreMediaData == true {
let time = CMTime(seconds: timestamp, preferredTimescale: CMTimeScale(600))
_adapter?.append(self.cvPixelBuffer, withPresentationTime: time)
if !hasWrittenFirstVideoFrame { hasWrittenFirstVideoFrame = true }
}
} else if output == _audioOutput {
if _assetWriterAudioInput?.isReadyForMoreMediaData == true, hasWrittenFirstVideoFrame {
_assetWriterAudioInput?.append(sampleBuffer)
}
}
break
case .end:
guard _assetWriterVideoInput?.isReadyForMoreMediaData == true, _assetWriter!.status != .failed else { break }
_assetWriterVideoInput?.markAsFinished()
_assetWriterAudioInput?.markAsFinished()
_assetWriter?.finishWriting { [weak self] in
guard let output = self?._assetWriter?.outputURL else { return }
self?._captureState = .idle
self?._assetWriter = nil
self?._assetWriterVideoInput = nil
self?._assetWriterAudioInput = nil
self?.previewRecordedVideo(with: output)
}
default:
break
}
}
uj5u.com熱心網友回復:
確實,在這種.capturing狀態下,您可以通過丟棄前面的音頻樣本緩沖區來確保寫入的第一個樣本緩沖區是視頻樣本緩沖區 -但是您仍然允許音頻樣本緩沖區的演示時間戳以writer.startSession(atSourceTime:). 這意味著您的視頻一開始什么也沒有,所以您不僅短暫地聽不到任何東西(這很難注意到),而且您也看不到任何東西,而您的視頻播放器恰好用黑框表示。
從這個角度來看,沒有黑框需要移除,只有一個空白需要填補。您可以通過從第一個視頻時間戳開始會話來填補這個空白。
這可以通過防止狀態中的非視頻樣本緩沖區來實作.start,或者通過移動writer.startSession(atSourceTime:)到if !hasWrittenFirstVideoFrame {}我猜不太干凈。
ps 為什么要在CMTime和 秒之間來回轉換?為什么不堅持CMTime?
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/348893.html
