Chromium Code Reviews| Index: media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java |
| diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..429c08f63c4b6c39f2ec75958593b3673d0eda8d |
| --- /dev/null |
| +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java |
| @@ -0,0 +1,270 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.media; |
| + |
| +import android.content.Context; |
| +import android.graphics.ImageFormat; |
| + |
| +import com.google.atap.tangoservice.Tango; |
| +import com.google.atap.tangoservice.TangoCameraIntrinsics; |
| +import com.google.atap.tangoservice.TangoConfig; |
| +import com.google.atap.tangoservice.TangoCoordinateFramePair; |
| +import com.google.atap.tangoservice.TangoErrorException; |
| +import com.google.atap.tangoservice.TangoEvent; |
| +import com.google.atap.tangoservice.TangoInvalidException; |
| +import com.google.atap.tangoservice.TangoOutOfDateException; |
| +import com.google.atap.tangoservice.TangoPointCloudData; |
| +import com.google.atap.tangoservice.TangoPoseData; |
| +import com.google.atap.tangoservice.TangoXyzIjData; |
| + |
| +import org.chromium.base.ContextUtils; |
| +import org.chromium.base.Log; |
| +import org.chromium.base.annotations.CalledByNative; |
| +import org.chromium.base.annotations.JNINamespace; |
| + |
| +import java.util.ArrayList; |
| + |
| +/** |
| + * This class extends the VideoCapture base class for manipulating a Tango Depth |
| + * video capture device. |
| + **/ |
| +@JNINamespace("media") |
| +@SuppressWarnings("deprecation") |
| +public class VideoCaptureTango extends VideoCapture { |
| + // Tango devices have additional depth, color and fisheye cameras. Those devices are identified |
| + // by model & device. |
| + private static final String[][] TANGO_DEVICE_LIST = { |
| + {"Project Tango Tablet Development Kit", "yellowstone"}, {"Lenovo PB2-690M", "PB2PRO"}}; |
| + |
| + private static class CamParams { |
| + final String mName; |
| + final int mCaptureApiType; |
| + final int mWidth; |
| + final int mHeight; |
| + |
| + CamParams(String name, int captureApiType, int width, int height) { |
| + mName = name; |
| + mCaptureApiType = captureApiType; |
| + mWidth = width; |
| + mHeight = height; |
| + } |
| + } |
| + |
| + private Tango mTango; |
| + private TangoCameraIntrinsics mIntrinsics; |
| + private final int mTangoCameraId; |
| + // The indexes must coincide with |CAM_PARAMS| defined below. |
| + private static final int DEPTH_CAMERA_ID = 0; |
| + private static final int FISHEYE_CAMERA_ID = 1; |
| + private static final int FOURMP_CAMERA_ID = 2; |
| + // As pointcloud is in color camera 3D space coordinates, we will project it to e.g. down-scaled |
| + // 1920x1080 fitting the depth resolution but keeping the camera frame aspect ratio, e.g. to 214 |
| + // x 120. When we project 3d pointcloud produced by depth camera of e.g. 224x172 (Lenovo Phab2 |
| + // Pro) or larger resolutions, we get continues depth frame (214x120) synchronized to color |
| + // camera frame with easy mapping color -> depth: color frame coordinates divided by scale gives |
| + // the position in the depth buffer. |
| + private static final CamParams CAM_PARAMS[] = { |
| + new CamParams("Tango depth", VideoCaptureApi.ANDROID_TANGO, 214, 120)}; |
| + |
| + private static final String TAG = "cr.mediatango"; |
| + |
| + private static boolean isTangoDevice() { |
| + for (String[] device : TANGO_DEVICE_LIST) { |
| + if (device[0].contentEquals(android.os.Build.MODEL) |
| + && device[1].contentEquals(android.os.Build.DEVICE)) { |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + // Checks if it is Tango device; returns 0 if it is not. |
| + static int getNumberOfCameras() { |
| + if (isTangoDevice()) return CAM_PARAMS.length; |
| + return 0; |
| + } |
| + |
| + private void setupTangoConfig() { |
| + TangoConfig config = mTango.getConfig(TangoConfig.CONFIG_TYPE_DEFAULT); |
| + config.putBoolean(TangoConfig.KEY_BOOLEAN_DEPTH, true); |
| + |
| + config.putInt(TangoConfig.KEY_INT_DEPTH_MODE, TangoConfig.TANGO_DEPTH_MODE_POINT_CLOUD); |
| + mTango.connect(config); |
| + assert mTangoCameraId == DEPTH_CAMERA_ID : "Only depth camera capture supported"; |
| + try { |
| + mIntrinsics = mTango.getCameraIntrinsics(TangoCameraIntrinsics.TANGO_CAMERA_COLOR); |
| + } catch (TangoErrorException e) { |
| + Log.e(TAG, "setupTangoConfig", e); |
| + } |
| + } |
| + |
| + static int getCaptureApiType(int index) { |
| + if (index < CAM_PARAMS.length) return CAM_PARAMS[index].mCaptureApiType; |
| + return VideoCaptureApi.UNKNOWN; |
| + } |
| + |
| + static String getName(int index) { |
| + if (index < CAM_PARAMS.length) return CAM_PARAMS[index].mName; |
| + return null; |
| + } |
| + |
| + static VideoCaptureFormat[] getDeviceSupportedFormats(int id) { |
| + ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); |
| + if (id == DEPTH_CAMERA_ID) { |
| + formatList.add(new VideoCaptureFormat(CAM_PARAMS[DEPTH_CAMERA_ID].mWidth, |
| + CAM_PARAMS[DEPTH_CAMERA_ID].mHeight, 5, ImageFormat.DEPTH16)); |
| + } |
| + return formatList.toArray(new VideoCaptureFormat[formatList.size()]); |
| + } |
| + |
| + VideoCaptureTango(int id, long nativeVideoCaptureDeviceAndroid) { |
| + super(0, nativeVideoCaptureDeviceAndroid); |
| + mTangoCameraId = id; |
| + } |
| + |
| + private void setTangoListeners() { |
| + final ArrayList<TangoCoordinateFramePair> framePairs = |
| + new ArrayList<TangoCoordinateFramePair>(); |
| + // Listen for new Tango data. |
| + mTango.connectListener(framePairs, new Tango.OnTangoUpdateListener() { |
| + @Override |
| + public void onPointCloudAvailable(final TangoPointCloudData pointCloudData) { |
| + if (pointCloudData.points != null) { |
| + nativeOnPointCloudAvailable(mNativeVideoCaptureDeviceAndroid, |
| + pointCloudData.points, pointCloudData.numPoints, |
| + pointCloudData.timestamp); |
| + } |
| + } |
| + |
| + @Override |
| + public void onPoseAvailable(final TangoPoseData pose) {} |
| + |
| + @Override |
| + public void onFrameAvailable(int id) {} |
| + |
| + @Override |
| + public void onXyzIjAvailable(final TangoXyzIjData d) {} |
| + |
| + @Override |
| + public void onTangoEvent(final TangoEvent event) {} |
| + }); |
| + } |
| + |
| + @Override |
| + public boolean startCapture() { |
| + if (mUseBackgroundThreadForTesting) { |
| + // Mock the data for the tests; this way tests are run on Tango and non-Tango devices. |
| + mIntrinsics = new TangoCameraIntrinsics(); |
| + mIntrinsics.width = 1920; |
| + mIntrinsics.height = 1080; |
| + mIntrinsics.cx = mIntrinsics.width / 2; |
| + mIntrinsics.cy = mIntrinsics.height / 2; |
| + mIntrinsics.fx = mIntrinsics.width; |
| + mIntrinsics.fy = mIntrinsics.width; |
| + return true; |
| + } |
| + final Context context = ContextUtils.getApplicationContext(); |
| + mTango = new Tango(context, new Runnable() { |
| + @Override |
| + public void run() { |
| + synchronized (context) { |
| + try { |
| + setupTangoConfig(); |
| + setTangoListeners(); |
| + nativeOnStarted(mNativeVideoCaptureDeviceAndroid); |
| + } catch (TangoOutOfDateException e) { |
| + Log.e(TAG, "exception_out_of_date", e); |
| + } catch (TangoErrorException e) { |
| + Log.e(TAG, "exception_tango_error", e); |
| + } catch (TangoInvalidException e) { |
|
mcasas
2017/07/15 00:12:36
I think you could bundle these three and Log() wil
|
| + Log.e(TAG, "R.string.exception_tango_invalid", e); |
| + } |
| + } |
| + } |
| + }); |
| + return true; |
| + } |
| + |
| + @Override |
| + public boolean stopCapture() { |
| + if (!mUseBackgroundThreadForTesting) mTango.disconnect(); |
| + mTango = null; |
| + return true; |
| + } |
| + |
| + @Override |
| + public boolean allocate(int width, int height, int frameRate) { |
| + mCaptureFormat = new VideoCaptureFormat(CAM_PARAMS[DEPTH_CAMERA_ID].mWidth, |
| + CAM_PARAMS[DEPTH_CAMERA_ID].mHeight, 5, ImageFormat.DEPTH16); |
| + return true; |
| + } |
| + |
| + @Override |
| + public void deallocate() {} |
| + |
| + @Override |
| + public boolean takePhoto(final long callbackId) { |
| + Log.e(TAG, "takePhoto is not supported with Tango depth camera"); |
| + return false; |
| + } |
| + |
| + @Override |
| + public void setPhotoOptions(double zoom, int focusMode, int exposureMode, double width, |
| + double height, float[] pointsOfInterest2D, boolean hasExposureCompensation, |
| + double exposureCompensation, int whiteBalanceMode, double iso, |
| + boolean hasRedEyeReduction, boolean redEyeReduction, int fillLightMode, |
| + boolean hasTorch, boolean torch, double colorTemperature) { |
| + Log.e(TAG, "setPhotoOptions is not supported with Tango depth camera"); |
| + } |
| + |
| + @Override |
| + public PhotoCapabilities getPhotoCapabilities() { |
| + PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder(); |
| + builder.setMinIso(0).setMaxIso(0).setCurrentIso(0).setStepIso(0); |
| + int width = CAM_PARAMS[DEPTH_CAMERA_ID].mWidth; |
| + int height = CAM_PARAMS[DEPTH_CAMERA_ID].mHeight; |
| + builder.setMinHeight(height).setMaxHeight(height).setStepHeight(1); |
| + builder.setMinWidth(width).setMaxWidth(width).setStepWidth(1); |
| + builder.setCurrentHeight(height).setCurrentWidth(width); |
| + builder.setMinZoom(0).setMaxZoom(0); |
| + builder.setCurrentZoom(0).setStepZoom(0); |
| + builder.setExposureMode(AndroidMeteringMode.CONTINUOUS); |
| + builder.setWhiteBalanceMode(AndroidMeteringMode.CONTINUOUS); |
| + builder.setFocusMode(AndroidMeteringMode.CONTINUOUS); |
| + return builder.build(); |
| + } |
| + |
| + @CalledByNative |
| + public double getFocalLengthX() { |
| + assert mTangoCameraId == DEPTH_CAMERA_ID : "Only depth camera capture supported"; |
| + final double scale = |
| + Math.ceil((double) mIntrinsics.width / (double) CAM_PARAMS[DEPTH_CAMERA_ID].mWidth); |
| + return mIntrinsics.fx / scale; |
| + } |
| + |
| + @CalledByNative |
| + public double getFocalLengthY() { |
| + assert mTangoCameraId == DEPTH_CAMERA_ID : "Only depth camera capture supported"; |
| + final double scale = |
| + Math.ceil((double) mIntrinsics.width / (double) CAM_PARAMS[DEPTH_CAMERA_ID].mWidth); |
| + return mIntrinsics.fy / scale; |
| + } |
| + |
| + @CalledByNative |
| + public double getPrincipalPointX() { |
| + assert mTangoCameraId == DEPTH_CAMERA_ID : "Only depth camera capture supported"; |
| + final double scale = |
| + Math.ceil((double) mIntrinsics.width / (double) CAM_PARAMS[DEPTH_CAMERA_ID].mWidth); |
| + return mIntrinsics.cx / scale; |
| + } |
| + |
| + @CalledByNative |
| + public double getPrincipalPointY() { |
| + assert mTangoCameraId == DEPTH_CAMERA_ID : "Only depth camera capture supported"; |
| + final double scale = |
| + Math.ceil((double) mIntrinsics.width / (double) CAM_PARAMS[DEPTH_CAMERA_ID].mWidth); |
| + return mIntrinsics.cy / scale; |
| + } |
| +} |