1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "mediaobject.h"
43 #include "backend.h"
44 
45 #include <QtCore>
46 #include <QtCore/QTimer>
47 #include <QtCore/QVector>
48 #include <QtCore/QFile>
49 #include <QtCore/QByteRef>
50 #include <QtCore/QStringList>
51 #include <QtCore/QEvent>
52 #include <QApplication>
53 
54 QT_BEGIN_NAMESPACE
55 
56 namespace Phonon
57 {
58 namespace Dummy
59 {
60 
61 static const char* riffId = "RIFF";
62 
MediaObject(Backend * backend,QObject * parent)63 MediaObject::MediaObject(Backend *backend, QObject *parent)
64         : QObject(parent)
65           , m_resumeState(false)
66           , m_oldState(Phonon::LoadingState)
67           , m_oldPos(0)
68           , currentPos(0)
69 {
70     Q_UNUSED(backend)
71 
72     m_error = Phonon::NoError;
73     m_tickInterval = 100; // 100ms
74     m_totalTime = 26000; // 26s
75     m_prefinishMark = 0;
76     m_transitionTime = 100; //100ms
77     m_hasVideo = false;
78     m_prefinishMarkReachedNotEmitted = true;
79     m_aboutToFinishEmitted = false;
80     m_pendingState = Phonon::LoadingState;
81     m_state = Phonon::LoadingState;
82     m_pendingState = Phonon::LoadingState;
83     m_tickTimer = new QTimer(this);
84     connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
85 }
86 
~MediaObject()87 MediaObject::~MediaObject()
88 {
89     delete m_tickTimer;
90 }
91 
stateString(const Phonon::State & state)92 QString stateString(const Phonon::State &state)
93 {
94     switch (state) {
95         case Phonon::LoadingState:
96             return QString("LoadingState");
97         case Phonon::StoppedState:
98             return QString("StoppedState");
99         case Phonon::PlayingState:
100             return QString("PlayingState");
101         case Phonon::BufferingState:
102             return QString("BufferingState");
103         case Phonon::PausedState:
104             return QString("PausedState");
105         case Phonon::ErrorState:
106             return QString("ErrorState");
107     }
108     return QString();
109 }
110 
saveState()111 void MediaObject::saveState()
112 {
113     if (m_resumeState)
114         return;
115 
116     if (m_pendingState == Phonon::PlayingState || m_pendingState == Phonon::PausedState) {
117         m_resumeState = true;
118         m_oldState = m_pendingState;
119         m_oldPos = currentPos;
120     }
121 }
122 
resumeState()123 void MediaObject::resumeState()
124 {
125     if (m_resumeState)
126         QMetaObject::invokeMethod(this, "setState", Qt::QueuedConnection, Q_ARG(State, m_oldState));
127 }
128 
129 /**
130  * !reimp
131  */
state() const132 State MediaObject::state() const
133 {
134     return m_state;
135 }
136 
137 /**
138  * !reimp
139  */
hasVideo() const140 bool MediaObject::hasVideo() const
141 {
142     return m_hasVideo;
143 }
144 
145 /**
146  * !reimp
147  */
isSeekable() const148 bool MediaObject::isSeekable() const
149 {
150     return true;
151 }
152 
153 /**
154  * !reimp
155  */
currentTime() const156 qint64 MediaObject::currentTime() const
157 {
158     if (m_resumeState)
159         return m_oldPos;
160 
161     switch (state()) {
162     case Phonon::PausedState:
163     case Phonon::BufferingState:
164     case Phonon::PlayingState:
165         return currentPos;
166     case Phonon::StoppedState:
167     case Phonon::LoadingState:
168         return 0;
169     case Phonon::ErrorState:
170         break;
171     }
172     return -1;
173 }
174 
175 /**
176  * !reimp
177  */
tickInterval() const178 qint32 MediaObject::tickInterval() const
179 {
180     return m_tickInterval;
181 }
182 
183 /**
184  * !reimp
185  */
setTickInterval(qint32 newTickInterval)186 void MediaObject::setTickInterval(qint32 newTickInterval)
187 {
188     m_tickInterval = newTickInterval;
189     if (m_tickInterval <= 0) {
190         m_tickTimer->setInterval(100);
191     } else
192         m_tickTimer->setInterval(newTickInterval);
193 }
194 
195 /**
196  * !reimp
197  */
play()198 void MediaObject::play()
199 {
200     if(m_state == Phonon::PlayingState)
201         return;
202     if(m_state == Phonon::ErrorState)
203         return;
204 
205     if(m_state != Phonon::PausedState)
206         m_tickTimer->stop();
207 
208     m_prefinishMarkReachedNotEmitted = true;
209     m_aboutToFinishEmitted = false;
210 
211     setState(Phonon::PlayingState);
212     m_resumeState = false;
213     m_tickTimer->start();
214 }
215 
216 /**
217  * !reimp
218  */
errorString() const219 QString MediaObject::errorString() const
220 {
221     return m_errorString;
222 }
223 
224 /**
225  * !reimp
226  */
errorType() const227 Phonon::ErrorType MediaObject::errorType() const
228 {
229     return m_error;
230 }
231 
setState(State newstate)232 void MediaObject::setState(State newstate)
233 {
234     if (m_state == newstate)
235         return;
236 
237     switch (newstate) {
238         case Phonon::PausedState:
239             m_pendingState = Phonon::PausedState;
240             emit stateChanged(newstate, m_state);
241             m_state = newstate;
242             break;
243         case Phonon::StoppedState:
244             m_pendingState = Phonon::StoppedState;
245             emit stateChanged(newstate, m_state);
246             m_state = newstate;
247             break;
248         case Phonon::PlayingState:
249             m_pendingState = Phonon::PlayingState;
250             emit stateChanged(newstate, m_state);
251             m_state = newstate;
252             break;
253         case Phonon::ErrorState:
254             emit stateChanged(newstate, m_state);
255             m_state = newstate;
256             break;
257         case Phonon::BufferingState:
258         case Phonon::LoadingState:
259             emit stateChanged(newstate, m_state);
260             m_state = newstate;
261             break;
262     }
263 }
264 
totalTime() const265 qint64 MediaObject::totalTime() const
266 {
267     return m_totalTime;
268 }
269 
prefinishMark() const270 qint32 MediaObject::prefinishMark() const
271 {
272     return m_prefinishMark;
273 }
274 
transitionTime() const275 qint32 MediaObject::transitionTime() const
276 {
277     return m_transitionTime;
278 }
279 
setTransitionTime(qint32 time)280 void MediaObject::setTransitionTime(qint32 time)
281 {
282     m_transitionTime = time;
283 }
284 
remainingTime() const285 qint64 MediaObject::remainingTime() const
286 {
287     if(currentTime() > totalTime())
288         return 0;
289 
290     return totalTime() - currentTime();
291 }
292 
source() const293 MediaSource MediaObject::source() const
294 {
295     return m_source;
296 }
297 
setNextSource(const MediaSource & source)298 void MediaObject::setNextSource(const MediaSource &source)
299 {
300     if (source.type() == MediaSource::Invalid &&
301         source.type() == MediaSource::Empty)
302         return;
303     m_nextSource = source;
304 }
305 
306 /*
307  * !reimp
308  */
setSource(const MediaSource & source)309 void MediaObject::setSource(const MediaSource &source)
310 {
311     QMultiMap<QString, QString> ret;
312 
313     ret.insert(QLatin1String("ARTIST"), "Nokia Dude");
314     ret.insert(QLatin1String("ALBUM"), "Sound of silence");
315     ret.insert(QLatin1String("DATE"), "2009");
316 
317     m_error = Phonon::NoError;
318     setState(Phonon::LoadingState);
319 
320     m_source = source;
321     currentPos = 0;
322 
323     if((source.fileName().contains(".avi")) ||
324        (source.fileName().contains(".mp4"))) {
325         m_hasVideo = true;
326         emit hasVideoChanged(m_hasVideo);
327     }
328     if(source.fileName().contains(".wav")) {
329         QFile file(source.fileName());
330         if (file.open(QIODevice::ReadOnly)) {
331             int len = file.read((char*)&header, sizeof(CombinedHeader));
332             if(len == sizeof(CombinedHeader)) {
333                 if(memcmp(&header.riff.descriptor.id, riffId, 4) != 0) {
334                     // Not a valid wav file, to satisfy unit test for mediaobject
335                     m_error = Phonon::FatalError;
336                     //m_state = Phonon::ErrorState;
337                     m_errorString = "Invalid wav file";
338                     setState(Phonon::ErrorState);
339                     file.close();
340                     return;
341                 }
342             }
343             file.close();
344         }
345     }
346     emit metaDataChanged(ret);
347     emit currentSourceChanged(source);
348     emit totalTimeChanged(m_totalTime);
349 
350     setState(Phonon::StoppedState);
351 }
352 
setPrefinishMark(qint32 newPrefinishMark)353 void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
354 {
355     m_prefinishMark = newPrefinishMark;
356     if (currentTime() < totalTime() - m_prefinishMark) // not about to finish
357         m_prefinishMarkReachedNotEmitted = true;
358 }
359 
pause()360 void MediaObject::pause()
361 {
362     if (state() != Phonon::PausedState)
363         setState(Phonon::PausedState);
364     m_resumeState = false;
365     m_tickTimer->stop();
366 }
367 
stop()368 void MediaObject::stop()
369 {
370     if (state() != Phonon::StoppedState) {
371         if(m_state != Phonon::ErrorState) {
372             setState(Phonon::StoppedState);
373         }
374         m_prefinishMarkReachedNotEmitted = true;
375     }
376     m_resumeState = false;
377     m_tickTimer->stop();
378 }
379 
emitTick()380 void MediaObject::emitTick()
381 {
382     if (m_resumeState) {
383         return;
384     }
385     if(m_tickInterval > 0)
386         currentPos += m_tickInterval;
387     else
388         currentPos += 100;
389 
390     qint64 currentTime = currentPos;
391     qint64 totalTime = m_totalTime;
392 
393     if (m_tickInterval > 0 && currentTime != m_previousTickTime) {
394         emit tick(currentTime);
395         m_previousTickTime = currentTime;
396     }
397     if (m_state == Phonon::PlayingState) {
398         if (currentTime >= totalTime - m_prefinishMark) {
399             if (m_prefinishMarkReachedNotEmitted) {
400                 m_prefinishMarkReachedNotEmitted = false;
401                 emit prefinishMarkReached(totalTime - currentTime);
402             }
403         }
404         // Prepare load of next source
405         if (currentTime >= totalTime - 500) {
406             if (!m_aboutToFinishEmitted) {
407                 m_aboutToFinishEmitted = true; // track is about to finish
408                 emit aboutToFinish();
409             }
410         }
411         if(currentTime >= totalTime) {
412             m_tickTimer->stop();
413             if(m_nextSource.type() != MediaSource::Invalid &&
414                     m_nextSource.type() != MediaSource::Empty) {
415                 setSource(m_nextSource);
416                 m_nextSource = MediaSource();
417                 m_pendingState = Phonon::PlayingState;
418             } else {
419                 setState(Phonon::PausedState);
420                 currentPos = 0;
421                 emit finished();
422             }
423         }
424     }
425 }
426 
seek(qint64 time)427 void MediaObject::seek(qint64 time)
428 {
429     // We will assume no buffering in the object so this is not needed.
430     currentPos = time;
431 }
432 
433 } // ns Dummy
434 } // ns Phonon
435 
436 QT_END_NAMESPACE
437 
438 #include "moc_mediaobject.cpp"
439