|
1 /* |
|
2 * Copyright (C) 2013-2014 Mozilla Foundation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #include "TestGonkCameraHardware.h" |
|
18 |
|
19 #include "mozilla/Preferences.h" |
|
20 #include "nsThreadUtils.h" |
|
21 |
|
22 using namespace android; |
|
23 using namespace mozilla; |
|
24 |
|
25 TestGonkCameraHardware::TestGonkCameraHardware(nsGonkCameraControl* aTarget, |
|
26 uint32_t aCameraId, |
|
27 const sp<Camera>& aCamera) |
|
28 : GonkCameraHardware(aTarget, aCameraId, aCamera) |
|
29 { |
|
30 DOM_CAMERA_LOGA("v===== Created TestGonkCameraHardware =====v\n"); |
|
31 DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", |
|
32 __func__, __LINE__, this, aTarget); |
|
33 MOZ_COUNT_CTOR(TestGonkCameraHardware); |
|
34 } |
|
35 |
|
36 TestGonkCameraHardware::~TestGonkCameraHardware() |
|
37 { |
|
38 MOZ_COUNT_DTOR(TestGonkCameraHardware); |
|
39 DOM_CAMERA_LOGA("^===== Destroyed TestGonkCameraHardware =====^\n"); |
|
40 } |
|
41 |
|
42 nsresult |
|
43 TestGonkCameraHardware::Init() |
|
44 { |
|
45 if (IsTestCase("init-failure")) { |
|
46 return NS_ERROR_FAILURE; |
|
47 } |
|
48 |
|
49 return GonkCameraHardware::Init(); |
|
50 } |
|
51 |
|
52 const nsCString |
|
53 TestGonkCameraHardware::TestCase() |
|
54 { |
|
55 const nsCString test = Preferences::GetCString("camera.control.test.hardware"); |
|
56 return test; |
|
57 } |
|
58 |
|
59 const nsCString |
|
60 TestGonkCameraHardware::GetExtraParameters() |
|
61 { |
|
62 /** |
|
63 * The contents of this pref are appended to the flattened string of |
|
64 * parameters stuffed into GonkCameraParameters by the camera library. |
|
65 * It consists of semicolon-delimited key=value pairs, e.g. |
|
66 * |
|
67 * focus-mode=auto;flash-mode=auto;preview-size=1024x768 |
|
68 * |
|
69 * The unflattening process breaks this string up on semicolon boundaries |
|
70 * and sets an entry in a hashtable of strings with the token before |
|
71 * the equals sign as the key, and the token after as the value. Because |
|
72 * the string is parsed in order, key=value pairs occuring later in the |
|
73 * string will replace value pairs appearing earlier, making it easy to |
|
74 * inject fake, testable values into the parameters table. |
|
75 * |
|
76 * One constraint of this approach is that neither the key nor the value |
|
77 * may contain equals signs or semicolons. We don't enforce that here |
|
78 * so that we can also test correct handling of improperly-formatted values. |
|
79 */ |
|
80 const nsCString parameters = Preferences::GetCString("camera.control.test.hardware.gonk.parameters"); |
|
81 DOM_CAMERA_LOGA("TestGonkCameraHardware : extra-parameters '%s'\n", |
|
82 parameters.get()); |
|
83 return parameters; |
|
84 } |
|
85 |
|
86 bool |
|
87 TestGonkCameraHardware::IsTestCaseInternal(const char* aTest, const char* aFile, int aLine) |
|
88 { |
|
89 if (TestCase().EqualsASCII(aTest)) { |
|
90 DOM_CAMERA_LOGA("TestGonkCameraHardware : test-case '%s' (%s:%d)\n", |
|
91 aTest, aFile, aLine); |
|
92 return true; |
|
93 } |
|
94 |
|
95 return false; |
|
96 } |
|
97 |
|
98 int |
|
99 TestGonkCameraHardware::TestCaseError(int aDefaultError) |
|
100 { |
|
101 // for now, just return the default error |
|
102 return aDefaultError; |
|
103 } |
|
104 |
|
105 int |
|
106 TestGonkCameraHardware::AutoFocus() |
|
107 { |
|
108 class AutoFocusFailure : public nsRunnable |
|
109 { |
|
110 public: |
|
111 AutoFocusFailure(nsGonkCameraControl* aTarget) |
|
112 : mTarget(aTarget) |
|
113 { } |
|
114 |
|
115 NS_IMETHODIMP |
|
116 Run() |
|
117 { |
|
118 OnAutoFocusComplete(mTarget, false); |
|
119 return NS_OK; |
|
120 } |
|
121 |
|
122 protected: |
|
123 nsGonkCameraControl* mTarget; |
|
124 }; |
|
125 |
|
126 if (IsTestCase("auto-focus-failure")) { |
|
127 return TestCaseError(UNKNOWN_ERROR); |
|
128 } |
|
129 if (IsTestCase("auto-focus-process-failure")) { |
|
130 nsresult rv = NS_DispatchToCurrentThread(new AutoFocusFailure(mTarget)); |
|
131 if (NS_SUCCEEDED(rv)) { |
|
132 return OK; |
|
133 } |
|
134 DOM_CAMERA_LOGE("Failed to dispatch AutoFocusFailure runnable (0x%08x)\n", rv); |
|
135 return UNKNOWN_ERROR; |
|
136 } |
|
137 |
|
138 return GonkCameraHardware::AutoFocus(); |
|
139 } |
|
140 |
|
141 // These classes have to be external to StartFaceDetection(), at least |
|
142 // until we pick up gcc 4.5, which supports local classes as template |
|
143 // arguments. |
|
144 class FaceDetected : public nsRunnable |
|
145 { |
|
146 public: |
|
147 FaceDetected(nsGonkCameraControl* aTarget) |
|
148 : mTarget(aTarget) |
|
149 { } |
|
150 |
|
151 ~FaceDetected() |
|
152 { |
|
153 ReleaseFacesArray(); |
|
154 } |
|
155 |
|
156 NS_IMETHODIMP |
|
157 Run() |
|
158 { |
|
159 InitMetaData(); |
|
160 OnFacesDetected(mTarget, &mMetaData); |
|
161 return NS_OK; |
|
162 } |
|
163 |
|
164 protected: |
|
165 virtual nsresult InitMetaData() = 0; |
|
166 |
|
167 nsresult |
|
168 AllocateFacesArray(uint32_t num) |
|
169 { |
|
170 mMetaData.faces = new camera_face_t[num]; |
|
171 return NS_OK; |
|
172 } |
|
173 |
|
174 nsresult |
|
175 ReleaseFacesArray() |
|
176 { |
|
177 delete [] mMetaData.faces; |
|
178 mMetaData.faces = nullptr; |
|
179 return NS_OK; |
|
180 } |
|
181 |
|
182 nsRefPtr<nsGonkCameraControl> mTarget; |
|
183 camera_frame_metadata_t mMetaData; |
|
184 }; |
|
185 |
|
186 class OneFaceDetected : public FaceDetected |
|
187 { |
|
188 public: |
|
189 OneFaceDetected(nsGonkCameraControl* aTarget) |
|
190 : FaceDetected(aTarget) |
|
191 { } |
|
192 |
|
193 nsresult |
|
194 InitMetaData() MOZ_OVERRIDE |
|
195 { |
|
196 mMetaData.number_of_faces = 1; |
|
197 AllocateFacesArray(1); |
|
198 mMetaData.faces[0].id = 1; |
|
199 mMetaData.faces[0].score = 2; |
|
200 mMetaData.faces[0].rect[0] = 3; |
|
201 mMetaData.faces[0].rect[1] = 4; |
|
202 mMetaData.faces[0].rect[2] = 5; |
|
203 mMetaData.faces[0].rect[3] = 6; |
|
204 mMetaData.faces[0].left_eye[0] = 7; |
|
205 mMetaData.faces[0].left_eye[1] = 8; |
|
206 mMetaData.faces[0].right_eye[0] = 9; |
|
207 mMetaData.faces[0].right_eye[1] = 10; |
|
208 mMetaData.faces[0].mouth[0] = 11; |
|
209 mMetaData.faces[0].mouth[1] = 12; |
|
210 |
|
211 return NS_OK; |
|
212 } |
|
213 }; |
|
214 |
|
215 class TwoFacesDetected : public FaceDetected |
|
216 { |
|
217 public: |
|
218 TwoFacesDetected(nsGonkCameraControl* aTarget) |
|
219 : FaceDetected(aTarget) |
|
220 { } |
|
221 |
|
222 nsresult |
|
223 InitMetaData() MOZ_OVERRIDE |
|
224 { |
|
225 mMetaData.number_of_faces = 2; |
|
226 AllocateFacesArray(2); |
|
227 mMetaData.faces[0].id = 1; |
|
228 mMetaData.faces[0].score = 2; |
|
229 mMetaData.faces[0].rect[0] = 3; |
|
230 mMetaData.faces[0].rect[1] = 4; |
|
231 mMetaData.faces[0].rect[2] = 5; |
|
232 mMetaData.faces[0].rect[3] = 6; |
|
233 mMetaData.faces[0].left_eye[0] = 7; |
|
234 mMetaData.faces[0].left_eye[1] = 8; |
|
235 mMetaData.faces[0].right_eye[0] = 9; |
|
236 mMetaData.faces[0].right_eye[1] = 10; |
|
237 mMetaData.faces[0].mouth[0] = 11; |
|
238 mMetaData.faces[0].mouth[1] = 12; |
|
239 mMetaData.faces[1].id = 13; |
|
240 mMetaData.faces[1].score = 14; |
|
241 mMetaData.faces[1].rect[0] = 15; |
|
242 mMetaData.faces[1].rect[1] = 16; |
|
243 mMetaData.faces[1].rect[2] = 17; |
|
244 mMetaData.faces[1].rect[3] = 18; |
|
245 mMetaData.faces[1].left_eye[0] = 19; |
|
246 mMetaData.faces[1].left_eye[1] = 20; |
|
247 mMetaData.faces[1].right_eye[0] = 21; |
|
248 mMetaData.faces[1].right_eye[1] = 22; |
|
249 mMetaData.faces[1].mouth[0] = 23; |
|
250 mMetaData.faces[1].mouth[1] = 24; |
|
251 |
|
252 return NS_OK; |
|
253 } |
|
254 }; |
|
255 |
|
256 class OneFaceNoFeaturesDetected : public FaceDetected |
|
257 { |
|
258 public: |
|
259 OneFaceNoFeaturesDetected(nsGonkCameraControl* aTarget) |
|
260 : FaceDetected(aTarget) |
|
261 { } |
|
262 |
|
263 nsresult |
|
264 InitMetaData() MOZ_OVERRIDE |
|
265 { |
|
266 mMetaData.number_of_faces = 1; |
|
267 AllocateFacesArray(1); |
|
268 mMetaData.faces[0].id = 1; |
|
269 // Test clamping 'score' to 100. |
|
270 mMetaData.faces[0].score = 1000; |
|
271 mMetaData.faces[0].rect[0] = 3; |
|
272 mMetaData.faces[0].rect[1] = 4; |
|
273 mMetaData.faces[0].rect[2] = 5; |
|
274 mMetaData.faces[0].rect[3] = 6; |
|
275 // Nullable values set to 'not-supported' specific values |
|
276 mMetaData.faces[0].left_eye[0] = -2000; |
|
277 mMetaData.faces[0].left_eye[1] = -2000; |
|
278 // Test other 'not-supported' values as well. We treat |
|
279 // anything outside the range [-1000, 1000] as invalid. |
|
280 mMetaData.faces[0].right_eye[0] = 1001; |
|
281 mMetaData.faces[0].right_eye[1] = -1001; |
|
282 mMetaData.faces[0].mouth[0] = -2000; |
|
283 mMetaData.faces[0].mouth[1] = 2000; |
|
284 |
|
285 return NS_OK; |
|
286 } |
|
287 }; |
|
288 |
|
289 class NoFacesDetected : public FaceDetected |
|
290 { |
|
291 public: |
|
292 NoFacesDetected(nsGonkCameraControl* aTarget) |
|
293 : FaceDetected(aTarget) |
|
294 { } |
|
295 |
|
296 nsresult |
|
297 InitMetaData() MOZ_OVERRIDE |
|
298 { |
|
299 mMetaData.number_of_faces = 0; |
|
300 mMetaData.faces = nullptr; |
|
301 |
|
302 return NS_OK; |
|
303 } |
|
304 }; |
|
305 |
|
306 int |
|
307 TestGonkCameraHardware::StartFaceDetection() |
|
308 { |
|
309 nsRefPtr<FaceDetected> faceDetected; |
|
310 |
|
311 if (IsTestCase("face-detection-detected-one-face")) { |
|
312 faceDetected = new OneFaceDetected(mTarget); |
|
313 } else if (IsTestCase("face-detection-detected-two-faces")) { |
|
314 faceDetected = new TwoFacesDetected(mTarget); |
|
315 } else if (IsTestCase("face-detection-detected-one-face-no-features")) { |
|
316 faceDetected = new OneFaceNoFeaturesDetected(mTarget); |
|
317 } else if (IsTestCase("face-detection-no-faces-detected")) { |
|
318 faceDetected = new NoFacesDetected(mTarget); |
|
319 } |
|
320 |
|
321 if (!faceDetected) { |
|
322 return GonkCameraHardware::StartFaceDetection(); |
|
323 } |
|
324 |
|
325 nsresult rv = NS_DispatchToCurrentThread(faceDetected); |
|
326 if (NS_FAILED(rv)) { |
|
327 DOM_CAMERA_LOGE("Failed to dispatch FaceDetected runnable (0x%08x)\n", rv); |
|
328 return UNKNOWN_ERROR; |
|
329 } |
|
330 |
|
331 return OK; |
|
332 } |
|
333 |
|
334 int |
|
335 TestGonkCameraHardware::StopFaceDetection() |
|
336 { |
|
337 if (IsTestCase("face-detection-detected-one-face") || |
|
338 IsTestCase("face-detection-detected-two-faces") || |
|
339 IsTestCase("face-detection-detected-one-face-no-features") || |
|
340 IsTestCase("face-detection-no-faces-detected")) |
|
341 { |
|
342 return OK; |
|
343 } |
|
344 |
|
345 return GonkCameraHardware::StopFaceDetection(); |
|
346 } |
|
347 |
|
348 int |
|
349 TestGonkCameraHardware::TakePicture() |
|
350 { |
|
351 class TakePictureFailure : public nsRunnable |
|
352 { |
|
353 public: |
|
354 TakePictureFailure(nsGonkCameraControl* aTarget) |
|
355 : mTarget(aTarget) |
|
356 { } |
|
357 |
|
358 NS_IMETHODIMP |
|
359 Run() |
|
360 { |
|
361 OnTakePictureError(mTarget); |
|
362 return NS_OK; |
|
363 } |
|
364 |
|
365 protected: |
|
366 nsGonkCameraControl* mTarget; |
|
367 }; |
|
368 |
|
369 if (IsTestCase("take-picture-failure")) { |
|
370 return TestCaseError(UNKNOWN_ERROR); |
|
371 } |
|
372 if (IsTestCase("take-picture-process-failure")) { |
|
373 nsresult rv = NS_DispatchToCurrentThread(new TakePictureFailure(mTarget)); |
|
374 if (NS_SUCCEEDED(rv)) { |
|
375 return OK; |
|
376 } |
|
377 DOM_CAMERA_LOGE("Failed to dispatch TakePictureFailure runnable (0x%08x)\n", rv); |
|
378 return UNKNOWN_ERROR; |
|
379 } |
|
380 |
|
381 return GonkCameraHardware::TakePicture(); |
|
382 } |
|
383 |
|
384 int |
|
385 TestGonkCameraHardware::StartPreview() |
|
386 { |
|
387 if (IsTestCase("start-preview-failure")) { |
|
388 return TestCaseError(UNKNOWN_ERROR); |
|
389 } |
|
390 |
|
391 return GonkCameraHardware::StartPreview(); |
|
392 } |
|
393 |
|
394 int |
|
395 TestGonkCameraHardware::StartAutoFocusMoving(bool aIsMoving) |
|
396 { |
|
397 class AutoFocusMoving : public nsRunnable |
|
398 { |
|
399 public: |
|
400 AutoFocusMoving(nsGonkCameraControl* aTarget, bool aIsMoving) |
|
401 : mTarget(aTarget) |
|
402 , mIsMoving(aIsMoving) |
|
403 { } |
|
404 |
|
405 NS_IMETHODIMP |
|
406 Run() |
|
407 { |
|
408 OnAutoFocusMoving(mTarget, mIsMoving); |
|
409 return NS_OK; |
|
410 } |
|
411 |
|
412 protected: |
|
413 nsGonkCameraControl* mTarget; |
|
414 bool mIsMoving; |
|
415 }; |
|
416 |
|
417 nsresult rv = NS_DispatchToCurrentThread(new AutoFocusMoving(mTarget, aIsMoving)); |
|
418 if (NS_SUCCEEDED(rv)) { |
|
419 return OK; |
|
420 } |
|
421 DOM_CAMERA_LOGE("Failed to dispatch AutoFocusMoving runnable (0x%08x)\n", rv); |
|
422 return UNKNOWN_ERROR; |
|
423 } |
|
424 |
|
425 int |
|
426 TestGonkCameraHardware::PushParameters(const GonkCameraParameters& aParams) |
|
427 { |
|
428 if (IsTestCase("push-parameters-failure")) { |
|
429 return TestCaseError(UNKNOWN_ERROR); |
|
430 } |
|
431 |
|
432 nsString focusMode; |
|
433 GonkCameraParameters& params = const_cast<GonkCameraParameters&>(aParams); |
|
434 params.Get(CAMERA_PARAM_FOCUSMODE, focusMode); |
|
435 if (focusMode.EqualsASCII("continuous-picture") || |
|
436 focusMode.EqualsASCII("continuous-video")) |
|
437 { |
|
438 if (IsTestCase("autofocus-moving-true")) { |
|
439 return StartAutoFocusMoving(true); |
|
440 } else if (IsTestCase("autofocus-moving-false")) { |
|
441 return StartAutoFocusMoving(false); |
|
442 } |
|
443 } |
|
444 |
|
445 return GonkCameraHardware::PushParameters(aParams); |
|
446 } |
|
447 |
|
448 nsresult |
|
449 TestGonkCameraHardware::PullParameters(GonkCameraParameters& aParams) |
|
450 { |
|
451 if (IsTestCase("pull-parameters-failure")) { |
|
452 return static_cast<nsresult>(TestCaseError(UNKNOWN_ERROR)); |
|
453 } |
|
454 |
|
455 String8 s = mCamera->getParameters(); |
|
456 nsCString extra = GetExtraParameters(); |
|
457 if (!extra.IsEmpty()) { |
|
458 s += ";"; |
|
459 s += extra.get(); |
|
460 } |
|
461 |
|
462 return aParams.Unflatten(s); |
|
463 } |
|
464 |
|
465 int |
|
466 TestGonkCameraHardware::StartRecording() |
|
467 { |
|
468 if (IsTestCase("start-recording-failure")) { |
|
469 return TestCaseError(UNKNOWN_ERROR); |
|
470 } |
|
471 |
|
472 return GonkCameraHardware::StartRecording(); |
|
473 } |
|
474 |
|
475 int |
|
476 TestGonkCameraHardware::StopRecording() |
|
477 { |
|
478 if (IsTestCase("stop-recording-failure")) { |
|
479 return TestCaseError(UNKNOWN_ERROR); |
|
480 } |
|
481 |
|
482 return GonkCameraHardware::StopRecording(); |
|
483 } |
|
484 |
|
485 int |
|
486 TestGonkCameraHardware::SetListener(const sp<GonkCameraListener>& aListener) |
|
487 { |
|
488 if (IsTestCase("set-listener-failure")) { |
|
489 return TestCaseError(UNKNOWN_ERROR); |
|
490 } |
|
491 |
|
492 return GonkCameraHardware::SetListener(aListener); |
|
493 } |
|
494 |
|
495 int |
|
496 TestGonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled) |
|
497 { |
|
498 if (IsTestCase("store-metadata-in-buffers-failure")) { |
|
499 return TestCaseError(UNKNOWN_ERROR); |
|
500 } |
|
501 |
|
502 return GonkCameraHardware::StoreMetaDataInBuffers(aEnabled); |
|
503 } |