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