AVAssetWriter - пропущены последние кадры - PullRequest
0 голосов
/ 14 июня 2019

Я использую AVAssetWriter для записи прямой трансляции с камеры, но я столкнулся с проблемой, когда отсутствуют последние кадры.Когда я нажимаю кнопку «Стоп», мое выходное видео пропадает примерно через 0,5 секунды.

Я думаю, это потому, что конвейер захвата видео слишком занят для последних кадров после того, как я нажму кнопку «Стоп».К вашему сведению, когда я отключаю режим стабилизации на off ИЛИ устанавливаю предустановку захвата сеанса на low или medium, у меня нет пропущенных кадров.

class ViewController: UIViewController, AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate {
    private var session: AVCaptureSession = AVCaptureSession()
    private var deviceInput: AVCaptureDeviceInput?
    private var previewLayer: AVCaptureVideoPreviewLayer?
    private var videoOutput: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput()
    private var audioOutput: AVCaptureAudioDataOutput = AVCaptureAudioDataOutput()

    private var videoDevice: AVCaptureDevice = (AVCaptureDevice.default(for: AVMediaType.video))!
    private var audioConnection: AVCaptureConnection?
    private var videoConnection: AVCaptureConnection?

    private var assetWriter: AVAssetWriter?
    private var audioInput: AVAssetWriterInput?
    private var videoInput: AVAssetWriterInput?

    private var fileManager: FileManager = FileManager()
    private var recordingURL: URL?

    private var isCameraRecording: Bool = false
    private var isRecordingSessionStarted: Bool = false

    private var recordingQueue = DispatchQueue(label: "recording.queue")


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.session.sessionPreset = AVCaptureSession.Preset.high

        self.recordingURL = URL(fileURLWithPath: "\(NSTemporaryDirectory() as String)/file.mov")
        if self.fileManager.isDeletableFile(atPath: self.recordingURL!.path) {
            _ = try? self.fileManager.removeItem(atPath: self.recordingURL!.path)
        }
        self.assetWriter = try? AVAssetWriter(outputURL: self.recordingURL!,
                                              fileType: AVFileType.mov)

        /*
        let compression = [//AVVideoAverageBitRateKey : 5000000,
                           AVVideoMaxKeyFrameIntervalKey : 1]
        */

        let videoSettings = [
            AVVideoCodecKey : AVVideoCodecType.h264,
            AVVideoWidthKey : 1080,
            AVVideoHeightKey : 1920,
            //AVVideoCompressionPropertiesKey : compression
            ] as [String : Any]

        self.videoInput = AVAssetWriterInput(mediaType: AVMediaType.video,
                                             outputSettings: videoSettings)

        self.videoInput?.expectsMediaDataInRealTime = true

        if self.assetWriter!.canAdd(self.videoInput!) {
            self.assetWriter?.add(self.videoInput!)
        }

        self.deviceInput = try? AVCaptureDeviceInput(device: self.videoDevice)

        self.previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
        self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill

        let rootLayer = self.view.layer
        self.previewLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width)

        rootLayer.insertSublayer(self.previewLayer!, at: 0)

        self.session.startRunning()

        DispatchQueue.main.async {
            self.session.beginConfiguration()

            if self.session.canAddInput(self.deviceInput!) {
                self.session.addInput(self.deviceInput!)
            }

            if self.session.canAddOutput(self.videoOutput) {
                self.session.addOutput(self.videoOutput)
            }

            if let connection = self.videoOutput.connection(with: AVMediaType.video) {
                if connection.isVideoOrientationSupported {
                    // Force recording to portrait
                    connection.videoOrientation = AVCaptureVideoOrientation.portrait
                }
            }

            self.videoConnection = self.videoOutput.connection(with: AVMediaType.video)
            if self.videoConnection?.isVideoStabilizationSupported == true {
                self.videoConnection?.preferredVideoStabilizationMode = .auto
            }
            self.session.commitConfiguration()
        }
    }

    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        if !self.isRecordingSessionStarted {
            let presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
            self.assetWriter?.startSession(atSourceTime: presentationTime)
            self.isRecordingSessionStarted = true
        }

        let description = CMSampleBufferGetFormatDescription(sampleBuffer)!

        if CMFormatDescriptionGetMediaType(description) == kCMMediaType_Audio {
            if self.audioInput!.isReadyForMoreMediaData {
                print("appendSampleBuffer audio");
                self.audioInput?.append(sampleBuffer)
            }
        } else {
            if self.videoInput!.isReadyForMoreMediaData {
                print("appendSampleBuffer video");
                if !self.videoInput!.append(sampleBuffer) {
                    print("Error writing video buffer");
                }
            }
        }
    }


    private func startRecording() {
        if self.assetWriter?.startWriting() != true {
            print("error: \(self.assetWriter?.error.debugDescription ?? "")")
        }

        self.videoOutput.setSampleBufferDelegate(self, queue: self.recordingQueue)
        self.audioOutput.setSampleBufferDelegate(self, queue: self.recordingQueue)
    }

    private func stopRecording() {
        self.videoInput?.markAsFinished()
        self.audioInput?.markAsFinished()

        self.assetWriter?.finishWriting {
            PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: self.recordingURL!)
            }) { saved, error in
                if saved {
                    let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
                    let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
                    alertController.addAction(defaultAction)

                    DispatchQueue.main.async {
                        self.present(alertController, animated: true, completion: nil)
                        self.resetAssetWriter()
                    }
                } else {
                    print(error.debugDescription)
                }
            }

            print("saved")
        }
    }
}

У вас есть идея?как решить эту проблему?

Спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...