~ubuntu-branches/ubuntu/oneiric/phonon/oneiric-201108111512

« back to all changes in this revision

Viewing changes to .pc/kubuntu_07_include_fix.diff/gstreamer/mediaobject.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2011-01-24 10:12:11 UTC
  • mfrom: (0.5.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20110124101211-w9rew7q0dmwbwhqx
Tags: 4:4.7.0really4.4.4-0ubuntu1
* New upstream release
* Xine and GStreamer backends now split out source, remove build-deps and
  binary packages from debian/control
* Remove 02_no_rpath.patch, now upstream
* Disable kubuntu04_no_va_mangle.patch, no longer applies
* Remove kubuntu_05_gst_codec_installer_window_id.diff, kubuntu_06_forward_events.diff,
  kubuntu_07_include_fix.diff, gstreamer now separate

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  This file is part of the KDE project.
2
 
 
3
 
    Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
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 by
7
 
    the Free Software Foundation, either version 2.1 or 3 of the License.
8
 
 
9
 
    This library is distributed in the hope that it will be useful,
10
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
    GNU Lesser General Public License for more details.
13
 
 
14
 
    You should have received a copy of the GNU Lesser General Public License
15
 
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
16
 
*/
17
 
#include <cmath>
18
 
#include <gst/interfaces/navigation.h>
19
 
#include <gst/interfaces/propertyprobe.h>
20
 
#include <gst/pbutils/install-plugins.h>
21
 
#include "common.h"
22
 
#include "mediaobject.h"
23
 
#include "videowidget.h"
24
 
#include "message.h"
25
 
#include "backend.h"
26
 
#include "streamreader.h"
27
 
#include "phononsrc.h"
28
 
#include "phonon-config-gstreamer.h"
29
 
 
30
 
#include <QtCore/QByteRef>
31
 
#include <QtCore/QCoreApplication>
32
 
#include <QtCore/QEvent>
33
 
#include <QtCore/QFile>
34
 
#include <QtCore/QLibrary>
35
 
#include <QtCore/QStringList>
36
 
#include <QtCore/QTimer>
37
 
#include <QtCore/QVector>
38
 
 
39
 
#define ABOUT_TO_FINNISH_TIME 2000
40
 
#define MAX_QUEUE_TIME 20 * GST_SECOND
41
 
 
42
 
QT_BEGIN_NAMESPACE
43
 
 
44
 
namespace Phonon
45
 
{
46
 
namespace Gstreamer
47
 
{
48
 
 
49
 
MediaObject::MediaObject(Backend *backend, QObject *parent)
50
 
        : QObject(parent)
51
 
        , MediaNode(backend, AudioSource | VideoSource)
52
 
        , m_resumeState(false)
53
 
        , m_oldState(Phonon::LoadingState)
54
 
        , m_oldPos(0)
55
 
        , m_state(Phonon::LoadingState)
56
 
        , m_pendingState(Phonon::LoadingState)
57
 
        , m_tickTimer(new QTimer(this))
58
 
        , m_prefinishMark(0)
59
 
        , m_transitionTime(0)
60
 
        , m_isStream(false)
61
 
        , m_posAtSeek(-1)
62
 
        , m_prefinishMarkReachedNotEmitted(true)
63
 
        , m_aboutToFinishEmitted(false)
64
 
        , m_loading(false)
65
 
        , m_capsHandler(0)
66
 
        , m_datasource(0)
67
 
        , m_decodebin(0)
68
 
        , m_audioPipe(0)
69
 
        , m_videoPipe(0)
70
 
        , m_totalTime(-1)
71
 
        , m_bufferPercent(0)
72
 
        , m_hasVideo(false)
73
 
        , m_videoStreamFound(false)
74
 
        , m_hasAudio(false)
75
 
        , m_seekable(false)
76
 
        , m_atEndOfStream(false)
77
 
        , m_atStartOfStream(false)
78
 
        , m_error(Phonon::NoError)
79
 
        , m_pipeline(0)
80
 
        , m_audioGraph(0)
81
 
        , m_videoGraph(0)
82
 
        , m_previousTickTime(-1)
83
 
        , m_resetNeeded(false)
84
 
        , m_autoplayTitles(true)
85
 
        , m_availableTitles(0)
86
 
        , m_currentTitle(1)
87
 
        , m_pendingTitle(1)
88
 
{
89
 
    qRegisterMetaType<GstCaps*>("GstCaps*");
90
 
    qRegisterMetaType<State>("State");
91
 
 
92
 
    static int count = 0;
93
 
    m_name = "MediaObject" + QString::number(count++);
94
 
 
95
 
    if (!m_backend->isValid()) {
96
 
        setError(tr("Cannot start playback. \n\nCheck your GStreamer installation and make sure you "
97
 
                    "\nhave libgstreamer-plugins-base installed."), Phonon::FatalError);
98
 
    } else {
99
 
        m_root = this;
100
 
        createPipeline();
101
 
        m_backend->addBusWatcher(this);
102
 
        connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
103
 
    }
104
 
    connect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
105
 
            this, SLOT(notifyStateChange(Phonon::State, Phonon::State)));
106
 
 
107
 
}
108
 
 
109
 
MediaObject::~MediaObject()
110
 
{
111
 
    m_backend->removeBusWatcher(this);
112
 
    if (m_pipeline) {
113
 
        gst_element_set_state(m_pipeline, GST_STATE_NULL);
114
 
        gst_object_unref(m_pipeline);
115
 
    }
116
 
    if (m_audioGraph) {
117
 
        gst_element_set_state(m_audioGraph, GST_STATE_NULL);
118
 
        gst_object_unref(m_audioGraph);
119
 
    }
120
 
    if (m_videoGraph) {
121
 
        gst_element_set_state(m_videoGraph, GST_STATE_NULL);
122
 
        gst_object_unref(m_videoGraph);
123
 
    }
124
 
}
125
 
 
126
 
QString stateString(const Phonon::State &state)
127
 
{
128
 
    switch (state) {
129
 
    case Phonon::LoadingState:
130
 
        return QString("LoadingState");
131
 
    case Phonon::StoppedState:
132
 
        return QString("StoppedState");
133
 
    case Phonon::PlayingState:
134
 
        return QString("PlayingState");
135
 
    case Phonon::BufferingState:
136
 
        return QString("BufferingState");
137
 
    case Phonon::PausedState:
138
 
        return QString("PausedState");
139
 
    case Phonon::ErrorState:
140
 
        return QString("ErrorState");
141
 
    }
142
 
    return QString();
143
 
}
144
 
 
145
 
void
146
 
pluginInstallationDone( GstInstallPluginsReturn res, gpointer userData )
147
 
{
148
 
    // Nothing inside yet
149
 
    Q_UNUSED(res);
150
 
    Q_UNUSED(userData);
151
 
}
152
 
 
153
 
void MediaObject::saveState()
154
 
{
155
 
    //Only first resumeState is respected
156
 
    if (m_resumeState)
157
 
        return;
158
 
 
159
 
    if (m_pendingState == Phonon::PlayingState || m_pendingState == Phonon::PausedState) {
160
 
        m_resumeState = true;
161
 
        m_oldState = m_pendingState;
162
 
        m_oldPos = getPipelinePos();
163
 
    }
164
 
}
165
 
 
166
 
void MediaObject::resumeState()
167
 
{
168
 
    if (m_resumeState)
169
 
        QMetaObject::invokeMethod(this, "setState", Qt::QueuedConnection, Q_ARG(State, m_oldState));
170
 
}
171
 
 
172
 
void MediaObject::newPadAvailable (GstPad *pad)
173
 
{
174
 
    GstCaps *caps;
175
 
    GstStructure *str;
176
 
    caps = gst_pad_get_caps (pad);
177
 
    if (caps) {
178
 
        str = gst_caps_get_structure (caps, 0);
179
 
        QString mediaString(gst_structure_get_name (str));
180
 
 
181
 
        if (mediaString.startsWith("video")) {
182
 
            connectVideo(pad);
183
 
        } else if (mediaString.startsWith("audio")) {
184
 
            connectAudio(pad);
185
 
        } else {
186
 
            m_backend->logMessage("Could not connect pad", Backend::Warning);
187
 
        }
188
 
        gst_caps_unref (caps);
189
 
    }
190
 
}
191
 
 
192
 
void MediaObject::cb_newpad (GstElement *decodebin,
193
 
                             GstPad     *pad,
194
 
                             gboolean    last,
195
 
                             gpointer    data)
196
 
{
197
 
    Q_UNUSED(decodebin);
198
 
    Q_UNUSED(pad);
199
 
    Q_UNUSED(last);
200
 
    Q_UNUSED(data);
201
 
 
202
 
    MediaObject *media = static_cast<MediaObject*>(data);
203
 
    Q_ASSERT(media);
204
 
    media->newPadAvailable(pad);
205
 
}
206
 
 
207
 
void MediaObject::noMorePadsAvailable ()
208
 
{
209
 
    if (m_missingCodecs.size() > 0) {
210
 
        bool canPlay = (m_hasAudio || m_videoStreamFound);
211
 
        Phonon::ErrorType error = canPlay ? Phonon::NormalError : Phonon::FatalError;
212
 
#ifdef PLUGIN_INSTALL_API
213
 
        GstInstallPluginsContext *ctx = gst_install_plugins_context_new();
214
 
        QWidget *activeWindow = QApplication::activeWindow();
215
 
        if (activeWindow) {
216
 
            gst_install_plugins_context_set_xid(ctx, static_cast<int>(activeWindow->winId()));
217
 
        }
218
 
        gchar *details[2];
219
 
        QByteArray missingCodec = m_missingCodecs.first().toLocal8Bit();
220
 
        details[0] = missingCodec.data();
221
 
        details[1] = NULL;
222
 
        GstInstallPluginsReturn status;
223
 
 
224
 
        status = gst_install_plugins_async(details, ctx, pluginInstallationDone, NULL);
225
 
        gst_install_plugins_context_free (ctx);
226
 
 
227
 
        if ( status != GST_INSTALL_PLUGINS_STARTED_OK )
228
 
        {
229
 
            if( status == GST_INSTALL_PLUGINS_HELPER_MISSING )
230
 
                setError(QString(tr("Missing codec helper script assistant.")), Phonon::FatalError);
231
 
            else
232
 
                setError(QString(tr("Plugin codec installation failed for codec: %1"))
233
 
                        .arg(m_missingCodecs[0].split('|')[3]), error);
234
 
        }
235
 
        m_missingCodecs.clear();
236
 
#else
237
 
        QString codecs = m_missingCodecs.join(", ");
238
 
 
239
 
        if (error == Phonon::NormalError && m_hasVideo && !m_videoStreamFound) {
240
 
            m_hasVideo = false;
241
 
            emit hasVideoChanged(false);
242
 
        }
243
 
        setError(QString(tr("A required codec is missing. You need to install the following codec(s) to play this content: %0")).arg(codecs), error);
244
 
        m_missingCodecs.clear();
245
 
#endif
246
 
    }
247
 
}
248
 
 
249
 
void MediaObject::cb_no_more_pads (GstElement * decodebin, gpointer data)
250
 
{
251
 
    Q_UNUSED(decodebin);
252
 
    MediaObject *media = static_cast<MediaObject*>(data);
253
 
    Q_ASSERT(media);
254
 
    QMetaObject::invokeMethod(media, "noMorePadsAvailable", Qt::QueuedConnection);
255
 
}
256
 
 
257
 
typedef void (*Ptr_gst_pb_utils_init)();
258
 
typedef gchar* (*Ptr_gst_pb_utils_get_codec_description)(const GstCaps *);
259
 
 
260
 
void MediaObject::cb_unknown_type (GstElement *decodebin, GstPad *pad, GstCaps *caps, gpointer data)
261
 
{
262
 
    Q_UNUSED(decodebin);
263
 
    Q_UNUSED(pad);
264
 
    MediaObject *media = static_cast<MediaObject*>(data);
265
 
    Q_ASSERT(media);
266
 
 
267
 
    QString value = "unknown codec";
268
 
 
269
 
    // These functions require GStreamer > 0.10.12
270
 
#ifndef QT_NO_LIBRARY
271
 
    static Ptr_gst_pb_utils_init p_gst_pb_utils_init = 0;
272
 
    static Ptr_gst_pb_utils_get_codec_description p_gst_pb_utils_get_codec_description = 0;
273
 
    if (!p_gst_pb_utils_init) {
274
 
        p_gst_pb_utils_init =  (Ptr_gst_pb_utils_init)QLibrary::resolve(QLatin1String("gstpbutils-0.10"), 0, "gst_pb_utils_init");
275
 
        p_gst_pb_utils_get_codec_description =  (Ptr_gst_pb_utils_get_codec_description)QLibrary::resolve(QLatin1String("gstpbutils-0.10"), 0, "gst_pb_utils_get_codec_description");
276
 
        if (p_gst_pb_utils_init)
277
 
            p_gst_pb_utils_init();
278
 
    }
279
 
    if (p_gst_pb_utils_get_codec_description) {
280
 
        gchar *codecName = NULL;
281
 
        codecName = p_gst_pb_utils_get_codec_description (caps);
282
 
        value = QString::fromUtf8(codecName);
283
 
        g_free (codecName);
284
 
    } else
285
 
#endif //QT_NO_LIBRARY
286
 
    {
287
 
        // For GStreamer versions < 0.10.12
288
 
        GstStructure *str = gst_caps_get_structure (caps, 0);
289
 
        value = QString::fromUtf8(gst_structure_get_name (str));
290
 
 
291
 
    }
292
 
 
293
 
#ifdef PLUGIN_INSTALL_API
294
 
    QString plugins = QString("gstreamer|0.10|%0|%1|decoder-%2")
295
 
        .arg( qApp->applicationName() )
296
 
        .arg( value )
297
 
        .arg( QString::fromUtf8(gst_caps_to_string (caps) ) );
298
 
    media->addMissingCodecName( plugins );
299
 
#else
300
 
    media->addMissingCodecName( value );
301
 
#endif
302
 
}
303
 
 
304
 
static void notifyVideoCaps(GObject *obj, GParamSpec *, gpointer data)
305
 
{
306
 
    GstPad *pad = GST_PAD(obj);
307
 
    GstCaps *caps = gst_pad_get_caps (pad);
308
 
    Q_ASSERT(caps);
309
 
    MediaObject *media = static_cast<MediaObject*>(data);
310
 
 
311
 
    // We do not want any more notifications until the source changes
312
 
    g_signal_handler_disconnect(pad, media->capsHandler());
313
 
 
314
 
    // setVideoCaps calls loadingComplete(), meaning we cannot call it from
315
 
    // the streaming thread
316
 
    QMetaObject::invokeMethod(media, "setVideoCaps", Qt::QueuedConnection, Q_ARG(GstCaps *, caps));
317
 
}
318
 
 
319
 
void MediaObject::setVideoCaps(GstCaps *caps)
320
 
{
321
 
    GstStructure *str;
322
 
    gint width, height;
323
 
 
324
 
    if ((str = gst_caps_get_structure (caps, 0))) {
325
 
        if (gst_structure_get_int (str, "width", &width) && gst_structure_get_int (str, "height", &height)) {
326
 
            gint aspectNum = 0;
327
 
            gint aspectDenum = 0;
328
 
            if (gst_structure_get_fraction(str, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) {
329
 
                if (aspectDenum > 0)
330
 
                    width = width*aspectNum/aspectDenum;
331
 
            }
332
 
            // Let child nodes know about our new video size
333
 
            QSize size(width, height);
334
 
            MediaNodeEvent event(MediaNodeEvent::VideoSizeChanged, &size);
335
 
            notify(&event);
336
 
        }
337
 
    }
338
 
    gst_caps_unref(caps);
339
 
}
340
 
 
341
 
// Adds an element to the pipeline if not previously added
342
 
bool MediaObject::addToPipeline(GstElement *elem)
343
 
{
344
 
    bool success = true;
345
 
    if (!GST_ELEMENT_PARENT(elem)) { // If not already in pipeline
346
 
        success = gst_bin_add(GST_BIN(m_pipeline), elem);
347
 
    }
348
 
    return success;
349
 
}
350
 
 
351
 
void MediaObject::connectVideo(GstPad *pad)
352
 
{
353
 
    GstState currentState = GST_STATE(m_pipeline);
354
 
    if (addToPipeline(m_videoGraph)) {
355
 
        GstPad *videopad = gst_element_get_pad (m_videoGraph, "sink");
356
 
        if (!GST_PAD_IS_LINKED (videopad) && (gst_pad_link (pad, videopad) == GST_PAD_LINK_OK)) {
357
 
            gst_element_set_state(m_videoGraph, currentState == GST_STATE_PLAYING ? GST_STATE_PLAYING : GST_STATE_PAUSED);
358
 
            m_videoStreamFound = true;
359
 
            m_backend->logMessage("Video track connected", Backend::Info, this);
360
 
            // Note that the notify::caps _must_ be installed after linking to work with Dapper
361
 
            m_capsHandler = g_signal_connect(pad, "notify::caps", G_CALLBACK(notifyVideoCaps), this);
362
 
 
363
 
            if (!m_loading && !m_hasVideo) {
364
 
                m_hasVideo = m_videoStreamFound;
365
 
                emit hasVideoChanged(m_hasVideo);
366
 
            }
367
 
        }
368
 
        gst_object_unref (videopad);
369
 
    } else {
370
 
        m_backend->logMessage("The video stream could not be plugged.", Backend::Info, this);
371
 
    }
372
 
}
373
 
 
374
 
void MediaObject::connectAudio(GstPad *pad)
375
 
{
376
 
    GstState currentState = GST_STATE(m_pipeline);
377
 
    if (addToPipeline(m_audioGraph)) {
378
 
        GstPad *audiopad = gst_element_get_pad (m_audioGraph, "sink");
379
 
        if (!GST_PAD_IS_LINKED (audiopad) && (gst_pad_link (pad, audiopad)==GST_PAD_LINK_OK)) {
380
 
            gst_element_set_state(m_audioGraph, currentState == GST_STATE_PLAYING ? GST_STATE_PLAYING : GST_STATE_PAUSED);
381
 
            m_hasAudio = true;
382
 
            m_backend->logMessage("Audio track connected", Backend::Info, this);
383
 
        }
384
 
        gst_object_unref (audiopad);
385
 
    } else {
386
 
        m_backend->logMessage("The audio stream could not be plugged.", Backend::Info, this);
387
 
    }
388
 
}
389
 
 
390
 
void MediaObject::cb_pad_added(GstElement *decodebin,
391
 
                               GstPad     *pad,
392
 
                               gpointer    data)
393
 
{
394
 
    Q_UNUSED(decodebin);
395
 
    GstPad *decodepad = static_cast<GstPad*>(data);
396
 
    gst_pad_link (pad, decodepad);
397
 
    //gst_object_unref (decodepad);
398
 
}
399
 
 
400
 
/**
401
 
 * Create a media source from a given URL.
402
 
 *
403
 
 * returns true if successful
404
 
 */
405
 
bool MediaObject::createPipefromURL(const QUrl &url)
406
 
{
407
 
    // Remove any existing data source
408
 
    if (m_datasource) {
409
 
        gst_bin_remove(GST_BIN(m_pipeline), m_datasource);
410
 
        // m_pipeline has the only ref to datasource
411
 
        m_datasource = 0;
412
 
    }
413
 
 
414
 
    // Verify that the uri can be parsed
415
 
    if (!url.isValid()) {
416
 
        m_backend->logMessage(QString("%1 is not a valid URI").arg(url.toString()));
417
 
        return false;
418
 
    }
419
 
 
420
 
    // Create a new datasource based on the input URL
421
 
    // add the 'file' scheme if it's missing; the double '/' is needed!
422
 
    QByteArray encoded_cstr_url = (url.scheme() == QLatin1String("") ?
423
 
                    "file://" + url.toEncoded() :
424
 
                    url.toEncoded());
425
 
    m_datasource = gst_element_make_from_uri(GST_URI_SRC, encoded_cstr_url.constData(), (const char*)NULL);
426
 
    if (!m_datasource)
427
 
        return false;
428
 
 
429
 
    // Set the device for MediaSource::Disc
430
 
    if (m_source.type() == MediaSource::Disc) {
431
 
 
432
 
        if (g_object_class_find_property (G_OBJECT_GET_CLASS (m_datasource), "device")) {
433
 
            QByteArray mediaDevice = QFile::encodeName(m_source.deviceName());
434
 
            if (!mediaDevice.isEmpty())
435
 
                g_object_set (G_OBJECT (m_datasource), "device", mediaDevice.constData(), (const char*)NULL);
436
 
        }
437
 
 
438
 
        // Also Set optical disc speed to 2X for Audio CD
439
 
        if (m_source.discType() == Phonon::Cd
440
 
            && (g_object_class_find_property (G_OBJECT_GET_CLASS (m_datasource), "read-speed"))) {
441
 
            g_object_set (G_OBJECT (m_datasource), "read-speed", 2, (const char*)NULL);
442
 
            m_backend->logMessage(QString("new device speed : 2X"), Backend::Info, this);
443
 
        }
444
 
  }
445
 
 
446
 
    /* make HTTP sources send extra headers so we get icecast
447
 
     * metadata in case the stream is an icecast stream */
448
 
    if (encoded_cstr_url.startsWith("http://")
449
 
        && g_object_class_find_property (G_OBJECT_GET_CLASS (m_datasource), "iradio-mode")) {
450
 
        g_object_set (m_datasource, "iradio-mode", TRUE, NULL);
451
 
        m_isStream = true;
452
 
    }
453
 
 
454
 
    // Link data source into pipeline
455
 
    gst_bin_add(GST_BIN(m_pipeline), m_datasource);
456
 
    if (!gst_element_link(m_datasource, m_decodebin)) {
457
 
        // For sources with dynamic pads (such as RtspSrc) we need to connect dynamically
458
 
        GstPad *decodepad = gst_element_get_pad (m_decodebin, "sink");
459
 
        g_signal_connect (m_datasource, "pad-added", G_CALLBACK (&cb_pad_added), decodepad);
460
 
    }
461
 
 
462
 
    return true;
463
 
}
464
 
 
465
 
/**
466
 
 * Create a media source from a media stream
467
 
 *
468
 
 * returns true if successful
469
 
 */
470
 
bool MediaObject::createPipefromStream(const MediaSource &source)
471
 
{
472
 
#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
473
 
    // Remove any existing data source
474
 
    if (m_datasource) {
475
 
        gst_bin_remove(GST_BIN(m_pipeline), m_datasource);
476
 
        // m_pipeline has the only ref to datasource
477
 
        m_datasource = 0;
478
 
    }
479
 
 
480
 
    m_datasource = GST_ELEMENT(g_object_new(phonon_src_get_type(), NULL));
481
 
    if (!m_datasource)
482
 
        return false;
483
 
 
484
 
    StreamReader *streamReader = new StreamReader(source);
485
 
    g_object_set (G_OBJECT (m_datasource), "iodevice", streamReader, (const char*)NULL);
486
 
 
487
 
    // Link data source into pipeline
488
 
    gst_bin_add(GST_BIN(m_pipeline), m_datasource);
489
 
    if (!gst_element_link(m_datasource, m_decodebin)) {
490
 
        gst_bin_remove(GST_BIN(m_pipeline), m_datasource);
491
 
        return false;
492
 
    }
493
 
    return true;
494
 
#else //QT_NO_PHONON_ABSTRACTMEDIASTREAM
495
 
    Q_UNUSED(source);
496
 
    return false;
497
 
#endif
498
 
}
499
 
 
500
 
void MediaObject::createPipeline()
501
 
{
502
 
    m_pipeline = gst_pipeline_new (NULL);
503
 
    gst_object_ref (GST_OBJECT (m_pipeline));
504
 
    gst_object_sink (GST_OBJECT (m_pipeline));
505
 
 
506
 
    m_decodebin = gst_element_factory_make ("decodebin2", NULL);
507
 
    g_signal_connect (m_decodebin, "new-decoded-pad", G_CALLBACK (&cb_newpad), this);
508
 
    g_signal_connect (m_decodebin, "unknown-type", G_CALLBACK (&cb_unknown_type), this);
509
 
    g_signal_connect (m_decodebin, "no-more-pads", G_CALLBACK (&cb_no_more_pads), this);
510
 
 
511
 
    gst_bin_add(GST_BIN(m_pipeline), m_decodebin);
512
 
 
513
 
    // Create a bin to contain the gst elements for this medianode
514
 
 
515
 
    // Set up audio graph
516
 
    m_audioGraph = gst_bin_new(NULL);
517
 
    gst_object_ref (GST_OBJECT (m_audioGraph));
518
 
    gst_object_sink (GST_OBJECT (m_audioGraph));
519
 
 
520
 
    // Note that these queues are only required for streaming content
521
 
    // And should ideally be created on demand as they will disable
522
 
    // pull-mode access. Also note that the max-size-time are increased to
523
 
    // reduce buffer overruns as these are not gracefully handled at the moment.
524
 
    m_audioPipe = gst_element_factory_make("queue", NULL);
525
 
    g_object_set(G_OBJECT(m_audioPipe), "max-size-time",  MAX_QUEUE_TIME, (const char*)NULL);
526
 
    gst_bin_add(GST_BIN(m_audioGraph), m_audioPipe);
527
 
    GstPad *audiopad = gst_element_get_pad (m_audioPipe, "sink");
528
 
    gst_element_add_pad (m_audioGraph, gst_ghost_pad_new ("sink", audiopad));
529
 
    gst_object_unref (audiopad);
530
 
 
531
 
    // Set up video graph
532
 
    m_videoGraph = gst_bin_new(NULL);
533
 
    gst_object_ref (GST_OBJECT (m_videoGraph));
534
 
    gst_object_sink (GST_OBJECT (m_videoGraph));
535
 
 
536
 
    m_videoPipe = gst_element_factory_make("queue", NULL);
537
 
    g_object_set(G_OBJECT(m_videoPipe), "max-size-time", MAX_QUEUE_TIME, (const char*)NULL);
538
 
    gst_bin_add(GST_BIN(m_videoGraph), m_videoPipe);
539
 
    GstPad *videopad = gst_element_get_pad (m_videoPipe, "sink");
540
 
    gst_element_add_pad (m_videoGraph, gst_ghost_pad_new ("sink", videopad));
541
 
    gst_object_unref (videopad);
542
 
 
543
 
    if (m_pipeline && m_decodebin && m_audioGraph && m_videoGraph && m_audioPipe && m_videoPipe)
544
 
        m_isValid = true;
545
 
    else
546
 
        m_backend->logMessage("Could not create pipeline for media object", Backend::Warning);
547
 
}
548
 
 
549
 
/**
550
 
 * !reimp
551
 
 */
552
 
State MediaObject::state() const
553
 
{
554
 
    return m_state;
555
 
}
556
 
 
557
 
/**
558
 
 * !reimp
559
 
 */
560
 
bool MediaObject::hasVideo() const
561
 
{
562
 
    return m_hasVideo;
563
 
}
564
 
 
565
 
/**
566
 
 * !reimp
567
 
 */
568
 
bool MediaObject::isSeekable() const
569
 
{
570
 
    return m_seekable;
571
 
}
572
 
 
573
 
/**
574
 
 * !reimp
575
 
 */
576
 
qint64 MediaObject::currentTime() const
577
 
{
578
 
    if (m_resumeState)
579
 
        return m_oldPos;
580
 
 
581
 
    switch (state()) {
582
 
    case Phonon::PausedState:
583
 
    case Phonon::BufferingState:
584
 
    case Phonon::PlayingState:
585
 
        return getPipelinePos();
586
 
    case Phonon::StoppedState:
587
 
    case Phonon::LoadingState:
588
 
        return 0;
589
 
    case Phonon::ErrorState:
590
 
        break;
591
 
    }
592
 
    return -1;
593
 
}
594
 
 
595
 
/**
596
 
 * !reimp
597
 
 */
598
 
qint32 MediaObject::tickInterval() const
599
 
{
600
 
    return m_tickInterval;
601
 
}
602
 
 
603
 
/**
604
 
 * !reimp
605
 
 */
606
 
void MediaObject::setTickInterval(qint32 newTickInterval)
607
 
{
608
 
    m_tickInterval = newTickInterval;
609
 
    if (m_tickInterval <= 0)
610
 
        m_tickTimer->setInterval(50);
611
 
    else
612
 
        m_tickTimer->setInterval(newTickInterval);
613
 
}
614
 
 
615
 
/**
616
 
 * !reimp
617
 
 */
618
 
void MediaObject::play()
619
 
{
620
 
    setState(Phonon::PlayingState);
621
 
    m_resumeState = false;
622
 
}
623
 
 
624
 
/**
625
 
 * !reimp
626
 
 */
627
 
QString MediaObject::errorString() const
628
 
{
629
 
    return m_errorString;
630
 
}
631
 
 
632
 
/**
633
 
 * !reimp
634
 
 */
635
 
Phonon::ErrorType MediaObject::errorType() const
636
 
{
637
 
    return m_error;
638
 
}
639
 
 
640
 
/**
641
 
 * Set the current state of the mediaObject.
642
 
 *
643
 
 * !### Note that both Playing and Paused states are set immediately
644
 
 *     This should obviously be done in response to actual gstreamer state changes
645
 
 */
646
 
void MediaObject::setState(State newstate)
647
 
{
648
 
    if (!isValid())
649
 
        return;
650
 
 
651
 
    if (m_state == newstate)
652
 
        return;
653
 
 
654
 
    if (m_loading) {
655
 
        // We are still loading. The state will be requested
656
 
        // when loading has completed.
657
 
        m_pendingState = newstate;
658
 
        return;
659
 
    }
660
 
 
661
 
    GstState currentState;
662
 
    gst_element_get_state (m_pipeline, &currentState, NULL, 1000);
663
 
 
664
 
    switch (newstate) {
665
 
    case Phonon::BufferingState:
666
 
        m_backend->logMessage("phonon state request: buffering", Backend::Info, this);
667
 
        break;
668
 
 
669
 
    case Phonon::PausedState:
670
 
        m_backend->logMessage("phonon state request: paused", Backend::Info, this);
671
 
        if (currentState == GST_STATE_PAUSED) {
672
 
            changeState(Phonon::PausedState);
673
 
        } else if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE) {
674
 
            m_pendingState = Phonon::PausedState;
675
 
        } else {
676
 
            m_backend->logMessage("phonon state request failed", Backend::Info, this);
677
 
        }
678
 
        break;
679
 
 
680
 
    case Phonon::StoppedState:
681
 
        m_backend->logMessage("phonon state request: Stopped", Backend::Info, this);
682
 
        if (currentState == GST_STATE_READY) {
683
 
            changeState(Phonon::StoppedState);
684
 
        } else if (gst_element_set_state(m_pipeline, GST_STATE_READY) != GST_STATE_CHANGE_FAILURE) {
685
 
            m_pendingState = Phonon::StoppedState;
686
 
        } else {
687
 
            m_backend->logMessage("phonon state request failed", Backend::Info, this);
688
 
        }
689
 
        m_atEndOfStream = false;
690
 
        break;
691
 
 
692
 
    case Phonon::PlayingState:
693
 
       if (m_resetNeeded) {
694
 
            // ### Note this is a workaround and it should really be gracefully
695
 
            // handled by medianode when we implement live connections.
696
 
            // This generally happens if medianodes have been connected after the MediaSource was set
697
 
            // Note that a side-effect of this is that we resend all meta data.
698
 
            gst_element_set_state(m_pipeline, GST_STATE_NULL);
699
 
            m_resetNeeded = false;
700
 
            // Send a source change so the X11 renderer
701
 
            // will re-set the overlay
702
 
            MediaNodeEvent event(MediaNodeEvent::SourceChanged);
703
 
            notify(&event);
704
 
        }
705
 
        m_backend->logMessage("phonon state request: Playing", Backend::Info, this);
706
 
        if (m_atEndOfStream) {
707
 
            m_backend->logMessage("EOS already reached", Backend::Info, this);
708
 
        } else if (currentState == GST_STATE_PLAYING) {
709
 
            changeState(Phonon::PlayingState);
710
 
        } else if (gst_element_set_state(m_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE) {
711
 
            m_pendingState = Phonon::PlayingState;
712
 
        } else {
713
 
            m_backend->logMessage("phonon state request failed", Backend::Info, this);
714
 
        }
715
 
        break;
716
 
 
717
 
    case Phonon::ErrorState:
718
 
        m_backend->logMessage("phonon state request : Error", Backend::Warning, this);
719
 
        m_backend->logMessage(QString("Last error : %0").arg(errorString()) , Backend::Warning, this);
720
 
        changeState(Phonon::ErrorState); //immediately set error state
721
 
        break;
722
 
 
723
 
    case Phonon::LoadingState:
724
 
        m_backend->logMessage("phonon state request: Loading", Backend::Info, this);
725
 
        changeState(Phonon::LoadingState);
726
 
        break;
727
 
    }
728
 
}
729
 
 
730
 
/*
731
 
 * Signals that the requested state has completed
732
 
 * by emitting stateChanged and updates the internal state.
733
 
 */
734
 
void MediaObject::changeState(State newstate)
735
 
{
736
 
    if (newstate == m_state)
737
 
        return;
738
 
 
739
 
    Phonon::State oldState = m_state;
740
 
    m_state = newstate; // m_state must be set before emitting, since
741
 
                        // Error state requires that state() will return the new value
742
 
    m_pendingState = newstate;
743
 
    emit stateChanged(newstate, oldState);
744
 
 
745
 
    switch (newstate) {
746
 
    case Phonon::PausedState:
747
 
        m_backend->logMessage("phonon state changed: paused", Backend::Info, this);
748
 
        break;
749
 
 
750
 
    case Phonon::BufferingState:
751
 
        m_backend->logMessage("phonon state changed: buffering", Backend::Info, this);
752
 
        break;
753
 
 
754
 
    case Phonon::PlayingState:
755
 
        m_backend->logMessage("phonon state changed: Playing", Backend::Info, this);
756
 
        break;
757
 
 
758
 
    case Phonon::StoppedState:
759
 
        m_backend->logMessage("phonon state changed: Stopped", Backend::Info, this);
760
 
        // We must reset the pipeline when playing again
761
 
        m_resetNeeded = true;
762
 
        m_tickTimer->stop();
763
 
        break;
764
 
 
765
 
    case Phonon::ErrorState:
766
 
        m_loading = false;
767
 
        m_backend->logMessage("phonon state changed : Error", Backend::Info, this);
768
 
        m_backend->logMessage(errorString(), Backend::Warning, this);
769
 
        break;
770
 
 
771
 
    case Phonon::LoadingState:
772
 
        m_backend->logMessage("phonon state changed: Loading", Backend::Info, this);
773
 
        break;
774
 
    }
775
 
}
776
 
 
777
 
void MediaObject::setError(const QString &errorString, Phonon::ErrorType error)
778
 
{
779
 
    m_errorString = errorString;
780
 
    m_error = error;
781
 
    m_tickTimer->stop();
782
 
 
783
 
    if (error == Phonon::FatalError) {
784
 
        m_hasVideo = false;
785
 
        emit hasVideoChanged(false);
786
 
        gst_element_set_state(m_pipeline, GST_STATE_READY);
787
 
        changeState(Phonon::ErrorState);
788
 
    } else {
789
 
        if (m_loading) //Flag error only after loading has completed
790
 
            m_pendingState = Phonon::ErrorState;
791
 
        else
792
 
            changeState(Phonon::ErrorState);
793
 
    }
794
 
}
795
 
 
796
 
qint64 MediaObject::totalTime() const
797
 
{
798
 
    return m_totalTime;
799
 
}
800
 
 
801
 
qint32 MediaObject::prefinishMark() const
802
 
{
803
 
    return m_prefinishMark;
804
 
}
805
 
 
806
 
qint32 MediaObject::transitionTime() const
807
 
{
808
 
    return m_transitionTime;
809
 
}
810
 
 
811
 
void MediaObject::setTransitionTime(qint32 time)
812
 
{
813
 
    m_transitionTime = time;
814
 
}
815
 
 
816
 
qint64 MediaObject::remainingTime() const
817
 
{
818
 
    return totalTime() - currentTime();
819
 
}
820
 
 
821
 
MediaSource MediaObject::source() const
822
 
{
823
 
    return m_source;
824
 
}
825
 
 
826
 
void MediaObject::setNextSource(const MediaSource &source)
827
 
{
828
 
    if (source.type() == MediaSource::Invalid &&
829
 
        source.type() == MediaSource::Empty)
830
 
        return;
831
 
    m_nextSource = source;
832
 
}
833
 
 
834
 
/**
835
 
 * Update total time value from the pipeline
836
 
 */
837
 
bool MediaObject::updateTotalTime()
838
 
{
839
 
    GstFormat   format = GST_FORMAT_TIME;
840
 
    gint64 duration = 0;
841
 
    if (gst_element_query_duration (GST_ELEMENT(m_pipeline), &format, &duration)) {
842
 
        setTotalTime(duration / GST_MSECOND);
843
 
        return true;
844
 
    }
845
 
    return false;
846
 
}
847
 
 
848
 
/**
849
 
 * Checks if the current source is seekable
850
 
 */
851
 
void MediaObject::updateSeekable()
852
 
{
853
 
    if (!isValid())
854
 
        return;
855
 
 
856
 
    GstQuery *query;
857
 
    gboolean result;
858
 
    gint64 start, stop;
859
 
    query = gst_query_new_seeking(GST_FORMAT_TIME);
860
 
    result = gst_element_query (m_pipeline, query);
861
 
    if (result) {
862
 
        gboolean seekable;
863
 
        GstFormat format;
864
 
        gst_query_parse_seeking (query, &format, &seekable, &start, &stop);
865
 
 
866
 
        if (m_seekable != seekable) {
867
 
            m_seekable = seekable;
868
 
            emit seekableChanged(m_seekable);
869
 
        }
870
 
 
871
 
        if (m_seekable)
872
 
            m_backend->logMessage("Stream is seekable", Backend::Info, this);
873
 
        else
874
 
            m_backend->logMessage("Stream is non-seekable", Backend::Info, this);
875
 
    } else {
876
 
        m_backend->logMessage("updateSeekable query failed", Backend::Info, this);
877
 
    }
878
 
    gst_query_unref (query);
879
 
}
880
 
 
881
 
qint64 MediaObject::getPipelinePos() const
882
 
{
883
 
    Q_ASSERT(m_pipeline);
884
 
 
885
 
    // Note some formats (usually mpeg) do not allow us to accurately seek to the
886
 
    // beginning or end of the file so we 'fake' it here rather than exposing the front end to potential issues.
887
 
    if (m_atEndOfStream)
888
 
        return totalTime();
889
 
    if (m_atStartOfStream)
890
 
        return 0;
891
 
    if (m_posAtSeek >= 0)
892
 
        return m_posAtSeek;
893
 
 
894
 
    gint64 pos = 0;
895
 
    GstFormat format = GST_FORMAT_TIME;
896
 
    gst_element_query_position (GST_ELEMENT(m_pipeline), &format, &pos);
897
 
    return (pos / GST_MSECOND);
898
 
}
899
 
 
900
 
/*
901
 
 * Internal method to set a new total time for the media object
902
 
 */
903
 
void MediaObject::setTotalTime(qint64 newTime)
904
 
{
905
 
 
906
 
    if (newTime == m_totalTime)
907
 
        return;
908
 
 
909
 
    m_totalTime = newTime;
910
 
 
911
 
    emit totalTimeChanged(m_totalTime);
912
 
}
913
 
 
914
 
/*
915
 
 * !reimp
916
 
 */
917
 
void MediaObject::setSource(const MediaSource &source)
918
 
{
919
 
    if (!isValid())
920
 
        return;
921
 
 
922
 
    // We have to reset the state completely here, otherwise
923
 
    // remnants of the old pipeline can result in strangenes
924
 
    // such as failing duration queries etc
925
 
    GstState state;
926
 
    gst_element_set_state(m_pipeline, GST_STATE_NULL);
927
 
    gst_element_get_state(m_pipeline, &state, NULL, 2000);
928
 
 
929
 
    m_source = source;
930
 
    emit currentSourceChanged(m_source);
931
 
    m_previousTickTime = -1;
932
 
    m_missingCodecs.clear();
933
 
 
934
 
    // Go into to loading state
935
 
    changeState(Phonon::LoadingState);
936
 
    m_loading = true;
937
 
    // IMPORTANT: Honor the m_resetNeeded flag as it currently stands.
938
 
    // See https://qa.mandriva.com/show_bug.cgi?id=56807
939
 
    //m_resetNeeded = false;
940
 
    m_resumeState = false;
941
 
    m_pendingState = Phonon::StoppedState;
942
 
 
943
 
     // Make sure we start out unconnected
944
 
    if (GST_ELEMENT_PARENT(m_audioGraph))
945
 
        gst_bin_remove(GST_BIN(m_pipeline), m_audioGraph);
946
 
    if (GST_ELEMENT_PARENT(m_videoGraph))
947
 
        gst_bin_remove(GST_BIN(m_pipeline), m_videoGraph);
948
 
 
949
 
    // Clear any existing errors
950
 
    m_aboutToFinishEmitted = false;
951
 
    m_error = NoError;
952
 
    m_errorString.clear();
953
 
 
954
 
    m_bufferPercent = 0;
955
 
    m_prefinishMarkReachedNotEmitted = true;
956
 
    m_aboutToFinishEmitted = false;
957
 
    m_hasAudio = false;
958
 
    m_videoStreamFound = false;
959
 
    setTotalTime(-1);
960
 
    m_atEndOfStream = false;
961
 
 
962
 
    m_availableTitles = 0;
963
 
    m_pendingTitle = 1;
964
 
    m_currentTitle = 1;
965
 
 
966
 
    // Clear existing meta tags
967
 
    m_metaData.clear();
968
 
    m_isStream = false;
969
 
 
970
 
    switch (source.type()) {
971
 
    case MediaSource::Url: {
972
 
            if (!createPipefromURL(source.url()))
973
 
                setError(tr("Could not open media source."));
974
 
        }
975
 
        break;
976
 
 
977
 
    case MediaSource::LocalFile: {
978
 
            if (!createPipefromURL(QUrl::fromLocalFile(source.fileName())))
979
 
                setError(tr("Could not open media source."));
980
 
        }
981
 
        break;
982
 
 
983
 
    case MediaSource::Invalid:
984
 
        setError(tr("Invalid source type."), Phonon::NormalError);
985
 
        break;
986
 
 
987
 
    case MediaSource::Empty:
988
 
        break;
989
 
 
990
 
    case MediaSource::Stream:
991
 
        if (!createPipefromStream(source))
992
 
            setError(tr("Could not open media source."));
993
 
        break;
994
 
 
995
 
    case MediaSource::Disc:
996
 
        {
997
 
       QString mediaUrl;
998
 
       switch (source.discType()) {
999
 
       case Phonon::NoDisc:
1000
 
                qWarning() << "I should never get to see a MediaSource that is a disc but doesn't specify which one";
1001
 
                return;
1002
 
            case Phonon::Cd:  // CD tracks can be specified by setting the url in the following way uri=cdda:4
1003
 
                mediaUrl = QLatin1String("cdda://");
1004
 
                break;
1005
 
            case Phonon::Dvd:
1006
 
                mediaUrl = QLatin1String("dvd://");
1007
 
                break;
1008
 
            case Phonon::Vcd:
1009
 
                mediaUrl = QLatin1String("vcd://");
1010
 
                break;
1011
 
            default:
1012
 
                qWarning() <<  "media " << source.discType() << " not implemented";
1013
 
                return;
1014
 
            }
1015
 
            if (mediaUrl.isEmpty() || !createPipefromURL(QUrl(mediaUrl)))
1016
 
                setError(tr("Could not open media source."));
1017
 
        }
1018
 
        break;
1019
 
 
1020
 
    default:
1021
 
        m_backend->logMessage("Source type not currently supported", Backend::Warning, this);
1022
 
        setError(tr("Could not open media source."), Phonon::NormalError);
1023
 
        break;
1024
 
    }
1025
 
 
1026
 
    MediaNodeEvent event(MediaNodeEvent::SourceChanged);
1027
 
    notify(&event);
1028
 
 
1029
 
    // We need to link this node to ensure that fake sinks are connected
1030
 
    // before loading, otherwise the stream will be blocked
1031
 
    link();
1032
 
    beginLoad();
1033
 
}
1034
 
 
1035
 
void MediaObject::beginLoad()
1036
 
{
1037
 
    if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE) {
1038
 
        m_backend->logMessage("Begin source load", Backend::Info, this);
1039
 
    } else {
1040
 
        setError(tr("Could not open media source."));
1041
 
    }
1042
 
}
1043
 
 
1044
 
// Called when we are ready to leave the loading state
1045
 
void MediaObject::loadingComplete()
1046
 
{
1047
 
    if (m_videoStreamFound) {
1048
 
        MediaNodeEvent event(MediaNodeEvent::VideoAvailable);
1049
 
        notify(&event);
1050
 
    }
1051
 
    getStreamInfo();
1052
 
    m_loading = false;
1053
 
 
1054
 
    setState(m_pendingState);
1055
 
    emit metaDataChanged(m_metaData);
1056
 
}
1057
 
 
1058
 
void MediaObject::getStreamInfo()
1059
 
{
1060
 
    updateSeekable();
1061
 
    updateTotalTime();
1062
 
 
1063
 
    if (m_videoStreamFound != m_hasVideo) {
1064
 
        m_hasVideo = m_videoStreamFound;
1065
 
        emit hasVideoChanged(m_hasVideo);
1066
 
    }
1067
 
 
1068
 
    if (m_source.discType() == Phonon::Cd) {
1069
 
        gint64 titleCount;
1070
 
        GstFormat format = gst_format_get_by_nick("track");
1071
 
        if (gst_element_query_duration (m_pipeline, &format, &titleCount)) {
1072
 
        //check if returned format is still "track",
1073
 
        //gstreamer sometimes returns the total time, if tracks information is not available.
1074
 
            if (qstrcmp(gst_format_get_name(format), "track") == 0)  {
1075
 
                int oldAvailableTitles = m_availableTitles;
1076
 
                m_availableTitles = (int)titleCount;
1077
 
                if (m_availableTitles != oldAvailableTitles) {
1078
 
                    emit availableTitlesChanged(m_availableTitles);
1079
 
                    m_backend->logMessage(QString("Available titles changed: %0").arg(m_availableTitles), Backend::Info, this);
1080
 
                }
1081
 
            }
1082
 
        }
1083
 
    }
1084
 
}
1085
 
 
1086
 
void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
1087
 
{
1088
 
    m_prefinishMark = newPrefinishMark;
1089
 
    if (currentTime() < totalTime() - m_prefinishMark) // not about to finish
1090
 
        m_prefinishMarkReachedNotEmitted = true;
1091
 
}
1092
 
 
1093
 
void MediaObject::pause()
1094
 
{
1095
 
    m_backend->logMessage("pause()", Backend::Info, this);
1096
 
    if (state() != Phonon::PausedState)
1097
 
        setState(Phonon::PausedState);
1098
 
    m_resumeState = false;
1099
 
}
1100
 
 
1101
 
void MediaObject::stop()
1102
 
{
1103
 
    if (state() != Phonon::StoppedState) {
1104
 
        setState(Phonon::StoppedState);
1105
 
        m_prefinishMarkReachedNotEmitted = true;
1106
 
    }
1107
 
    m_resumeState = false;
1108
 
}
1109
 
 
1110
 
void MediaObject::seek(qint64 time)
1111
 
{
1112
 
    if (!isValid())
1113
 
        return;
1114
 
 
1115
 
    if (isSeekable()) {
1116
 
        switch (state()) {
1117
 
        case Phonon::PlayingState:
1118
 
        case Phonon::StoppedState:
1119
 
        case Phonon::PausedState:
1120
 
        case Phonon::BufferingState:
1121
 
            m_backend->logMessage(QString("Seek to pos %0").arg(time), Backend::Info, this);
1122
 
 
1123
 
            if (time <= 0)
1124
 
                m_atStartOfStream = true;
1125
 
            else
1126
 
                m_atStartOfStream = false;
1127
 
 
1128
 
            m_posAtSeek = getPipelinePos();
1129
 
            m_tickTimer->stop();
1130
 
 
1131
 
            if (gst_element_seek(m_pipeline, 1.0, GST_FORMAT_TIME,
1132
 
                                 GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET,
1133
 
                                 time * GST_MSECOND, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
1134
 
            break;
1135
 
        case Phonon::LoadingState:
1136
 
        case Phonon::ErrorState:
1137
 
            return;
1138
 
        }
1139
 
 
1140
 
        quint64 current = currentTime();
1141
 
        quint64 total = totalTime();
1142
 
 
1143
 
        if (current < total - m_prefinishMark)
1144
 
            m_prefinishMarkReachedNotEmitted = true;
1145
 
        if (current < total - ABOUT_TO_FINNISH_TIME)
1146
 
            m_aboutToFinishEmitted = false;
1147
 
        m_atEndOfStream = false;
1148
 
    }
1149
 
}
1150
 
 
1151
 
void MediaObject::emitTick()
1152
 
{
1153
 
    if (m_resumeState) {
1154
 
        return;
1155
 
    }
1156
 
 
1157
 
    qint64 currentTime = getPipelinePos();
1158
 
    qint64 totalTime = m_totalTime;
1159
 
 
1160
 
    if (m_tickInterval > 0 && currentTime != m_previousTickTime) {
1161
 
        emit tick(currentTime);
1162
 
        m_previousTickTime = currentTime;
1163
 
    }
1164
 
    if (m_state == Phonon::PlayingState) {
1165
 
        if (currentTime >= totalTime - m_prefinishMark) {
1166
 
            if (m_prefinishMarkReachedNotEmitted) {
1167
 
                m_prefinishMarkReachedNotEmitted = false;
1168
 
                emit prefinishMarkReached(totalTime - currentTime);
1169
 
            }
1170
 
        }
1171
 
        // Prepare load of next source
1172
 
        if (currentTime >= totalTime - ABOUT_TO_FINNISH_TIME) {
1173
 
            if (m_source.type() == MediaSource::Disc &&
1174
 
                m_autoplayTitles &&
1175
 
                m_availableTitles > 1 &&
1176
 
                m_currentTitle < m_availableTitles) {
1177
 
                m_aboutToFinishEmitted = false;
1178
 
            } else if (!m_aboutToFinishEmitted) {
1179
 
                m_aboutToFinishEmitted = true; // track is about to finish
1180
 
                emit aboutToFinish();
1181
 
            }
1182
 
        }
1183
 
    }
1184
 
}
1185
 
 
1186
 
 
1187
 
/*
1188
 
 * Used to iterate through the gst_tag_list and extract values
1189
 
 */
1190
 
void foreach_tag_function(const GstTagList *list, const gchar *tag, gpointer user_data)
1191
 
{
1192
 
    TagMap *newData = static_cast<TagMap *>(user_data);
1193
 
    QString value;
1194
 
    GType type = gst_tag_get_type(tag);
1195
 
    switch (type) {
1196
 
    case G_TYPE_STRING: {
1197
 
            char *str = 0;
1198
 
            gst_tag_list_get_string(list, tag, &str);
1199
 
            value = QString::fromUtf8(str);
1200
 
            g_free(str);
1201
 
        }
1202
 
        break;
1203
 
 
1204
 
    case G_TYPE_BOOLEAN: {
1205
 
            int bval;
1206
 
            gst_tag_list_get_boolean(list, tag, &bval);
1207
 
            value = QString::number(bval);
1208
 
        }
1209
 
        break;
1210
 
 
1211
 
    case G_TYPE_INT: {
1212
 
            int ival;
1213
 
            gst_tag_list_get_int(list, tag, &ival);
1214
 
            value = QString::number(ival);
1215
 
        }
1216
 
        break;
1217
 
 
1218
 
    case G_TYPE_UINT: {
1219
 
            unsigned int uival;
1220
 
            gst_tag_list_get_uint(list, tag, &uival);
1221
 
            value = QString::number(uival);
1222
 
        }
1223
 
        break;
1224
 
 
1225
 
    case G_TYPE_FLOAT: {
1226
 
            float fval;
1227
 
            gst_tag_list_get_float(list, tag, &fval);
1228
 
            value = QString::number(fval);
1229
 
        }
1230
 
        break;
1231
 
 
1232
 
    case G_TYPE_DOUBLE: {
1233
 
            double dval;
1234
 
            gst_tag_list_get_double(list, tag, &dval);
1235
 
            value = QString::number(dval);
1236
 
        }
1237
 
        break;
1238
 
 
1239
 
    default:
1240
 
        //qDebug("Unsupported tag type: %s", g_type_name(type));
1241
 
        break;
1242
 
    }
1243
 
 
1244
 
    QString key = QString(tag).toUpper();
1245
 
    QString currVal = newData->value(key);
1246
 
    if (!value.isEmpty() && !(newData->contains(key) && currVal == value))
1247
 
        newData->insert(key, value);
1248
 
}
1249
 
 
1250
 
/**
1251
 
 * Triggers playback after a song has completed in the current media queue
1252
 
 */
1253
 
void MediaObject::beginPlay()
1254
 
{
1255
 
    setSource(m_nextSource);
1256
 
    m_nextSource = MediaSource();
1257
 
    m_pendingState = Phonon::PlayingState;
1258
 
}
1259
 
 
1260
 
/**
1261
 
 * Handle GStreamer bus messages
1262
 
 */
1263
 
void MediaObject::handleBusMessage(const Message &message)
1264
 
{
1265
 
 
1266
 
    if (!isValid())
1267
 
        return;
1268
 
 
1269
 
    GstMessage *gstMessage = message.rawMessage();
1270
 
    Q_ASSERT(m_pipeline);
1271
 
 
1272
 
    if (m_backend->debugLevel() >= Backend::Debug) {
1273
 
        int type = GST_MESSAGE_TYPE(gstMessage);
1274
 
        gchar* name = gst_element_get_name(gstMessage->src);
1275
 
        QString msgString = QString("Bus: %0 (%1)").arg(gst_message_type_get_name ((GstMessageType)type)).arg(name);
1276
 
        g_free(name);
1277
 
        m_backend->logMessage(msgString, Backend::Debug, this);
1278
 
    }
1279
 
 
1280
 
    switch (GST_MESSAGE_TYPE (gstMessage)) {
1281
 
 
1282
 
    case GST_MESSAGE_EOS:
1283
 
        m_backend->logMessage("EOS received", Backend::Info, this);
1284
 
        handleEndOfStream();
1285
 
        break;
1286
 
 
1287
 
    case GST_MESSAGE_TAG: {
1288
 
            GstTagList* tag_list = 0;
1289
 
            gst_message_parse_tag(gstMessage, &tag_list);
1290
 
            if (tag_list) {
1291
 
                TagMap newTags;
1292
 
                gst_tag_list_foreach (tag_list, &foreach_tag_function, &newTags);
1293
 
                gst_tag_list_free(tag_list);
1294
 
 
1295
 
                // Determine if we should no fake the album/artist tags.
1296
 
                // This is a little confusing as we want to fake it on initial
1297
 
                // connection where title, album and artist are all missing.
1298
 
                // There are however times when we get just other information,
1299
 
                // e.g. codec, and so we want to only do clever stuff if we
1300
 
                // have a commonly available tag (ORGANIZATION) or we have a
1301
 
                // change in title
1302
 
                bool fake_it =
1303
 
                   (m_isStream
1304
 
                    && ((!newTags.contains("TITLE")
1305
 
                         && newTags.contains("ORGANIZATION"))
1306
 
                        || (newTags.contains("TITLE")
1307
 
                            && m_metaData.value("TITLE") != newTags.value("TITLE")))
1308
 
                    && !newTags.contains("ALBUM")
1309
 
                    && !newTags.contains("ARTIST"));
1310
 
 
1311
 
                TagMap oldMap = m_metaData; // Keep a copy of the old one for reference
1312
 
 
1313
 
                // Now we've checked the new data, append any new meta tags to the existing tag list
1314
 
                // We cannot use TagMap::iterator as this is a multimap and when streaming data
1315
 
                // could in theory be lost.
1316
 
                QList<QString> keys = newTags.keys();
1317
 
                for (QList<QString>::iterator i = keys.begin(); i != keys.end(); ++i) {
1318
 
                    QString key = *i;
1319
 
                    if (m_isStream) {
1320
 
                        // If we're streaming, we need to remove data in m_metaData
1321
 
                        // in order to stop it filling up indefinitely (as it's a multimap)
1322
 
                        m_metaData.remove(key);
1323
 
                    }
1324
 
                    QList<QString> values = newTags.values(key);
1325
 
                    for (QList<QString>::iterator j = values.begin(); j != values.end(); ++j) {
1326
 
                        QString value = *j;
1327
 
                        QString currVal = m_metaData.value(key);
1328
 
                        if (!m_metaData.contains(key) || currVal != value) {
1329
 
                            m_metaData.insert(key, value);
1330
 
                        }
1331
 
                    }
1332
 
                }
1333
 
 
1334
 
                m_backend->logMessage("Meta tags found", Backend::Info, this);
1335
 
                if (oldMap != m_metaData) {
1336
 
                    // This is a bit of a hack to ensure that stream metadata is
1337
 
                    // returned. We get as much as we can from the Shoutcast server's
1338
 
                    // StreamTitle= header. If further info is decoded from the stream
1339
 
                    // itself later, then it will overwrite this info.
1340
 
                    if (m_isStream && fake_it) {
1341
 
                        m_metaData.remove("ALBUM");
1342
 
                        m_metaData.remove("ARTIST");
1343
 
 
1344
 
                        // Detect whether we want to "fill in the blanks"
1345
 
                        QString str;
1346
 
                        if (m_metaData.contains("TITLE"))
1347
 
                        {
1348
 
                            str = m_metaData.value("TITLE");
1349
 
                            int splitpoint;
1350
 
                            // Check to see if our title matches "%s - %s"
1351
 
                            // Where neither %s are empty...
1352
 
                            if ((splitpoint = str.indexOf(" - ")) > 0
1353
 
                                && str.size() > (splitpoint+3)) {
1354
 
                                m_metaData.insert("ARTIST", str.left(splitpoint));
1355
 
                                m_metaData.replace("TITLE", str.mid(splitpoint+3));
1356
 
                            }
1357
 
                        } else {
1358
 
                            str = m_metaData.value("GENRE");
1359
 
                            if (!str.isEmpty())
1360
 
                                m_metaData.insert("TITLE", str);
1361
 
                            else
1362
 
                                m_metaData.insert("TITLE", "Streaming Data");
1363
 
                        }
1364
 
                        if (!m_metaData.contains("ARTIST")) {
1365
 
                            str = m_metaData.value("LOCATION");
1366
 
                            if (!str.isEmpty())
1367
 
                                m_metaData.insert("ARTIST", str);
1368
 
                            else
1369
 
                                m_metaData.insert("ARTIST", "Streaming Data");
1370
 
                        }
1371
 
                        str = m_metaData.value("ORGANIZATION");
1372
 
                        if (!str.isEmpty())
1373
 
                            m_metaData.insert("ALBUM", str);
1374
 
                        else
1375
 
                            m_metaData.insert("ALBUM", "Streaming Data");
1376
 
                    }
1377
 
                    // As we manipulate the title, we need to recompare
1378
 
                    // oldMap and m_metaData here...
1379
 
                    if (oldMap != m_metaData && !m_loading)
1380
 
                        emit metaDataChanged(m_metaData);
1381
 
                }
1382
 
                        }
1383
 
        }
1384
 
        break;
1385
 
 
1386
 
    case GST_MESSAGE_STATE_CHANGED : {
1387
 
 
1388
 
            if (gstMessage->src != GST_OBJECT(m_pipeline))
1389
 
                return;
1390
 
 
1391
 
            GstState oldState;
1392
 
            GstState newState;
1393
 
            GstState pendingState;
1394
 
            gst_message_parse_state_changed (gstMessage, &oldState, &newState, &pendingState);
1395
 
 
1396
 
            if (newState == pendingState)
1397
 
                return;
1398
 
 
1399
 
            m_posAtSeek = -1;
1400
 
 
1401
 
            switch (newState) {
1402
 
 
1403
 
            case GST_STATE_PLAYING :
1404
 
                m_atStartOfStream = false;
1405
 
                m_backend->logMessage("gstreamer: pipeline state set to playing", Backend::Info, this);
1406
 
                m_tickTimer->start();
1407
 
                changeState(Phonon::PlayingState);
1408
 
                if ((m_source.type() == MediaSource::Disc) && (m_currentTitle != m_pendingTitle)) {
1409
 
                    setTrack(m_pendingTitle);
1410
 
                }
1411
 
                if (m_resumeState && m_oldState == Phonon::PlayingState) {
1412
 
                    seek(m_oldPos);
1413
 
                    m_resumeState = false;
1414
 
                }
1415
 
                break;
1416
 
 
1417
 
            case GST_STATE_NULL:
1418
 
                m_backend->logMessage("gstreamer: pipeline state set to null", Backend::Info, this);
1419
 
                m_tickTimer->stop();
1420
 
                break;
1421
 
 
1422
 
            case GST_STATE_PAUSED :
1423
 
                m_backend->logMessage("gstreamer: pipeline state set to paused", Backend::Info, this);
1424
 
                m_tickTimer->start();
1425
 
                if (state() == Phonon::LoadingState) {
1426
 
                    // No_more_pads is not emitted from the decodebin in older versions (0.10.4)
1427
 
                    noMorePadsAvailable();
1428
 
                    loadingComplete();
1429
 
                } else if (m_resumeState && m_oldState == Phonon::PausedState) {
1430
 
                    changeState(Phonon::PausedState);
1431
 
                    m_resumeState = false;
1432
 
                    break;
1433
 
                } else {
1434
 
                    // A lot of autotests can break if we allow all paused changes through.
1435
 
                    if (m_pendingState == Phonon::PausedState) {
1436
 
                        changeState(Phonon::PausedState);
1437
 
                    }
1438
 
                }
1439
 
                break;
1440
 
 
1441
 
            case GST_STATE_READY :
1442
 
                if (!m_loading && m_pendingState == Phonon::StoppedState)
1443
 
                    changeState(Phonon::StoppedState);
1444
 
                m_backend->logMessage("gstreamer: pipeline state set to ready", Backend::Debug, this);
1445
 
                m_tickTimer->stop();
1446
 
                if ((m_source.type() == MediaSource::Disc) && (m_currentTitle != m_pendingTitle)) {
1447
 
                    setTrack(m_pendingTitle);
1448
 
                }
1449
 
                break;
1450
 
 
1451
 
            case GST_STATE_VOID_PENDING :
1452
 
                m_backend->logMessage("gstreamer: pipeline state set to pending (void)", Backend::Debug, this);
1453
 
                m_tickTimer->stop();
1454
 
                break;
1455
 
            }
1456
 
            break;
1457
 
        }
1458
 
 
1459
 
    case GST_MESSAGE_ERROR: {
1460
 
            gchar *debug;
1461
 
            GError *err;
1462
 
            QString logMessage;
1463
 
            gst_message_parse_error (gstMessage, &err, &debug);
1464
 
            gchar *errorMessage = gst_error_get_message (err->domain, err->code);
1465
 
            logMessage.sprintf("Error: %s Message:%s (%s) Code:%d", debug, err->message, errorMessage, err->code);
1466
 
            m_backend->logMessage(logMessage, Backend::Warning);
1467
 
            g_free(errorMessage);
1468
 
            g_free (debug);
1469
 
 
1470
 
            if (err->domain == GST_RESOURCE_ERROR) {
1471
 
                if (err->code == GST_RESOURCE_ERROR_NOT_FOUND) {
1472
 
                    setError(tr("Could not locate media source."), Phonon::FatalError);
1473
 
                } else if (err->code == GST_RESOURCE_ERROR_OPEN_READ) {
1474
 
                    setError(tr("Could not open media source."), Phonon::FatalError);
1475
 
                } else if (err->code == GST_RESOURCE_ERROR_BUSY) {
1476
 
                   // We need to check if this comes from an audio device by looking at sink caps
1477
 
                   GstPad* sinkPad = gst_element_get_static_pad(GST_ELEMENT(gstMessage->src), "sink");
1478
 
                   if (sinkPad) {
1479
 
                        GstCaps *caps = gst_pad_get_caps (sinkPad);
1480
 
                        GstStructure *str = gst_caps_get_structure (caps, 0);
1481
 
                        if (g_strrstr (gst_structure_get_name (str), "audio"))
1482
 
                            setError(tr("Could not open audio device. The device is already in use."), Phonon::NormalError);
1483
 
                        else
1484
 
                            setError(err->message, Phonon::FatalError);
1485
 
                        gst_caps_unref (caps);
1486
 
                        gst_object_unref (sinkPad);
1487
 
                   }
1488
 
               } else {
1489
 
                    setError(QString(err->message), Phonon::FatalError);
1490
 
               }
1491
 
           } else if (err->domain == GST_STREAM_ERROR) {
1492
 
                switch (err->code) {
1493
 
                case GST_STREAM_ERROR_WRONG_TYPE:
1494
 
                case GST_STREAM_ERROR_TYPE_NOT_FOUND:
1495
 
                    setError(tr("Could not decode media source."), Phonon::FatalError);
1496
 
                    break;
1497
 
                default:
1498
 
                    setError(tr("Could not open media source."), Phonon::FatalError);
1499
 
                    break;
1500
 
                }
1501
 
           } else {
1502
 
                setError(QString(err->message), Phonon::FatalError);
1503
 
            }
1504
 
            g_error_free (err);
1505
 
            break;
1506
 
        }
1507
 
 
1508
 
    case GST_MESSAGE_WARNING: {
1509
 
            gchar *debug;
1510
 
            GError *err;
1511
 
            gst_message_parse_warning(gstMessage, &err, &debug);
1512
 
            QString msgString;
1513
 
            msgString.sprintf("Warning: %s\nMessage:%s", debug, err->message);
1514
 
            m_backend->logMessage(msgString, Backend::Warning);
1515
 
            g_free (debug);
1516
 
            g_error_free (err);
1517
 
            break;
1518
 
        }
1519
 
 
1520
 
    case GST_MESSAGE_ELEMENT: {
1521
 
            GstMessage *gstMessage = message.rawMessage();
1522
 
            const GstStructure *gstStruct = gst_message_get_structure(gstMessage); //do not free this
1523
 
            if (g_strrstr (gst_structure_get_name (gstStruct), "prepare-xwindow-id")) {
1524
 
                MediaNodeEvent videoHandleEvent(MediaNodeEvent::VideoHandleRequest);
1525
 
                notify(&videoHandleEvent);
1526
 
            }
1527
 
            break;
1528
 
        }
1529
 
 
1530
 
    case GST_MESSAGE_DURATION: {
1531
 
            m_backend->logMessage("GST_MESSAGE_DURATION", Backend::Debug, this);
1532
 
            updateTotalTime();
1533
 
            break;
1534
 
        }
1535
 
 
1536
 
    case GST_MESSAGE_BUFFERING: {
1537
 
            gint percent = 0;
1538
 
            gst_structure_get_int (gstMessage->structure, "buffer-percent", &percent); //gst_message_parse_buffering was introduced in 0.10.11
1539
 
 
1540
 
            if (m_bufferPercent != percent) {
1541
 
                emit bufferStatus(percent);
1542
 
                m_backend->logMessage(QString("Stream buffering %0").arg(percent), Backend::Debug, this);
1543
 
                m_bufferPercent = percent;
1544
 
            }
1545
 
 
1546
 
            if (m_state != Phonon::BufferingState)
1547
 
                emit stateChanged(m_state, Phonon::BufferingState);
1548
 
            else if (percent == 100)
1549
 
                emit stateChanged(Phonon::BufferingState, m_state);
1550
 
            break;
1551
 
        }
1552
 
        //case GST_MESSAGE_INFO:
1553
 
        //case GST_MESSAGE_STREAM_STATUS:
1554
 
        //case GST_MESSAGE_CLOCK_PROVIDE:
1555
 
        //case GST_MESSAGE_NEW_CLOCK:
1556
 
        //case GST_MESSAGE_STEP_DONE:
1557
 
        //case GST_MESSAGE_LATENCY: only from 0.10.12
1558
 
        //case GST_MESSAGE_ASYNC_DONE: only from 0.10.13
1559
 
    default:
1560
 
        break;
1561
 
    }
1562
 
 
1563
 
    switch (gst_navigation_message_get_type(gstMessage)) {
1564
 
    case GST_NAVIGATION_MESSAGE_MOUSE_OVER: {
1565
 
        gboolean active;
1566
 
        if (!gst_navigation_message_parse_mouse_over(gstMessage, &active)) {
1567
 
            break;
1568
 
        }
1569
 
        MediaNodeEvent mouseOverEvent(MediaNodeEvent::VideoMouseOver, &active);
1570
 
        notify(&mouseOverEvent);
1571
 
        break;
1572
 
    }
1573
 
    default:
1574
 
        break;
1575
 
    }
1576
 
 
1577
 
}
1578
 
 
1579
 
void MediaObject::handleEndOfStream()
1580
 
{
1581
 
    // If the stream is not seekable ignore
1582
 
    // otherwise chained radio broadcasts would stop
1583
 
 
1584
 
 
1585
 
    if (m_atEndOfStream)
1586
 
        return;
1587
 
 
1588
 
    if (!m_seekable)
1589
 
        m_atEndOfStream = true;
1590
 
 
1591
 
    if (m_source.type() == MediaSource::Disc &&
1592
 
        m_autoplayTitles &&
1593
 
        m_availableTitles > 1 &&
1594
 
        m_currentTitle < m_availableTitles) {
1595
 
        _iface_setCurrentTitle(m_currentTitle + 1);
1596
 
        return;
1597
 
    }
1598
 
 
1599
 
    if (m_nextSource.type() != MediaSource::Invalid
1600
 
        && m_nextSource.type() != MediaSource::Empty) {  // We only emit finish when the queue is actually empty
1601
 
        QTimer::singleShot (qMax(0, transitionTime()), this, SLOT(beginPlay()));
1602
 
    } else {
1603
 
        m_pendingState = Phonon::PausedState;
1604
 
        emit finished();
1605
 
        if (!m_seekable) {
1606
 
            setState(Phonon::StoppedState);
1607
 
            // Note the behavior for live streams is not properly defined
1608
 
            // But since we cant seek to 0, we don't have much choice other than stopping
1609
 
            // the stream
1610
 
        } else {
1611
 
            // Only emit paused if the finished signal
1612
 
            // did not result in a new state
1613
 
            if (m_pendingState == Phonon::PausedState)
1614
 
                setState(m_pendingState);
1615
 
        }
1616
 
    }
1617
 
}
1618
 
 
1619
 
void MediaObject::invalidateGraph()
1620
 
{
1621
 
    m_resetNeeded = true;
1622
 
    if (m_state == Phonon::PlayingState || m_state == Phonon::PausedState) {
1623
 
        changeState(Phonon::StoppedState);
1624
 
    }
1625
 
}
1626
 
 
1627
 
// Notifes the pipeline about state changes in the media object
1628
 
void MediaObject::notifyStateChange(Phonon::State newstate, Phonon::State oldstate)
1629
 
{
1630
 
    Q_UNUSED(oldstate);
1631
 
    MediaNodeEvent event(MediaNodeEvent::StateChanged, &newstate);
1632
 
    notify(&event);
1633
 
}
1634
 
 
1635
 
#ifndef QT_NO_PHONON_MEDIACONTROLLER
1636
 
//interface management
1637
 
bool MediaObject::hasInterface(Interface iface) const
1638
 
{
1639
 
    return iface == AddonInterface::TitleInterface;
1640
 
}
1641
 
 
1642
 
QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> &params)
1643
 
{
1644
 
    if (hasInterface(iface)) {
1645
 
 
1646
 
        switch (iface)
1647
 
        {
1648
 
        case TitleInterface:
1649
 
            switch (command)
1650
 
            {
1651
 
            case availableTitles:
1652
 
                return _iface_availableTitles();
1653
 
            case title:
1654
 
                return _iface_currentTitle();
1655
 
            case setTitle:
1656
 
                _iface_setCurrentTitle(params.first().toInt());
1657
 
                break;
1658
 
            case autoplayTitles:
1659
 
                return m_autoplayTitles;
1660
 
            case setAutoplayTitles:
1661
 
                m_autoplayTitles = params.first().toBool();
1662
 
                break;
1663
 
            }
1664
 
            break;
1665
 
                default:
1666
 
            break;
1667
 
        }
1668
 
    }
1669
 
    return QVariant();
1670
 
}
1671
 
#endif
1672
 
 
1673
 
int MediaObject::_iface_availableTitles() const
1674
 
{
1675
 
    return m_availableTitles;
1676
 
}
1677
 
 
1678
 
int MediaObject::_iface_currentTitle() const
1679
 
{
1680
 
    return m_currentTitle;
1681
 
}
1682
 
 
1683
 
void MediaObject::_iface_setCurrentTitle(int title)
1684
 
{
1685
 
    m_backend->logMessage(QString("setCurrentTitle %0").arg(title), Backend::Info, this);
1686
 
    if ((title == m_currentTitle) || (title == m_pendingTitle))
1687
 
        return;
1688
 
 
1689
 
    m_pendingTitle = title;
1690
 
 
1691
 
    if (m_state == Phonon::PlayingState || m_state == Phonon::StoppedState) {
1692
 
        setTrack(m_pendingTitle);
1693
 
    } else {
1694
 
        setState(Phonon::StoppedState);
1695
 
    }
1696
 
}
1697
 
 
1698
 
void MediaObject::setTrack(int title)
1699
 
{
1700
 
    if (((m_state != Phonon::PlayingState) && (m_state != Phonon::StoppedState)) || (title < 1) || (title > m_availableTitles))
1701
 
        return;
1702
 
 
1703
 
 
1704
 
    //let's seek to the beginning of the song
1705
 
    GstFormat trackFormat = gst_format_get_by_nick("track");
1706
 
    m_backend->logMessage(QString("setTrack %0").arg(title), Backend::Info, this);
1707
 
    if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, title - 1)) {
1708
 
        m_currentTitle = title;
1709
 
        updateTotalTime();
1710
 
        m_atEndOfStream = false;
1711
 
        emit titleChanged(title);
1712
 
        emit totalTimeChanged(totalTime());
1713
 
    }
1714
 
}
1715
 
 
1716
 
} // ns Gstreamer
1717
 
} // ns Phonon
1718
 
 
1719
 
QT_END_NAMESPACE
1720
 
 
1721
 
#include "moc_mediaobject.cpp"