--- /dev/null
+/*
+ * This file is part of QMAFW
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights
+ * reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This software, including documentation, is protected by copyright controlled
+ * by Nokia Corporation. All rights are reserved. Copying, including
+ * reproducing, storing, adapting or translating, any or all of this material
+ * requires the prior written consent of Nokia Corporation. This material also
+ * contains confidential information which may not be disclosed to others
+ * without the prior written consent of Nokia.
+ *
+ */
+
+#include <QDebug>
+#include <QMetaMethod>
+#include <QTimer>
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <X11/Xdefs.h>
+
+#include <MafwCallbackHelper.h>
+#include <MafwMetadata.h>
+#include <MafwMediaInfo.h>
+
+#include "MafwGstRenderer.h"
+#include "MafwBlankingPreventer.h"
+#include "mafw-gst-renderer-worker.h"
+#include "MafwGstRendererVolume.h"
+#include "MafwGstRendererDolby.h"
+#include "MafwGstRendererNetworkMonitor.h"
+#include "MafwGstRendererHaltState.h"
+#include "MafwGstRendererPlaylistFileUtility.h"
+#include "MafwGstScreenshot.h"
+#include "MafwMmcMonitor.h"
+
+#include <QSettings>
+#include <contextsubscriber/contextproperty.h>
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QDateTime>
+#include <QSparqlConnection>
+#include <QSparqlResult>
+#include <QSparqlQuery>
+#include <QSparqlError>
+
+// milliseconds, stamp after playing this much
+const int PLAYED_STAMP_INTERVAL = 5000;
+
+const int MAX_SUPPORTED_HEIGHT = 720;
+const int MAX_SUPPORTED_WIDTH = 1280;
+// how many time we retried to make played stamps
+const int PLAYED_STAMP_TRIES = 3;
+
+static const int ISO_DATE_BASE_LENGTH = 19; // length of "CCYY-MM-DDThh:mm:ss"
+
+// property names
+const QString PROPERTY_DOLBY_STATE_MUSIC = "mobile-surround-state-music";
+const QString PROPERTY_DOLBY_STATE_MUSIC_ROOM = "mobile-surround-state-music-room";
+const QString PROPERTY_DOLBY_STATE_MUSIC_COLOR = "mobile-surround-state-music-color";
+const QString PROPERTY_DOLBY_STATE_VIDEO = "mobile-surround-state-video";
+const QString PROPERTY_DOLBY_STATE_VIDEO_ROOM = "mobile-surround-state-video-room";
+const QString PROPERTY_DOLBY_STATE_VIDEO_COLOR = "mobile-surround-state-video-color";
+const QString PROPERTY_VOLUME = "volume";
+const QString PROPERTY_AUTOPAINT = "autopaint";
+const QString PROPERTY_COLORKEY = "colorkey";
+const QString PROPERTY_XID = "xid";
+const QString PROPERTY_RENDER_RECT = "render-rectangle";
+const QString PROPERTY_CURRENT_FRAME_ON_PAUSE = "current-frame-on-pause";
+const QString PROPERTY_PLAYBACK_SPEED = "playback-speed";
+const QString PROPERTY_FORCE_ASPECT_RATIO = "force-aspect-ratio";
+
+// audio/video destinations
+const QString CONTEXT_FW_PROPERTY_AUDIO_ROUTE = "/com/nokia/policy/audio_route";
+const QString CONTEXT_FW_PROPERTY_VIDEO_ROUTE = "/com/nokia/policy/video_route";
+const QString AUDIO_ROUTE_NULL = "null";
+const QString AUDIO_ROUTE_IHF = "ihf";
+const QString AUDIO_ROUTE_FMRADIO = "fmtx";
+const QString AUDIO_ROUTE_IHF_AND_FMRADIO = "ihfandfmtx";
+const QString AUDIO_ROUTE_EARPIECE = "earpiece";
+const QString AUDIO_ROUTE_EARPIECE_AND_TVOUT = "earpieceandtvout";
+const QString AUDIO_ROUTE_TV_OUT = "tvout";
+const QString AUDIO_ROUTE_IHF_AND_TV_OUT = "ihfandtvout";
+const QString AUDIO_ROUTE_HEADPHONE = "headphone";
+const QString AUDIO_ROUTE_HEADSET = "headset";
+const QString AUDIO_ROUTE_BTHSP = "bthsp";
+const QString AUDIO_ROUTE_BTA2DP = "bta2dp";
+const QString AUDIO_ROUTE_IHF_AND_HEADSET = "ihfandheadset";
+const QString AUDIO_ROUTE_IHF_AND_HEADPHONE = "ihfandheadphone";
+const QString AUDIO_ROUTE_IHF_AND_BTHSP = "ihfandbthsp";
+const QString AUDIO_ROUTE_TV_OUT_AND_BTHSP = "tvoutandbthsp";
+const QString AUDIO_ROUTE_TV_OUT_AND_BTA2DP = "tvoutandbta2dp";
+const QString VIDEO_ROUTE_TV_OUT = "tvout";
+const QString VIDEO_ROUTE_BUILT_IN = "builtin";
+const QString VIDEO_ROUTE_BUILT_IN_AND_TV_OUT = "builtinandtvout";
+
+const QString DBUS_INTERFACE_DBUS="org.freedesktop.DBus";
+const QString DBUS_SIGNAL_NAME_OWNER_CHANGED="NameOwnerChanged";
+const QString DBUS_NAME_PCFD = "com.nokia.policy.pcfd";
+
+/********************************************************************
+ * MafwGstRenderer::MafwGstRenderer
+ ********************************************************************/
+MafwGstRenderer::MafwGstRenderer(const QString& uuid,
+ const QString& pluginName,
+ const QString& name,
+ QObject *parent)
+ :
+ MafwBasicRenderer(uuid, pluginName, name, parent),
+ m_initialized(false),
+ m_currentState(MafwRenderer::Invalid),
+ m_nextContent(""),
+ m_currentContent(""),
+ m_playingItem(MafwBasicRenderer::UnknownUri),
+ m_blankingPreventer(0),
+ m_screenshot(0),
+ m_networkMonitor(new MafwGstRendererNetworkMonitor()),
+ m_volume(0),
+ m_playedStamped(false),
+ m_playedStampTryCounter(0),
+ m_sparqlConnection(new QSparqlConnection("QTRACKER", QSparqlConnectionOptions(), this)),
+ m_urnQueryResult(0),
+ m_stampItResult(0),
+ m_playlistFileUtil(0),
+ m_playingPlaylistFile(false),
+ m_unsupportedTypeError(0),
+ m_playedPlaylistItem(false),
+ m_mmcMonitor(0)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ m_dolby = new MafwGstRendererDolby(this);
+ connect(m_dolby, SIGNAL(mafwDHMMusicPropertyChanged()),
+ this, SLOT(handleDHMMusicPropertyChanged()));
+ connect(m_dolby, SIGNAL(mafwDHMVideoPropertyChanged()),
+ this, SLOT(handleDHMVideoPropertyChanged()));
+
+ m_worker = 0;
+ m_videoRoute = 0;
+ m_audioRoute = 0;
+ gst_init(0, 0);
+
+ /* make this connection a queued connection to postpone results delivery,
+ * so that results callback is not called already in the function that
+ * initiates this procedure */
+ QObject::connect(this, SIGNAL(signalGetPosition(QObject*,
+ const char*)),
+ this, SLOT(slotGetPosition(QObject*,
+ const char*)),
+ Qt::QueuedConnection);
+
+ /* make this connection a queued connection to postpone results delivery,
+ * so that results callback is not called already in the function that
+ * initiates this procedure */
+ QObject::connect(this, SIGNAL(signalMafwProperty(QString,
+ QObject*,
+ const char*)),
+ this, SLOT(slotMafwProperty(QString,
+ QObject*,
+ const char*)),
+ Qt::QueuedConnection);
+
+ /* make this connection a queued connection to postpone results delivery,
+ * so that results callback is not called already in the function that
+ * initiates this procedure */
+ QObject::connect(this, SIGNAL(signalGetCurrentMediaInfo(QObject*,
+ const char*,
+ const QString)),
+ this, SLOT(slotGetCurrentMediaInfo(QObject*,
+ const char*,
+ const QString)),
+ Qt::QueuedConnection);
+
+ m_playedStampTimer.setSingleShot(true);
+ connect(&m_playedStampTimer,SIGNAL(timeout()),this,SLOT(slotStamp()));
+
+ m_videoRoute = new ContextProperty(CONTEXT_FW_PROPERTY_VIDEO_ROUTE);
+ m_audioRoute = new ContextProperty(CONTEXT_FW_PROPERTY_AUDIO_ROUTE);
+
+ connectNameOwnerChanged();
+
+ m_playlistNextTimer.setSingleShot(true);
+ connect(&m_playlistNextTimer, SIGNAL(timeout()),
+ this, SLOT(playNextURIFromPlaylist()));
+
+ /* connection is to track when policy is on/off */
+ connect(this, SIGNAL(mafwPropertyChanged(const QString, const QVariant)),
+ this, SLOT(handlePropertyChanged(const QString&, const QVariant&)));
+
+ /* Connection to handle online status message if necessary */
+ connect(m_networkMonitor, SIGNAL(prepareNetworkChange()),
+ this, SLOT(haltStreaming()));
+ connect(m_networkMonitor, SIGNAL(networkChangeFinished()),
+ this, SLOT(continueStreaming()));
+
+ connect(&m_haltState, SIGNAL(decayed()),
+ this, SLOT(stopStreaming()));
+}
+
+/********************************************************************
+ * MafwGstRenderer::~MafwGstRenderer
+ ********************************************************************/
+MafwGstRenderer::~MafwGstRenderer()
+{
+
+ qDebug() << __PRETTY_FUNCTION__;
+ delete m_volume;
+ // this releases the resources allocated by the worker, do this before
+ // releasing anything else so any callbacks from worker won't be called
+ mafw_gst_renderer_worker_exit(m_worker);
+
+ delete m_videoRoute;
+ delete m_audioRoute;
+ delete m_networkMonitor;
+ delete m_screenshot;
+ delete m_urnQueryResult;
+ delete m_stampItResult;
+ delete m_sparqlConnection;
+
+ g_free(m_worker);
+
+ if( m_unsupportedTypeError )
+ {
+ g_error_free(m_unsupportedTypeError);
+ }
+}
+
+
+/********************************************************************
+ * MafwGstRenderer::initialize
+ ********************************************************************/
+bool MafwGstRenderer::initialize(QSettings *settings)
+{
+
+ qDebug() << __PRETTY_FUNCTION__;
+
+ //if already initialized do nothing
+ if (m_initialized)
+ {
+ return m_initialized;
+ }
+
+ m_initialized = MafwBasicRenderer::initialize();
+
+ if (m_initialized)
+ {
+ // fail to apply default policy is not considered fatal for now
+ if (MafwBasicRenderer::setDefaultRendererPolicy(MafwRendererPolicy::MediaPlayer))
+ {
+ MafwRendererPolicy *policy = rendererPolicy();
+ Q_ASSERT(policy);
+ policy->setDefaultResources(MafwRendererPolicy::Audio);
+ }
+ else
+ {
+ qWarning() << "Setting default policy failed, continuing";
+ }
+
+ m_blankingPreventer = new MafwBlankingPreventer(this);
+
+ m_screenshot = new MafwGstScreenshot(this);
+
+ connect(m_screenshot, SIGNAL(screenshotTaken(char*,GError*)),
+ this, SLOT(handleScreenshot(char*,GError*)));
+ connect(m_screenshot, SIGNAL(screenshotCancelled()),
+ this, SLOT(cancelScreenshot()));
+
+ m_worker = mafw_gst_renderer_worker_new(this);
+ m_worker->notify_play_handler = &playCallback;
+ m_worker->notify_pause_handler = &pauseCallback;
+ m_worker->notify_error_handler = &errorCallback;
+ m_worker->notify_eos_handler = &eosCallback;
+ m_worker->notify_ready_state_handler = &readyStateCallback;
+ m_worker->notify_metadata_handler = &metadataCallback;
+ m_worker->notify_property_handler = &propertyCallback;
+ m_worker->notify_buffer_status_handler = &bufferStatusCallback;
+ m_worker->blanking__control_handler = &blankingControlCallback;
+ m_worker->screenshot_handler = &screenshotCallback;
+
+ setConfiguration(settings);
+
+ // Initialize Dolby support
+ m_dolby->initialize();
+
+ m_mmcMonitor = new MafwMmcMonitor(this);
+ connect( m_mmcMonitor, SIGNAL( preUnmount() ), this, SLOT( mmcPreUnmount() ) );
+
+ // connect the audio routes AND check the current values of routes.
+ connect( m_videoRoute, SIGNAL( valueChanged() ), this, SLOT( slotRouteChanged() ) );
+ connect( m_audioRoute, SIGNAL( valueChanged() ), this, SLOT( slotRouteChanged() ) );
+ slotRouteChanged();
+ }
+ return m_initialized;
+}
+
+/********************************************************************
+ * MafwGstRenderer::playCallback
+ ********************************************************************/
+void MafwGstRenderer::playCallback(MafwGstRendererWorker *worker,
+ gpointer owner)
+{
+ Q_UNUSED(worker);
+
+ qDebug() << __PRETTY_FUNCTION__;
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ if( self->m_currentState == MafwRenderer::Paused )
+ {
+ Q_EMIT self->rendererResumed();
+ }
+ else
+ {
+ if( self->m_playingPlaylistFile )
+ {
+ if( !self->m_playedPlaylistItem )
+ {
+ qDebug() << "Emitting playing item event";
+ Q_EMIT self->rendererPlaying(static_cast<int>(self->m_playingItem));
+ self->m_playedPlaylistItem = true;
+ }
+ }
+ else
+ {
+ Q_EMIT self->rendererPlaying(static_cast<int>(self->m_playingItem));
+ }
+ }
+
+ if( mafw_gst_renderer_worker_get_position(worker)==0 )
+ {
+ self->m_playedStamped = false;
+ self->m_playedStampTryCounter = 0;
+ }
+
+ if( !self->m_playedStamped )
+ {
+ const QUrl url = self->m_currentContent.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+ if (url.scheme() == "file")
+ {
+ qDebug() << __PRETTY_FUNCTION__ << "starting play stamp timer.";
+ self->m_playedStampTimer.start(PLAYED_STAMP_INTERVAL);
+ }
+ }
+
+ self->m_currentState = MafwRenderer::Playing;
+}
+
+/********************************************************************
+ * MafwGstRenderer::bufferStatusCallback
+ ********************************************************************/
+void MafwGstRenderer::bufferStatusCallback(MafwGstRendererWorker *worker,
+ gpointer owner,
+ gdouble percent)
+{
+
+ Q_UNUSED(worker);
+
+ qDebug() << __PRETTY_FUNCTION__;
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ Q_EMIT self->bufferingInfo(static_cast<float>(percent));
+
+}
+/********************************************************************
+ * MafwGstRenderer::constructMafwError
+ ********************************************************************/
+MafwError MafwGstRenderer::constructMafwError(const GError* error)
+{
+ MafwError mafwError;
+ guint32 code = static_cast<guint32>(error->code);
+
+ //for streams the media not found is other than the default
+ if( code == WORKER_ERROR_MEDIA_NOT_FOUND && mafw_gst_renderer_worker_get_streaming(m_worker) )
+ {
+ mafwError.setCode(MafwError::RendererError_URINotAvailable);
+ }
+ else if(code == WORKER_ERROR_UNSUPPORTED_TYPE)
+ {
+ handleResolutionError(mafwError);
+ }
+ else if (errorMap().contains(code))
+ {
+ mafwError.setCode(errorMap().value(code));
+ }
+ else
+ {
+ mafwError.setCode(MafwError::NothingButErrors);
+ }
+
+ mafwError.setMessage(error->message);
+ return mafwError;
+}
+
+/********************************************************************
+ * MafwGstRenderer::errorCallback
+ ********************************************************************/
+void MafwGstRenderer::errorCallback(MafwGstRendererWorker *worker,
+ gpointer owner,
+ const GError *error)
+{
+ Q_UNUSED(worker);
+ qWarning() << __PRETTY_FUNCTION__ << error->message;
+ MafwError mafwError;
+ guint32 code;
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ code = static_cast<guint32>(error->code);
+
+ //The content might be a playlist file which was tried to play without
+ //mime type. This case can be detected by trying to play it as playlist
+ //file. If that was not the case same error will be signalled via
+ //MafwGstRenderer::handlePlaylistFileParsingErrors
+ if (!self->m_playingPlaylistFile &&
+ !self->m_unsupportedTypeError &&
+ code == WORKER_ERROR_POSSIBLY_PLAYLIST_TYPE)
+ {
+ QMap<QString, QVariant> mime;
+ mime[MAFW_METADATA_KEY_MIME] = "audio/x-scpls";
+ self->m_currentContent.setMetaData(mime);
+ self->doPlay(self->m_currentContent);
+ self->m_unsupportedTypeError = g_error_copy(error);
+ qWarning() << __PRETTY_FUNCTION__ << "Probably we were trying to play playlist file without mime type. If that's the case use bool play(url, 'audio/x-scpls').";
+ qWarning() << __PRETTY_FUNCTION__ << "Trying to play as playlist file now...";
+ return;
+ }
+ mafwError = self->constructMafwError(error);
+
+ /* We release resources when we got error that causes stop.
+ * WORKER_ERROR_CANNOT_SET_POSITION and WORKER_ERROR_DRM_NOT_ALLOWED error don't cause stop.
+ */
+ if((code != WORKER_ERROR_CANNOT_SET_POSITION
+ && code != WORKER_ERROR_DRM_NOT_ALLOWED)
+ && !self->m_playingPlaylistFile)
+ {
+ Q_EMIT self->rendererError(mafwError);
+ MafwRendererPolicy *policy = self->rendererPolicy();
+ Q_ASSERT(policy);
+ if( policy )
+ {
+ policy->release();
+ qDebug() << __PRETTY_FUNCTION__ << "Resources released because of error" << mafwError.code();
+ }
+ else
+ {
+ qWarning() << __PRETTY_FUNCTION__ << "No policy exists!";
+ }
+
+ self->doStop();
+ }
+ else if (code != WORKER_ERROR_CANNOT_SET_POSITION && code != WORKER_ERROR_DRM_NOT_ALLOWED) //Try next uri
+ {
+ //using singleshot gives worker/gstreamer time to do
+ //cleanup before calling worker_play
+ if (self->m_playlistFileUtil->getUriList().isEmpty())
+ {
+ //delayed call to playNextURIFromPlaylist used to give the parser
+ //enough time to read new items from the playlist
+ self->m_playlistFileUtil->setPendingError(mafwError);
+
+ self->m_playlistNextTimer.start(1000);
+ }
+ else
+ {
+ self->m_playlistNextTimer.start(0);
+ }
+ }
+ else
+ {
+ Q_EMIT self->rendererError(mafwError);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::propertyCallback
+ ********************************************************************/
+void MafwGstRenderer::propertyCallback(MafwGstRendererWorker *worker,
+ gpointer owner,
+ gint id,
+ GValue *value)
+{
+
+ QString name;
+
+ Q_UNUSED(worker);
+
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ switch (id)
+ {
+ case WORKER_PROPERTY_AUTOPAINT:
+ name = PROPERTY_AUTOPAINT;
+ break;
+ case WORKER_PROPERTY_COLORKEY:
+ name = PROPERTY_COLORKEY;
+ break;
+ case WORKER_PROPERTY_XID:
+ name = PROPERTY_XID;
+ break;
+ case WORKER_PROPERTY_CURRENT_FRAME_ON_PAUSE:
+ name = PROPERTY_CURRENT_FRAME_ON_PAUSE;
+ break;
+ case WORKER_PROPERTY_PLAYBACK_SPEED:
+ name = PROPERTY_PLAYBACK_SPEED;
+ break;
+ case WORKER_PROPERTY_FORCE_ASPECT_RATIO:
+ name = PROPERTY_FORCE_ASPECT_RATIO;
+ break;
+ case WORKER_PROPERTY_RENDER_RECTANGLE:
+ name = PROPERTY_RENDER_RECT;
+ break;
+ default:
+ qWarning() << __PRETTY_FUNCTION__ << "unknown property id:" << id;
+ return;
+ break;
+ }
+
+ qDebug() << __PRETTY_FUNCTION__ << name;
+
+ QVariant result = getValue(value);
+
+ if (result.isValid())
+ {
+ Q_EMIT self->mafwPropertyChanged(name, result);
+ }
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::blankingControlCallback
+ ********************************************************************/
+void MafwGstRenderer::blankingControlCallback(MafwGstRendererWorker *worker,
+ gpointer owner, gboolean prohibit)
+{
+
+ Q_UNUSED(worker);
+ qDebug() << __PRETTY_FUNCTION__ << prohibit;
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+ if(self->m_videoRoute->value() == VIDEO_ROUTE_TV_OUT ||
+ self->m_videoRoute->value() == VIDEO_ROUTE_BUILT_IN_AND_TV_OUT)
+ {
+ prohibit = false;
+ }
+
+ if( prohibit )
+ {
+ self->m_blankingPreventer->blankingProhibit();
+ }
+ else
+ {
+ self->m_blankingPreventer->blankingAllow();
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::screenshotCallback
+ ********************************************************************/
+void MafwGstRenderer::screenshotCallback(MafwGstRendererWorker *worker,
+ gpointer owner, GstBuffer *buffer,
+ const char *filename, gboolean cancel)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ MafwGstRenderer *self = static_cast<MafwGstRenderer*>(owner);
+
+ if(cancel)
+ {
+ self->m_screenshot->cancelPauseFrame();
+ }
+ else
+ {
+ if(!self->m_screenshot->savePauseFrame(buffer, filename))
+ {
+ worker->taking_screenshot = FALSE;
+ qCritical() << "Failed to create pause frame pipeline";
+ }
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::getValue
+ ********************************************************************/
+QVariant MafwGstRenderer::getValue(const GValue *v)
+{
+
+ QVariant result;
+
+ if (G_IS_VALUE(v))
+ {
+ if (G_VALUE_TYPE(v) == G_TYPE_STRING)
+ {
+ // tags from GStreamer are always expected to be UTF-8
+ result = QVariant(QString::fromUtf8(g_value_get_string(v)));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_UINT)
+ {
+ result = QVariant(g_value_get_uint(v));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_INT)
+ {
+ result = QVariant(g_value_get_int(v));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_BOOLEAN)
+ {
+ result = QVariant::fromValue<bool>(g_value_get_boolean(v));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_DOUBLE)
+ {
+ result = QVariant(g_value_get_double(v));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_INT64)
+ {
+ result = QVariant(g_value_get_int64(v));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_FLOAT)
+ {
+ result = QVariant(g_value_get_float(v));
+ }
+ else if (G_VALUE_TYPE(v) == G_TYPE_VALUE_ARRAY)
+ {
+ GValueArray *vals = static_cast<GValueArray*>(g_value_get_boxed(v));
+ if( vals->n_values == 4 )
+ {
+ result = QString("%1,%2,%3,%4")
+ .arg(g_value_get_int(g_value_array_get_nth(vals, 0)))
+ .arg(g_value_get_int(g_value_array_get_nth(vals, 1)))
+ .arg(g_value_get_int(g_value_array_get_nth(vals, 2)))
+ .arg(g_value_get_int(g_value_array_get_nth(vals, 3)));
+ }
+ else
+ {
+ qWarning() << "Invalid rect values received? Size:" << vals->n_values;
+ }
+
+ }
+ else
+ {
+ qWarning() << "unsupported value g_type";
+ }
+ }
+
+ return result;
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::metadataCallback
+ ********************************************************************/
+void MafwGstRenderer::metadataCallback(MafwGstRendererWorker *worker,
+ gpointer owner,
+ gint key,
+ GType type,
+ gpointer value)
+{
+
+ QList<QVariant> results;
+
+ Q_UNUSED(worker);
+
+ qDebug() << __PRETTY_FUNCTION__ << key << metadataMap().value(key);
+
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ if (metadataMap().contains(key))
+ {
+ if (type == G_TYPE_VALUE_ARRAY)
+ {
+ uint i;
+ GValueArray *vals = static_cast<GValueArray*>(value);
+ for (i = 0; i < vals->n_values; i++)
+ {
+ QVariant v = getValue(g_value_array_get_nth(vals, i));
+ if (v.isValid())
+ {
+ results << v;
+ }
+ }
+
+ QString mafwMetadataKey = metadataMap().value(key);
+
+ self->appendRelatedMetadata(mafwMetadataKey, &results);
+
+ Q_EMIT self->metadataChanged(mafwMetadataKey, results);
+ self->m_currentMetaData.insert(mafwMetadataKey, results);
+ }
+ else
+ {
+ qWarning() << "unsupported g_type";
+ }
+ }
+ else
+ {
+ qWarning() << "unknown metadata key:" << key;
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::appendRelatedMetadata
+ ********************************************************************/
+void MafwGstRenderer::appendRelatedMetadata(const QString key, QList<QVariant>* results)
+{
+ if(key == MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI)
+ {
+ gint position = mafw_gst_renderer_worker_get_position(m_worker);
+ if(position < 0)
+ {
+ position = 0;
+ }
+
+ QUrl uri = m_currentContent.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+ *results << uri.toEncoded().constData();
+ *results << QVariant(position);
+ }
+}
+
+//
+// QMafw renderer interface implementation
+//
+
+/********************************************************************
+ * MafwGstRenderer::doPlay
+ ********************************************************************/
+void MafwGstRenderer::doPlay(const MafwContent& content)
+{
+ Q_ASSERT_X(false, "MafwGstRenderer", "Wrong play function called!");
+ Q_UNUSED(content);
+}
+
+/********************************************************************
+ * MafwGstRenderer::doPlay
+ ********************************************************************/
+void MafwGstRenderer::doPlay(const MafwMediaInfo& mediaInfo)
+{
+ //Preserve m_currentContent for keeping usage count up if the same item is
+ //played again.
+ if(mediaInfo.uuid().isEmpty() ||
+ mediaInfo.uuid() != m_currentContent.uuid())
+ {
+ m_currentContent = mediaInfo;
+ }
+ m_playingItem = MafwBasicRenderer::CurrentUri;
+ m_currentMetaData.clear();
+
+ const QUrl url = mediaInfo.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+ qDebug() << __PRETTY_FUNCTION__ << url.toEncoded();
+
+ m_haltState.clear();
+
+ if( !m_mmcMonitor->isMounted() && url.toString().startsWith( MafwMmcMonitor::MMC_URI_PREFIX ) )
+ {
+ qDebug() << "MafwGstRenderer::doPlay: Can't play MMC not mounted";
+ MafwError mafwError(MafwError::RendererError_MmcNotAvailable, url.toEncoded());
+ Q_EMIT rendererError(mafwError);
+ return;
+ }
+
+ m_playedPlaylistItem = false;
+ m_playingPlaylistFile = false;
+ if (m_unsupportedTypeError)
+ {
+ g_error_free(m_unsupportedTypeError);
+ m_unsupportedTypeError = 0;
+ }
+
+ if( url.isValid() )
+ {
+ stopTimers();
+
+ // Set correct value for the Dolby Headphones Mobile effect plugin
+ set_dolby_music_property(m_worker, m_dolby->getMusicDolbyState());
+ set_dolby_music_sound_property(m_worker, m_dolby->getMusicDolbyRoom(), TRUE);
+ set_dolby_music_sound_property(m_worker, m_dolby->getMusicDolbyColor(), FALSE);
+ set_dolby_video_property(m_worker, m_dolby->getVideoDolbyState());
+ set_dolby_video_sound_property(m_worker, m_dolby->getVideoDolbyRoom(), TRUE);
+ set_dolby_video_sound_property(m_worker, m_dolby->getVideoDolbyColor(), FALSE);
+
+ const QString& mimeType = mediaInfo.firstMetaData(MAFW_METADATA_KEY_MIME).toString();
+ if (mimeType == "audio/x-scpls" )
+ {
+ if (!m_playlistFileUtil)
+ {
+ m_playlistFileUtil = new MafwGstRendererPlaylistFileUtility(this);
+ connect(m_playlistFileUtil, SIGNAL(firstItemParsed()),
+ this, SLOT(startPlayingPlaylistFile()), Qt::QueuedConnection);
+ connect(m_playlistFileUtil, SIGNAL(parsingReady(bool)),
+ this, SLOT(handlePlaylistFileParsingErrors(bool)), Qt::QueuedConnection);
+ }
+ m_playlistFileUtil->parsePlaylistFile(url);
+
+ }
+ else
+ {
+ playURI(url.toEncoded());
+
+ QVariant startPosition = mediaInfo.firstMetaData(MAFW_METADATA_KEY_START_POSITION);
+ if( startPosition.isValid() )
+ {
+ uint pos = startPosition.toUInt();
+ qDebug() << "Immediate seek requested to: " << pos;
+ doSeek(pos, MafwRenderer::SeekAbsolute);
+ }
+ else
+ {
+ QVariant pausePosition = mediaInfo.firstMetaData(MAFW_METADATA_KEY_PAUSED_POSITION);
+ if( pausePosition.isValid() )
+ {
+ uint position = pausePosition.toUInt();
+ qDebug() << "Immediate pause requested at:" << position;
+ mafw_gst_renderer_worker_pause_at(m_worker, position);
+ }
+ }
+ }
+ }
+ else
+ {
+ MafwError mafwError(MafwError::RendererError_InvalidURI, url.toString());
+ Q_EMIT rendererError(mafwError);
+ doStop();
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::doStop
+ ********************************************************************/
+void MafwGstRenderer::doStop()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ mafw_gst_renderer_worker_stop(m_worker);
+ m_currentState = MafwRenderer::Stopped;
+ m_playingItem = MafwBasicRenderer::UnknownUri;
+
+ m_haltState.clear();
+
+ stopTimers();
+ Q_EMIT rendererStopped();
+}
+
+/********************************************************************
+ * MafwGstRenderer::doPause
+ ********************************************************************/
+void MafwGstRenderer::doPause()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ if( m_haltState.isSet() && m_haltState.state() == MafwRenderer::Playing )
+ {
+ m_haltState.setState(MafwRenderer::Paused);
+ m_currentState = MafwRenderer::Paused;
+ Q_EMIT rendererPaused();
+ }
+ else
+ {
+ mafw_gst_renderer_worker_pause(m_worker);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::doResume
+ ********************************************************************/
+void MafwGstRenderer::doResume()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ if( m_currentState == MafwRenderer::Paused && m_haltState.isSet() && m_haltState.state() == MafwRenderer::Paused )
+ {
+ mafw_gst_renderer_worker_play(m_worker, m_haltState.uri().toAscii().constData());
+ m_currentState = MafwRenderer::Paused;
+ if( m_haltState.position() > 0 )
+ {
+ doSeek(m_haltState.position(), MafwRenderer::SeekAbsolute);
+ }
+ }
+ else
+ {
+ mafw_gst_renderer_worker_resume(m_worker);
+ }
+
+ if( m_haltState.isSet() )
+ {
+ m_haltState.clear();
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::doSeek
+ ********************************************************************/
+void MafwGstRenderer::doSeek(int position, MafwRenderer::SeekMode seekMode)
+{
+ GError *error = 0;
+
+ qDebug() << __PRETTY_FUNCTION__;
+
+ GstSeekType seekType;
+ if( MafwRenderer::SeekAbsolute == seekMode )
+ {
+ seekType = GST_SEEK_TYPE_SET;
+ }
+ else if( MafwRenderer::SeekRelative == seekMode )
+ {
+ seekType = GST_SEEK_TYPE_CUR;
+ }
+ else
+ {
+ qCritical("MafwGstRenderer: Invalid seek operation requested!");
+ return;
+ }
+
+ mafw_gst_renderer_worker_set_position(m_worker,
+ seekType,
+ position,
+ &error);
+
+ if (error)
+ {
+ MafwError mafwError;
+ mafwError.setCode(MafwError::RendererError_CannotSetPosition);
+ mafwError.setMessage(error->message);
+ Q_EMIT rendererError(mafwError);
+ g_error_free(error);
+ }
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::doNextHint
+ ********************************************************************/
+bool MafwGstRenderer::doNextHint(const MafwContent& content)
+{
+ Q_ASSERT_X(false, "MafwGstRenderer", "Wrong play function called!");
+ Q_UNUSED(content);
+ return false;
+}
+
+/********************************************************************
+ * MafwGstRenderer::doNextHint
+ ********************************************************************/
+bool MafwGstRenderer::doNextHint(const MafwMediaInfo& mediaInfo)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ m_nextContent = mediaInfo;
+ // If we have already reached EOS trigger a new play attempt because the
+ // next content was signalled too late. However, if we have gone from playing
+ // state we can not continue, because we have released resources.
+ if (m_worker->eos && (m_currentState == MafwRenderer::Playing))
+ {
+ QTimer::singleShot(0, this, SLOT(playNext()));
+ }
+ return true;
+}
+
+/********************************************************************
+ * MafwGstRenderer::getPosition
+ ********************************************************************/
+bool MafwGstRenderer::getPosition(QObject* resultsReceiver,
+ const char* resultsMember)
+{
+
+ Q_EMIT signalGetPosition(resultsReceiver,
+ resultsMember);
+
+ return true;
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::setMafwProperty
+ ********************************************************************/
+bool MafwGstRenderer::setMafwProperty(const QString& name,
+ const QVariant& value)
+{
+ qDebug() << __PRETTY_FUNCTION__ << name;
+
+ bool success = true;
+ if (name == PROPERTY_VOLUME)
+ {
+ if (!m_volume)
+ {
+ m_volume = new MafwGstRendererVolume();
+ connect(m_volume, SIGNAL(volumeChanged(uint)), this, SLOT(handleVolumeChange(uint)));
+ }
+ success = m_volume->setVolume(value.toUInt());
+ }
+ else if (name == PROPERTY_DOLBY_STATE_MUSIC)
+ {
+ success = m_dolby->setMusicDolbyState(value.toUInt());
+ if (success)
+ {
+ set_dolby_music_property(m_worker, m_dolby->getMusicDolbyState());
+ }
+ }
+ else if (name == PROPERTY_DOLBY_STATE_MUSIC_ROOM)
+ {
+ success = m_dolby->setMusicDolbyState(value.toInt());
+ if (success)
+ {
+ set_dolby_music_sound_property(m_worker, m_dolby->getMusicDolbyRoom(), TRUE);
+ }
+ }
+ else if (name == PROPERTY_DOLBY_STATE_MUSIC_COLOR)
+ {
+ success = m_dolby->setMusicDolbyState(value.toInt());
+ if (success)
+ {
+ set_dolby_music_sound_property(m_worker, m_dolby->getMusicDolbyColor(), FALSE);
+ }
+ }
+ else if (name == PROPERTY_DOLBY_STATE_VIDEO)
+ {
+ success = m_dolby->setVideoDolbyState(value.toUInt());
+ if (success)
+ {
+ set_dolby_video_property(m_worker, m_dolby->getVideoDolbyState());
+ }
+ }
+ else if (name == PROPERTY_DOLBY_STATE_VIDEO_ROOM)
+ {
+ success = m_dolby->setVideoDolbyState(value.toInt());
+ if (success)
+ {
+ set_dolby_video_sound_property(m_worker, m_dolby->getVideoDolbyRoom(), TRUE);
+ }
+ }
+ else if (name == PROPERTY_DOLBY_STATE_VIDEO_COLOR)
+ {
+ success = m_dolby->setVideoDolbyState(value.toInt());
+ if (success)
+ {
+ set_dolby_video_sound_property(m_worker, m_dolby->getVideoDolbyColor(), FALSE);
+ }
+ }
+ else if (name == PROPERTY_AUTOPAINT)
+ {
+ mafw_gst_renderer_worker_set_autopaint(m_worker, value.toBool());
+ }
+ else if (name == PROPERTY_XID)
+ {
+ if (rendererPolicy())
+ {
+ rendererPolicy()->setDefaultResources(MafwRendererPolicy::Audio | MafwRendererPolicy::Video);
+ }
+ else
+ {
+ qCritical() << __PRETTY_FUNCTION__ << "unable to append video to default resources";
+ }
+
+ mafw_gst_renderer_worker_set_xid(m_worker, value.toUInt());
+ }
+ else if (name == PROPERTY_CURRENT_FRAME_ON_PAUSE)
+ {
+ mafw_gst_renderer_worker_set_current_frame_on_pause(m_worker,
+ value.toBool());
+ }
+ else if (name == PROPERTY_PLAYBACK_SPEED)
+ {
+ success = mafw_gst_renderer_worker_set_playback_speed(m_worker, value.toFloat());
+ }
+ else if (name == PROPERTY_FORCE_ASPECT_RATIO)
+ {
+ mafw_gst_renderer_worker_set_force_aspect_ratio(m_worker, value.toBool());
+ }
+ else if( name == PROPERTY_RENDER_RECT )
+ {
+ if( value.type() != QVariant::String )
+ {
+ qWarning() << "MafwGstRenderer Invalid ("<<PROPERTY_RENDER_RECT<<") value received:" << value;
+ }
+ else
+ {
+ QString str = value.toString();
+ QStringList list = str.split(",");
+ bool success = true;
+ int array[4]; // x, y, width, height
+ if( list.size() != 4 )
+ {
+ success=false;
+ }
+ else
+ {
+ for( int i = 0; i < 4 && success; ++i )
+ {
+ QString str = list.at(i);
+ array[i] = str.toInt(&success);
+ }
+ }
+ if( !success )
+ {
+ qWarning() << "Invalid property (" << name << ") value received: " << value;
+ }
+ else
+ {
+ render_rectangle rect = {array[0], array[1], array[2], array[3]};
+ mafw_gst_renderer_worker_set_render_rectangle(m_worker, &rect);
+ }
+ }
+ }
+ else
+ {
+ success = MafwBasicRenderer::setMafwProperty(name, value);
+ }
+
+ if (!success)
+ {
+ MafwError err;
+ err.setCode(MafwError::RendererError_CannotSetProperty);
+ Q_EMIT rendererError(err);
+ }
+
+ return success;
+}
+
+/********************************************************************
+ * MafwGstRenderer::mafwProperty
+ ********************************************************************/
+bool MafwGstRenderer::mafwProperty(QString& name,
+ QObject* receiver,
+ const char* member)
+{
+
+ qDebug() << __PRETTY_FUNCTION__;
+
+ Q_EMIT signalMafwProperty(name, receiver, member);
+
+ return true;
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::mafwProperty
+ ********************************************************************/
+bool MafwGstRenderer::getCurrentMediaInfo(QObject* receiver,
+ const char* member,
+ const QString& metadataKey)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ if(m_currentState == MafwRenderer::Playing || m_currentState == MafwRenderer::Paused)
+ {
+ Q_EMIT signalGetCurrentMediaInfo(receiver, member, metadataKey);
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/********************************************************************
+ * MafwGstRenderer::pauseCallback
+ ********************************************************************/
+void MafwGstRenderer::pauseCallback(MafwGstRendererWorker *worker,
+ gpointer owner)
+{
+
+ Q_UNUSED(worker);
+
+ qDebug() << __PRETTY_FUNCTION__;
+
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ self->m_playedStampTimer.stop();
+
+ Q_EMIT self->rendererPaused();
+
+ //are we staying in paused after stopped state (pauseAt requested)
+ //we'll need to inform the MafwBasicRenderer to give the next item to play after current
+ //if so start fetching next also in this case
+ if( self->m_currentState == MafwRenderer::Stopped )
+ {
+ Q_EMIT self->rendererReadyForNext(self->m_playingItem);
+ }
+
+ self->m_currentState = MafwRenderer::Paused;
+}
+
+/********************************************************************
+ * MafwGstRenderer::eosCallback
+ * Renderer does not stop here, because there could be set next item to play.
+ ********************************************************************/
+void MafwGstRenderer::eosCallback(MafwGstRendererWorker *worker,
+ gpointer owner)
+{
+
+ Q_UNUSED(worker);
+
+ qDebug() << __PRETTY_FUNCTION__;
+
+ MafwGstRenderer* self = static_cast<MafwGstRenderer*>(owner);
+
+ //this is very special case to restart playing streams of undetermined duration and nonseekable
+ if( mafw_gst_renderer_worker_get_streaming(worker)
+ && mafw_gst_renderer_worker_get_last_known_duration(worker) < 0
+ && !mafw_gst_renderer_worker_get_seekable(worker) )
+ {
+ QTimer::singleShot(0, self, SLOT(restartPlay()));
+ return;
+ }
+
+ if( self->m_playedStampTimer.isActive() ) // eos before stamped, stamp now
+ {
+ self->m_playedStampTimer.stop();
+ self->slotStamp();
+ }
+
+ if (self->m_playingPlaylistFile) //Try next uri if exists
+ {
+ self->m_playlistNextTimer.start(0);
+ }
+ else
+ {
+ QTimer::singleShot(0, self, SLOT(playNext()));
+ Q_EMIT self->rendererEos();
+ }
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::restartPlay
+ * Slot to call asynchronously to restart playback (e.g. when internet radio disconnect due to network issues)
+ ********************************************************************/
+void MafwGstRenderer::restartPlay()
+{
+ //only restart if we're still playing
+ if( m_currentState == MafwRenderer::Playing )
+ {
+ doPlay(m_currentContent);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::readyStateCallback
+ * Worker informs via this when it is no longer using any resources when paused
+ ********************************************************************/
+void MafwGstRenderer::readyStateCallback(MafwGstRendererWorker *worker, gpointer owner)
+{
+ Q_UNUSED(worker);
+
+ MafwGstRenderer *self = static_cast<MafwGstRenderer*>(owner);
+
+ if( self->m_currentState != MafwRenderer::Paused )
+ {
+ qCritical("MafwGstRenderer: Ready state informed, but not in PAUSED state! Not releasing resources!");
+ return;
+ }
+
+ MafwRendererPolicy *policy = self->rendererPolicy();
+ if( policy )
+ {
+ policy->release();
+ }
+}
+
+//
+//Private implementation
+//
+
+/********************************************************************
+ * MafwGstRenderer::slotGetPosition
+ ********************************************************************/
+void MafwGstRenderer::slotGetPosition(QObject* resultsReceiver,
+ const char* resultsMember)
+{
+ QMetaMethod method;
+ bool methodFound;
+ gint pos;
+
+ if(m_currentState == MafwRenderer::Stopped)
+ {
+ pos = 0;
+ }
+ else if( m_haltState.isSet() )
+ {
+ pos = m_haltState.position();
+ }
+ else
+ {
+ /* this returns -1 on failure */
+ pos = mafw_gst_renderer_worker_get_position(m_worker);
+ }
+
+ if (pos < 0)
+ {
+ MafwError err;
+ err.setCode(MafwError::RendererError_CannotGetPosition);
+ Q_EMIT rendererError(err);
+ }
+ else
+ {
+ methodFound = MafwCallbackHelper::getCallbackMethod(resultsReceiver,
+ resultsMember,
+ method);
+
+ if (!methodFound ||
+ method.invoke(resultsReceiver, Q_ARG(uint, pos)) == false)
+ {
+ qCritical() << "Invoking the get position callback method failed!";
+ }
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::slotMafwProperty
+ ********************************************************************/
+void MafwGstRenderer::slotMafwProperty(const QString& name,
+ QObject* receiver,
+ const char* member)
+{
+
+ QVariant prop;
+ QMetaMethod method;
+ bool methodFound;
+
+ if (name == PROPERTY_VOLUME)
+ {
+ if (!m_volume)
+ {
+ m_volume = new MafwGstRendererVolume();
+ connect(m_volume, SIGNAL(volumeChanged(uint)), this, SLOT(handleVolumeChange(uint)));
+ }
+
+ uint value = m_volume->getVolume();
+ prop = QVariant(value);
+ }
+ else if (name == PROPERTY_DOLBY_STATE_MUSIC)
+ {
+ uint value = m_dolby->getMusicDolbyState();
+ prop = QVariant(value);
+ }
+ else if (name == PROPERTY_DOLBY_STATE_VIDEO)
+ {
+ uint value = m_dolby->getVideoDolbyState();
+ prop = QVariant(value);
+ }
+ else if (name == PROPERTY_AUTOPAINT)
+ {
+ gboolean value;
+ value = mafw_gst_renderer_worker_get_autopaint(m_worker);
+ prop = QVariant(value);
+ }
+ else if (name == PROPERTY_COLORKEY)
+ {
+ gint value;
+ value = mafw_gst_renderer_worker_get_colorkey(m_worker);
+ prop = QVariant(value);
+ }
+ else if (name == PROPERTY_XID)
+ {
+ XID value;
+ value = mafw_gst_renderer_worker_get_xid(m_worker);
+ prop = QVariant(static_cast<uint>(value));
+ }
+ else if (name == PROPERTY_PLAYBACK_SPEED)
+ {
+ gfloat value;
+ value = mafw_gst_renderer_worker_get_playback_speed(m_worker);
+ prop = QVariant(value);
+ }
+ else if (name == PROPERTY_FORCE_ASPECT_RATIO)
+ {
+ gboolean value;
+ value = mafw_gst_renderer_worker_get_force_aspect_ratio(m_worker);
+ prop = QVariant(value);
+ }
+ else if( name == PROPERTY_RENDER_RECT)
+ {
+ const render_rectangle *rect = mafw_gst_renderer_worker_get_render_rectangle(m_worker);
+ prop = QString("%1,%2,%3,%4")
+ .arg(rect->x).arg(rect->y).arg(rect->width).arg(rect->height);
+ }
+ else
+ {
+ qWarning() << "unknown property: " << name;
+ }
+
+ methodFound = MafwCallbackHelper::getCallbackMethod(receiver,
+ member,
+ method);
+
+ if (!methodFound || method.invoke(receiver,
+ Q_ARG(QString, name),
+ Q_ARG(QVariant, prop)) == false)
+ {
+ qCritical() << "Invoking the callback method failed!";
+ }
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::slotStamp
+ ********************************************************************/
+void MafwGstRenderer::slotStamp()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ QString uid=m_currentContent.uuid();
+ if( !uid.isEmpty() )
+ {
+ // create live node from MAFW object ID. Tracker case only implemented
+ // here. There definitely should be helper function for this.
+ const QString TRACKER_SOURCE_UUID = "MafwTrackerSource";
+ const QString MAFW_UUID_SEPARATOR = "::";
+
+ QString source = uid.section(MAFW_UUID_SEPARATOR, 0 , 0);
+
+ if ( source == TRACKER_SOURCE_UUID )
+ {
+ QString uniqueNodeIdentifier = uid.section(MAFW_UUID_SEPARATOR, 1, 1);
+ if (uniqueNodeIdentifier.length() > 0)
+ {
+ int counter = m_currentContent.firstMetaData(MAFW_METADATA_KEY_PLAY_COUNT).toInt();
+ counter++;
+ qDebug() << "MafwGstRenderer::slotStamp counter" << counter;
+ m_currentContent.appendMetaData(MAFW_METADATA_KEY_PLAY_COUNT, QList<QVariant>() << QVariant(counter));
+
+ int storedDuration = m_currentContent.firstMetaData(MAFW_METADATA_KEY_DURATION).toInt();
+ int currentDuration = mafw_gst_renderer_worker_get_duration(m_worker);
+ int stampDuration = -1;
+ if( currentDuration >= 0 && storedDuration != currentDuration )
+ {
+ qDebug() << "Will store new duration:" << currentDuration;
+ stampDuration = currentDuration;
+ Q_EMIT(metadataChanged(MAFW_METADATA_KEY_DURATION, QList<QVariant>() << stampDuration));
+ }
+
+ stampIt(uniqueNodeIdentifier, counter, stampDuration);
+ }
+ }
+ }
+ else // UUID is unknown
+ {
+ const QUrl url = m_currentContent.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+
+ if( url.isValid() && url.toString().startsWith("file://") )
+ {
+ qDebug() << "MafwGstRenderer::slotStamp query from tracker" << url;
+
+ QSparqlQuery query(QString("SELECT ?urn ?usageCount ?duration WHERE { "
+ "?urn nie:url \"%1\". "
+ "OPTIONAL { "
+ "?urn nie:usageCounter ?usageCount. "
+ "?urn nfo:duration ?duration } "
+ "}")
+ .arg(url.toEncoded().constData()));
+
+ delete m_urnQueryResult;
+ m_urnQueryResult = m_sparqlConnection->exec(query);
+ connect(m_urnQueryResult, SIGNAL(finished()),
+ this, SLOT(slotStampQueryReady()));
+ }
+ }
+
+ m_playedStamped=true;
+}
+
+/********************************************************************
+ * MafwGstRenderer::slotStampQueryReady
+ ********************************************************************/
+void MafwGstRenderer::slotStampQueryReady()
+{
+ m_playedStampTryCounter++;
+ if( !m_urnQueryResult || m_urnQueryResult->hasError() || !m_urnQueryResult->first() )
+ {
+ qWarning() << "MafwGstRenderer::slotStampQueryReady: surprising result";
+ if (!m_playedStampTimer.isActive()
+ && m_currentState == MafwRenderer::Playing
+ && m_playedStampTryCounter < PLAYED_STAMP_TRIES)
+ {
+ qDebug() << __PRETTY_FUNCTION__ << "restarting timer.";
+ m_playedStampTimer.start(PLAYED_STAMP_INTERVAL);
+ }
+ else
+ {
+ qWarning() << __PRETTY_FUNCTION__ << "played stamping didn't succeeded.";
+ m_playedStamped = false;
+ }
+ }
+ else
+ {
+ QString urn = m_urnQueryResult->stringValue(0);
+ int usageCount = m_urnQueryResult->stringValue(1).toInt();
+ int storedDuration = m_urnQueryResult->stringValue(2).toInt();
+
+ int currentDuration = mafw_gst_renderer_worker_get_duration(m_worker);
+
+ int mediaDuration = -1;
+ if( storedDuration != currentDuration)
+ {
+ mediaDuration = currentDuration;
+ Q_EMIT(metadataChanged(MAFW_METADATA_KEY_DURATION, QList<QVariant>() << mediaDuration));
+ }
+
+ qDebug() << "MafwGstRenderer::slotStampQueryReady" << urn << usageCount << mediaDuration;
+
+ stampIt(urn, usageCount+1, mediaDuration);
+ }
+
+
+ delete m_urnQueryResult;
+ m_urnQueryResult = 0;
+}
+
+/********************************************************************
+ * MafwGstRenderer::stopTimers
+ ********************************************************************/
+void MafwGstRenderer::stopTimers()
+{
+ m_playlistNextTimer.stop();
+ if (m_playlistFileUtil)
+ {
+ m_playlistFileUtil->takePendingError();
+ }
+ m_playedStampTimer.stop();
+}
+
+/********************************************************************
+ * MafwGstRenderer::stampIt
+ ********************************************************************/
+void MafwGstRenderer::stampIt(const QString& urn, int usageCount, int mediaDuration)
+{
+ QString isoDate=QDateTime::currentDateTime().toUTC().toString(Qt::ISODate);
+ // Add UTC mark "Z" if it is missing (Qt behaviour has changed it seems to add it nowadays)
+ if( isoDate.length()==ISO_DATE_BASE_LENGTH )
+ {
+ isoDate.append("Z");
+ }
+
+ QSparqlQuery update;
+ if( mediaDuration > -1 )
+ {
+ update.setQuery(QString(
+ " DELETE { <%1> nie:contentAccessed ?old } "
+ " WHERE { <%1> nie:contentAccessed ?old } "
+ " DELETE { <%1> nie:usageCounter ?oldu } "
+ " WHERE { <%1> nie:usageCounter ?oldu } "
+ " DELETE { <%1> nfo:duration ?oldd } "
+ " WHERE { <%1> nfo:duration ?oldd } "
+ " INSERT { <%1> nie:contentAccessed \"%2\" . "
+ " <%1> nie:usageCounter \"%3\" . "
+ " <%1> nfo:duration \"%4\" }")
+ .arg(urn)
+ .arg(isoDate)
+ .arg(usageCount)
+ .arg(mediaDuration));
+ }
+ else
+ {
+ update.setQuery(QString(
+ "DELETE { <%1> nie:contentAccessed ?old } "
+ " WHERE { <%1> nie:contentAccessed ?old } "
+ "DELETE { <%1> nie:usageCounter ?oldu } "
+ " WHERE { <%1> nie:usageCounter ?oldu } "
+ "INSERT { <%1> nie:contentAccessed \"%2\" . "
+ " <%1> nie:usageCounter \"%3\"}")
+ .arg(urn)
+ .arg(isoDate)
+ .arg(usageCount));
+ }
+
+ update.setType(QSparqlQuery::InsertStatement);
+
+
+ delete m_stampItResult;
+ m_stampItResult = m_sparqlConnection->exec(update);
+ connect(m_stampItResult, SIGNAL(finished()),
+ this, SLOT(slotStampItDone()));
+}
+
+/********************************************************************
+ * MafwGstRenderer::slotStampItDone()
+ ********************************************************************/
+void MafwGstRenderer::slotStampItDone()
+{
+ if( !m_stampItResult )
+ {
+ qWarning() << "Stampit cannot be done without stmapit result! Invalid slot call?";
+ return;
+ }
+
+ if( m_stampItResult->hasError() )
+ {
+ qWarning() << "Stampit failed:" << m_stampItResult->lastError().message();
+ }
+ delete m_stampItResult;
+ m_stampItResult = 0;
+}
+
+/********************************************************************
+ * MafwGstRenderer::slotRouteChanged()
+ ********************************************************************/
+void MafwGstRenderer::slotRouteChanged()
+{
+ QSet<int> set;
+ QString route;
+
+ // 1. add audio route(s) to the route set
+ route = m_audioRoute->value().toString();
+ qDebug() << "audio route is:" << route;
+ if (audioRouteMap().contains(route))
+ {
+ Q_FOREACH (int value, audioRouteMap().value(route))
+ {
+ set.insert(value);
+ }
+ }
+ else
+ {
+ // TODO: Is it ok to use NULL here?
+ qWarning() << "adding null route (audio)";
+ set.insert(WORKER_OUTPUT_NULL);
+ }
+
+ // 2. add video route(s) to the route set
+ route = m_videoRoute->value().toString();
+ qDebug() << "video route is:" << route;
+ if (videoRouteMap().contains(route))
+ {
+ Q_FOREACH (int value, videoRouteMap().value(route))
+ {
+ set.insert(value);
+ }
+ }
+ else
+ {
+ // TODO: Is it ok to use NULL here?
+ qWarning() << "adding null route (video)";
+ set.insert(WORKER_OUTPUT_NULL);
+ }
+
+ // 3. finally notify the worker about the current routes
+ GSList *destinations = NULL;
+ Q_FOREACH (int value, set)
+ {
+ destinations = g_slist_append(destinations, GINT_TO_POINTER(value));
+ }
+ mafw_gst_renderer_worker_notify_media_destination(this->m_worker,
+ destinations);
+ g_slist_free(destinations);
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::playURI
+ ********************************************************************/
+void MafwGstRenderer::playURI(const QString& uri)
+{
+ m_playedStamped = false;
+ m_playedStampTryCounter = 0;
+
+ //little hack to get pause-to-play transition to be signalled
+ //correctly, in case different URI is asked to be played.
+ //So it's not resume transition
+ m_currentState = MafwRenderer::Stopped;
+ mafw_gst_renderer_worker_play(m_worker, uri.toAscii().constData());
+ m_nextContent = MafwMediaInfo();
+}
+
+/********************************************************************
+ * MafwGstRenderer::startPlayingPlaylistFile
+ ********************************************************************/
+void MafwGstRenderer::startPlayingPlaylistFile()
+{
+ m_playlistNextTimer.stop();
+ QString uri = QString();
+ if (m_playlistFileUtil)
+ {
+ uri = m_playlistFileUtil->takeFirstUri();
+ m_playlistFileUtil->takePendingError();
+ }
+ else
+ {
+ qCritical() << __PRETTY_FUNCTION__ << "playlist file util is NULL!";
+ }
+
+ if (!uri.isEmpty())
+ {
+ qDebug() << __PRETTY_FUNCTION__ << uri;
+
+ if( !m_mmcMonitor->isMounted() && uri.startsWith( MafwMmcMonitor::MMC_URI_PREFIX ) )
+ {
+ qDebug() << "MafwGstRenderer::startPlayingPlaylistFile: Can't play MMC not mounted";
+ MafwError mafwError(MafwError::RendererError_MmcNotAvailable, uri);
+ Q_EMIT rendererError(mafwError);
+ return;
+ }
+
+ m_playingPlaylistFile = true;
+ mafw_gst_renderer_worker_play(m_worker, uri.toAscii().constData());
+ QList<QVariant> metadataValue;
+ metadataValue << uri;
+ Q_EMIT metadataChanged(MAFW_METADATA_KEY_URI, metadataValue);
+ }
+ else
+ {
+ MafwError err;
+ err.setCode(MafwError::RendererError_PlaylistParsing);
+ Q_EMIT rendererError(err);
+ }
+}
+
+
+/********************************************************************
+ * MafwGstRenderer::handlePlaylistFileParsingErrors
+ ********************************************************************/
+void MafwGstRenderer::handlePlaylistFileParsingErrors(bool succeeded)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ if (!succeeded)
+ {
+ if (m_unsupportedTypeError)
+ {
+ errorCallback(m_worker, this, m_unsupportedTypeError);
+ g_error_free(m_unsupportedTypeError);
+ m_unsupportedTypeError = 0;
+ }
+ else
+ {
+ MafwError err;
+ err.setCode(MafwError::RendererError_PlaylistParsing);
+ Q_EMIT rendererError(err);
+ }
+ }
+ else if (!m_playingPlaylistFile)
+ {
+ qDebug() << __PRETTY_FUNCTION__ << "waiting for playlist file items...";
+ MafwError err;
+ err.setCode(MafwError::RendererError_NoPlaylist);
+ m_playlistFileUtil->setPendingError(err);
+ m_playlistNextTimer.start(1000);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::playNext
+ ********************************************************************/
+void MafwGstRenderer::playNext()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ m_playingPlaylistFile = false;
+ m_playedPlaylistItem = false;
+
+ //Preserve m_currentContent for keeping usage count up if the same item is
+ //played again.
+ if( !m_nextContent.uuid().isEmpty() && (m_nextContent.uuid() == m_currentContent.uuid()) )
+ {
+ m_nextContent = m_currentContent;
+ }
+
+ const QUrl nextURI = m_nextContent.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+ if( !nextURI.isEmpty() )
+ {
+ m_playingItem = MafwBasicRenderer::NextUri;
+ m_currentContent = m_nextContent;
+ m_nextContent = MafwMediaInfo();
+
+ playURI(nextURI.toEncoded());
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::playNextURIFromPlaylist
+ ********************************************************************/
+void MafwGstRenderer::playNextURIFromPlaylist()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ QString uri = m_playlistFileUtil->takeFirstUri();
+
+ bool okToPlay=true;
+ if(uri.isEmpty())
+ {
+ okToPlay=false;
+ }
+ else if( !m_mmcMonitor->isMounted() && uri.startsWith( MafwMmcMonitor::MMC_URI_PREFIX ) )
+ {
+ qDebug() << "MafwGstRenderer::playNextURIFromPlaylist: Can't play MMC not mounted";
+ MafwError mafwError(MafwError::RendererError_MmcNotAvailable, uri);
+ m_playlistFileUtil->setPendingError( mafwError );
+ okToPlay=false;
+ }
+
+ if (okToPlay)
+ {
+ m_playlistFileUtil->takePendingError(); // clear it, we have a new candidate
+ qDebug() << "Trying next uri: " << uri;
+ mafw_gst_renderer_worker_play(m_worker, uri.toAscii().constData());
+ QList<QVariant> metadataValue;
+ metadataValue << uri;
+ Q_EMIT metadataChanged(MAFW_METADATA_KEY_URI, metadataValue);
+ }
+ else
+ {
+ m_playingPlaylistFile = false;
+
+ if (m_playedPlaylistItem)
+ {
+ Q_EMIT rendererEos();
+ }
+ m_playedPlaylistItem = false;
+
+
+ MafwError mafwError = m_playlistFileUtil->takePendingError();
+ if ( mafwError.code() != MafwError::NoError)
+ {
+ Q_EMIT rendererError(mafwError);
+ doStop();
+ MafwRendererPolicy *policy = rendererPolicy();
+ if( policy )
+ {
+ policy->release();
+ }
+ }
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::slotCurrentMediaInfo
+ ********************************************************************/
+void MafwGstRenderer::slotGetCurrentMediaInfo(QObject* receiver, const char* member, const QString& metadataKey)
+{
+ MafwMediaInfo info(m_currentContent.uuid());
+
+ //get all metadata
+ if(metadataKey.isEmpty())
+ {
+ info.setMetaData(m_currentMetaData);
+ }
+ //get one item
+ else
+ {
+ QMap<QString, QList<QVariant> >::const_iterator iter = m_currentMetaData.find(metadataKey);
+ if (iter != m_currentMetaData.end())
+ {
+ info.appendMetaData(iter.key(), iter.value());
+ }
+ }
+
+ sendMediaInfo(info, receiver, member);
+}
+
+/********************************************************************
+ * MafwGstRenderer::handleVolumeChange
+ ********************************************************************/
+void MafwGstRenderer::handleVolumeChange(uint level)
+{
+ qDebug() << "MafwGstRenderer::handleVolumeChange: " << level;
+ Q_EMIT mafwPropertyChanged(PROPERTY_VOLUME, level);
+}
+
+/********************************************************************
+ * MafwGstRenderer::stopStreaming
+ ********************************************************************/
+void MafwGstRenderer::stopStreaming()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ if( mafw_gst_renderer_worker_get_streaming(m_worker) )
+ {
+ mafw_gst_renderer_worker_stop(m_worker);
+ stopTimers();
+ }
+
+ // emit error and stop for real, only if no valid halt state is set
+ if( !m_haltState.isSet() )
+ {
+ doStop();
+ MafwError error;
+ error.setCode(MafwError::RendererError_StreamDisconnected);
+ Q_EMIT rendererError(error);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::haltStreaming
+ ********************************************************************/
+void MafwGstRenderer::haltStreaming()
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ if( mafw_gst_renderer_worker_get_streaming(m_worker) )
+ {
+ QString uri;
+ if( m_playlistNextTimer.isActive() )
+ {
+ uri = m_playlistFileUtil->takeFirstUri();
+ }
+ else
+ {
+ uri = mafw_gst_renderer_worker_get_uri(m_worker);
+ }
+
+ int position = -1;
+ if( mafw_gst_renderer_worker_get_seekable(m_worker) )
+ {
+ position = mafw_gst_renderer_worker_get_position(m_worker);
+ if( position < 0 )
+ {
+ qWarning() << "Cannot resume to correct position after networkchange!";
+ }
+ }
+
+ //make sure we've uri to resume, the playlist parser may have been trying to parse something
+ if( uri.length() > 0 )
+ {
+ m_haltState = MafwGstRendererHaltState(uri, m_currentState, position);
+ //valid haltstate constructed, clear the possible pending error in playlist handling
+ if( m_playlistFileUtil )
+ {
+ m_playlistFileUtil->takePendingError();
+ }
+ }
+ else
+ {
+ //just in case
+ m_haltState.clear();
+ }
+
+ //now actually stop, and depending on the haltstate validity it will also emit error
+ stopStreaming();
+ }
+ else
+ {
+ qDebug() << "Not streaming!";
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::continueStreaming
+ ********************************************************************/
+void MafwGstRenderer::continueStreaming()
+{
+ if( mafw_gst_renderer_worker_get_streaming(m_worker) || m_haltState.isSet() )
+ {
+ //if not yet halted, do it now
+ if( !m_haltState.isSet() )
+ {
+ haltStreaming();
+ }
+
+ m_playingItem = MafwBasicRenderer::CurrentUri;
+
+ if( m_haltState.state() == MafwRenderer::Playing )
+ {
+ mafw_gst_renderer_worker_play(m_worker, m_haltState.uri().toAscii().constData());
+ int pausePos = m_haltState.position() > 0 ? m_haltState.position() : 0;
+
+ if( m_haltState.state() == MafwRenderer::Playing && pausePos > 0 )
+ {
+ qDebug() << "Resuming streaming from position: " << m_haltState.position();
+ doSeek(m_haltState.position(), MafwRenderer::SeekAbsolute);
+ }
+ m_haltState.clear();
+ }
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::handlePropertyChanged
+ ********************************************************************/
+void MafwGstRenderer::handlePropertyChanged(const QString& name,
+ const QVariant& value)
+{
+ // This is a way to check if the policy is on. We need to set the
+ // PAUSED-to-READY timeout to zero, since we need to be sure that the
+ // resources really get released by the GStreamer. It is unfortunate that
+ // a doPause() and PAUSED state is not enough, e.g. in case of XVideo.
+ if (name == MAFW_RENDERER_PROPERTY_POLICY_OVERRIDE)
+ {
+ guint timeout;
+ if (value.toBool() == true)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = MAFW_GST_RENDERER_WORKER_READY_TIMEOUT;
+ }
+ mafw_gst_renderer_worker_set_ready_timeout(m_worker, timeout);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::handleDHMMusicPropertyChanged
+ ********************************************************************/
+void MafwGstRenderer::handleDHMMusicPropertyChanged()
+{
+ if (m_worker)
+ {
+ qDebug() << "MafwGstRenderer::handleDHMMusicPropertyChanged set_dolby_music_property" << m_dolby->getMusicDolbyState();
+ set_dolby_music_property(m_worker, m_dolby->getMusicDolbyState());
+ set_dolby_music_sound_property(m_worker, m_dolby->getMusicDolbyRoom(), TRUE);
+ set_dolby_music_sound_property(m_worker, m_dolby->getMusicDolbyColor(), FALSE);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::handleDHMVideoPropertyChanged
+ ********************************************************************/
+void MafwGstRenderer::handleDHMVideoPropertyChanged()
+{
+ if (m_worker)
+ {
+ qDebug() << "MafwGstRenderer::handleDHMVideoPropertyChanged set_dolby_video_property" << m_dolby->getVideoDolbyState();
+ set_dolby_video_property(m_worker, m_dolby->getVideoDolbyState());
+ set_dolby_video_sound_property(m_worker, m_dolby->getVideoDolbyRoom(), TRUE);
+ set_dolby_video_sound_property(m_worker, m_dolby->getVideoDolbyColor(), FALSE);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::handleScreenshot
+ ********************************************************************/
+void MafwGstRenderer::handleScreenshot(char *location, GError *error)
+{
+ if(!error)
+ {
+ QList<QVariant> results;
+ results << location;
+
+ QString mafwMetadataKey = MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI;
+
+ appendRelatedMetadata(mafwMetadataKey, &results);
+ Q_EMIT metadataChanged(mafwMetadataKey, results);
+ m_currentMetaData.insert(mafwMetadataKey, results);
+ }
+ else
+ {
+ qCritical() << error->message;
+ }
+
+ m_worker->taking_screenshot = FALSE;
+}
+
+/********************************************************************
+ * MafwGstRenderer::cancelScreenshot
+ ********************************************************************/
+void MafwGstRenderer::cancelScreenshot()
+{
+ if(m_worker)
+ {
+ m_worker->taking_screenshot = FALSE;
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::sendMediaInfo
+ ********************************************************************/
+void MafwGstRenderer::sendMediaInfo(const MafwMediaInfo& info, QObject* receiver, const char* member)
+{
+ QMetaMethod method;
+ bool methodFound;
+
+ methodFound = MafwCallbackHelper::getCallbackMethod(receiver, member, method);
+
+ if (!methodFound)
+ {
+ MafwError err;
+ err.setCode(MafwError::CallbackSlotNotFound);
+ Q_EMIT error(err);
+ }
+ //actual result callback call is inside this if()
+ else if( !method.invoke(receiver, Q_ARG(MafwMediaInfo, info)) )
+ {
+ MafwError err;
+ err.setCode(MafwError::CallbackCouldNotInvoke);
+ Q_EMIT error(err);
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::mmcPreUnmount
+ ********************************************************************/
+void MafwGstRenderer::mmcPreUnmount()
+{
+ qDebug() << "MafwGstRenderer::mmcPreUnmount" << m_currentState;
+
+ if( m_currentState!=MafwRenderer::Stopped )
+ {
+ const QUrl url = m_currentContent.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+ if( url.toString().startsWith(MafwMmcMonitor::MMC_URI_PREFIX) )
+ {
+ qDebug() << "MafwGstRenderer::mmcPreUnmount: playing from MMC, going to stop";
+ doStop();
+ MafwError mafwError(MafwError::RendererError_MmcNotAvailable, url.toEncoded());
+ Q_EMIT rendererError(mafwError);
+ }
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::connectNameOwnerChanged
+ ********************************************************************/
+bool MafwGstRenderer::connectNameOwnerChanged()
+{
+ QStringList argumentMatch;
+ argumentMatch << DBUS_NAME_PCFD;
+
+ QDBusConnection connection = QDBusConnection::systemBus();
+ return connection.connect( QString(),
+ QString(),
+ DBUS_INTERFACE_DBUS,
+ DBUS_SIGNAL_NAME_OWNER_CHANGED,
+ argumentMatch,
+ QString(),
+ this,
+ SLOT(handleContextProviderRemoval(QDBusMessage) ) );
+}
+
+/********************************************************************
+ * MafwGstRenderer::handleContextProviderRemoval
+ ********************************************************************/
+void MafwGstRenderer::handleContextProviderRemoval( const QDBusMessage& message )
+{
+ QList<QVariant> arguments;
+ QString name;
+ QString oldName;
+ QString newName;
+
+ arguments= message.arguments();
+
+ if ( message.type() == QDBusMessage::SignalMessage && arguments.size()==3 )
+ {
+
+ name = arguments.at(0).toString();
+ oldName = arguments.at(1).toString();
+ newName = arguments.at(2).toString();
+
+ if ( oldName.length() && newName.length()==0 )
+ {
+ qDebug() << "MafwGstRenderer::handleContextProviderRemoval context provider died";
+
+ // add null output
+ GSList *destinations = NULL;
+ destinations = g_slist_append(destinations,
+ GINT_TO_POINTER(WORKER_OUTPUT_NULL));
+ mafw_gst_renderer_worker_notify_media_destination(this->m_worker,
+ destinations);
+ g_slist_free(destinations);
+ }
+ }
+}
+
+/********************************************************************
+ * MafwGstRenderer::handleResolutionError
+ ********************************************************************/
+void MafwGstRenderer::handleResolutionError(MafwError &error)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+ const QUrl url = m_currentContent.firstMetaData(MAFW_METADATA_KEY_URI).toUrl();
+ MafwError::Code errorCode = MafwError::RendererError_UnsuppertedType;
+
+ if( url.isValid() && url.toString().startsWith("file://") )
+ {
+ qDebug() << __PRETTY_FUNCTION__ << url;
+
+ QSparqlQuery query(QString("SELECT ?height ?width WHERE { "
+ "?_u nie:url \"%1\" ."
+ "?_u nfo:height ?height . "
+ "?_u nfo:width ?width }")
+ .arg(QString(url.toEncoded())));
+
+ QSparqlResult *result = m_sparqlConnection->syncExec(query);
+
+ if( result->hasError() )
+ {
+ qWarning() << __PRETTY_FUNCTION__ << " surprising result";
+ qWarning() << result->lastError().message();
+ }
+ else if( result->first() )
+ {
+ int height = result->stringValue(0).toInt();
+ int width = result->stringValue(1).toInt();
+
+ if (height > MAX_SUPPORTED_HEIGHT || width > MAX_SUPPORTED_WIDTH)
+ {
+ errorCode = MafwError::RendererError_UnsupportedResolution;
+ }
+ }
+ delete result;
+ }
+ error.setCode(errorCode);
+}
+
+/********************************************************************
+ * MafwGstRenderer::setConfiguration
+ ********************************************************************/
+void MafwGstRenderer::setConfiguration(QSettings *settings)
+{
+ //if no settings use "factory" configuration
+ if( !settings )
+ {
+ return;
+ }
+
+ configuration *defaultconfig = mafw_gst_renderer_worker_create_default_configuration(m_worker);
+
+ //pipeline settings
+ settings->beginGroup("pipeline");
+ QVariant value = readSettingsValue(settings, "audio-sink", defaultconfig->asink);
+ qFree(defaultconfig->asink);
+ defaultconfig->asink = g_strdup(value.toString().toAscii());
+
+ value = readSettingsValue(settings, "video-sink", defaultconfig->vsink);
+ qFree(defaultconfig->vsink);
+ defaultconfig->vsink = g_strdup(value.toString().toAscii());
+
+ value = readSettingsValue(settings, "flags", defaultconfig->flags);
+ defaultconfig->flags = value.toInt();
+
+ value = readSettingsValue(settings, "use_dhmmixer", defaultconfig->use_dhmmixer);
+ defaultconfig->use_dhmmixer = value.toBool();
+
+ value = readSettingsValue(settings, "buffer-time", defaultconfig->buffer_time);
+ defaultconfig->buffer_time = value.toULongLong();
+
+ value = readSettingsValue(settings, "latency-time", defaultconfig->latency_time);
+ defaultconfig->latency_time = value.toULongLong();
+
+ value = readSettingsValue(settings, "autoload_subtitles", defaultconfig->autoload_subtitles);
+ defaultconfig->autoload_subtitles = value.toBool();
+
+ value = readSettingsValue(settings, "subtitle_encoding", defaultconfig->subtitle_encoding);
+ qFree(defaultconfig->subtitle_encoding);
+ defaultconfig->subtitle_encoding = g_strdup(value.toString().toAscii());
+
+ value = readSettingsValue(settings, "subtitle_font", defaultconfig->subtitle_font);
+ qFree(defaultconfig->subtitle_font);
+ defaultconfig->subtitle_font = g_strdup(value.toString().toAscii());
+ settings->endGroup();
+
+ //timers
+ settings->beginGroup("timers");
+ value = readSettingsValue(settings, "pause-frame", defaultconfig->milliseconds_to_pause_frame);
+ defaultconfig->milliseconds_to_pause_frame = value.toUInt();
+
+ value = readSettingsValue(settings, "pause-to-ready", defaultconfig->seconds_to_pause_to_ready);
+ defaultconfig->seconds_to_pause_to_ready = value.toUInt();
+ settings->endGroup();
+
+ //dhmmixer
+ settings->beginGroup("dhmmixer");
+ value = readSettingsValue(settings, "dhm-music-surround", defaultconfig->mobile_surround_music.state);
+ defaultconfig->mobile_surround_music.state = value.toUInt();
+
+ value = readSettingsValue(settings, "dhm-music-color", defaultconfig->mobile_surround_music.color);
+ defaultconfig->mobile_surround_music.color = value.toInt();
+
+ value = readSettingsValue(settings, "dhm-music-room-size", defaultconfig->mobile_surround_music.room);
+ defaultconfig->mobile_surround_music.room = value.toInt();
+
+ value = readSettingsValue(settings, "dhm-video-surround", defaultconfig->mobile_surround_video.state);
+ defaultconfig->mobile_surround_video.state = value.toUInt();
+
+ value = readSettingsValue(settings, "dhm-video-color", defaultconfig->mobile_surround_video.color);
+ defaultconfig->mobile_surround_video.color = value.toInt();
+
+ value = readSettingsValue(settings, "dhm-video-room-size", defaultconfig->mobile_surround_video.room);
+ defaultconfig->mobile_surround_video.room = value.toInt();
+ settings->endGroup();
+
+ mafw_gst_renderer_worker_set_configuration(m_worker, defaultconfig);
+}
+
+/********************************************************************
+ * MafwGstRenderer::readSettingsValue
+ ********************************************************************/
+QVariant MafwGstRenderer::readSettingsValue(QSettings *settings,
+ const QString &valueName,
+ const QVariant &defaultValue) const
+{
+ QVariant value = settings->value(valueName, defaultValue);
+ if( !settings->contains(valueName) )
+ {
+ qWarning() << "No value for: (" << valueName << ") in configuration file! Using factory default";
+ }
+ return value;
+}
+
+/********************************************************************
+ * MafwGstRenderer::errorMap
+ ********************************************************************/
+const QHash<int, MafwError::Code>& MafwGstRenderer::errorMap()
+{
+
+ static QHash<int, MafwError::Code> map;
+
+ if (map.isEmpty())
+ {
+ /* initialize error map */
+ map[WORKER_ERROR_PLAYBACK] =
+ MafwError::RendererError_Playback;
+ map[WORKER_ERROR_VIDEO_CODEC_NOT_FOUND] =
+ MafwError::RendererError_VideoCodeNotFound;
+ map[WORKER_ERROR_AUDIO_CODEC_NOT_FOUND] =
+ MafwError::RendererError_AudioCodecNotFound;
+ map[WORKER_ERROR_CODEC_NOT_FOUND] =
+ MafwError::RendererError_CodecNotFound;
+ map[WORKER_ERROR_UNSUPPORTED_TYPE] =
+ MafwError::RendererError_UnsuppertedType;
+ map[WORKER_ERROR_POSSIBLY_PLAYLIST_TYPE] =
+ MafwError::RendererError_UnsuppertedType;
+ map[WORKER_ERROR_UNABLE_TO_PERFORM] =
+ MafwError::RendererError_UnableToPerform;
+ map[WORKER_ERROR_CANNOT_SET_POSITION] =
+ MafwError::RendererError_CannotSetPosition;
+ map[WORKER_ERROR_PLAYLIST_PARSING] =
+ MafwError::RendererError_PlaylistParsing;
+ map[WORKER_ERROR_DRM_NO_LICENSE] =
+ MafwError::RendererError_DRMNoLicense;
+ map[WORKER_ERROR_DRM_NOT_ALLOWED] =
+ MafwError::RendererError_DRMNotAllowed;
+ map[WORKER_ERROR_DRM_OTHER] =
+ MafwError::RendererError_DRMOther;
+ map[WORKER_ERROR_STREAM_DISCONNECTED] =
+ MafwError::RendererError_StreamDisconnected;
+ map[WORKER_ERROR_INVALID_URI] =
+ MafwError::RendererError_InvalidURI;
+ map[WORKER_ERROR_MEDIA_NOT_FOUND] =
+ MafwError::RendererError_MediaNotFound;
+ map[WORKER_ERROR_CORRUPTED_FILE] =
+ MafwError::RendererError_CorruptedFile;
+ map[WORKER_ERROR_TYPE_NOT_AVAILABLE] =
+ MafwError::RendererError_TypeNotAvailable;
+ }
+
+ return map;
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::metadataMap
+ ********************************************************************/
+const QHash<int, QString>& MafwGstRenderer::metadataMap()
+{
+
+ static QHash<int, QString> map;
+
+ if (map.isEmpty())
+ {
+ /* initialize metadata key map */
+ map[WORKER_METADATA_KEY_TITLE] =
+ MAFW_METADATA_KEY_TITLE;
+ map[WORKER_METADATA_KEY_ARTIST] =
+ MAFW_METADATA_KEY_ARTIST;
+ map[WORKER_METADATA_KEY_AUDIO_CODEC] =
+ MAFW_METADATA_KEY_AUDIO_CODEC;
+ map[WORKER_METADATA_KEY_VIDEO_CODEC] =
+ MAFW_METADATA_KEY_VIDEO_CODEC;
+ map[WORKER_METADATA_KEY_BITRATE] =
+ MAFW_METADATA_KEY_BITRATE;
+ map[WORKER_METADATA_KEY_ENCODING] =
+ MAFW_METADATA_KEY_ENCODING;
+ map[WORKER_METADATA_KEY_ALBUM] =
+ MAFW_METADATA_KEY_ALBUM;
+ map[WORKER_METADATA_KEY_GENRE] =
+ MAFW_METADATA_KEY_GENRE;
+ map[WORKER_METADATA_KEY_TRACK] =
+ MAFW_METADATA_KEY_TRACK;
+ map[WORKER_METADATA_KEY_ORGANIZATION] =
+ MAFW_METADATA_KEY_ORGANIZATION;
+ map[WORKER_METADATA_KEY_RENDERER_ART_URI] =
+ MAFW_METADATA_KEY_RENDERER_ART_URI;
+ map[WORKER_METADATA_KEY_RES_X] =
+ MAFW_METADATA_KEY_RES_X;
+ map[WORKER_METADATA_KEY_RES_Y] =
+ MAFW_METADATA_KEY_RES_Y;
+ map[WORKER_METADATA_KEY_VIDEO_FRAMERATE] =
+ MAFW_METADATA_KEY_VIDEO_FRAMERATE;
+ map[WORKER_METADATA_KEY_DURATION] =
+ MAFW_METADATA_KEY_DURATION;
+ map[WORKER_METADATA_KEY_IS_SEEKABLE] =
+ MAFW_METADATA_KEY_IS_SEEKABLE;
+ map[WORKER_METADATA_KEY_PAUSED_THUMBNAIL_URI] =
+ MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI;
+ map[WORKER_METADATA_KEY_URI] =
+ MAFW_METADATA_KEY_URI;
+ }
+
+ return map;
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::audioRouteMap
+ ********************************************************************/
+const QHash<QString, QList<int> >& MafwGstRenderer::audioRouteMap()
+{
+
+ static QHash<QString, QList<int> > map;
+
+ if (map.isEmpty())
+ {
+ map[AUDIO_ROUTE_NULL] = QList<int>() << WORKER_OUTPUT_NULL;
+ map[AUDIO_ROUTE_IHF] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS;
+ map[AUDIO_ROUTE_FMRADIO] = QList<int>() << WORKER_OUTPUT_FM_RADIO;
+ map[AUDIO_ROUTE_IHF_AND_FMRADIO] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS << WORKER_OUTPUT_FM_RADIO;
+
+ // earpiece is intentionally handled as builtdin speaker, well it kinda is
+ map[AUDIO_ROUTE_EARPIECE] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS;
+ map[AUDIO_ROUTE_EARPIECE_AND_TVOUT] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS << WORKER_OUTPUT_TVOUT;
+
+ map[AUDIO_ROUTE_TV_OUT] = QList<int>() << WORKER_OUTPUT_HEADPHONE_JACK;
+ map[AUDIO_ROUTE_IHF_AND_TV_OUT] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS << WORKER_OUTPUT_TVOUT;
+ map[AUDIO_ROUTE_HEADPHONE] = QList<int>() << WORKER_OUTPUT_HEADPHONE_JACK;
+ map[AUDIO_ROUTE_HEADSET] = QList<int>() << WORKER_OUTPUT_HEADPHONE_JACK;
+ map[AUDIO_ROUTE_BTHSP] = QList<int>() << WORKER_OUTPUT_BLUETOOTH_AUDIO;
+ map[AUDIO_ROUTE_BTA2DP] = QList<int>() << WORKER_OUTPUT_BLUETOOTH_AUDIO;
+ map[AUDIO_ROUTE_IHF_AND_HEADSET] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS << WORKER_OUTPUT_HEADPHONE_JACK;
+ map[AUDIO_ROUTE_IHF_AND_HEADPHONE] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS << WORKER_OUTPUT_HEADPHONE_JACK;
+ map[AUDIO_ROUTE_IHF_AND_BTHSP] = QList<int>() << WORKER_OUTPUT_BUILTIN_SPEAKERS << WORKER_OUTPUT_BLUETOOTH_AUDIO;
+ map[AUDIO_ROUTE_TV_OUT_AND_BTHSP] = QList<int>() << WORKER_OUTPUT_HEADPHONE_JACK << WORKER_OUTPUT_BLUETOOTH_AUDIO;
+ map[AUDIO_ROUTE_TV_OUT_AND_BTA2DP] = QList<int>() << WORKER_OUTPUT_HEADPHONE_JACK << WORKER_OUTPUT_BLUETOOTH_AUDIO;
+ }
+
+ return map;
+
+}
+
+/********************************************************************
+ * MafwGstRenderer::videoRouteMap
+ ********************************************************************/
+const QHash<QString, QList<int> >& MafwGstRenderer::videoRouteMap()
+{
+
+ static QHash<QString, QList<int> > map;
+
+ if (map.isEmpty())
+ {
+ map[VIDEO_ROUTE_TV_OUT] = QList<int>() << WORKER_OUTPUT_TVOUT;
+ map[VIDEO_ROUTE_BUILT_IN] = QList<int>() << WORKER_OUTPUT_BUILTIN_DISPLAY;
+ map[VIDEO_ROUTE_BUILT_IN_AND_TV_OUT] = QList<int>() << WORKER_OUTPUT_BUILTIN_DISPLAY << WORKER_OUTPUT_TVOUT;
+ }
+
+ return map;
+
+}
+