Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / media / base / android / java / src / org / chromium / media / VideoCaptureTango.java
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.
4
5 package org.chromium.media;
6
7 import android.content.Context;
8 import android.graphics.ImageFormat;
9 import android.hardware.Camera;
10 import android.util.Log;
11
12 import java.nio.ByteBuffer;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15
16 /**
17  * This class extends the VideoCapture base class for manipulating a Tango
18  * device's cameras, namely the associated Depth (z-Buffer), Fisheye and back-
19  * facing 4MP video capture devices. These devices are differentiated via the
20  * |id| passed on constructor, according to the index correspondence in
21  * |s_CAM_PARAMS|; all devices |id| are index 0 towards the parent VideoCapture.
22  **/
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)};
34
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;
47
48     private static final byte CHROMA_ZERO_LEVEL = 127;
49     private static final String TAG = "VideoCaptureTango";
50
51     static int numberOfCameras() {
52         return CAM_PARAMS.length;
53     }
54
55     static VideoCaptureFactory.CamParams getCamParams(int index) {
56         if (index >= CAM_PARAMS.length) return null;
57         return CAM_PARAMS[index];
58     }
59
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));
68       }
69       return formatList.toArray(new CaptureFormat[formatList.size()]);
70     }
71
72     VideoCaptureTango(Context context,
73                       int id,
74                       long nativeVideoCaptureDeviceAndroid) {
75         // All Tango cameras are like the back facing one for the generic
76         // VideoCapture code.
77         super(context, 0, nativeVideoCaptureDeviceAndroid);
78         mTangoCameraId = id;
79     }
80
81     @Override
82     protected void setCaptureParameters(
83             int width,
84             int height,
85             int frameRate,
86             Camera.Parameters cameraParameters) {
87       mCaptureFormat = new CaptureFormat(CAM_PARAMS[mTangoCameraId].mWidth,
88                                          CAM_PARAMS[mTangoCameraId].mHeight,
89                                          frameRate,
90                                          ImageFormat.YV12);
91       // Connect Tango SuperFrame mode. Available sf modes are "all",
92       // "big-rgb", "small-rgb", "depth", "ir".
93         cameraParameters.set("sf-mode", "all");
94     }
95
96     @Override
97     protected void allocateBuffers() {
98         mFrameBuffer = ByteBuffer.allocateDirect(
99                 mCaptureFormat.mWidth * mCaptureFormat.mHeight * 3 / 2);
100         // Prefill Chroma to their zero-equivalent for the cameras that only
101         // provide Luma component.
102         Arrays.fill(mFrameBuffer.array(), CHROMA_ZERO_LEVEL);
103     }
104
105     @Override
106     protected void setPreviewCallback(Camera.PreviewCallback cb) {
107         mCamera.setPreviewCallback(cb);
108     }
109
110     @Override
111     public void onPreviewFrame(byte[] data, Camera camera) {
112         mPreviewBufferLock.lock();
113         try {
114             if (!mIsRunning) {
115                 return;
116             }
117             if (data.length == SF_WIDTH * SF_FULL_HEIGHT) {
118                 int rotation = getDeviceOrientation();
119                 if (rotation != mDeviceOrientation) {
120                     mDeviceOrientation = rotation;
121                 }
122                 if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
123                     rotation = 360 - rotation;
124                 }
125                 rotation = (mCameraOrientation + rotation) % 360;
126
127                 if (mTangoCameraId == DEPTH_CAMERA_ID) {
128                     int sizeY = SF_WIDTH * SF_LINES_DEPTH;
129                     int startY =
130                         SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE +
131                                     SF_LINES_RESERVED);
132                     // Depth is composed of 16b samples in which only 12b are
133                     // used. Throw away lowest 4 resolution bits. Android
134                     // platforms are big endian, LSB in lowest address. In this
135                     // case Chroma components are unused. No need to write them
136                     // explicitly since they're filled to 128 on creation.
137                     byte depthsample;
138                     for (int j = startY; j < startY + 2 * sizeY; j += 2) {
139                         depthsample = (byte)((data[j + 1] << 4) |
140                                              ((data[j] & 0xF0) >> 4));
141                         mFrameBuffer.put(depthsample);
142                     }
143                     for (int j = 0;
144                          j < mCaptureFormat.mWidth * mCaptureFormat.mHeight -
145                                  sizeY;
146                          ++j)
147                       mFrameBuffer.put((byte)0);
148                 } else if (mTangoCameraId == FISHEYE_CAMERA_ID) {
149                     int sizeY = SF_WIDTH * SF_LINES_FISHEYE;
150                     int startY = SF_WIDTH * SF_LINES_HEADER;
151                     // Fisheye is black and white so Chroma components are
152                     // unused. No need to write them explicitly since they're
153                     // filled to 128 on creation.
154                     ByteBuffer.wrap(data, startY, sizeY)
155                               .get(mFrameBuffer.array(), 0, sizeY);
156                 } else if (mTangoCameraId == FOURMP_CAMERA_ID) {
157                     int startY =
158                         SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE +
159                                     SF_LINES_RESERVED + SF_LINES_DEPTH_PADDED);
160                     int sizeY = SF_WIDTH * SF_LINES_BIGIMAGE;
161
162                     // The spec is completely inaccurate on the location, sizes
163                     // and format of these channels.
164                     int startU = SF_WIDTH * (SF_HEIGHT + SF_OFFSET_4MP_CHROMA);
165                     int sizeU = SF_WIDTH * SF_LINES_BIGIMAGE / 4;
166                     int startV = (SF_WIDTH * SF_HEIGHT * 5 / 4) +
167                             SF_WIDTH * SF_OFFSET_4MP_CHROMA;
168                     int sizeV = SF_WIDTH * SF_LINES_BIGIMAGE / 4;
169
170                     // Equivalent to the following |for| loop but much faster:
171                     // for (int i = START; i < START + SIZE; ++i)
172                     //     mFrameBuffer.put(data[i]);
173                     ByteBuffer.wrap(data, startY, sizeY)
174                               .get(mFrameBuffer.array(), 0, sizeY);
175                     ByteBuffer.wrap(data, startU, sizeU)
176                               .get(mFrameBuffer.array(), sizeY, sizeU);
177                     ByteBuffer.wrap(data, startV, sizeV)
178                               .get(mFrameBuffer.array(), sizeY + sizeU, sizeV);
179                 } else {
180                     Log.e(TAG, "Unknown camera, #id: " + mTangoCameraId);
181                     return;
182                 }
183                 mFrameBuffer.rewind();  // Important!
184                 nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid,
185                                        mFrameBuffer.array(),
186                                        mFrameBuffer.capacity(),
187                                        rotation);
188             }
189         } finally {
190             mPreviewBufferLock.unlock();
191         }
192     }
193 }