iOS - AVassetWriter выводит файл размером 0 и не выдает ошибок - PullRequest
0 голосов
/ 15 января 2019

Я все еще очень новичок в swift (и программировании), и я пытаюсь вывести CVPixelbuffer, который я получаю из ARFrame, в видео в реальном времени (без AR-элементов сверху).

Я настроил AVAssetWriter и Input, и в каждом кадре я пытаюсь добавить CVPixelbuffer (преобразованный в CMSampleBuffer).

Код создает файл размером 0 байт и не выдает никаких ошибок. Я не могу понять, что не так, и документация довольно загадочна для моего уровня.

Я почти уверен, что метка времени для каждого кадра неверна. Но я не могу понять, нужно ли каждому простому кадру время или общее время с начала видео.

Тогда есть размер и формат cvPixelBuffer. Я также не могу понять, нужно ли мне сообщать AVAssetwriter, каков размер и формат поступающих данных, или он сам это выясняет.

Спасибо за ваше время.

class VideoHandler
{
    var videoWriter: AVAssetWriter?
    var videoWriterInput: AVAssetWriterInput?

    var videoURL = URL(fileURLWithPath: "")
    let videoSize = CGSize(width: 1280, height: 720)

    func start()
    {
        videoURL = getDocumentsDirectory().appendingPathComponent("Temp.mov")

        do {
            try FileManager.default.removeItem(at: videoURL)
        } catch {}

        do {
            try videoWriter = AVAssetWriter(outputURL: videoURL, fileType: AVFileType.mov)
        } catch let error as NSError {
            print(error)
            videoWriter = nil
        }

        let videoSettings: [String : AnyObject] = [
            AVVideoCodecKey  : AVVideoCodecType.h264 as AnyObject,
            AVVideoWidthKey  : videoSize.width as AnyObject,
            AVVideoHeightKey : videoSize.height as AnyObject,
            //        AVVideoCompressionPropertiesKey : [
            //          AVVideoAverageBitRateKey : NSInteger(1000000),
            //          AVVideoMaxKeyFrameIntervalKey : NSInteger(16),
            //          AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel
            //        ]
            ]

        videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
        videoWriterInput?.expectsMediaDataInRealTime = true

        //TODO: Force unwrapping... so dangerous but don't know any other way.

        if videoWriter!.canAdd(videoWriterInput!)
        {
            videoWriter!.add(videoWriterInput!)
        }
        else
        {
            print("Can't add videoWriterInput")
        }

        if videoWriter?.startWriting() ?? false
        {
            videoWriter?.startSession(atSourceTime: CMTime.zero)
        }

        print("Video writing started.")
    }


    func addFrame(buffer: CVPixelBuffer, timeStamp: Double)
    {
        if videoWriterInput?.isReadyForMoreMediaData ?? false
        {
            videoWriterInput?.append(getCMSampleBuffer(buffer: buffer, timeStamp: timeStamp))
        }
    }

    func getCMSampleBuffer(buffer: CVPixelBuffer, timeStamp: Double) -> CMSampleBuffer
    {
        // Don't know yet how to set the time and timescale stuff properly.

        var info = CMSampleTimingInfo()
//        info.presentationTimeStamp = CMTime.zero
        info.presentationTimeStamp = CMTime(seconds: timeStamp, preferredTimescale: 1)
//        info.duration = CMTime.invalid
//        info.decodeTimeStamp = CMTime.invalid

        var formatDesc: CMFormatDescription? = nil

        CMVideoFormatDescriptionCreateForImageBuffer(
            allocator: kCFAllocatorDefault,
            imageBuffer: buffer,
            formatDescriptionOut: &formatDesc)

        var sampleBuffer: CMSampleBuffer? = nil

        CMSampleBufferCreateReadyWithImageBuffer(allocator: kCFAllocatorDefault,
                                                 imageBuffer: buffer,
                                                 formatDescription: formatDesc!,
                                                 sampleTiming: &info,
                                                 sampleBufferOut: &sampleBuffer);

        return sampleBuffer!
    }


    func stop()
    {
        videoWriter?.finishWriting
        {
            print("Video writing finished.")
        }
        videoWriter = nil
    }

    func getDocumentsDirectory() -> URL
    {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }

}
...