aboutsummaryrefslogtreecommitdiff
path: root/apps/TestingCamera/src/com/android/testingcamera/CallbackProcessor.java
blob: 4b3887879ecd6955fd4c3869115a5d459d35e66f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package com.android.testingcamera;

import android.content.res.Resources;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.Matrix4f;
import android.renderscript.RenderScript;
import android.renderscript.Script;
import android.renderscript.ScriptGroup;
import android.renderscript.ScriptIntrinsicColorMatrix;
import android.renderscript.Type;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;

/**
 *  Process preview callback data for display.
 *  This is done by constructing a two-step Renderscript group,
 *  the first of which converts from various YUV formats to 8bpp YUV, and
 *  the second of which converts from YUV to RGB.
 *
 *  The processing is done in a background thread, and the result is produced
 *  into an Allocation that's backed by a SurfaceView
 */
class CallbackProcessor {
    private SurfaceView mCallbackView;
    private Surface mCallbackSurface;

    private Object mTaskLock = new Object();

    private RenderScript mRS;
    private Allocation mAllocationIn;
    private Allocation mAllocationOut;
    private ScriptGroup mConverter;

    private int mWidth;
    private int mHeight;
    private int mFormat;

    private boolean mDone = false;
    private boolean mTaskInProgress = false;

    static final private int kStopTimeout = 2000; // ms

    private static final String TAG = "CallbackProcessor";

    public CallbackProcessor(int width, int height, int format,
            Resources res, SurfaceView callbackView,
            int viewWidth, int viewHeight,
            RenderScript rs) {
        mWidth = width;
        mHeight = height;
        mFormat = format;
        mRS = rs;
        mCallbackView = callbackView;

        int inputSize = TestingCamera.getCallbackBufferSize(mWidth, mHeight,
                mFormat);
        mAllocationIn = Allocation.createSized(mRS, Element.U8(mRS), inputSize);

        Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
        tb.setX(viewWidth);
        tb.setY(viewHeight);
        Type outType = tb.create();

        mAllocationOut = Allocation.createTyped(mRS, outType,
                Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);

        ScriptC_callback swizzleScript = new ScriptC_callback(mRS);
        swizzleScript.bind_yuv_in(mAllocationIn);
        swizzleScript.invoke_init_convert(mWidth, mHeight,
            mFormat, viewWidth, viewHeight);
        Script.KernelID swizzleId;

        switch (mFormat) {
        case ImageFormat.NV21:
            swizzleId = swizzleScript.getKernelID_convert_semiplanar();
            break;
        case ImageFormat.YV12:
            swizzleId = swizzleScript.getKernelID_convert_planar();
            break;
        case ImageFormat.YUY2:
            swizzleId = swizzleScript.getKernelID_convert_interleaved();
            break;
        case ImageFormat.UNKNOWN:
        default:
            swizzleId = swizzleScript.getKernelID_convert_unknown();
        }

        ScriptGroup.Builder b = new ScriptGroup.Builder(rs);
        b.addKernel(swizzleId);
        mConverter = b.create();

        mConverter.setOutput(swizzleId, mAllocationOut);
    }

    public boolean stop() {
        synchronized(mTaskLock) {
            mDone = true;
            long startTime = SystemClock.elapsedRealtime();
            while (mTaskInProgress) {
                try {
                    mTaskLock.wait(kStopTimeout);
                } catch (InterruptedException e) {
                    // ignored, keep waiting
                }
                long endTime = SystemClock.elapsedRealtime();
                if (endTime - startTime > kStopTimeout) {
                    return false;
                }
            }
        }
        mAllocationOut.setSurface(null);
        return true;
    }

    public void displayCallback(byte[] data) {
        synchronized(mTaskLock) {
            if (mTaskInProgress || mDone) return;
            mTaskInProgress = true;
        }
        if (mCallbackSurface == null) {
            mCallbackView.getHolder().setFormat(PixelFormat.RGBA_8888);
            mCallbackSurface = mCallbackView.getHolder().getSurface();
            if (mCallbackSurface == null) return;
            mAllocationOut.setSurface(mCallbackSurface);
        }
        new ProcessCallbackTask().execute(data);
    }

    private class ProcessCallbackTask extends AsyncTask<byte[], Void, Boolean> {

        @Override
        protected Boolean doInBackground(byte[]... datas) {
            byte[] data = datas[0];

            mAllocationIn.copyFrom(data);
            mConverter.execute();
            mAllocationOut.ioSend();

            synchronized(mTaskLock) {
                mTaskInProgress = false;
                mTaskLock.notify();
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
        }
    }

}