Hello all,
I've run into a rather annoying problem. I'm currently working on an app that should record a video of what happens on screen, and after some memory issues (see: http://forums.xamarin.com/discussion/13249/cvpixelbufferpool-woes), it works just fine.
BUT if I try and record another video, and not force quitting the app before doing it, I run into a EXC_BAD_ACCESS exception. It took me a while to find the issue, but it stems from using CGImageRelease.
And yes, I know, CGImages should be automatically released, but they aren't, so if I don't use CGImageRelease the app will crash with an out of memory exception. :)
So, now I have the problem of either: - run out of memory in a matter of seconds (not an option). - or only allow the user to record ONE video (not really an option either).
So, the question is, how to use CGImageRelease without getting EXC_BAD_ACCESS exceptions?
The code in question:
` private void CaptureFrame(CMTime frameTime) { try { lock(mLock) { // get image from current view UIGraphics.BeginImageContext(mFrameSize);
using(CGContext context = UIGraphics.GetCurrentContext ()) { this.Layer.RenderInContext (context);
using(UIImage image = UIGraphics.GetImageFromCurrentImageContext ()) { UIGraphics.EndImageContext ();
// get pixel buffer and fill it with the image using(CVPixelBuffer pxBuffer = mAvAdaptor.PixelBufferPool.CreatePixelBuffer ()) { pxBuffer.Lock(CVOptionFlags.None);
try { IntPtr pxdata = pxBuffer.BaseAddress;
if(pxdata != IntPtr.Zero) { using(var rgbColorSpace = CGColorSpace.CreateDeviceRGB()) { using(CGImage cgImage = image.CGImage) { using(CGBitmapContext bitmapContext = new CGBitmapContext(pxdata, cgImage.Width, cgImage.Height, 8, 4 * cgImage.Width, rgbColorSpace, CGImageAlphaInfo.NoneSkipFirst)) { if(bitmapContext != null) { bitmapContext.DrawImage(new RectangleF(0, 0, cgImage.Width, cgImage.Height), cgImage); }
bitmapContext.Dispose (); }
// remove this and we get no EXC_BAD_ACCESS exceptions // but remove this and the app runs out of memory in a matter of seconds... CGImageRelease(cgImage.Handle); } } } } finally { pxBuffer.Unlock(CVOptionFlags.None);
CVPixelBufferRelease(pxBuffer.Handle); }
// and finally append buffer to adapter if (mAvAdaptor != null && mAvAdaptor.AssetWriterInput.ReadyForMoreMediaData && pxBuffer != null) { bool result = mAvAdaptor.AppendPixelBufferWithPresentationTime (pxBuffer, frameTime); } } } } } } catch(Exception ex) { Console.WriteLine ("{0} - {1}", ex.Message, ex.StackTrace); } }`