AVAssetWriterInput ReadyForMoreMediaData always false

246 Views Asked by At

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"
    }

}

}

1

There are 1 best solutions below

0
Shimamura Uzuki On BEST ANSWER

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.