[vlc-devel] [PATCH] VLC as UPnP/DLNA MediaRenderer
Pierre Lamot
pierre at videolabs.io
Wed Jul 10 10:03:16 CEST 2019
Hi,
This patch doesn't compile with the current version libupnp of from the
contribs (1.6.19).
It probably needs to be updated first.
On 2019-07-06 17:13, Johan Gunnarsson wrote:
> 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>
More information about the vlc-devel
mailing list