Chromium Code Reviews| Index: media/capture/video/android/video_capture_device_tango_android.cc |
| diff --git a/media/capture/video/android/video_capture_device_tango_android.cc b/media/capture/video/android/video_capture_device_tango_android.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f7618d8eaa0b1c14a53d01c89f9b3a1c5bf45711 |
| --- /dev/null |
| +++ b/media/capture/video/android/video_capture_device_tango_android.cc |
| @@ -0,0 +1,189 @@ |
| +// Copyright (c) 2017 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. |
| + |
| +#include "media/capture/video/android/video_capture_device_tango_android.h" |
| + |
| +#include <stdint.h> |
| +#include <utility> |
| + |
| +#include "base/android/jni_android.h" |
| +#include "base/android/jni_array.h" |
| +#include "base/android/jni_string.h" |
| +#include "base/numerics/safe_conversions.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/threading/thread_task_runner_handle.h" |
| +#include "jni/VideoCaptureTango_jni.h" |
| + |
| +namespace media { |
| + |
| +VideoCaptureDeviceTangoAndroid::VideoCaptureDeviceTangoAndroid( |
| + const VideoCaptureDeviceDescriptor& device_descriptor) |
| + : VideoCaptureDeviceAndroid(device_descriptor), weak_ptr_factory_(this) {} |
| + |
| +VideoCaptureDeviceTangoAndroid::~VideoCaptureDeviceTangoAndroid() {} |
| + |
| +void VideoCaptureDeviceTangoAndroid::OnPointCloudAvailable( |
| + JNIEnv* env, |
| + jobject obj, |
| + jobject points_buffer, |
| + jint num_points, |
| + jdouble timestamp) { |
| + if (!IsClientConfiguredForIncomingData()) |
| + return; |
| + if (!intrinsics_ && !(intrinsics_ = GetIntrinsics(env, obj))) |
| + return; |
| + const base::TimeTicks current_time = base::TimeTicks::Now(); |
| + ProcessFirstFrameAvailable(current_time); |
| + |
| + const base::TimeDelta capture_time = base::TimeDelta::FromSecondsD(timestamp); |
|
mcasas
2017/07/15 00:12:37
Move this to the vicinity of l.174? Try to allocat
|
| + |
| + float* const src = |
| + reinterpret_cast<float*>(env->GetDirectBufferAddress(points_buffer)); |
| + CHECK(src); |
| + |
| + const int width = capture_format_.frame_size.width(); |
| + const int height = capture_format_.frame_size.height(); |
| + |
| + const int buffer_length = width * height * 2; |
| + std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_length]); |
|
mcasas
2017/07/15 00:12:36
Since we keep ownership of |buffer|, it might be g
|
| + |
| + uint16_t* out = reinterpret_cast<uint16_t*>(buffer.get()); |
| + memset(out, 0, buffer_length); |
| + |
| + const float fx = intrinsics_->fx; |
| + const float fy = intrinsics_->fy; |
| + // Add 0.5 to cx and cy to avoid the need for round() in operations bellow. |
| + float cx = intrinsics_->cx + 0.5; |
| + float cy = intrinsics_->cy + 0.5; |
| + |
| + int current_x = INT_MIN; |
| + int current_y = INT_MIN; |
| + |
| + // Optimization: process 4 points at once as they are likely adjacent. |
| + // TODO(aleksandar.stojiljkovic): Try optimizing using NEON. |
| + // https://crbug.com/674440 |
| + float* src_end = src + num_points / 4 * 16; |
| + |
| + for (const float* item = src; item < src_end; item += 16) { |
| +#define x0 item[0] |
| +#define y0 item[1] |
| +#define z0 item[2] |
| +#define x1 item[4] |
| +#define y1 item[5] |
| +#define z1 item[6] |
| +#define x2 item[8] |
| +#define y2 item[9] |
| +#define z2 item[10] |
| +#define x3 item[12] |
| +#define y3 item[13] |
| +#define z3 item[14] |
| + |
| + const int row3 = static_cast<int>(fy * y3 / z3 + cy); |
| + |
| + if (row3 < 0 || row3 >= height) { |
| + // heuristics: due to radial distortion, early leave as the rest of data |
| + // is outside color image. Constant 5 is large enough to compensate the |
| + // radial distortion. |
| + if (row3 >= height + 5) |
| + break; |
| + continue; |
| + } |
| + |
| + const int column3 = static_cast<int>(fx * x3 / z3 + cx); |
| + |
| + // We know that the cameras are configured to support range < 7 meters but |
| + // allow values up to 16m here. Multiplying depth by 4096 gives values with |
| + // good exposure in 16-bit unsigned range. |
| + uint16_t depth[] = { |
| + static_cast<uint16_t>(z0 * 4096), static_cast<uint16_t>(z1 * 4096), |
| + static_cast<uint16_t>(z2 * 4096), static_cast<uint16_t>(z3 * 4096)}; |
| + DCHECK(z0 * 4096 <= 65535 && z1 * 4096 <= 65535 && z2 * 4096 <= 65535 && |
| + z3 * 4096 <= 65535); |
| + |
| + unsigned out_offset3 = row3 * width + column3; |
| + if (column3 == current_x + 4 && row3 == current_y) { |
| + // All values are packed. We only need to copy depth while there is no |
| + // need to calculate x and y. |
| + if (column3 - 3 < width) |
| + out[out_offset3 - 3] = depth[0]; |
| + if (column3 - 2 < width) |
| + out[out_offset3 - 2] = depth[1]; |
| + if (column3 - 1 < width) |
| + out[out_offset3 - 1] = depth[2]; |
| + if (column3 < width) |
| + out[out_offset3] = depth[3]; |
| + current_x += 4; |
| + continue; |
| + } |
| + |
| + int row1 = static_cast<int>(fy * y1 / z1 + cy); |
| + int column1 = static_cast<int>(fx * x1 / z1 + cx); |
| + |
| + unsigned out_offset1 = row1 * width + column1; |
| + if (column1 == current_x + 2 && row1 == current_y && column1 < width) { |
| + if (row1 >= 0 && row1 < height) { |
| + out[out_offset1 - 1] = depth[0]; |
| + out[out_offset1] = depth[1]; |
| + } |
| + } else if (column1 == current_x + 3 && row1 == current_y && |
| + column1 < width) { |
| + // This compensates for the radial distortion side effects (vertical lines |
| + // with missing values) near the vertical edges. |
| + if (row1 >= 0 && row1 < height) { |
| + out[out_offset1 - 2] = depth[0]; |
| + out[out_offset1 - 1] = depth[0]; |
| + out[out_offset1] = depth[1]; |
| + } |
| + } else { |
| + const int row0 = static_cast<int>(fy * y0 / z0 + cy); |
| + const int column0 = static_cast<int>(fx * x0 / z0 + cx); |
| + if (row0 >= 0 && row0 < height && column0 >= 0 && column0 < width) |
| + out[row0 * width + column0] = depth[0]; |
| + if (row1 >= 0 && row1 < height && column1 >= 0 && column1 < width) |
| + out[out_offset1] = depth[1]; |
| + } |
| + |
| + if (column3 == column1 + 2 && row3 == row1) { |
| + if (column3 - 1 >= 0 && column3 - 1 < width) |
| + out[out_offset3 - 1] = depth[2]; |
| + if (column3 >= 0 && column3 < width) |
| + out[out_offset3] = depth[3]; |
| + } else if (column3 == column1 + 3 && row3 == row1) { |
| + // This compensates for the radial distortion side effects (vertical lines |
| + // with missing values) near the vertical edges. |
| + if (column3 - 2 >= 0 && column3 - 2 < width) |
| + out[out_offset3 - 2] = depth[2]; |
| + if (column3 - 1 >= 0 && column3 - 1 < width) |
| + out[out_offset3 - 1] = depth[2]; |
| + if (column3 >= 0 && column3 < width) |
| + out[out_offset3] = depth[3]; |
| + } else { |
| + const int row2 = static_cast<int>(fy * y2 / z2 + cy); |
| + const int column2 = static_cast<int>(fx * x2 / z2 + cx); |
| + if (row2 >= 0 && row2 < height && column2 >= 0 && column2 < width) |
| + out[row2 * width + column2] = depth[2]; |
| + if (row3 >= 0 && column3 >= 0 && column3 < width) |
| + out[out_offset3] = depth[3]; |
| + } |
| + current_x = (column3 >= 0) ? column3 : INT_MIN; |
| + current_y = row3; |
| + } |
| + |
| + if (AdvanceToNextFrameTime(current_time)) { |
| + SendIncomingDataToClient(buffer.get(), buffer_length, 0, current_time, |
| + capture_time); |
| + } |
| +} |
| + |
| +std::unique_ptr<VideoCaptureDeviceTangoAndroid::TangoIntrinsics> |
| +VideoCaptureDeviceTangoAndroid::GetIntrinsics(JNIEnv* env, jobject obj) { |
| + std::unique_ptr<TangoIntrinsics> intrinsics(new TangoIntrinsics); |
| + intrinsics->cx = Java_VideoCaptureTango_getPrincipalPointX(env, obj); |
| + intrinsics->cy = Java_VideoCaptureTango_getPrincipalPointY(env, obj); |
| + intrinsics->fx = Java_VideoCaptureTango_getFocalLengthX(env, obj); |
| + intrinsics->fy = Java_VideoCaptureTango_getFocalLengthY(env, obj); |
| + return intrinsics; |
| +} |
| + |
| +} // namespace media |