вместо цикла пока. Вы блокируете возврат метода до тех пор, пока не будет достигнут этот обработчик завершения?

едаю содержимое моего приложения на свой RTMP-сервер и использую RPBroadcastSampleHandler.

Одним из методов является

override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
    switch sampleBufferType {
    case .video:
        streamer.appendSampleBuffer(sampleBuffer, withType: .video)
        captureOutput(sampleBuffer)
    case .audioApp:
        streamer.appendSampleBuffer(sampleBuffer, withType: .audio)
        captureAudioOutput(sampleBuffer)
    case .audioMic:
        ()
    }
}

И метод captureOutput является

self.lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);

    // Append the sampleBuffer into videoWriterInput
    if self.isRecordingVideo {
        if self.videoWriterInput!.isReadyForMoreMediaData {
            if self.videoWriter!.status == AVAssetWriterStatus.writing {
                let whetherAppendSampleBuffer = self.videoWriterInput!.append(sampleBuffer)
                print(">>>>>>>>>>>>>The time::: \(self.lastSampleTime.value)/\(self.lastSampleTime.timescale)")
                if whetherAppendSampleBuffer {
                    print("DEBUG::: Append sample buffer successfully")
                } else {
                    print("WARN::: Append sample buffer failed")
                }
            } else {
                print("WARN:::The videoWriter status is not writing")
            }
        } else {
            print("WARN:::Cannot append sample buffer into videoWriterInput")
        }
    }

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

    let fileManager = FileManager.default
    let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    self.videoOutputFullFileName = documentsPath.stringByAppendingPathComponent(str: "test_capture_video.mp4")

    if self.videoOutputFullFileName == nil {
        print("ERROR:The video output file name is nil")
        return
    }

    self.isRecordingVideo = true
    if fileManager.fileExists(atPath: self.videoOutputFullFileName!) {
        print("WARN:::The file: \(self.videoOutputFullFileName!) exists, will delete the existing file")
        do {
            try fileManager.removeItem(atPath: self.videoOutputFullFileName!)
        } catch let error as NSError {
            print("WARN:::Cannot delete existing file: \(self.videoOutputFullFileName!), error: \(error.debugDescription)")
        }

    } else {
        print("DEBUG:::The file \(self.videoOutputFullFileName!) doesn't exist")
    }

    let screen = UIScreen.main
    let screenBounds = info.size
    let videoCompressionPropertys = [
        AVVideoAverageBitRateKey: screenBounds.width * screenBounds.height * 10.1
    ]

    let videoSettings: [String: Any] = [
        AVVideoCodecKey: AVVideoCodecH264,
        AVVideoWidthKey: screenBounds.width,
        AVVideoHeightKey: screenBounds.height,
        AVVideoCompressionPropertiesKey: videoCompressionPropertys
    ]

    self.videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)

    guard let videoWriterInput = self.videoWriterInput else {
        print("ERROR:::No video writer input")
        return
    }

    videoWriterInput.expectsMediaDataInRealTime = true

    // Add the audio input
    var acl = AudioChannelLayout()
    memset(&acl, 0, MemoryLayout<AudioChannelLayout>.size)
    acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
    let audioOutputSettings: [String: Any] =
        [ AVFormatIDKey: kAudioFormatMPEG4AAC,
          AVSampleRateKey : 44100,
          AVNumberOfChannelsKey : 1,
          AVEncoderBitRateKey : 64000,
          AVChannelLayoutKey : Data(bytes: &acl, count: MemoryLayout<AudioChannelLayout>.size)]

    audioWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: audioOutputSettings)

    guard let audioWriterInput = self.audioWriterInput else {
        print("ERROR:::No audio writer input")
        return
    }

    audioWriterInput.expectsMediaDataInRealTime = true

    do {
        self.videoWriter = try AVAssetWriter(outputURL: URL(fileURLWithPath: self.videoOutputFullFileName!), fileType: AVFileTypeMPEG4)
    } catch let error as NSError {
        print("ERROR:::::>>>>>>>>>>>>>Cannot init videoWriter, error:\(error.localizedDescription)")
    }

    guard let videoWriter = self.videoWriter else {
        print("ERROR:::No video writer")
        return
    }

    if videoWriter.canAdd(videoWriterInput) {
        videoWriter.add(videoWriterInput)
    } else {
        print("ERROR:::Cannot add videoWriterInput into videoWriter")
    }

    //Add audio input
    if videoWriter.canAdd(audioWriterInput) {
        videoWriter.add(audioWriterInput)
    } else {
        print("ERROR:::Cannot add audioWriterInput into videoWriter")
    }

    if videoWriter.status != AVAssetWriterStatus.writing {
        print("DEBUG::::::::::::::::The videoWriter status is not writing, and will start writing the video.")

        let hasStartedWriting = videoWriter.startWriting()
        if hasStartedWriting {
            videoWriter.startSession(atSourceTime: self.lastSampleTime)
            print("DEBUG:::Have started writting on videoWriter, session at source time: \(self.lastSampleTime)")
            LOG(videoWriter.status.rawValue)
        } else {
            print("WARN:::Fail to start writing on videoWriter")
        }
    } else {
        print("WARN:::The videoWriter.status is writing now, so cannot start writing action on videoWriter")
    }

И затем сохранение и завершение записи в конце потока:

    print("DEBUG::: Starting to process recorder final...")
    print("DEBUG::: videoWriter status: \(self.videoWriter!.status.rawValue)")
    self.isRecordingVideo = false

    guard let videoWriterInput = self.videoWriterInput else {
        print("ERROR:::No video writer input")
        return
    }
    guard let videoWriter = self.videoWriter else {
        print("ERROR:::No video writer")
        return
    }

    guard let audioWriterInput = self.audioWriterInput else {
        print("ERROR:::No audio writer input")
        return
    }

    videoWriterInput.markAsFinished()
    audioWriterInput.markAsFinished()
    videoWriter.finishWriting {
        if videoWriter.status == AVAssetWriterStatus.completed {
            print("DEBUG:::The videoWriter status is completed")

            let fileManager = FileManager.default
            if fileManager.fileExists(atPath: self.videoOutputFullFileName!) {
                print("DEBUG:::The file: \(self.videoOutputFullFileName ?? "") has been saved in documents folder, and is ready to be moved to camera roll")


                let sharedFileURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.jp.awalker.co.Hotter")
                guard let documentsPath = sharedFileURL?.path else {
                    LOG("ERROR:::No shared file URL path")
                    return
                }
                let finalFilename = documentsPath.stringByAppendingPathComponent(str: "test_capture_video.mp4")

                //Check whether file exists
                if fileManager.fileExists(atPath: finalFilename) {
                    print("WARN:::The file: \(finalFilename) exists, will delete the existing file")
                    do {
                        try fileManager.removeItem(atPath: finalFilename)
                    } catch let error as NSError {
                        print("WARN:::Cannot delete existing file: \(finalFilename), error: \(error.debugDescription)")
                    }
                } else {
                    print("DEBUG:::The file \(self.videoOutputFullFileName!) doesn't exist")
                }

                do {
                    try fileManager.copyItem(at: URL(fileURLWithPath: self.videoOutputFullFileName!), to: URL(fileURLWithPath: finalFilename))
                }
                catch let error as NSError {
                    LOG("ERROR:::\(error.debugDescription)")
                }

                PHPhotoLibrary.shared().performChanges({
                    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: finalFilename))
                }) { completed, error in
                    if completed {
                        print("Video \(self.videoOutputFullFileName ?? "") has been moved to camera roll")
                    }

                    if error != nil {
                        print ("ERROR:::Cannot move the video \(self.videoOutputFullFileName ?? "") to camera roll, error: \(error!.localizedDescription)")
                    }
                }

            } else {
                print("ERROR:::The file: \(self.videoOutputFullFileName ?? "") doesn't exist, so can't move this file camera roll")
            }
        } else {
            print("WARN:::The videoWriter status is not completed, stauts: \(videoWriter.status)")
        }
    }

У меня проблема в том, что код завершения finishWriting никогда не достигается. Автор остается в состоянии «запись», поэтому видеофайл не сохраняется.

Если я удаляю строку «finishWriting» и просто оставляю код завершения для запуска, файл сохраняется, но не завершается должным образом, и когда я пытаюсь просмотреть его, он не воспроизводится, поскольку в нем, вероятно, отсутствуют метаданные.

Есть ли другой способ сделать это? На самом деле я не хочу начинать захват с помощью AVKit для сохранения записи, потому что он потребляет слишком много ресурсов процессора, а CMSampleBuffer в RPBroadcastSampleHandler уже содержит видеоданные, но, возможно, использование AVKit вообще является неправильным шагом?

Что я должен изменить? Как сохранить видео из этого CMSampleBuffer?

 Marty09 окт. 2018 г., 09:49
Вы захотите умножить размер экрана на масштаб экрана также, для полного качества.
 McDonal_1106 февр. 2019 г., 11:08
Как ты решил это? Я тоже застрял в той же строкеЕсли я уберу строку "finishWriting" , Я могу сохранить файл в doc.dir, но я не могу воспроизвести этот файл, а также я не могу сохранить этот видеофайл в камере моего телефона. Вы можете мне помочь ??

Ответы на вопрос(1)

https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwritingwithcompletionhandl

This method returns immediately and causes its work to be performed asynchronously

когдаbroadcastFinished возвращается, ваше расширение убито. Единственный способ заставить это работать - заблокировать возврат метода до завершения обработки видео. Я не уверен, что это правильный способ сделать это (кажется странным), но это работает. Что-то вроде этого:

        var finishedWriting = false
        videoWriter.finishWriting {
            NSLog("DEBUG:::The videoWriter finished writing.")
            if videoWriter.status == .completed {
                NSLog("DEBUG:::The videoWriter status is completed")

                let fileManager = FileManager.default
                if fileManager.fileExists(atPath: self.videoOutputFullFileName!) {
                    NSLog("DEBUG:::The file: \(self.videoOutputFullFileName ?? "") has been saved in documents folder, and is ready to be moved to camera roll")

                    let sharedFileURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.xxx.com")
                    guard let documentsPath = sharedFileURL?.path else {
                        NSLog("ERROR:::No shared file URL path")
                        finishedWriting = true
                        return
                    }
                    let finalFilename = documentsPath + "/test_capture_video.mp4"

                    //Check whether file exists
                    if fileManager.fileExists(atPath: finalFilename) {
                        NSLog("WARN:::The file: \(finalFilename) exists, will delete the existing file")
                        do {
                            try fileManager.removeItem(atPath: finalFilename)
                        } catch let error as NSError {
                            NSLog("WARN:::Cannot delete existing file: \(finalFilename), error: \(error.debugDescription)")
                        }
                    } else {
                        NSLog("DEBUG:::The file \(self.videoOutputFullFileName!) doesn't exist")
                    }

                    do {
                        try fileManager.copyItem(at: URL(fileURLWithPath: self.videoOutputFullFileName!), to: URL(fileURLWithPath: finalFilename))
                    }
                    catch let error as NSError {
                        NSLog("ERROR:::\(error.debugDescription)")
                    }

                    PHPhotoLibrary.shared().performChanges({
                        PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: "xxx")
                        PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: finalFilename))
                    }) { completed, error in
                        if completed {
                            NSLog("Video \(self.videoOutputFullFileName ?? "") has been moved to camera roll")
                        }

                        if error != nil {
                            NSLog("ERROR:::Cannot move the video \(self.videoOutputFullFileName ?? "") to camera roll, error: \(error!.localizedDescription)")
                        }

                        finishedWriting = true
                    }

                } else {
                    NSLog("ERROR:::The file: \(self.videoOutputFullFileName ?? "") doesn't exist, so can't move this file camera roll")
                    finishedWriting = true
                }
            } else {
                NSLog("WARN:::The videoWriter status is not completed, status: \(videoWriter.status)")
                finishedWriting = true
            }
        }

        while finishedWriting == false {
    //          NSLog("DEBUG:::Waiting to finish writing...")
        }

Я думаю, что вы должны также позвонитьextensionContext.completeRequest в какой-то момент, но мой работает нормально без негопожимание плечами.

 McDonal_1106 февр. 2019 г., 10:00
Я сделал по твоему коду. Вы можете мне помочь?
 Marty07 февр. 2019 г., 18:40
Это должно быть на самом деле с помощьюDispatchGroup вместо цикла пока. Вы блокируете возврат метода до тех пор, пока не будет достигнут этот обработчик завершения?
 McDonal_1106 февр. 2019 г., 10:00
Я пытаюсь сохранить записанное видео в Camera Roll. Но это не работает. У меня есть содержание в этом записанном URL. Я проверил это путем преобразования в данные. Теперь я звоню,PHPhotoLibrary.shared (). PerformChanges ({ , затем обработчик завершения, ни успех, ни неудача не запускаются. Можете ли вы помочь мне в этом. ??

Ваш ответ на вопрос