Merge lp:~fboucault/camera-app/fix_test_video_resolution into lp:camera-app
- fix_test_video_resolution
- Merge into trunk
Proposed by
Florian Boucault
Status: | Superseded |
---|---|
Proposed branch: | lp:~fboucault/camera-app/fix_test_video_resolution |
Merge into: | lp:camera-app |
Diff against target: |
768 lines (+418/-51) 10 files modified
BottomEdgeIndicators.qml (+1/-1) CameraApp/advancedcamerasettings.cpp (+125/-0) CameraApp/advancedcamerasettings.h (+11/-0) OptionButton.qml (+1/-1) ViewFinderOverlay.qml (+170/-4) ViewFinderView.qml (+15/-16) manifest.json.in (+1/-1) tests/autopilot/camera_app/emulators/main_window.py (+20/-1) tests/autopilot/camera_app/tests/test_capture.py (+27/-27) tests/autopilot/camera_app/tests/test_zoom.py (+47/-0) |
To merge this branch: | bzr merge lp:~fboucault/camera-app/fix_test_video_resolution |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email: mp+278981@code.launchpad.net |
Commit message
Fix AP test camera_
Description of the change
To post a comment you must log in.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'BottomEdgeIndicators.qml' |
2 | --- BottomEdgeIndicators.qml 2015-10-22 12:21:14 +0000 |
3 | +++ BottomEdgeIndicators.qml 2015-11-30 15:11:56 +0000 |
4 | @@ -84,7 +84,7 @@ |
5 | id: indicatorIcon |
6 | anchors.fill: parent |
7 | color: "white" |
8 | - name: modelData && modelData.isToggle ? modelData.icon : modelData.get(model.selectedIndex).icon |
9 | + name: modelData && modelData.isToggle ? modelData.icon : (modelData.get(model.selectedIndex) ? modelData.get(model.selectedIndex).icon : "") |
10 | source: name ? "image://theme/%1".arg(name) : (modelData.iconSource || "") |
11 | visible: source != "" |
12 | } |
13 | |
14 | === modified file 'CameraApp/advancedcamerasettings.cpp' |
15 | --- CameraApp/advancedcamerasettings.cpp 2015-10-27 09:17:27 +0000 |
16 | +++ CameraApp/advancedcamerasettings.cpp 2015-11-30 15:11:56 +0000 |
17 | @@ -26,6 +26,10 @@ |
18 | #include <QtMultimedia/QVideoDeviceSelectorControl> |
19 | #include <QtMultimedia/QCameraFlashControl> |
20 | #include <QtMultimedia/QCameraExposureControl> |
21 | +#include <QGuiApplication> |
22 | +#include <QScreen> |
23 | + |
24 | +#include <cmath> |
25 | |
26 | // Definition of this enum value is duplicated in qtubuntu-camera |
27 | static const QCameraExposure::ExposureMode ExposureHdr = static_cast<QCameraExposure::ExposureMode>(QCameraExposure::ExposureModeVendor + 1); |
28 | @@ -222,6 +226,12 @@ |
29 | QObject::connect(m_cameraControl, |
30 | SIGNAL(captureModeChanged(QCamera::CaptureModes)), |
31 | this, SIGNAL(resolutionChanged())); |
32 | + QObject::connect(m_cameraControl, |
33 | + SIGNAL(captureModeChanged(QCamera::CaptureModes)), |
34 | + this, SIGNAL(maximumResolutionChanged())); |
35 | + QObject::connect(m_cameraControl, |
36 | + SIGNAL(captureModeChanged(QCamera::CaptureModes)), |
37 | + this, SIGNAL(fittingResolutionChanged())); |
38 | } |
39 | |
40 | m_cameraFlashControl = flashControlFromCamera(m_camera); |
41 | @@ -237,6 +247,8 @@ |
42 | m_videoEncoderControl = videoEncoderControlFromCamera(m_camera); |
43 | |
44 | Q_EMIT resolutionChanged(); |
45 | + Q_EMIT maximumResolutionChanged(); |
46 | + Q_EMIT fittingResolutionChanged(); |
47 | Q_EMIT hasFlashChanged(); |
48 | Q_EMIT hasHdrChanged(); |
49 | Q_EMIT hdrEnabledChanged(); |
50 | @@ -260,6 +272,8 @@ |
51 | } |
52 | Q_EMIT activeCameraIndexChanged(); |
53 | Q_EMIT resolutionChanged(); |
54 | + Q_EMIT maximumResolutionChanged(); |
55 | + Q_EMIT fittingResolutionChanged(); |
56 | Q_EMIT hasFlashChanged(); |
57 | Q_EMIT videoSupportedResolutionsChanged(); |
58 | } |
59 | @@ -277,6 +291,117 @@ |
60 | return QSize(); |
61 | } |
62 | |
63 | +QSize AdvancedCameraSettings::imageCaptureResolution() const |
64 | +{ |
65 | + if (m_imageEncoderControl != 0) { |
66 | + return m_imageEncoderControl->imageSettings().resolution(); |
67 | + } |
68 | + |
69 | + return QSize(); |
70 | +} |
71 | + |
72 | +QSize AdvancedCameraSettings::videoRecorderResolution() const |
73 | +{ |
74 | + if (m_videoEncoderControl != 0) { |
75 | + return m_videoEncoderControl->videoSettings().resolution(); |
76 | + } |
77 | + |
78 | + return QSize(); |
79 | +} |
80 | + |
81 | +QSize AdvancedCameraSettings::maximumResolution() const |
82 | +{ |
83 | + if (m_imageEncoderControl) { |
84 | + QList<QSize> sizes = m_imageEncoderControl->supportedResolutions( |
85 | + m_imageEncoderControl->imageSettings()); |
86 | + |
87 | + QSize maximumSize; |
88 | + long maximumPixels = 0; |
89 | + |
90 | + QList<QSize>::const_iterator it = sizes.begin(); |
91 | + while (it != sizes.end()) { |
92 | + const long pixels = ((long)((*it).width())) * ((long)((*it).height())); |
93 | + if (pixels > maximumPixels) { |
94 | + maximumSize = *it; |
95 | + maximumPixels = pixels; |
96 | + } |
97 | + ++it; |
98 | + } |
99 | + |
100 | + return maximumSize; |
101 | + } |
102 | + |
103 | + return QSize(); |
104 | +} |
105 | + |
106 | +float AdvancedCameraSettings::getScreenAspectRatio() const |
107 | +{ |
108 | + float screenAspectRatio; |
109 | + QScreen *screen = QGuiApplication::primaryScreen(); |
110 | + Q_ASSERT(!screen); |
111 | + const int kScreenWidth = screen->geometry().width(); |
112 | + const int kScreenHeight = screen->geometry().height(); |
113 | + Q_ASSERT(kScreenWidth > 0 && kScreenHeight > 0); |
114 | + |
115 | + screenAspectRatio = (kScreenWidth > kScreenHeight) ? |
116 | + ((float)kScreenWidth / (float)kScreenHeight) : ((float)kScreenHeight / (float)kScreenWidth); |
117 | + |
118 | + return screenAspectRatio; |
119 | +} |
120 | + |
121 | +QSize AdvancedCameraSettings::fittingResolution() const |
122 | +{ |
123 | + QList<float> prioritizedAspectRatios; |
124 | + prioritizedAspectRatios.append(getScreenAspectRatio()); |
125 | + const float backAspectRatios[4] = { 16.0f/9.0f, 3.0f/2.0f, 4.0f/3.0f, 5.0f/4.0f }; |
126 | + for (int i=0; i<4; ++i) { |
127 | + if (!prioritizedAspectRatios.contains(backAspectRatios[i])) { |
128 | + prioritizedAspectRatios.append(backAspectRatios[i]); |
129 | + } |
130 | + } |
131 | + |
132 | + if (m_imageEncoderControl) { |
133 | + QList<QSize> sizes = m_imageEncoderControl->supportedResolutions( |
134 | + m_imageEncoderControl->imageSettings()); |
135 | + |
136 | + QSize optimalSize; |
137 | + long optimalPixels = 0; |
138 | + |
139 | + if (!sizes.empty()) { |
140 | + float aspectRatio; |
141 | + |
142 | + // Loop over all reported camera resolutions until we find the highest |
143 | + // one that matches the current prioritized aspect ratio. If it doesn't |
144 | + // find one on the current aspect ration, it selects the next ratio and |
145 | + // tries again. |
146 | + QList<float>::const_iterator ratioIt = prioritizedAspectRatios.begin(); |
147 | + while (ratioIt != prioritizedAspectRatios.end()) { |
148 | + // Don't update the aspect ratio when using this function for finding |
149 | + // the optimal thumbnail size as it will affect the preview window size |
150 | + aspectRatio = (*ratioIt); |
151 | + |
152 | + QList<QSize>::const_iterator it = sizes.begin(); |
153 | + while (it != sizes.end()) { |
154 | + const float ratio = (float)(*it).width() / (float)(*it).height(); |
155 | + const long pixels = ((long)((*it).width())) * ((long)((*it).height())); |
156 | + const float EPSILON = 0.02; |
157 | + if (fabs(ratio - aspectRatio) < EPSILON && pixels > optimalPixels) { |
158 | + optimalSize = *it; |
159 | + optimalPixels = pixels; |
160 | + } |
161 | + ++it; |
162 | + } |
163 | + if (optimalPixels > 0) break; |
164 | + ++ratioIt; |
165 | + } |
166 | + } |
167 | + |
168 | + return optimalSize; |
169 | + } |
170 | + |
171 | + return QSize(); |
172 | +} |
173 | + |
174 | QStringList AdvancedCameraSettings::videoSupportedResolutions() const |
175 | { |
176 | if (m_videoEncoderControl) { |
177 | |
178 | === modified file 'CameraApp/advancedcamerasettings.h' |
179 | --- CameraApp/advancedcamerasettings.h 2014-12-08 19:12:52 +0000 |
180 | +++ CameraApp/advancedcamerasettings.h 2015-11-30 15:11:56 +0000 |
181 | @@ -40,6 +40,10 @@ |
182 | Q_PROPERTY (int activeCameraIndex READ activeCameraIndex WRITE setActiveCameraIndex |
183 | NOTIFY activeCameraIndexChanged) |
184 | Q_PROPERTY (QSize resolution READ resolution NOTIFY resolutionChanged) |
185 | + Q_PROPERTY (QSize imageCaptureResolution READ imageCaptureResolution) |
186 | + Q_PROPERTY (QSize videoRecorderResolution READ videoRecorderResolution) |
187 | + Q_PROPERTY (QSize maximumResolution READ maximumResolution NOTIFY maximumResolutionChanged) |
188 | + Q_PROPERTY (QSize fittingResolution READ fittingResolution NOTIFY fittingResolutionChanged) |
189 | Q_PROPERTY (QStringList videoSupportedResolutions READ videoSupportedResolutions NOTIFY videoSupportedResolutionsChanged) |
190 | Q_PROPERTY (bool hasFlash READ hasFlash NOTIFY hasFlashChanged) |
191 | Q_PROPERTY (bool hdrEnabled READ hdrEnabled WRITE setHdrEnabled NOTIFY hdrEnabledChanged) |
192 | @@ -53,6 +57,11 @@ |
193 | void setCamera(QObject* camera); |
194 | void setActiveCameraIndex(int index); |
195 | QSize resolution() const; |
196 | + QSize imageCaptureResolution() const; |
197 | + QSize videoRecorderResolution() const; |
198 | + QSize maximumResolution() const; |
199 | + QSize fittingResolution() const; |
200 | + float getScreenAspectRatio() const; |
201 | QStringList videoSupportedResolutions() const; |
202 | bool hasFlash() const; |
203 | bool hasHdr() const; |
204 | @@ -66,6 +75,8 @@ |
205 | void cameraChanged(); |
206 | void activeCameraIndexChanged(); |
207 | void resolutionChanged(); |
208 | + void maximumResolutionChanged(); |
209 | + void fittingResolutionChanged(); |
210 | void hasFlashChanged(); |
211 | void hasHdrChanged(); |
212 | void hdrEnabledChanged(); |
213 | |
214 | === modified file 'OptionButton.qml' |
215 | --- OptionButton.qml 2015-10-22 12:21:14 +0000 |
216 | +++ OptionButton.qml 2015-11-30 15:11:56 +0000 |
217 | @@ -23,7 +23,7 @@ |
218 | property var model |
219 | property string settingsProperty: model.settingsProperty |
220 | |
221 | - iconName: model.isToggle || !model.get(model.selectedIndex).icon ? model.icon : model.get(model.selectedIndex).icon |
222 | + iconName: !model.get(model.selectedIndex).icon ? model.icon : model.get(model.selectedIndex).icon |
223 | iconSource: (model && model.iconSource) ? model.iconSource : "" |
224 | on: model.isToggle ? model.get(model.selectedIndex).value : true |
225 | enabled: model.available |
226 | |
227 | === modified file 'ViewFinderOverlay.qml' |
228 | --- ViewFinderOverlay.qml 2015-10-22 12:21:14 +0000 |
229 | +++ ViewFinderOverlay.qml 2015-11-30 15:11:56 +0000 |
230 | @@ -50,6 +50,13 @@ |
231 | property bool gridEnabled: false |
232 | property bool preferRemovableStorage: false |
233 | property string videoResolution: "1920x1080" |
234 | + property bool playShutterSound: true |
235 | + // FIXME: stores the resolution selected for 2 cameras. Instead it should: |
236 | + // - support any number of cameras |
237 | + // - not rely on the camera index but on Camera.deviceId |
238 | + // Ref.: http://doc.qt.io/qt-5/qml-qtmultimedia-camera.html#deviceId-prop |
239 | + property string photoResolution0 |
240 | + property string photoResolution1 |
241 | |
242 | onFlashModeChanged: if (flashMode != Camera.FlashOff) hdrEnabled = false; |
243 | onHdrEnabledChanged: if (hdrEnabled) flashMode = Camera.FlashOff |
244 | @@ -87,12 +94,83 @@ |
245 | value: settings.videoResolution |
246 | } |
247 | |
248 | + Binding { |
249 | + target: camera.imageCapture |
250 | + property: "resolution" |
251 | + value: settings["photoResolution" + camera.advanced.activeCameraIndex] |
252 | + } |
253 | + |
254 | + Connections { |
255 | + target: camera.imageCapture |
256 | + onResolutionChanged: { |
257 | + // FIXME: this is a necessary workaround because: |
258 | + // - Neither camera.viewfinder.resolution nor camera.advanced.resolution |
259 | + // emit a changed signal when the underlying AalViewfinderSettingsControl's |
260 | + // resolution changes |
261 | + // - we know that qtubuntu-camera changes the resolution of the |
262 | + // viewfinder automatically when the capture resolution is set |
263 | + // - we need camera.viewfinder.resolution to hold the right |
264 | + // value |
265 | + camera.viewfinder.resolution = camera.advanced.resolution; |
266 | + } |
267 | + } |
268 | + |
269 | + Connections { |
270 | + target: camera.videoRecorder |
271 | + onResolutionChanged: { |
272 | + // FIXME: see workaround setting camera.viewfinder.resolution above |
273 | + camera.viewfinder.resolution = camera.advanced.resolution; |
274 | + } |
275 | + } |
276 | + |
277 | + Connections { |
278 | + target: camera |
279 | + onCaptureModeChanged: { |
280 | + // FIXME: see workaround setting camera.viewfinder.resolution above |
281 | + camera.viewfinder.resolution = camera.advanced.resolution; |
282 | + } |
283 | + } |
284 | + |
285 | function resolutionToLabel(resolution) { |
286 | // takes in a resolution string (e.g. "1920x1080") and returns a nicer |
287 | // form of it for display in the UI: "1080p" |
288 | return resolution.split("x").pop() + "p"; |
289 | } |
290 | |
291 | + function sizeToString(size) { |
292 | + return size.width + "x" + size.height; |
293 | + } |
294 | + |
295 | + function stringToSize(resolution) { |
296 | + var r = resolution.split("x"); |
297 | + return Qt.size(r[0], r[1]); |
298 | + } |
299 | + |
300 | + function sizeToAspectRatio(size) { |
301 | + var ratio = Math.max(size.width, size.height) / Math.min(size.width, size.height); |
302 | + var maxDenominator = 12; |
303 | + var epsilon; |
304 | + var numerator; |
305 | + var denominator; |
306 | + var bestDenominator; |
307 | + var bestEpsilon = 10000; |
308 | + for (denominator = 2; denominator <= maxDenominator; denominator++) { |
309 | + numerator = ratio * denominator; |
310 | + epsilon = Math.abs(Math.round(numerator) - numerator); |
311 | + if (epsilon < bestEpsilon) { |
312 | + bestEpsilon = epsilon; |
313 | + bestDenominator = denominator; |
314 | + } |
315 | + } |
316 | + numerator = Math.round(ratio * bestDenominator); |
317 | + return "%1:%2".arg(numerator).arg(bestDenominator); |
318 | + } |
319 | + |
320 | + function sizeToMegapixels(size) { |
321 | + var megapixels = (size.width * size.height) / 1000000; |
322 | + return parseFloat(megapixels.toFixed(1)) |
323 | + } |
324 | + |
325 | function updateVideoResolutionOptions() { |
326 | // Clear and refill videoResolutionOptionsModel with available resolutions |
327 | // Try to only display well known resolutions: 1080p, 720p and 480p |
328 | @@ -110,25 +188,71 @@ |
329 | } |
330 | |
331 | // If resolution setting chosen is not supported select the highest available resolution |
332 | - if (supported.indexOf(settings.videoResolution) == -1) { |
333 | + if (supported.length > 0 && supported.indexOf(settings.videoResolution) == -1) { |
334 | settings.videoResolution = supported[supported.length - 1]; |
335 | } |
336 | } |
337 | |
338 | + function updatePhotoResolutionOptions() { |
339 | + // Clear and refill photoResolutionOptionsModel with available resolutions |
340 | + photoResolutionOptionsModel.clear(); |
341 | + |
342 | + var optionMaximum = {"icon": "", |
343 | + "label": "%1 (%2MP)".arg(sizeToAspectRatio(camera.advanced.maximumResolution)) |
344 | + .arg(sizeToMegapixels(camera.advanced.maximumResolution)), |
345 | + "value": sizeToString(camera.advanced.maximumResolution)}; |
346 | + |
347 | + var optionFitting = {"icon": "", |
348 | + "label": "%1 (%2MP)".arg(sizeToAspectRatio(camera.advanced.fittingResolution)) |
349 | + .arg(sizeToMegapixels(camera.advanced.fittingResolution)), |
350 | + "value": sizeToString(camera.advanced.fittingResolution)}; |
351 | + |
352 | + photoResolutionOptionsModel.insert(0, optionMaximum); |
353 | + if (camera.advanced.fittingResolution != camera.advanced.maximumResolution) { |
354 | + photoResolutionOptionsModel.insert(1, optionFitting); |
355 | + } |
356 | + |
357 | + var photoResolution = settings["photoResolution" + camera.advanced.activeCameraIndex]; |
358 | + // If resolution setting chosen is not supported select the fitting resolution |
359 | + if (photoResolution != optionFitting.value && |
360 | + photoResolution != optionMaximum.value) { |
361 | + settings["photoResolution" + camera.advanced.activeCameraIndex] = optionFitting.value; |
362 | + } |
363 | + } |
364 | + |
365 | Component.onCompleted: { |
366 | + camera.cameraState = Camera.LoadedState; |
367 | updateVideoResolutionOptions(); |
368 | + updatePhotoResolutionOptions(); |
369 | + // FIXME: see workaround setting camera.viewfinder.resolution above |
370 | + camera.viewfinder.resolution = camera.advanced.resolution; |
371 | + camera.cameraState = Camera.ActiveState; |
372 | } |
373 | |
374 | Connections { |
375 | target: camera.advanced |
376 | onVideoSupportedResolutionsChanged: updateVideoResolutionOptions(); |
377 | + onFittingResolutionChanged: updatePhotoResolutionOptions(); |
378 | + onMaximumResolutionChanged: updatePhotoResolutionOptions(); |
379 | } |
380 | |
381 | Connections { |
382 | target: camera.advanced |
383 | onActiveCameraIndexChanged: { |
384 | + var hasPhotoResolutionSetting = (settings["photoResolution" + camera.advanced.activeCameraIndex] != "") |
385 | + // FIXME: use camera.advanced.imageCaptureResolution instead of camera.imageCapture.resolution |
386 | + // because the latter is not updated when the backend changes the resolution |
387 | + settings["photoResolution" + camera.advanced.activeCameraIndex] = sizeToString(camera.advanced.imageCaptureResolution); |
388 | + settings.videoResolution = sizeToString(camera.advanced.videoRecorderResolution); |
389 | + updatePhotoResolutionOptions(); |
390 | updateVideoResolutionOptions(); |
391 | - camera.videoRecorder.resolution = settings.videoResolution; |
392 | + // FIXME: see workaround setting camera.viewfinder.resolution above |
393 | + camera.viewfinder.resolution = camera.advanced.resolution; |
394 | + |
395 | + // If no resolution has ever been chosen, select the one that fits the screen |
396 | + if (!hasPhotoResolutionSetting) { |
397 | + settings["photoResolution" + camera.advanced.activeCameraIndex] = sizeToString(camera.advanced.fittingResolution); |
398 | + } |
399 | } |
400 | } |
401 | |
402 | @@ -280,7 +404,7 @@ |
403 | property bool isToggle: true |
404 | property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled) |
405 | property bool available: camera.advanced.hasHdr |
406 | - property bool visible: true |
407 | + property bool visible: camera.captureMode === Camera.CaptureStillImage |
408 | property bool showInIndicators: true |
409 | |
410 | ListElement { |
411 | @@ -404,6 +528,41 @@ |
412 | property bool available: true |
413 | property bool visible: camera.captureMode == Camera.CaptureVideo |
414 | property bool showInIndicators: false |
415 | + }, |
416 | + ListModel { |
417 | + id: shutterSoundOptionsModel |
418 | + |
419 | + property string settingsProperty: "playShutterSound" |
420 | + property string icon: "" |
421 | + property string label: "" |
422 | + property bool isToggle: true |
423 | + property int selectedIndex: bottomEdge.indexForValue(shutterSoundOptionsModel, settings.playShutterSound) |
424 | + property bool available: true |
425 | + property bool visible: camera.captureMode === Camera.CaptureStillImage |
426 | + property bool showInIndicators: false |
427 | + |
428 | + ListElement { |
429 | + icon: "audio-volume-high" |
430 | + label: QT_TR_NOOP("On") |
431 | + value: true |
432 | + } |
433 | + ListElement { |
434 | + icon: "audio-volume-muted" |
435 | + label: QT_TR_NOOP("Off") |
436 | + value: false |
437 | + } |
438 | + }, |
439 | + ListModel { |
440 | + id: photoResolutionOptionsModel |
441 | + |
442 | + property string settingsProperty: "photoResolution" + camera.advanced.activeCameraIndex |
443 | + property string icon: "" |
444 | + property string label: sizeToAspectRatio(stringToSize(settings[settingsProperty])) |
445 | + property bool isToggle: false |
446 | + property int selectedIndex: bottomEdge.indexForValue(photoResolutionOptionsModel, settings[settingsProperty]) |
447 | + property bool available: true |
448 | + property bool visible: camera.captureMode == Camera.CaptureStillImage |
449 | + property bool showInIndicators: false |
450 | } |
451 | ] |
452 | |
453 | @@ -571,18 +730,25 @@ |
454 | function completeSwitch() { |
455 | viewFinderSwitcherAnimation.restart(); |
456 | camera.switchInProgress = false; |
457 | + zoomControl.value = camera.currentZoom; |
458 | } |
459 | |
460 | function changeRecordMode() { |
461 | if (camera.captureMode == Camera.CaptureVideo) camera.videoRecorder.stop() |
462 | camera.captureMode = (camera.captureMode == Camera.CaptureVideo) ? Camera.CaptureStillImage : Camera.CaptureVideo |
463 | + zoomControl.value = camera.currentZoom |
464 | + } |
465 | + |
466 | + Connections { |
467 | + target: Qt.application |
468 | + onActiveChanged: if (active) zoomControl.value = camera.currentZoom |
469 | } |
470 | |
471 | Timer { |
472 | id: shootingTimer |
473 | repeat: true |
474 | triggeredOnStart: true |
475 | - |
476 | + |
477 | property int remainingSecs: 0 |
478 | |
479 | onTriggered: { |
480 | |
481 | === modified file 'ViewFinderView.qml' |
482 | --- ViewFinderView.qml 2015-11-16 15:54:25 +0000 |
483 | +++ ViewFinderView.qml 2015-11-30 15:11:56 +0000 |
484 | @@ -36,6 +36,7 @@ |
485 | Camera { |
486 | id: camera |
487 | captureMode: Camera.CaptureStillImage |
488 | + cameraState: Camera.UnloadedState |
489 | StateSaver.properties: "captureMode" |
490 | |
491 | function manualFocus(x, y) { |
492 | @@ -67,10 +68,6 @@ |
493 | StateSaver.properties: "activeCameraIndex" |
494 | } |
495 | |
496 | - Component.onCompleted: { |
497 | - camera.start(); |
498 | - } |
499 | - |
500 | /* Use only digital zoom for now as it's what phone cameras mostly use. |
501 | TODO: if optical zoom is available, maximumZoom should be the combined |
502 | range of optical and digital zoom and currentZoom should adjust the two |
503 | @@ -121,12 +118,14 @@ |
504 | target: Qt.application |
505 | onActiveChanged: { |
506 | if (Qt.application.active) { |
507 | - camera.start() |
508 | + if (camera.cameraState == Camera.LoadedState) { |
509 | + camera.cameraState = Camera.ActiveState; |
510 | + } |
511 | } else if (!application.desktopMode) { |
512 | if (camera.videoRecorder.recorderState == CameraRecorder.RecordingState) { |
513 | camera.videoRecorder.stop(); |
514 | } |
515 | - camera.stop() |
516 | + camera.cameraState = Camera.LoadedState; |
517 | } |
518 | } |
519 | } |
520 | @@ -250,16 +249,16 @@ |
521 | axis.x: 0; axis.y: 1; axis.z: 0 |
522 | angle: application.desktopMode ? 180 : 0 |
523 | } |
524 | - |
525 | - ViewFinderGeometry { |
526 | - id: viewFinderGeometry |
527 | - anchors.centerIn: parent |
528 | - |
529 | - cameraResolution: camera.advanced.resolution |
530 | - viewFinderHeight: viewFinder.height |
531 | - viewFinderWidth: viewFinder.width |
532 | - viewFinderOrientation: viewFinder.orientation |
533 | - } |
534 | + } |
535 | + |
536 | + ViewFinderGeometry { |
537 | + id: viewFinderGeometry |
538 | + anchors.centerIn: parent |
539 | + |
540 | + cameraResolution: camera.viewfinder.resolution |
541 | + viewFinderHeight: viewFinder.height |
542 | + viewFinderWidth: viewFinder.width |
543 | + viewFinderOrientation: viewFinder.orientation |
544 | } |
545 | |
546 | Item { |
547 | |
548 | === modified file 'manifest.json.in' |
549 | --- manifest.json.in 2015-11-23 15:07:14 +0000 |
550 | +++ manifest.json.in 2015-11-30 15:11:56 +0000 |
551 | @@ -1,6 +1,6 @@ |
552 | { |
553 | "description": "An application to take pictures and videos with the device cameras", |
554 | - "framework": "ubuntu-sdk-15.04.2", |
555 | + "framework": "ubuntu-sdk-15.04.3", |
556 | "architecture": "@CLICK_ARCH@", |
557 | "hooks": { |
558 | "camera": { |
559 | |
560 | === modified file 'tests/autopilot/camera_app/emulators/main_window.py' |
561 | --- tests/autopilot/camera_app/emulators/main_window.py 2015-11-20 15:01:02 +0000 |
562 | +++ tests/autopilot/camera_app/emulators/main_window.py 2015-11-30 15:11:56 +0000 |
563 | @@ -7,7 +7,7 @@ |
564 | |
565 | from camera_app.emulators.panel import Panel |
566 | from autopilot.matchers import Eventually |
567 | -from testtools.matchers import Equals |
568 | +from testtools.matchers import Equals, NotEquals |
569 | |
570 | |
571 | class MainWindow(object): |
572 | @@ -163,3 +163,22 @@ |
573 | tx, ty, (tx + view_switcher.width // 2), ty, rate=1) |
574 | viewfinder = self.get_viewfinder() |
575 | testCase.assertThat(viewfinder.inView, Eventually(Equals(True))) |
576 | + |
577 | + def switch_cameras(self): |
578 | + # Swap cameras and wait for camera to settle |
579 | + shoot_button = self.get_exposure_button() |
580 | + swap_camera_button = self.get_swap_camera_button() |
581 | + self.app.pointing_device.move_to_object(swap_camera_button) |
582 | + self.app.pointing_device.click() |
583 | + shoot_button.enabled.wait_for(True) |
584 | + |
585 | + def switch_recording_mode(self): |
586 | + record_control = self.get_record_control() |
587 | + |
588 | + # Wait for the camera overlay to be loaded |
589 | + record_control.enabled.wait_for(True) |
590 | + record_control.width.wait_for(NotEquals(0)) |
591 | + record_control.height.wait_for(NotEquals(0)) |
592 | + |
593 | + self.app.pointing_device.move_to_object(record_control) |
594 | + self.app.pointing_device.click() |
595 | |
596 | === modified file 'tests/autopilot/camera_app/tests/test_capture.py' |
597 | --- tests/autopilot/camera_app/tests/test_capture.py 2015-10-05 13:14:12 +0000 |
598 | +++ tests/autopilot/camera_app/tests/test_capture.py 2015-11-30 15:11:56 +0000 |
599 | @@ -90,13 +90,11 @@ |
600 | |
601 | """ |
602 | # Get all the elements |
603 | - record_control = self.main_window.get_record_control() |
604 | stop_watch = self.main_window.get_stop_watch() |
605 | exposure_button = self.main_window.get_exposure_button() |
606 | |
607 | # Click the record button to toggle photo/video mode |
608 | - self.pointing_device.move_to_object(record_control) |
609 | - self.pointing_device.click() |
610 | + self.main_window.switch_recording_mode() |
611 | |
612 | # Before recording the stop watch should read zero recording time |
613 | # and not be visible anyway. |
614 | @@ -137,8 +135,7 @@ |
615 | # Now stop the video and go back to picture mode and check if |
616 | # everything resets itself to previous states |
617 | self.pointing_device.click() |
618 | - self.pointing_device.move_to_object(record_control) |
619 | - self.pointing_device.click() |
620 | + self.main_window.switch_recording_mode() |
621 | |
622 | self.assertThat(stop_watch.opacity, Eventually(Equals(0.0))) |
623 | |
624 | @@ -268,7 +265,7 @@ |
625 | """Test recording videos at a set resolution and switching cameras""" |
626 | def test_video_resolution_setting_switching_cameras(self): |
627 | # switch to video recording and empty video folder |
628 | - self.switch_to_video_recording() |
629 | + self.main_window.switch_recording_mode() |
630 | self.delete_all_videos() |
631 | |
632 | # select the first resolution for the current camera |
633 | @@ -277,32 +274,25 @@ |
634 | self.set_video_resolution(initial_resolution) |
635 | |
636 | # switch cameras and select the last resolution for the current camera |
637 | - self.switch_cameras() |
638 | + self.main_window.switch_cameras() |
639 | resolutions = self.get_available_video_resolutions() |
640 | expected_resolution = resolutions[-1] |
641 | self.assertThat(expected_resolution, NotEquals(initial_resolution)) |
642 | self.set_video_resolution(expected_resolution) |
643 | |
644 | # switch back to the initial camera and record a video |
645 | - self.switch_cameras() |
646 | + self.main_window.switch_cameras() |
647 | self.record_video(2) |
648 | video_file = self.get_first_video() |
649 | height = self.read_video_height(video_file) |
650 | + expected_resolution = self.get_selected_video_resolution() |
651 | expected_height = self.height_from_resolution_label( |
652 | expected_resolution) |
653 | self.assertThat(height, Equals(expected_height)) |
654 | |
655 | - def switch_cameras(self): |
656 | - # Swap cameras and wait for camera to settle |
657 | - shoot_button = self.main_window.get_exposure_button() |
658 | - swap_camera_button = self.main_window.get_swap_camera_button() |
659 | - self.pointing_device.move_to_object(swap_camera_button) |
660 | - self.pointing_device.click() |
661 | - self.assertThat(shoot_button.enabled, Eventually(Equals(True))) |
662 | - |
663 | """Test recording videos at various resolutions""" |
664 | def test_video_resolution_setting(self): |
665 | - self.switch_to_video_recording() |
666 | + self.main_window.switch_recording_mode() |
667 | resolutions = self.get_available_video_resolutions() |
668 | |
669 | for resolution_label in resolutions: |
670 | @@ -316,16 +306,6 @@ |
671 | self.assertThat(height, Equals(expected_height)) |
672 | self.dismiss_first_photo_hint() |
673 | |
674 | - def switch_to_video_recording(self): |
675 | - record_control = self.main_window.get_record_control() |
676 | - # Wait for the camera overlay to be loaded |
677 | - self.assertThat(record_control.enabled, Eventually(Equals(True))) |
678 | - self.assertThat(record_control.width, Eventually(NotEquals(0))) |
679 | - self.assertThat(record_control.height, Eventually(NotEquals(0))) |
680 | - |
681 | - self.pointing_device.move_to_object(record_control) |
682 | - self.pointing_device.click() |
683 | - |
684 | def get_available_video_resolutions(self): |
685 | # open bottom edge |
686 | bottom_edge = self.main_window.get_bottom_edge() |
687 | @@ -346,6 +326,26 @@ |
688 | bottom_edge.close() |
689 | return resolutions |
690 | |
691 | + def get_selected_video_resolution(self): |
692 | + # open bottom edge |
693 | + bottom_edge = self.main_window.get_bottom_edge() |
694 | + bottom_edge.open() |
695 | + |
696 | + # open video resolution option value selector showing the possible |
697 | + # values |
698 | + video_resolution_button = ( |
699 | + self.main_window.get_video_resolution_button()) |
700 | + self.pointing_device.move_to_object(video_resolution_button) |
701 | + self.pointing_device.click() |
702 | + option_value_selector = self.main_window.get_option_value_selector() |
703 | + self.assertThat( |
704 | + option_value_selector.visible, Eventually(Equals(True))) |
705 | + optionButtons = option_value_selector.select_many("OptionValueButton") |
706 | + resolutions = [button.label for button in optionButtons if button.selected] |
707 | + |
708 | + bottom_edge.close() |
709 | + return resolutions[0] |
710 | + |
711 | def delete_all_videos(self): |
712 | video_files = os.listdir(self.videos_dir) |
713 | for f in video_files: |
714 | |
715 | === modified file 'tests/autopilot/camera_app/tests/test_zoom.py' |
716 | --- tests/autopilot/camera_app/tests/test_zoom.py 2015-04-29 15:56:44 +0000 |
717 | +++ tests/autopilot/camera_app/tests/test_zoom.py 2015-11-30 15:11:56 +0000 |
718 | @@ -68,3 +68,50 @@ |
719 | |
720 | self.pointing_device.drag(tx, ty, (tx - zoom_control.width), ty) |
721 | self.assertThat(zoom_control.value, Eventually(Equals(1.0))) |
722 | + |
723 | + """Tests zoom is reset to minimum on camera switch""" |
724 | + def test_zoom_reset_on_camera_change(self): |
725 | + zoom_control = self.main_window.get_zoom_control() |
726 | + zoom_slider = self.main_window.get_zoom_slider() |
727 | + |
728 | + self.activate_zoom() |
729 | + x, y, w, h = zoom_slider.globalRect |
730 | + tx = x + (w // 2) |
731 | + ty = y + (h // 2) |
732 | + self.pointing_device.drag(tx, ty, (tx + zoom_control.width), ty) |
733 | + self.assertThat( |
734 | + zoom_control.value, Eventually(Equals(zoom_control.maximumValue))) |
735 | + |
736 | + self.main_window.switch_cameras() |
737 | + self.assertThat( |
738 | + zoom_control.value, Eventually(Equals(zoom_control.minimumValue))) |
739 | + |
740 | + self.activate_zoom() |
741 | + self.pointing_device.drag(tx, ty, (tx + zoom_control.width), ty) |
742 | + self.assertThat( |
743 | + zoom_control.value, Eventually(Equals(zoom_control.maximumValue))) |
744 | + |
745 | + self.main_window.switch_cameras() |
746 | + self.assertThat( |
747 | + zoom_control.value, Eventually(Equals(zoom_control.minimumValue))) |
748 | + |
749 | + """Tests zoom is reset to minimum on recording mode switch""" |
750 | + def test_zoom_reset_on_recording_mode_change(self): |
751 | + zoom_control = self.main_window.get_zoom_control() |
752 | + zoom_slider = self.main_window.get_zoom_slider() |
753 | + |
754 | + self.activate_zoom() |
755 | + x, y, w, h = zoom_slider.globalRect |
756 | + tx = x + (w // 2) |
757 | + ty = y + (h // 2) |
758 | + self.pointing_device.drag(tx, ty, (tx + zoom_control.width), ty) |
759 | + self.assertThat( |
760 | + zoom_control.value, Eventually(Equals(zoom_control.maximumValue))) |
761 | + |
762 | + self.main_window.switch_recording_mode() |
763 | + self.assertThat( |
764 | + zoom_control.value, Eventually(Equals(zoom_control.minimumValue))) |
765 | + |
766 | + # Ideally we should test the same thing when switching back to photo |
767 | + # mode, however due to http://pad.lv/1191088 zooming when recording |
768 | + # video is disabled, so adding that test is pointless until fixed. |