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
44QT_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
53class tst_QMediaPlayerBackend : public QObject
54{
55 Q_OBJECT
56public slots:
57 void init();
58 void cleanup();
59 void initTestCase();
60
61private 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
86private:
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
104class TestVideoSurface : public QAbstractVideoSurface
105{
106 Q_OBJECT
107public:
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
123private:
124 bool m_storeFrames;
125 QList<QVideoFrame::PixelFormat> m_supported;
126};
127
128class ProbeDataHandler : public QObject
129{
130 Q_OBJECT
131
132public:
133 ProbeDataHandler() : isVideoFlushCalled(false) { }
134
135 QList<QVideoFrame> m_frameList;
136 QList<QAudioBuffer> m_bufferList;
137 bool isVideoFlushCalled;
138
139public slots:
140 void processFrame(const QVideoFrame&);
141 void processBuffer(const QAudioBuffer&);
142 void flushVideo();
143 void flushAudio();
144};
145
146void tst_QMediaPlayerBackend::init()
147{
148}
149
150QMediaContent 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
180bool tst_QMediaPlayerBackend::isWavSupported()
181{
182 return !localWavFile.isNull();
183}
184
185void 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
213void tst_QMediaPlayerBackend::cleanup()
214{
215}
216
217void tst_QMediaPlayerBackend::construction()
218{
219 QMediaPlayer player;
220 QTRY_VERIFY(player.isAvailable());
221}
222
223void 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
259void 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
309void 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
324void 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
479void 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()
583class DeleteLaterAtEos : public QObject
584{
585 Q_OBJECT
586public:
587 DeleteLaterAtEos(QMediaPlayer* p) : player(p)
588 {
589 }
590
591public slots:
592 void play()
593 {
594 QVERIFY(connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
595 this, SLOT(onMediaStatusChanged(QMediaPlayer::MediaStatus))));
596 player->play();
597 }
598
599private slots:
600 void onMediaStatusChanged(QMediaPlayer::MediaStatus status)
601 {
602 if (status == QMediaPlayer::EndOfMedia) {
603 player-> deleteLater();
604 player = 0;
605 }
606 }
607
608private:
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
614void 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
636void 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
683void 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
696void 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
741void 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
766void 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
844void 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
963void 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
1003void 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
1040void 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
1207void 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
1343void 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
1374void 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
1394void 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
1421void 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
1454void 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
1476void 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
1495TestVideoSurface::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
1507QList<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
1517bool 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
1527void TestVideoSurface::stop()
1528{
1529 QAbstractVideoSurface::stop();
1530}
1531
1532bool 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
1541void ProbeDataHandler::processFrame(const QVideoFrame &frame)
1542{
1543 m_frameList.append(t: frame);
1544}
1545
1546void ProbeDataHandler::processBuffer(const QAudioBuffer &buffer)
1547{
1548 m_bufferList.append(t: buffer);
1549}
1550
1551void ProbeDataHandler::flushVideo()
1552{
1553 isVideoFlushCalled = true;
1554}
1555
1556void ProbeDataHandler::flushAudio()
1557{
1558
1559}
1560
1561QTEST_MAIN(tst_QMediaPlayerBackend)
1562#include "tst_qmediaplayerbackend.moc"
1563
1564

source code of qtmultimedia/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp