[vlc-devel] [PATCH] VLC as UPnP/DLNA MediaRenderer
Johan Gunnarsson
johan.gunnarsson at gmail.com
Sat Jul 6 17:13:05 CEST 2019
This adds support for VLC to act as a UPnP/DLNA MediaRenderer on the
LAN. This means a UPnP/DLNA control point can "cast" media to a VLC
instance and control playback.
Consider this patch a proof of concept.
---
modules/control/dlna.cpp | 706 ++++++++++++++++++
modules/control/dlna.hpp | 68 ++
modules/services_discovery/Makefile.am | 4 +-
modules/services_discovery/upnp-wrapper.cpp | 117 ++-
modules/services_discovery/upnp-wrapper.hpp | 11 +-
modules/services_discovery/upnp.cpp | 15 +-
modules/services_discovery/upnp.hpp | 1 +
modules/stream_out/dlna/dlna.hpp | 2 +-
share/Makefile.am | 5 +
share/upnp/AVTransportSCPD.xml | 585 +++++++++++++++
share/upnp/ConnectionManagerSCPD.xml | 182 +++++
share/upnp/RenderingControlSCPD.xml | 762 ++++++++++++++++++++
12 files changed, 2445 insertions(+), 13 deletions(-)
create mode 100644 modules/control/dlna.cpp
create mode 100644 modules/control/dlna.hpp
create mode 100644 share/upnp/AVTransportSCPD.xml
create mode 100644 share/upnp/ConnectionManagerSCPD.xml
create mode 100644 share/upnp/RenderingControlSCPD.xml
diff --git a/modules/control/dlna.cpp b/modules/control/dlna.cpp
new file mode 100644
index 0000000000..3f41ca515a
--- /dev/null
+++ b/modules/control/dlna.cpp
@@ -0,0 +1,706 @@
+/*****************************************************************************
+ * dlna.cpp : DLNA MediaRenderer module
+ *****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * Authors: Johan Gunnarsson <johan.gunnarsson at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string>
+#include <map>
+#include <iostream>
+#include <regex>
+
+#include "../services_discovery/upnp-wrapper.hpp"
+
+#include <vlc_plugin.h>
+#include <vlc_interface.h>
+#include <vlc_playlist.h>
+#include <vlc_player.h>
+#include <vlc_input.h>
+
+#include "dlna.hpp"
+
+#define SINK_PROTOCOL_INFO ("http-get:*:video/mpeg:*," \
+ "http-get:*:video/mp4:*," \
+ "http-get:*:video/vnd.dlna.mpeg-tts:*," \
+ "http-get:*:video/avi:*," \
+ "http-get:*:video/x-matroska:*," \
+ "http-get:*:video/x-ms-wmv:*," \
+ "http-get:*:video/wtv:*," \
+ "http-get:*:audio/mpeg:*," \
+ "http-get:*:audio/mp3:*," \
+ "http-get:*:audio/mp4:*," \
+ "http-get:*:audio/x-ms-wma*," \
+ "http-get:*:audio/wav:*," \
+ "http-get:*:audio/L16:*," \
+ "http-get:*image/jpeg:*," \
+ "http-get:*image/png:*," \
+ "http-get:*image/gif:*," \
+ "http-get:*image/tiff:*")
+
+struct intf_sys_t
+{
+ UpnpInstanceWrapper *p_upnp;
+ std::shared_ptr<DLNA::EventHandler> p_listener;
+ vlc_playlist_t *playlist;
+ vlc_player_t *player;
+ vlc_player_listener_id *p_player_listener;
+ vlc_player_aout_listener_id *p_player_aout_listener;
+};
+
+typedef std::map<std::string,std::string> parammap;
+
+typedef bool (*ActionRequestHandler)( parammap&, parammap&, intf_thread_t* );
+
+namespace DLNA
+{
+
+static bool
+handle_AVT_SetAVTransportURI( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(out_params);
+
+ std::string s = in_params["CurrentURI"];
+
+ input_item_t *item = input_item_New(s.c_str(), NULL);
+ if( !item )
+ {
+ printf("failed to parse URL?");
+ return -1;
+ }
+
+ vlc_player_t *player = vlc_playlist_GetPlayer( p_intf->p_sys->playlist );
+
+ vlc_player_Lock( player );
+ vlc_player_SetCurrentMedia( player, item );
+ vlc_player_Start( player );
+ vlc_player_Unlock( player );
+
+ input_item_Release( item );
+
+ return true;
+}
+
+static bool
+handle_AVT_GetMediaInfo( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(in_params);
+ VLC_UNUSED(p_intf);
+
+ out_params["MediaDuration"] = "00:00";
+
+ return true;
+}
+
+
+static bool
+handle_AVT_GetTransportInfo( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(in_params);
+
+ vlc_player_t *player = vlc_playlist_GetPlayer( p_intf->p_sys->playlist );
+
+ vlc_player_Lock( player );
+ enum vlc_player_state state = vlc_player_GetState( player );
+ vlc_player_Unlock( player );
+
+ switch( state )
+ {
+ case VLC_PLAYER_STATE_STOPPED:
+ out_params["CurrentTransportState"] = "STOPPED";
+ break;
+ case VLC_PLAYER_STATE_PLAYING:
+ out_params["CurrentTransportState"] = "PLAYING";
+ break;
+ case VLC_PLAYER_STATE_PAUSED:
+ out_params["CurrentTransportState"] = "PAUSED_PLAYBACK";
+ break;
+ case VLC_PLAYER_STATE_STARTED: /* fall through */
+ case VLC_PLAYER_STATE_STOPPING:
+ out_params["CurrentTransportState"] = "TRANSITIONING";
+ break;
+ default:
+ out_params["CurrentTransportState"] = "UNKNOWN";
+ break;
+ }
+
+ out_params["CurrentTransportStatus"] = "";
+ out_params["CurrentSpeed"] = "";
+
+ return true;
+}
+
+static bool
+handle_AVT_Stop( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(in_params);
+ VLC_UNUSED(out_params);
+
+ vlc_player_t *player = vlc_playlist_GetPlayer( p_intf->p_sys->playlist );
+
+ vlc_player_Lock( player );
+ vlc_player_Stop( player );
+ vlc_player_Unlock( player );
+
+ return true;
+}
+
+static bool
+handle_AVT_Play( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(in_params);
+ VLC_UNUSED(out_params);
+
+ vlc_player_t *player = vlc_playlist_GetPlayer( p_intf->p_sys->playlist );
+
+ vlc_player_Lock( player );
+ vlc_player_Resume( player );
+ vlc_player_Unlock( player );
+
+ return true;
+}
+
+static bool
+handle_AVT_Pause( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(in_params);
+ VLC_UNUSED(out_params);
+
+ vlc_player_t *player = vlc_playlist_GetPlayer( p_intf->p_sys->playlist );
+
+ vlc_player_Lock( player );
+ vlc_player_Pause( player );
+ vlc_player_Unlock( player );
+
+ return true;
+}
+
+static bool
+handle_CM_GetProtocolInfo( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(p_intf);
+ VLC_UNUSED(in_params);
+
+ out_params["Source"] = "";
+ out_params["Sink"] = SINK_PROTOCOL_INFO;
+
+ return true;
+}
+
+static bool
+handle_RC_GetVolume( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(in_params);
+
+ /* Volume as in range [0.0, 2.0] or -1.0 if no audio */
+ float volume = vlc_player_aout_GetVolume( p_intf->p_sys->player );
+ if( volume < 0.0 )
+ volume = 0.0;
+ else if( volume > 2.0 )
+ volume = 2.0;
+
+ std::string v = std::to_string( std::round( volume * 50 ) );
+
+ out_params["CurrentVolume"] = v;
+
+ return true;
+}
+
+static bool
+handle_RC_SetVolume( parammap& in_params, parammap& out_params,
+ intf_thread_t *p_intf )
+{
+ VLC_UNUSED(out_params);
+
+ /* Volume in range [0, 100] */
+ unsigned long volume = std::stoul( in_params["DesiredVolume"] );
+ if( volume > 100 )
+ volume = 100;
+
+ int ret = vlc_player_aout_SetVolume( p_intf->p_sys->player, volume / 50.0 );
+ if ( ret != UPNP_E_SUCCESS )
+ {
+ }
+
+ return true;
+}
+
+#define SRV_AVT "urn:upnp-org:serviceId:AVTransport"
+#define SRV_RC "urn:upnp-org:serviceId:RenderingControl"
+#define SRV_CM "urn:upnp-org:serviceId:ConnectionManager"
+
+static struct {
+ const char *service;
+ const char *action;
+ ActionRequestHandler handler;
+} actions[] = {
+ { SRV_AVT, "SetAVTransportURI", handle_AVT_SetAVTransportURI },
+ { SRV_AVT, "GetMediaInfo", handle_AVT_GetMediaInfo },
+ { SRV_AVT, "GetTransportInfo", handle_AVT_GetTransportInfo },
+ { SRV_AVT, "Stop", handle_AVT_Stop },
+ { SRV_AVT, "Play", handle_AVT_Play },
+ { SRV_AVT, "Pause", handle_AVT_Pause },
+ { SRV_CM, "GetProtocolInfo", handle_CM_GetProtocolInfo },
+ { SRV_RC, "GetVolume", handle_RC_GetVolume },
+ { SRV_RC, "SetVolume", handle_RC_SetVolume },
+ { NULL, NULL, NULL }
+};
+
+static parammap
+build_param_map( IXML_Node *p_node )
+{
+ parammap params;
+
+ for( IXML_Node *param = ixmlNode_getFirstChild( p_node );
+ param != NULL;
+ param = ixmlNode_getNextSibling( param ) )
+ {
+ const DOMString key = ixmlNode_getNodeName(param);
+ if( !key )
+ continue;
+
+ IXML_Node *vnode = ixmlNode_getFirstChild( param );
+ if( !vnode )
+ continue;
+
+ const DOMString value = ixmlNode_getNodeValue( vnode );
+ if( !value )
+ continue;
+
+ params[std::string(key)] = std::string(value);
+ }
+
+ return params;
+}
+
+static std::string
+build_event_xml( const char **keys, const char **values, int count )
+{
+ IXML_Document *doc = NULL;
+ IXML_Element *event = NULL;
+ IXML_Element *instance = NULL;
+ DOMString xmlcstr = NULL;
+ std::string xmlstr;
+
+ doc = ixmlDocument_createDocument();
+ if( !doc )
+ return xmlstr;
+
+ event = ixmlDocument_createElement( doc, "Event" );
+ if( !event )
+ goto err1;
+
+ if( ixmlNode_appendChild( (IXML_Node *)doc,
+ (IXML_Node *)event ) != IXML_SUCCESS )
+ {
+ ixmlElement_free( event );
+ goto err1;
+ }
+
+ instance = ixmlDocument_createElement( doc, "InstanceID" );
+ if( !instance )
+ goto err1;
+
+ if( ixmlNode_appendChild( (IXML_Node *)event,
+ (IXML_Node *)instance ) != IXML_SUCCESS )
+ {
+ ixmlElement_free( instance );
+ goto err1;
+ }
+
+ if( ixmlElement_setAttribute( instance, "val", "0") != IXML_SUCCESS )
+ goto err1;
+
+ for( int i = 0; i < count; i++ )
+ {
+ IXML_Element *arg = ixmlDocument_createElement( doc, keys[i] );
+ if( !arg )
+ goto err1;
+
+ if( ixmlNode_appendChild( (IXML_Node *)instance,
+ (IXML_Node *)arg ) != IXML_SUCCESS )
+ {
+ ixmlElement_free( arg );
+ goto err1;
+ }
+
+ if( ixmlElement_setAttribute( arg, "val", values[i]) != IXML_SUCCESS )
+ goto err1;
+ }
+
+ xmlcstr = ixmlNodetoString( (IXML_Node *)doc );
+ if( !xmlcstr )
+ goto err1;
+
+ xmlstr = std::string(xmlcstr);
+
+ free( xmlcstr );
+
+ ixmlDocument_free( doc );
+
+ xmlstr = std::regex_replace(xmlstr, std::regex("&"), "&");
+ xmlstr = std::regex_replace(xmlstr, std::regex("\""), """);
+ xmlstr = std::regex_replace(xmlstr, std::regex("\'"), "'");
+ xmlstr = std::regex_replace(xmlstr, std::regex("<"), "<");
+ xmlstr = std::regex_replace(xmlstr, std::regex(">"), ">");
+
+ return xmlstr;
+
+err1:
+ ixmlDocument_free( doc );
+
+ return xmlstr;
+}
+
+int EventHandler::onActionRequest( UpnpActionRequest *p_event,
+ void *p_user_data )
+{
+ (void) p_user_data;
+
+ /* For example urn:upnp-org:serviceId:AVTransport */
+ char *service_id = UpnpActionRequest_get_ServiceID_cstr( p_event );
+
+ /* For example SetAVTransportURI */
+ char *action_name = UpnpActionRequest_get_ActionName_cstr( p_event );
+
+ /* "Body" XML node in the request */
+ IXML_Document *body = UpnpActionRequest_get_ActionRequest( p_event );
+ if( !body )
+ return 0;
+
+ for( IXML_Node *action = ixmlNode_getFirstChild( (IXML_Node*) body );
+ action != NULL;
+ action = ixmlNode_getNextSibling( action ) )
+ {
+ parammap in_params = build_param_map( action );
+
+ for( size_t i = 0; actions[i].handler; i++ )
+ {
+ if( strcmp( actions[i].service, service_id ) ||
+ strcmp( actions[i].action, action_name ) )
+ continue;
+
+ parammap out_params;
+
+ p_event->ErrCode = UPNP_E_SUCCESS;
+ p_event->ActionResult = NULL;
+
+ int r = actions[i].handler( in_params, out_params, p_intf );
+ if( !r )
+ continue;
+
+ p_event->ActionResult = UpnpMakeActionResponse( action_name,
+ service_id,
+ 0,
+ NULL );
+ if( !p_event->ActionResult )
+ continue;
+
+ for( auto& x : out_params )
+ {
+ int r = UpnpAddToActionResponse( &p_event->ActionResult,
+ action_name,
+ service_id,
+ x.first.c_str(),
+ x.second.c_str() );
+ if( r != UPNP_E_SUCCESS )
+ {
+ if( p_event->ActionResult != NULL )
+ ixmlDocument_free( p_event->ActionResult );
+ p_event->ActionResult = NULL;
+
+ return r;
+ }
+ }
+
+ return UPNP_E_SUCCESS;
+ }
+ }
+
+ // TODO: return "not implemented"
+
+ return UPNP_E_INTERNAL_ERROR;
+}
+
+int EventHandler::onGetVarRequest( UpnpStateVarRequest *p_event,
+ void *p_user_data )
+{
+ (void) p_event;
+ (void) p_user_data;
+
+ // TODO
+
+ return -124;
+}
+
+int EventHandler::onSubscriptionRequest( UpnpSubscriptionRequest *p_event,
+ void *p_user_data )
+{
+ (void) p_user_data;
+
+ /* For example urn:upnp-org:serviceId:AVTransport */
+ char *service_id = UpnpSubscriptionRequest_get_ServiceId_cstr( p_event );
+
+ /* For example ... */
+ char *udn = UpnpSubscriptionRequest_get_UDN_cstr( p_event );
+
+ std::string event_xml = build_event_xml( NULL, NULL, 0 );
+std::cout << event_xml << std::endl;
+
+ const char *var_keys[1] = { "LastChange" };
+ const char *var_values[1] = { event_xml.c_str() };
+
+ int ret = UpnpAcceptSubscription( p_intf->p_sys->p_upnp->device_handle(),
+ udn,
+ service_id,
+ (const char **) &var_keys,
+ (const char **) &var_values,
+ 1,
+ p_event->Sid );
+ if( ret != UPNP_E_SUCCESS )
+ {
+ printf("UpnpAcceptSubscription failed\n");
+ }
+
+ return ret;
+}
+
+int EventHandler::onEvent( Upnp_EventType event_type,
+ UpnpEventPtr p_event,
+ void* p_user_data )
+{
+ switch( event_type )
+ {
+ case UPNP_CONTROL_ACTION_REQUEST:
+ return onActionRequest( (UpnpActionRequest *) p_event, p_user_data );
+ case UPNP_CONTROL_GET_VAR_REQUEST:
+ return onGetVarRequest( (UpnpStateVarRequest *) p_event, p_user_data );
+ case UPNP_EVENT_SUBSCRIPTION_REQUEST:
+ return onSubscriptionRequest( (UpnpSubscriptionRequest *) p_event,
+ p_user_data );
+ default:
+ break;
+ }
+
+ return -123;
+}
+
+static void
+player_state_changed( vlc_player_t *player, enum vlc_player_state state,
+ void *data )
+{
+ (void) player;
+
+ intf_thread_t *p_intf = (intf_thread_t *)data;
+
+ const char *new_state;
+
+ switch (state)
+ {
+ case VLC_PLAYER_STATE_STOPPED:
+ new_state = "STOPPED";
+ break;
+ case VLC_PLAYER_STATE_PLAYING:
+ new_state = "PLAYING";
+ break;
+ case VLC_PLAYER_STATE_PAUSED:
+ new_state = "PAUSED_PLAYBACK";
+ break;
+ case VLC_PLAYER_STATE_STARTED: /* fall through */
+ case VLC_PLAYER_STATE_STOPPING:
+ new_state = "TRANSITIONING";
+ break;
+ default:
+ new_state = "UNKNOWN";
+ break;
+ }
+
+ const char *event_keys[1] = { "TransportState" };
+ const char *event_values[1] = { new_state };
+
+ std::string event_xml = build_event_xml( event_keys, event_values, 1 );
+std::cout << event_xml << std::endl;
+
+ const char *var_keys[1] = { "LastChange" };
+ const char *var_values[1] = { event_xml.c_str() };
+
+ int ret = UpnpNotify( p_intf->p_sys->p_upnp->device_handle(),
+ UPNP_UDN,
+ SRV_AVT,
+ (const char **) &var_keys,
+ (const char **) &var_values,
+ 1 );
+ if ( ret != UPNP_E_SUCCESS )
+ {
+ printf("UpnpNotify failed\n");
+ }
+}
+
+static void
+player_aout_volume_changed( vlc_player_t *player, float new_volume,
+ void *data )
+{
+ (void) player;
+
+ intf_thread_t *p_intf = (intf_thread_t *) data;
+
+ if( new_volume < 0.0 )
+ new_volume = 0.0;
+ else if( new_volume > 2.0 )
+ new_volume = 2.0;
+
+ /* Volume in range [0, 100] */
+ std::string v = std::to_string( std::round( new_volume * 50 ) );
+
+ const char *event_keys[1] = { "Volume" };
+ const char *event_values[1] = { v.c_str() };
+
+ std::string event_xml = build_event_xml( event_keys, event_values, 1 );
+std::cout << event_xml << std::endl;
+
+ const char *var_keys[1] = { "LastChange" };
+ const char *var_values[1] = { event_xml.c_str() };
+
+ int ret = UpnpNotify( p_intf->p_sys->p_upnp->device_handle(),
+ UPNP_UDN,
+ SRV_RC,
+ (const char **) &var_keys,
+ (const char **) &var_values,
+ 1 );
+ if ( ret != UPNP_E_SUCCESS )
+ {
+ printf("UpnpNotify failed\n");
+ }
+}
+
+int OpenControl( vlc_object_t *p_this )
+{
+ intf_thread_t *p_intf = (intf_thread_t *)p_this;
+
+ p_intf->p_sys = (intf_sys_t *)calloc ( 1, sizeof( intf_sys_t ) );
+ if( unlikely(p_intf->p_sys == NULL) )
+ return VLC_ENOMEM;
+
+ p_intf->p_sys->playlist = vlc_intf_GetMainPlaylist( p_intf );
+ if( !p_intf->p_sys->playlist )
+ goto error1;
+
+ p_intf->p_sys->p_upnp = UpnpInstanceWrapper::get( p_this );
+ if( !p_intf->p_sys->p_upnp )
+ goto error1;
+
+ /* Start the UPnP MediaRenderer service */
+ p_intf->p_sys->p_upnp->startMediaRenderer( p_this );
+ try
+ {
+ p_intf->p_sys->p_listener =
+ std::make_shared<DLNA::EventHandler>( p_intf );
+ }
+ catch ( const std::bad_alloc& )
+ {
+ msg_Err( p_this, "Failed to alloc");
+ goto error2;
+ }
+
+ p_intf->p_sys->p_upnp->addListener( p_intf->p_sys->p_listener );
+
+ static struct vlc_player_cbs player_cbs = {};
+ player_cbs.on_state_changed = player_state_changed;
+
+ static struct vlc_player_aout_cbs player_aout_cbs = {};
+ player_aout_cbs.on_volume_changed = player_aout_volume_changed;
+
+ p_intf->p_sys->player = vlc_playlist_GetPlayer( p_intf->p_sys->playlist );
+ if( !p_intf->p_sys->player )
+ goto error2;
+
+ vlc_playlist_Lock( p_intf->p_sys->playlist );
+
+ p_intf->p_sys->p_player_listener =
+ vlc_player_AddListener( p_intf->p_sys->player, &player_cbs, p_intf );
+ if ( !p_intf->p_sys->p_player_listener )
+ goto error3;
+
+ p_intf->p_sys->p_player_aout_listener =
+ vlc_player_aout_AddListener( p_intf->p_sys->player, &player_aout_cbs,
+ p_intf );
+ if ( !p_intf->p_sys->p_player_aout_listener )
+ goto error4;
+
+ vlc_playlist_Unlock( p_intf->p_sys->playlist );
+
+ msg_Info( p_this, "Started MediaRenderer service");
+
+ return VLC_SUCCESS;
+
+error4:
+ vlc_playlist_Lock( p_intf->p_sys->playlist );
+ vlc_player_RemoveListener( p_intf->p_sys->player,
+ p_intf->p_sys->p_player_listener );
+ vlc_playlist_Unlock( p_intf->p_sys->playlist );
+
+error3:
+ p_intf->p_sys->p_upnp->removeListener( p_intf->p_sys->p_listener );
+
+error2:
+ p_intf->p_sys->p_upnp->release();
+
+error1:
+ free( p_intf->p_sys );
+
+ return VLC_EGENERIC;
+}
+
+void CloseControl( vlc_object_t *p_this )
+{
+ intf_thread_t *p_intf = (intf_thread_t *)p_this;
+
+ /* Remove player listeners */
+ vlc_playlist_Lock( p_intf->p_sys->playlist );
+ vlc_player_aout_RemoveListener( p_intf->p_sys->player,
+ p_intf->p_sys->p_player_aout_listener );
+ vlc_player_RemoveListener( p_intf->p_sys->player,
+ p_intf->p_sys->p_player_listener );
+ vlc_playlist_Unlock( p_intf->p_sys->playlist );
+
+ /* Remove UPnP listener */
+ p_intf->p_sys->p_upnp->removeListener( p_intf->p_sys->p_listener );
+
+ /* Stop the UPnP MediaRenderer service */
+ p_intf->p_sys->p_upnp->stopMediaRenderer( p_this );
+
+ p_intf->p_sys->p_upnp->release();
+
+ free( p_intf->p_sys );
+
+ msg_Info( p_this, "Stopped MediaRenderer service");
+}
+
+} // namespace DLNA
diff --git a/modules/control/dlna.hpp b/modules/control/dlna.hpp
new file mode 100644
index 0000000000..c6ee6439a8
--- /dev/null
+++ b/modules/control/dlna.hpp
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * dlna.cpp : DLNA MediaRenderer module
+ *****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * Authors: Johan Gunnarsson <johan.gunnarsson at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef CONTROL_DLNA_H
+#define CONTROL_DLNA_H
+
+#include "../services_discovery/upnp-wrapper.hpp"
+
+#include <vlc_plugin.h>
+#include <vlc_interface.h>
+
+namespace DLNA
+{
+
+int OpenControl( vlc_object_t *p_this );
+void CloseControl( vlc_object_t *p_this );
+
+class EventHandler : public UpnpInstanceWrapper::Listener
+{
+public:
+ EventHandler( intf_thread_t *_p_intf )
+ : p_intf( _p_intf )
+ {
+ }
+
+ ~EventHandler()
+ {
+ }
+
+ int onEvent( Upnp_EventType event_type,
+ UpnpEventPtr p_event,
+ void *p_user_data ) override;
+
+private:
+ intf_thread_t *p_intf = NULL;
+
+ int onActionRequest( UpnpActionRequest *p_event,
+ void *p_user_data );
+
+ int onGetVarRequest( UpnpStateVarRequest *p_event,
+ void *p_user_data );
+
+ int onSubscriptionRequest( UpnpSubscriptionRequest *p_event,
+ void *p_user_data );
+};
+
+} // namespace DLNA
+
+#endif
diff --git a/modules/services_discovery/Makefile.am b/modules/services_discovery/Makefile.am
index 72c21efb56..65af52edf8 100644
--- a/modules/services_discovery/Makefile.am
+++ b/modules/services_discovery/Makefile.am
@@ -31,7 +31,9 @@ libupnp_plugin_la_SOURCES = services_discovery/upnp.cpp services_discovery/upnp.
stream_out/dlna/profile_names.hpp \
stream_out/dlna/dlna_common.hpp \
stream_out/dlna/dlna.hpp \
- stream_out/dlna/dlna.cpp
+ stream_out/dlna/dlna.cpp \
+ control/dlna.hpp \
+ control/dlna.cpp
libupnp_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(UPNP_CFLAGS)
libupnp_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)'
libupnp_plugin_la_LIBADD = $(UPNP_LIBS)
diff --git a/modules/services_discovery/upnp-wrapper.cpp b/modules/services_discovery/upnp-wrapper.cpp
index c001492f37..9f3748ec21 100644
--- a/modules/services_discovery/upnp-wrapper.cpp
+++ b/modules/services_discovery/upnp-wrapper.cpp
@@ -36,19 +36,65 @@
#include "upnp-wrapper.hpp"
#include <vlc_cxx_helpers.hpp>
+static const char *mediarenderer_desc =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
+ "<specVersion>"
+ "<major>1</major>"
+ "<minor>0</minor>"
+ "</specVersion>"
+ "<device>"
+ "<deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType>"
+ "<friendlyName>VLC media player</friendlyName>" /* TODO: include hostname */
+ "<manufacturer>VideoLAN</manufacturer>"
+ "<modelName>" PACKAGE_NAME "</modelName>"
+ "<modelNumber>" PACKAGE_VERSION "</modelNumber>"
+ "<modelURL>https://www.videolan.org/vlc/</modelURL>"
+ "<UDN>" UPNP_UDN "</UDN>" /* TODO: generate at each startup */
+ "<serviceList>"
+ "<service>"
+ "<serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType>"
+ "<serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId>"
+ "<SCPDURL>/RenderingControlSCPD.xml</SCPDURL>"
+ "<controlURL>/upnp/control/RenderingControl</controlURL>"
+ "<eventSubURL>/upnp/event/RenderingControl</eventSubURL>"
+ "</service>"
+ "<service>"
+ "<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>"
+ "<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>"
+ "<SCPDURL>/ConnectionManagerSCPD.xml</SCPDURL>"
+ "<controlURL>/upnp/control/ConnectionManager</controlURL>"
+ "<eventSubURL>/upnp/event/ConnectionManager</eventSubURL>"
+ "</service>"
+ "<service>"
+ "<serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>"
+ "<serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>"
+ "<SCPDURL>/AVTransportSCPD.xml</SCPDURL>"
+ "<controlURL>/upnp/control/AVTransport</controlURL>"
+ "<eventSubURL>/upnp/event/AVTransport</eventSubURL>"
+ "</service>"
+ "</serviceList>"
+ "</device>"
+ "</root>";
+
UpnpInstanceWrapper* UpnpInstanceWrapper::s_instance;
UpnpInstanceWrapper::Listeners UpnpInstanceWrapper::s_listeners;
vlc_mutex_t UpnpInstanceWrapper::s_lock = VLC_STATIC_MUTEX;
UpnpInstanceWrapper::UpnpInstanceWrapper()
- : m_handle( -1 )
+ : m_client_handle( -1 )
+ , m_device_handle( -1 )
, m_refcount( 0 )
+ , m_mediarenderer_refcount( 0 )
{
}
UpnpInstanceWrapper::~UpnpInstanceWrapper()
{
- UpnpUnRegisterClient( m_handle );
+ if( m_client_handle > 0 )
+ UpnpUnRegisterClient( m_client_handle );
+ if( m_device_handle > 0 )
+ UpnpUnRegisterRootDevice( m_device_handle );
UpnpFinish();
}
@@ -86,7 +132,7 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
ixmlRelaxParser( 1 );
/* Register a control point */
- i_res = UpnpRegisterClient( Callback, NULL, &instance->m_handle );
+ i_res = UpnpRegisterClient( Callback, NULL, &instance->m_client_handle );
if( i_res != UPNP_E_SUCCESS )
{
msg_Err( p_obj, "Client registration failed: %s", UpnpGetErrorMessage( i_res ) );
@@ -104,6 +150,15 @@ UpnpInstanceWrapper *UpnpInstanceWrapper::get(vlc_object_t *p_obj)
delete instance;
return NULL;
}
+
+ char *root = config_GetSysPath( VLC_PKG_DATA_DIR, "upnp" );
+ if( (i_res = UpnpSetWebServerRootDir( root )) != UPNP_E_SUCCESS)
+ {
+ msg_Warn( p_obj, "UpnpSetWebServerRootDir failed: %s",
+ UpnpGetErrorMessage( i_res ));
+ }
+ free( root );
+
s_instance = instance;
}
s_instance->m_refcount++;
@@ -123,9 +178,14 @@ void UpnpInstanceWrapper::release()
delete p_delete;
}
-UpnpClient_Handle UpnpInstanceWrapper::handle() const
+UpnpClient_Handle UpnpInstanceWrapper::client_handle() const
+{
+ return m_client_handle;
+}
+
+UpnpDevice_Handle UpnpInstanceWrapper::device_handle() const
{
- return m_handle;
+ return m_device_handle;
}
int UpnpInstanceWrapper::Callback(Upnp_EventType event_type, UpnpEventPtr p_event, void *p_user_data)
@@ -153,3 +213,50 @@ void UpnpInstanceWrapper::removeListener(ListenerPtr listener)
if ( iter != s_listeners.end() )
s_listeners.erase( iter );
}
+
+void UpnpInstanceWrapper::startMediaRenderer( vlc_object_t *p_obj )
+{
+ vlc::threads::mutex_locker lock( &s_lock );
+ if( m_mediarenderer_refcount == 0 )
+ {
+ int i_res;
+ if( (i_res = UpnpEnableWebserver( TRUE )) != UPNP_E_SUCCESS)
+ {
+ msg_Err( p_obj, "Failed to enable webserver: %s", UpnpGetErrorMessage( i_res ) );
+ return;
+ }
+ i_res = UpnpRegisterRootDevice2( UPNPREG_BUF_DESC,
+ mediarenderer_desc,
+ strlen(mediarenderer_desc),
+ 1,
+ Callback,
+ NULL,
+ &m_device_handle );
+ if( i_res != UPNP_E_SUCCESS )
+ {
+ msg_Err( p_obj, "Device registration failed: %s", UpnpGetErrorMessage( i_res ) );
+ }
+ }
+ m_mediarenderer_refcount++;
+}
+
+void UpnpInstanceWrapper::stopMediaRenderer( vlc_object_t *p_obj )
+{
+ vlc::threads::mutex_locker lock( &s_lock );
+ m_mediarenderer_refcount--;
+ if( m_mediarenderer_refcount == 0 )
+ {
+ int i_res;
+ i_res = UpnpUnRegisterRootDevice( m_device_handle );
+ if( i_res != UPNP_E_SUCCESS )
+ {
+ msg_Err( p_obj, "Device unregistration failed: %s", UpnpGetErrorMessage( i_res ) );
+ }
+ m_device_handle = -1;
+ i_res = UpnpEnableWebserver( FALSE );
+ if( i_res != UPNP_E_SUCCESS )
+ {
+ msg_Warn( p_obj, "Failed to disable webserver: %s", UpnpGetErrorMessage( i_res ) );
+ }
+ }
+}
diff --git a/modules/services_discovery/upnp-wrapper.hpp b/modules/services_discovery/upnp-wrapper.hpp
index 3002599172..e0e4652b08 100644
--- a/modules/services_discovery/upnp-wrapper.hpp
+++ b/modules/services_discovery/upnp-wrapper.hpp
@@ -41,6 +41,8 @@
#include <upnp.h>
#include <upnptools.h>
+#define UPNP_UDN "uuid:034fc8dc-ec22-44e5-a79b-38c935f11663"
+
#if UPNP_VERSION < 10800
typedef void* UpnpEventPtr;
#else
@@ -70,8 +72,10 @@ public:
private:
static UpnpInstanceWrapper* s_instance;
static vlc_mutex_t s_lock;
- UpnpClient_Handle m_handle;
+ UpnpClient_Handle m_client_handle;
+ UpnpClient_Handle m_device_handle;
int m_refcount;
+ int m_mediarenderer_refcount;
typedef std::shared_ptr<Listener> ListenerPtr;
typedef std::vector<ListenerPtr> Listeners;
static Listeners s_listeners;
@@ -80,9 +84,12 @@ public:
// This increases the refcount before returning the instance
static UpnpInstanceWrapper* get( vlc_object_t* p_obj );
void release();
- UpnpClient_Handle handle() const;
+ UpnpClient_Handle client_handle() const;
+ UpnpDevice_Handle device_handle() const;
void addListener(ListenerPtr listener);
void removeListener(ListenerPtr listener);
+ void startMediaRenderer( vlc_object_t *p_obj );
+ void stopMediaRenderer( vlc_object_t *p_obj );
private:
static int Callback( Upnp_EventType event_type, UpnpEventPtr p_event, void* p_user_data );
diff --git a/modules/services_discovery/upnp.cpp b/modules/services_discovery/upnp.cpp
index a467caaaa8..310969f908 100644
--- a/modules/services_discovery/upnp.cpp
+++ b/modules/services_discovery/upnp.cpp
@@ -180,6 +180,13 @@ vlc_module_begin()
add_string(SOUT_CFG_PREFIX "base_url", NULL, BASE_URL_TEXT, BASE_URL_LONGTEXT, false)
add_string(SOUT_CFG_PREFIX "url", NULL, URL_TEXT, URL_LONGTEXT, false)
add_renderer_opts(SOUT_CFG_PREFIX)
+
+ add_submodule()
+ set_description( N_("UPnP/DLNA MediaRenderer") )
+ set_category( CAT_INTERFACE )
+ set_subcategory( SUBCAT_INTERFACE_CONTROL )
+ set_callbacks( DLNA::OpenControl, DLNA::CloseControl )
+ set_capability( "interface", 0 )
vlc_module_end()
/*
@@ -271,7 +278,7 @@ SearchThread( void *p_data )
services_discovery_sys_t *p_sys = reinterpret_cast<services_discovery_sys_t *>( p_sd->p_sys );
/* Search for media servers */
- int i_res = UpnpSearchAsync( p_sys->p_upnp->handle(), 5,
+ int i_res = UpnpSearchAsync( p_sys->p_upnp->client_handle(), 5,
MEDIA_SERVER_DEVICE_TYPE, MEDIA_SERVER_DEVICE_TYPE );
if( i_res != UPNP_E_SUCCESS )
{
@@ -280,7 +287,7 @@ SearchThread( void *p_data )
}
/* Search for Sat Ip servers*/
- i_res = UpnpSearchAsync( p_sys->p_upnp->handle(), 5,
+ i_res = UpnpSearchAsync( p_sys->p_upnp->client_handle(), 5,
SATIP_SERVER_DEVICE_TYPE, MEDIA_SERVER_DEVICE_TYPE );
if( i_res != UPNP_E_SUCCESS )
msg_Err( p_sd, "Error sending search request: %s", UpnpGetErrorMessage( i_res ) );
@@ -1249,7 +1256,7 @@ IXML_Document* MediaServer::_browseAction( const char* psz_object_id_,
/* Setup an interruptible callback that will call sendActionCb if not
* interrupted by vlc_interrupt_kill */
i11eCb = new Upnp_i11e_cb( sendActionCb, &p_response );
- i_res = UpnpSendActionAsync( sys->p_upnp->handle(),
+ i_res = UpnpSendActionAsync( sys->p_upnp->client_handle(),
m_psz_root,
CONTENT_DIRECTORY_SERVICE_TYPE,
NULL, /* ignored in SDK, must be NULL */
@@ -1587,7 +1594,7 @@ void *SearchThread(void *data)
renderer_discovery_sys_t *p_sys = (renderer_discovery_sys_t*)p_rd->p_sys;
int i_res;
- i_res = UpnpSearchAsync(p_sys->p_upnp->handle(), UPNP_SEARCH_TIMEOUT_SECONDS,
+ i_res = UpnpSearchAsync(p_sys->p_upnp->client_handle(), UPNP_SEARCH_TIMEOUT_SECONDS,
MEDIA_RENDERER_DEVICE_TYPE, MEDIA_RENDERER_DEVICE_TYPE);
if( i_res != UPNP_E_SUCCESS )
{
diff --git a/modules/services_discovery/upnp.hpp b/modules/services_discovery/upnp.hpp
index 4eef534f91..ca527af250 100644
--- a/modules/services_discovery/upnp.hpp
+++ b/modules/services_discovery/upnp.hpp
@@ -29,6 +29,7 @@
#include "upnp-wrapper.hpp"
#include "../stream_out/dlna/dlna_common.hpp"
+#include "../control/dlna.hpp"
#include <vlc_url.h>
#include <vlc_interrupt.h>
diff --git a/modules/stream_out/dlna/dlna.hpp b/modules/stream_out/dlna/dlna.hpp
index 128ee1c423..6bda6a7587 100644
--- a/modules/stream_out/dlna/dlna.hpp
+++ b/modules/stream_out/dlna/dlna.hpp
@@ -76,7 +76,7 @@ public:
: parent(p_stream)
, base_url(base_url)
, device_url(device_url)
- , handle(upnp->handle())
+ , handle(upnp->client_handle())
, ConnectionID("0")
, AVTransportID("0")
, RcsID("0")
diff --git a/share/Makefile.am b/share/Makefile.am
index e5b801a5be..3c2b34641b 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -67,6 +67,11 @@ if KDE_SOLID
soliddata_DATA = $(DIST_solid)
endif
+nobase_dist_pkgdata_DATA += \
+ upnp/AVTransportSCPD.xml \
+ upnp/ConnectionManagerSCPD.xml \
+ upnp/RenderingControlSCPD.xml
+
DIST_icons = \
vlc512x512.png
diff --git a/share/upnp/AVTransportSCPD.xml b/share/upnp/AVTransportSCPD.xml
new file mode 100644
index 0000000000..fbb75f9978
--- /dev/null
+++ b/share/upnp/AVTransportSCPD.xml
@@ -0,0 +1,585 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <actionList>
+ <action>
+ <name>SetAVTransportURI</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURI</name>
+ <direction>in</direction>
+ <relatedStateVariable>AVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURIMetaData</name>
+ <direction>in</direction>
+ <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetNextAVTransportURI</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURI</name>
+ <direction>in</direction>
+ <relatedStateVariable>NextAVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURIMetaData</name>
+ <direction>in</direction>
+ <relatedStateVariable>NextAVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetMediaInfo</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NrTracks</name>
+ <direction>out</direction>
+ <relatedStateVariable>NumberOfTracks</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MediaDuration</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentMediaDuration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>AVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURIMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>NextAVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURIMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>NextAVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMedium</name>
+ <direction>out</direction>
+ <relatedStateVariable>PlaybackStorageMedium</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecordMedium</name>
+ <direction>out</direction>
+ <relatedStateVariable>RecordStorageMedium</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>WriteStatus</name>
+ <direction>out</direction>
+ <relatedStateVariable>RecordMediumWriteStatus</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetTransportInfo</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentTransportState</name>
+ <direction>out</direction>
+ <relatedStateVariable>TransportState</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentTransportStatus</name>
+ <direction>out</direction>
+ <relatedStateVariable>TransportStatus</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentSpeed</name>
+ <direction>out</direction>
+ <relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetPositionInfo</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Track</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrack</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TrackDuration</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrackDuration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TrackMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrackMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TrackURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrackURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RelTime</name>
+ <direction>out</direction>
+ <relatedStateVariable>RelativeTimePosition</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AbsTime</name>
+ <direction>out</direction>
+ <relatedStateVariable>AbsoluteTimePosition</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RelCount</name>
+ <direction>out</direction>
+ <relatedStateVariable>RelativeCounterPosition</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AbsCount</name>
+ <direction>out</direction>
+ <relatedStateVariable>AbsoluteCounterPosition</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetDeviceCapabilities</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMedia</name>
+ <direction>out</direction>
+ <relatedStateVariable>PossiblePlaybackStorageMedia</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecMedia</name>
+ <direction>out</direction>
+ <relatedStateVariable>PossibleRecordStorageMedia</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecQualityModes</name>
+ <direction>out</direction>
+ <relatedStateVariable>PossibleRecordQualityModes</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetTransportSettings</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMode</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentPlayMode</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecQualityMode</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentRecordQualityMode</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Stop</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Play</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Speed</name>
+ <direction>in</direction>
+ <relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Pause</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Seek</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Unit</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_SeekMode</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Target</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_SeekTarget</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Next</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>Previous</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetPlayMode</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NewPlayMode</name>
+ <direction>in</direction>
+ <relatedStateVariable>CurrentPlayMode</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetCurrentTransportActions</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Actions</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTransportActions</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+ <serviceStateTable>
+ <stateVariable sendEvents="no">
+ <name>TransportState</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>STOPPED</allowedValue>
+ <allowedValue>PAUSED_PLAYBACK</allowedValue>
+ <allowedValue>PAUSED_RECORDING</allowedValue>
+ <allowedValue>PLAYING</allowedValue>
+ <allowedValue>RECORDING</allowedValue>
+ <allowedValue>TRANSITIONING</allowedValue>
+ <allowedValue>NO_MEDIA_PRESENT</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>TransportStatus</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>OK</allowedValue>
+ <allowedValue>ERROR_OCCURRED</allowedValue>
+ <allowedValue>vendor-defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>PlaybackStorageMedium</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>UNKNOWN</allowedValue>
+ <allowedValue>DV</allowedValue>
+ <allowedValue>MINI-DV</allowedValue>
+ <allowedValue>VHS</allowedValue>
+ <allowedValue>W-VHS</allowedValue>
+ <allowedValue>S-VHS</allowedValue>
+ <allowedValue>D-VHS</allowedValue>
+ <allowedValue>VHSC</allowedValue>
+ <allowedValue>VIDEO8</allowedValue>
+ <allowedValue>HI8</allowedValue>
+ <allowedValue>CD-ROM</allowedValue>
+ <allowedValue>CD-DA</allowedValue>
+ <allowedValue>CD-R</allowedValue>
+ <allowedValue>CD-RW</allowedValue>
+ <allowedValue>VIDEO-CD</allowedValue>
+ <allowedValue>SACD</allowedValue>
+ <allowedValue>MD-AUDIO</allowedValue>
+ <allowedValue>MD-PICTURE</allowedValue>
+ <allowedValue>DVD-ROM</allowedValue>
+ <allowedValue>DVD-VIDEO</allowedValue>
+ <allowedValue>DVD-R</allowedValue>
+ <allowedValue>DVD+RW</allowedValue>
+ <allowedValue>DVD-RW</allowedValue>
+ <allowedValue>DVD-RAM</allowedValue>
+ <allowedValue>DVD-AUDIO</allowedValue>
+ <allowedValue>DAT</allowedValue>
+ <allowedValue>LD</allowedValue>
+ <allowedValue>HDD</allowedValue>
+ <allowedValue>MICRO-MV</allowedValue>
+ <allowedValue>NETWORK</allowedValue>
+ <allowedValue>NONE</allowedValue>
+ <allowedValue>NOT_IMPLEMENTED</allowedValue>
+ <allowedValue>vendor-defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>RecordStorageMedium</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>UNKNOWN</allowedValue>
+ <allowedValue>DV</allowedValue>
+ <allowedValue>MINI-DV</allowedValue>
+ <allowedValue>VHS</allowedValue>
+ <allowedValue>W-VHS</allowedValue>
+ <allowedValue>S-VHS</allowedValue>
+ <allowedValue>D-VHS</allowedValue>
+ <allowedValue>VHSC</allowedValue>
+ <allowedValue>VIDEO8</allowedValue>
+ <allowedValue>HI8</allowedValue>
+ <allowedValue>CD-ROM</allowedValue>
+ <allowedValue>CD-DA</allowedValue>
+ <allowedValue>CD-R</allowedValue>
+ <allowedValue>CD-RW</allowedValue>
+ <allowedValue>VIDEO-CD</allowedValue>
+ <allowedValue>SACD</allowedValue>
+ <allowedValue>MD-AUDIO</allowedValue>
+ <allowedValue>MD-PICTURE</allowedValue>
+ <allowedValue>DVD-ROM</allowedValue>
+ <allowedValue>DVD-VIDEO</allowedValue>
+ <allowedValue>DVD-R</allowedValue>
+ <allowedValue>DVD+RW</allowedValue>
+ <allowedValue>DVD-RW</allowedValue>
+ <allowedValue>DVD-RAM</allowedValue>
+ <allowedValue>DVD-AUDIO</allowedValue>
+ <allowedValue>DAT</allowedValue>
+ <allowedValue>LD</allowedValue>
+ <allowedValue>HDD</allowedValue>
+ <allowedValue>MICRO-MV</allowedValue>
+ <allowedValue>NETWORK</allowedValue>
+ <allowedValue>NONE</allowedValue>
+ <allowedValue>NOT_IMPLEMENTED</allowedValue>
+ <allowedValue>vendor-defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>PossiblePlaybackStorageMedia</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>PossibleRecordStorageMedia</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentPlayMode</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>NORMAL</allowedValue>
+ <allowedValue>SHUFFLE</allowedValue>
+ <allowedValue>REPEAT_ONE</allowedValue>
+ <allowedValue>REPEAT_ALL</allowedValue>
+ <allowedValue>RANDOM</allowedValue>
+ <allowedValue>DIRECT_1</allowedValue>
+ <allowedValue>INTRO</allowedValue>
+ </allowedValueList>
+ <defaultValue>NORMAL</defaultValue>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>TransportPlaySpeed</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>1</allowedValue>
+ <allowedValue>vendor-defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>RecordMediumWriteStatus</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>WRITABLE</allowedValue>
+ <allowedValue>PROTECTED</allowedValue>
+ <allowedValue>NOT_WRITABLE</allowedValue>
+ <allowedValue>UNKNOWN</allowedValue>
+ <allowedValue>NOT_IMPLEMENTED</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentRecordQualityMode</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>0:EP</allowedValue>
+ <allowedValue>1:LP</allowedValue>
+ <allowedValue>2:SP</allowedValue>
+ <allowedValue>0:BASIC</allowedValue>
+ <allowedValue>1:MEDIUM</allowedValue>
+ <allowedValue>2:HIGH</allowedValue>
+ <allowedValue>NOT_IMPLEMENTED</allowedValue>
+ <allowedValue>vendor-defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>PossibleRecordQualityModes</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>NumberOfTracks</name>
+ <dataType>ui4</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>vendor-defined</maximum>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentTrack</name>
+ <dataType>ui4</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>vendor-defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentTrackDuration</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentMediaDuration</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentTrackMetaData</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentTrackURI</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>AVTransportURI</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>AVTransportURIMetaData</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>NextAVTransportURI</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>NextAVTransportURIMetaData</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>RelativeTimePosition</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>AbsoluteTimePosition</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>RelativeCounterPosition</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>AbsoluteCounterPosition</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>CurrentTransportActions</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>LastChange</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_SeekMode</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>ABS_TIME</allowedValue>
+ <allowedValue>REL_TIME</allowedValue>
+ <allowedValue>ABS_COUNT</allowedValue>
+ <allowedValue>REL_COUNT</allowedValue>
+ <allowedValue>TRACK_NR</allowedValue>
+ <allowedValue>CHANNEL_FREQ</allowedValue>
+ <allowedValue>TAPE-INDEX</allowedValue>
+ <allowedValue>FRAME</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_SeekTarget</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_InstanceID</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+ </serviceStateTable>
+</scpd>
diff --git a/share/upnp/ConnectionManagerSCPD.xml b/share/upnp/ConnectionManagerSCPD.xml
new file mode 100644
index 0000000000..77c835bebe
--- /dev/null
+++ b/share/upnp/ConnectionManagerSCPD.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <actionList>
+ <action>
+ <name>GetProtocolInfo</name>
+ <argumentList>
+ <argument>
+ <name>Source</name>
+ <direction>out</direction>
+ <relatedStateVariable>SourceProtocolInfo</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Sink</name>
+ <direction>out</direction>
+ <relatedStateVariable>SinkProtocolInfo</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>PrepareForConnection</name>
+ <argumentList>
+ <argument>
+ <name>RemoteProtocolInfo</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PeerConnectionManager</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PeerConnectionID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Direction</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ConnectionID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AVTransportID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RcsID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>ConnectionComplete</name>
+ <argumentList>
+ <argument>
+ <name>ConnectionID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetCurrentConnectionIDs</name>
+ <argumentList>
+ <argument>
+ <name>ConnectionIDs</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetCurrentConnectionInfo</name>
+ <argumentList>
+ <argument>
+ <name>ConnectionID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RcsID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AVTransportID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ProtocolInfo</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PeerConnectionManager</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PeerConnectionID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Direction</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Status</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+ <serviceStateTable>
+ <stateVariable sendEvents="yes">
+ <name>SourceProtocolInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>SinkProtocolInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>CurrentConnectionIDs</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ConnectionStatus</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>OK</allowedValue>
+ <allowedValue>ContentFormatMismatch</allowedValue>
+ <allowedValue>InsufficientBandwidth</allowedValue>
+ <allowedValue>UnreliableChannel</allowedValue>
+ <allowedValue>Unknown</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ConnectionManager</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Direction</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>Input</allowedValue>
+ <allowedValue>Output</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ProtocolInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ConnectionID</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_AVTransportID</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_RcsID</name>
+ <dataType>i4</dataType>
+ </stateVariable>
+ </serviceStateTable>
+</scpd>
diff --git a/share/upnp/RenderingControlSCPD.xml b/share/upnp/RenderingControlSCPD.xml
new file mode 100644
index 0000000000..5c5c0f9a5e
--- /dev/null
+++ b/share/upnp/RenderingControlSCPD.xml
@@ -0,0 +1,762 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <actionList>
+ <action>
+ <name>ListPresets</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentPresetNameList</name>
+ <direction>out</direction>
+ <relatedStateVariable>PresetNameList</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SelectPreset</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PresetName</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_PresetName</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetBrightness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentBrightness</name>
+ <direction>out</direction>
+ <relatedStateVariable>Brightness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetBrightness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredBrightness</name>
+ <direction>in</direction>
+ <relatedStateVariable>Brightness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetContrast</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentContrast</name>
+ <direction>out</direction>
+ <relatedStateVariable>Contrast</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetContrast</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredContrast</name>
+ <direction>in</direction>
+ <relatedStateVariable>Contrast</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetSharpness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentSharpness</name>
+ <direction>out</direction>
+ <relatedStateVariable>Sharpness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetSharpness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredSharpness</name>
+ <direction>in</direction>
+ <relatedStateVariable>Sharpness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetRedVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentRedVideoGain</name>
+ <direction>out</direction>
+ <relatedStateVariable>RedVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetRedVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredRedVideoGain</name>
+ <direction>in</direction>
+ <relatedStateVariable>RedVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetGreenVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentGreenVideoGain</name>
+ <direction>out</direction>
+ <relatedStateVariable>GreenVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetGreenVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredGreenVideoGain</name>
+ <direction>in</direction>
+ <relatedStateVariable>GreenVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetBlueVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentBlueVideoGain</name>
+ <direction>out</direction>
+ <relatedStateVariable>BlueVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetBlueVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredBlueVideoGain</name>
+ <direction>in</direction>
+ <relatedStateVariable>BlueVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetRedVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentRedVideoBlackLevel</name>
+ <direction>out</direction>
+ <relatedStateVariable>RedVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetRedVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredRedVideoBlackLevel</name>
+ <direction>in</direction>
+ <relatedStateVariable>RedVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetGreenVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentGreenVideoBlackLevel</name>
+ <direction>out</direction>
+ <relatedStateVariable>GreenVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetGreenVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredGreenVideoBlackLevel</name>
+ <direction>in</direction>
+ <relatedStateVariable>GreenVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetBlueVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentBlueVideoBlackLevel</name>
+ <direction>out</direction>
+ <relatedStateVariable>BlueVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetBlueVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredBlueVideoBlackLevel</name>
+ <direction>in</direction>
+ <relatedStateVariable>BlueVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetColorTemperature</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentColorTemperature</name>
+ <direction>out</direction>
+ <relatedStateVariable>ColorTemperature</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetColorTemperature</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredColorTemperature</name>
+ <direction>in</direction>
+ <relatedStateVariable>ColorTemperature</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetHorizontalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentHorizontalKeystone</name>
+ <direction>out</direction>
+ <relatedStateVariable>HorizontalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetHorizontalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredHorizontalKeystone</name>
+ <direction>in</direction>
+ <relatedStateVariable>HorizontalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetVerticalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentVerticalKeystone</name>
+ <direction>out</direction>
+ <relatedStateVariable>VerticalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetVerticalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredVerticalKeystone</name>
+ <direction>in</direction>
+ <relatedStateVariable>VerticalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetMute</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentMute</name>
+ <direction>out</direction>
+ <relatedStateVariable>Mute</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetMute</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredMute</name>
+ <direction>in</direction>
+ <relatedStateVariable>Mute</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetVolume</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentVolume</name>
+ <direction>out</direction>
+ <relatedStateVariable>Volume</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetVolume</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredVolume</name>
+ <direction>in</direction>
+ <relatedStateVariable>Volume</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetVolumeDB</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentVolume</name>
+ <direction>out</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetVolumeDB</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredVolume</name>
+ <direction>in</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetVolumeDBRange</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MinValue</name>
+ <direction>out</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MaxValue</name>
+ <direction>out</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>GetLoudness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentLoudness</name>
+ <direction>out</direction>
+ <relatedStateVariable>Loudness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>SetLoudness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredLoudness</name>
+ <direction>in</direction>
+ <relatedStateVariable>Loudness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+ <serviceStateTable>
+ <stateVariable sendEvents="no">
+ <name>PresetNameList</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="yes">
+ <name>LastChange</name>
+ <dataType>string</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>Brightness</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>Contrast</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>Sharpness</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>RedVideoGain</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>GreenVideoGain</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>BlueVideoGain</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>RedVideoBlackLevel</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>GreenVideoBlackLevel</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>BlueVideoBlackLevel</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>ColorTemperature</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>HorizontalKeystone</name>
+ <dataType>i2</dataType>
+ <allowedValueRange>
+ <minimum>Vendor defined (Must be >= 0)</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>VerticalKeystone</name>
+ <dataType>i2</dataType>
+ <allowedValueRange>
+ <minimum>Vendor defined (Must be >= 0)</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>Mute</name>
+ <dataType>boolean</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>Volume</name>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>VolumeDB</name>
+ <dataType>i2</dataType>
+ <allowedValueRange>
+ <minimum>Vendor defined</minimum>
+ <maximum>Vendor defined</maximum>
+ <step>Vendor defined</step>
+ </allowedValueRange>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>Loudness</name>
+ <dataType>boolean</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Channel</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>Master</allowedValue>
+ <allowedValue>LF</allowedValue>
+ <allowedValue>RF</allowedValue>
+ <allowedValue>CF</allowedValue>
+ <allowedValue>LFE</allowedValue>
+ <allowedValue>LS</allowedValue>
+ <allowedValue>RS</allowedValue>
+ <allowedValue>LFC</allowedValue>
+ <allowedValue>RFC</allowedValue>
+ <allowedValue>SD</allowedValue>
+ <allowedValue>SL</allowedValue>
+ <allowedValue>SR</allowedValue>
+ <allowedValue>T</allowedValue>
+ <allowedValue>B</allowedValue>
+ <allowedValue>Vendor defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_InstanceID</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_PresetName</name>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>FactoryDefaults</allowedValue>
+ <allowedValue>InstallationDefaults</allowedValue>
+ <allowedValue>Vendor defined</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+ </serviceStateTable>
+</scpd>
--
2.17.1
More information about the vlc-devel
mailing list