title: Out of Memory when using compresstojpeg on multiple YuvImage one at a time
body: <p>I am building an app that buffers <strong>N</strong> camera frames and when the user taps a button it saves the photo using all the saved frames applying an effect.</p>
  
  <p>I am saving the photo and processing the frames on an <strong>AsyncTask</strong>. When I execute it, I remove everything from the screen and leave only a <strong>TextView</strong> to display the progress of saving the photo.</p>
  
  <p>Currently the <strong>AsyncTask</strong> <code>doInBackground</code> looks like this:</p>
  
  <pre><code>protected Void doInBackground(Integer... params) 
  {
      int w = mBuffer.get(0).getWidth();
      int h = mBuffer.get(0).getHeight();
      int lineHeight = h / mBuffer.size();
      int currentHeight = 0;
  
      Log.d("output", "saving photo "+w+", "+h);
  
      for (int i = 0; i &lt; mBuffer.size(); i++)
      {
          YuvImage image = mBuffer.get(i);
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          if (!image.compressToJpeg(new Rect(0, currentHeight, w, lineHeight), 50, out))
          {
              Log.d("output", "problem converting yuv to jpg");
              break;
          }
  
          currentHeight += lineHeight;
  
          Log.d("output", "currentHeight: "+currentHeight);
  
          publishProgress((int)((i / (float)mBuffer.size()) * 100));
          try
          {
              out.flush();
              out.close();
              out = null;
          }
          catch(Exception e)
          {
  
          }
  
          System.gc();
  
          try
          {
              Thread.sleep(200);
          }
          catch(Exception e)
          {
  
          }
  
          if (isCancelled())
          {
              break;
          }
      }
      return null;
  }
  </code></pre>
  
  <p>I've already removed a lot of not-so-important parts of code for this issue and this is only the part that is raising <strong>OutOfMemory</strong> issue. Basically what I'm doing there is getting a line from the image and compressing it to JPEG. But it only manages to compress the first image. When it goes to the second one, it raises an <strong>OutOfMemory</strong> exception.</p>
  
  <p>The <code>System.GC()</code>, <code>Thread.sleep()</code>, <code>out.flush()</code>, <code>out.close()</code>, are all unsuccessful attemps to fix the problem.</p>
  
  <p>The currently size of mBuffer is 5, initially it was 32. From the DDMS Heap debug, its Heap Size is 30MB and it is allocating 9MB. Apparently there is a lot of room to grow. If I remove the <code>compressToJpeg()</code> the <strong>AsyncTask</strong> completes just fine. </p>
  
  <p>Does anyone have a solution for this problem? Should I try my own YUV -> JPEG converter?</p>
  
  <p><strong>EDIT with most recent code:</strong></p>
  
  <pre><code>protected Void doInBackground(Integer... params) 
  {
      int lineHeight = mHeight / mBufferSize;
      int currentHeight = 0;
  
      Log.d("output", "saving photo: "+mWidth+", "+mHeight);
      File outputPath = new File(Environment.getExternalStorageDirectory().getPath() + "/camerafluid-output");
      outputPath.mkdirs();
      for (int i = 0; i &lt; mBufferSize; i++)
      {
          File imageFile = new File(mCachePath, "image-"+0);
          File outputFile = new File(outputPath, "image-"+0);
  
          if (!imageFile.exists())
          {
              Log.d("output", "image "+i+" not found on cache directory");
              continue;
          }
  
          try
          {
              int size = (int)imageFile.length();
              byte[] imageBytes = new byte[size];
              BufferedInputStream buf = new BufferedInputStream(new FileInputStream(imageFile));
              buf.read(imageBytes, 0, size);
              buf.close();
  
              YuvImage image = new YuvImage(imageBytes, ImageFormat.NV21, mWidth, mHeight, null);
              imageBytes = null;
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              if (!image.compressToJpeg(new Rect(0, currentHeight, mWidth, lineHeight), 50, out))
              {
                  Log.d("output", "problem converting yuv to jpg");
                  break;
              }
  
              FileOutputStream s = new FileOutputStream(outputFile);
              s.write(out.toByteArray());
              s.flush();
              s.close();
              s = null;
  
              currentHeight += lineHeight;
              Log.d("output", "currentHeight: "+currentHeight);
  
              publishProgress((int)((i / (float)mBufferSize) * 100));
              System.gc();
          }
          catch(Exception e)
          {
  
          }
  
          if (isCancelled())
          {
              break;
          }
      }
      return null;
  }
  </code></pre>
  
  <p>I am saving the images correctly as I can see the saved files on the cache directory, here is the Activity part which saves the buffer and executes the AsyncTask:</p>
  
  <pre><code>public void takePhoto()
  {
      Log.d("output", "taking photo");
  
      //vfranchi - save the buffer on the disk to free memory
      File cachePath = new File(getExternalCacheDir().getPath() + "/buffer");
      if (!cachePath.exists())
      {
          Log.d("output", "cache path doesnt exist");
          cachePath.mkdirs();
      }
  
      int count = 0;
      int width = mPhotoBuffer.get(0).getWidth();
      int height = mPhotoBuffer.get(0).getHeight();
      int bufferSize = mPhotoBuffer.size();
      for(YuvImage i : mPhotoBuffer)
      {
          File f = new File(cachePath, "image-"+count);
          try
          {
              FileOutputStream s = new FileOutputStream(f);
              s.write(i.getYuvData());
              s.flush();
              s.close();
              Log.d("output", "saved image "+count);
              count++;
          }
          catch(Exception e)
          {
              Log.d("output", "unable to save image "+count+"\n"+e.getLocalizedMessage());
          }
      }
  
      mPhotoBuffer = null;
      System.gc();
  
      SavePhotoTask task = new SavePhotoTask(this, cachePath, width, height, bufferSize);
      task.execute(0);
  }
  </code></pre>
  
  <p>Logcat beggining at the taking photo method:</p>
  
  <pre><code>03-30 12:48:08.534: D/output(15359): taking photo
  03-30 12:48:09.011: V/Camera-JNI(15359): setHasPreviewCallback: installed:0, manualBuffer:0
  03-30 12:48:09.011: V/Camera-JNI(15359): get_native_camera: context=0x138520, camera=0x1eea78
  03-30 12:48:09.011: V/Camera-JNI(15359): Clearing callback buffers, 0 remained
  03-30 12:48:09.011: V/Camera-JNI(15359): stopPreview
  03-30 12:48:09.011: V/Camera-JNI(15359): get_native_camera: context=0x138520, camera=0x1eea78
  03-30 12:48:15.323: D/output(15359): saved image 0
  03-30 12:48:16.628: D/output(15359): saved image 1
  03-30 12:48:17.880: D/output(15359): saved image 2
  03-30 12:48:19.073: D/output(15359): saved image 3
  03-30 12:48:20.545: D/output(15359): saved image 4
  03-30 12:48:28.089: D/output(15359): saving photo: 848, 480
  03-30 12:48:33.276: D/skia(15359): onFlyCompress
  03-30 12:48:35.020: D/output(15359): currentHeight: 96
  03-30 12:48:44.144: D/output(15359): currentHeight: 192
  03-30 12:48:52.581: D/skia(15359): onFlyCompress
  03-30 12:48:52.878: I/dalvikvm-heap(15359): Grow heap (frag case) to 14.975MB for 2095120-byte allocation
  03-30 12:48:53.112: I/dalvikvm-heap(15359): Grow heap (frag case) to 17.975MB for 4192272-byte allocation
  03-30 12:48:53.566: I/dalvikvm-heap(15359): Grow heap (frag case) to 23.975MB for 8386576-byte allocation
  03-30 12:48:54.855: I/dalvikvm-heap(15359): Grow heap (frag case) to 35.975MB for 16775184-byte allocation
  03-30 12:48:56.620: I/dalvikvm-heap(15359): Forcing collection of SoftReferences for 33552400-byte allocation
  03-30 12:48:56.644: E/dalvikvm-heap(15359): Out of memory on a 33552400-byte allocation.
  03-30 12:48:56.644: I/dalvikvm(15359): "AsyncTask #1" prio=5 tid=12 RUNNABLE
  03-30 12:48:56.644: I/dalvikvm(15359):   | group="main" sCount=0 dsCount=0 obj=0x40d71628 self=0x22eea8
  03-30 12:48:56.644: I/dalvikvm(15359):   | sysTid=15404 nice=10 sched=0/0 cgrp=bg_non_interactive handle=2056240
  03-30 12:48:56.651: I/dalvikvm(15359):   | schedstat=( 3387725830 799926751 876 ) utm=327 stm=10 core=0
  03-30 12:48:56.651: I/dalvikvm(15359):   at java.io.ByteArrayOutputStream.expand(ByteArrayOutputStream.java:~91)
  03-30 12:48:56.651: I/dalvikvm(15359):   at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:201)
  03-30 12:48:56.651: I/dalvikvm(15359):   at android.graphics.YuvImage.nativeCompressToJpeg(Native Method)
  03-30 12:48:56.667: I/dalvikvm(15359):   at android.graphics.YuvImage.compressToJpeg(YuvImage.java:141)
  03-30 12:48:56.667: I/dalvikvm(15359):   at com.vfranchi.camerafluid.SavePhotoTask.doInBackground(SavePhotoTask.java:78)
  03-30 12:48:56.667: I/dalvikvm(15359):   at com.vfranchi.camerafluid.SavePhotoTask.doInBackground(SavePhotoTask.java:1)
  03-30 12:48:56.667: I/dalvikvm(15359):   at android.os.AsyncTask$2.call(AsyncTask.java:287)
  03-30 12:48:56.667: I/dalvikvm(15359):   at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
  03-30 12:48:56.667: I/dalvikvm(15359):   at java.util.concurrent.FutureTask.run(FutureTask.java:137)
  03-30 12:48:56.667: I/dalvikvm(15359):   at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
  03-30 12:48:56.667: I/dalvikvm(15359):   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
  03-30 12:48:56.667: I/dalvikvm(15359):   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
  03-30 12:48:56.667: I/dalvikvm(15359):   at java.lang.Thread.run(Thread.java:856)
  03-30 12:48:56.683: W/System.err(15359): java.lang.OutOfMemoryError
  03-30 12:48:56.683: W/System.err(15359):    at java.io.ByteArrayOutputStream.expand(ByteArrayOutputStream.java:91)
  03-30 12:48:56.683: W/System.err(15359):    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:201)
  03-30 12:48:56.691: W/System.err(15359):    at android.graphics.YuvImage.nativeCompressToJpeg(Native Method)
  03-30 12:48:56.691: W/System.err(15359):    at android.graphics.YuvImage.compressToJpeg(YuvImage.java:141)
  03-30 12:48:56.691: W/System.err(15359):    at com.vfranchi.camerafluid.SavePhotoTask.doInBackground(SavePhotoTask.java:78)
  03-30 12:48:56.691: W/System.err(15359):    at com.vfranchi.camerafluid.SavePhotoTask.doInBackground(SavePhotoTask.java:1)
  03-30 12:48:56.691: W/System.err(15359):    at android.os.AsyncTask$2.call(AsyncTask.java:287)
  03-30 12:48:56.698: W/System.err(15359):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
  03-30 12:48:56.698: W/System.err(15359):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
  03-30 12:48:56.698: W/System.err(15359):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
  03-30 12:48:56.698: W/System.err(15359):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
  03-30 12:48:56.698: W/System.err(15359):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
  03-30 12:48:56.706: W/System.err(15359):    at java.lang.Thread.run(Thread.java:856)
  03-30 12:48:56.706: D/skia(15359): ------- write threw an exception
  03-30 12:49:15.181: W/jdwp(15359): Debugger is telling the VM to exit with code=1
  03-30 12:49:15.181: I/dalvikvm(15359): GC lifetime allocation: 5 bytes
  </code></pre>
  
  <p>The YuvImage buffer is created on the onPreviewFrame which is shown below:</p>
  
  <pre><code>public void onPreviewFrame(byte[] data, Camera camera) 
  {
      Camera.Parameters parameters = camera.getParameters();
      Camera.Size size = parameters.getPreviewSize();
  
      // Generate a YuvImage from the camera data
      int w = size.width;
      int h = size.height;
      YuvImage photoImage = new YuvImage(data, parameters.getPreviewFormat(), w, h, null);
      mPhotoBuffer.add(0, photoImage);
  
      if (mPhotoBuffer.size() &gt; mBufferSize)
      {
          mPhotoBuffer.remove(mPhotoBuffer.size() - 1);
      }
  }
  </code></pre>