title: How to get Android camera preview data?
body: <p>My camera app displays a camera preview on the screen and also processes it in the background.  Here is the relevant code, condensed as much as possible (e.g. no error handling or field declarations shown):</p>
  
  <pre><code>public final class CameraView extends SurfaceView implements
            SurfaceHolder.Callback, Runnable, PreviewCallback {
  
      public CameraView(Context context, AttributeSet attrs) {
          super(context, attrs);
          mHolder = getHolder();
          mHolder.addCallback(this);
          mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
  
      void openCamera() {
          // Called from parent activity after setting content view to CameraView
          mCamera = Camera.open();
          mCamera.setPreviewCallbackWithBuffer(this);
      }
  
      public void surfaceCreated(SurfaceHolder holder) {
          new Thread(this).start(); 
  
          // Set CameraView to the optimal camera preview size
  
          final Camera.Parameters params = mCamera.getParameters();
          final List&lt;Camera.Size&gt; sizes = params.getSupportedPreviewSizes();
          final int screenWidth = ((View) getParent()).getWidth();
          int minDiff = Integer.MAX_VALUE;
          Camera.Size bestSize = null;
  
          if (getResources().getConfiguration().orientation 
                  == Configuration.ORIENTATION_LANDSCAPE) {
              // Find the camera preview width that best matches the
              // width of the surface.
              for (Camera.Size size : sizes) {
                  final int diff = Math.abs(size.width - screenWidth);
                  if (diff &lt; minDiff) {
                      minDiff = diff;
                      bestSize = size;
                  }
              }
          } else {
              // Find the camera preview HEIGHT that best matches the 
              // width of the surface, since the camera preview is rotated.
              mCamera.setDisplayOrientation(90);
              for (Camera.Size size : sizes) {
                  final int diff = Math.abs(size.height - screenWidth);
                  if (Math.abs(size.height - screenWidth) &lt; minDiff) {
                      minDiff = diff;
                      bestSize = size;
                  }
              }
          }
  
          final int previewWidth = bestSize.width;
          final int previewHeight = bestSize.height;
  
          ViewGroup.LayoutParams layoutParams = getLayoutParams();
          layoutParams.height = previewHeight;
          layoutParams.width = previewWidth;
          setLayoutParams(layoutParams);
  
          params.setPreviewFormat(ImageFormat.NV21);
          mCamera.setParameters(params);
  
          int size = previewWidth * previewHeight * 
              ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
          mBuffer = new byte[size];
          mCamera.addCallbackBuffer(mBuffer);
  
          mCamera.setPreviewDisplay(mHolder);
          mCamera.startPreview();
      }
  
      public void onPreviewFrame(byte[] data, Camera camera) {
          CameraView.this.notify();
      }
  
      public void run() {
          mThreadRun = true;
          while (mThreadRun) {
              synchronized (this) {
                  this.wait();
                  processFrame(mBuffer); // convert to RGB and rotate - not shown
              }
              // Request a new frame from the camera by putting 
              // the buffer back into the queue
              mCamera.addCallbackBuffer(mBuffer);
          }
  
          mHolder.removeCallback(this);
          mCamera.stopPreview();
          mCamera.setPreviewCallback(null);
          mCamera.release();
          mCamera = null;
      }
  
      public void surfaceDestroyed(SurfaceHolder holder) {
          mThreadRun = false;
      }
  }
  </code></pre>
  
  <p>On all devices, the camera preview displays properly, and on most (emulator, Samsung Galaxy S3, etc.) the data stored in <code>mBuffer</code> is also correct (after NV21 to RGB conversion and rotation, of course).  However, <strong>a number of devices do not supply the correct data in onPreviewFrame</strong>.  I'm sure that the data is being converted to RGB correctly after it's received, so the problem appears to be in the raw data supplied to <code>mBuffer</code>.  I've noticed <a href="https://code.google.com/p/android/issues/detail?id=37655" rel="nofollow noreferrer">this bug report</a> relating to the YV12 (alias YUV420p) camera preview format, but I'm using the old default, NV21 (alias YUV420sp), which must be supported according to the <a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/source.android.com/en//compatibility/4.2/android-4.2-cdd.pdf" rel="nofollow noreferrer">compatibility standard</a> (see 7.5.3.2, bottom of page 29).</p>
  
  <p>For example, for this scene (shown here in Camera Preview on the Samsung Galaxy Tab 2):</p>
  
  <p><img src="https://i.stack.imgur.com/1Xkpu.jpg" alt="enter image description here"></p>
  
  <p>the data passed to <code>mBuffer</code> on the Tab 2 looks like:</p>
  
  <p><img src="https://i.stack.imgur.com/YNKvX.png" alt="enter image description here"></p>
  
  <p>and on the Motorola Droid 4 looks like:</p>
  
  <p><img src="https://i.stack.imgur.com/BRK26.png" alt="enter image description here"></p>
  
  <p><strong>What is the correct way to get Android camera preview data across all devices?</strong></p>
  
  <p><strong>Edit:</strong> for <code>processFrame()</code>, I used OpenCV to convert to RGB and rotate.  See <a href="https://stackoverflow.com/a/18117241/1397061">this answer</a> and <a href="https://stackoverflow.com/a/27511835/1397061">this answer</a>.</p>