1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.media;
7 import android.content.Context;
8 import android.graphics.ImageFormat;
9 import android.util.Log;
11 import java.nio.ByteBuffer;
12 import java.util.ArrayList;
13 import java.util.Arrays;
16 * This class extends the VideoCapture base class for manipulating a Tango
17 * device's cameras, namely the associated Depth (z-Buffer), Fisheye and back-
18 * facing 4MP video capture devices. These devices are differentiated via the
19 * |id| passed on constructor, according to the index correspondence in
20 * |s_CAM_PARAMS|; all devices |id| are index 0 towards the parent VideoCapture.
22 @SuppressWarnings("deprecation")
23 public class VideoCaptureTango extends VideoCapture {
24 private ByteBuffer mFrameBuffer = null;
25 private final int mTangoCameraId;
26 // The indexes must coincide with the s_CAM_PARAMS used below.
27 private static final int DEPTH_CAMERA_ID = 0;
28 private static final int FISHEYE_CAMERA_ID = 1;
29 private static final int FOURMP_CAMERA_ID = 2;
30 private static final VideoCaptureFactory.CamParams CAM_PARAMS[] = {
31 new VideoCaptureFactory.CamParams(DEPTH_CAMERA_ID, "depth", 320, 240),
32 new VideoCaptureFactory.CamParams(FISHEYE_CAMERA_ID, "fisheye", 640, 480),
33 new VideoCaptureFactory.CamParams(FOURMP_CAMERA_ID, "4MP", 1280, 720)};
35 // SuperFrame size definitions. Note that total size is the amount of lines
36 // multiplied by 3/2 due to Chroma components following.
37 private static final int SF_WIDTH = 1280;
38 private static final int SF_HEIGHT = 1168;
39 private static final int SF_FULL_HEIGHT = SF_HEIGHT * 3 / 2;
40 private static final int SF_LINES_HEADER = 16;
41 private static final int SF_LINES_FISHEYE = 240;
42 private static final int SF_LINES_RESERVED = 80; // Spec says 96.
43 private static final int SF_LINES_DEPTH = 60;
44 private static final int SF_LINES_DEPTH_PADDED = 112; // Spec says 96.
45 private static final int SF_LINES_BIGIMAGE = 720;
46 private static final int SF_OFFSET_4MP_CHROMA = 112;
48 private static final byte CHROMA_ZERO_LEVEL = 127;
49 private static final String TAG = "VideoCaptureTango";
51 static int numberOfCameras() {
52 return CAM_PARAMS.length;
55 static VideoCaptureFactory.CamParams getCamParams(int index) {
56 if (index >= CAM_PARAMS.length) return null;
57 return CAM_PARAMS[index];
60 static CaptureFormat[] getDeviceSupportedFormats(int id) {
61 ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
62 if (id == DEPTH_CAMERA_ID) {
63 formatList.add(new CaptureFormat(320, 180, 5, ImageFormat.YV12));
64 } else if (id == FISHEYE_CAMERA_ID) {
65 formatList.add(new CaptureFormat(640, 480, 30, ImageFormat.YV12));
66 } else if (id == FOURMP_CAMERA_ID) {
67 formatList.add(new CaptureFormat(1280, 720, 20, ImageFormat.YV12));
69 return formatList.toArray(new CaptureFormat[formatList.size()]);
72 VideoCaptureTango(Context context, int id, long nativeVideoCaptureDeviceAndroid) {
73 // All Tango cameras are like the back facing one for the generic VideoCapture code.
74 super(context, 0, nativeVideoCaptureDeviceAndroid);
79 protected void setCaptureParameters(int width, int height, int frameRate,
80 android.hardware.Camera.Parameters cameraParameters) {
81 mCaptureFormat = new CaptureFormat(CAM_PARAMS[mTangoCameraId].mWidth,
82 CAM_PARAMS[mTangoCameraId].mHeight,
85 // Connect Tango SuperFrame mode. Available sf modes are "all",
86 // "big-rgb", "small-rgb", "depth", "ir".
87 cameraParameters.set("sf-mode", "all");
91 protected void allocateBuffers() {
92 mFrameBuffer = ByteBuffer.allocateDirect(
93 mCaptureFormat.mWidth * mCaptureFormat.mHeight * 3 / 2);
94 // Prefill Chroma to their zero-equivalent for the cameras that only
95 // provide Luma component.
96 Arrays.fill(mFrameBuffer.array(), CHROMA_ZERO_LEVEL);
100 protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) {
101 mCamera.setPreviewCallback(cb);
105 public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
106 mPreviewBufferLock.lock();
108 if (!mIsRunning) return;
110 if (data.length == SF_WIDTH * SF_FULL_HEIGHT) {
111 int rotation = getDeviceOrientation();
112 if (rotation != mDeviceOrientation) {
113 mDeviceOrientation = rotation;
115 if (mCameraFacing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
116 rotation = 360 - rotation;
118 rotation = (mCameraOrientation + rotation) % 360;
120 if (mTangoCameraId == DEPTH_CAMERA_ID) {
121 int sizeY = SF_WIDTH * SF_LINES_DEPTH;
123 SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE + SF_LINES_RESERVED);
124 // Depth is composed of 16b samples in which only 12b are
125 // used. Throw away lowest 4 resolution bits. Android
126 // platforms are big endian, LSB in lowest address. In this
127 // case Chroma components are unused. No need to write them
128 // explicitly since they're filled to 128 on creation.
130 for (int j = startY; j < startY + 2 * sizeY; j += 2) {
131 depthsample = (byte) ((data[j + 1] << 4) | ((data[j] & 0xF0) >> 4));
132 mFrameBuffer.put(depthsample);
134 for (int j = 0; j < mCaptureFormat.mWidth * mCaptureFormat.mHeight - sizeY;
136 mFrameBuffer.put((byte) 0);
138 } else if (mTangoCameraId == FISHEYE_CAMERA_ID) {
139 int sizeY = SF_WIDTH * SF_LINES_FISHEYE;
140 int startY = SF_WIDTH * SF_LINES_HEADER;
141 // Fisheye is black and white so Chroma components are unused. No need to write
142 // them explicitly since they're filled to 128 on creation.
143 ByteBuffer.wrap(data, startY, sizeY).get(mFrameBuffer.array(), 0, sizeY);
144 } else if (mTangoCameraId == FOURMP_CAMERA_ID) {
145 int startY = SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE +
146 SF_LINES_RESERVED + SF_LINES_DEPTH_PADDED);
147 int sizeY = SF_WIDTH * SF_LINES_BIGIMAGE;
149 // The spec is completely inaccurate on the location, sizes
150 // and format of these channels.
151 int startU = SF_WIDTH * (SF_HEIGHT + SF_OFFSET_4MP_CHROMA);
152 int sizeU = SF_WIDTH * SF_LINES_BIGIMAGE / 4;
153 int startV = (SF_WIDTH * SF_HEIGHT * 5 / 4) + SF_WIDTH * SF_OFFSET_4MP_CHROMA;
154 int sizeV = SF_WIDTH * SF_LINES_BIGIMAGE / 4;
156 // Equivalent to the following |for| loop but much faster:
157 // for (int i = START; i < START + SIZE; ++i)
158 // mFrameBuffer.put(data[i]);
159 ByteBuffer.wrap(data, startY, sizeY)
160 .get(mFrameBuffer.array(), 0, sizeY);
161 ByteBuffer.wrap(data, startU, sizeU)
162 .get(mFrameBuffer.array(), sizeY, sizeU);
163 ByteBuffer.wrap(data, startV, sizeV)
164 .get(mFrameBuffer.array(), sizeY + sizeU, sizeV);
166 Log.e(TAG, "Unknown camera, #id: " + mTangoCameraId);
169 mFrameBuffer.rewind(); // Important!
170 nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, mFrameBuffer.array(),
171 mFrameBuffer.capacity(), rotation);
174 mPreviewBufferLock.unlock();