1/*
2 * Copyright 2013 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
17package androidx.media.filterfw.samples.simplecamera;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.ImageFormat;
22import android.hardware.camera2.CameraAccessException;
23import android.hardware.camera2.CameraCharacteristics;
24import android.hardware.camera2.CameraDevice;
25import android.hardware.camera2.CameraCaptureSession;
26import android.hardware.camera2.CameraManager;
27import android.hardware.camera2.CaptureFailure;
28import android.hardware.camera2.CaptureRequest;
29import android.hardware.camera2.CaptureResult;
30import android.hardware.camera2.TotalCaptureResult;
31import android.os.Handler;
32import android.renderscript.Allocation;
33import android.renderscript.Element;
34import android.renderscript.RenderScript;
35import android.renderscript.ScriptIntrinsicYuvToRGB;
36import android.renderscript.Type;
37import android.util.Log;
38import android.view.Surface;
39import com.android.ex.camera2.blocking.BlockingCameraManager;
40import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
41import com.android.ex.camera2.blocking.BlockingSessionCallback;
42import androidx.media.filterfw.Filter;
43import androidx.media.filterfw.Frame;
44import androidx.media.filterfw.FrameImage2D;
45import androidx.media.filterfw.FrameType;
46import androidx.media.filterfw.FrameValue;
47import androidx.media.filterfw.MffContext;
48import androidx.media.filterfw.OutputPort;
49import androidx.media.filterfw.Signature;
50
51import java.util.ArrayList;
52import java.util.List;
53
54public class Camera2Source extends Filter implements Allocation.OnBufferAvailableListener {
55
56    private boolean mNewFrameAvailable = false;
57    private FrameType mOutputType;
58    private static final String TAG = "Camera2Source";
59    private CameraManager mCameraManager;
60    private CameraDevice mCamera;
61    private CameraCaptureSession mCameraSession;
62    private RenderScript mRS;
63    private Surface mSurface;
64    private CameraCharacteristics mProperties;
65    private CameraTestThread mLooperThread;
66    private int mHeight = 480;
67    private int mWidth = 640;
68    private Allocation mAllocationIn;
69    private ScriptIntrinsicYuvToRGB rgbConverter;
70    private Allocation mAllocationOut;
71    private Bitmap mBitmap;
72
73    private static final long SESSION_TIMEOUT_MS = 2000;
74
75    class MyCameraListener extends CameraManager.AvailabilityCallback {
76
77        @Override
78        public void onCameraAvailable(String cameraId) {
79            // TODO Auto-generated method stub
80            Log.v(TAG, "camera available to open");
81        }
82
83        @Override
84        public void onCameraUnavailable(String cameraId) {
85            // TODO Auto-generated method stub
86            Log.v(TAG, "camera unavailable to open");
87        }
88
89    }
90
91    class MyCaptureCallback extends CameraCaptureSession.CaptureCallback {
92
93        @Override
94        public void onCaptureCompleted(CameraCaptureSession camera, CaptureRequest request,
95                TotalCaptureResult result) {
96            // TODO Auto-generated method stub
97            Log.v(TAG, "in onCaptureComplete");
98
99        }
100
101        @Override
102        public void onCaptureFailed(CameraCaptureSession camera, CaptureRequest request,
103                CaptureFailure failure) {
104            // TODO Auto-generated method stub
105            Log.v(TAG, "onCaptureFailed is being called");
106        }
107
108    }
109
110    public Camera2Source(MffContext context, String name) {
111        super(context, name);
112        mOutputType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
113
114        Context ctx = context.getApplicationContext();
115        mCameraManager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);
116
117        mRS = RenderScript.create(context.getApplicationContext());
118    }
119
120    @Override
121    public Signature getSignature() {
122        return new Signature()
123                .addOutputPort("timestamp", Signature.PORT_OPTIONAL, FrameType.single(long.class))
124                .addOutputPort("video", Signature.PORT_REQUIRED, mOutputType)
125                .addOutputPort("orientation", Signature.PORT_REQUIRED,
126                        FrameType.single(float.class))
127                .disallowOtherPorts();
128    }
129
130    @Override
131    protected void onClose() {
132        Log.v(TAG, "onClose being called");
133        try {
134            mCamera.close();
135            mSurface.release();
136            mLooperThread.close();
137        } catch (Exception e) {
138            // TODO Auto-generated catch block
139            e.printStackTrace();
140        }
141    }
142
143    @Override
144    protected void onOpen() {
145        mLooperThread = new CameraTestThread();
146        Handler mHandler;
147        try {
148            mHandler = mLooperThread.start();
149        } catch (Exception e) {
150            // TODO Auto-generated catch block
151            e.printStackTrace();
152            throw new RuntimeException(e);
153        }
154
155        try {
156            String backCameraId = "0";
157            BlockingCameraManager blkManager = new BlockingCameraManager(mCameraManager);
158            mCamera = blkManager.openCamera(backCameraId, /*listener*/null, mHandler);
159        } catch (CameraAccessException e) {
160            e.printStackTrace();
161            throw new RuntimeException(e);
162        } catch (BlockingOpenException e) {
163            e.printStackTrace();
164            throw new RuntimeException(e);
165        }
166
167        Element ele = Element.createPixel(mRS, Element.DataType.UNSIGNED_8,
168                Element.DataKind.PIXEL_YUV);
169
170        rgbConverter = ScriptIntrinsicYuvToRGB.create(mRS,ele);
171        Type.Builder yuvBuilder = new Type.Builder(mRS,ele);
172
173        yuvBuilder.setYuvFormat(ImageFormat.YUV_420_888);
174        yuvBuilder.setX(mWidth);
175        yuvBuilder.setY(mHeight);
176        mAllocationIn = Allocation.createTyped(mRS, yuvBuilder.create(),
177                Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT);
178        mSurface = mAllocationIn.getSurface();
179        mAllocationIn.setOnBufferAvailableListener(this);
180        rgbConverter.setInput(mAllocationIn);
181
182        mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
183        mAllocationOut = Allocation.createFromBitmap(mRS, mBitmap);
184
185
186        Log.v(TAG, "mcamera: " + mCamera);
187
188        List<Surface> surfaces = new ArrayList<Surface>();
189        surfaces.add(mSurface);
190        CaptureRequest.Builder mCaptureRequest = null;
191        try {
192            BlockingSessionCallback blkSession = new BlockingSessionCallback();
193
194            mCamera.createCaptureSession(surfaces, blkSession, mHandler);
195            mCaptureRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
196            mCaptureRequest.addTarget(mSurface);
197
198            mCameraSession = blkSession.waitAndGetSession(SESSION_TIMEOUT_MS);
199
200        } catch (CameraAccessException e) {
201            e.printStackTrace();
202            throw new RuntimeException(e);
203        }
204
205        try {
206            mCameraSession.setRepeatingRequest(mCaptureRequest.build(), new MyCaptureCallback(),
207                    mHandler);
208        } catch (CameraAccessException e) {
209            e.printStackTrace();
210            throw new RuntimeException(e);
211        }
212        mProperties = null;
213        try {
214            mProperties = mCameraManager.getCameraCharacteristics(mCamera.getId());
215        } catch (CameraAccessException e) {
216            e.printStackTrace();
217            throw new RuntimeException(e);
218        }
219
220    }
221
222    @Override
223    protected void onProcess() {
224        Log.v(TAG, "on Process");
225        if (nextFrame()) {
226            OutputPort outPort = getConnectedOutputPort("video");
227
228            // Create a 2D frame that will hold the output
229            int[] dims = new int[] {
230                    mWidth, mHeight
231            };
232            FrameImage2D outputFrame = Frame.create(mOutputType, dims).asFrameImage2D();
233            rgbConverter.forEach(mAllocationOut);
234            mAllocationOut.copyTo(mBitmap);
235            outputFrame.setBitmap(mBitmap);
236            outPort.pushFrame(outputFrame);
237            outputFrame.release();
238
239            OutputPort orientationPort = getConnectedOutputPort("orientation");
240            FrameValue orientationFrame = orientationPort.fetchAvailableFrame(null).asFrameValue();
241
242            // FIXME: Hardcoded value because ORIENTATION returns null, Qualcomm
243            // bug
244            Integer orientation = mProperties.get(CameraCharacteristics.SENSOR_ORIENTATION);
245            float temp;
246            if (orientation != null) {
247                temp = orientation.floatValue();
248            } else {
249                temp = 90.0f;
250            }
251            orientationFrame.setValue(temp);
252            orientationPort.pushFrame(orientationFrame);
253        }
254    }
255
256    private synchronized boolean nextFrame() {
257        boolean frameAvailable = mNewFrameAvailable;
258        if (frameAvailable) {
259            mNewFrameAvailable = false;
260        } else {
261            enterSleepState();
262        }
263        return frameAvailable;
264    }
265
266    public void onBufferAvailable(Allocation a) {
267        Log.v(TAG, "on Buffer Available");
268        a.ioReceive();
269        synchronized (this) {
270            mNewFrameAvailable = true;
271        }
272        wakeUp();
273    }
274
275
276}
277