1
/* This file is part of the KDE project
2
Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Lesser General Public
6
License as published by the Free Software Foundation; either
7
version 2.1 of the License, or (at your option) version 3, or any
8
later version accepted by the membership of KDE e.V. (or its
9
successor approved by the membership of KDE e.V.), Nokia Corporation
10
(or its successors, if any) and the KDE Free Qt Foundation, which shall
11
act as a proxy defined in Section 6 of version 3 of the license.
13
This library is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
Lesser General Public License for more details.
18
You should have received a copy of the GNU Lesser General Public
19
License along with this library. If not, see <http://www.gnu.org/licenses/>.
23
#include "mediaobject.h"
24
#include <QtCore/QTimer>
26
#include "../../abstractmediastream_p.h"
27
#include <QtCore/QVector>
29
#include <QtCore/QFile>
30
#include <QtCore/QByteRef>
31
#include <QtCore/QStringList>
32
#include "audionode.h"
33
#include "videonode.h"
39
static const int SAMPLE_RATE = 44100;
40
static const float SAMPLE_RATE_FLOAT = 44100.0f;
41
static const int FRAME_RATE = 25;
42
static const int SAMPLES_PER_FRAME = SAMPLE_RATE / FRAME_RATE;
44
MediaObject::MediaObject(QObject *parent)
46
, m_state(Phonon::LoadingState)
47
, m_tickTimer(new QTimer(this))
48
, m_bufferSize(SAMPLES_PER_FRAME)//512)
49
, m_lastSamplesMissing(0)
52
, m_prefinishMarkReachedNotEmitted(true), m_waiting(0)
55
connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
58
MediaObject::~MediaObject()
63
State MediaObject::state() const
69
bool MediaObject::hasVideo() const
75
bool MediaObject::isSeekable() const
81
qint64 MediaObject::currentTime() const
86
case Phonon::PausedState:
87
case Phonon::BufferingState:
88
return m_startTime.msecsTo(m_pauseTime);
89
case Phonon::PlayingState:
90
return m_startTime.elapsed();
91
case Phonon::StoppedState:
92
case Phonon::LoadingState:
94
case Phonon::ErrorState:
100
qint32 MediaObject::tickInterval() const
103
return m_tickInterval;
106
void MediaObject::setTickInterval(qint32 newTickInterval)
109
m_tickInterval = newTickInterval;
110
if (m_tickInterval <= 0)
111
m_tickTimer->setInterval(50);
113
m_tickTimer->setInterval(newTickInterval);
116
void MediaObject::play()
119
if (m_state == Phonon::LoadingState) {
120
setState(Phonon::BufferingState);
122
setState(Phonon::PlayingState);
126
QString MediaObject::errorString() const
131
Phonon::ErrorType MediaObject::errorType() const
133
return Phonon::NoError;
136
void MediaObject::setState(State newstate)
138
if (newstate == m_state)
140
State oldstate = m_state;
144
case Phonon::PausedState:
145
case Phonon::BufferingState:
148
case Phonon::PlayingState:
149
m_tickTimer->start();
150
if (oldstate == Phonon::PausedState || oldstate == Phonon::BufferingState)
151
m_startTime = m_startTime.addMSecs(m_pauseTime.elapsed());
155
case Phonon::StoppedState:
156
case Phonon::ErrorState:
157
case Phonon::LoadingState:
158
m_startTime = QTime();
161
//kDebug(604) << "emit stateChanged(" << newstate << ", " << oldstate << ")";
162
emit stateChanged(newstate, oldstate);
165
static const float TWOPI = 6.28318530718f;
166
static const float maxFrequency = 1760.0f;
167
static const float minFrequency = 440.0f;
168
static const float frequencyToDelta = TWOPI / SAMPLE_RATE_FLOAT;
170
void MediaObject::fillBuffer(QVector<float> *buffer)
172
//static QFile createdump("createdump");
173
//if (!createdump.isOpen())
174
//createdump.open(QIODevice::WriteOnly);
176
m_frequency *= 1.059463094359f;
177
if (m_frequency > maxFrequency)
178
m_frequency = minFrequency;
179
float delta = frequencyToDelta * m_frequency;
181
float *data = buffer->data();
182
const float * const end = data + m_bufferSize;
186
const float sample = std::sin(m_position);
187
//createdump.write(QByteArray::number(sample) + "\n");
190
if (m_position > TWOPI)
195
void MediaObject::fillFrameData(Phonon::Experimental::VideoFrame *frame)
198
//X static quint32 frameCount = 0;
199
//X quint8 *dataPtr = reinterpret_cast<quint8 *>(frame->data.data());
200
//X for (int y = 0; y < frame->height; ++y)
201
//X for (int x = 0; x < frame->width; ++x)
203
//X *dataPtr++ = static_cast<quint8>(0xff);
204
//X *dataPtr++ = static_cast<quint8>((x + frameCount) * 2 / 3); //red
205
//X *dataPtr++ = static_cast<quint8>(y + frameCount); //green
206
//X *dataPtr++ = static_cast<quint8>(frameCount / 2); //blue
211
qint64 MediaObject::totalTime() const
214
return 1000 *60 *3; // 3 minutes
217
qint32 MediaObject::prefinishMark() const
220
return m_prefinishMark;
223
qint32 MediaObject::transitionTime() const
225
return m_transitionTime;
228
void MediaObject::setTransitionTime(qint32 time)
230
m_transitionTime = time;
233
qint64 MediaObject::remainingTime() const
235
return totalTime() - currentTime();
239
MediaSource MediaObject::source() const
244
void MediaObject::setNextSource(const MediaSource &source)
246
m_nextSource = source;
249
void MediaObject::setSource(const MediaSource &source)
252
m_prefinishMarkReachedNotEmitted = true;
254
setState(Phonon::LoadingState);
255
switch (m_source.type()) {
256
case MediaSource::Invalid:
257
case MediaSource::Empty:
259
case MediaSource::Disc:
260
Q_ASSERT(m_source.discType() != NoDisc);
262
case MediaSource::Url:
263
Q_ASSERT(m_source.url().isValid());
265
case MediaSource::LocalFile:
266
Q_ASSERT(!m_source.fileName().isEmpty());
268
case MediaSource::Stream:
269
//Stream *s = new Stream(m_source, this);
270
//Q_ASSERT(qobject_cast<Stream *>(m_source.stream()->d_ptr->streamInterface));
273
emit totalTimeChanged(totalTime());
274
QMultiMap<QString, QString> metaData;
275
metaData.insert("TITLE", "Fake video");
276
metaData.insert("ARTIST", "Matthias Kretz");
277
emit metaDataChanged(metaData);
278
emit currentSourceChanged(m_source);
279
QTimer::singleShot(50, this, SLOT(loadingComplete()));
282
void MediaObject::loadingComplete()
284
if (state() == Phonon::LoadingState) {
285
setState(Phonon::StoppedState);
286
} else if (state() == Phonon::BufferingState) {
287
setState(Phonon::PlayingState);
291
void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
294
m_prefinishMark = newPrefinishMark;
295
if (currentTime() < totalTime() - m_prefinishMark) // not about to finish
296
m_prefinishMarkReachedNotEmitted = true;
299
void MediaObject::pause()
307
setState(Phonon::PausedState);
316
void MediaObject::stop()
319
if (state() == Phonon::PlayingState || state() == Phonon::BufferingState || state() == Phonon::PausedState) {
321
setState(Phonon::StoppedState);
323
m_frequency = 440.0f;
324
m_prefinishMarkReachedNotEmitted = true;
328
void MediaObject::seek(qint64 time)
335
case Phonon::PausedState:
336
case Phonon::BufferingState:
337
m_startTime = m_pauseTime;
339
case Phonon::PlayingState:
342
case Phonon::StoppedState:
343
case Phonon::ErrorState:
344
case Phonon::LoadingState:
345
return; // cannot seek
347
m_startTime = m_startTime.addMSecs(-time);
350
if (currentTime() < totalTime() - m_prefinishMark) // not about to finish
351
m_prefinishMarkReachedNotEmitted = true;
354
void MediaObject::emitTick()
356
//kDebug(604) << "emit tick(" << currentTime() << ")";
357
int tickInterval = 50;
358
if (m_tickInterval > 0)
360
emit tick(currentTime());
361
tickInterval = m_tickInterval;
363
/* if (m_waiting == 0) {
364
QVector<float> buffer(m_bufferSize);
365
Experimental::VideoFrame frame;
368
frame.colorspace = Experimental::VideoFrame::Format_RGBA8;
369
frame.data.resize(frame.width * frame.height * 4);
371
const int availableSamples = tickInterval * SAMPLE_RATE / 1000 + m_lastSamplesMissing;
372
const int bufferCount = availableSamples / m_bufferSize;
373
m_lastSamplesMissing = availableSamples - bufferCount * m_bufferSize;
374
for (int i = 0; i < bufferCount; ++i)
377
foreach (AudioNode *an, m_audioNodes) {
378
an->processBuffer(buffer);
380
fillFrameData(&frame);
381
foreach (VideoNode *vn, m_videoNodes) {
382
vn->processFrame(frame);
387
if (currentTime() >= totalTime() - m_prefinishMark) { // about to finish
388
if (m_prefinishMarkReachedNotEmitted) {
389
m_prefinishMarkReachedNotEmitted = false;
390
emit prefinishMarkReached(totalTime() - currentTime());
393
if (currentTime() >= totalTime()) // finished
395
emit aboutToFinish();
396
if (m_nextSource.type() == MediaSource::Empty || m_nextSource.type() == MediaSource::Invalid) {
400
m_prefinishMarkReachedNotEmitted = true;
401
m_source = m_nextSource;
402
m_nextSource = MediaSource();
403
switch (m_source.type()) {
404
case MediaSource::Empty:
405
case MediaSource::Invalid:
407
case MediaSource::Disc:
408
Q_ASSERT(m_source.discType() != NoDisc);
410
case MediaSource::Url:
411
Q_ASSERT(m_source.url().isValid());
413
case MediaSource::LocalFile:
414
Q_ASSERT(!m_source.fileName().isEmpty());
416
case MediaSource::Stream:
419
emit totalTimeChanged(totalTime());
420
QMultiMap<QString, QString> metaData;
421
metaData.insert("TITLE", "Fake video");
422
metaData.insert("ARTIST", "Matthias Kretz");
423
emit metaDataChanged(metaData);
427
bool MediaObject::wait()
433
bool MediaObject::done()
439
void MediaObject::addAudioNode(AudioNode *node)
441
m_audioNodes << node;
442
node->setHasInput(true);
445
void MediaObject::addVideoNode(VideoNode *node)
447
m_videoNodes << node;
448
node->setHasInput(true);
451
bool MediaObject::removeAudioNode(AudioNode *node)
453
node->setHasInput(false);
454
return 1 == m_audioNodes.removeAll(node);
457
bool MediaObject::removeVideoNode(VideoNode *node)
459
node->setHasInput(false);
460
return 1 == m_videoNodes.removeAll(node);
465
#include "moc_mediaobject.cpp"