swiftavassetwriteravassetwriterinput

AVAssetWriterInput ReadyForMoreMediaData always false


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

}

}


Solution

  • 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.