1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | #include <QDebug> |
31 | #include <qabstractvideosurface.h> |
32 | #include "qmediaservice.h" |
33 | #include "qmediaplayer.h" |
34 | #include "qaudioprobe.h" |
35 | #include "qvideoprobe.h" |
36 | #include <qmediaplaylist.h> |
37 | #include <qmediametadata.h> |
38 | |
39 | #include "../shared/mediafileselector.h" |
40 | //TESTED_COMPONENT=src/multimedia |
41 | |
42 | #include <QtMultimedia/private/qtmultimedia-config_p.h> |
43 | |
44 | QT_USE_NAMESPACE |
45 | |
46 | /* |
47 | This is the backend conformance test. |
48 | |
49 | Since it relies on platform media framework and sound hardware |
50 | it may be less stable. |
51 | */ |
52 | |
53 | class tst_QMediaPlayerBackend : public QObject |
54 | { |
55 | Q_OBJECT |
56 | public slots: |
57 | void init(); |
58 | void cleanup(); |
59 | void initTestCase(); |
60 | |
61 | private slots: |
62 | void construction(); |
63 | void loadMedia(); |
64 | void unloadMedia(); |
65 | void loadMediaInLoadingState(); |
66 | void playPauseStop(); |
67 | void processEOS(); |
68 | void deleteLaterAtEOS(); |
69 | void volumeAndMuted(); |
70 | void volumeAcrossFiles_data(); |
71 | void volumeAcrossFiles(); |
72 | void initialVolume(); |
73 | void seekPauseSeek(); |
74 | void seekInStoppedState(); |
75 | void subsequentPlayback(); |
76 | void probes(); |
77 | void playlist(); |
78 | void playlistObject(); |
79 | void surfaceTest_data(); |
80 | void surfaceTest(); |
81 | void multipleSurfaces(); |
82 | void metadata(); |
83 | void playerStateAtEOS(); |
84 | void playFromBuffer(); |
85 | |
86 | private: |
87 | QMediaContent selectVideoFile(const QStringList& mediaCandidates); |
88 | bool isWavSupported(); |
89 | |
90 | //one second local wav file |
91 | QMediaContent localWavFile; |
92 | QMediaContent localWavFile2; |
93 | QMediaContent localVideoFile; |
94 | QMediaContent localCompressedSoundFile; |
95 | QMediaContent localFileWithMetadata; |
96 | |
97 | bool m_inCISystem; |
98 | }; |
99 | |
100 | /* |
101 | This is a simple video surface which records all presented frames. |
102 | */ |
103 | |
104 | class TestVideoSurface : public QAbstractVideoSurface |
105 | { |
106 | Q_OBJECT |
107 | public: |
108 | explicit TestVideoSurface(bool storeFrames = true); |
109 | |
110 | void setSupportedFormats(const QList<QVideoFrame::PixelFormat>& formats) { m_supported = formats; } |
111 | |
112 | //video surface |
113 | QList<QVideoFrame::PixelFormat> supportedPixelFormats( |
114 | QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; |
115 | |
116 | bool start(const QVideoSurfaceFormat &format); |
117 | void stop(); |
118 | bool present(const QVideoFrame &frame); |
119 | |
120 | QList<QVideoFrame> m_frameList; |
121 | int m_totalFrames; // used instead of the list when frames are not stored |
122 | |
123 | private: |
124 | bool m_storeFrames; |
125 | QList<QVideoFrame::PixelFormat> m_supported; |
126 | }; |
127 | |
128 | class ProbeDataHandler : public QObject |
129 | { |
130 | Q_OBJECT |
131 | |
132 | public: |
133 | ProbeDataHandler() : isVideoFlushCalled(false) { } |
134 | |
135 | QList<QVideoFrame> m_frameList; |
136 | QList<QAudioBuffer> m_bufferList; |
137 | bool isVideoFlushCalled; |
138 | |
139 | public slots: |
140 | void processFrame(const QVideoFrame&); |
141 | void processBuffer(const QAudioBuffer&); |
142 | void flushVideo(); |
143 | void flushAudio(); |
144 | }; |
145 | |
146 | void tst_QMediaPlayerBackend::init() |
147 | { |
148 | } |
149 | |
150 | QMediaContent tst_QMediaPlayerBackend::selectVideoFile(const QStringList& mediaCandidates) |
151 | { |
152 | // select supported video format |
153 | QMediaPlayer player; |
154 | TestVideoSurface *surface = new TestVideoSurface; |
155 | player.setVideoOutput(surface); |
156 | |
157 | QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); |
158 | |
159 | for (const QString &s : mediaCandidates) { |
160 | QFileInfo videoFile(s); |
161 | if (!videoFile.exists()) |
162 | continue; |
163 | QMediaContent media = QMediaContent(QUrl::fromLocalFile(localfile: videoFile.absoluteFilePath())); |
164 | player.setMedia(media); |
165 | player.pause(); |
166 | |
167 | for (int i = 0; i < 2000 && surface->m_frameList.isEmpty() && errorSpy.isEmpty(); i+=50) { |
168 | QTest::qWait(ms: 50); |
169 | } |
170 | |
171 | if (!surface->m_frameList.isEmpty() && errorSpy.isEmpty()) { |
172 | return media; |
173 | } |
174 | errorSpy.clear(); |
175 | } |
176 | |
177 | return QMediaContent(); |
178 | } |
179 | |
180 | bool tst_QMediaPlayerBackend::isWavSupported() |
181 | { |
182 | return !localWavFile.isNull(); |
183 | } |
184 | |
185 | void tst_QMediaPlayerBackend::initTestCase() |
186 | { |
187 | QMediaPlayer player; |
188 | if (!player.isAvailable()) |
189 | QSKIP("Media player service is not available" ); |
190 | |
191 | qRegisterMetaType<QMediaContent>(); |
192 | |
193 | localWavFile = MediaFileSelector::selectMediaFile(mediaCandidates: QStringList() << QFINDTESTDATA("testdata/test.wav" )); |
194 | localWavFile2 = MediaFileSelector::selectMediaFile(mediaCandidates: QStringList() << QFINDTESTDATA("testdata/_test.wav" ));; |
195 | |
196 | QStringList mediaCandidates; |
197 | mediaCandidates << QFINDTESTDATA("testdata/colors.mp4" ); |
198 | #ifndef SKIP_OGV_TEST |
199 | mediaCandidates << QFINDTESTDATA("testdata/colors.ogv" ); |
200 | #endif |
201 | localVideoFile = MediaFileSelector::selectMediaFile(mediaCandidates); |
202 | |
203 | mediaCandidates.clear(); |
204 | mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mp3" ); |
205 | mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mkv" ); |
206 | localCompressedSoundFile = MediaFileSelector::selectMediaFile(mediaCandidates); |
207 | |
208 | localFileWithMetadata = MediaFileSelector::selectMediaFile(mediaCandidates: QStringList() << QFINDTESTDATA("testdata/nokia-tune.mp3" )); |
209 | |
210 | qgetenv(varName: "QT_TEST_CI" ).toInt(ok: &m_inCISystem,base: 10); |
211 | } |
212 | |
213 | void tst_QMediaPlayerBackend::cleanup() |
214 | { |
215 | } |
216 | |
217 | void tst_QMediaPlayerBackend::construction() |
218 | { |
219 | QMediaPlayer player; |
220 | QTRY_VERIFY(player.isAvailable()); |
221 | } |
222 | |
223 | void tst_QMediaPlayerBackend::loadMedia() |
224 | { |
225 | if (!isWavSupported()) |
226 | QSKIP("Sound format is not supported" ); |
227 | |
228 | QMediaPlayer player; |
229 | |
230 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
231 | QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); |
232 | |
233 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
234 | QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); |
235 | QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent))); |
236 | QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); |
237 | |
238 | player.setMedia(media: localWavFile); |
239 | |
240 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
241 | |
242 | QVERIFY(player.mediaStatus() != QMediaPlayer::NoMedia); |
243 | QVERIFY(player.mediaStatus() != QMediaPlayer::InvalidMedia); |
244 | QVERIFY(player.media() == localWavFile); |
245 | QVERIFY(player.currentMedia() == localWavFile); |
246 | |
247 | QCOMPARE(stateSpy.count(), 0); |
248 | QVERIFY(statusSpy.count() > 0); |
249 | QCOMPARE(mediaSpy.count(), 1); |
250 | QCOMPARE(mediaSpy.last()[0].value<QMediaContent>(), localWavFile); |
251 | QCOMPARE(currentMediaSpy.last()[0].value<QMediaContent>(), localWavFile); |
252 | |
253 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
254 | |
255 | QVERIFY(player.isAudioAvailable()); |
256 | QVERIFY(!player.isVideoAvailable()); |
257 | } |
258 | |
259 | void tst_QMediaPlayerBackend::unloadMedia() |
260 | { |
261 | if (!isWavSupported()) |
262 | QSKIP("Sound format is not supported" ); |
263 | |
264 | QMediaPlayer player; |
265 | player.setNotifyInterval(50); |
266 | |
267 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
268 | QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); |
269 | QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent))); |
270 | QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); |
271 | QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); |
272 | QSignalSpy durationSpy(&player, SIGNAL(positionChanged(qint64))); |
273 | |
274 | player.setMedia(media: localWavFile); |
275 | |
276 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
277 | |
278 | QVERIFY(player.position() == 0); |
279 | QVERIFY(player.duration() > 0); |
280 | |
281 | player.play(); |
282 | |
283 | QTRY_VERIFY(player.position() > 0); |
284 | QVERIFY(player.duration() > 0); |
285 | |
286 | stateSpy.clear(); |
287 | statusSpy.clear(); |
288 | mediaSpy.clear(); |
289 | currentMediaSpy.clear(); |
290 | positionSpy.clear(); |
291 | durationSpy.clear(); |
292 | |
293 | player.setMedia(media: QMediaContent()); |
294 | |
295 | QVERIFY(player.position() <= 0); |
296 | QVERIFY(player.duration() <= 0); |
297 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
298 | QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); |
299 | QCOMPARE(player.media(), QMediaContent()); |
300 | QCOMPARE(player.currentMedia(), QMediaContent()); |
301 | |
302 | QVERIFY(!stateSpy.isEmpty()); |
303 | QVERIFY(!statusSpy.isEmpty()); |
304 | QVERIFY(!mediaSpy.isEmpty()); |
305 | QVERIFY(!currentMediaSpy.isEmpty()); |
306 | QVERIFY(!positionSpy.isEmpty()); |
307 | } |
308 | |
309 | void tst_QMediaPlayerBackend::loadMediaInLoadingState() |
310 | { |
311 | if (!isWavSupported()) |
312 | QSKIP("Sound format is not supported" ); |
313 | |
314 | QMediaPlayer player; |
315 | player.setMedia(media: localWavFile); |
316 | player.play(); |
317 | QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadingMedia); |
318 | // Sets new media while old has not been finished. |
319 | player.setMedia(media: localWavFile); |
320 | QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadingMedia); |
321 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
322 | } |
323 | |
324 | void tst_QMediaPlayerBackend::playPauseStop() |
325 | { |
326 | if (!isWavSupported()) |
327 | QSKIP("Sound format is not supported" ); |
328 | |
329 | QMediaPlayer player; |
330 | player.setNotifyInterval(50); |
331 | |
332 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
333 | QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); |
334 | QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); |
335 | QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); |
336 | |
337 | // Check play() without a media |
338 | player.play(); |
339 | |
340 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
341 | QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); |
342 | QCOMPARE(player.error(), QMediaPlayer::NoError); |
343 | QCOMPARE(player.position(), 0); |
344 | QCOMPARE(stateSpy.count(), 0); |
345 | QCOMPARE(statusSpy.count(), 0); |
346 | QCOMPARE(positionSpy.count(), 0); |
347 | QCOMPARE(errorSpy.count(), 0); |
348 | |
349 | // Check pause() without a media |
350 | player.pause(); |
351 | |
352 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
353 | QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); |
354 | QCOMPARE(player.error(), QMediaPlayer::NoError); |
355 | QCOMPARE(player.position(), 0); |
356 | QCOMPARE(stateSpy.count(), 0); |
357 | QCOMPARE(statusSpy.count(), 0); |
358 | QCOMPARE(positionSpy.count(), 0); |
359 | QCOMPARE(errorSpy.count(), 0); |
360 | |
361 | // The rest is with a valid media |
362 | |
363 | player.setMedia(media: localWavFile); |
364 | |
365 | QCOMPARE(player.position(), qint64(0)); |
366 | |
367 | player.play(); |
368 | |
369 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
370 | |
371 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
372 | |
373 | QCOMPARE(stateSpy.count(), 1); |
374 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState); |
375 | QTRY_VERIFY(statusSpy.count() > 0 && |
376 | statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::BufferedMedia); |
377 | |
378 | QTRY_VERIFY(player.position() > 100); |
379 | QVERIFY(player.duration() > 0); |
380 | QVERIFY(positionSpy.count() > 0); |
381 | QVERIFY(positionSpy.last()[0].value<qint64>() > 0); |
382 | |
383 | stateSpy.clear(); |
384 | statusSpy.clear(); |
385 | positionSpy.clear(); |
386 | |
387 | qint64 positionBeforePause = player.position(); |
388 | player.pause(); |
389 | |
390 | QCOMPARE(player.state(), QMediaPlayer::PausedState); |
391 | QCOMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
392 | |
393 | QCOMPARE(stateSpy.count(), 1); |
394 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PausedState); |
395 | |
396 | QTest::qWait(ms: 2000); |
397 | |
398 | QVERIFY(qAbs(player.position() - positionBeforePause) < 150); |
399 | QCOMPARE(positionSpy.count(), 1); |
400 | |
401 | stateSpy.clear(); |
402 | statusSpy.clear(); |
403 | |
404 | player.stop(); |
405 | |
406 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
407 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
408 | |
409 | QCOMPARE(stateSpy.count(), 1); |
410 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); |
411 | //it's allowed to emit statusChanged() signal async |
412 | QTRY_COMPARE(statusSpy.count(), 1); |
413 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia); |
414 | |
415 | //ensure the position is reset to 0 at stop and positionChanged(0) is emitted |
416 | QCOMPARE(player.position(), qint64(0)); |
417 | QCOMPARE(positionSpy.last()[0].value<qint64>(), qint64(0)); |
418 | QVERIFY(player.duration() > 0); |
419 | |
420 | stateSpy.clear(); |
421 | statusSpy.clear(); |
422 | positionSpy.clear(); |
423 | |
424 | player.play(); |
425 | |
426 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
427 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
428 | QCOMPARE(stateSpy.count(), 1); |
429 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState); |
430 | QCOMPARE(statusSpy.count(), 1); // Should not go through Loading again when play -> stop -> play |
431 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia); |
432 | |
433 | player.stop(); |
434 | stateSpy.clear(); |
435 | statusSpy.clear(); |
436 | positionSpy.clear(); |
437 | |
438 | player.setMedia(media: localWavFile2); |
439 | |
440 | QTRY_VERIFY(statusSpy.count() > 0); |
441 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
442 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia); |
443 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
444 | QCOMPARE(stateSpy.count(), 0); |
445 | |
446 | player.play(); |
447 | |
448 | QTRY_VERIFY(player.position() > 100); |
449 | |
450 | player.setMedia(media: localWavFile); |
451 | |
452 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
453 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia); |
454 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
455 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); |
456 | QCOMPARE(player.position(), 0); |
457 | QCOMPARE(positionSpy.last()[0].value<qint64>(), 0); |
458 | |
459 | stateSpy.clear(); |
460 | statusSpy.clear(); |
461 | positionSpy.clear(); |
462 | |
463 | player.play(); |
464 | |
465 | QTRY_VERIFY(player.position() > 100); |
466 | |
467 | player.setMedia(media: QMediaContent()); |
468 | |
469 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::NoMedia); |
470 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::NoMedia); |
471 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
472 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); |
473 | QCOMPARE(player.position(), 0); |
474 | QCOMPARE(positionSpy.last()[0].value<qint64>(), 0); |
475 | QCOMPARE(player.duration(), 0); |
476 | } |
477 | |
478 | |
479 | void tst_QMediaPlayerBackend::processEOS() |
480 | { |
481 | if (!isWavSupported()) |
482 | QSKIP("Sound format is not supported" ); |
483 | |
484 | QMediaPlayer player; |
485 | player.setNotifyInterval(50); |
486 | |
487 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
488 | QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); |
489 | QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); |
490 | |
491 | player.setMedia(media: localWavFile); |
492 | |
493 | player.play(); |
494 | player.setPosition(900); |
495 | |
496 | //wait up to 5 seconds for EOS |
497 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
498 | |
499 | QVERIFY(statusSpy.count() > 0); |
500 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia); |
501 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
502 | QCOMPARE(stateSpy.count(), 2); |
503 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); |
504 | |
505 | //at EOS the position stays at the end of file |
506 | QCOMPARE(player.position(), player.duration()); |
507 | QVERIFY(positionSpy.count() > 0); |
508 | QCOMPARE(positionSpy.last()[0].value<qint64>(), player.duration()); |
509 | |
510 | stateSpy.clear(); |
511 | statusSpy.clear(); |
512 | positionSpy.clear(); |
513 | |
514 | player.play(); |
515 | |
516 | //position is reset to start |
517 | QTRY_VERIFY(player.position() < 100); |
518 | QTRY_VERIFY(positionSpy.count() > 0); |
519 | QCOMPARE(positionSpy.first()[0].value<qint64>(), 0); |
520 | |
521 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
522 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
523 | |
524 | QCOMPARE(stateSpy.count(), 1); |
525 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState); |
526 | QVERIFY(statusSpy.count() > 0); |
527 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia); |
528 | |
529 | player.setPosition(900); |
530 | //wait up to 5 seconds for EOS |
531 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
532 | QVERIFY(statusSpy.count() > 0); |
533 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia); |
534 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
535 | QCOMPARE(stateSpy.count(), 2); |
536 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState); |
537 | |
538 | //position stays at the end of file |
539 | QCOMPARE(player.position(), player.duration()); |
540 | QVERIFY(positionSpy.count() > 0); |
541 | QCOMPARE(positionSpy.last()[0].value<qint64>(), player.duration()); |
542 | |
543 | //after setPosition EndOfMedia status should be reset to Loaded |
544 | stateSpy.clear(); |
545 | statusSpy.clear(); |
546 | player.setPosition(500); |
547 | |
548 | //this transition can be async, so allow backend to perform it |
549 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
550 | |
551 | QCOMPARE(stateSpy.count(), 0); |
552 | QTRY_VERIFY(statusSpy.count() > 0 && |
553 | statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::LoadedMedia); |
554 | |
555 | player.play(); |
556 | player.setPosition(900); |
557 | //wait up to 5 seconds for EOS |
558 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
559 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
560 | QCOMPARE(player.position(), player.duration()); |
561 | |
562 | stateSpy.clear(); |
563 | statusSpy.clear(); |
564 | positionSpy.clear(); |
565 | |
566 | // pause() should reset position to beginning and status to Buffered |
567 | player.pause(); |
568 | |
569 | QTRY_COMPARE(player.position(), 0); |
570 | QTRY_VERIFY(positionSpy.count() > 0); |
571 | QCOMPARE(positionSpy.first()[0].value<qint64>(), 0); |
572 | |
573 | QCOMPARE(player.state(), QMediaPlayer::PausedState); |
574 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
575 | |
576 | QCOMPARE(stateSpy.count(), 1); |
577 | QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PausedState); |
578 | QVERIFY(statusSpy.count() > 0); |
579 | QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia); |
580 | } |
581 | |
582 | // Helper class for tst_QMediaPlayerBackend::deleteLaterAtEOS() |
583 | class DeleteLaterAtEos : public QObject |
584 | { |
585 | Q_OBJECT |
586 | public: |
587 | DeleteLaterAtEos(QMediaPlayer* p) : player(p) |
588 | { |
589 | } |
590 | |
591 | public slots: |
592 | void play() |
593 | { |
594 | QVERIFY(connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), |
595 | this, SLOT(onMediaStatusChanged(QMediaPlayer::MediaStatus)))); |
596 | player->play(); |
597 | } |
598 | |
599 | private slots: |
600 | void onMediaStatusChanged(QMediaPlayer::MediaStatus status) |
601 | { |
602 | if (status == QMediaPlayer::EndOfMedia) { |
603 | player-> deleteLater(); |
604 | player = 0; |
605 | } |
606 | } |
607 | |
608 | private: |
609 | QMediaPlayer* player; |
610 | }; |
611 | |
612 | // Regression test for |
613 | // QTBUG-24927 - deleteLater() called to QMediaPlayer from its signal handler does not work as expected |
614 | void tst_QMediaPlayerBackend::deleteLaterAtEOS() |
615 | { |
616 | if (!isWavSupported()) |
617 | QSKIP("Sound format is not supported" ); |
618 | |
619 | QPointer<QMediaPlayer> player(new QMediaPlayer); |
620 | DeleteLaterAtEos deleter(player); |
621 | player->setMedia(media: localWavFile); |
622 | |
623 | // Create an event loop for verifying deleteLater behavior instead of using |
624 | // QTRY_VERIFY or QTest::qWait. QTest::qWait makes extra effort to process |
625 | // DeferredDelete events during the wait, which interferes with this test. |
626 | QEventLoop loop; |
627 | QTimer::singleShot(msec: 0, receiver: &deleter, SLOT(play())); |
628 | QTimer::singleShot(msec: 5000, receiver: &loop, SLOT(quit())); |
629 | connect(sender: player.data(), SIGNAL(destroyed()), receiver: &loop, SLOT(quit())); |
630 | loop.exec(); |
631 | // Verify that the player was destroyed within the event loop. |
632 | // This check will fail without the fix for QTBUG-24927. |
633 | QVERIFY(player.isNull()); |
634 | } |
635 | |
636 | void tst_QMediaPlayerBackend::volumeAndMuted() |
637 | { |
638 | //volume and muted properties should be independent |
639 | QMediaPlayer player; |
640 | QVERIFY(player.volume() > 0); |
641 | QVERIFY(!player.isMuted()); |
642 | |
643 | player.setMedia(media: localWavFile); |
644 | player.pause(); |
645 | |
646 | QVERIFY(player.volume() > 0); |
647 | QVERIFY(!player.isMuted()); |
648 | |
649 | QSignalSpy volumeSpy(&player, SIGNAL(volumeChanged(int))); |
650 | QSignalSpy mutedSpy(&player, SIGNAL(mutedChanged(bool))); |
651 | |
652 | //setting volume to 0 should not trigger muted |
653 | player.setVolume(0); |
654 | QTRY_COMPARE(player.volume(), 0); |
655 | QVERIFY(!player.isMuted()); |
656 | QCOMPARE(volumeSpy.count(), 1); |
657 | QCOMPARE(volumeSpy.last()[0].toInt(), player.volume()); |
658 | QCOMPARE(mutedSpy.count(), 0); |
659 | |
660 | player.setVolume(50); |
661 | QTRY_COMPARE(player.volume(), 50); |
662 | QVERIFY(!player.isMuted()); |
663 | QCOMPARE(volumeSpy.count(), 2); |
664 | QCOMPARE(volumeSpy.last()[0].toInt(), player.volume()); |
665 | QCOMPARE(mutedSpy.count(), 0); |
666 | |
667 | player.setMuted(true); |
668 | QTRY_VERIFY(player.isMuted()); |
669 | QVERIFY(player.volume() > 0); |
670 | QCOMPARE(volumeSpy.count(), 2); |
671 | QCOMPARE(mutedSpy.count(), 1); |
672 | QCOMPARE(mutedSpy.last()[0].toBool(), player.isMuted()); |
673 | |
674 | player.setMuted(false); |
675 | QTRY_VERIFY(!player.isMuted()); |
676 | QVERIFY(player.volume() > 0); |
677 | QCOMPARE(volumeSpy.count(), 2); |
678 | QCOMPARE(mutedSpy.count(), 2); |
679 | QCOMPARE(mutedSpy.last()[0].toBool(), player.isMuted()); |
680 | |
681 | } |
682 | |
683 | void tst_QMediaPlayerBackend::volumeAcrossFiles_data() |
684 | { |
685 | QTest::addColumn<int>(name: "volume" ); |
686 | QTest::addColumn<bool>(name: "muted" ); |
687 | |
688 | QTest::newRow(dataTag: "100 unmuted" ) << 100 << false; |
689 | QTest::newRow(dataTag: "50 unmuted" ) << 50 << false; |
690 | QTest::newRow(dataTag: "0 unmuted" ) << 0 << false; |
691 | QTest::newRow(dataTag: "100 muted" ) << 100 << true; |
692 | QTest::newRow(dataTag: "50 muted" ) << 50 << true; |
693 | QTest::newRow(dataTag: "0 muted" ) << 0 << true; |
694 | } |
695 | |
696 | void tst_QMediaPlayerBackend::volumeAcrossFiles() |
697 | { |
698 | #ifdef Q_OS_LINUX |
699 | if (m_inCISystem) |
700 | QSKIP("QTBUG-26577 Fails with gstreamer backend on ubuntu 10.4" ); |
701 | #endif |
702 | |
703 | QFETCH(int, volume); |
704 | QFETCH(bool, muted); |
705 | |
706 | QMediaPlayer player; |
707 | |
708 | //volume and muted should not be preserved between player instances |
709 | QVERIFY(player.volume() > 0); |
710 | QVERIFY(!player.isMuted()); |
711 | |
712 | player.setVolume(volume); |
713 | player.setMuted(muted); |
714 | |
715 | QTRY_COMPARE(player.volume(), volume); |
716 | QTRY_COMPARE(player.isMuted(), muted); |
717 | |
718 | player.setMedia(media: localWavFile); |
719 | QCOMPARE(player.volume(), volume); |
720 | QCOMPARE(player.isMuted(), muted); |
721 | |
722 | player.pause(); |
723 | |
724 | //to ensure the backend doesn't change volume/muted |
725 | //async during file loading. |
726 | |
727 | QTRY_COMPARE(player.volume(), volume); |
728 | QCOMPARE(player.isMuted(), muted); |
729 | |
730 | player.setMedia(media: QMediaContent()); |
731 | QTRY_COMPARE(player.volume(), volume); |
732 | QCOMPARE(player.isMuted(), muted); |
733 | |
734 | player.setMedia(media: localWavFile); |
735 | player.pause(); |
736 | |
737 | QTRY_COMPARE(player.volume(), volume); |
738 | QCOMPARE(player.isMuted(), muted); |
739 | } |
740 | |
741 | void tst_QMediaPlayerBackend::initialVolume() |
742 | { |
743 | if (!isWavSupported()) |
744 | QSKIP("Sound format is not supported" ); |
745 | |
746 | { |
747 | QMediaPlayer player; |
748 | player.setVolume(1); |
749 | player.setMedia(media: localWavFile); |
750 | QCOMPARE(player.volume(), 1); |
751 | player.play(); |
752 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
753 | QCOMPARE(player.volume(), 1); |
754 | } |
755 | |
756 | { |
757 | QMediaPlayer player; |
758 | player.setMedia(media: localWavFile); |
759 | QCOMPARE(player.volume(), 100); |
760 | player.play(); |
761 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
762 | QCOMPARE(player.volume(), 100); |
763 | } |
764 | } |
765 | |
766 | void tst_QMediaPlayerBackend::seekPauseSeek() |
767 | { |
768 | if (localVideoFile.isNull()) |
769 | QSKIP("No supported video file" ); |
770 | |
771 | QMediaPlayer player; |
772 | |
773 | QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); |
774 | |
775 | TestVideoSurface *surface = new TestVideoSurface; |
776 | player.setVideoOutput(surface); |
777 | |
778 | player.setMedia(media: localVideoFile); |
779 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
780 | QVERIFY(surface->m_frameList.isEmpty()); // frame must not appear until we call pause() or play() |
781 | |
782 | positionSpy.clear(); |
783 | qint64 position = 7000; |
784 | player.setPosition(position); |
785 | QTRY_VERIFY(!positionSpy.isEmpty() && qAbs(player.position() - position) < (qint64)500); |
786 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
787 | QTest::qWait(ms: 250); // wait a bit to ensure the frame is not rendered |
788 | QVERIFY(surface->m_frameList.isEmpty()); // still no frame, we must call pause() or play() to see a frame |
789 | |
790 | player.pause(); |
791 | QTRY_COMPARE(player.state(), QMediaPlayer::PausedState); // it might take some time for the operation to be completed |
792 | QTRY_VERIFY_WITH_TIMEOUT(!surface->m_frameList.isEmpty(), 10000); // we must see a frame at position 7000 here |
793 | |
794 | // Make sure that the frame has a timestamp before testing - not all backends provides this |
795 | if (!surface->m_frameList.back().isValid() || surface->m_frameList.back().startTime() < 0) |
796 | QSKIP("No timestamp" ); |
797 | |
798 | { |
799 | QVideoFrame frame = surface->m_frameList.back(); |
800 | #if !QT_CONFIG(directshow) |
801 | const qint64 elapsed = (frame.startTime() / 1000) - position; // frame.startTime() is microsecond, position is milliseconds. |
802 | QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData()); |
803 | #endif |
804 | QCOMPARE(frame.width(), 160); |
805 | QCOMPARE(frame.height(), 120); |
806 | |
807 | // create QImage for QVideoFrame to verify RGB pixel colors |
808 | QVERIFY(frame.map(QAbstractVideoBuffer::ReadOnly)); |
809 | QImage image(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(format: frame.pixelFormat())); |
810 | QVERIFY(!image.isNull()); |
811 | QVERIFY(qRed(image.pixel(0, 0)) >= 230); // conversion from YUV => RGB, that's why it's not 255 |
812 | QVERIFY(qGreen(image.pixel(0, 0)) < 20); |
813 | QVERIFY(qBlue(image.pixel(0, 0)) < 20); |
814 | frame.unmap(); |
815 | } |
816 | |
817 | surface->m_frameList.clear(); |
818 | positionSpy.clear(); |
819 | position = 12000; |
820 | player.setPosition(position); |
821 | QTRY_VERIFY(!positionSpy.isEmpty() && qAbs(player.position() - position) < (qint64)500); |
822 | QCOMPARE(player.state(), QMediaPlayer::PausedState); |
823 | QVERIFY(!surface->m_frameList.isEmpty()); |
824 | |
825 | { |
826 | QVideoFrame frame = surface->m_frameList.back(); |
827 | #if !QT_CONFIG(directshow) |
828 | const qint64 elapsed = (frame.startTime() / 1000) - position; |
829 | QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData()); |
830 | #endif |
831 | QCOMPARE(frame.width(), 160); |
832 | QCOMPARE(frame.height(), 120); |
833 | |
834 | QVERIFY(frame.map(QAbstractVideoBuffer::ReadOnly)); |
835 | QImage image(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(format: frame.pixelFormat())); |
836 | QVERIFY(!image.isNull()); |
837 | QVERIFY(qRed(image.pixel(0, 0)) < 20); |
838 | QVERIFY(qGreen(image.pixel(0, 0)) >= 230); |
839 | QVERIFY(qBlue(image.pixel(0, 0)) < 20); |
840 | frame.unmap(); |
841 | } |
842 | } |
843 | |
844 | void tst_QMediaPlayerBackend::seekInStoppedState() |
845 | { |
846 | if (localVideoFile.isNull()) |
847 | QSKIP("No supported video file" ); |
848 | |
849 | QMediaPlayer player; |
850 | player.setNotifyInterval(500); |
851 | |
852 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
853 | QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); |
854 | |
855 | player.setMedia(media: localVideoFile); |
856 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
857 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
858 | QCOMPARE(player.position(), 0); |
859 | QVERIFY(player.isSeekable()); |
860 | |
861 | stateSpy.clear(); |
862 | positionSpy.clear(); |
863 | |
864 | qint64 position = 5000; |
865 | player.setPosition(position); |
866 | |
867 | QTRY_VERIFY(qAbs(player.position() - position) < qint64(500)); |
868 | QCOMPARE(positionSpy.count(), 1); |
869 | QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500)); |
870 | |
871 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
872 | QCOMPARE(stateSpy.count(), 0); |
873 | |
874 | QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
875 | |
876 | positionSpy.clear(); |
877 | |
878 | player.play(); |
879 | |
880 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
881 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
882 | QVERIFY(qAbs(player.position() - position) < qint64(500)); |
883 | |
884 | QTest::qWait(ms: 2000); |
885 | // Check that it never played from the beginning |
886 | QVERIFY(player.position() > (position - 500)); |
887 | for (int i = 0; i < positionSpy.count(); ++i) |
888 | QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500)); |
889 | |
890 | // ------ |
891 | // Same tests but after play() --> stop() |
892 | |
893 | player.stop(); |
894 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
895 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
896 | QCOMPARE(player.position(), 0); |
897 | |
898 | stateSpy.clear(); |
899 | positionSpy.clear(); |
900 | |
901 | player.setPosition(position); |
902 | |
903 | QTRY_VERIFY(qAbs(player.position() - position) < qint64(500)); |
904 | QCOMPARE(positionSpy.count(), 1); |
905 | QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500)); |
906 | |
907 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
908 | QCOMPARE(stateSpy.count(), 0); |
909 | |
910 | QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
911 | |
912 | positionSpy.clear(); |
913 | |
914 | player.play(); |
915 | |
916 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
917 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
918 | QVERIFY(qAbs(player.position() - position) < qint64(500)); |
919 | |
920 | QTest::qWait(ms: 2000); |
921 | // Check that it never played from the beginning |
922 | QVERIFY(player.position() > (position - 500)); |
923 | for (int i = 0; i < positionSpy.count(); ++i) |
924 | QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500)); |
925 | |
926 | // ------ |
927 | // Same tests but after reaching the end of the media |
928 | |
929 | player.setPosition(player.duration() - 500); |
930 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
931 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
932 | QCOMPARE(player.position(), player.duration()); |
933 | |
934 | stateSpy.clear(); |
935 | positionSpy.clear(); |
936 | |
937 | player.setPosition(position); |
938 | |
939 | QTRY_VERIFY(qAbs(player.position() - position) < qint64(500)); |
940 | QCOMPARE(positionSpy.count(), 1); |
941 | QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500)); |
942 | |
943 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
944 | QCOMPARE(stateSpy.count(), 0); |
945 | |
946 | QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); |
947 | |
948 | positionSpy.clear(); |
949 | |
950 | player.play(); |
951 | |
952 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
953 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia); |
954 | QVERIFY(qAbs(player.position() - position) < qint64(500)); |
955 | |
956 | QTest::qWait(ms: 2000); |
957 | // Check that it never played from the beginning |
958 | QVERIFY(player.position() > (position - 500)); |
959 | for (int i = 0; i < positionSpy.count(); ++i) |
960 | QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500)); |
961 | } |
962 | |
963 | void tst_QMediaPlayerBackend::subsequentPlayback() |
964 | { |
965 | #ifdef Q_OS_LINUX |
966 | if (m_inCISystem) |
967 | QSKIP("QTBUG-26769 Fails with gstreamer backend on ubuntu 10.4, setPosition(0)" ); |
968 | #endif |
969 | |
970 | if (localCompressedSoundFile.isNull()) |
971 | QSKIP("Sound format is not supported" ); |
972 | |
973 | QMediaPlayer player; |
974 | player.setMedia(media: localCompressedSoundFile); |
975 | player.play(); |
976 | |
977 | QCOMPARE(player.error(), QMediaPlayer::NoError); |
978 | QTRY_COMPARE(player.state(), QMediaPlayer::PlayingState); |
979 | QTRY_COMPARE_WITH_TIMEOUT(player.mediaStatus(), QMediaPlayer::EndOfMedia, 15000); |
980 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
981 | // Could differ by up to 1 compressed frame length |
982 | QVERIFY(qAbs(player.position() - player.duration()) < 100); |
983 | QVERIFY(player.position() > 0); |
984 | |
985 | player.play(); |
986 | QTRY_COMPARE(player.state(), QMediaPlayer::PlayingState); |
987 | QTRY_VERIFY_WITH_TIMEOUT(player.position() > 2000 && player.position() < 5000, 10000); |
988 | player.pause(); |
989 | QCOMPARE(player.state(), QMediaPlayer::PausedState); |
990 | // make sure position does not "jump" closer to the end of the file |
991 | QVERIFY(player.position() > 2000 && player.position() < 5000); |
992 | // try to seek back to zero |
993 | player.setPosition(0); |
994 | QTRY_COMPARE(player.position(), qint64(0)); |
995 | player.play(); |
996 | QCOMPARE(player.state(), QMediaPlayer::PlayingState); |
997 | QTRY_VERIFY_WITH_TIMEOUT(player.position() > 2000 && player.position() < 5000, 10000); |
998 | player.pause(); |
999 | QCOMPARE(player.state(), QMediaPlayer::PausedState); |
1000 | QVERIFY(player.position() > 2000 && player.position() < 5000); |
1001 | } |
1002 | |
1003 | void tst_QMediaPlayerBackend::probes() |
1004 | { |
1005 | if (localVideoFile.isNull()) |
1006 | QSKIP("No supported video file" ); |
1007 | |
1008 | QMediaPlayer *player = new QMediaPlayer; |
1009 | |
1010 | TestVideoSurface *surface = new TestVideoSurface; |
1011 | player->setVideoOutput(surface); |
1012 | |
1013 | QVideoProbe *videoProbe = new QVideoProbe; |
1014 | QAudioProbe *audioProbe = new QAudioProbe; |
1015 | |
1016 | ProbeDataHandler probeHandler; |
1017 | connect(sender: videoProbe, SIGNAL(videoFrameProbed(QVideoFrame)), receiver: &probeHandler, SLOT(processFrame(QVideoFrame))); |
1018 | connect(sender: videoProbe, SIGNAL(flush()), receiver: &probeHandler, SLOT(flushVideo())); |
1019 | connect(sender: audioProbe, SIGNAL(audioBufferProbed(QAudioBuffer)), receiver: &probeHandler, SLOT(processBuffer(QAudioBuffer))); |
1020 | connect(sender: audioProbe, SIGNAL(flush()), receiver: &probeHandler, SLOT(flushAudio())); |
1021 | |
1022 | if (!videoProbe->setSource(player)) |
1023 | QSKIP("QVideoProbe is not supported" ); |
1024 | audioProbe->setSource(player); |
1025 | |
1026 | player->setMedia(media: localVideoFile); |
1027 | QTRY_COMPARE(player->mediaStatus(), QMediaPlayer::LoadedMedia); |
1028 | |
1029 | player->pause(); |
1030 | QTRY_COMPARE(surface->m_frameList.size(), 1); |
1031 | QVERIFY(!probeHandler.m_frameList.isEmpty()); |
1032 | QTRY_VERIFY(!probeHandler.m_bufferList.isEmpty()); |
1033 | |
1034 | delete player; |
1035 | QTRY_VERIFY(probeHandler.isVideoFlushCalled); |
1036 | delete videoProbe; |
1037 | delete audioProbe; |
1038 | } |
1039 | |
1040 | void tst_QMediaPlayerBackend::playlist() |
1041 | { |
1042 | QMediaPlayer player; |
1043 | |
1044 | QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent))); |
1045 | QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); |
1046 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
1047 | QSignalSpy mediaStatusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); |
1048 | QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); |
1049 | |
1050 | QFileInfo fileInfo(QFINDTESTDATA("testdata/sample.m3u" )); |
1051 | player.setMedia(media: QUrl::fromLocalFile(localfile: fileInfo.absoluteFilePath())); |
1052 | |
1053 | player.play(); |
1054 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1055 | |
1056 | if (player.mediaStatus() == QMediaPlayer::InvalidMedia || mediaSpy.count() == 1) |
1057 | QSKIP("QMediaPlayer does not support loading M3U playlists as QMediaPlaylist" ); |
1058 | |
1059 | QCOMPARE(mediaSpy.count(), 2); |
1060 | // sample.m3u -> sample.m3u resolved -> test.wav -> |
1061 | // nested1.m3u -> nested1.m3u resolved -> test.wav -> |
1062 | // nested2.m3u -> nested2.m3u resolved -> |
1063 | // test.wav -> _test.wav |
1064 | // currentMediaChanged signals not emmitted for |
1065 | // nested1.m3u\_test.wav and nested2.m3u\_test.wav |
1066 | // because current media stays the same |
1067 | QCOMPARE(currentMediaSpy.count(), 11); |
1068 | QCOMPARE(stateSpy.count(), 2); |
1069 | QCOMPARE(errorSpy.count(), 0); |
1070 | QCOMPARE(mediaStatusSpy.count(), 19); // 6 x (LoadingMedia -> BufferedMedia -> EndOfMedia) + NoMedia |
1071 | |
1072 | mediaSpy.clear(); |
1073 | currentMediaSpy.clear(); |
1074 | stateSpy.clear(); |
1075 | mediaStatusSpy.clear(); |
1076 | errorSpy.clear(); |
1077 | |
1078 | player.play(); |
1079 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1080 | QCOMPARE(mediaSpy.count(), 0); |
1081 | QCOMPARE(currentMediaSpy.count(), 8); |
1082 | QCOMPARE(stateSpy.count(), 2); |
1083 | QCOMPARE(errorSpy.count(), 0); |
1084 | QCOMPARE(mediaStatusSpy.count(), 19); // 6 x (LoadingMedia -> BufferedMedia -> EndOfMedia) + NoMedia |
1085 | |
1086 | mediaSpy.clear(); |
1087 | currentMediaSpy.clear(); |
1088 | stateSpy.clear(); |
1089 | mediaStatusSpy.clear(); |
1090 | errorSpy.clear(); |
1091 | |
1092 | // <<< Invalid - 1st pass >>> |
1093 | fileInfo.setFile(QFINDTESTDATA("testdata/invalid_media.m3u" )); |
1094 | player.setMedia(media: QUrl::fromLocalFile(localfile: fileInfo.absoluteFilePath())); |
1095 | |
1096 | player.play(); |
1097 | QTRY_COMPARE(player.state(), QMediaPlayer::StoppedState); |
1098 | // playlist -> resolved playlist |
1099 | QCOMPARE(mediaSpy.count(), 2); |
1100 | // playlist -> resolved playlist -> invalid -> "" |
1101 | QCOMPARE(currentMediaSpy.count(), 4); |
1102 | QCOMPARE(stateSpy.count(), 2); |
1103 | QCOMPARE(errorSpy.count(), 1); |
1104 | QCOMPARE(mediaStatusSpy.count(), 3); // LoadingMedia -> InvalidMedia -> NoMedia |
1105 | |
1106 | mediaSpy.clear(); |
1107 | currentMediaSpy.clear(); |
1108 | stateSpy.clear(); |
1109 | mediaStatusSpy.clear(); |
1110 | errorSpy.clear(); |
1111 | |
1112 | // <<< Invalid - 2nd pass >>> |
1113 | player.play(); |
1114 | QTRY_COMPARE(player.state(), QMediaPlayer::StoppedState); |
1115 | // media is not changed |
1116 | QCOMPARE(mediaSpy.count(), 0); |
1117 | // resolved playlist -> invalid -> "" |
1118 | QCOMPARE(currentMediaSpy.count(), 3); |
1119 | QCOMPARE(stateSpy.count(), 2); |
1120 | QCOMPARE(errorSpy.count(), 1); |
1121 | QCOMPARE(mediaStatusSpy.count(), 3); // LoadingMedia -> InvalidMedia -> NoMedia |
1122 | |
1123 | mediaSpy.clear(); |
1124 | currentMediaSpy.clear(); |
1125 | stateSpy.clear(); |
1126 | mediaStatusSpy.clear(); |
1127 | errorSpy.clear(); |
1128 | |
1129 | // <<< Invalid2 - 1st pass >>> |
1130 | fileInfo.setFile(QFINDTESTDATA("/testdata/invalid_media2.m3u" )); |
1131 | player.setMedia(media: QUrl::fromLocalFile(localfile: fileInfo.absoluteFilePath())); |
1132 | |
1133 | player.play(); |
1134 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 20000); |
1135 | // playlist -> resolved playlist |
1136 | QCOMPARE(mediaSpy.count(), 2); |
1137 | // playlist -> resolved playlist -> test.wav -> invalid -> test.wav -> "" |
1138 | QCOMPARE(currentMediaSpy.count(), 6); |
1139 | QCOMPARE(stateSpy.count(), 2); |
1140 | QCOMPARE(errorSpy.count(), 1); |
1141 | QCOMPARE(mediaStatusSpy.count(), 9); // 3 x LoadingMedia + 2 x (BufferedMedia -> EndOfMedia) + InvalidMedia + NoMedia (not in this order) |
1142 | |
1143 | mediaSpy.clear(); |
1144 | currentMediaSpy.clear(); |
1145 | stateSpy.clear(); |
1146 | mediaStatusSpy.clear(); |
1147 | errorSpy.clear(); |
1148 | |
1149 | // <<< Invalid2 - 2nd pass >>> |
1150 | player.play(); |
1151 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 20000); |
1152 | // playlist -> resolved playlist |
1153 | QCOMPARE(mediaSpy.count(), 0); |
1154 | // playlist -> test.wav -> invalid -> test.wav -> "" |
1155 | QCOMPARE(currentMediaSpy.count(), 5); |
1156 | QCOMPARE(stateSpy.count(), 2); |
1157 | QCOMPARE(errorSpy.count(), 1); |
1158 | QCOMPARE(mediaStatusSpy.count(), 9); // 3 x LoadingMedia + 2 x (BufferedMedia -> EndOfMedia) + InvalidMedia + NoMedia (not in this order) |
1159 | |
1160 | mediaSpy.clear(); |
1161 | currentMediaSpy.clear(); |
1162 | stateSpy.clear(); |
1163 | mediaStatusSpy.clear(); |
1164 | errorSpy.clear(); |
1165 | |
1166 | // <<< Recursive - 1st pass >>> |
1167 | fileInfo.setFile(QFINDTESTDATA("testdata/recursive_master.m3u" )); |
1168 | player.setMedia(media: QUrl::fromLocalFile(localfile: fileInfo.absoluteFilePath())); |
1169 | |
1170 | player.play(); |
1171 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 20000); |
1172 | // master playlist -> resolved master playlist |
1173 | QCOMPARE(mediaSpy.count(), 2); |
1174 | // master playlist -> resolved master playlist -> |
1175 | // recursive playlist -> resolved recursive playlist -> |
1176 | // recursive playlist (this URL is already in the chain of playlists, so the playlist is not resolved) -> |
1177 | // invalid -> test.wav -> "" |
1178 | QCOMPARE(currentMediaSpy.count(), 8); |
1179 | QCOMPARE(stateSpy.count(), 2); |
1180 | // there is one invalid media in the master playlist |
1181 | QCOMPARE(errorSpy.count(), 1); |
1182 | QCOMPARE(mediaStatusSpy.count(), 6); // LoadingMedia -> InvalidMedia -> LoadingMedia -> BufferedMedia |
1183 | // -> EndOfMedia -> NoMedia |
1184 | |
1185 | mediaSpy.clear(); |
1186 | currentMediaSpy.clear(); |
1187 | stateSpy.clear(); |
1188 | mediaStatusSpy.clear(); |
1189 | errorSpy.clear(); |
1190 | |
1191 | // <<< Recursive - 2nd pass >>> |
1192 | player.play(); |
1193 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 20000); |
1194 | QCOMPARE(mediaSpy.count(), 0); |
1195 | // resolved master playlist -> |
1196 | // resolved recursive playlist -> |
1197 | // recursive playlist (this URL is already in the chain of playlists, so the playlist is not resolved) -> |
1198 | // invalid -> test.wav -> "" |
1199 | QCOMPARE(currentMediaSpy.count(), 6); |
1200 | QCOMPARE(stateSpy.count(), 2); |
1201 | // there is one invalid media in the master playlist |
1202 | QCOMPARE(errorSpy.count(), 1); |
1203 | QCOMPARE(mediaStatusSpy.count(), 6); // LoadingMedia -> InvalidMedia -> LoadingMedia -> BufferedMedia |
1204 | // -> EndOfMedia -> NoMedia |
1205 | } |
1206 | |
1207 | void tst_QMediaPlayerBackend::playlistObject() |
1208 | { |
1209 | if (!isWavSupported()) |
1210 | QSKIP("Sound format is not supported" ); |
1211 | |
1212 | QMediaPlayer player; |
1213 | |
1214 | QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent))); |
1215 | QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); |
1216 | QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State))); |
1217 | QSignalSpy mediaStatusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); |
1218 | QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); |
1219 | |
1220 | // --- empty playlist |
1221 | QMediaPlaylist emptyPlaylist; |
1222 | player.setPlaylist(&emptyPlaylist); |
1223 | |
1224 | player.play(); |
1225 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1226 | |
1227 | QCOMPARE(mediaSpy.count(), 1); |
1228 | QCOMPARE(currentMediaSpy.count(), 1); // Empty media |
1229 | QCOMPARE(stateSpy.count(), 0); |
1230 | QCOMPARE(errorSpy.count(), 0); |
1231 | QCOMPARE(mediaStatusSpy.count(), 0); |
1232 | |
1233 | mediaSpy.clear(); |
1234 | currentMediaSpy.clear(); |
1235 | stateSpy.clear(); |
1236 | mediaStatusSpy.clear(); |
1237 | errorSpy.clear(); |
1238 | |
1239 | // --- Valid playlist |
1240 | QMediaPlaylist playlist; |
1241 | playlist.addMedia(content: QUrl::fromLocalFile(localfile: QFileInfo(QFINDTESTDATA("testdata/test.wav" )).absoluteFilePath())); |
1242 | playlist.addMedia(content: QUrl::fromLocalFile(localfile: QFileInfo(QFINDTESTDATA("testdata/_test.wav" )).absoluteFilePath())); |
1243 | player.setPlaylist(&playlist); |
1244 | |
1245 | player.play(); |
1246 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1247 | |
1248 | QCOMPARE(mediaSpy.count(), 1); |
1249 | QCOMPARE(currentMediaSpy.count(), 3); // test.wav -> _test.wav -> NoMedia |
1250 | QCOMPARE(stateSpy.count(), 2); |
1251 | QCOMPARE(errorSpy.count(), 0); |
1252 | QCOMPARE(mediaStatusSpy.count(), 7); // 2 x (LoadingMedia -> BufferedMedia -> EndOfMedia) + NoMedia |
1253 | |
1254 | mediaSpy.clear(); |
1255 | currentMediaSpy.clear(); |
1256 | stateSpy.clear(); |
1257 | mediaStatusSpy.clear(); |
1258 | errorSpy.clear(); |
1259 | |
1260 | player.play(); |
1261 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1262 | |
1263 | QCOMPARE(mediaSpy.count(), 0); |
1264 | QCOMPARE(currentMediaSpy.count(), 4); // playlist -> test.wav -> _test.wav -> NoMedia |
1265 | QCOMPARE(stateSpy.count(), 2); |
1266 | QCOMPARE(errorSpy.count(), 0); |
1267 | QCOMPARE(mediaStatusSpy.count(), 7); // 2 x (LoadingMedia -> BufferedMedia -> EndOfMedia) + NoMedia |
1268 | |
1269 | player.setPlaylist(nullptr); |
1270 | |
1271 | mediaSpy.clear(); |
1272 | currentMediaSpy.clear(); |
1273 | stateSpy.clear(); |
1274 | mediaStatusSpy.clear(); |
1275 | errorSpy.clear(); |
1276 | |
1277 | // --- Nested playlist |
1278 | QMediaPlaylist nestedPlaylist; |
1279 | nestedPlaylist.addMedia(content: QUrl::fromLocalFile(localfile: QFileInfo(QFINDTESTDATA("testdata/_test.wav" )).absoluteFilePath())); |
1280 | nestedPlaylist.addMedia(content: QUrl::fromLocalFile(localfile: QFileInfo(QFINDTESTDATA("testdata/test.wav" )).absoluteFilePath())); |
1281 | nestedPlaylist.addMedia(content: &playlist); |
1282 | player.setPlaylist(&nestedPlaylist); |
1283 | |
1284 | player.play(); |
1285 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1286 | |
1287 | QCOMPARE(mediaSpy.count(), 1); |
1288 | QCOMPARE(currentMediaSpy.count(), 6); // _test.wav -> test.wav -> nested playlist |
1289 | // -> test.wav -> _test.wav -> NoMedia |
1290 | QCOMPARE(stateSpy.count(), 2); |
1291 | QCOMPARE(errorSpy.count(), 0); |
1292 | QCOMPARE(mediaStatusSpy.count(), 13); // 4 x (LoadingMedia -> BufferedMedia -> EndOfMedia) + NoMedia |
1293 | |
1294 | player.setPlaylist(nullptr); |
1295 | |
1296 | mediaSpy.clear(); |
1297 | currentMediaSpy.clear(); |
1298 | stateSpy.clear(); |
1299 | mediaStatusSpy.clear(); |
1300 | errorSpy.clear(); |
1301 | |
1302 | // --- playlist with invalid media |
1303 | QMediaPlaylist invalidPlaylist; |
1304 | invalidPlaylist.addMedia(content: QUrl("invalid" )); |
1305 | invalidPlaylist.addMedia(content: QUrl::fromLocalFile(localfile: QFileInfo(QFINDTESTDATA("testdata/test.wav" )).absoluteFilePath())); |
1306 | |
1307 | player.setPlaylist(&invalidPlaylist); |
1308 | |
1309 | player.play(); |
1310 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1311 | |
1312 | QCOMPARE(mediaSpy.count(), 1); |
1313 | QCOMPARE(currentMediaSpy.count(), 3); // invalid -> test.wav -> NoMedia |
1314 | QCOMPARE(stateSpy.count(), 2); |
1315 | QCOMPARE(errorSpy.count(), 1); |
1316 | QCOMPARE(mediaStatusSpy.count(), 6); // Loading -> Invalid -> Loading -> Buffered -> EndOfMedia -> NoMedia |
1317 | |
1318 | player.setPlaylist(nullptr); |
1319 | |
1320 | mediaSpy.clear(); |
1321 | currentMediaSpy.clear(); |
1322 | stateSpy.clear(); |
1323 | mediaStatusSpy.clear(); |
1324 | errorSpy.clear(); |
1325 | |
1326 | // --- playlist with only invalid media |
1327 | QMediaPlaylist invalidPlaylist2; |
1328 | invalidPlaylist2.addMedia(content: QUrl("invalid" )); |
1329 | invalidPlaylist2.addMedia(content: QUrl("invalid2" )); |
1330 | |
1331 | player.setPlaylist(&invalidPlaylist2); |
1332 | |
1333 | player.play(); |
1334 | QTRY_COMPARE_WITH_TIMEOUT(player.state(), QMediaPlayer::StoppedState, 10000); |
1335 | |
1336 | QCOMPARE(mediaSpy.count(), 1); |
1337 | QCOMPARE(currentMediaSpy.count(), 3); // invalid -> invalid2 -> NoMedia |
1338 | QCOMPARE(stateSpy.count(), 2); |
1339 | QCOMPARE(errorSpy.count(), 2); |
1340 | QCOMPARE(mediaStatusSpy.count(), 5); // Loading -> Invalid -> Loading -> Invalid -> NoMedia |
1341 | } |
1342 | |
1343 | void tst_QMediaPlayerBackend::surfaceTest_data() |
1344 | { |
1345 | QTest::addColumn< QList<QVideoFrame::PixelFormat> >(name: "formatsList" ); |
1346 | |
1347 | QList<QVideoFrame::PixelFormat> formatsRGB; |
1348 | formatsRGB << QVideoFrame::Format_RGB32 |
1349 | << QVideoFrame::Format_ARGB32 |
1350 | << QVideoFrame::Format_RGB565 |
1351 | << QVideoFrame::Format_BGRA32; |
1352 | |
1353 | QList<QVideoFrame::PixelFormat> formatsYUV; |
1354 | formatsYUV << QVideoFrame::Format_YUV420P |
1355 | << QVideoFrame::Format_YUV422P |
1356 | << QVideoFrame::Format_YV12 |
1357 | << QVideoFrame::Format_UYVY |
1358 | << QVideoFrame::Format_YUYV |
1359 | << QVideoFrame::Format_NV12 |
1360 | << QVideoFrame::Format_NV21; |
1361 | |
1362 | QTest::newRow(dataTag: "RGB formats" ) |
1363 | << formatsRGB; |
1364 | |
1365 | #if !QT_CONFIG(directshow) |
1366 | QTest::newRow(dataTag: "YVU formats" ) |
1367 | << formatsYUV; |
1368 | #endif |
1369 | |
1370 | QTest::newRow(dataTag: "RGB & YUV formats" ) |
1371 | << formatsRGB + formatsYUV; |
1372 | } |
1373 | |
1374 | void tst_QMediaPlayerBackend::surfaceTest() |
1375 | { |
1376 | // 25 fps video file |
1377 | if (localVideoFile.isNull()) |
1378 | QSKIP("No supported video file" ); |
1379 | |
1380 | QFETCH(QList<QVideoFrame::PixelFormat>, formatsList); |
1381 | |
1382 | TestVideoSurface surface(false); |
1383 | surface.setSupportedFormats(formatsList); |
1384 | QMediaPlayer player; |
1385 | player.setVideoOutput(&surface); |
1386 | player.setMedia(media: localVideoFile); |
1387 | player.play(); |
1388 | QTRY_VERIFY(player.position() >= 1000); |
1389 | if (surface.error() == QAbstractVideoSurface::UnsupportedFormatError) |
1390 | QSKIP("None of the pixel formats is supported by the backend" ); |
1391 | QVERIFY2(surface.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1" ).arg(surface.m_totalFrames))); |
1392 | } |
1393 | |
1394 | void tst_QMediaPlayerBackend::multipleSurfaces() |
1395 | { |
1396 | if (localVideoFile.isNull()) |
1397 | QSKIP("No supported video file" ); |
1398 | |
1399 | QList<QVideoFrame::PixelFormat> formats1; |
1400 | formats1 << QVideoFrame::Format_RGB32 |
1401 | << QVideoFrame::Format_ARGB32; |
1402 | QList<QVideoFrame::PixelFormat> formats2; |
1403 | formats2 << QVideoFrame::Format_YUV420P |
1404 | << QVideoFrame::Format_RGB32; |
1405 | |
1406 | TestVideoSurface surface1(false); |
1407 | surface1.setSupportedFormats(formats1); |
1408 | TestVideoSurface surface2(false); |
1409 | surface2.setSupportedFormats(formats2); |
1410 | |
1411 | QMediaPlayer player; |
1412 | player.setVideoOutput(QVector<QAbstractVideoSurface *>() << &surface1 << &surface2); |
1413 | player.setMedia(media: localVideoFile); |
1414 | player.play(); |
1415 | QTRY_VERIFY(player.position() >= 1000); |
1416 | QVERIFY2(surface1.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1" ).arg(surface1.m_totalFrames))); |
1417 | QVERIFY2(surface2.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1" ).arg(surface2.m_totalFrames))); |
1418 | QCOMPARE(surface1.m_totalFrames, surface2.m_totalFrames); |
1419 | } |
1420 | |
1421 | void tst_QMediaPlayerBackend::metadata() |
1422 | { |
1423 | if (localFileWithMetadata.isNull()) |
1424 | QSKIP("No supported media file" ); |
1425 | |
1426 | QMediaPlayer player; |
1427 | |
1428 | QSignalSpy metadataAvailableSpy(&player, SIGNAL(metaDataAvailableChanged(bool))); |
1429 | QSignalSpy metadataChangedSpy(&player, SIGNAL(metaDataChanged())); |
1430 | |
1431 | player.setMedia(media: localFileWithMetadata); |
1432 | |
1433 | QTRY_VERIFY(player.isMetaDataAvailable()); |
1434 | QCOMPARE(metadataAvailableSpy.count(), 1); |
1435 | QVERIFY(metadataAvailableSpy.last()[0].toBool()); |
1436 | QVERIFY(metadataChangedSpy.count() > 0); |
1437 | |
1438 | QCOMPARE(player.metaData(QMediaMetaData::Title).toString(), QStringLiteral("Nokia Tune" )); |
1439 | QCOMPARE(player.metaData(QMediaMetaData::ContributingArtist).toString(), QStringLiteral("TestArtist" )); |
1440 | QCOMPARE(player.metaData(QMediaMetaData::AlbumTitle).toString(), QStringLiteral("TestAlbum" )); |
1441 | |
1442 | metadataAvailableSpy.clear(); |
1443 | metadataChangedSpy.clear(); |
1444 | |
1445 | player.setMedia(media: QMediaContent()); |
1446 | |
1447 | QVERIFY(!player.isMetaDataAvailable()); |
1448 | QCOMPARE(metadataAvailableSpy.count(), 1); |
1449 | QVERIFY(!metadataAvailableSpy.last()[0].toBool()); |
1450 | QCOMPARE(metadataChangedSpy.count(), 1); |
1451 | QVERIFY(player.availableMetaData().isEmpty()); |
1452 | } |
1453 | |
1454 | void tst_QMediaPlayerBackend::playerStateAtEOS() |
1455 | { |
1456 | if (!isWavSupported()) |
1457 | QSKIP("Sound format is not supported" ); |
1458 | |
1459 | QMediaPlayer player; |
1460 | |
1461 | bool endOfMediaReceived = false; |
1462 | connect(sender: &player, signal: &QMediaPlayer::mediaStatusChanged, slot: [&](QMediaPlayer::MediaStatus status) { |
1463 | if (status == QMediaPlayer::EndOfMedia) { |
1464 | QCOMPARE(player.state(), QMediaPlayer::StoppedState); |
1465 | endOfMediaReceived = true; |
1466 | } |
1467 | }); |
1468 | |
1469 | player.setMedia(media: localWavFile); |
1470 | player.play(); |
1471 | |
1472 | QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia); |
1473 | QVERIFY(endOfMediaReceived); |
1474 | } |
1475 | |
1476 | void tst_QMediaPlayerBackend::playFromBuffer() |
1477 | { |
1478 | if (localVideoFile.isNull()) |
1479 | QSKIP("No supported video file" ); |
1480 | |
1481 | TestVideoSurface surface(false); |
1482 | QMediaPlayer player; |
1483 | player.setVideoOutput(&surface); |
1484 | QFile file(localVideoFile.request().url().toLocalFile()); |
1485 | if (!file.open(flags: QIODevice::ReadOnly)) |
1486 | QSKIP("Could not open file" ); |
1487 | player.setMedia(media: localVideoFile, stream: &file); |
1488 | player.play(); |
1489 | QTRY_VERIFY(player.position() >= 1000); |
1490 | if (surface.error() == QAbstractVideoSurface::UnsupportedFormatError) |
1491 | QSKIP("None of the pixel formats is supported by the backend" ); |
1492 | QVERIFY2(surface.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1" ).arg(surface.m_totalFrames))); |
1493 | } |
1494 | |
1495 | TestVideoSurface::TestVideoSurface(bool storeFrames): |
1496 | m_totalFrames(0), |
1497 | m_storeFrames(storeFrames) |
1498 | { |
1499 | // set default formats |
1500 | m_supported << QVideoFrame::Format_RGB32 |
1501 | << QVideoFrame::Format_ARGB32 |
1502 | << QVideoFrame::Format_ARGB32_Premultiplied |
1503 | << QVideoFrame::Format_RGB565 |
1504 | << QVideoFrame::Format_RGB555; |
1505 | } |
1506 | |
1507 | QList<QVideoFrame::PixelFormat> TestVideoSurface::supportedPixelFormats( |
1508 | QAbstractVideoBuffer::HandleType handleType) const |
1509 | { |
1510 | if (handleType == QAbstractVideoBuffer::NoHandle) { |
1511 | return m_supported; |
1512 | } else { |
1513 | return QList<QVideoFrame::PixelFormat>(); |
1514 | } |
1515 | } |
1516 | |
1517 | bool TestVideoSurface::start(const QVideoSurfaceFormat &format) |
1518 | { |
1519 | if (!isFormatSupported(format)) { |
1520 | setError(UnsupportedFormatError); |
1521 | return false; |
1522 | } |
1523 | |
1524 | return QAbstractVideoSurface::start(format); |
1525 | } |
1526 | |
1527 | void TestVideoSurface::stop() |
1528 | { |
1529 | QAbstractVideoSurface::stop(); |
1530 | } |
1531 | |
1532 | bool TestVideoSurface::present(const QVideoFrame &frame) |
1533 | { |
1534 | if (m_storeFrames) |
1535 | m_frameList.push_back(t: frame); |
1536 | m_totalFrames++; |
1537 | return true; |
1538 | } |
1539 | |
1540 | |
1541 | void ProbeDataHandler::processFrame(const QVideoFrame &frame) |
1542 | { |
1543 | m_frameList.append(t: frame); |
1544 | } |
1545 | |
1546 | void ProbeDataHandler::processBuffer(const QAudioBuffer &buffer) |
1547 | { |
1548 | m_bufferList.append(t: buffer); |
1549 | } |
1550 | |
1551 | void ProbeDataHandler::flushVideo() |
1552 | { |
1553 | isVideoFlushCalled = true; |
1554 | } |
1555 | |
1556 | void ProbeDataHandler::flushAudio() |
1557 | { |
1558 | |
1559 | } |
1560 | |
1561 | QTEST_MAIN(tst_QMediaPlayerBackend) |
1562 | #include "tst_qmediaplayerbackend.moc" |
1563 | |
1564 | |