I am creating this app that is a camera. Because it is being created for iOS 9, I have to test it on an old device. In that case an iPad 3. The app works perfectly on a new iPad Pro 9.7 but fails to write video after a while on iPad 3.
What happens is, the app starts writing frames fine but suddenly fails.
I am using this method to store each frame:
- (void)writeToFileFrame:(CIImage *) finalImage
withSampleTime:(CMSampleTimingInfo)sampleTime
andSize:(CGSize)size
{
if (!_assetWriter) {
if (![self initializeAssetWriter]) {
return;
}
}
// convert CIImage to CMSampleBufferRef and save
CGRect extent = [finalImage extent];
CGFloat width = CGRectGetWidth(extent);
CGFloat height = CGRectGetHeight(extent);
if ((width == 0) && (height == 0)) return;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSDictionary dictionary], (id)kCVPixelBufferIOSurfacePropertiesKey,
@(YES), kCVPixelBufferCGImageCompatibilityKey,
@(YES), kCVPixelBufferCGBitmapContextCompatibilityKey,
kCVImageBufferYCbCrMatrix_ITU_R_601_4, kCVImageBufferYCbCrMatrixKey,
kCVImageBufferColorPrimaries_ITU_R_709_2, kCVImageBufferColorPrimariesKey, nil];
// Initialize the video input if this is not done yet
if (!_readyToWriteVideo) {
_readyToWriteVideo = [self setupAssetWriterVideoInputWithSize:size];
}
CVPixelBufferRef pixelBuffer = NULL;
CVPixelBufferCreate(kCFAllocatorSystemDefault, width, height, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef) options, &pixelBuffer);
CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
[_ciContext render:finalImage toCVPixelBuffer:pixelBuffer];
CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
CMVideoFormatDescriptionRef videoInfo = NULL;
CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, &videoInfo);
CMSampleBufferRef oBuf;
OSStatus status = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, true, NULL, NULL, videoInfo, &sampleTime, &oBuf);
CVPixelBufferRelease(pixelBuffer);
CFRelease(videoInfo);
if (status != noErr) {
NSLog(@"error creating CMSampleBufferCreateForImageBuffer");
CFRelease(oBuf);
return;
}
// Write video data to file only when all the inputs are ready
if ([self inputsReadyToWriteToFile]) {
if (_assetWriter.error) {
NSLog(@"%@",[_assetWriter.error localizedDescription]);
return;
}
[self writeSampleBuffer:oBuf ofType:AVMediaTypeVideo];
}
CFRelease(oBuf);
}
- (void)writeSampleBuffer:(CMSampleBufferRef)sampleBuffer ofType:(NSString *)mediaType
{
if ( _assetWriter.status == AVAssetWriterStatusUnknown ) {
NSLog(@"unknown state");
// If the asset writer status is unknown, implies writing hasn't started yet, hence start writing with start time as the buffer's presentation timestamp
if ([_assetWriter startWriting]) {
[_assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
} else {
// error
}
}
if ( _assetWriter.status == AVAssetWriterStatusWriting ) {
// If the asset writer status is writing, append sample buffer to its corresponding asset writer input
if (mediaType == AVMediaTypeVideo) {
if (_assetWriterVideoInput.readyForMoreMediaData) {
if (![_assetWriterVideoInput appendSampleBuffer:sampleBuffer]) {
NSLog(@"error: %@", [_assetWriter.error localizedFailureReason]); //Ⓐ
}
}
}
else if (mediaType == AVMediaTypeAudio) {
if (_assetWriterAudioInput.readyForMoreMediaData) {
if (![_assetWriterAudioInput appendSampleBuffer:sampleBuffer]) {
// error
}
}
}
}
if ( _assetWriter.status == AVAssetWriterStatusFailed ) {
NSLog(@"error");
}
}
This works fine on iPad Pro 9.7 but on iPad 3 it hits line Ⓐ, failing miserably, after writing frames correctly for about 5 seconds.
Fails with error -536870211, that is obviously an unknown error.
I have checked the apps for leaks and there is none.
Any ideas?
NOTE: I have discovered now that the problems only shows if I am writing frames from the rear camera (HD). If I switch to the front camera VGA (640x480) it works fine. So it has to do with memory allocations but I have checked that with instruments and the app is using about 12 MB of memory and this value is more or less constant. No leaks.
That error is kIOReturnNoMemory
Memory can't be allocated (0xe00002bd).Value: 0xe00002bd (-536870211)
so it sounds like you're using too much memory. You need to make sure that you're retaining as few sample buffers and their derivatives for as short a period of time as possible.
The Instruments app memory and GPU tools and Xcode's memory gauges should be helpful too.