Commit f2000460 authored by Trever Fischer's avatar Trever Fischer
Browse files

Merge branch 'master' into better-pulse-support

parents 06a299a9 ab47dd61
......@@ -10,10 +10,22 @@ include_directories(${PHONON_INCLUDES} ${QT_INCLUDES})
set(PHONON_GST_MAJOR_VERSION "4")
set(PHONON_GST_MINOR_VERSION "6")
set(PHONON_GST_PATCH_VERSION "0")
set(PHONON_GST_PATCH_VERSION "50")
set(PHONON_GST_VERSION "${PHONON_GST_MAJOR_VERSION}.${PHONON_GST_MINOR_VERSION}.${PHONON_GST_PATCH_VERSION}")
add_definitions(-DPHONON_GST_VERSION="${PHONON_GST_VERSION}")
add_subdirectory(gstreamer)
macro_display_feature_log()
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/phonon-backend-gstreamer-${PHONON_GST_VERSION}.tar.xz
COMMAND
git archive --prefix=phonon-backend-gstreamer-${PHONON_GST_VERSION}/ HEAD | xz > ${CMAKE_BINARY_DIR}/phonon-backend-gstreamer-${PHONON_GST_VERSION}.tar.xz
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_custom_target(
dist
DEPENDS ${CMAKE_BINARY_DIR}/phonon-backend-gstreamer-${PHONON_GST_VERSION}.tar.xz
)
......@@ -18,19 +18,18 @@
#ifndef Phonon_GSTREAMER_ABSTRACTRENDERER_H
#define Phonon_GSTREAMER_ABSTRACTRENDERER_H
#include <phonon/videowidget.h>
#include <gst/gstelement.h>
#ifndef QT_NO_PHONON_VIDEO
QT_BEGIN_NAMESPACE
#include <phonon/videowidget.h>
class QString;
namespace Phonon
{
namespace Gstreamer
{
class VideoWidget;
class AbstractRenderer
{
......@@ -55,6 +54,4 @@ protected:
}
} //namespace Phonon::Gstreamer
QT_END_NAMESPACE
#endif //QT_NO_PHONON_VIDEO
#endif // Phonon_GSTREAMER_ABSTRACTRENDERER_H
......@@ -105,6 +105,7 @@ inline void AudioDataOutput::convertAndEmit()
for (int i = 0 ; i < m_channels ; ++i) {
map.insert(static_cast<Phonon::AudioDataOutput::Channel>(i), m_channelBuffers[i]);
Q_ASSERT(i == 0 || m_channelBuffers[i - 1].size() == m_channelBuffers[i].size());
}
emit dataReady(map);
......@@ -146,19 +147,24 @@ void AudioDataOutput::processBuffer(GstElement*, GstBuffer* buffer, GstPad*, gpo
int nBlockToSend = (that->m_pendingData.size() + gstBufferSize) / (dataSize * that->m_channels);
if (nBlockToSend == 0) { // add data to pending buffer
const int prevPendingSize = that->m_pendingData.size();
for (quint32 i = 0; i < gstBufferSize ; ++i) {
#warning should be QBA, so we can work with append(buffer, size)
that->m_pendingData.append(gstBufferData[i]);
return;
}
Q_ASSERT(prevPendingSize + gstBufferSize == that->m_pendingData.size());
return;
}
// SENDING DATA
// 1) I empty the stored data
if (that->m_pendingData.size() != 0) {
// Since pendingData is a concatenation of buffers it must share its
// attribute of being a multiple of channelCount
Q_ASSERT((that->m_pendingData.size() % that->m_channels) == 0);
for (int i = 0; i < that->m_pendingData.size(); i += that->m_channels) {
//TODO: Figure out why it crashes without the second test (bug #279791)
for (int j = 0; (j < that->m_channels) && (i+j < that->m_pendingData.size()); ++j) {
for (int j = 0; j < that->m_channels; ++j) {
that->m_channelBuffers[j].append(that->m_pendingData[i+j]);
}
}
......
......@@ -15,10 +15,11 @@
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "audioeffect.h"
#include "backend.h"
#include "medianode.h"
#include "effectmanager.h"
#include "audioeffect.h"
#include <gst/gst.h>
#ifndef QT_NO_PHONON_EFFECT
......
......@@ -117,6 +117,8 @@ Backend::~Backend()
{
if (GlobalSubtitles::self)
delete GlobalSubtitles::self;
if (GlobalAudioChannels::self)
delete GlobalAudioChannels::self;
delete m_effectManager;
delete m_deviceManager;
PulseSupport::shutdown();
......@@ -309,6 +311,9 @@ QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const
case Phonon::SubtitleType:
list << GlobalSubtitles::instance()->globalIndexes();
break;
case Phonon::AudioChannelType:
list << GlobalAudioChannels::instance()->globalIndexes();
break;
default:
break;
}
......@@ -353,6 +358,14 @@ QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescripti
}
break;
case Phonon::AudioChannelType: {
const AudioChannelDescription description = GlobalAudioChannels::instance()->fromIndex(index);
ret.insert("name", description.name());
ret.insert("description", description.description());
ret.insert("type", description.property("type"));
}
break;
default:
break;
}
......
......@@ -42,8 +42,7 @@ class Backend : public QObject, public BackendInterface
Q_INTERFACES(Phonon::BackendInterface)
public:
Backend(QObject *parent = 0, const QVariantList & = QVariantList());
explicit Backend(QObject *parent = 0, const QVariantList & = QVariantList());
virtual ~Backend();
DeviceManager* deviceManager() const;
......
......@@ -15,9 +15,10 @@
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gst/interfaces/propertyprobe.h>
#include "phonon-config-gstreamer.h"
#include "devicemanager.h"
#include <gst/interfaces/propertyprobe.h>
#include "phonon-config-gstreamer.h" // krazy:exclude=includes
#include "backend.h"
#include "debug.h"
#include "gsthelper.h"
......
......@@ -66,7 +66,7 @@ void Effect::setupEffectParams()
GParamSpec **property_specs;
guint propertyCount, i;
property_specs = g_object_class_list_properties(G_OBJECT_GET_CLASS (m_effectElement), &propertyCount);
for (i = 0; i < propertyCount; i++) {
for (i = 0; i < propertyCount; ++i) {
GParamSpec *param = property_specs[i];
if (param->flags & G_PARAM_WRITABLE) {
QString propertyName = g_param_spec_get_name (param);
......
......@@ -15,8 +15,9 @@
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gst/interfaces/propertyprobe.h>
#include "effectmanager.h"
#include <gst/interfaces/propertyprobe.h>
#include "backend.h"
#include "gsthelper.h"
......
......@@ -15,6 +15,10 @@
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QT_NO_OPENGL
#include "glrenderer.h"
#endif
#include <QtGui/QPainter>
#include <QtGui/QResizeEvent>
......@@ -24,7 +28,6 @@
#include "debug.h"
#include "mediaobject.h"
#include "qwidgetvideosink.h"
#include "glrenderer.h"
#include "qrgb.h"
#include "videowidget.h"
......
......@@ -15,9 +15,10 @@
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gsthelper.h"
#include <gst/interfaces/propertyprobe.h>
#include <gst/gst.h>
#include "gsthelper.h"
#include "mediaobject.h"
#include "backend.h"
......
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=PhononBackend
MimeType=application/x-annodex;video/quicktime;video/x-quicktime;audio/x-m4a;application/x-quicktimeplayer;video/mkv;video/msvideo;video/x-msvideo;video/x-flic;audio/x-aiff;audio/aiff;audio/x-pn-aiff;audio/x-realaudio;audio/basic;audio/x-basic;audio/x-pn-au;audio/x-8svx;audio/8svx;audio/x-16sv;audio/168sv;image/x-ilbm;image/ilbm;video/x-anim;video/anim;image/png;image/x-png;video/mng;video/x-mng;audio/x-ogg;audio/x-speex+ogg;application/ogg;application/ogg;audio/vnd.rn-realaudio;audio/x-pn-realaudio-plugin;audio/x-real-audio;application/vnd.rn-realmedia;video/mpeg;video/x-mpeg;audio/x-wav;audio/wav;audio/x-pn-wav;audio/x-pn-windows-acm;audio/mpeg2;audio/x-mpeg2;audio/mpeg3;audio/x-mpeg3;audio/mpeg;audio/x-mpeg;x-mpegurl;audio/x-mpegurl;audio/mp3;audio/mpeg;audio/x-ape;
MimeType=application/x-annodex;video/quicktime;video/x-quicktime;audio/x-m4a;application/x-quicktimeplayer;video/mkv;video/msvideo;video/x-msvideo;video/x-flic;audio/x-aiff;audio/aiff;audio/x-pn-aiff;audio/x-realaudio;audio/basic;audio/x-basic;audio/x-pn-au;audio/x-8svx;audio/8svx;audio/x-16sv;audio/168sv;image/x-ilbm;image/ilbm;video/x-anim;video/anim;image/png;image/x-png;video/mng;video/x-mng;audio/x-ogg;audio/x-speex+ogg;application/ogg;audio/vnd.rn-realaudio;audio/x-pn-realaudio-plugin;audio/x-real-audio;application/vnd.rn-realmedia;video/mpeg;video/x-mpeg;audio/x-wav;audio/wav;audio/x-pn-wav;audio/x-pn-windows-acm;audio/mpeg2;audio/x-mpeg2;audio/mpeg3;audio/x-mpeg3;audio/mpeg;audio/x-mpeg;audio/x-mpegurl;audio/mp3;audio/x-ape;
X-KDE-Library=phonon_gstreamer
X-KDE-PhononBackendInfo-InterfaceVersion=1
X-KDE-PhononBackendInfo-Version=@PHONON_GST_VERSION@
......@@ -77,7 +77,7 @@ Comment[en_GB]=Phonon GStreamer backend
Comment[es]=Motor GStreamer para Phonon
Comment[et]=Phononi GStreameri taustaprogramm
Comment[eu]=Phonon GStreamer backend
Comment[fi]=Phonon GStreamer-taustaohjelma
Comment[fi]=Phonon GStreamer -taustaohjelma
Comment[fr]=Moteur GStreamer pour Phonon
Comment[ga]=Inneall GStreamer le haghaidh Phonon
Comment[gl]=Infraestrutura de GStreamer para Phonon
......
......@@ -17,13 +17,13 @@
along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mediaobject.h"
#include <cmath>
#include <gst/interfaces/navigation.h>
#include <gst/interfaces/propertyprobe.h>
#include "mediaobject.h"
#include "backend.h"
#include "streamreader.h"
#include "phonon-config-gstreamer.h"
#include "debug.h"
#include "gsthelper.h"
#include "pipeline.h"
......@@ -77,6 +77,9 @@ MediaObject::MediaObject(Backend *backend, QObject *parent)
, m_waitingForNextSource(false)
, m_waitingForPreviousSource(false)
, m_skippingEOS(false)
, m_doingEOS(false)
, m_skipGapless(false)
, m_handlingAboutToFinish(false)
{
qRegisterMetaType<GstCaps*>("GstCaps*");
qRegisterMetaType<State>("State");
......@@ -93,6 +96,7 @@ MediaObject::MediaObject(Backend *backend, QObject *parent)
m_pipeline = new Pipeline(this);
m_isValid = true;
GlobalSubtitles::instance()->register_(this);
GlobalAudioChannels::instance()->register_(this);
connect(m_pipeline, SIGNAL(aboutToFinish()),
this, SLOT(handleAboutToFinish()), Qt::DirectConnection);
......@@ -121,6 +125,8 @@ MediaObject::MediaObject(Backend *backend, QObject *parent)
connect(m_pipeline, SIGNAL(textTagChanged(int)),
this, SLOT(getSubtitleInfo(int)));
connect(m_pipeline, SIGNAL(audioTagChanged(int)),
this, SLOT(getAudioChannelInfo(int)));
connect(m_pipeline, SIGNAL(trackCountChanged(int)),
this, SLOT(handleTrackCountChange(int)));
......@@ -133,9 +139,11 @@ MediaObject::~MediaObject()
if (m_pipeline) {
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline->element()));
g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
gst_object_unref(bus);
delete m_pipeline;
}
GlobalSubtitles::instance()->unregister_(this);
GlobalAudioChannels::instance()->unregister_(this);
}
void MediaObject::saveState()
......@@ -253,6 +261,8 @@ Phonon::ErrorType MediaObject::errorType() const
void MediaObject::setError(const QString &errorString, Phonon::ErrorType error)
{
DEBUG_BLOCK;
debug() << errorString;
m_errorString = errorString;
m_error = error;
// Perform this asynchronously because this is also called from within the pipeline's cb_error
......@@ -298,7 +308,7 @@ void MediaObject::changeSubUri(const Mrl &mrl)
if (customFont.isNull()) {
QFont videoWidgetFont = QApplication::font("VideoWidget");
fontDesc = videoWidgetFont.family() + " " + QString::number(videoWidgetFont.pointSize());
fontDesc = videoWidgetFont.family() + ' ' + QString::number(videoWidgetFont.pointSize());
}
//FIXME: Try to detect common encodings, like libvlc does
g_object_set(G_OBJECT(m_pipeline->element()), "suburi", mrl.toEncoded().constData(),
......@@ -318,10 +328,10 @@ void MediaObject::autoDetectSubtitle()
// Remove the file extension
QString absCompleteBaseName = m_source.fileName();
absCompleteBaseName.replace(QFileInfo(absCompleteBaseName).suffix(), "");
absCompleteBaseName.replace(QFileInfo(absCompleteBaseName).suffix(), QChar());
// Looking for a subtitle in the same directory and matching the same name
foreach(QLatin1String ext, exts) {
foreach(const QLatin1String &ext, exts) {
if (QFile::exists(absCompleteBaseName + ext)) {
changeSubUri(Mrl("file://" + absCompleteBaseName + ext));
break;
......@@ -333,23 +343,27 @@ void MediaObject::autoDetectSubtitle()
void MediaObject::setNextSource(const MediaSource &source)
{
DEBUG_BLOCK;
debug() << "Got next source. Waiting for end of current.";
m_aboutToFinishLock.lock();
// If next source is valid and is not empty (an empty source is sent by Phonon if
// there are no more sources) skip EOS for the current source in order to seamlessly
// pass to the next source.
if (source.type() == Phonon::MediaSource::Invalid ||
source.type() == Phonon::MediaSource::Empty)
m_skippingEOS = false;
else
m_skippingEOS = true;
m_waitingForNextSource = true;
m_waitingForPreviousSource = false;
m_pipeline->setSource(source);
m_aboutToFinishWait.wakeAll();
if (m_handlingAboutToFinish) {
debug() << "Got next source. Waiting for end of current.";
// If next source is valid and is not empty (an empty source is sent by Phonon if
// there are no more sources) skip EOS for the current source in order to seamlessly
// pass to the next source.
if (source.type() == Phonon::MediaSource::Invalid ||
source.type() == Phonon::MediaSource::Empty)
m_skippingEOS = false;
else
m_skippingEOS = true;
m_waitingForNextSource = true;
m_waitingForPreviousSource = false;
m_skipGapless = false;
m_pipeline->setSource(source);
m_aboutToFinishWait.wakeAll();
} else
qDebug() << "Ignoring source as no aboutToFinish handling is in progress.";
m_aboutToFinishLock.unlock();
}
......@@ -382,6 +396,7 @@ void MediaObject::setSource(const MediaSource &source)
m_source = source;
autoDetectSubtitle();
m_pipeline->setSource(source);
m_skipGapless = false;
m_aboutToFinishWait.wakeAll();
//emit currentSourceChanged(source);
}
......@@ -389,9 +404,40 @@ void MediaObject::setSource(const MediaSource &source)
// Called when we are ready to leave the loading state
void MediaObject::loadingComplete()
{
DEBUG_BLOCK;
link();
}
void MediaObject::getAudioChannelInfo(int stream)
{
gint channelCount = 0;
g_object_get(G_OBJECT(m_pipeline->element()), "n-audio", &channelCount, NULL);
if (channelCount)
GlobalAudioChannels::instance()->add(this, -1, tr("Default"), "");
for (gint i = 0; i < channelCount; ++i) {
GstTagList *tags = 0;
g_signal_emit_by_name (G_OBJECT(m_pipeline->element()), "get-audio-tags",
i, &tags);
if (tags) {
gchar *tagLangCode = 0;
gchar *tagCodecName = 0;
gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &tagCodecName);
gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &tagLangCode);
QString name;
if (tagLangCode)
name = QLatin1String(tagLangCode);
else
name = tr("Unknown");
if (tagCodecName)
name = QString("%1 [%2]").arg(name, QLatin1String(tagCodecName));
GlobalAudioChannels::instance()->add(this, i, name);
g_free(tagLangCode);
g_free(tagCodecName);
}
}
emit availableAudioChannelsChanged();
}
void MediaObject::getSubtitleInfo(int stream)
{
gint spuCount = 0; // Sub picture units.
......@@ -412,6 +458,9 @@ void MediaObject::getSubtitleInfo(int stream)
else
name = tr("Unknown");
GlobalSubtitles::instance()->add(this, i, name);
// tagLangCode was implicat converted to QString, so we can drop
// the ref.
g_free(tagLangCode);
}
}
emit availableSubtitlesChanged();
......@@ -426,6 +475,7 @@ void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
void MediaObject::pause()
{
DEBUG_BLOCK;
requestState(Phonon::PausedState);
}
......@@ -542,19 +592,28 @@ void MediaObject::handleStateChange(GstState oldState, GstState newState)
if (newState == GST_STATE_READY)
emit tick(0);
emit stateChanged(m_state, prevPhononState);
if (!m_doingEOS)
emit stateChanged(m_state, prevPhononState);
}
void MediaObject::handleEndOfStream()
{
DEBUG_BLOCK;
if (!m_skippingEOS) {
emit finished();
m_aboutToFinishWait.wakeAll();
m_pipeline->setState(GST_STATE_READY);
debug() << "not skipping EOS";
m_doingEOS = true;
{ // When working on EOS we do not want signals emitted to avoid bogus UI updates.
emit stateChanged(Phonon::StoppedState, m_state);
m_aboutToFinishWait.wakeAll();
m_aboutToFinishLock.unlock();
m_pipeline->setState(GST_STATE_READY);
emit finished();
}
m_doingEOS = false;
} else {
debug() << "skipping EOS";
GstState state = m_pipeline->state();
m_pipeline->setState(GST_STATE_READY);
m_pipeline->state();
m_pipeline->setState(state);
m_skippingEOS = false;
}
......@@ -565,7 +624,7 @@ void MediaObject::handleEndOfStream()
bool MediaObject::hasInterface(Interface iface) const
{
return iface == AddonInterface::TitleInterface || iface == AddonInterface::NavigationInterface
|| iface == AddonInterface::SubtitleInterface;
|| iface == AddonInterface::SubtitleInterface || iface == AddonInterface::AudioChannelInterface;
}
QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> &params)
......@@ -621,12 +680,47 @@ QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QV
break;
}
break;
case AudioChannelInterface:
switch(command)
{
case availableAudioChannels:
return QVariant::fromValue(_iface_availableAudioChannels());
break;
case currentAudioChannel:
return QVariant::fromValue(_iface_currentAudioChannel());
break;
case setCurrentAudioChannel:
if (params.isEmpty() || !params.first().canConvert<AudioChannelDescription>()) {
error() << Q_FUNC_INFO << "arguments invalid";
return QVariant();
}
_iface_setCurrentAudioChannel(params.first().value<AudioChannelDescription>());
break;
}
break;
}
}
return QVariant();
}
#endif
QList<AudioChannelDescription> MediaObject::_iface_availableAudioChannels() const
{
return GlobalAudioChannels::instance()->listFor(this);
}
AudioChannelDescription MediaObject::_iface_currentAudioChannel() const
{
return m_currentAudioChannel;
}
void MediaObject::_iface_setCurrentAudioChannel(const AudioChannelDescription &channel)
{
const int localIndex = GlobalAudioChannels::instance()->localIdFor(this, channel.index());
g_object_set(G_OBJECT(m_pipeline->element()), "current-audio", localIndex, NULL);
m_currentAudioChannel = channel;
}
QList<MediaController::NavigationMenu> MediaObject::_iface_availableMenus() const
{
return m_pipeline->availableMenus();
......@@ -722,8 +816,8 @@ void MediaObject::_iface_setCurrentSubtitle(const SubtitleDescription &subtitle)
if (subtitle.property("type").toString() == "file") {
QString filename = subtitle.name();
if (!filename.startsWith("file://"))
filename.prepend("file://");
if (!filename.startsWith(QLatin1String("file://")))
filename.prepend(QLatin1String("file://"));
// It's not possible to change the suburi when the pipeline is PLAYING mainly
// because the pipeline has not been built with the subtitle element. A workaround
// consists to restart the pipeline and set the suburi property (totem does exactly the same thing)
......@@ -796,6 +890,19 @@ void MediaObject::setMetaData(QMultiMap<QString, QString> newData)
void MediaObject::requestState(Phonon::State state)
{
DEBUG_BLOCK;
// Only abort handling here iff the handler is active.
if (m_aboutToFinishLock.tryLock()) {
// Note that this is not condition to unlocking, so the nesting is
// necessary.
if (m_handlingAboutToFinish) {
qDebug() << "Aborting aboutToFinish handling.";
m_skipGapless = true;
m_aboutToFinishWait.wakeAll();
}
m_aboutToFinishLock.unlock();
}
debug() << state;
switch (state) {
case Phonon::PlayingState:
m_pipeline->setState(GST_STATE_PLAYING);
......@@ -818,16 +925,31 @@ void MediaObject::requestState(Phonon::State state)
void MediaObject::handleAboutToFinish()
{
DEBUG_BLOCK;
debug() << "About to finish";
m_aboutToFinishLock.lock();
emit aboutToFinish();
m_handlingAboutToFinish = true;
if (!m_waitingForNextSource)
emit aboutToFinish();
// Three seconds should be more than enough for any application to get their act together.
// Any longer than that and they have bigger issues. If Phonon does no supply a next source
// within 3 seconds, treat as if there is no next source to come, and finish the current source.
if (m_aboutToFinishWait.wait(&m_aboutToFinishLock, 3000))
debug() << "Finally got a source";
else
m_skippingEOS = false;
if (!m_skipGapless) {
if (m_aboutToFinishWait.wait(&m_aboutToFinishLock, 3000)) {
debug() << "Finally got a source";
if (m_skipGapless) { // Was explicitly set by stateChange interrupt
debug() << "...oh, no, just got aborted, skipping EOS";
m_skippingEOS = false;
}
} else {
warning() << "aboutToFinishWait timed out!";
m_skippingEOS = false;
}
} else {
debug() << "Skipping gapless audio";
m_skippingEOS = false;
}
m_handlingAboutToFinish = false;
m_aboutToFinishLock.unlock();
}
......
......@@ -33,7 +33,7 @@
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>
#include "phonon-config-gstreamer.h"
#include "phonon-config-gstreamer.h" // krazy:exclude=includes
QT_BEGIN_NAMESPACE
......@@ -194,6 +194,7 @@ protected:
private Q_SLOTS:
void handleTrackCountChange(int tracks);
void getSubtitleInfo(int stream);
void getAudioChannelInfo(int stream);
void emitTick();
void beginPlay();
void autoDetectSubtitle();
......@@ -223,6 +224,10 @@ private:
void changeTitle(const QString &format, int title);
void changeSubUri(const Mrl &mrl);
QList<AudioChannelDescription> _iface_availableAudioChannels() const;
AudioChannelDescription _iface_currentAudioChannel() const;
void _iface_setCurrentAudioChannel(const AudioChannelDescription &channel);
bool m_resumeState;
State m_oldState;
quint64 m_oldPos;
......@@ -253,9 +258,10 @@ private:
int m_availableTitles;
int m_currentTitle;
SubtitleDescription m_currentSubtitle;
AudioChannelDescription m_currentAudioChannel;
int m_pendingTitle;
// When we emit aboutToFinish(), libphonon calls setNextSource. To achive gapless playback,
// When we emit aboutToFinish(), libphonon calls setNextSource. To achieve gapless playback,
// the pipeline is immediately told to start using that new source. This can break seeking
// since the aboutToFinish signal tends to be emitted around 15 seconds or so prior to actually
// ending the current track.
......@@ -269,6 +275,7 @@ private:
bool m_waitingForPreviousSource;
bool m_skippingEOS;
bool m_doingEOS; // To prevent superfluously signal emission.