88
88
static int count = 0;
89
89
m_name = "MediaObject" + QString::number(count++);
91
if (!m_backend->isValid()) {
92
setError(tr("Cannot start playback. \n\nCheck your GStreamer installation and make sure you "
93
"\nhave libgstreamer-plugins-base installed."), Phonon::FatalError);
96
m_pipeline = new Pipeline(this);
98
GlobalSubtitles::instance()->register_(this);
100
connect(m_pipeline, SIGNAL(aboutToFinish()),
101
this, SLOT(handleAboutToFinish()), Qt::DirectConnection);
102
connect(m_pipeline, SIGNAL(eos()),
103
this, SLOT(handleEndOfStream()));
104
connect(m_pipeline, SIGNAL(warning(QString)),
105
this, SLOT(logWarning(QString)));
106
connect(m_pipeline, SIGNAL(durationChanged(qint64)),
107
this, SLOT(handleDurationChange(qint64)));
108
connect(m_pipeline, SIGNAL(buffering(int)),
109
this, SIGNAL(bufferStatus(int)));
110
connect(m_pipeline, SIGNAL(stateChanged(GstState,GstState)),
111
this, SLOT(handleStateChange(GstState,GstState)));
112
connect(m_pipeline, SIGNAL(errorMessage(QString,Phonon::ErrorType)),
113
this, SLOT(setError(QString,Phonon::ErrorType)));
114
connect(m_pipeline, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)),
115
this, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)));
116
connect(m_pipeline, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)),
117
this, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)));
118
connect(m_pipeline, SIGNAL(videoAvailabilityChanged(bool)),
119
this, SIGNAL(hasVideoChanged(bool)));
120
connect(m_pipeline, SIGNAL(seekableChanged(bool)),
121
this, SIGNAL(seekableChanged(bool)));
122
connect(m_pipeline, SIGNAL(streamChanged()),
123
this, SLOT(handleStreamChange()));
125
connect(m_pipeline, SIGNAL(textTagChanged(int)),
126
this, SLOT(getSubtitleInfo(int)));
127
connect(m_pipeline, SIGNAL(trackCountChanged(int)),
128
this, SLOT(handleTrackCountChange(int)));
130
connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
92
m_pipeline = new Pipeline(this);
94
GlobalSubtitles::instance()->register_(this);
96
connect(m_pipeline, SIGNAL(aboutToFinish()),
97
this, SLOT(handleAboutToFinish()), Qt::DirectConnection);
98
connect(m_pipeline, SIGNAL(eos()),
99
this, SLOT(handleEndOfStream()));
100
connect(m_pipeline, SIGNAL(warning(QString)),
101
this, SLOT(logWarning(QString)));
102
connect(m_pipeline, SIGNAL(durationChanged(qint64)),
103
this, SLOT(handleDurationChange(qint64)));
104
connect(m_pipeline, SIGNAL(buffering(int)),
105
this, SIGNAL(bufferStatus(int)));
106
connect(m_pipeline, SIGNAL(stateChanged(GstState,GstState)),
107
this, SLOT(handleStateChange(GstState,GstState)));
108
connect(m_pipeline, SIGNAL(errorMessage(QString,Phonon::ErrorType)),
109
this, SLOT(setError(QString,Phonon::ErrorType)));
110
connect(m_pipeline, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)),
111
this, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)));
112
connect(m_pipeline, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)),
113
this, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)));
114
connect(m_pipeline, SIGNAL(videoAvailabilityChanged(bool)),
115
this, SIGNAL(hasVideoChanged(bool)));
116
connect(m_pipeline, SIGNAL(seekableChanged(bool)),
117
this, SIGNAL(seekableChanged(bool)));
118
connect(m_pipeline, SIGNAL(streamChanged()),
119
this, SLOT(handleStreamChange()));
121
connect(m_pipeline, SIGNAL(textTagChanged(int)),
122
this, SLOT(getSubtitleInfo(int)));
123
connect(m_pipeline, SIGNAL(trackCountChanged(int)),
124
this, SLOT(handleTrackCountChange(int)));
126
connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
134
129
MediaObject::~MediaObject()
861
850
m_aboutToFinishLock.lock();
862
851
m_handlingAboutToFinish = true;
863
852
emit aboutToFinish();
864
// Three seconds should be more than enough for any application to get their act together.
865
// Any longer than that and they have bigger issues. If Phonon does no supply a next source
866
// within 3 seconds, treat as if there is no next source to come, and finish the current source.
853
// As our signal gets emitted queued we need to wait here until either a
854
// new source or a timeout is reached.
855
// If we got a new source in time -> hooray + gapless
856
// If we did not get a new source in time -> boooh + stop()
867
857
if (!m_skipGapless) {
868
if (m_aboutToFinishWait.wait(&m_aboutToFinishLock, 3000)) {
869
debug() << "Finally got a source";
870
if (m_skipGapless) { // Was explicitly set by stateChange interrupt
871
debug() << "...oh, no, just got aborted, skipping EOS";
872
m_skippingEOS = false;
875
warning() << "aboutToFinishWait timed out!";
876
m_skippingEOS = false;
858
// Dynamic lock timeout is our friend.
859
// Instead of simply waiting for a fixed amount of ms for the next source, we wait for the
860
// most sensible amount of time. This is whatever amount of time is remaining to play
861
// minus a 0.5 seconds safety delta (time values not precise etc.).
862
// A source for which we have no time or for which the remaining time is < 0.5 seconds is
863
// immediately unlocked again. Otherwise the consumer has as much time as gst gave us to
865
// This in particular prevents pointless excessive locking on sources which have a totalTime
866
// < whatever fixed lock value we have (so that we'd lock longer than what we are playing).
867
// An issue apparent with notification-like sounds, that are rather short and do not need
868
// gapless transitioning. As outlined in https://bugs.kde.org/show_bug.cgi?id=307530
869
unsigned long timeout = 0;
870
if (totalTime() <= 0 || (remainingTime() - 500 <= 0))
873
timeout = remainingTime() - 500;
875
debug() << "waiting for" << timeout;
876
if (m_aboutToFinishWait.wait(&m_aboutToFinishLock, timeout)) {
877
debug() << "Finally got a source";
878
if (m_skipGapless) { // Was explicitly set by stateChange interrupt
879
debug() << "...oh, no, just got aborted, skipping EOS";
880
m_skippingEOS = false;
883
warning() << "aboutToFinishWait timed out!";
884
m_skippingEOS = false;
879
debug() << "Skipping gapless audio";
880
m_skippingEOS = false;
887
debug() << "Skipping gapless audio";
888
m_skippingEOS = false;
882
890
m_handlingAboutToFinish = false;
883
891
m_aboutToFinishLock.unlock();