1package com.android.testingcamera; 2 3import android.content.res.Resources; 4import android.graphics.ImageFormat; 5import android.graphics.PixelFormat; 6import android.os.AsyncTask; 7import android.os.SystemClock; 8import android.renderscript.Allocation; 9import android.renderscript.Element; 10import android.renderscript.Matrix4f; 11import android.renderscript.RenderScript; 12import android.renderscript.Script; 13import android.renderscript.ScriptGroup; 14import android.renderscript.ScriptIntrinsicColorMatrix; 15import android.renderscript.Type; 16import android.util.Log; 17import android.view.Surface; 18import android.view.SurfaceView; 19 20/** 21 * Process preview callback data for display. 22 * This is done by constructing a two-step Renderscript group, 23 * the first of which converts from various YUV formats to 8bpp YUV, and 24 * the second of which converts from YUV to RGB. 25 * 26 * The processing is done in a background thread, and the result is produced 27 * into an Allocation that's backed by a SurfaceView 28 */ 29class CallbackProcessor { 30 private SurfaceView mCallbackView; 31 private Surface mCallbackSurface; 32 33 private Object mTaskLock = new Object(); 34 35 private RenderScript mRS; 36 private Allocation mAllocationIn; 37 private Allocation mAllocationOut; 38 private ScriptGroup mConverter; 39 40 private int mWidth; 41 private int mHeight; 42 private int mFormat; 43 44 private boolean mDone = false; 45 private boolean mTaskInProgress = false; 46 47 /** 48 * JFIF standard YCbCr <-> RGB conversion matrix, 49 * column-major order. 50 */ 51 static final private float[] kJpegYuv2Rgb = new float[] { 52 1.f, 1.f, 1.f, 0.f, 53 0.f, -0.34414f, 1.772f, 0.f, 54 1.402f, -0.71414f, 0.f, 0.f, 55 -0.701f, 0.529f, -0.886f, 1.0f 56 }; 57 58 static final private int kStopTimeout = 2000; // ms 59 60 private static final String TAG = "CallbackProcessor"; 61 62 public CallbackProcessor(int width, int height, int format, 63 Resources res, SurfaceView callbackView, 64 int viewWidth, int viewHeight, 65 RenderScript rs) { 66 mWidth = width; 67 mHeight = height; 68 mFormat = format; 69 mRS = rs; 70 mCallbackView = callbackView; 71 72 int inputSize = TestingCamera.getCallbackBufferSize(mWidth, mHeight, 73 mFormat); 74 mAllocationIn = Allocation.createSized(mRS, Element.U8(mRS), inputSize); 75 76 Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS)); 77 tb.setX(viewWidth); 78 tb.setY(viewHeight); 79 Type outType = tb.create(); 80 81 mAllocationOut = Allocation.createTyped(mRS, outType, 82 Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT); 83 84 ScriptC_callback swizzleScript = 85 new ScriptC_callback(mRS, res, R.raw.callback); 86 swizzleScript.bind_yuv_in(mAllocationIn); 87 swizzleScript.invoke_init_convert(mWidth, mHeight, 88 mFormat, viewWidth, viewHeight); 89 Script.KernelID swizzleId; 90 switch (mFormat) { 91 case ImageFormat.NV21: 92 swizzleId = swizzleScript.getKernelID_convert_semiplanar(); 93 break; 94 case ImageFormat.YV12: 95 swizzleId = swizzleScript.getKernelID_convert_planar(); 96 break; 97 case ImageFormat.YUY2: 98 swizzleId = swizzleScript.getKernelID_convert_interleaved(); 99 break; 100 case ImageFormat.UNKNOWN: 101 default: 102 swizzleId = swizzleScript.getKernelID_convert_unknown(); 103 } 104 105 ScriptIntrinsicColorMatrix colorMatrix = 106 ScriptIntrinsicColorMatrix.create(rs, Element.U8_4(mRS)); 107 108 Matrix4f yuv2rgb = new Matrix4f(kJpegYuv2Rgb); 109 colorMatrix.setColorMatrix(yuv2rgb); 110 111 ScriptGroup.Builder b = new ScriptGroup.Builder(rs); 112 b.addKernel(swizzleId); 113 b.addKernel(colorMatrix.getKernelID()); 114 b.addConnection(outType, swizzleId, 115 colorMatrix.getKernelID()); 116 mConverter = b.create(); 117 118 mConverter.setOutput(colorMatrix.getKernelID(), mAllocationOut); 119 } 120 121 public boolean stop() { 122 synchronized(mTaskLock) { 123 mDone = true; 124 long startTime = SystemClock.elapsedRealtime(); 125 while (mTaskInProgress) { 126 try { 127 mTaskLock.wait(kStopTimeout); 128 } catch (InterruptedException e) { 129 // ignored, keep waiting 130 } 131 long endTime = SystemClock.elapsedRealtime(); 132 if (endTime - startTime > kStopTimeout) { 133 return false; 134 } 135 } 136 } 137 mAllocationOut.setSurface(null); 138 return true; 139 } 140 141 public void displayCallback(byte[] data) { 142 synchronized(mTaskLock) { 143 if (mTaskInProgress || mDone) return; 144 mTaskInProgress = true; 145 } 146 if (mCallbackSurface == null) { 147 mCallbackView.getHolder().setFormat(PixelFormat.RGBA_8888); 148 mCallbackSurface = mCallbackView.getHolder().getSurface(); 149 if (mCallbackSurface == null) return; 150 mAllocationOut.setSurface(mCallbackSurface); 151 } 152 new ProcessCallbackTask().execute(data); 153 } 154 155 private class ProcessCallbackTask extends AsyncTask<byte[], Void, Boolean> { 156 157 @Override 158 protected Boolean doInBackground(byte[]... datas) { 159 byte[] data = datas[0]; 160 161 mAllocationIn.copyFrom(data); 162 mConverter.execute(); 163 mAllocationOut.ioSend(); 164 165 synchronized(mTaskLock) { 166 mTaskInProgress = false; 167 mTaskLock.notify(); 168 } 169 return true; 170 } 171 172 @Override 173 protected void onPostExecute(Boolean result) { 174 } 175 } 176 177} 178