iOS swift - запись нескольких сегментов видео останавливается при воспроизведении в AVQueuePlayer - PullRequest
0 голосов
/ 05 июля 2018

Позвольте мне начать с того, что я долго искал ответ на этот вопрос и прочитал много SO-сообщений, но ни один не дал ответ, который мне нужен

Я пытаюсь записать несколько сегментов видео, используя несколько AVAssetWriters / AVAssetWriterInputs, которые я создаю и немедленно ставлю в очередь.

Проблема, с которой я сталкиваюсь, заключается в том, что хотя видео записываются правильно, когда они вводят AVQueuePlayer для воспроизведения в последовательном порядке, возникает случайное явление, при котором одно из видео останавливается, но продолжается для воспроизведения аудио.

Подготовка AVAssetWriters / VideoInputs / AudioInputs:

private func setupAssetWriters() {
    guard assetWriterOne == nil else { return }
    // This just returns a URL with ".mov" as the file suffix in the absoluteString.
    outputFileURLOne = createRandomizedURL(withMediaFileType: .mov) 
    (assetWriterOne, videoAssetWriterInputOne, audioAssetWriterInputOne) = configureAssetWriter(withInput: outputFileURLOne)

    guard assetWriterTwo == nil else { return }
    outputFileURLTwo = createRandomizedURL(withMediaFileType: .mov)
    (assetWriterTwo, videoAssetWriterInputTwo, audioAssetWriterInputTwo) = configureAssetWriter(withInput: outputFileURLTwo)

    guard assetWriterThree == nil else { return }
    outputFileURLThree = createRandomizedURL(withMediaFileType: .mov)
    (assetWriterThree, videoAssetWriterInputThree, audioAssetWriterInputThree) = configureAssetWriter(withInput: outputFileURLThree)

    guard assetWriterFour == nil else { return }
    outputFileURLFour = createRandomizedURL(withMediaFileType: .mov)
    (assetWriterFour, videoAssetWriterInputFour, audioAssetWriterInputFour) = configureAssetWriter(withInput: outputFileURLFour)

}


private func configureAssetWriter(withInput url: URL) -> (assetWriter: AVAssetWriter?, videoAssetWriterInput: AVAssetWriterInput?, audioAssetWriterInput: AVAssetWriterInput?){
    do {

        let recommendedVideoSettings: [String: Any]? = videoDataOutput?.recommendedVideoSettingsForAssetWriter(writingTo: AVFileType.mov)
        let assetWriter = try VideoAssetWriter(url: url, fileType: AVFileType.mov)
        guard
            assetWriter.canApply(outputSettings: recommendedVideoSettings, forMediaType: AVMediaType.video),
            assetWriter.canApply(outputSettings: audioSettings, forMediaType: AVMediaType.audio)
            else { return (nil, nil, nil) }

        let videoAssetWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: recommendedVideoSettings, sourceFormatHint: videoFormatDescription)
        let audioAssetWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: audioSettings)

        guard
            assetWriter.canAdd(videoAssetWriterInput),
            assetWriter.canAdd(audioAssetWriterInput)
            else { return (nil, nil, nil) }
        assetWriter.add(videoAssetWriterInput)
        assetWriter.add(audioAssetWriterInput)

        videoAssetWriterInput.expectsMediaDataInRealTime = true
        audioAssetWriterInput.expectsMediaDataInRealTime = true

        return (assetWriter, videoAssetWriterInput, audioAssetWriterInput)

    } catch let error {
        print("error getting AVAssetWriter: \(error.localizedDescription)")
        return (nil, nil, nil)
    }
}

Обработка CMSampleBuffers Теперь, когда AVAssetWriters и их входы инициализированы, я нажимаю кнопку записи, которая изменяет значение Bool, чтобы сообщить AVCaptureVideoDataOutputSampleBufferDelegate о начале захвата CMSampleBuffers:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    if isRecording {
        processVideoSamples(withOutput: output, sampleBuffer: sampleBuffer)
    }
}








private func processVideoSamples(withOutput output: AVCaptureOutput, sampleBuffer: CMSampleBuffer) {

    switch currentVideoInProcess {
    case 1:
        processSamples(usingAssetWriter: assetWriterOne, videoAssetWriterInput: videoAssetWriterInputOne, audioAssetWriterInput: audioAssetWriterInputOne, withOutput: output, sampleBuffer: sampleBuffer)
        break

    case 2:
        processSamples(usingAssetWriter: assetWriterTwo, videoAssetWriterInput: videoAssetWriterInputTwo, audioAssetWriterInput: audioAssetWriterInputTwo, withOutput: output, sampleBuffer: sampleBuffer)
        break

    case 3:
        processSamples(usingAssetWriter: assetWriterThree, videoAssetWriterInput: videoAssetWriterInputThree, audioAssetWriterInput: audioAssetWriterInputThree, withOutput: output, sampleBuffer: sampleBuffer)
        break

    case 4:
        processSamples(usingAssetWriter: assetWriterFour, videoAssetWriterInput: videoAssetWriterInputFour, audioAssetWriterInput: audioAssetWriterInputFour, withOutput: output, sampleBuffer: sampleBuffer)
        break

    default:

        break
    }
}


private func processSamples(usingAssetWriter assetWriter: VideoAssetWriter?, videoAssetWriterInput: AVAssetWriterInput?, audioAssetWriterInput: AVAssetWriterInput?, withOutput output: AVCaptureOutput, sampleBuffer: CMSampleBuffer) {

        guard CMSampleBufferDataIsReady(sampleBuffer) else { return }

        guard
            assetWriter != nil,
            videoAssetWriterInput != nil,
            audioAssetWriterInput != nil
            else { return }

        if assetWriter?.status == .unknown {
            if let _ = output as? AVCaptureVideoDataOutput {
                print("\n STARTED RECORDING")
                assetWriter?.startWriting()
                closingTime = sampleTime
                let startRecordingTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
                assetWriter?.startSession(atSourceTime: startRecordingTime)
            } else {
                print("output type unknown")
                return
            }
        }

        if assetWriter?.status == .failed { return }

        guard assetWriter?.status == .writing else { return }

        if let _ = output as? AVCaptureVideoDataOutput {

            if (videoAssetWriterInput?.isReadyForMoreMediaData)! {
                videoAssetWriterInput?.append(sampleBuffer)   
                if assetWriter?.hasWrittenFirstVideoSample == false {
                    print("added 1st video frame")
                    assetWriter?.hasWrittenFirstVideoSample = true
                }
            } else {
                print("video writer not ready")
            }
        } else if let _ = output as? AVCaptureAudioDataOutput {
            if (audioAssetWriterInput?.isReadyForMoreMediaData)! && assetWriter?.hasWrittenFirstVideoSample == true {
                audioAssetWriterInput?.append(sampleBuffer)
            } else {
                print("audio writer not ready OR video not written yet")
            }
        }

}

Получение видеофрагментов от каждого AVAssetWriter

Что касается получения сегментов видео, я вызываю эту функцию endRecording через определенные интервалы, чтобы получить видео из outputURL каждого AVAssetWriter:

private func endRecordingOfCurrentWriter(completion: @escaping (_ fileURL: URL?, _ error: Error?) -> ()) {

    // currentVideoInProcess is just an Int to keep track of which AVAssetWriter is current processing CMSampleBuffers
    switch currentVideoInProcess {
    case 1:
        endRecording(atOutputURL: outputFileURLOne, usingAssetWriter: assetWriterOne, videoAssetWriterInput: videoAssetWriterInputOne, audioAssetWriterInput: audioAssetWriterInputOne) { (fileURL, error) in
            completion(fileURL, error)
        }
        break

    case 2:
        endRecording(atOutputURL: outputFileURLTwo, usingAssetWriter: assetWriterTwo, videoAssetWriterInput: videoAssetWriterInputTwo, audioAssetWriterInput: audioAssetWriterInputTwo) { (fileURL, error) in
            completion(fileURL, error)
        }
        break

    case 3:
        endRecording(atOutputURL: outputFileURLThree, usingAssetWriter: assetWriterThree, videoAssetWriterInput: videoAssetWriterInputThree, audioAssetWriterInput: audioAssetWriterInputThree) { (fileURL, error) in
            completion(fileURL, error)
        }
        break

    case 4:
        endRecording(atOutputURL: outputFileURLFour, usingAssetWriter: assetWriterFour, videoAssetWriterInput: videoAssetWriterInputFour, audioAssetWriterInput: audioAssetWriterInputFour) { (fileURL, error) in
            completion(fileURL, error)
        }
        break

    default: break
    }

    currentVideoInProcess += 1

}

private func endRecording(atOutputURL outputURL: URL, usingAssetWriter assetWriter: VideoAssetWriter?, videoAssetWriterInput: AVAssetWriterInput?, audioAssetWriterInput: AVAssetWriterInput?, completion: @escaping (_ fileURL: URL?, _ error: Error?) -> ()) {

    if assetWriter?.status.rawValue == 1 {
        videoAssetWriterInput?.markAsFinished()
        audioAssetWriterInput?.markAsFinished()
    }

    assetWriter?.finishWriting() {
        let status = assetWriter?.status

        guard assetWriter?.error == nil else {
            if let error = assetWriter?.error {
                completion(nil, assetWriter?.error)
            }
            return
        }

        switch status {
        case .completed?:
            completion(assetWriter?.outputURL, nil)
        case .failed?:
            completion(nil, assetWriter?.error)
        case .cancelled?:
            completion(nil, assetWriter?.error)
        default:
            completion(nil, assetWriter?.error)
        }
    }
}

Проблема

Чтобы сформулировать проблему, эта реализация создает четыре видео практически без проблем в каждом видео, , однако , проблема возникает, когда я пытаюсь воспроизвести эти видео в последовательном порядке, используя AVQueuePlayer. Одно из этих видео случайным образом зависает ТОЛЬКО в самом начале , продолжая при этом воспроизводить звук до конца. Я мог выполнять эту запись столько раз, сколько мне нравится, и получать разные результаты. Фактически, есть некоторые случаи, когда замораживание НЕ происходит вообще и воспроизводит все видео без проблем.

Но чаще всего замораживание происходит с совершенно случайными интервалами. Эта заморозка не разморозить . Остальная часть приложения по-прежнему отзывчива (я могу нажать кнопку, чтобы удалить все видео и позволить мне начать все заново с самого начала). Но повторное выполнение записи приведет к остановке через некоторый другой случайный интервал времени.

Я пытался объединить видео в однокадровое видео и воспроизвести его в приложении "Фотографии", и там также происходит случайное замораживание. НО , я также загружаю видео отдельно в приложение «Фотографии» (четыре коротких видео и одно длинное видео загружаются в фотографии), и каждое видео воспроизводится совершенно нормально, если воспроизводится отдельно.

1 Ответ

0 голосов
/ 05 июля 2018

Я раньше не использовал AVQueuePlayer для воспроизведения нескольких видео, но по своему опыту я предлагаю вам попробовать AVMutableComposition для подключения видео. и используйте AVPlayer для его воспроизведения.

...