Home | History | Annotate | Download | only in formats
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.cts.verifier.camera.formats;
     17 
     18 import com.android.cts.verifier.PassFailButtons;
     19 import com.android.cts.verifier.R;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Color;
     25 import android.graphics.ColorMatrix;
     26 import android.graphics.ColorMatrixColorFilter;
     27 import android.graphics.ImageFormat;
     28 import android.graphics.Matrix;
     29 import android.graphics.SurfaceTexture;
     30 import android.hardware.Camera;
     31 import android.os.AsyncTask;
     32 import android.os.Bundle;
     33 import android.os.Handler;
     34 import android.util.Log;
     35 import android.util.SparseArray;
     36 import android.view.View;
     37 import android.view.TextureView;
     38 import android.widget.AdapterView;
     39 import android.widget.ArrayAdapter;
     40 import android.widget.ImageView;
     41 import android.widget.Spinner;
     42 
     43 import java.io.IOException;
     44 import java.lang.InterruptedException;
     45 import java.lang.Math;
     46 import java.lang.Thread;
     47 import java.util.ArrayList;
     48 import java.util.Comparator;
     49 import java.util.List;
     50 import java.util.TreeSet;
     51 
     52 /**
     53  * Tests for manual verification of the CDD-required camera output formats
     54  * for preview callbacks
     55  */
     56 public class CameraFormatsActivity extends PassFailButtons.Activity
     57         implements TextureView.SurfaceTextureListener, Camera.PreviewCallback {
     58 
     59     private static final String TAG = "CameraFormats";
     60 
     61     private TextureView mPreviewView;
     62     private SurfaceTexture mPreviewTexture;
     63     private int mPreviewTexWidth;
     64     private int mPreviewTexHeight;
     65 
     66     private ImageView mFormatView;
     67 
     68     private Spinner mCameraSpinner;
     69     private Spinner mFormatSpinner;
     70     private Spinner mResolutionSpinner;
     71 
     72     private int mCurrentCameraId = -1;
     73     private Camera mCamera;
     74 
     75     private List<Camera.Size> mPreviewSizes;
     76     private Camera.Size mNextPreviewSize;
     77     private Camera.Size mPreviewSize;
     78     private List<Integer> mPreviewFormats;
     79     private int mNextPreviewFormat;
     80     private int mPreviewFormat;
     81     private SparseArray<String> mPreviewFormatNames;
     82 
     83     private ColorMatrixColorFilter mYuv2RgbFilter;
     84 
     85     private Bitmap mCallbackBitmap;
     86     private int[] mRgbData;
     87     private int mRgbWidth;
     88     private int mRgbHeight;
     89 
     90     private static final int STATE_OFF = 0;
     91     private static final int STATE_PREVIEW = 1;
     92     private static final int STATE_NO_CALLBACKS = 2;
     93     private int mState = STATE_OFF;
     94     private boolean mProcessInProgress = false;
     95     private boolean mProcessingFirstFrame = false;
     96 
     97     private TreeSet<String> mTestedCombinations = new TreeSet<String>();
     98     private TreeSet<String> mUntestedCombinations = new TreeSet<String>();
     99 
    100     @Override
    101     public void onCreate(Bundle savedInstanceState) {
    102         super.onCreate(savedInstanceState);
    103 
    104         setContentView(R.layout.cf_main);
    105         setPassFailButtonClickListeners();
    106         setInfoResources(R.string.camera_format, R.string.cf_info, -1);
    107 
    108         mPreviewView = (TextureView) findViewById(R.id.preview_view);
    109         mFormatView = (ImageView) findViewById(R.id.format_view);
    110 
    111         mPreviewView.setSurfaceTextureListener(this);
    112 
    113         int numCameras = Camera.getNumberOfCameras();
    114         String[] cameraNames = new String[numCameras];
    115         for (int i = 0; i < numCameras; i++) {
    116             cameraNames[i] = "Camera " + i;
    117             mUntestedCombinations.add("All combinations for Camera " + i + "\n");
    118         }
    119         mCameraSpinner = (Spinner) findViewById(R.id.cameras_selection);
    120         mCameraSpinner.setAdapter(
    121             new ArrayAdapter<String>(
    122                 this, R.layout.cf_format_list_item, cameraNames));
    123         mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
    124 
    125         mFormatSpinner = (Spinner) findViewById(R.id.format_selection);
    126         mFormatSpinner.setOnItemSelectedListener(mFormatSelectedListener);
    127 
    128         mResolutionSpinner = (Spinner) findViewById(R.id.resolution_selection);
    129         mResolutionSpinner.setOnItemSelectedListener(mResolutionSelectedListener);
    130 
    131         // Must be kept in sync with android.graphics.ImageFormat manually
    132         mPreviewFormatNames = new SparseArray(7);
    133         mPreviewFormatNames.append(ImageFormat.JPEG, "JPEG");
    134         mPreviewFormatNames.append(ImageFormat.NV16, "NV16");
    135         mPreviewFormatNames.append(ImageFormat.NV21, "NV21");
    136         mPreviewFormatNames.append(ImageFormat.RGB_565, "RGB_565");
    137         mPreviewFormatNames.append(ImageFormat.UNKNOWN, "UNKNOWN");
    138         mPreviewFormatNames.append(ImageFormat.YUY2, "YUY2");
    139         mPreviewFormatNames.append(ImageFormat.YV12, "YV12");
    140 
    141         // Need YUV->RGB conversion in many cases
    142 
    143         ColorMatrix y2r = new ColorMatrix();
    144         y2r.setYUV2RGB();
    145         float[] yuvOffset = new float[] {
    146             1.f, 0.f, 0.f, 0.f, 0.f,
    147             0.f, 1.f, 0.f, 0.f, -128.f,
    148             0.f, 0.f, 1.f, 0.f, -128.f,
    149             0.f, 0.f, 0.f, 1.f, 0.f
    150         };
    151 
    152         ColorMatrix yOffset = new ColorMatrix(yuvOffset);
    153 
    154         ColorMatrix yTotal = new ColorMatrix();
    155         yTotal.setConcat(y2r, yOffset);
    156 
    157         mYuv2RgbFilter = new ColorMatrixColorFilter(yTotal);
    158     }
    159 
    160     @Override
    161     public void onResume() {
    162         super.onResume();
    163 
    164         setUpCamera(mCameraSpinner.getSelectedItemPosition());
    165     }
    166 
    167     @Override
    168     public void onPause() {
    169         super.onPause();
    170 
    171         shutdownCamera();
    172     }
    173 
    174     @Override
    175     public String getTestDetails() {
    176         StringBuilder reportBuilder = new StringBuilder();
    177         reportBuilder.append("Tested combinations:\n");
    178         for (String combination: mTestedCombinations) {
    179             reportBuilder.append(combination);
    180         }
    181         reportBuilder.append("Untested combinations:\n");
    182         for (String combination: mUntestedCombinations) {
    183             reportBuilder.append(combination);
    184         }
    185         return reportBuilder.toString();
    186     }
    187 
    188 
    189     public void onSurfaceTextureAvailable(SurfaceTexture surface,
    190             int width, int height) {
    191         mPreviewTexture = surface;
    192         mPreviewTexWidth = width;
    193         mPreviewTexHeight = height;
    194         if (mCamera != null) {
    195             startPreview();
    196         }
    197     }
    198 
    199     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    200         // Ignored, Camera does all the work for us
    201     }
    202 
    203     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    204         return true;
    205     }
    206 
    207     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    208         // Invoked every time there's a new Camera preview frame
    209     }
    210 
    211     private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
    212             new AdapterView.OnItemSelectedListener() {
    213                 public void onItemSelected(AdapterView<?> parent,
    214                         View view, int pos, long id) {
    215                     if (mCurrentCameraId != pos) {
    216                         setUpCamera(pos);
    217                     }
    218                 }
    219 
    220                 public void onNothingSelected(AdapterView parent) {
    221 
    222                 }
    223 
    224             };
    225 
    226     private AdapterView.OnItemSelectedListener mResolutionSelectedListener =
    227             new AdapterView.OnItemSelectedListener() {
    228                 public void onItemSelected(AdapterView<?> parent,
    229                         View view, int position, long id) {
    230                     if (mPreviewSizes.get(position) != mPreviewSize) {
    231                         mNextPreviewSize = mPreviewSizes.get(position);
    232                         startPreview();
    233                     }
    234                 }
    235 
    236                 public void onNothingSelected(AdapterView parent) {
    237 
    238                 }
    239 
    240             };
    241 
    242 
    243     private AdapterView.OnItemSelectedListener mFormatSelectedListener =
    244             new AdapterView.OnItemSelectedListener() {
    245                 public void onItemSelected(AdapterView<?> parent,
    246                         View view, int position, long id) {
    247                     if (mPreviewFormats.get(position) != mNextPreviewFormat) {
    248                         mNextPreviewFormat = mPreviewFormats.get(position);
    249                         startPreview();
    250                     }
    251                 }
    252 
    253                 public void onNothingSelected(AdapterView parent) {
    254 
    255                 }
    256 
    257             };
    258 
    259 
    260 
    261     private void setUpCamera(int id) {
    262         shutdownCamera();
    263 
    264         mCurrentCameraId = id;
    265         mCamera = Camera.open(id);
    266         Camera.Parameters p = mCamera.getParameters();
    267 
    268         // Get preview resolutions
    269 
    270         List<Camera.Size> unsortedSizes = p.getSupportedPreviewSizes();
    271 
    272         class SizeCompare implements Comparator<Camera.Size> {
    273             public int compare(Camera.Size lhs, Camera.Size rhs) {
    274                 if (lhs.width < rhs.width) return -1;
    275                 if (lhs.width > rhs.width) return 1;
    276                 if (lhs.height < rhs.height) return -1;
    277                 if (lhs.height > rhs.height) return 1;
    278                 return 0;
    279             }
    280         };
    281 
    282         SizeCompare s = new SizeCompare();
    283         TreeSet<Camera.Size> sortedResolutions = new TreeSet<Camera.Size>(s);
    284         sortedResolutions.addAll(unsortedSizes);
    285 
    286         mPreviewSizes = new ArrayList<Camera.Size>(sortedResolutions);
    287 
    288         String[] availableSizeNames = new String[mPreviewSizes.size()];
    289         for (int i = 0; i < mPreviewSizes.size(); i++) {
    290             availableSizeNames[i] =
    291                     Integer.toString(mPreviewSizes.get(i).width) + " x " +
    292                     Integer.toString(mPreviewSizes.get(i).height);
    293         }
    294         mResolutionSpinner.setAdapter(
    295             new ArrayAdapter<String>(
    296                 this, R.layout.cf_format_list_item, availableSizeNames));
    297 
    298         // Get preview formats
    299 
    300         mPreviewFormats =  p.getSupportedPreviewFormats();
    301 
    302         String[] availableFormatNames = new String[mPreviewFormats.size()];
    303         for (int i = 0; i < mPreviewFormats.size(); i++) {
    304             availableFormatNames[i] =
    305                     mPreviewFormatNames.get(mPreviewFormats.get(i));
    306         }
    307         mFormatSpinner.setAdapter(
    308             new ArrayAdapter<String>(
    309                 this, R.layout.cf_format_list_item, availableFormatNames));
    310 
    311         // Update untested entries
    312 
    313         mUntestedCombinations.remove("All combinations for Camera " + id + "\n");
    314         for (Camera.Size previewSize: mPreviewSizes) {
    315             for (int previewFormat: mPreviewFormats) {
    316                 String combination = "Camera " + id + ", "
    317                         + previewSize.width + "x" + previewSize.height
    318                         + ", " + mPreviewFormatNames.get(previewFormat)
    319                         + "\n";
    320                 if (!mTestedCombinations.contains(combination)) {
    321                     mUntestedCombinations.add(combination);
    322                 }
    323             }
    324         }
    325 
    326         // Set initial values
    327 
    328         mNextPreviewSize = mPreviewSizes.get(0);
    329         mResolutionSpinner.setSelection(0);
    330 
    331         mNextPreviewFormat = mPreviewFormats.get(0);
    332         mFormatSpinner.setSelection(0);
    333 
    334         if (mPreviewTexture != null) {
    335             startPreview();
    336         }
    337     }
    338 
    339     private void shutdownCamera() {
    340         if (mCamera != null) {
    341             mCamera.setPreviewCallback(null);
    342             mCamera.stopPreview();
    343             mCamera.release();
    344             mCamera = null;
    345             mState = STATE_OFF;
    346         }
    347     }
    348 
    349     private void startPreview() {
    350         if (mState != STATE_OFF) {
    351             // Stop for a while to drain callbacks
    352             mCamera.setPreviewCallback(null);
    353             mCamera.stopPreview();
    354             mState = STATE_OFF;
    355             Handler h = new Handler();
    356             Runnable mDelayedPreview = new Runnable() {
    357                 public void run() {
    358                     startPreview();
    359                 }
    360             };
    361             h.postDelayed(mDelayedPreview, 300);
    362             return;
    363         }
    364         mState = STATE_PREVIEW;
    365 
    366         Matrix transform = new Matrix();
    367         float widthRatio = mNextPreviewSize.width / (float)mPreviewTexWidth;
    368         float heightRatio = mNextPreviewSize.height / (float)mPreviewTexHeight;
    369 
    370         transform.setScale(1, heightRatio/widthRatio);
    371         transform.postTranslate(0,
    372                 mPreviewTexHeight * (1 - heightRatio/widthRatio)/2);
    373 
    374         mPreviewView.setTransform(transform);
    375 
    376         mPreviewFormat = mNextPreviewFormat;
    377         mPreviewSize   = mNextPreviewSize;
    378 
    379         Camera.Parameters p = mCamera.getParameters();
    380         p.setPreviewFormat(mPreviewFormat);
    381         p.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    382         mCamera.setParameters(p);
    383 
    384         mCamera.setPreviewCallback(this);
    385         switch (mPreviewFormat) {
    386             case ImageFormat.NV16:
    387             case ImageFormat.NV21:
    388             case ImageFormat.YUY2:
    389             case ImageFormat.YV12:
    390                 mFormatView.setColorFilter(mYuv2RgbFilter);
    391                 break;
    392             default:
    393                 mFormatView.setColorFilter(null);
    394                 break;
    395         }
    396 
    397         // Filter out currently untestable formats
    398         switch (mPreviewFormat) {
    399             case ImageFormat.NV16:
    400             case ImageFormat.RGB_565:
    401             case ImageFormat.UNKNOWN:
    402             case ImageFormat.JPEG:
    403                 AlertDialog.Builder builder =
    404                         new AlertDialog.Builder(CameraFormatsActivity.this);
    405                 builder.setMessage("Unsupported format " +
    406                         mPreviewFormatNames.get(mPreviewFormat) +
    407                         "; consider this combination as pass. ")
    408                         .setTitle("Missing test" )
    409                         .setNeutralButton("Back", null);
    410                 builder.show();
    411                 mState = STATE_NO_CALLBACKS;
    412                 mCamera.setPreviewCallback(null);
    413                 break;
    414             default:
    415                 // supported
    416                 break;
    417         }
    418 
    419         mProcessingFirstFrame = true;
    420         try {
    421             mCamera.setPreviewTexture(mPreviewTexture);
    422             mCamera.startPreview();
    423         } catch (IOException ioe) {
    424             // Something bad happened
    425             Log.e(TAG, "Unable to start up preview");
    426         }
    427     }
    428 
    429     private class ProcessPreviewDataTask extends AsyncTask<byte[], Void, Boolean> {
    430         protected Boolean doInBackground(byte[]... datas) {
    431             byte[] data = datas[0];
    432             try {
    433                 if (mRgbData == null ||
    434                         mPreviewSize.width != mRgbWidth ||
    435                         mPreviewSize.height != mRgbHeight) {
    436 
    437                     mRgbData = new int[mPreviewSize.width * mPreviewSize.height * 4];
    438                     mRgbWidth = mPreviewSize.width;
    439                     mRgbHeight = mPreviewSize.height;
    440                 }
    441                 switch(mPreviewFormat) {
    442                     case ImageFormat.NV21:
    443                         convertFromNV21(data, mRgbData);
    444                         break;
    445                     case ImageFormat.YV12:
    446                         convertFromYV12(data, mRgbData);
    447                         break;
    448                     case ImageFormat.YUY2:
    449                         convertFromYUY2(data, mRgbData);
    450                         break;
    451                     case ImageFormat.NV16:
    452                     case ImageFormat.RGB_565:
    453                     case ImageFormat.UNKNOWN:
    454                     case ImageFormat.JPEG:
    455                     default:
    456                         convertFromUnknown(data, mRgbData);
    457                         break;
    458                 }
    459 
    460                 if (mCallbackBitmap == null ||
    461                         mRgbWidth != mCallbackBitmap.getWidth() ||
    462                         mRgbHeight != mCallbackBitmap.getHeight() ) {
    463                     mCallbackBitmap =
    464                             Bitmap.createBitmap(
    465                                 mRgbWidth, mRgbHeight,
    466                                 Bitmap.Config.ARGB_8888);
    467                 }
    468                 mCallbackBitmap.setPixels(mRgbData, 0, mRgbWidth,
    469                         0, 0, mRgbWidth, mRgbHeight);
    470             } catch (OutOfMemoryError o) {
    471                 Log.e(TAG, "Out of memory trying to process preview data");
    472                 return false;
    473             }
    474             return true;
    475         }
    476 
    477         protected void onPostExecute(Boolean result) {
    478             if (result) {
    479                 mFormatView.setImageBitmap(mCallbackBitmap);
    480                 if (mProcessingFirstFrame) {
    481                     mProcessingFirstFrame = false;
    482                     String combination = "Camera " + mCurrentCameraId + ", "
    483                             + mPreviewSize.width + "x" + mPreviewSize.height
    484                             + ", " + mPreviewFormatNames.get(mPreviewFormat)
    485                             + "\n";
    486                     mUntestedCombinations.remove(combination);
    487                     mTestedCombinations.add(combination);
    488                 }
    489             }
    490             mProcessInProgress = false;
    491         }
    492 
    493     }
    494 
    495     public void onPreviewFrame(byte[] data, Camera camera) {
    496         if (mProcessInProgress || mState != STATE_PREVIEW) return;
    497 
    498         int expectedBytes;
    499         switch (mPreviewFormat) {
    500             case ImageFormat.YV12:
    501                 // YV12 may have stride != width.
    502                 int w = mPreviewSize.width;
    503                 int h = mPreviewSize.height;
    504                 int yStride = (int)Math.ceil(w / 16.0) * 16;
    505                 int uvStride = (int)Math.ceil(yStride / 2 / 16.0) * 16;
    506                 int ySize = yStride * h;
    507                 int uvSize = uvStride * h / 2;
    508                 expectedBytes = ySize + uvSize * 2;
    509                 break;
    510             case ImageFormat.NV21:
    511             case ImageFormat.YUY2:
    512             default:
    513                 expectedBytes = mPreviewSize.width * mPreviewSize.height *
    514                         ImageFormat.getBitsPerPixel(mPreviewFormat) / 8;
    515                 break;
    516         }
    517         if (expectedBytes != data.length) {
    518             AlertDialog.Builder builder =
    519                     new AlertDialog.Builder(CameraFormatsActivity.this);
    520             builder.setMessage("Mismatched size of buffer! Expected " +
    521                     expectedBytes + ", but got " +
    522                     data.length + " bytes instead!")
    523                     .setTitle("Error trying to use format "
    524                             + mPreviewFormatNames.get(mPreviewFormat))
    525                     .setNeutralButton("Back", null);
    526 
    527             builder.show();
    528 
    529             mState = STATE_NO_CALLBACKS;
    530             mCamera.setPreviewCallback(null);
    531             return;
    532         }
    533 
    534         mProcessInProgress = true;
    535         new ProcessPreviewDataTask().execute(data);
    536     }
    537 
    538     private void convertFromUnknown(byte[] data, int[] rgbData) {
    539         int w = mPreviewSize.width;
    540         int h = mPreviewSize.height;
    541         // RGBA output
    542         int index = 0;
    543         for (int y = 0; y < h; y++) {
    544             int rgbIndex = y * w;
    545             for (int x = 0; x < mPreviewSize.width/3; x++) {
    546                 int r = data[index + 0] & 0xFF;
    547                 int g = data[index + 1] & 0xFF;
    548                 int b = data[index + 2] & 0xFF;
    549                 rgbData[rgbIndex] = Color.rgb(r,g,b);
    550                 rgbIndex += 1;
    551                 index += 3;
    552             }
    553         }
    554     }
    555 
    556     // NV21 is a semi-planar 4:2:0 format, in the order YVU, which means we have:
    557     // a W x H-size 1-byte-per-pixel Y plane, then
    558     // a W/2 x H/2-size 2-byte-per-pixel plane, where each pixel has V then U.
    559     private void convertFromNV21(byte[] data, int rgbData[]) {
    560         int w = mPreviewSize.width;
    561         int h = mPreviewSize.height;
    562         // RGBA output
    563         int rgbIndex = 0;
    564         int yIndex = 0;
    565         int uvRowIndex = w*h;
    566         int uvRowInc = 0;
    567         for (int y = 0; y < h; y++) {
    568             int uvInc = 0;
    569             int vIndex = uvRowIndex;
    570             int uIndex = uvRowIndex + 1;
    571 
    572             uvRowIndex += uvRowInc * w;
    573             uvRowInc = (uvRowInc + 1) & 0x1;
    574 
    575             for (int x = 0; x < w; x++) {
    576                 int yv = data[yIndex] & 0xFF;
    577                 int uv = data[uIndex] & 0xFF;
    578                 int vv = data[vIndex] & 0xFF;
    579                 rgbData[rgbIndex] =
    580                         Color.rgb(yv, uv, vv);
    581 
    582                 rgbIndex += 1;
    583                 yIndex += 1;
    584                 uIndex += uvInc;
    585                 vIndex += uvInc;
    586                 uvInc = (uvInc + 2) & 0x2;
    587             }
    588         }
    589     }
    590 
    591     // YV12 is a planar 4:2:0 format, in the order YVU, which means we have:
    592     // a W x H-size 1-byte-per-pixel Y plane, then
    593     // a W/2 x H/2-size 1-byte-per-pixel V plane, then
    594     // a W/2 x H/2-size 1-byte-per-pixel U plane
    595     // The stride may not be equal to width, since it has to be a multiple of
    596     // 16 pixels for both the Y and UV planes.
    597     private void convertFromYV12(byte[] data, int rgbData[]) {
    598         int w = mPreviewSize.width;
    599         int h = mPreviewSize.height;
    600         // RGBA output
    601         int rgbIndex = 0;
    602         int yStride = (int)Math.ceil(w / 16.0) * 16;
    603         int uvStride = (int)Math.ceil(yStride/2/16.0) * 16;
    604         int ySize = yStride * h;
    605         int uvSize = uvStride * h / 2;
    606 
    607         int uRowIndex = ySize + uvSize;
    608         int vRowIndex = ySize;
    609 
    610         int uv_w = w/2;
    611         for (int y = 0; y < h; y++) {
    612             int yIndex = yStride * y;
    613             int uIndex = uRowIndex;
    614             int vIndex = vRowIndex;
    615 
    616             if ( (y & 0x1) == 1) {
    617                 uRowIndex += uvStride;
    618                 vRowIndex += uvStride;
    619             }
    620 
    621             int uv = 0, vv = 0;
    622             for (int x = 0; x < w; x++) {
    623                 if ( (x & 0x1)  == 0) {
    624                     uv = data[uIndex] & 0xFF;
    625                     vv = data[vIndex] & 0xFF;
    626                     uIndex++;
    627                     vIndex++;
    628                 }
    629                 int yv = data[yIndex] & 0xFF;
    630                 rgbData[rgbIndex] =
    631                         Color.rgb(yv, uv, vv);
    632 
    633                 rgbIndex += 1;
    634                 yIndex += 1;
    635             }
    636         }
    637     }
    638 
    639     // YUY2 is an interleaved 4:2:2 format: YU,YV,YU,YV
    640     private void convertFromYUY2(byte[] data, int[] rgbData) {
    641         int w = mPreviewSize.width;
    642         int h = mPreviewSize.height;
    643         // RGBA output
    644         int yIndex = 0;
    645         int uIndex = 1;
    646         int vIndex = 3;
    647         int rgbIndex = 0;
    648         for (int y = 0; y < h; y++) {
    649             for (int x = 0; x < w; x++) {
    650                 int yv = data[yIndex] & 0xFF;
    651                 int uv = data[uIndex] & 0xFF;
    652                 int vv = data[vIndex] & 0xFF;
    653                 rgbData[rgbIndex] = Color.rgb(yv,uv,vv);
    654                 rgbIndex += 1;
    655                 yIndex += 2;
    656                 if ( (x & 0x1) == 1 ) {
    657                     uIndex += 4;
    658                     vIndex += 4;
    659                 }
    660             }
    661         }
    662     }
    663 
    664 }