Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
18f84613
Commit
18f84613
authored
Feb 02, 2006
by
Rémi Denis-Courmont
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add libupnp-based UPnP discovery from Christian Henz
with proper configure checks
parent
9efe16d1
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1198 additions
and
6 deletions
+1198
-6
THANKS
THANKS
+1
-1
configure.ac
configure.ac
+27
-3
modules/services_discovery/Modules.am
modules/services_discovery/Modules.am
+2
-1
modules/services_discovery/upnp_cc.cpp
modules/services_discovery/upnp_cc.cpp
+1
-1
modules/services_discovery/upnp_intel.cpp
modules/services_discovery/upnp_intel.cpp
+1167
-0
No files found.
THANKS
View file @
18f84613
...
...
@@ -30,7 +30,7 @@ Bruno Vella <allevb at tin.it> - Italian localization
Carlo Calabrò <murray at via.ecp.fr> - Italian localization
Carsten Gottbehüt <gottbehuet at active-elements dot de> - v4l hotplug fix
Chris Clepper - OpenGL fix
Christian Henz -
UPnP service discovery
fixes
Christian Henz -
libupnp service discovery plugin, CyberLink UPnP
fixes
Christof Baumgaertner - dbox web intf
Christophe Mutricy <xtophe at nxtelevision dot com> - many fixes (preferences, M3U, ...)
Christopher Johnson <cjohnson at mint.net> - Qt fix in vlc.spec
...
...
configure.ac
View file @
18f84613
...
...
@@ -4092,8 +4092,8 @@ AS_IF([test "${CXX}" != "" -a "${enable_cyberlink}" = "yes" || (test "${enable_c
CPPFLAGS_cyberlink="-I${real_cyberlink_tree}/include"
CPPFLAGS="${CPPFLAGS} ${CPPFLAGS_cyberlink}"
AC_CHECK_HEADERS([cybergarage/upnp/MediaServer.h],
[ VLC_ADD_CXXFLAGS([upnp], [${CPPFLAGS_cyberlink}])
VLC_ADD_PLUGINS([upnp])
[ VLC_ADD_CXXFLAGS([upnp
_cc
], [${CPPFLAGS_cyberlink}])
VLC_ADD_PLUGINS([upnp
_cc
])
],[
AC_MSG_ERROR([cannot find CyberLink for C++ headers])
])
...
...
@@ -4130,7 +4130,7 @@ class testclass : public SearchResponseListener, public MediaPlayer
AS_IF([test "${LIBS_cclink}" == "no"],
[AC_MSG_FAILURE([cannot find XML parser for CyberLink])])
AC_MSG_RESULT([${LIBS_cclink}])
VLC_ADD_LDFLAGS([upnp], [${real_cyberlink_tree}/lib/unix/libclink.a -lpthread ${LIBS_cclink}])
VLC_ADD_LDFLAGS([upnp
_cc
], [${real_cyberlink_tree}/lib/unix/libclink.a -lpthread ${LIBS_cclink}])
], [
AC_MSG_RESULT(no)
AC_MSG_ERROR([cannot find ${real_cyberlink_tree}/lib/unix/libclink.a, make sure you compiled CyberLink for C++ in ${with_cyberlink_tree}])
...
...
@@ -4140,6 +4140,30 @@ class testclass : public SearchResponseListener, public MediaPlayer
])
])
dnl
dnl UPnP Plugin (Intel SDK)
dnl
AC_ARG_ENABLE(upnp,
[ --enable-upnp Intel UPnP SDK (default auto)])
VLC_ADD_CXXFLAGS([upnp_intel], [ ])
AS_IF([test "x${enable_upnp}" != "xno"], [
AC_CHECK_LIB([upnp], [UpnpInit], [has_upnp="yes"], [has_upnp="no"], [-lpthread])
AS_IF([test "x${enable_upnp}" != "x" && test "${has_upnp}" == "no"], [
AC_MSG_ERROR([cannot find Intel UPnP SDK (libupnp)])
])
AS_IF([test "${has_upnp}" == "yes"], [
VLC_ADD_LDFLAGS([upnp_intel], [-lupnp])
])
], [
has_upnp="no"
])
AS_IF([test "${has_upnp}" == "yes"], [
VLC_ADD_PLUGINS([upnp_intel])
])
dnl
dnl Interface plugins
dnl
...
...
modules/services_discovery/Modules.am
View file @
18f84613
...
...
@@ -2,6 +2,7 @@ SOURCES_sap = sap.c
SOURCES_hal = hal.c
SOURCES_daap = daap.c
SOURCES_shout = shout.c
SOURCES_upnp = upnp.cpp
SOURCES_upnp_cc = upnp_cc.cpp
SOURCES_upnp_intel = upnp_intel.cpp
SOURCES_bonjour = bonjour.c
SOURCES_podcast = podcast.c
modules/services_discovery/upnp.cpp
→
modules/services_discovery/upnp
_cc
.cpp
View file @
18f84613
/*****************************************************************************
* upnp.cpp : UPnP discovery module
* upnp
_cc
.cpp : UPnP discovery module
*****************************************************************************
* Copyright (C) 2004-2005 the VideoLAN team
* $Id$
...
...
modules/services_discovery/upnp_intel.cpp
0 → 100644
View file @
18f84613
/*****************************************************************************
* Upnp_intell.cpp : UPnP discovery module (Intel SDK)
*****************************************************************************
* Copyright (C) 2004-2006 the VideoLAN team
* $Id$
*
* Authors: Rémi Denis-Courmont <rem # videolan.org> (original plugin)
* Christian Henz <henz # c-lab.de>
*
* UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*****************************************************************************/
/*
\TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
\TODO: Change names to VLC standard ???
*/
#include <stdlib.h>
#include <vector>
#include <string>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#undef PACKAGE_NAME
#include <vlc/vlc.h>
#include <vlc/intf.h>
// VLC handle
struct
services_discovery_sys_t
{
playlist_item_t
*
p_node
;
playlist_t
*
p_playlist
;
};
// Constants
const
char
*
MEDIA_SERVER_DEVICE_TYPE
=
"urn:schemas-upnp-org:device:MediaServer:1"
;
const
char
*
CONTENT_DIRECTORY_SERVICE_TYPE
=
"urn:schemas-upnp-org:service:ContentDirectory:1"
;
// Classes
class
MediaServer
;
class
MediaServerList
;
class
Item
;
class
Container
;
// Cookie that is passed to the callback
typedef
struct
{
services_discovery_t
*
serviceDiscovery
;
UpnpClient_Handle
clientHandle
;
MediaServerList
*
serverList
;
}
Cookie
;
// Class definitions...
class
Lockable
{
public:
Lockable
(
Cookie
*
c
)
{
vlc_mutex_init
(
c
->
serviceDiscovery
,
&
_mutex
);
}
~
Lockable
()
{
vlc_mutex_destroy
(
&
_mutex
);
}
void
lock
()
{
vlc_mutex_lock
(
&
_mutex
);
}
void
unlock
()
{
vlc_mutex_unlock
(
&
_mutex
);
}
private:
vlc_mutex_t
_mutex
;
};
class
Locker
{
public:
Locker
(
Lockable
*
l
)
{
_lockable
=
l
;
_lockable
->
lock
();
}
~
Locker
()
{
_lockable
->
unlock
();
}
private:
Lockable
*
_lockable
;
};
class
MediaServer
{
public:
static
void
parseDeviceDescription
(
IXML_Document
*
doc
,
const
char
*
location
,
Cookie
*
cookie
);
MediaServer
(
const
char
*
UDN
,
const
char
*
friendlyName
,
Cookie
*
cookie
);
~
MediaServer
();
const
char
*
getUDN
()
const
;
const
char
*
getFriendlyName
()
const
;
void
setContentDirectoryEventURL
(
const
char
*
url
);
const
char
*
getContentDirectoryEventURL
()
const
;
void
setContentDirectoryControlURL
(
const
char
*
url
);
const
char
*
getContentDirectoryControlURL
()
const
;
void
subscribeToContentDirectory
();
void
fetchContents
();
void
setPlaylistNode
(
playlist_item_t
*
node
);
bool
compareSID
(
const
char
*
sid
);
private:
bool
_fetchContents
(
Container
*
parent
);
void
_buildPlaylist
(
Container
*
container
);
IXML_Document
*
_browseAction
(
const
char
*
,
const
char
*
,
const
char
*
,
const
char
*
,
const
char
*
,
const
char
*
);
Cookie
*
_cookie
;
Container
*
_contents
;
playlist_item_t
*
_playlistNode
;
std
::
string
_UDN
;
std
::
string
_friendlyName
;
std
::
string
_contentDirectoryEventURL
;
std
::
string
_contentDirectoryControlURL
;
int
_subscriptionTimeOut
;
Upnp_SID
_subscriptionID
;
};
class
MediaServerList
{
public:
MediaServerList
(
Cookie
*
cookie
);
~
MediaServerList
();
bool
addServer
(
MediaServer
*
s
);
void
removeServer
(
const
char
*
UDN
);
MediaServer
*
getServer
(
const
char
*
UDN
);
MediaServer
*
getServerBySID
(
const
char
*
);
private:
Cookie
*
_cookie
;
std
::
vector
<
MediaServer
*>
_list
;
};
class
Item
{
public:
Item
(
Container
*
parent
,
const
char
*
objectID
,
const
char
*
title
,
const
char
*
resource
);
const
char
*
getObjectID
()
const
;
const
char
*
getTitle
()
const
;
const
char
*
getResource
()
const
;
void
setPlaylistNode
(
playlist_item_t
*
node
);
playlist_item_t
*
getPlaylistNode
()
const
;
private:
playlist_item_t
*
_playlistNode
;
Container
*
_parent
;
std
::
string
_objectID
;
std
::
string
_title
;
std
::
string
_resource
;
};
class
Container
{
public:
Container
(
Container
*
parent
,
const
char
*
objectID
,
const
char
*
title
);
~
Container
();
void
addItem
(
Item
*
item
);
void
addContainer
(
Container
*
container
);
const
char
*
getObjectID
()
const
;
const
char
*
getTitle
()
const
;
unsigned
int
getNumItems
()
const
;
unsigned
int
getNumContainers
()
const
;
Item
*
getItem
(
unsigned
int
i
)
const
;
Container
*
getContainer
(
unsigned
int
i
)
const
;
void
setPlaylistNode
(
playlist_item_t
*
node
);
playlist_item_t
*
getPlaylistNode
()
const
;
private:
playlist_item_t
*
_playlistNode
;
Container
*
_parent
;
std
::
string
_objectID
;
std
::
string
_title
;
std
::
vector
<
Item
*>
_items
;
std
::
vector
<
Container
*>
_containers
;
};
// VLC callback prototypes
static
int
Open
(
vlc_object_t
*
);
static
void
Close
(
vlc_object_t
*
);
static
void
Run
(
services_discovery_t
*
p_sd
);
// Module descriptor
vlc_module_begin
();
set_shortname
(
"UPnP"
);
set_description
(
_
(
"Universal Plug'n'Play discovery ( Intel SDK )"
)
);
set_category
(
CAT_PLAYLIST
);
set_subcategory
(
SUBCAT_PLAYLIST_SD
);
set_capability
(
"services_discovery"
,
0
);
set_callbacks
(
Open
,
Close
);
vlc_module_end
();
// More prototypes...
static
Lockable
*
CallbackLock
;
static
int
Callback
(
Upnp_EventType
eventType
,
void
*
event
,
void
*
pCookie
);
char
*
xml_makeSpecialChars
(
const
char
*
in
);
const
char
*
xml_getChildElementValue
(
IXML_Element
*
parent
,
const
char
*
tagName
);
IXML_Document
*
parseBrowseResult
(
IXML_Document
*
doc
);
// VLC callbacks...
static
int
Open
(
vlc_object_t
*
p_this
)
{
services_discovery_t
*
p_sd
=
(
services_discovery_t
*
)
p_this
;
services_discovery_sys_t
*
p_sys
=
(
services_discovery_sys_t
*
)
malloc
(
sizeof
(
services_discovery_sys_t
)
);
playlist_view_t
*
p_view
;
vlc_value_t
val
;
p_sd
->
pf_run
=
Run
;
p_sd
->
p_sys
=
p_sys
;
/* Create our playlist node */
p_sys
->
p_playlist
=
(
playlist_t
*
)
vlc_object_find
(
p_sd
,
VLC_OBJECT_PLAYLIST
,
FIND_ANYWHERE
);
if
(
!
p_sys
->
p_playlist
)
{
msg_Warn
(
p_sd
,
"unable to find playlist, cancelling UPnP listening"
);
return
VLC_EGENERIC
;
}
p_view
=
playlist_ViewFind
(
p_sys
->
p_playlist
,
VIEW_CATEGORY
);
p_sys
->
p_node
=
playlist_NodeCreate
(
p_sys
->
p_playlist
,
VIEW_CATEGORY
,
"UPnP"
,
p_view
->
p_root
);
p_sys
->
p_node
->
i_flags
|=
PLAYLIST_RO_FLAG
;
p_sys
->
p_node
->
i_flags
&=
~
PLAYLIST_SKIP_FLAG
;
val
.
b_bool
=
VLC_TRUE
;
var_Set
(
p_sys
->
p_playlist
,
"intf-change"
,
val
);
return
VLC_SUCCESS
;
}
static
void
Close
(
vlc_object_t
*
p_this
)
{
services_discovery_t
*
p_sd
=
(
services_discovery_t
*
)
p_this
;
services_discovery_sys_t
*
p_sys
=
p_sd
->
p_sys
;
if
(
p_sys
->
p_playlist
)
{
playlist_NodeDelete
(
p_sys
->
p_playlist
,
p_sys
->
p_node
,
VLC_TRUE
,
VLC_TRUE
);
vlc_object_release
(
p_sys
->
p_playlist
);
}
free
(
p_sys
);
}
static
void
Run
(
services_discovery_t
*
p_sd
)
{
int
res
;
res
=
UpnpInit
(
0
,
0
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
msg_Err
(
p_sd
,
"%s"
,
UpnpGetErrorMessage
(
res
)
);
return
;
}
Cookie
cookie
;
cookie
.
serviceDiscovery
=
p_sd
;
cookie
.
serverList
=
new
MediaServerList
(
&
cookie
);
CallbackLock
=
new
Lockable
(
&
cookie
);
res
=
UpnpRegisterClient
(
Callback
,
&
cookie
,
&
cookie
.
clientHandle
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
msg_Err
(
p_sd
,
"%s"
,
UpnpGetErrorMessage
(
res
)
);
goto
shutDown
;
}
res
=
UpnpSearchAsync
(
cookie
.
clientHandle
,
5
,
MEDIA_SERVER_DEVICE_TYPE
,
&
cookie
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
msg_Err
(
p_sd
,
"%s"
,
UpnpGetErrorMessage
(
res
)
);
goto
shutDown
;
}
msg_Dbg
(
p_sd
,
"UPnP discovery started"
);
while
(
!
p_sd
->
b_die
)
{
msleep
(
500
);
}
msg_Dbg
(
p_sd
,
"UPnP discovery stopped"
);
shutDown:
UpnpFinish
();
delete
cookie
.
serverList
;
delete
CallbackLock
;
}
// XML utility functions:
// Returns the value of a child element, or 0 on error
const
char
*
xml_getChildElementValue
(
IXML_Element
*
parent
,
const
char
*
tagName
)
{
if
(
!
parent
)
return
0
;
if
(
!
tagName
)
return
0
;
char
*
s
=
strdup
(
tagName
);
IXML_NodeList
*
nodeList
=
ixmlElement_getElementsByTagName
(
parent
,
s
);
free
(
s
);
if
(
!
nodeList
)
return
0
;
IXML_Node
*
element
=
ixmlNodeList_item
(
nodeList
,
0
);
ixmlNodeList_free
(
nodeList
);
if
(
!
element
)
return
0
;
IXML_Node
*
textNode
=
ixmlNode_getFirstChild
(
element
);
if
(
!
textNode
)
return
0
;
return
ixmlNode_getNodeValue
(
textNode
);
}
// Replaces "<" with "<" etc.
// Returns a newly created string that has to be freed by the caller.
// Returns 0 on error ( out of mem )
// \TODO: Probably not very robust!!!
char
*
xml_makeSpecialChars
(
const
char
*
in
)
{
if
(
!
in
)
return
0
;
char
*
result
=
(
char
*
)
malloc
(
strlen
(
in
)
+
1
);
if
(
!
result
)
return
0
;
char
*
out
=
result
;
while
(
*
in
)
{
if
(
strncmp
(
"&"
,
in
,
5
)
==
0
)
{
*
out
=
'&'
;
in
+=
5
;
out
++
;
}
else
if
(
strncmp
(
"""
,
in
,
6
)
==
0
)
{
*
out
=
'"'
;
in
+=
6
;
out
++
;
}
else
if
(
strncmp
(
">"
,
in
,
4
)
==
0
)
{
*
out
=
'>'
;
in
+=
4
;
out
++
;
}
else
if
(
strncmp
(
"<"
,
in
,
4
)
==
0
)
{
*
out
=
'<'
;
in
+=
4
;
out
++
;
}
else
{
*
out
=
*
in
;
in
++
;
out
++
;
}
}
*
out
=
'\0'
;
return
result
;
}
// Extracts the result document from a SOAP response
IXML_Document
*
parseBrowseResult
(
IXML_Document
*
doc
)
{
if
(
!
doc
)
return
0
;
IXML_NodeList
*
resultList
=
ixmlDocument_getElementsByTagName
(
doc
,
"Result"
);
if
(
!
resultList
)
return
0
;
IXML_Node
*
resultNode
=
ixmlNodeList_item
(
resultList
,
0
);
ixmlNodeList_free
(
resultList
);
if
(
!
resultNode
)
return
0
;
IXML_Node
*
textNode
=
ixmlNode_getFirstChild
(
resultNode
);
if
(
!
textNode
)
return
0
;
const
char
*
resultString
=
ixmlNode_getNodeValue
(
textNode
);
char
*
resultXML
=
xml_makeSpecialChars
(
resultString
);
IXML_Document
*
browseDoc
=
ixmlParseBuffer
(
resultXML
);
free
(
resultXML
);
return
browseDoc
;
}
// Handles all UPnP events
static
int
Callback
(
Upnp_EventType
eventType
,
void
*
event
,
void
*
pCookie
)
{
Locker
locker
(
CallbackLock
);
Cookie
*
cookie
=
(
Cookie
*
)
pCookie
;
switch
(
eventType
)
{
case
UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
case
UPNP_DISCOVERY_SEARCH_RESULT
:
{
struct
Upnp_Discovery
*
discovery
=
(
struct
Upnp_Discovery
*
)
event
;
IXML_Document
*
descriptionDoc
=
0
;
int
res
;
res
=
UpnpDownloadXmlDoc
(
discovery
->
Location
,
&
descriptionDoc
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: Could not download device description!"
,
__FILE__
,
__LINE__
);
return
res
;
}
MediaServer
::
parseDeviceDescription
(
descriptionDoc
,
discovery
->
Location
,
cookie
);
ixmlDocument_free
(
descriptionDoc
);
}
break
;
case
UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
:
{
struct
Upnp_Discovery
*
discovery
=
(
struct
Upnp_Discovery
*
)
event
;
cookie
->
serverList
->
removeServer
(
discovery
->
DeviceId
);
}
break
;
case
UPNP_EVENT_RECEIVED
:
{
Upnp_Event
*
e
=
(
Upnp_Event
*
)
event
;
MediaServer
*
server
=
cookie
->
serverList
->
getServerBySID
(
e
->
Sid
);
if
(
server
)
server
->
fetchContents
();
}
break
;
case
UPNP_EVENT_AUTORENEWAL_FAILED
:
case
UPNP_EVENT_SUBSCRIPTION_EXPIRED
:
{
// Re-subscribe...
Upnp_Event_Subscribe
*
s
=
(
Upnp_Event_Subscribe
*
)
event
;
MediaServer
*
server
=
cookie
->
serverList
->
getServerBySID
(
s
->
Sid
);
if
(
server
)
server
->
subscribeToContentDirectory
();
}
break
;
default:
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )"
,
__FILE__
,
__LINE__
,
eventType
);
break
;
}
return
UPNP_E_SUCCESS
;
}
// Class implementations...
// MediaServer...
void
MediaServer
::
parseDeviceDescription
(
IXML_Document
*
doc
,
const
char
*
location
,
Cookie
*
cookie
)
{
if
(
!
doc
)
{
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: NULL"
,
__FILE__
,
__LINE__
);
return
;
}
if
(
!
location
)
{
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: NULL"
,
__FILE__
,
__LINE__
);
return
;
}
const
char
*
baseURL
=
location
;
// Try to extract baseURL
IXML_NodeList
*
urlList
=
ixmlDocument_getElementsByTagName
(
doc
,
"baseURL"
);
if
(
urlList
)
{
if
(
IXML_Node
*
urlNode
=
ixmlNodeList_item
(
urlList
,
0
)
)
{
IXML_Node
*
textNode
=
ixmlNode_getFirstChild
(
urlNode
);
if
(
textNode
)
baseURL
=
ixmlNode_getNodeValue
(
textNode
);
}
ixmlNodeList_free
(
urlList
);
}
// Get devices
IXML_NodeList
*
deviceList
=
ixmlDocument_getElementsByTagName
(
doc
,
"device"
);
if
(
deviceList
)
{
for
(
unsigned
int
i
=
0
;
i
<
ixmlNodeList_length
(
deviceList
);
i
++
)
{
IXML_Element
*
deviceElement
=
(
IXML_Element
*
)
ixmlNodeList_item
(
deviceList
,
i
);
const
char
*
deviceType
=
xml_getChildElementValue
(
deviceElement
,
"deviceType"
);
if
(
!
deviceType
)
{
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: no deviceType!"
,
__FILE__
,
__LINE__
);
continue
;
}
if
(
strcmp
(
MEDIA_SERVER_DEVICE_TYPE
,
deviceType
)
!=
0
)
continue
;
const
char
*
UDN
=
xml_getChildElementValue
(
deviceElement
,
"UDN"
);
if
(
!
UDN
)
{
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: no UDN!"
,
__FILE__
,
__LINE__
);
continue
;
}
if
(
cookie
->
serverList
->
getServer
(
UDN
)
!=
0
)
continue
;
const
char
*
friendlyName
=
xml_getChildElementValue
(
deviceElement
,
"friendlyName"
);
if
(
!
friendlyName
)
{
msg_Dbg
(
cookie
->
serviceDiscovery
,
"%s:%d: no friendlyName!"
,
__FILE__
,
__LINE__
);
continue
;
}
MediaServer
*
server
=
new
MediaServer
(
UDN
,
friendlyName
,
cookie
);
if
(
!
cookie
->
serverList
->
addServer
(
server
)
)
{
delete
server
;
server
=
0
;
continue
;
}
// Check for ContentDirectory service...
IXML_NodeList
*
serviceList
=
ixmlElement_getElementsByTagName
(
deviceElement
,
"service"
);
if
(
serviceList
)
{
for
(
unsigned
int
j
=
0
;
j
<
ixmlNodeList_length
(
serviceList
);
j
++
)
{
IXML_Element
*
serviceElement
=
(
IXML_Element
*
)
ixmlNodeList_item
(
serviceList
,
j
);
const
char
*
serviceType
=
xml_getChildElementValue
(
serviceElement
,
"serviceType"
);
if
(
!
serviceType
)
continue
;
if
(
strcmp
(
CONTENT_DIRECTORY_SERVICE_TYPE
,
serviceType
)
!=
0
)
continue
;
const
char
*
eventSubURL
=
xml_getChildElementValue
(
serviceElement
,
"eventSubURL"
);
if
(
!
eventSubURL
)
continue
;
const
char
*
controlURL
=
xml_getChildElementValue
(
serviceElement
,
"controlURL"
);
if
(
!
controlURL
)
continue
;
// Try to subscribe to ContentDirectory service
char
*
url
=
(
char
*
)
malloc
(
strlen
(
baseURL
)
+
strlen
(
eventSubURL
)
+
1
);
if
(
url
)
{
char
*
s1
=
strdup
(
baseURL
);
char
*
s2
=
strdup
(
eventSubURL
);
if
(
UpnpResolveURL
(
s1
,
s2
,
url
)
==
UPNP_E_SUCCESS
)
{
// msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
server
->
setContentDirectoryEventURL
(
url
);
server
->
subscribeToContentDirectory
();
}
free
(
s1
);
free
(
s2
);
free
(
url
);
}
// Try to browse content directory...
url
=
(
char
*
)
malloc
(
strlen
(
baseURL
)
+
strlen
(
controlURL
)
+
1
);
if
(
url
)
{
char
*
s1
=
strdup
(
baseURL
);
char
*
s2
=
strdup
(
controlURL
);
if
(
UpnpResolveURL
(
s1
,
s2
,
url
)
==
UPNP_E_SUCCESS
)
{
// msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
server
->
setContentDirectoryControlURL
(
url
);
server
->
fetchContents
();
}
free
(
s1
);
free
(
s2
);
free
(
url
);
}
}
ixmlNodeList_free
(
serviceList
);
}
}
ixmlNodeList_free
(
deviceList
);
}
}
MediaServer
::
MediaServer
(
const
char
*
UDN
,
const
char
*
friendlyName
,
Cookie
*
cookie
)
{
_cookie
=
cookie
;
_UDN
=
UDN
;
_friendlyName
=
friendlyName
;
_contents
=
0
;
_playlistNode
=
0
;
}
MediaServer
::~
MediaServer
()
{
if
(
_contents
)
{
playlist_NodeDelete
(
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
,
_playlistNode
,
true
,
true
);
}
delete
_contents
;
}
const
char
*
MediaServer
::
getUDN
()
const
{
const
char
*
s
=
_UDN
.
c_str
();
return
s
;
}
const
char
*
MediaServer
::
getFriendlyName
()
const
{
const
char
*
s
=
_friendlyName
.
c_str
();
return
s
;
}
void
MediaServer
::
setContentDirectoryEventURL
(
const
char
*
url
)
{
_contentDirectoryEventURL
=
url
;
}
const
char
*
MediaServer
::
getContentDirectoryEventURL
()
const
{
const
char
*
s
=
_contentDirectoryEventURL
.
c_str
();
return
s
;
}
void
MediaServer
::
setContentDirectoryControlURL
(
const
char
*
url
)
{
_contentDirectoryControlURL
=
url
;
}
const
char
*
MediaServer
::
getContentDirectoryControlURL
()
const
{
return
_contentDirectoryControlURL
.
c_str
();
}
void
MediaServer
::
subscribeToContentDirectory
()
{
const
char
*
url
=
getContentDirectoryEventURL
();
if
(
!
url
||
strcmp
(
url
,
""
)
==
0
)
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"No subscription url set!"
);
return
;
}
int
timeOut
=
1810
;
Upnp_SID
sid
;
int
res
=
UpnpSubscribe
(
_cookie
->
clientHandle
,
url
,
&
timeOut
,
sid
);
if
(
res
==
UPNP_E_SUCCESS
)
{
_subscriptionTimeOut
=
timeOut
;
memcpy
(
_subscriptionID
,
sid
,
sizeof
(
Upnp_SID
)
);
}
else
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"%s:%d: WARNING: '%s': %s"
,
__FILE__
,
__LINE__
,
getFriendlyName
(),
UpnpGetErrorMessage
(
res
)
);
}
}
IXML_Document
*
MediaServer
::
_browseAction
(
const
char
*
pObjectID
,
const
char
*
pBrowseFlag
,
const
char
*
pFilter
,
const
char
*
pStartingIndex
,
const
char
*
pRequestedCount
,
const
char
*
pSortCriteria
)
{
IXML_Document
*
action
=
0
;
IXML_Document
*
response
=
0
;
const
char
*
url
=
getContentDirectoryControlURL
();
if
(
!
url
||
strcmp
(
url
,
""
)
==
0
)
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"No subscription url set!"
);
return
0
;
}
char
*
ObjectID
=
strdup
(
pObjectID
);
char
*
BrowseFlag
=
strdup
(
pBrowseFlag
);
char
*
Filter
=
strdup
(
pFilter
);
char
*
StartingIndex
=
strdup
(
pStartingIndex
);
char
*
RequestedCount
=
strdup
(
pRequestedCount
);
char
*
SortCriteria
=
strdup
(
pSortCriteria
);
char
*
serviceType
=
strdup
(
CONTENT_DIRECTORY_SERVICE_TYPE
);
int
res
;
res
=
UpnpAddToAction
(
&
action
,
"Browse"
,
serviceType
,
"ObjectID"
,
ObjectID
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
/* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */
goto
browseActionCleanup
;
}
res
=
UpnpAddToAction
(
&
action
,
"Browse"
,
serviceType
,
"BrowseFlag"
,
BrowseFlag
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
/* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */
goto
browseActionCleanup
;
}
res
=
UpnpAddToAction
(
&
action
,
"Browse"
,
serviceType
,
"Filter"
,
Filter
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
/* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */
goto
browseActionCleanup
;
}
res
=
UpnpAddToAction
(
&
action
,
"Browse"
,
serviceType
,
"StartingIndex"
,
StartingIndex
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
/* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */
goto
browseActionCleanup
;
}
res
=
UpnpAddToAction
(
&
action
,
"Browse"
,
serviceType
,
"RequestedCount"
,
RequestedCount
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
/* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */
goto
browseActionCleanup
;
}
res
=
UpnpAddToAction
(
&
action
,
"Browse"
,
serviceType
,
"SortCriteria"
,
SortCriteria
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
/* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */
goto
browseActionCleanup
;
}
res
=
UpnpSendAction
(
_cookie
->
clientHandle
,
url
,
CONTENT_DIRECTORY_SERVICE_TYPE
,
0
,
action
,
&
response
);
if
(
res
!=
UPNP_E_SUCCESS
)
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"%s:%d: ERROR: %s"
,
__FILE__
,
__LINE__
,
UpnpGetErrorMessage
(
res
)
);
ixmlDocument_free
(
response
);
response
=
0
;
}
browseActionCleanup:
free
(
ObjectID
);
free
(
BrowseFlag
);
free
(
Filter
);
free
(
StartingIndex
);
free
(
RequestedCount
);
free
(
SortCriteria
);
free
(
serviceType
);
ixmlDocument_free
(
action
);
return
response
;
}
void
MediaServer
::
fetchContents
()
{
Container
*
root
=
new
Container
(
0
,
"0"
,
getFriendlyName
()
);
_fetchContents
(
root
);
if
(
_contents
)
{
vlc_mutex_lock
(
&
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
->
object_lock
);
playlist_NodeEmpty
(
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
,
_playlistNode
,
true
);
vlc_mutex_unlock
(
&
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
->
object_lock
);
delete
_contents
;
}
_contents
=
root
;
_contents
->
setPlaylistNode
(
_playlistNode
);
_buildPlaylist
(
_contents
);
}
bool
MediaServer
::
_fetchContents
(
Container
*
parent
)
{
if
(
!
parent
)
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"%s:%d: parent==NULL"
,
__FILE__
,
__LINE__
);
return
false
;
}
IXML_Document
*
response
=
_browseAction
(
parent
->
getObjectID
(),
"BrowseDirectChildren"
,
"*"
,
"0"
,
"0"
,
""
);
if
(
!
response
)
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"%s:%d: ERROR!"
,
__FILE__
,
__LINE__
);
return
false
;
}
IXML_Document
*
result
=
parseBrowseResult
(
response
);
ixmlDocument_free
(
response
);
if
(
!
result
)
{
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"%s:%d: ERROR!"
,
__FILE__
,
__LINE__
);
return
false
;
}
IXML_NodeList
*
containerNodeList
=
ixmlDocument_getElementsByTagName
(
result
,
"container"
);
if
(
containerNodeList
)
{
for
(
unsigned
int
i
=
0
;
i
<
ixmlNodeList_length
(
containerNodeList
);
i
++
)
{
IXML_Element
*
containerElement
=
(
IXML_Element
*
)
ixmlNodeList_item
(
containerNodeList
,
i
);
const
char
*
objectID
=
ixmlElement_getAttribute
(
containerElement
,
"id"
);
if
(
!
objectID
)
continue
;
const
char
*
childCountStr
=
ixmlElement_getAttribute
(
containerElement
,
"childCount"
);
if
(
!
childCountStr
)
continue
;
int
childCount
=
atoi
(
childCountStr
);
const
char
*
title
=
xml_getChildElementValue
(
containerElement
,
"dc:title"
);
if
(
!
title
)
continue
;
const
char
*
resource
=
xml_getChildElementValue
(
containerElement
,
"res"
);
if
(
resource
&&
childCount
<
1
)
{
Item
*
item
=
new
Item
(
parent
,
objectID
,
title
,
resource
);
parent
->
addItem
(
item
);
}
else
{
Container
*
container
=
new
Container
(
parent
,
objectID
,
title
);
parent
->
addContainer
(
container
);
if
(
childCount
>
0
)
_fetchContents
(
container
);
}
}
ixmlNodeList_free
(
containerNodeList
);
}
IXML_NodeList
*
itemNodeList
=
ixmlDocument_getElementsByTagName
(
result
,
"item"
);
if
(
itemNodeList
)
{
for
(
unsigned
int
i
=
0
;
i
<
ixmlNodeList_length
(
itemNodeList
);
i
++
)
{
IXML_Element
*
itemElement
=
(
IXML_Element
*
)
ixmlNodeList_item
(
itemNodeList
,
i
);
const
char
*
objectID
=
ixmlElement_getAttribute
(
itemElement
,
"id"
);
if
(
!
objectID
)
continue
;
const
char
*
title
=
xml_getChildElementValue
(
itemElement
,
"dc:title"
);
if
(
!
title
)
continue
;
const
char
*
resource
=
xml_getChildElementValue
(
itemElement
,
"res"
);
if
(
!
resource
)
continue
;
Item
*
item
=
new
Item
(
parent
,
objectID
,
title
,
resource
);
parent
->
addItem
(
item
);
}
ixmlNodeList_free
(
itemNodeList
);
}
ixmlDocument_free
(
result
);
return
true
;
}
void
MediaServer
::
_buildPlaylist
(
Container
*
parent
)
{
for
(
unsigned
int
i
=
0
;
i
<
parent
->
getNumContainers
();
i
++
)
{
Container
*
container
=
parent
->
getContainer
(
i
);
playlist_item_t
*
parentNode
=
parent
->
getPlaylistNode
();
char
*
title
=
strdup
(
container
->
getTitle
()
);
playlist_item_t
*
node
=
playlist_NodeCreate
(
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
,
VIEW_CATEGORY
,
title
,
parentNode
);
free
(
title
);
container
->
setPlaylistNode
(
node
);
_buildPlaylist
(
container
);
}
for
(
unsigned
int
i
=
0
;
i
<
parent
->
getNumItems
();
i
++
)
{
Item
*
item
=
parent
->
getItem
(
i
);
playlist_item_t
*
parentNode
=
parent
->
getPlaylistNode
();
playlist_item_t
*
node
=
playlist_ItemNew
(
_cookie
->
serviceDiscovery
,
item
->
getResource
(),
item
->
getTitle
()
);
playlist_NodeAddItem
(
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
,
node
,
VIEW_CATEGORY
,
parentNode
,
PLAYLIST_APPEND
,
PLAYLIST_END
);
item
->
setPlaylistNode
(
node
);
}
}
void
MediaServer
::
setPlaylistNode
(
playlist_item_t
*
playlistNode
)
{
_playlistNode
=
playlistNode
;
}
bool
MediaServer
::
compareSID
(
const
char
*
sid
)
{
return
(
strncmp
(
_subscriptionID
,
sid
,
sizeof
(
Upnp_SID
)
)
==
0
);
}
// MediaServerList...
MediaServerList
::
MediaServerList
(
Cookie
*
cookie
)
{
_cookie
=
cookie
;
}
MediaServerList
::~
MediaServerList
()
{
for
(
unsigned
int
i
=
0
;
i
<
_list
.
size
();
i
++
)
{
delete
_list
[
i
];
}
}
bool
MediaServerList
::
addServer
(
MediaServer
*
s
)
{
if
(
getServer
(
s
->
getUDN
()
)
!=
0
)
return
false
;
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"Adding server '%s'"
,
s
->
getFriendlyName
()
);
_list
.
push_back
(
s
);
char
*
name
=
strdup
(
s
->
getFriendlyName
()
);
playlist_item_t
*
node
=
playlist_NodeCreate
(
_cookie
->
serviceDiscovery
->
p_sys
->
p_playlist
,
VIEW_CATEGORY
,
name
,
_cookie
->
serviceDiscovery
->
p_sys
->
p_node
);
free
(
name
);
s
->
setPlaylistNode
(
node
);
return
true
;
}
MediaServer
*
MediaServerList
::
getServer
(
const
char
*
UDN
)
{
MediaServer
*
result
=
0
;
for
(
unsigned
int
i
=
0
;
i
<
_list
.
size
();
i
++
)
{
if
(
strcmp
(
UDN
,
_list
[
i
]
->
getUDN
()
)
==
0
)
{
result
=
_list
[
i
];
break
;
}
}
return
result
;
}
MediaServer
*
MediaServerList
::
getServerBySID
(
const
char
*
sid
)
{
MediaServer
*
server
=
0
;
for
(
unsigned
int
i
=
0
;
i
<
_list
.
size
();
i
++
)
{
if
(
_list
[
i
]
->
compareSID
(
sid
)
)
{
server
=
_list
[
i
];
break
;
}
}
return
server
;
}
void
MediaServerList
::
removeServer
(
const
char
*
UDN
)
{
MediaServer
*
server
=
getServer
(
UDN
);
if
(
!
server
)
return
;
msg_Dbg
(
_cookie
->
serviceDiscovery
,
"Removing server '%s'"
,
server
->
getFriendlyName
()
);
std
::
vector
<
MediaServer
*>::
iterator
it
;
for
(
it
=
_list
.
begin
();
it
!=
_list
.
end
();
it
++
)
{
if
(
*
it
==
server
)
{
_list
.
erase
(
it
);
delete
server
;
break
;
}
}
}
// Item...
Item
::
Item
(
Container
*
parent
,
const
char
*
objectID
,
const
char
*
title
,
const
char
*
resource
)
{
_parent
=
parent
;
_objectID
=
objectID
;
_title
=
title
;
_resource
=
resource
;
_playlistNode
=
0
;
}
const
char
*
Item
::
getObjectID
()
const
{
return
_objectID
.
c_str
();
}
const
char
*
Item
::
getTitle
()
const
{
return
_title
.
c_str
();
}
const
char
*
Item
::
getResource
()
const
{
return
_resource
.
c_str
();
}
void
Item
::
setPlaylistNode
(
playlist_item_t
*
node
)
{
_playlistNode
=
node
;
}
playlist_item_t
*
Item
::
getPlaylistNode
()
const
{
return
_playlistNode
;
}
// Container...
Container
::
Container
(
Container
*
parent
,
const
char
*
objectID
,
const
char
*
title
)
{
_parent
=
parent
;
_objectID
=
objectID
;
_title
=
title
;
_playlistNode
=
0
;
}
Container
::~
Container
()
{
for
(
unsigned
int
i
=
0
;
i
<
_containers
.
size
();
i
++
)
{
delete
_containers
[
i
];
}
for
(
unsigned
int
i
=
0
;
i
<
_items
.
size
();
i
++
)
{
delete
_items
[
i
];
}
}
void
Container
::
addItem
(
Item
*
item
)
{
_items
.
push_back
(
item
);
}
void
Container
::
addContainer
(
Container
*
container
)
{
_containers
.
push_back
(
container
);
}
const
char
*
Container
::
getObjectID
()
const
{
return
_objectID
.
c_str
();
}
const
char
*
Container
::
getTitle
()
const
{
return
_title
.
c_str
();
}
unsigned
int
Container
::
getNumItems
()
const
{
return
_items
.
size
();
}
unsigned
int
Container
::
getNumContainers
()
const
{
return
_containers
.
size
();
}
Item
*
Container
::
getItem
(
unsigned
int
i
)
const
{
if
(
i
<
_items
.
size
()
)
return
_items
[
i
];
return
0
;
}
Container
*
Container
::
getContainer
(
unsigned
int
i
)
const
{
if
(
i
<
_containers
.
size
()
)
return
_containers
[
i
];
return
0
;
}
void
Container
::
setPlaylistNode
(
playlist_item_t
*
node
)
{
_playlistNode
=
node
;
}
playlist_item_t
*
Container
::
getPlaylistNode
()
const
{
return
_playlistNode
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment