Вопрос

I am recording video using AVCaptureMovieFileOutput. Instead of retaining the captured video for the entire recording time, however, I would like only to retain the last 2 minutes of video. In essence, I would like to create a trailing buffer of video.

I have tried to implement this by setting movieFragmentInterval equal to 15 seconds. As these 15 seconds buffered, the first 15 seconds of the MOV file would be trimmed off using this code:

//This would be called 7 seconds after the video stream started buffering.
-(void)startTrimTimer
{
    trimTimer = [NSTimer scheduledTimerWithTimeInterval:15 target:self selector:@selector(trimFlashbackBuffer) userInfo:nil repeats:YES];
}

    -(void)trimFlashbackBuffer
    {
        //make sure that there is enough video before trimming off 15 seconds
        if(trimReadyCount<3){
            trimReadyCount++;
            return;
        }

        AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/flashbackBuffer.MOV",tripDirectory]] options:nil]; 

        AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
        exportSession.outputURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/flashbackBuffer.MOV",tripDirectory]];
        exportSession.outputFileType = AVFileTypeQuickTimeMovie;
        CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(15000, 1000), CMTimeMake(120000, 1000));
        exportSession.timeRange = timeRange;

        [exportSession exportAsynchronouslyWithCompletionHandler:^{
            switch (exportSession.status) {
                case AVAssetExportSessionStatusCompleted:
                    // Custom method to import the Exported Video
                    [self loadAssetFromFile:exportSession.outputURL];
                    break;
                case AVAssetExportSessionStatusFailed:
                    //
                    NSLog(@"Failed:%@",exportSession.error);
                    break;
                case AVAssetExportSessionStatusCancelled:
                    //
                    NSLog(@"Canceled:%@",exportSession.error);
                    break;
                default:
                    break;
            }
        }];

    }

However, I am receiving the following error every time trimFlashbackBuffer is called:

Failed:Error Domain=AVFoundationErrorDomain Code=-11823 "Cannot Save" UserInfo=0x12e710 {NSLocalizedRecoverySuggestion=Try saving again., NSLocalizedDescription=Cannot Save}

Is this because the file is already being written to by AVCaptureMovieFileOutput?

How could I achieve the effect of a seamless trailing video buffer, if this method cannot work?

Thanks!

Это было полезно?

Решение

not sure that what you trying to achieve here will work, this is because like you said, you are writing the file while trying to trim it, why cant you record the video and trim it later? If you really want to just keep two minutes of videos at any time you might want to try using AVCaptureVideoDataOutput,using this you will get video frames and you can use AVAssetWriter to write it to compress and write frames to a file, check out this SO question which talks about how to do that This code to write video+audio through AVAssetWriter and AVAssetWriterInputs is not working. Why?

Другие советы

I suspect the error you are getting is because you are trying to overwrite the same file as in the export URL. The documentation says "The export will fail if you try to overwrite an existing file, or write a file outside of the application’s sandbox. If you need to overwrite an existing file, you must remove it first."

To get the last two minutes of the video, you might want to obtain it's duration first using loadValuesAsynchronouslyForKeys which is another asynchronous call. Using this duration, you can create a time range and trim the video by exporting it to a different URL.

CMTime start = CMTimeMakeWithSeconds(durationObtained - 120, 600); 
CMTime duration = CMTimeMakeWithSeconds(120, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);  
exportSession.timeRange = range;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top