I'm trying to record CVPixelbuffer in realtime.
But I can't append buffer because assetWriterInput.isReadyForMoreMediaData
always return false.
Can someone explain why this value always false? Thank's.
class VideoRecorder{
static var shared = VideoRecorder()
var avAssetWriter: AVAssetWriter?
var Adaptor: AVAssetWriterInputPixelBufferAdaptor?
var Settings: RecorderSetting?
struct RecorderSetting{
var videoSetting: [String : Any]
var Path: URL
}
func makeVideoSettings(width: Int, height: Int, BitRate: Double) -> [String : Any]{
let VideoCompressionProperties = [
AVVideoAverageBitRateKey: Double(width * height) * BitRate
]
let videoSettings:[String : Any] = [
AVVideoCodecKey: AVVideoCodecType.hevc,
AVVideoWidthKey: width,
AVVideoHeightKey: height,
AVVideoCompressionPropertiesKey: VideoCompressionProperties
]
return videoSettings
}
func makePath(FileName: String) -> URL{
return URL(fileURLWithPath:
NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/\(FileName).mp4")
}
func setup(width: Int, height: Int,
BitRate: Double, FileName: String){
let setting = makeVideoSettings(width: width, height: height, BitRate: BitRate)
let Path = makePath(FileName: FileName)
Settings = RecorderSetting(videoSetting: setting, Path: Path)
}
func StartSession(FirstFrame: CVPixelBuffer) throws{
let attribute: [String : Any] = [
kCVPixelBufferPixelFormatTypeKey as String: CVPixelBufferGetPixelFormatType(FirstFrame),
kCVPixelBufferWidthKey as String: CVPixelBufferGetWidth(FirstFrame),
kCVPixelBufferHeightKey as String: CVPixelBufferGetHeight(FirstFrame)
]
if (Settings == nil){throw "Settings invalid"}
let writerInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: Settings!.videoSetting)
Adaptor =
AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput, sourcePixelBufferAttributes: attribute)
Adaptor?.assetWriterInput.expectsMediaDataInRealTime = true
do{
avAssetWriter = try AVAssetWriter(url: Settings!.Path, fileType: AVFileType.mp4)
if (avAssetWriter!.canAdd(writerInput)){
let StartTime = Date().timeIntervalSince1970.toCMTime()
avAssetWriter?.startWriting()
avAssetWriter?.startSession(atSourceTime: StartTime)
try? WriteBuffer(Buffer: FirstFrame, time: StartTime)
}else{
throw "Add AVWriterInput Error"
}
}catch{
throw "Initializing Error"
}
}
func StopSession(){
if(Adaptor?.assetWriterInput.isReadyForMoreMediaData == false){return}
Adaptor?.assetWriterInput.markAsFinished()
avAssetWriter?.finishWriting(completionHandler: {
if let outputPath = self.Settings?.Path{
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputPath)
}) { saved, error in
if saved {
try? FileManager().removeItem(at: outputPath)
print("Saved ")
/*let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
// fetchResult is your latest video PHAsset
// To fetch latest image replace .video with .image*/
}
}
}
})
}
func WriteBuffer(Buffer: CVPixelBuffer, time: CMTime) throws{
if(self.Adaptor != nil){
if (self.Adaptor!.assetWriterInput.isReadyForMoreMediaData){
let whetherPixelBufferAppendedtoAdaptor = self.Adaptor!.append(Buffer, withPresentationTime: time)
if(!whetherPixelBufferAppendedtoAdaptor){
print(avAssetWriter?.error as Any)
}
}else{
throw "Writer Input is not Ready"
}
}else{
throw "PixelBufferAdaptor invalild"
}
}
}
Find the problem.
This method AVAssetWriterInput.canAdd()
only check can asset writer add the input,
You need to call .add()
to add asset input before you start writing.