I am using AVAssetWriter to write video frames from ARSession using delegate.
func session(_ session: ARSession, didUpdate frame: ARFrame)
See below the code used to write images.
How can I set custom frame rate like 24, 30 or 60 etc as per our needs.
In output settings the value given for AVVideoExpectedSourceFrameRateKey is 30. But what ever value we given for it, Always getting 'Frame Rate' as 60 when check with VLC player -> media information -> Codec Details
What changes should I make to set desired frame rate? Thanks in Advance.
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
}
Reply from Apple support:
if you want to actually change the frame rate of the movie with AVAssetWriter, then you would have to correctly set the timestamps for each individual video frame as you write them out.
One way to do this is with an AVAssetWriterInputPixelBufferAdaptor object. For example, you use an AVAssetWriterInputPixelBufferAdaptor to append video samples packaged as CVPixelBuffer objects to a single AVAssetWriterInput object.
The initialization code looks something like this:
...
Then, to specify a certain playback frame rate (15 fps, for example) for the new movie, the timestamps (represented as CMTime structures) should be specified as 1/15 of a second apart. Here's a code snippet:
Another alternative is to use CMSampleBufferCreateCopyWithNewTiming to retime the buffers, then pass them to AVAssetWriter. Here's a rough outline:
//////////
I tried with 'CMSampleBufferCreateCopyWithNewTiming'. FPS is properly set. But got a slowmotion ouput. Following is my code.