Reputation: 217
I have the code below that captures jpeg frames at 30fps and records the video in mp4 format. I'm trying to wrap the processFrame method in a dispatch_async call so that the recording process will not lockup the video player. The problem with this is I'm getting Memory Warning level 2 and the app ultimately crashes after a few seconds. I can see that the dispatch_async method loads the queue in memory as it tries to append each frame in the recorded video output ,and at 30fps, it doesn't have enough time to process the frame and release used memory. I tried using dispatch_after to delay execution of processFrame but it doesn't help. Any ideas? Should I be doing this differently?
This method gets called at around 30 times per second.
//Process the data sent by the server and send follow-up commands if needed
-(void)processServerData:(NSData *)data{
//render the video in the UIImage control
UIImage *image =[UIImage imageWithData:data];
imageCtrl.image = image;
//record the frame in the background
dispatch_async(recordingQueue,^{[self processFrame:image];});
}
}
processFrame method
//function for processing each frame for recording
-(void) processFrame:(UIImage *) image {
if (myRecorder.frameCounter < myRecorder.maxFrames)
{
if([myRecorder.writerInput isReadyForMoreMediaData])
{
CMTime frameTime = CMTimeMake(1, myRecorder.timeScale);
CMTime lastTime=CMTimeMake(myRecorder.frameCounter, myRecorder.timeScale);
CMTime presentTime=CMTimeAdd(lastTime, frameTime);
buffer = [Recorder pixelBufferFromCGImage:image.CGImage size:myRecorder.imageSize];
if(buffer)
{
[myRecorder.adaptor appendPixelBuffer:buffer withPresentationTime:presentTime];
myRecorder.frameCounter++;
CVBufferRelease(buffer);
if (myRecorder.frameCounter==myRecorder.maxFrames)
{
[myRecorder finishSession];
myRecorder.frameCounter=0;
myRecorder.isRecording = NO;
}
}
else
{
NSLog(@"Buffer is empty");
}
}
else
{
NSLog(@"adaptor not ready frameCounter=%d ",myRecorder.frameCounter );
}
}
}
Upvotes: 3
Views: 2156
Reputation: 217
Solved! I discovered that I can use dispatch_async_semaphore to prevent the queue from being overloaded with too much request that it won't have enough time to release allocated resources.
Here's my updated code:
long success = dispatch_semaphore_wait(recordingSemaphore, DISPATCH_TIME_FOREVER);
if (success != 0 )
{
NSLog(@"Frame skipped");
}
else
{
dispatch_async(recordingQueue,^{
dispatch_semaphore_signal(recordingSemaphore);
[self processFrame:image];
});
}
The dispatch_semaphore created somewhere in my code. Here, I told the semaphore to accept up to only 50 request first and finish the processing before accepting anymore request.
dispatch_semaphore_t recordingSemaphore = dispatch_semaphore_create((long) 50); //so far stable at 50
Upvotes: 3