Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Side by Side Diff: media/capture/video/android/video_capture_device_tango_android_unittest.cc

Issue 2983473002: Android Tango depth camera capture support.
Patch Set: rename tango_api files to tango_client_api_glue Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/tango_client_api_glue.h"
11 #include "media/capture/video/android/video_capture_device_factory_android.h"
12 #include "media/capture/video/android/video_capture_device_tango_android.h"
13 #include "media/capture/video_capture_types.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 using Tango = media::TangoClientApiGlue;
18 using Intrinsics = media::TangoClientApiGlue::TangoCameraIntrinsics;
19
20 namespace media {
21
22 namespace {
23
24 const int kWidth = 214;
25 const int kHeight = 120;
26 // Projected point cloud values to color frame could be are outside color frame
27 // bounds. kMargin defines how larger is the depth frame area compared to color.
28 const int kMargin = 20;
29
30 bool IsTangoDevice() {
31 static const char* tango_models[] = {"Project Tango Tablet Development Kit",
32 "Lenovo PB2-690M", "ASUS_A002",
33 "ASUS_A002A"};
34 const std::string model = base::SysInfo::HardwareModelName();
35 const char** result =
36 std::find(tango_models, tango_models + arraysize(tango_models), model);
37 return (result < tango_models + arraysize(tango_models));
38 }
39
40 void OnPointCloudAvailable(void* context,
41 const Tango::TangoPointCloud* point_cloud) {}
42
43 void createTestTangoPointCloud(float* data,
44 int xstart,
45 int ystart,
46 int xend,
47 int yend,
48 float cx,
49 float cy,
50 float fx,
51 float fy) {
52 float* out = data;
53 for (int i = ystart; i < yend; i++) {
54 for (int j = xstart; j < xend; j++) {
55 // Skip some elements to mimic Tango behavior.
56 if ((j % 3 && (i + j) % 15 == 0) || j % 20 == 0)
57 continue;
58 // Generate depth value based on row and column. Maximum value is 16m.
59 float depth =
60 16.0 / (yend - ystart + xend - xstart) * (j - xstart + i - ystart);
61 float xvalue = depth * (j - cx) / fx;
62 float yvalue = depth * (i - cy) / fy;
63 out[0] = xvalue;
64 out[1] = yvalue;
65 out[2] = depth;
66 out[3] = 1.0;
67 out += 4;
68 }
69 }
70 }
71
72 } // namespace
73
74 class MockVideoCaptureClient : public VideoCaptureDevice::Client {
75 public:
76 MOCK_METHOD2(OnError,
77 void(const tracked_objects::Location& from_here,
78 const std::string& reason));
79 MOCK_CONST_METHOD0(GetBufferPoolUtilization, double(void));
80 MOCK_METHOD0(OnStarted, void(void));
81
82 explicit MockVideoCaptureClient(
83 base::Callback<void(const uint8_t*, int, const VideoCaptureFormat&)>
84 frame_cb)
85 : frame_cb_(frame_cb) {}
86
87 void OnIncomingCapturedData(const uint8_t* data,
88 int length,
89 const VideoCaptureFormat& format,
90 int rotation,
91 base::TimeTicks reference_time,
92 base::TimeDelta timestamp,
93 int frame_feedback_id) override {
94 ASSERT_GT(length, 0);
95 ASSERT_TRUE(data);
96 frame_cb_.Run(data, length, format);
97 }
98
99 // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
100 Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
101 media::VideoPixelFormat format,
102 media::VideoPixelStorage storage,
103 int frame_feedback_id) override {
104 NOTREACHED() << "This should never be called";
105 return Buffer();
106 }
107 void OnIncomingCapturedBuffer(Buffer buffer,
108 const VideoCaptureFormat& format,
109 base::TimeTicks reference_time,
110 base::TimeDelta timestamp) override {
111 NOTREACHED() << "This should never be called";
112 }
113 void OnIncomingCapturedBufferExt(
114 Buffer buffer,
115 const VideoCaptureFormat& format,
116 base::TimeTicks reference_time,
117 base::TimeDelta timestamp,
118 gfx::Rect visible_rect,
119 const VideoFrameMetadata& additional_metadata) override {
120 NOTREACHED() << "This should never be called";
121 }
122 Buffer ResurrectLastOutputBuffer(const gfx::Size& dimensions,
123 media::VideoPixelFormat format,
124 media::VideoPixelStorage storage,
125 int frame_feedback_id) {
126 NOTREACHED() << "This should never be called";
127 return Buffer();
128 }
129
130 private:
131 base::Callback<void(const uint8_t*, int, const VideoCaptureFormat&)>
132 frame_cb_;
133 };
134
135 class VideoCaptureDeviceTangoAndroidTest
136 : public testing::TestWithParam<gfx::Size> {
137 protected:
138 VideoCaptureDeviceTangoAndroidTest()
139 : video_capture_device_factory_(VideoCaptureDeviceFactory::CreateFactory(
140 base::ThreadTaskRunnerHandle::Get())),
141 device_descriptors_(new VideoCaptureDeviceDescriptors()),
142 video_capture_client_(new MockVideoCaptureClient(
143 base::Bind(&VideoCaptureDeviceTangoAndroidTest::OnFrameCaptured,
144 base::Unretained(this)))) {}
145
146 void SetUp() override {
147 video_capture_device_factory_->GetDeviceDescriptors(
148 device_descriptors_.get());
149 VideoCaptureDeviceTangoAndroid::RegisterVideoCaptureDevice(
150 base::android::AttachCurrentThread());
151
152 static_cast<VideoCaptureDeviceFactoryAndroid*>(
153 video_capture_device_factory_.get())
154 ->ConfigureForTesting();
155 }
156
157 std::unique_ptr<VideoCaptureDeviceDescriptor> GetTangoDepthDescriptor() {
158 for (const auto& descriptor : *device_descriptors_) {
159 if (descriptor.capture_api == VideoCaptureApi::ANDROID_TANGO) {
160 return std::unique_ptr<VideoCaptureDeviceDescriptor>(
161 new VideoCaptureDeviceDescriptor(descriptor));
162 }
163 }
164 return nullptr;
165 }
166
167 void OnFrameCaptured(const uint8_t* data,
168 int length,
169 const VideoCaptureFormat& format) {
170 memcpy(last_data_, data, length);
171 last_length_ = length;
172 last_format_ = format;
173 }
174
175 void SetIntrinsics(VideoCaptureDeviceTangoAndroid* tango_device,
176 double cx,
177 double cy,
178 double fx,
179 double fy) {
180 tango_device->intrinsics_ = std::unique_ptr<Intrinsics>(new Intrinsics());
181 tango_device->intrinsics_->cx = cx;
182 tango_device->intrinsics_->cy = cy;
183 tango_device->intrinsics_->fx = fx;
184 tango_device->intrinsics_->fy = fy;
185 }
186
187 // Non-Tango devices enter error state and we need to force it to configured
188 // in order to deliver the frame to client.
189 void SetConfiguredState(VideoCaptureDeviceTangoAndroid* tango_device) {
190 tango_device->state_ = VideoCaptureDeviceTangoAndroid::kConfigured;
191 }
192
193 base::test::ScopedTaskEnvironment scoped_task_environment_;
194 const std::unique_ptr<VideoCaptureDeviceFactory>
195 video_capture_device_factory_;
196 std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors_;
197 std::unique_ptr<MockVideoCaptureClient> video_capture_client_;
198 VideoCaptureFormat last_format_;
199 uint16_t last_data_[kWidth * kHeight] = {0};
200 int last_length_ = 0;
201 };
202
203 TEST_F(VideoCaptureDeviceTangoAndroidTest, OnPointCloudAvailable) {
204 VideoCaptureDeviceFactoryAndroid* android_factory =
205 static_cast<VideoCaptureDeviceFactoryAndroid*>(
206 video_capture_device_factory_.get());
207
208 std::unique_ptr<VideoCaptureDeviceDescriptor> tango_depth_descriptor(
209 GetTangoDepthDescriptor());
210 ASSERT_TRUE(IsTangoDevice() ^ tango_depth_descriptor == nullptr);
211 if (!tango_depth_descriptor) {
212 // Create mock Tango device. Tango depth id value is equal to number of
213 // system cameras; see VideoCaptureFactory.java.
214 tango_depth_descriptor = std::unique_ptr<VideoCaptureDeviceDescriptor>(
215 new VideoCaptureDeviceDescriptor(
216 "Tango Mock Test Depth",
217 base::IntToString(device_descriptors_->size()),
218 VideoCaptureApi::ANDROID_TANGO));
219 }
220 std::unique_ptr<VideoCaptureDevice> device =
221 android_factory->CreateDevice(*tango_depth_descriptor);
222 ASSERT_TRUE(device);
223
224 VideoCaptureDeviceTangoAndroid* tango_device =
225 static_cast<VideoCaptureDeviceTangoAndroid*>(device.get());
226
227 VideoCaptureParams capture_params;
228 capture_params.requested_format.frame_size.SetSize(kWidth, kHeight);
229 capture_params.requested_format.frame_rate = 5;
230 capture_params.requested_format.pixel_format = PIXEL_FORMAT_Y16;
231 tango_device->AllocateAndStart(capture_params,
232 std::move(video_capture_client_));
233 if (!IsTangoDevice())
234 SetConfiguredState(tango_device);
235
236 const int length = (kWidth + 2 * kMargin) * (kHeight + 2 * kMargin);
237 float buffer[length * 4] = {0};
238
239 const double cx = 1920. / 18;
240 const double cy = 1080. / 18;
241 const double fxy = 1920. / 9;
242 SetIntrinsics(tango_device, cx, cy, fxy, fxy);
243
244 createTestTangoPointCloud(buffer, -kMargin, -kMargin, kWidth + kMargin,
245 kHeight + kMargin, cx, cy, fxy, fxy);
246 tango_device->OnPointCloudAvailable(length, buffer, 0);
247
248 // Verify the conversion of the generated point cloud to depth buffer.
249 for (int i = 0; i < kHeight; i++) {
250 for (int j = 0; j < kWidth; j++) {
251 // Skip the same elements as in createTestTangoPointCloud.
252 if ((j % 3 && (i + j) % 15 == 0) || j % 20 == 0) {
253 continue;
254 }
255 // Generate depth value based on row and column. Maximum value is 16m.
256 float depth =
257 16.0 / (kWidth + kHeight + 4 * kMargin) * (j + kMargin + i + kMargin);
258 ASSERT_TRUE(static_cast<uint16_t>(depth * 4096) ==
259 last_data_[j + i * kWidth]);
260 EXPECT_EQ(static_cast<uint16_t>(depth * 4096), last_data_[j + i * kWidth])
261 << "x:" << j << " ,y:" << i;
262 }
263 }
264 tango_device->StopAndDeAllocate();
265 }
266
267 TEST_F(VideoCaptureDeviceTangoAndroidTest, TangoApiNotFound) {
268 if (IsTangoDevice())
269 return; // Skip if it is a Tango device.
270 JNIEnv* env = base::android::AttachCurrentThread();
271 EXPECT_EQ(Tango::TANGO_METHOD_NOT_FOUND,
272 Tango::TangoService_setBinder(env, nullptr));
273 EXPECT_EQ(Tango::TANGO_METHOD_NOT_FOUND,
274 Tango::TangoService_connect(nullptr, nullptr));
275 EXPECT_EQ(Tango::TANGO_METHOD_NOT_FOUND,
276 Tango::TangoConfig_setBool(nullptr, "config_enable_depth", true));
277 EXPECT_EQ(Tango::TANGO_METHOD_NOT_FOUND,
278 Tango::TangoConfig_setInt32(nullptr, "config_depth_mode", 0));
279 EXPECT_EQ(Tango::TANGO_METHOD_NOT_FOUND,
280 Tango::TangoService_connectOnPointCloudAvailable(
281 OnPointCloudAvailable, this));
282 Intrinsics intrinsics;
283 EXPECT_EQ(Tango::TANGO_METHOD_NOT_FOUND,
284 Tango::TangoService_getCameraIntrinsics(Tango::TANGO_CAMERA_COLOR,
285 &intrinsics));
286 ASSERT_FALSE(Tango::TangoService_getConfig(Tango::TANGO_CONFIG_DEFAULT));
287 Tango::TangoService_disconnect();
288 }
289
290 TEST_F(VideoCaptureDeviceTangoAndroidTest, TangoApi) {
291 if (!IsTangoDevice())
292 return; // Skip if not a Tango device.
293
294 // Previous versions of Tango allow calls to some of the methods even without
295 // connecting to service. We want to verify that the methods are resolved and
296 // checking that return error is not TANGO_METHOD_NOT_FOUND is sufficient.
297 JNIEnv* env = base::android::AttachCurrentThread();
298 EXPECT_EQ(Tango::TANGO_ERROR, Tango::TangoService_setBinder(env, nullptr));
299 EXPECT_NE(Tango::TANGO_METHOD_NOT_FOUND,
300 Tango::TangoConfig_setBool(nullptr, "config_enable_depth", true));
301 EXPECT_NE(Tango::TANGO_METHOD_NOT_FOUND,
302 Tango::TangoConfig_setInt32(nullptr, "config_depth_mode", 0));
303 EXPECT_NE(Tango::TANGO_METHOD_NOT_FOUND,
304 Tango::TangoService_connectOnPointCloudAvailable(
305 OnPointCloudAvailable, this));
306 Intrinsics intrinsics;
307 EXPECT_NE(Tango::TANGO_METHOD_NOT_FOUND,
308 Tango::TangoService_getCameraIntrinsics(Tango::TANGO_CAMERA_COLOR,
309 &intrinsics));
310 Tango::TangoService_disconnect();
311 }
312
313 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698