Merge lp:~renatofilho/mediaplayer-app/fix-1116768 into lp:mediaplayer-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Gustavo Pichorim Boiko
Approved revision: 74
Merged at revision: 69
Proposed branch: lp:~renatofilho/mediaplayer-app/fix-1116768
Merge into: lp:mediaplayer-app
Diff against target: 365 lines (+171/-40)
13 files modified
CMakeLists.txt (+3/-0)
src/qml/player/Controls.qml (+5/-5)
src/qml/player/SceneFrame.qml (+2/-1)
src/thumbnail-pipeline-gst.cpp (+58/-17)
src/thumbnail-pipeline-gst.h (+5/-2)
src/thumbnail-provider.cpp (+3/-8)
tests/CMakeLists.txt (+3/-1)
tests/autopilot/mediaplayer_app/tests/test_player_with_video.py (+1/-6)
tests/images/CMakeLists.txt (+1/-0)
tests/unittest/CMakeLists.txt (+30/-0)
tests/unittest/test-config.h.in (+1/-0)
tests/unittest/thumbnailtest.cpp (+57/-0)
tests/videos/CMakeLists.txt (+2/-0)
To merge this branch: bzr merge lp:~renatofilho/mediaplayer-app/fix-1116768
Reviewer Review Type Date Requested Status
Gustavo Pichorim Boiko (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+157104@code.launchpad.net

Commit message

Avoid black thumbnails on scene selector.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
69. By Renato Araujo Oliveira Filho

Fixed scene selector for small videos.
Returned a invalid QImage if a good frame was not found, during the thumbnail generation.

70. By Renato Araujo Oliveira Filho

Removed invalid comment.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
71. By Renato Araujo Oliveira Filho

uses standard deviation to discovery a good image for thumbnail.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
72. By Renato Araujo Oliveira Filho

Created unit test to check for valid thumbnails.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

Please use data-driven tests for trying the isMeaningful functions instead of one function per image.

review: Needs Fixing
73. By Renato Araujo Oliveira Filho

Make sure that image and videos sample data get processed first then unittest project to correct export the variables.

74. By Renato Araujo Oliveira Filho

Updated test to use data-driven tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

Looks good now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-02-21 17:40:40 +0000
3+++ CMakeLists.txt 2013-04-04 22:28:27 +0000
4@@ -19,6 +19,9 @@
5
6 set(MEDIAPLAYER_DIR ${CMAKE_INSTALL_DATADIR}/media-player)
7
8+# Tests
9+enable_testing()
10+
11 add_subdirectory(data)
12 add_subdirectory(sdk)
13 add_subdirectory(src)
14
15=== modified file 'src/qml/player/Controls.qml'
16--- src/qml/player/Controls.qml 2013-03-06 15:10:16 +0000
17+++ src/qml/player/Controls.qml 2013-04-04 22:28:27 +0000
18@@ -276,12 +276,12 @@
19 _sceneSelectorModel.clear()
20 var frameSize = video.duration / 10;
21 for (var i = 0; i < 10; ++i) {
22- // TODO: discuss this with designers
23- // shift 3s to avoid black frame in the position 0
24 var pos = Math.floor(i * frameSize);
25- _sceneSelectorModel.append({"thumbnail": "image://video/" + video.source + "/" + (pos + 3000),
26- "start" : pos,
27- "duration" : frameSize})
28+ if (pos <= video.duration) {
29+ _sceneSelectorModel.append({"thumbnail": "image://video/" + video.source + "/" + pos,
30+ "start" : pos,
31+ "duration" : frameSize})
32+ }
33 }
34 }
35 }
36
37=== modified file 'src/qml/player/SceneFrame.qml'
38--- src/qml/player/SceneFrame.qml 2013-03-06 13:33:38 +0000
39+++ src/qml/player/SceneFrame.qml 2013-04-04 22:28:27 +0000
40@@ -53,13 +53,14 @@
41 }
42 }
43
44-
45 image: Image {
46 id: _image
47
48 fillMode: Image.PreserveAspectCrop
49 smooth: true
50 asynchronous: true
51+ sourceSize.width: _shape.width
52+ sourceSize.height: _shape.height
53 }
54 }
55
56
57=== modified file 'src/thumbnail-pipeline-gst.cpp'
58--- src/thumbnail-pipeline-gst.cpp 2013-02-12 14:30:57 +0000
59+++ src/thumbnail-pipeline-gst.cpp 2013-04-04 22:28:27 +0000
60@@ -16,6 +16,7 @@
61
62 #include <QDebug>
63 #include <gst/gst.h>
64+#include <math.h>
65
66 #include "thumbnail-pipeline-gst.h"
67
68@@ -152,31 +153,71 @@
69 return QImage();
70 }
71
72-QImage ThumbnailPipeline::parseImage(ThumbnailImageData *buffer)
73+QImage ThumbnailPipeline::parseImage(ThumbnailImageData *buffer) const
74 {
75 return parseImageGst(buffer);
76 }
77
78-QImage ThumbnailPipeline::request(qint64 time)
79+// use standard deviation of the histogram to discovery a good image
80+bool ThumbnailPipeline::isMeaningful(QImage img)
81+{
82+ const static int threshold = 15;
83+ const float average = (img.height() * img.width()) / 256;
84+ int histogram[256];
85+
86+ memset(histogram, 0, sizeof(int) * 256);
87+ for(int h=0, hMax = img.height(); h < hMax; h++) {
88+ for(int w=0, wMax = img.width(); w < wMax; w++) {
89+ histogram[qGray(img.pixel(w, h))]++;
90+ }
91+ }
92+
93+ float sum = 0;
94+ for(int i=0; i < 256; i++) {
95+ sum += pow(average - histogram[i], 2);
96+ }
97+
98+ return ((sqrt(sum / 256) / average) <= threshold);
99+}
100+
101+QImage ThumbnailPipeline::request(qint64 time, QSize size, bool skipBlack)
102 {
103 if (m_pipeline == 0) {
104 qWarning() << "Pipiline not ready";
105 return QImage();
106 }
107
108- gst_element_seek (m_pipeline, 1.0,
109- GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
110- GST_SEEK_TYPE_SET, time * GST_MSECOND,
111- GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
112-
113- /* And wait for this seek to complete */
114- gst_element_get_state (m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
115-
116- /* get frame */
117- ThumbnailImageData *buf = 0;
118-
119- g_signal_emit_by_name (m_pipeline, "convert-frame", m_caps, &buf);
120- QImage img = parseImage (buf);
121-
122- return img;
123+
124+ QImage firstImage;
125+ while(time < m_duration) {
126+ gst_element_seek (m_pipeline, 1.0,
127+ GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
128+ GST_SEEK_TYPE_SET, time * GST_MSECOND,
129+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
130+
131+ /* And wait for this seek to complete */
132+ gst_element_get_state (m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
133+
134+ /* get frame */
135+ ThumbnailImageData *buf = 0;
136+
137+ g_signal_emit_by_name (m_pipeline, "convert-frame", m_caps, &buf);
138+ QImage img = parseImage (buf);
139+ if (size.isValid()) {
140+ img = img.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
141+ }
142+
143+ if (skipBlack && !isMeaningful (img)) {
144+ if (firstImage.isNull()) {
145+ firstImage = img.copy();
146+ }
147+ time += 1000;
148+ continue;
149+ } else {
150+ return img;
151+ }
152+ }
153+
154+ // return the original frame if any other was found
155+ return firstImage;
156 }
157
158=== modified file 'src/thumbnail-pipeline-gst.h'
159--- src/thumbnail-pipeline-gst.h 2013-02-12 14:30:57 +0000
160+++ src/thumbnail-pipeline-gst.h 2013-04-04 22:28:27 +0000
161@@ -30,7 +30,8 @@
162 void setUri(const QString &uri);
163 QString uri() const;
164
165- QImage request(qint64 time);
166+ QImage request(qint64 time, QSize size, bool skipBlack=true);
167+ static bool isMeaningful(QImage img);
168
169 private:
170 GstElement *m_pipeline;
171@@ -41,7 +42,9 @@
172 void setup();
173 bool start();
174 void stop();
175- QImage parseImage(ThumbnailImageData *buffer);
176+
177+ QImage parseImage(ThumbnailImageData *buffer) const;
178+
179 };
180
181 #endif
182
183=== modified file 'src/thumbnail-provider.cpp'
184--- src/thumbnail-provider.cpp 2013-02-12 14:30:57 +0000
185+++ src/thumbnail-provider.cpp 2013-04-04 22:28:27 +0000
186@@ -88,15 +88,10 @@
187 if (m_cache.contains(time)) {
188 img = m_cache[time];
189 } else {
190- img = m_player->request(time).copy();
191+ img = m_player->request(time, requestedSize).copy();
192 m_cache.insert(time, img);
193 }
194
195- if (requestedSize.isValid()) {
196- *size = requestedSize;
197- return img.scaled(requestedSize);
198- } else {
199- *size = img.size();
200- return img;
201- }
202+ *size = img.size();
203+ return img;
204 }
205
206=== modified file 'tests/CMakeLists.txt'
207--- tests/CMakeLists.txt 2013-03-04 14:17:19 +0000
208+++ tests/CMakeLists.txt 2013-04-04 22:28:27 +0000
209@@ -1,2 +1,4 @@
210+add_subdirectory(videos)
211+add_subdirectory(images)
212 add_subdirectory(autopilot)
213-add_subdirectory(videos)
214+add_subdirectory(unittest)
215
216=== modified file 'tests/autopilot/mediaplayer_app/tests/test_player_with_video.py'
217--- tests/autopilot/mediaplayer_app/tests/test_player_with_video.py 2013-04-01 22:17:06 +0000
218+++ tests/autopilot/mediaplayer_app/tests/test_player_with_video.py 2013-04-04 22:28:27 +0000
219@@ -9,16 +9,11 @@
220
221 from __future__ import absolute_import
222
223-from testtools.matchers import Equals, NotEquals, GreaterThan
224+from testtools.matchers import Equals, GreaterThan
225 from autopilot.matchers import Eventually
226
227 from mediaplayer_app.tests import MediaplayerAppTestCase
228
229-import unittest
230-import time
231-import os
232-from os import path
233-
234 class TestPlayerWithVideo(MediaplayerAppTestCase):
235 """Tests the main media player features while playing a video """
236
237
238=== added directory 'tests/images'
239=== added file 'tests/images/CMakeLists.txt'
240--- tests/images/CMakeLists.txt 1970-01-01 00:00:00 +0000
241+++ tests/images/CMakeLists.txt 2013-04-04 22:28:27 +0000
242@@ -0,0 +1,1 @@
243+project(sample_images)
244
245=== added file 'tests/images/frame0.png'
246Binary files tests/images/frame0.png 1970-01-01 00:00:00 +0000 and tests/images/frame0.png 2013-04-04 22:28:27 +0000 differ
247=== added file 'tests/images/frame10.png'
248Binary files tests/images/frame10.png 1970-01-01 00:00:00 +0000 and tests/images/frame10.png 2013-04-04 22:28:27 +0000 differ
249=== added file 'tests/images/frame100.png'
250Binary files tests/images/frame100.png 1970-01-01 00:00:00 +0000 and tests/images/frame100.png 2013-04-04 22:28:27 +0000 differ
251=== added file 'tests/images/frame20.png'
252Binary files tests/images/frame20.png 1970-01-01 00:00:00 +0000 and tests/images/frame20.png 2013-04-04 22:28:27 +0000 differ
253=== added directory 'tests/unittest'
254=== added file 'tests/unittest/CMakeLists.txt'
255--- tests/unittest/CMakeLists.txt 1970-01-01 00:00:00 +0000
256+++ tests/unittest/CMakeLists.txt 2013-04-04 22:28:27 +0000
257@@ -0,0 +1,30 @@
258+add_definitions(-DTEST_SUITE)
259+
260+if(NOT CTEST_TESTING_TIMEOUT)
261+ set(CTEST_TESTING_TIMEOUT 60)
262+endif()
263+
264+include_directories(${CMAKE_BINARY_DIR}
265+ ${mediaplayer_src_SOURCE_DIR}
266+ ${GSTLIB_INCLUDE_DIRS}
267+)
268+
269+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-config.h.in
270+ ${CMAKE_CURRENT_BINARY_DIR}/test-config.h)
271+
272+# thumbnail-test ##############################################################
273+add_executable(thumbnailtest
274+ thumbnailtest.cpp
275+ ${mediaplayer_src_SOURCE_DIR}/thumbnail-pipeline-gst.cpp
276+)
277+
278+qt5_use_modules(thumbnailtest Gui Core Test)
279+add_test(thumbnailtest thumbnailtest -xunitxml -o test_thumbnailtest.xml)
280+set_tests_properties(thumbnailtest PROPERTIES
281+ TIMEOUT ${CTEST_TESTING_TIMEOUT}
282+ ENVIRONMENT "QT_QPA_PLATFORM=minimal")
283+target_link_libraries(thumbnailtest
284+ ${GSTLIB_LDFLAGS})
285+###############################################################################
286+
287+
288
289=== added file 'tests/unittest/test-config.h.in'
290--- tests/unittest/test-config.h.in 1970-01-01 00:00:00 +0000
291+++ tests/unittest/test-config.h.in 2013-04-04 22:28:27 +0000
292@@ -0,0 +1,1 @@
293+static const char* SAMPLE_IMAGES_DIR = "@sample_images_SOURCE_DIR@";
294
295=== added file 'tests/unittest/thumbnailtest.cpp'
296--- tests/unittest/thumbnailtest.cpp 1970-01-01 00:00:00 +0000
297+++ tests/unittest/thumbnailtest.cpp 2013-04-04 22:28:27 +0000
298@@ -0,0 +1,57 @@
299+/*
300+ * Copyright 2013 Canonical Ltd.
301+ *
302+ * This program is free software; you can redistribute it and/or modify
303+ * it under the terms of the GNU Lesser General Public License as published by
304+ * the Free Software Foundation; version 3.
305+ *
306+ * This program is distributed in the hope that it will be useful,
307+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
308+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
309+ * GNU Lesser General Public License for more details.
310+ *
311+ * You should have received a copy of the GNU Lesser General Public License
312+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
313+ *
314+ * Authors:
315+ * Renato Araujo Oliveira Filho <renato@canonical.com>
316+ */
317+
318+#include "thumbnail-pipeline-gst.h"
319+#include "test-config.h"
320+
321+#include <QtTest>
322+#include <QDebug>
323+#include <QImage>
324+
325+class ThumbnailTest : public QObject
326+{
327+ Q_OBJECT
328+private Q_SLOTS:
329+ void testImageIsMeaningful_data()
330+ {
331+ QTest::addColumn<QString>("path");
332+ QTest::addColumn<bool>("expectedResult");
333+
334+ QTest::newRow("black image") << QString(SAMPLE_IMAGES_DIR) + "/frame0.png" << false;
335+ QTest::newRow("non black image") << QString(SAMPLE_IMAGES_DIR) + "/frame10.png" << true;
336+ QTest::newRow("collored image") << QString(SAMPLE_IMAGES_DIR) + "/frame20.png" << true;
337+ QTest::newRow("almost black image") << QString(SAMPLE_IMAGES_DIR) + "/frame100.png" << false;
338+ }
339+
340+ void testImageIsMeaningful()
341+ {
342+ QFETCH(QString, path);
343+ QFETCH(bool, expectedResult);
344+
345+ QImage img = QImage(path);
346+ QVERIFY(!img.isNull());
347+
348+ bool result = ThumbnailPipeline::isMeaningful(img);
349+ QCOMPARE(result, expectedResult);
350+ }
351+};
352+
353+QTEST_MAIN(ThumbnailTest)
354+
355+#include "thumbnailtest.moc"
356
357=== modified file 'tests/videos/CMakeLists.txt'
358--- tests/videos/CMakeLists.txt 2013-03-04 14:17:19 +0000
359+++ tests/videos/CMakeLists.txt 2013-04-04 22:28:27 +0000
360@@ -1,3 +1,5 @@
361+project(sample_videos)
362+
363 set(MEDIAPLAYER_TEST_VIDEOS small.mp4)
364
365 install(FILES ${MEDIAPLAYER_TEST_VIDEOS}

Subscribers

People subscribed via source and target branches