iosavfoundationavmutablecompositionavassetavassetreader

Exporting with AVAssetReaderVideoCompositionOutput goes partway and then exits with AVFoundationErrorDomain Code=-11800


I've built an AVMutableComposition and VideoComposition from AVAssets and am able to play it. I am also able to export it using AVAssetExportSession, however AVAssetExportSession does not offer much control of the settings, so I'm exporting it using AVAssetReader/AVAssetWriter, but unfortunately I'm getting an error I don't understand and an only partially written output file.

Here is the code I have so far. I've left out the writer and as much other stuff as possible (including some error checking) that I think is irrelevant to make the code easier to read because it's a lot. Note that I haven't yet dealt with the audio track -- I'm trying to do this one step at a time, but maybe that's my problem?

The variable asset is the AVMutableComposition.

// ------- reader
_assetReader = [AVAssetReader assetReaderWithAsset:asset error:error];

_assetReader.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
_duration = asset.duration;

// --- video reader
NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];

NSDictionary *videoOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];

_assetReaderOutput = [AVAssetReaderVideoCompositionOutput assetReaderVideoCompositionOutputWithVideoTracks:videoTracks
                                                                                             videoSettings:videoOptions];
((AVAssetReaderVideoCompositionOutput *)_assetReaderOutput).videoComposition = composition;
[_assetReader addOutput:_assetReaderOutput];


// -- leaving out the export settings and construction
//    of the assetWriter since that doesn't seem to be the issue


// -- here's the actual export:

[self.assetWriter startWriting];
[self.assetWriter startSessionAtSourceTime:kCMTimeZero];
[self.assetReader startReading];


CMSampleBufferRef buffer;


while ( [self.assetReader status]==AVAssetReaderStatusReading )
{
    if(![self.assetWriterInput isReadyForMoreMediaData])
        continue;

    buffer = [self.assetReaderOutput copyNextSampleBuffer];


    NSLog(@"READING");

    if(buffer)
        [self.assetWriterInput appendSampleBuffer:buffer];

    NSLog(@"WRITTING...");


}
if( self.assetReader.status == AVAssetReaderStatusFailed ) {
    NSLog( @"%@", self.assetReader.error ); //<--------- I get a problem here
}


//Finish the session:
[self.assetWriterInput markAsFinished];
[self.assetWriter finishWriting];
NSLog(@"Write Ended");

The problem is the loop exits after a short time and I get this output:

Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x17f1dfa0 {NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x191617f0 "The operation couldn’t be completed. (OSStatus error -308.)", NSLocalizedFailureReason=An unknown error occurred (-308)}

Solution

  • It seems I was following some bad sample code, rather than correct sample code provided by apple, which is pretty clear, if extremely verbose, about how to do this:

    https://developer.apple.com/library/Mac/DOCUMENTATION/AudioVideo/Conceptual/AVFoundationPG/Articles/05_Export.html#//apple_ref/doc/uid/TP40010188-CH9-SW2

    Although I've completely rewritten my code, I think the specific problem I was having was not calling CFRelease on my CMSampleBufferRef returned from [self.assetReaderOutput copyNextSampleBuffer]. You need to do that inside the loop.