AVAssetWriter - установить пользовательскую частоту кадров

Я использую AVAssetWriter для записи видеокадров из ARSession с использованием делегата.

func session(_ session: ARSession, didUpdate frame: ARFrame)

См. ниже код, используемый для записи изображений.

Как я могу установить пользовательскую частоту кадров, например 24, 30 или 60 и т. Д., В соответствии с нашими потребностями.

В настройках вывода значение, указанное для AVVideoExpectedSourceFrameRateKey, равно 30. Но какое бы значение мы ни давали для него, всегда получая «Частота кадров» как 60 при проверке с помощью проигрывателя VLC -> информация о мультимедиа -> сведения о кодеке.

Какие изменения я должен внести, чтобы установить желаемую частоту кадров? Заранее спасибо.

func writeImage(_ image: CVPixelBuffer, thisTimestamp: TimeInterval) {

        guard let videoDirector = videoWriter else { return }

        serialQueue.async(execute: {

            let scale = CMTimeScale(NSEC_PER_SEC)

            if (!self.seenTimestamps.contains(thisTimestamp)) {

                self.seenTimestamps.append(thisTimestamp)
                let pts = CMTime(value: CMTimeValue((thisTimestamp) * Double(scale)),
                                 timescale: scale)
                var timingInfo = CMSampleTimingInfo(duration: kCMTimeInvalid,
                                                    presentationTimeStamp: pts,
                                                    decodeTimeStamp: kCMTimeInvalid)

                var vidInfo:CMVideoFormatDescription! = nil
                CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, image, &vidInfo)

                var sampleBuffer:CMSampleBuffer! = nil
                CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, image, true, nil, nil, vidInfo, &timingInfo, &sampleBuffer)

                let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!

                if self.videoWriterInput == nil {

                    let width = CVPixelBufferGetWidth(imageBuffer)
                    let height = CVPixelBufferGetHeight(imageBuffer)


                    let numPixels: Double = Double(width * height);
                    let bitsPerPixel = 11.4;
                    let bitsPerSecond = Int(numPixels * bitsPerPixel)

                    // add video input
                    let outputSettings: [String: Any] = [
                        AVVideoCodecKey : AVVideoCodecType.h264,
                        AVVideoWidthKey : width,
                        AVVideoHeightKey : height,
                        AVVideoCompressionPropertiesKey : [
                            AVVideoExpectedSourceFrameRateKey: 30,
                            AVVideoAverageBitRateKey : bitsPerSecond,
                            AVVideoMaxKeyFrameIntervalKey : 1
                        ]
                    ]
                    self.videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: outputSettings)
                    self.videoWriterInput?.expectsMediaDataInRealTime = true
                    guard let input = self.videoWriterInput else { return }

                    if videoDirector.canAdd(input) {
                        videoDirector.add(input)
                    }
                    videoDirector.startWriting()
                }

                let writable = self.canWrite()
                if writable, self.sessionAtSourceTime == nil {
                    let timeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
                    self.sessionAtSourceTime = timeStamp
                    videoDirector.startSession(atSourceTime: timeStamp)
                }

                if self.videoWriterInput?.isReadyForMoreMediaData == true {
                    let appendResult = self.videoWriterInput?.append(sampleBuffer)
                    if appendResult == false {
                        printDebug("writer status: \(videoDirector.status.rawValue)")
                        printDebug("writer error: \(videoDirector.error.debugDescription)")
                    }
                }
            }
        })
    }
    func canWrite() -> Bool {
        return isRecording && videoWriter?.status == .writing
    }

person jpulikkottil    schedule 30.01.2019    source источник
comment
В документации для AVVideoExpectedSourceFrameRateKey говорится: "Это не используется для управления частотой кадров; это предоставляется как подсказка для видеокодера». Возможно, CMSampleTimingInfo.duration необходимо установить на желаемую частоту кадров?   -  person Jonathan    schedule 23.02.2019
comment
@jpulikkottil, вы нашли решение вышеуказанной проблемы?   -  person Hardik Thakkar    schedule 14.06.2019
comment
Что мы можем сделать, так это запустить таймер и получить изображение с ARCamera. Так что если вам нужно 60 кадров в секунду, то запустите таймер с интервалом 1/60. Но не рекомендуется делать это без крайней необходимости.   -  person jpulikkottil    schedule 18.06.2019


Ответы (1)


Ответ службы поддержки Apple:

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

Один из способов сделать это — использовать объект AVAssetWriterInputPixelBufferAdaptor. Например, вы используете AVAssetWriterInputPixelBufferAdaptor для добавления образцов видео, упакованных как объекты CVPixelBuffer, к одному объекту AVAssetWriterInput.

Код инициализации выглядит примерно так:

[dictionary setObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(NSString*) kCVPixelBufferPixelFormatTypeKey];

...

AVAssetWriterInputPixelBufferAdaptor *avAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:assetWriterInput sourcePixelBufferAttributes:dictionary];

Затем, чтобы указать определенную частоту кадров воспроизведения (например, 15 кадров в секунду) для нового фильма, метки времени (представленные в виде структур CMTime) должны быть указаны с разницей в 1/15 секунды. Вот фрагмент кода:

CMTime frameTime = CMTimeMake(1, 15);
result = [avAdaptor appendPixelBuffer:buffer withPresentationTime:frameTime];

Другой альтернативой является использование CMSampleBufferCreateCopyWithNewTiming для изменения времени буферов, а затем передача их в AVAssetWriter. Вот примерный план:

CMSampleTimingInfo sampleTimingInfo = {0};
CMSampleBufferRef newBuffer = NULL;

CMSampleBufferGetSampleTimingInfo(existingSampleBuffer, 0, &sampleTimingInfo);

sampleTimingInfo.duration = CMTimeMake(1, 30) // Specify new frame rate.
sampleTimingInfo.presentationTimeStamp =          CMTimeAdd(previousPresentationTimeStamp, sampleTimingInfo.duration);
previousPresentationTimeStamp = sampleTimingInfo.presentationTimeStamp;

OSStatus status = CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, existingSampleBuffer, 1, &sampleTimingInfo, &newBuffer);

if (status == noErr) {
/* Write your newBuffer here */
}

//////////

Я пробовал с «CMSampleBufferCreateCopyWithNewTiming». FPS выставлен правильно. Но получил замедленный выход. Ниже приведен мой код.

autoreleasepool {
                        //set FPS
                        var sampleTimingInfo: CMSampleTimingInfo = CMSampleTimingInfo(duration: kCMTimeInvalid, presentationTimeStamp: CMTime(), decodeTimeStamp: kCMTimeInvalid)
                        var newBuffer: CMSampleBuffer!  = nil

                        CMSampleBufferGetSampleTimingInfo(sampleBufferMain, 0, &sampleTimingInfo);

                        sampleTimingInfo.duration = CMTimeMake(1, 15) // Specify new frame rate.
                        sampleTimingInfo.presentationTimeStamp = CMTimeAdd(self.previousPresentationTimeStamp, sampleTimingInfo.duration)
                        self.previousPresentationTimeStamp = sampleTimingInfo.presentationTimeStamp

                        let status: OSStatus = CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sampleBufferMain, 1, &sampleTimingInfo, &newBuffer);

                        if status == noErr {
                            let appendResult = self.videoWriterInput?.append(newBuffer)
                            if appendResult == false {
                                printError("writer status: \(String(describing: self.videoWriter?.status.rawValue))")
                                printError("writer error: \(self.videoWriter?.error.debugDescription ?? "")")
                            }
                        } else {
                            print("write error")
                        }
                    }
person jpulikkottil    schedule 18.06.2019