iosiphoneavassetwriterreplaykitrpscreenrecorder

ReplayKit saving video fails first try with mic


Scenario 1:

  1. Fire up AVAssetWriter with Audio / Video writer input.
  2. Use RPScreenRecorder to start recording with no microphone and process the sample buffers.
  3. File writes out fine to Photos on first try.

Scenario 2:

  1. Fire up AVAssetWriter with Audio / Video writer input.
  2. Use RPScreenRecorder to start recording with microphone enabled and process the sample buffers.
  3. File writes fails to write out on the first try.

    UserInfo={NSLocalizedRecoverySuggestion=Try saving again., NSLocalizedDescription=Cannot Save, NSUnderlyingError=0x1c464f3c0 {Error Domain=NSOSStatusErrorDomain Code=-12412 "(null)"}} 2017-10-26 23:25:16.896673-0400 [2135:771655] Status FAILS!: 3 Error Domain=AVFoundationErrorDomain Code=-11823 "Cannot Save"

2nd try works fine.

What am I doing wrong?


Solution

  • Added retry logic to circumvent the issue. Not the greatest solution but it works.

    [self.screenRecorder startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
            if(CMSampleBufferDataIsReady(sampleBuffer) == false || self.assetWriter == nil)
            {
                return;
            }
    
            if (self.assetWriter.status == AVAssetWriterStatusFailed) {
                NSLog(@"AVWriter Failed!");
                return;
            }
    
            if (CMSampleBufferDataIsReady(sampleBuffer)) {
                if(self.assetWriter.status == AVAssetWriterStatusWriting) {
                    if (bufferType == RPSampleBufferTypeVideo) {
                        if (!self.startedSession) {
    
                            dispatch_async(dispatch_get_main_queue(), ^{
                                _startDate = [NSDate date];
                                _recordingTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateRecordingTime) userInfo:nil repeats:YES];
    
                                // Disable the idle timer while recording
                                [UIApplication sharedApplication].idleTimerDisabled = YES;
                            });
    
                            CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
                            [self.assetWriter startSessionAtSourceTime:pts];
                            self.startedSession = YES;
                            NSLog(@"MP4Writer: started session in appendVideoSample");
                        }
    
                        if (CMTimeCompare(kCMTimeInvalid, self.firstVideoFrameTime) == 0) {
                            self.firstVideoFrameTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
                        }
    
                        if (self.assetWriterVideoInput.readyForMoreMediaData) {
                            @try {
                                [self.assetWriterVideoInput appendSampleBuffer:sampleBuffer];
                            }
                            @catch(NSException *expection) {
                                NSLog(@"Missed Video Buffer: %@", self.assetWriter.error);
                            }
                        }
                    }                
    
                    if (bufferType == RPSampleBufferTypeAudioMic) {
                        if (CMTimeCompare(kCMTimeInvalid, self.firstVideoFrameTime) == 0 ||
                            CMTimeCompare(self.firstVideoFrameTime, CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) == 1) {
                            return;
                        }
    
                        if (!self.startedSession) {
                            CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
                            [self.assetWriter startSessionAtSourceTime:pts];
                            self.startedSession = YES;
                            NSLog(@"MP4Writer: started session in appendAudioSample");
                        }
    
                        if (self.assetWriterAudioInput.isReadyForMoreMediaData) {
                            @try {
                                [self.assetWriterAudioInput appendSampleBuffer:sampleBuffer];
                            }
                            @catch(NSException *expection) {
                                NSLog(@"Missed Audio Buffer: %@", self.assetWriter.error);
                            }
                        }
                    }
                }
    
            }
        } completionHandler:^(NSError * _Nullable error) {
            if (!error) {
                NSLog(@"Recording started successfully.");
            }
        }];