1 /*
2 Copyright (C) 2012 Openismus GmbH
3 @author Mathias Hasselmann <mathias@openismus.com>
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published
7 by the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "qgsttest.h"
19
20 #include <QGlib/Connect>
21 #include <QGlib/Error>
22
23 #include <QGst/Caps>
24 #include <QGst/ClockTime>
25 #include <QGst/Discoverer>
26
27 #include <gst/pbutils/pbutils.h>
28
29 // Declare a simple tag list that actually supports iteration. Needed for our test data.
30 typedef QPair<const char *, QGlib::Value> Tag;
31 typedef QList<Tag> TagList;
32
makeTag(const char * name,const QGlib::Value & value)33 static Tag makeTag(const char *name, const QGlib::Value &value)
34 {
35 return qMakePair(name, value);
36 }
37
38 namespace QGlib {
39
40 // Declare a simple compare operator for QGlib::Value.
41 // Comparing by string value isn't accurate at all, but good enough for our testing purposes.
operator ==(const Value & a,const Value & b)42 static bool operator ==(const Value &a, const Value &b)
43 {
44 bool okA = false, okB = false;
45 return a.type() == b.type() && a.toString(&okA) == b.toString(&okB) && okA && okB;
46 }
47
operator !=(const Value & a,const Value & b)48 static bool operator !=(const Value &a, const Value &b)
49 {
50 return !(a == b);
51 }
52
53 } //namespace QGlib
54
55 // This classes describes what kind of streams we expect.
56 class StreamInfo : public QSharedData
57 {
58 Q_DISABLE_COPY(StreamInfo)
59
60 public:
61 enum DebugMode
62 {
63 NoDebug,
64 Debug
65 };
66
67 enum CapsMode
68 {
69 AutoCaps,
70 ManualCaps
71 };
72
~StreamInfo()73 virtual ~StreamInfo() {}
74
75 protected:
StreamInfo(const QString & caps,CapsMode capsMode)76 StreamInfo(const QString &caps, CapsMode capsMode)
77 : m_isNative(true)
78 , m_caps(QGst::Caps::fromString(caps))
79 , m_capsMode(capsMode)
80 {
81 }
82
83 template<typename T>
84 class Field
85 {
86 public:
Field()87 Field()
88 : m_value(T())
89 , m_assigned(false)
90 {
91 }
92
operator =(const T & value)93 Field & operator =(const T &value)
94 {
95 m_value = value;
96 m_assigned = true;
97 return *this;
98 }
99
operator ==(const T & other) const100 bool operator ==(const T &other) const
101 {
102 return !m_assigned || m_value == other;
103 }
104
operator !=(const T & other) const105 bool operator !=(const T &other) const
106 {
107 return !operator =(other);
108 }
109
110 private:
111 T m_value;
112 bool m_assigned;
113 };
114
115 public:
acceptStreamType(QGlib::Type streamType) const116 virtual bool acceptStreamType(QGlib::Type streamType) const
117 {
118 return streamType.isA(GST_TYPE_DISCOVERER_STREAM_INFO);
119 }
120
acceptStream(QGst::DiscovererStreamInfoPtr info) const121 virtual bool acceptStream(QGst::DiscovererStreamInfoPtr info) const
122 {
123 return acceptStream(info, NoDebug);
124 }
125
acceptStream(QGst::DiscovererStreamInfoPtr info,DebugMode debugMode) const126 virtual bool acceptStream(QGst::DiscovererStreamInfoPtr info, DebugMode debugMode) const
127 {
128 if (info.isNull()) {
129 if (debugMode) {
130 qDebug() << "stream info is null";
131 }
132 return false;
133 }
134
135 if (info->tags().isEmpty() != m_tags.isEmpty()) {
136 if (debugMode) {
137 qDebug() << (m_tags.isEmpty() ? "tags should be empty, but aren't"
138 : "none of the expected tags found");
139 qDebug() << m_tags;
140 }
141
142 return false;
143 }
144
145 Q_FOREACH(const Tag &tag, m_tags) {
146 if (info->tags().tagValue(tag.first) != tag.second) {
147 if (debugMode) {
148 qDebug() << tag.first << "tag has bad value\n"
149 << " expected value:" << tag.second << "\n"
150 << " actual value: " << info->tags().tagValue(tag.first);
151 }
152 return false;
153 }
154 }
155
156 if (!info->caps()->isAlwaysCompatibleWith(m_caps)) {
157 if (debugMode) {
158 qDebug() << "capabilities are incompatible\n"
159 << " expected caps:" << m_caps->toString() << "\n"
160 << " actual caps: " << info->caps()->toString();
161 }
162
163 return false;
164 }
165
166 return true;
167 }
168
setNative(bool value)169 StreamInfo * setNative(bool value) { m_isNative = value; return this; }
isNative() const170 bool isNative() const { return m_isNative; }
171
addTag(const char * name,const QGlib::Value & value)172 StreamInfo * addTag(const char *name, const QGlib::Value &value)
173 {
174 m_tags += makeTag(name, value);
175 return this;
176 }
177
addCap(const char * name,const QGlib::Value & value)178 StreamInfo * addCap(const char *name, const QGlib::Value &value)
179 {
180 if (m_capsMode == AutoCaps) {
181 m_caps->setValue(name, value);
182 }
183
184 return this;
185 }
186
isImage() const187 bool isImage() const
188 {
189 return m_caps->toString().startsWith("image/");
190 }
191
192 private:
193 bool m_isNative;
194 TagList m_tags;
195 QGst::CapsPtr m_caps;
196 CapsMode m_capsMode;
197 };
198
199 class AudioStreamInfo : public StreamInfo
200 {
201 public:
AudioStreamInfo(const QString & caps,CapsMode capsMode=AutoCaps)202 AudioStreamInfo(const QString &caps, CapsMode capsMode = AutoCaps)
203 : StreamInfo(caps, capsMode)
204 {
205 }
206
acceptStreamType(QGlib::Type streamType) const207 bool acceptStreamType(QGlib::Type streamType) const
208 {
209 return streamType.isA(GST_TYPE_DISCOVERER_AUDIO_INFO);
210 }
211
acceptStream(QGst::DiscovererStreamInfoPtr info) const212 bool acceptStream(QGst::DiscovererStreamInfoPtr info) const
213 {
214 QGst::DiscovererAudioInfoPtr audioInfo = info.dynamicCast<QGst::DiscovererAudioInfo>();
215
216 return !audioInfo.isNull()
217 && StreamInfo::acceptStream(info)
218 && m_channels == audioInfo->channels()
219 && m_sampleRate == audioInfo->sampleRate()
220 && m_bitrate == audioInfo->bitrate()
221 && m_depth == audioInfo->depth();
222 }
223
setChannels(uint value)224 AudioStreamInfo * setChannels(uint value) { m_channels = value; addCap("channels", int(value)); return this; }
setSampleRate(uint value)225 AudioStreamInfo * setSampleRate(uint value) { m_sampleRate = value; addCap("rate", int(value)); return this; }
setBitrate(uint value)226 AudioStreamInfo * setBitrate(uint value) { m_bitrate = value; addTag("bitrate", value); return this; }
setDepth(uint value)227 AudioStreamInfo * setDepth(uint value) { m_depth = value; return this; }
228
229 private:
230 Field<uint> m_channels;
231 Field<uint> m_sampleRate;
232 Field<uint> m_bitrate;
233 Field<uint> m_depth;
234 };
235
236 class VideoStreamInfo : public StreamInfo
237 {
238 public:
VideoStreamInfo(const QString & caps,CapsMode capsMode=AutoCaps)239 VideoStreamInfo(const QString &caps, CapsMode capsMode = AutoCaps)
240 : StreamInfo(caps, capsMode)
241 {
242 if (capsMode != AutoCaps) {
243 qWarning("working around PNG decoder bug");
244 }
245 }
246
acceptStreamType(QGlib::Type streamType) const247 bool acceptStreamType(QGlib::Type streamType) const
248 {
249 return streamType.isA(GST_TYPE_DISCOVERER_VIDEO_INFO);
250 }
251
acceptStream(QGst::DiscovererStreamInfoPtr info) const252 bool acceptStream(QGst::DiscovererStreamInfoPtr info) const
253 {
254 QGst::DiscovererVideoInfoPtr videoInfo = info.dynamicCast<QGst::DiscovererVideoInfo>();
255
256 return !videoInfo.isNull()
257 && StreamInfo::acceptStream(info)
258 && isImage() == videoInfo->isImage()
259 && m_width == videoInfo->width()
260 && m_height == videoInfo->height()
261 && m_interlaced == videoInfo->isInterlaced()
262 && m_bitrate == videoInfo->bitrate()
263 && m_framerate == videoInfo->framerate()
264 && m_pixelAspectRatio == videoInfo->pixelAspectRatio();
265 }
266
setWidth(uint value)267 VideoStreamInfo * setWidth(uint value) { m_width = value; addCap("width", int(value)); return this; }
setHeight(uint value)268 VideoStreamInfo * setHeight(uint value) { m_height = value; addCap("height", int(value)); return this; }
setInterlaced(bool value)269 VideoStreamInfo * setInterlaced(bool value) { m_interlaced = value; return this; }
setBitrate(uint value)270 VideoStreamInfo * setBitrate(uint value) { m_bitrate = value; addTag("bitrate", value); return this; }
setFramerate(const QGst::Fraction & value)271 VideoStreamInfo * setFramerate(const QGst::Fraction &value) { m_framerate = value; addCap("framerate", QGlib::Value::create(value)); return this; }
setPixelAspectRatio(const QGst::Fraction & value)272 VideoStreamInfo * setPixelAspectRatio(const QGst::Fraction &value) { m_pixelAspectRatio = value; addCap("pixel-aspect-ratio", QGlib::Value::create(value)); return this; }
273
274 private:
275 Field<uint> m_width;
276 Field<uint> m_height;
277 Field<bool> m_interlaced;
278 Field<uint> m_bitrate;
279 Field<QGst::Fraction> m_framerate;
280 Field<QGst::Fraction> m_pixelAspectRatio;
281 };
282
283 typedef QSharedDataPointer<StreamInfo> StreamInfoPtr;
284 typedef QList<StreamInfoPtr> StreamInfoList;
285
286 // A few filtering algorithms
287 template<class Container, class UnaryPredicate>
filter(const Container & input,UnaryPredicate predicate)288 static Container filter(const Container &input, UnaryPredicate predicate)
289 {
290 Container result;
291
292 const typename Container::ConstIterator end = input.constEnd();
293 for(typename Container::ConstIterator it = input.constBegin(); it != end; ++it) {
294 if (predicate(it->data())) {
295 result += *it;
296 }
297 }
298
299 return result;
300 }
301
filterByStreamType(const StreamInfoList & streams,QGlib::Type streamType)302 static StreamInfoList filterByStreamType(const StreamInfoList &streams, QGlib::Type streamType)
303 {
304 return filter(streams, std::bind2nd(std::mem_fun(&StreamInfo::acceptStreamType), streamType));
305 }
306
307 // Declare a few metatypes, so that we can use this types for test data.
308 Q_DECLARE_METATYPE(QGst::ClockTime)
309 Q_DECLARE_METATYPE(StreamInfoList)
310 Q_DECLARE_METATYPE(TagList)
311
312 // The actual test
313 class DiscovererTest : public QGstTest
314 {
315 Q_OBJECT
316
317 public:
318 enum DiscoveryState {
319 DiscoveryPending,
320 DiscoveryStarted,
321 UriDiscovered,
322 DiscoveryFinished
323 };
324
325 private:
326 void setupDiscoveryData();
327 void verifyStreamInfo(QGst::DiscovererInfoPtr info);
328
329 private Q_SLOTS:
330 void cleanup();
331
332 void testGLibTypes_data();
333 void testGLibTypes();
334
335 void testSyncDiscovery_data();
336 void testSyncDiscovery();
337
338 void testAsyncDiscovery_data();
339 void testAsyncDiscovery();
340
341 protected: // mark as protected to avoid that QTestLib invoking those methods as test
342 void onStartingDiscovery();
343 void onUriDiscovered(QGst::DiscovererInfoPtr info, const QGlib::Error &error);
344 void onDiscoveryFinished();
345
346 private:
347 QScopedPointer<QEventLoop> m_eventLoop;
348 DiscoveryState m_discoveryState;
349 };
350
351 namespace QTest {
toString(const DiscovererTest::DiscoveryState & state)352 template<> char *toString(const DiscovererTest::DiscoveryState &state)
353 {
354 switch(state) {
355 case DiscovererTest::DiscoveryPending: return toString("discovery-pending");
356 case DiscovererTest::DiscoveryStarted: return toString("discovery-started");
357 case DiscovererTest::UriDiscovered: return toString("uri-discovered");
358 case DiscovererTest::DiscoveryFinished: return toString("discovery-finished");
359 }
360
361 return toString(uint(state));
362 }
363 }
364
setupDiscoveryData()365 void DiscovererTest::setupDiscoveryData()
366 {
367 QTest::addColumn<QUrl>("uri");
368 QTest::addColumn<int>("errorCode");
369 QTest::addColumn<QString>("errorDomain");
370 QTest::addColumn<QGst::ClockTime>("duration");
371 QTest::addColumn<bool>("seekable");
372 QTest::addColumn<StreamInfoList>("streams");
373 QTest::addColumn<TagList>("expectedTags");
374
375 const bool Seekable = true;
376 const bool NonSeekable = false;
377 const QUrl baseUrl = QUrl::fromLocalFile(QString::fromLocal8Bit(SRCDIR) + "/");
378
379 QTest::newRow("null")
380 << QUrl("about:null")
381 << int(GST_CORE_ERROR_MISSING_PLUGIN)
382 << QString::fromUtf8(g_quark_to_string(GST_CORE_ERROR))
383 << QGst::ClockTime(0) << Seekable
384 << StreamInfoList()
385 << TagList();
386 QTest::newRow("numbers.ogv")
387 << baseUrl.resolved(QUrl::fromEncoded("data/numbers.ogv")) << 0 << QString()
388 << QGst::ClockTime(2017333333) << Seekable
389 << (StreamInfoList()
390 << StreamInfoPtr((new AudioStreamInfo("audio/x-flac, framed=true"))
391 ->setSampleRate(48000)->setChannels(1)->setDepth(16)
392 ->addTag("audio-codec", "FLAC")
393 ->addTag("container-format", "Ogg"))
394 << StreamInfoPtr((new AudioStreamInfo("audio/x-flac"))
395 ->setSampleRate(48000)->setChannels(1)
396 ->setNative(false))
397 << StreamInfoPtr((new VideoStreamInfo("video/x-theora"))
398 ->setWidth(160)->setHeight(120)->setInterlaced(false)
399 ->setBitrate(200000)->setFramerate(QGst::Fraction(5, 1))
400 ->setPixelAspectRatio(QGst::Fraction(1, 1))
401 ->addTag("container-format", "Ogg")
402 ->addTag("video-codec", "Theora")))
403 << (TagList()
404 << makeTag("container-format", "Ogg")
405 << makeTag("audio-codec", "FLAC")
406 << makeTag("video-codec", "Theora")
407 << makeTag("bitrate", 200000U));
408 QTest::newRow("numbers07.png")
409 << baseUrl.resolved(QUrl::fromEncoded("data/numbers07.png")) << 0 << QString()
410 << QGst::ClockTime(0) << Seekable
411 << (StreamInfoList()
412 << StreamInfoPtr((new VideoStreamInfo("image/png", VideoStreamInfo::ManualCaps))
413 ->setWidth(160)->setHeight(120)->setInterlaced(false)))
414 << (TagList());
415 QTest::newRow("numbers07.jpg")
416 << baseUrl.resolved(QUrl::fromEncoded("data/numbers07.jpg")) << 0 << QString()
417 << QGst::ClockTime(0) << NonSeekable
418 << (StreamInfoList()
419 << StreamInfoPtr((new VideoStreamInfo("image/jpeg"))
420 ->setWidth(120)->setHeight(160)->setInterlaced(false)))
421 << (TagList());
422 QTest::newRow("sine.ogg")
423 << baseUrl.resolved(QUrl::fromEncoded("data/sine.ogg")) << 0 << QString()
424 << QGst::ClockTime::fromSeconds(2) << Seekable
425 << (StreamInfoList()
426 << StreamInfoPtr((new AudioStreamInfo("audio/x-vorbis"))
427 ->setSampleRate(48000)->setChannels(1)
428 ->setBitrate(80000)
429 ->addTag("container-format", "Ogg")
430 ->addTag("audio-codec", "Vorbis")))
431 << (TagList()
432 << makeTag("container-format", "Ogg")
433 << makeTag("audio-codec", "Vorbis")
434 << makeTag("bitrate", 80000U));
435 }
436
verifyStreamInfo(QGst::DiscovererInfoPtr info)437 void DiscovererTest::verifyStreamInfo(QGst::DiscovererInfoPtr info)
438 {
439 // verify discovery result
440 QVERIFY(!info.isNull());
441 QTEST(info->uri(), "uri");
442 QTEST(QString(), "errorDomain");
443 QCOMPARE(info->result(), QGst::DiscovererOk);
444 QTEST(info->duration(), "duration");
445 QTEST(info->seekable(), "seekable");
446 QVERIFY(!info->misc().isValid());
447
448 QFETCH(TagList, expectedTags);
449 QCOMPARE(info->tags().isEmpty(), expectedTags.isEmpty());
450
451 Q_FOREACH(const Tag &tag, expectedTags) {
452 QCOMPARE(info->tags().tagValue(tag.first), tag.second);
453 }
454
455 QFETCH(StreamInfoList, streams);
456 QVERIFY(!info->streamInfo().isNull());
457
458 if (streams.count() != 1 || !(*streams.constBegin())->isImage()) {
459 const QGst::DiscovererContainerInfoPtr root = info->streamInfo().dynamicCast<QGst::DiscovererContainerInfo>();
460 QVERIFY(!root.isNull());
461
462 const StreamInfoList nativeStreams = filter(streams, std::mem_fun(&StreamInfo::isNative));
463 QCOMPARE(root->streams().count(), nativeStreams.count());
464 } else {
465 const QGst::DiscovererVideoInfoPtr root = info->streamInfo().dynamicCast<QGst::DiscovererVideoInfo>();
466 QVERIFY(!root.isNull());
467 QVERIFY(root->isImage());
468 }
469
470 const StreamInfoList audioStreams = filterByStreamType(streams, GST_TYPE_DISCOVERER_AUDIO_INFO);
471 QCOMPARE(info->streams(GST_TYPE_DISCOVERER_AUDIO_INFO).count(), audioStreams.count());
472 QCOMPARE(info->audioStreams().count(), audioStreams.count());
473
474 const StreamInfoList videoStreams = filterByStreamType(streams, GST_TYPE_DISCOVERER_VIDEO_INFO);
475 QCOMPARE(info->streams(GST_TYPE_DISCOVERER_VIDEO_INFO).count(), videoStreams.count());
476 QCOMPARE(info->videoStreams().count(), videoStreams.count());
477
478 const StreamInfoList subtitleStreams = filterByStreamType(streams, GST_TYPE_DISCOVERER_SUBTITLE_INFO);
479 QCOMPARE(info->streams(GST_TYPE_DISCOVERER_SUBTITLE_INFO).count(), subtitleStreams.count());
480 QCOMPARE(info->subtitleStreams().count(), subtitleStreams.count());
481
482 const StreamInfoList containerStreams = filterByStreamType(streams, GST_TYPE_DISCOVERER_CONTAINER_INFO);
483 QCOMPARE(info->streams(GST_TYPE_DISCOVERER_CONTAINER_INFO).count(), containerStreams.count());
484 QCOMPARE(info->containerStreams().count(), containerStreams.count());
485
486 QCOMPARE(info->streams(GST_TYPE_DISCOVERER_STREAM_INFO).count(), streams.count());
487 QCOMPARE(info->streams().count(), streams.count());
488
489 Q_FOREACH(const QGst::DiscovererStreamInfoPtr &streamInfo, info->streams()) {
490 const StreamInfoList::ConstIterator it =
491 std::find_if(streams.constBegin(), streams.constEnd(),
492 std::bind2nd(std::mem_fun(&StreamInfo::acceptStream), streamInfo));
493
494 if (it == streams.constEnd()) {
495 Q_FOREACH(const StreamInfoPtr expectedInfo, streams) {
496 expectedInfo->acceptStream(streamInfo, StreamInfo::Debug);
497 }
498
499 QFAIL(qPrintable("Unexpected stream: " + streamInfo->caps()->toString()));
500 }
501
502 streams.removeAt(it - streams.constBegin());
503 QVERIFY(!streamInfo->misc().isValid());
504 }
505
506 QVERIFY(streams.isEmpty());
507 }
508
cleanup()509 void DiscovererTest::cleanup()
510 {
511 m_eventLoop.reset();
512 }
513
testGLibTypes_data()514 void DiscovererTest::testGLibTypes_data()
515 {
516 QTest::addColumn<QString>("wrappedTypeName");
517 QTest::addColumn<QString>("expectedTypeName");
518
519 QTest::newRow("discoverer")
520 << QGlib::GetType<QGst::Discoverer>().name()
521 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER));
522 QTest::newRow("discoverer-info")
523 << QGlib::GetType<QGst::DiscovererInfo>().name()
524 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER_INFO));
525 QTest::newRow("discoverer-stream-info")
526 << QGlib::GetType<QGst::DiscovererStreamInfo>().name()
527 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER_STREAM_INFO));
528 QTest::newRow("discoverer-container-info")
529 << QGlib::GetType<QGst::DiscovererContainerInfo>().name()
530 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER_CONTAINER_INFO));
531 QTest::newRow("discoverer-audio-info")
532 << QGlib::GetType<QGst::DiscovererAudioInfo>().name()
533 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER_AUDIO_INFO));
534 QTest::newRow("discoverer-video-info")
535 << QGlib::GetType<QGst::DiscovererVideoInfo>().name()
536 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER_VIDEO_INFO));
537 QTest::newRow("discoverer-subtitle-info")
538 << QGlib::GetType<QGst::DiscovererSubtitleInfo>().name()
539 << QString::fromLatin1(g_type_name(GST_TYPE_DISCOVERER_SUBTITLE_INFO));
540 }
541
testGLibTypes()542 void DiscovererTest::testGLibTypes()
543 {
544 QFETCH(QString, wrappedTypeName);
545 QFETCH(QString, expectedTypeName);
546
547 QCOMPARE(wrappedTypeName, expectedTypeName);
548 }
549
testSyncDiscovery_data()550 void DiscovererTest::testSyncDiscovery_data()
551 {
552 setupDiscoveryData();
553 }
554
testSyncDiscovery()555 void DiscovererTest::testSyncDiscovery()
556 {
557 // create a discoverer
558 QGst::DiscovererPtr discoverer = QGst::Discoverer::create(QGst::ClockTime::fromSeconds(1));
559 QVERIFY(!discoverer.isNull());
560
561 // test discovery request
562 QGst::DiscovererInfoPtr info;
563 QFETCH(QUrl, uri);
564
565 try {
566 info = discoverer->discoverUri(uri);
567 } catch(const QGlib::Error &error) {
568 QTEST(error.domain().toString(), "errorDomain");
569 QTEST(error.code(), "errorCode");
570 return;
571 }
572
573 verifyStreamInfo(info);
574 }
575
testAsyncDiscovery_data()576 void DiscovererTest::testAsyncDiscovery_data()
577 {
578 setupDiscoveryData();
579 }
580
testAsyncDiscovery()581 void DiscovererTest::testAsyncDiscovery()
582 {
583 // glib event loop required - see QCoreApplicationPrivate::createEventDispatcher() for the defines check
584 #if defined(Q_OS_WIN) || defined(Q_OS_BLACKBERRY) || defined(QT_NO_GLIB)
585 QSKIP_PORT("Platform does not have a GLib event loop", SkipAll);
586 #endif
587
588 // setup discovery timeout
589 m_eventLoop.reset(new QEventLoop);
590 QTimer::singleShot(3000, m_eventLoop.data(), SLOT(quit()));
591
592 // create a discoverer
593 m_discoveryState = DiscoveryPending;
594 QGst::DiscovererPtr discoverer = QGst::Discoverer::create(QGst::ClockTime::fromSeconds(1));
595 QVERIFY(!discoverer.isNull());
596
597 QVERIFY(QGlib::connect(discoverer, "starting", this, &DiscovererTest::onStartingDiscovery, 0/*QGlib::PassSender*/));
598 QVERIFY(QGlib::connect(discoverer, "discovered", this, &DiscovererTest::onUriDiscovered));
599 QVERIFY(QGlib::connect(discoverer, "finished", this, &DiscovererTest::onDiscoveryFinished, QGlib::PassSender));
600
601 // place URI to discover
602 QFETCH(QUrl, uri);
603 discoverer->start();
604 QVERIFY(discoverer->discoverUriAsync(uri));
605
606 // verify discovery succeeded
607 QVERIFY2(m_eventLoop->exec(), "Discovery timeout out");
608 QCOMPARE(m_discoveryState, DiscoveryFinished);
609 }
610
onStartingDiscovery()611 void DiscovererTest::onStartingDiscovery()
612 {
613 QCOMPARE(m_discoveryState, DiscoveryPending);
614 m_discoveryState = DiscoveryStarted;
615 }
616
onUriDiscovered(QGst::DiscovererInfoPtr info,const QGlib::Error & error)617 void DiscovererTest::onUriDiscovered(QGst::DiscovererInfoPtr info, const QGlib::Error &error)
618 {
619 QCOMPARE(m_discoveryState, DiscoveryStarted);
620 m_discoveryState = UriDiscovered;
621
622 if (error) {
623 QTEST(error.domain().toString(), "errorDomain");
624 QTEST(error.code(), "errorCode");
625 return;
626 }
627
628 verifyStreamInfo(info);
629 }
630
onDiscoveryFinished()631 void DiscovererTest::onDiscoveryFinished()
632 {
633 m_eventLoop->exit(1);
634 QCOMPARE(m_discoveryState, UriDiscovered);
635 m_discoveryState = DiscoveryFinished;
636 }
637
638 QTEST_MAIN(DiscovererTest)
639
640 #include "moc_qgsttest.cpp"
641 #include "discoverertest.moc"
642