Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 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 #include "video_capture_device_tango_android.h" | |
| 6 #include "base/android/jni_android.h" | |
| 7 #include "base/strings/string_number_conversions.h" | |
| 8 #include "base/sys_info.h" | |
| 9 #include "base/test/scoped_task_environment.h" | |
| 10 #include "media/capture/video/android/video_capture_device_factory_android.h" | |
| 11 #include "media/capture/video/android/video_capture_device_tango_android.h" | |
| 12 #include "media/capture/video_capture_types.h" | |
| 13 #include "testing/gmock/include/gmock/gmock.h" | |
| 14 #include "testing/gtest/include/gtest/gtest.h" | |
| 15 | |
| 16 namespace media { | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 const int kWidth = 214; | |
| 21 const int kHeight = 120; | |
| 22 // Projected point cloud values to color frame could be are outside color frame | |
| 23 // bounds. kMargin defines how larger is the depth frame area compared to color. | |
| 24 const int kMargin = 20; | |
| 25 | |
| 26 bool IsTangoDevice() { | |
| 27 const std::string model = base::SysInfo::HardwareModelName(); | |
| 28 return (model == "Project Tango Tablet Development Kit" || | |
| 29 model == "Lenovo PB2-690M"); | |
|
mcasas
2017/07/15 00:12:37
Usually these name lists are moved out as static a
| |
| 30 } | |
| 31 | |
| 32 void createTestTangoPointCloud(float* data, | |
| 33 int xstart, | |
| 34 int ystart, | |
| 35 int xend, | |
| 36 int yend, | |
| 37 float cx, | |
| 38 float cy, | |
| 39 float fx, | |
| 40 float fy) { | |
| 41 float* out = data; | |
| 42 for (int i = ystart; i < yend; i++) { | |
| 43 for (int j = xstart; j < xend; j++) { | |
| 44 // Skip some elements to mimic Tango behavior. | |
| 45 if ((j % 3 && (i + j) % 15 == 0) || j % 20 == 0) | |
| 46 continue; | |
| 47 // Generate depth value based on row and column. Maximum value is 16m. | |
| 48 float depth = | |
| 49 16.0 / (yend - ystart + xend - xstart) * (j - xstart + i - ystart); | |
| 50 float xvalue = depth * (j - cx) / fx; | |
| 51 float yvalue = depth * (i - cy) / fy; | |
| 52 out[0] = xvalue; | |
| 53 out[1] = yvalue; | |
| 54 out[2] = depth; | |
| 55 out[3] = 1.0; | |
| 56 out += 4; | |
| 57 } | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 } // namespace | |
| 62 | |
| 63 class MockVideoCaptureClient : public VideoCaptureDevice::Client { | |
| 64 public: | |
| 65 MOCK_METHOD2(OnError, | |
| 66 void(const tracked_objects::Location& from_here, | |
| 67 const std::string& reason)); | |
| 68 MOCK_CONST_METHOD0(GetBufferPoolUtilization, double(void)); | |
| 69 MOCK_METHOD0(OnStarted, void(void)); | |
| 70 | |
| 71 explicit MockVideoCaptureClient( | |
| 72 base::Callback<void(const uint8_t*, int, const VideoCaptureFormat&)> | |
| 73 frame_cb) | |
| 74 : frame_cb_(frame_cb) {} | |
| 75 | |
| 76 void OnIncomingCapturedData(const uint8_t* data, | |
| 77 int length, | |
| 78 const VideoCaptureFormat& format, | |
| 79 int rotation, | |
| 80 base::TimeTicks reference_time, | |
| 81 base::TimeDelta timestamp, | |
| 82 int frame_feedback_id) override { | |
| 83 ASSERT_GT(length, 0); | |
| 84 ASSERT_TRUE(data); | |
| 85 frame_cb_.Run(data, length, format); | |
| 86 } | |
| 87 | |
| 88 // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>. | |
| 89 Buffer ReserveOutputBuffer(const gfx::Size& dimensions, | |
| 90 media::VideoPixelFormat format, | |
| 91 media::VideoPixelStorage storage, | |
| 92 int frame_feedback_id) override { | |
| 93 NOTREACHED() << "This should never be called"; | |
| 94 return Buffer(); | |
| 95 } | |
| 96 void OnIncomingCapturedBuffer(Buffer buffer, | |
| 97 const VideoCaptureFormat& format, | |
| 98 base::TimeTicks reference_time, | |
| 99 base::TimeDelta timestamp) override { | |
| 100 NOTREACHED() << "This should never be called"; | |
| 101 } | |
| 102 void OnIncomingCapturedBufferExt( | |
| 103 Buffer buffer, | |
| 104 const VideoCaptureFormat& format, | |
| 105 base::TimeTicks reference_time, | |
| 106 base::TimeDelta timestamp, | |
| 107 gfx::Rect visible_rect, | |
| 108 const VideoFrameMetadata& additional_metadata) override { | |
| 109 NOTREACHED() << "This should never be called"; | |
| 110 } | |
| 111 Buffer ResurrectLastOutputBuffer(const gfx::Size& dimensions, | |
| 112 media::VideoPixelFormat format, | |
| 113 media::VideoPixelStorage storage, | |
| 114 int frame_feedback_id) { | |
| 115 NOTREACHED() << "This should never be called"; | |
| 116 return Buffer(); | |
| 117 } | |
| 118 | |
| 119 private: | |
| 120 base::Callback<void(const uint8_t*, int, const VideoCaptureFormat&)> | |
| 121 frame_cb_; | |
| 122 }; | |
| 123 | |
| 124 class VideoCaptureDeviceTangoAndroidTest | |
| 125 : public testing::TestWithParam<gfx::Size> { | |
| 126 protected: | |
| 127 VideoCaptureDeviceTangoAndroidTest() | |
| 128 : video_capture_device_factory_(VideoCaptureDeviceFactory::CreateFactory( | |
| 129 base::ThreadTaskRunnerHandle::Get())), | |
| 130 device_descriptors_(new VideoCaptureDeviceDescriptors()), | |
| 131 video_capture_client_(new MockVideoCaptureClient( | |
| 132 base::Bind(&VideoCaptureDeviceTangoAndroidTest::OnFrameCaptured, | |
| 133 base::Unretained(this)))) {} | |
| 134 | |
| 135 void SetUp() override { | |
| 136 video_capture_device_factory_->GetDeviceDescriptors( | |
| 137 device_descriptors_.get()); | |
| 138 VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice( | |
| 139 base::android::AttachCurrentThread()); | |
| 140 | |
| 141 static_cast<VideoCaptureDeviceFactoryAndroid*>( | |
| 142 video_capture_device_factory_.get()) | |
| 143 ->ConfigureForTesting(); | |
| 144 } | |
| 145 | |
| 146 std::unique_ptr<VideoCaptureDeviceDescriptor> GetTangoDepthDescriptor() { | |
| 147 for (const auto& descriptor : *device_descriptors_) { | |
| 148 if (descriptor.capture_api == VideoCaptureApi::ANDROID_TANGO) { | |
| 149 return std::unique_ptr<VideoCaptureDeviceDescriptor>( | |
| 150 new VideoCaptureDeviceDescriptor(descriptor)); | |
| 151 } | |
| 152 } | |
| 153 return nullptr; | |
| 154 } | |
| 155 | |
| 156 void OnFrameCaptured(const uint8_t* data, | |
| 157 int length, | |
| 158 const VideoCaptureFormat& format) { | |
| 159 memcpy(last_data_, data, length); | |
| 160 last_length_ = length; | |
| 161 last_format_ = format; | |
| 162 } | |
| 163 | |
| 164 static std::unique_ptr<VideoCaptureDeviceTangoAndroid::TangoIntrinsics> | |
| 165 GetIntrinsics(const VideoCaptureDeviceTangoAndroid& tango_device) { | |
| 166 if (!tango_device.intrinsics_) | |
| 167 return nullptr; | |
| 168 return std::unique_ptr<VideoCaptureDeviceTangoAndroid::TangoIntrinsics>( | |
| 169 new VideoCaptureDeviceTangoAndroid::TangoIntrinsics( | |
| 170 *(tango_device.intrinsics_))); | |
| 171 } | |
| 172 | |
| 173 static jobject GetJavaCaptureObject( | |
| 174 const VideoCaptureDeviceTangoAndroid& tango_device) { | |
| 175 return tango_device.j_capture_.obj(); | |
| 176 } | |
| 177 | |
| 178 base::test::ScopedTaskEnvironment scoped_task_environment_; | |
| 179 const std::unique_ptr<VideoCaptureDeviceFactory> | |
| 180 video_capture_device_factory_; | |
| 181 std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors_; | |
| 182 std::unique_ptr<MockVideoCaptureClient> video_capture_client_; | |
| 183 VideoCaptureFormat last_format_; | |
| 184 uint16_t last_data_[kWidth * kHeight] = {0}; | |
| 185 int last_length_ = 0; | |
| 186 }; | |
| 187 | |
| 188 TEST_F(VideoCaptureDeviceTangoAndroidTest, OnPointCloudAvailable) { | |
| 189 VideoCaptureDeviceFactoryAndroid* android_factory = | |
| 190 static_cast<VideoCaptureDeviceFactoryAndroid*>( | |
| 191 video_capture_device_factory_.get()); | |
| 192 | |
| 193 std::unique_ptr<VideoCaptureDeviceDescriptor> tango_depth_descriptor( | |
| 194 GetTangoDepthDescriptor()); | |
| 195 ASSERT_TRUE(IsTangoDevice() ^ tango_depth_descriptor == nullptr); | |
| 196 if (!tango_depth_descriptor) { | |
| 197 // Create mock Tango device. Tango depth id value is equal to number of | |
| 198 // system cameras; see VideoCaptureFactory.java. | |
| 199 tango_depth_descriptor = std::unique_ptr<VideoCaptureDeviceDescriptor>( | |
| 200 new VideoCaptureDeviceDescriptor( | |
| 201 "Tango Mock Test Depth", | |
| 202 base::IntToString(device_descriptors_->size()), | |
| 203 VideoCaptureApi::ANDROID_TANGO)); | |
| 204 } | |
| 205 std::unique_ptr<VideoCaptureDevice> device = | |
| 206 android_factory->CreateDevice(*tango_depth_descriptor); | |
| 207 ASSERT_TRUE(device); | |
| 208 | |
| 209 VideoCaptureDeviceTangoAndroid* tango_device = | |
| 210 static_cast<VideoCaptureDeviceTangoAndroid*>(device.get()); | |
| 211 const auto intrinsics_null = GetIntrinsics(*tango_device); | |
| 212 ASSERT_FALSE(intrinsics_null); | |
| 213 | |
| 214 VideoCaptureParams capture_params; | |
| 215 capture_params.requested_format.frame_size.SetSize(kWidth, kHeight); | |
| 216 capture_params.requested_format.frame_rate = 5; | |
| 217 capture_params.requested_format.pixel_format = PIXEL_FORMAT_Y16; | |
| 218 tango_device->AllocateAndStart(capture_params, | |
| 219 std::move(video_capture_client_)); | |
| 220 | |
| 221 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 222 | |
| 223 const int length = (kWidth + 2 * kMargin) * (kHeight + 2 * kMargin); | |
| 224 float buffer[length * 4] = {0}; | |
| 225 jobject byte_buffer = | |
| 226 env->NewDirectByteBuffer(static_cast<void*>(buffer), sizeof(buffer)); | |
| 227 | |
| 228 // Call this once before running with the real values to get tango intrinsics | |
| 229 // as they are fetched on first OnPointCloudAvailable call. | |
| 230 tango_device->OnPointCloudAvailable(env, GetJavaCaptureObject(*tango_device), | |
| 231 byte_buffer, length, 0); | |
| 232 | |
| 233 // Mock values for intrinsics are set in VideoCaptureTango.java. | |
| 234 const auto intrinsics = GetIntrinsics(*tango_device); | |
| 235 ASSERT_TRUE(intrinsics); | |
| 236 EXPECT_NEAR(213.333333, intrinsics->fx, 0.000005); | |
| 237 EXPECT_NEAR(213.333333, intrinsics->fy, 0.000005); | |
| 238 EXPECT_NEAR(106.666667, intrinsics->cx, 0.000005); | |
| 239 EXPECT_EQ(60., intrinsics->cy); | |
| 240 | |
| 241 createTestTangoPointCloud(buffer, -kMargin, -kMargin, kWidth + kMargin, | |
| 242 kHeight + kMargin, intrinsics->cx, intrinsics->cy, | |
| 243 intrinsics->fx, intrinsics->fy); | |
| 244 tango_device->OnPointCloudAvailable(env, GetJavaCaptureObject(*tango_device), | |
| 245 byte_buffer, length, 0); | |
| 246 | |
| 247 // Verify the conversion of the generated point cloud to depth buffer. | |
| 248 for (int i = 0; i < kHeight; i++) { | |
| 249 for (int j = 0; j < kWidth; j++) { | |
| 250 // Skip the same elements as in createTestTangoPointCloud. | |
| 251 if ((j % 3 && (i + j) % 15 == 0) || j % 20 == 0) { | |
| 252 continue; | |
| 253 } | |
| 254 // Generate depth value based on row and column. Maximum value is 16m. | |
| 255 float depth = | |
| 256 16.0 / (kWidth + kHeight + 4 * kMargin) * (j + kMargin + i + kMargin); | |
| 257 EXPECT_EQ(static_cast<uint16_t>(depth * 4096), last_data_[j + i * kWidth]) | |
| 258 << "x:" << j << " ,y:" << i; | |
| 259 } | |
| 260 } | |
| 261 tango_device->StopAndDeAllocate(); | |
| 262 } | |
| 263 | |
| 264 } // namespace media | |
| OLD | NEW |