std::string MediaServerList::getIconURL( IXML_Element* p_device_elem, const char* psz_base_url ) { std::string res; IXML_NodeList* p_icon_lists = ixmlElement_getElementsByTagName( p_device_elem, "iconList" ); if ( p_icon_lists == NULL ) return res; IXML_Element* p_icon_list = (IXML_Element*)ixmlNodeList_item( p_icon_lists, 0 ); if ( p_icon_list != NULL ) { IXML_NodeList* p_icons = ixmlElement_getElementsByTagName( p_icon_list, "icon" ); if ( p_icons != NULL ) { unsigned int maxWidth = 0; unsigned int maxHeight = 0; for ( unsigned int i = 0; i < ixmlNodeList_length( p_icons ); ++i ) { IXML_Element* p_icon = (IXML_Element*)ixmlNodeList_item( p_icons, i ); const char* widthStr = xml_getChildElementValue( p_icon, "width" ); const char* heightStr = xml_getChildElementValue( p_icon, "height" ); if ( widthStr == NULL || heightStr == NULL ) continue; unsigned int width = atoi( widthStr ); unsigned int height = atoi( heightStr ); if ( width <= maxWidth || height <= maxHeight ) continue; const char* iconUrl = xml_getChildElementValue( p_icon, "url" ); if ( iconUrl == NULL ) continue; maxWidth = width; maxHeight = height; res = iconUrl; } ixmlNodeList_free( p_icons ); } } ixmlNodeList_free( p_icon_lists ); if ( res.empty() == false ) { vlc_url_t url; vlc_UrlParse( &url, psz_base_url ); char* psz_url; if ( asprintf( &psz_url, "%s://%s:%u%s", url.psz_protocol, url.psz_host, url.i_port, res.c_str() ) < 0 ) res.clear(); else { res = psz_url; free( psz_url ); } vlc_UrlClean( &url ); } return res; }
/* * Extracts the result document from a SOAP response */ IXML_Document* parseBrowseResult( IXML_Document* p_doc ) { assert( p_doc ); const char* psz_result_string = xml_getChildElementValue( p_doc, "Result" ); if( !psz_result_string ) return NULL; IXML_Document* p_browse_doc = ixmlParseBuffer( psz_result_string ); return p_browse_doc; }
/* * Extracts the result document from a SOAP response */ IXML_Document* parseBrowseResult( IXML_Document* p_doc ) { assert( p_doc ); // ixml*_getElementsByTagName will ultimately only case the pointer to a Node // pointer, and pass it to a private function. Don't bother have a IXML_Document // version of getChildElementValue const char* psz_raw_didl = xml_getChildElementValue( (IXML_Element*)p_doc, "Result" ); if( !psz_raw_didl ) return NULL; /* First, try parsing the buffer as is */ IXML_Document* p_result_doc = ixmlParseBuffer( psz_raw_didl ); if( !p_result_doc ) { /* Missing namespaces confuse the ixml parser. This is a very ugly * hack but it is needeed until devices start sending valid XML. * * It works that way: * * The DIDL document is extracted from the Result tag, then wrapped into * a valid XML header and a new root tag which contains missing namespace * definitions so the ixml parser understands it. * * If you know of a better workaround, please oh please fix it */ const char* psz_xml_result_fmt = "<?xml version=\"1.0\" ?>" "<Result xmlns:sec=\"urn:samsung:metadata:2009\">%s</Result>"; char* psz_xml_result_string = NULL; if( -1 == asprintf( &psz_xml_result_string, psz_xml_result_fmt, psz_raw_didl) ) return NULL; p_result_doc = ixmlParseBuffer( psz_xml_result_string ); free( psz_xml_result_string ); } if( !p_result_doc ) return NULL; IXML_NodeList *p_elems = ixmlDocument_getElementsByTagName( p_result_doc, "DIDL-Lite" ); IXML_Node *p_node = ixmlNodeList_item( p_elems, 0 ); ixmlNodeList_free( p_elems ); return (IXML_Document*)p_node; }
/* * Get the number value from a SOAP response */ int xml_getNumber( IXML_Document* p_doc, const char* psz_tag_name ) { assert( p_doc ); assert( psz_tag_name ); const char* psz = xml_getChildElementValue( p_doc, psz_tag_name ); if( !psz ) return 0; char *psz_end; long l = strtol( psz, &psz_end, 10 ); if( *psz_end || l < 0 || l > INT_MAX ) return 0; return (int)l; }
bool MediaServer::addContainer( IXML_Element* containerElement ) { char* psz_url; const char* objectID = ixmlElement_getAttribute( containerElement, "id" ); if ( !objectID ) return false; const char* title = xml_getChildElementValue( containerElement, "dc:title" ); if ( !title ) return false; if( asprintf( &psz_url, "upnp://%s?ObjectID=%s", m_psz_root, objectID ) < 0 ) return false; input_item_t* p_item = input_item_NewDirectory( psz_url, title, ITEM_NET ); free( psz_url); if ( !p_item ) return false; input_item_CopyOptions( p_item, m_node->p_item ); input_item_node_AppendItem( m_node, p_item ); input_item_Release( p_item ); return true; }
bool MediaServer::_fetchContents( Container* parent ) { if (!parent) { msg_Dbg( _p_sd, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; } IXML_Document* response = _browseAction( parent->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" ); if ( !response ) { msg_Dbg( _p_sd, "%s:%d: ERROR! No response from browse() action", __FILE__, __LINE__ ); return false; } IXML_Document* result = parseBrowseResult( response ); ixmlDocument_free( response ); if ( !result ) { msg_Dbg( _p_sd, "%s:%d: ERROR! browse() response parsing failed", __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::parseDeviceDescription( IXML_Document* doc, const char* location, services_discovery_t* p_sd ) { if ( !doc ) { msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ ); return; } if ( !location ) { msg_Dbg( p_sd, "%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( p_sd, "%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( p_sd, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; } if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 ) continue; const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" ); if ( !friendlyName ) { msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; } MediaServer* server = new MediaServer( UDN, friendlyName, p_sd ); if ( !p_sd->p_sys->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 ) { 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 ) { server->setContentDirectoryControlURL( url ); server->fetchContents(); } free( s1 ); free( s2 ); free( url ); } } ixmlNodeList_free( serviceList ); } } ixmlNodeList_free( deviceList ); } }
void MediaServerList::parseNewServer( IXML_Document *doc, const std::string &location ) { if ( !doc ) { msg_Err( m_sd, "Null IXML_Document" ); return; } if ( location.empty() ) { msg_Err( m_sd, "Empty location" ); return; } const char* psz_base_url = location.c_str(); /* Try to extract baseURL */ IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( doc, "URLBase" ); if ( p_url_list ) { if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) ) { IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node ); if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node ); } ixmlNodeList_free( p_url_list ); } /* Get devices */ IXML_NodeList* p_device_list = ixmlDocument_getElementsByTagName( doc, "device" ); if ( !p_device_list ) return; for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ ) { IXML_Element* p_device_element = ( IXML_Element* ) ixmlNodeList_item( p_device_list, i ); if( !p_device_element ) continue; const char* psz_device_type = xml_getChildElementValue( p_device_element, "deviceType" ); if ( !psz_device_type ) { msg_Warn( m_sd, "No deviceType found!" ); continue; } if ( strncmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type, strlen( MEDIA_SERVER_DEVICE_TYPE ) - 1 ) && strncmp( SATIP_SERVER_DEVICE_TYPE, psz_device_type, strlen( SATIP_SERVER_DEVICE_TYPE ) - 1 ) ) continue; const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" ); if ( !psz_udn ) { msg_Warn( m_sd, "No UDN!" ); continue; } /* Check if server is already added */ if ( getServer( psz_udn ) ) { msg_Warn( m_sd, "Server with uuid '%s' already exists.", psz_udn ); continue; } const char* psz_friendly_name = xml_getChildElementValue( p_device_element, "friendlyName" ); if ( !psz_friendly_name ) { msg_Dbg( m_sd, "No friendlyName!" ); continue; } std::string iconUrl = getIconURL( p_device_element, psz_base_url ); // We now have basic info, we need to get the content browsing url // so the access module can browse without fetching the manifest again if ( !strncmp( SATIP_SERVER_DEVICE_TYPE, psz_device_type, strlen( SATIP_SERVER_DEVICE_TYPE ) - 1 ) ) { SD::MediaServerDesc* p_server = NULL; vlc_url_t url; vlc_UrlParse( &url, psz_base_url ); char *psz_satip_channellist = config_GetPsz(m_sd, "satip-channelist"); if( !psz_satip_channellist ) { break; } /* a user may have provided a custom playlist url */ if (strncmp(psz_satip_channellist, "CustomList", 10) == 0) { char *psz_satip_playlist_url = config_GetPsz( m_sd, "satip-channellist-url" ); if ( psz_satip_playlist_url ) { p_server = new(std::nothrow) SD::MediaServerDesc( psz_udn, psz_friendly_name, psz_satip_playlist_url, iconUrl ); if( likely( p_server ) ) { p_server->satIpHost = url.psz_host; p_server->isSatIp = true; if( !addServer( p_server ) ) { delete p_server; } } /* to comply with the SAT>IP specification, we don't fall back on another channel list if this path failed */ free( psz_satip_playlist_url ); vlc_UrlClean( &url ); continue; } } /* If requested by the user, check for a SAT>IP m3u list, which may be provided by some rare devices */ if (strncmp(psz_satip_channellist, "ServerList", 10) == 0) { const char* psz_m3u_url = xml_getChildElementValue( p_device_element, "satip:X_SATIPM3U" ); if ( psz_m3u_url ) { if ( strncmp( "http", psz_m3u_url, 4) ) { char* psz_url = NULL; if ( UpnpResolveURL2( psz_base_url, psz_m3u_url, &psz_url ) == UPNP_E_SUCCESS ) { p_server = new(std::nothrow) SD::MediaServerDesc( psz_udn, psz_friendly_name, psz_url, iconUrl ); free(psz_url); } } else { p_server = new(std::nothrow) SD::MediaServerDesc( psz_udn, psz_friendly_name, psz_m3u_url, iconUrl ); } if ( unlikely( !p_server ) ) break; p_server->satIpHost = url.psz_host; p_server->isSatIp = true; if ( !addServer( p_server ) ) delete p_server; free(psz_satip_channellist); } else { msg_Warn( m_sd, "SAT>IP server '%s' did not provide a playlist", url.psz_host); } /* to comply with the SAT>IP specifications, we don't fallback on another channel list if this path failed */ vlc_UrlClean( &url ); continue; } /* Normally, fetch a playlist from the web, * which will be processed by a lua script a bit later */ char *psz_url; if (asprintf( &psz_url, "http://www.satip.info/Playlists/%s.m3u", psz_satip_channellist ) < 0 ) { vlc_UrlClean( &url ); free( psz_satip_channellist ); continue; } p_server = new(std::nothrow) SD::MediaServerDesc( psz_udn, psz_friendly_name, psz_url, iconUrl ); if( likely( p_server ) ) { p_server->satIpHost = url.psz_host; p_server->isSatIp = true; if( !addServer( p_server ) ) { delete p_server; } } free( psz_url ); free( psz_satip_channellist ); vlc_UrlClean( &url ); continue; } /* Check for ContentDirectory service. */ IXML_NodeList* p_service_list = ixmlElement_getElementsByTagName( p_device_element, "service" ); if ( !p_service_list ) continue; for ( unsigned int j = 0; j < ixmlNodeList_length( p_service_list ); j++ ) { IXML_Element* p_service_element = (IXML_Element*)ixmlNodeList_item( p_service_list, j ); const char* psz_service_type = xml_getChildElementValue( p_service_element, "serviceType" ); if ( !psz_service_type ) { msg_Warn( m_sd, "No service type found." ); continue; } int k = strlen( CONTENT_DIRECTORY_SERVICE_TYPE ) - 1; if ( strncmp( CONTENT_DIRECTORY_SERVICE_TYPE, psz_service_type, k ) ) continue; const char* psz_control_url = xml_getChildElementValue( p_service_element, "controlURL" ); if ( !psz_control_url ) { msg_Warn( m_sd, "No control url found." ); continue; } /* Try to browse content directory. */ char* psz_url = ( char* ) malloc( strlen( psz_base_url ) + strlen( psz_control_url ) + 1 ); if ( psz_url ) { if ( UpnpResolveURL( psz_base_url, psz_control_url, psz_url ) == UPNP_E_SUCCESS ) { SD::MediaServerDesc* p_server = new(std::nothrow) SD::MediaServerDesc( psz_udn, psz_friendly_name, psz_url, iconUrl ); free( psz_url ); if ( unlikely( !p_server ) ) break; if ( !addServer( p_server ) ) { delete p_server; continue; } } else free( psz_url ); } } ixmlNodeList_free( p_service_list ); } ixmlNodeList_free( p_device_list ); }
bool MediaServer::addItem( IXML_Element* itemElement ) { ItemDescriptionHolder holder; if (!holder.init(itemElement)) return false; /* Try to extract all resources in DIDL */ IXML_NodeList* p_resource_list = ixmlDocument_getElementsByTagName( (IXML_Document*) itemElement, "res" ); if ( !p_resource_list) return false; int list_lenght = ixmlNodeList_length( p_resource_list ); if (list_lenght <= 0 ) { ixmlNodeList_free( p_resource_list ); return false; } input_item_t *p_item = NULL; for (int index = 0; index < list_lenght; index++) { IXML_Element* p_resource = ( IXML_Element* ) ixmlNodeList_item( p_resource_list, index ); const char* rez_type = ixmlElement_getAttribute( p_resource, "protocolInfo" ); if (strncmp(rez_type, "http-get:*:video/", 17) == 0 && holder.media_type == ItemDescriptionHolder::VIDEO) { if (!p_item) p_item = holder.createNewItem(p_resource); holder.addSubtitleSlave(p_resource); } else if (strncmp(rez_type, "http-get:*:image/", 17) == 0) switch (holder.media_type) { case ItemDescriptionHolder::IMAGE: if (!p_item) { p_item = holder.createNewItem(p_resource); break; } case ItemDescriptionHolder::VIDEO: case ItemDescriptionHolder::AUDIO: holder.setArtworkURL(p_resource); break; } else if (strncmp(rez_type, "http-get:*:text/", 16) == 0) holder.addSlave(xml_getChildElementValue( p_resource, "res" ), SLAVE_TYPE_SPU); else if (strncmp(rez_type, "http-get:*:audio/", 17) == 0) { if (holder.media_type == ItemDescriptionHolder::AUDIO) { if (!p_item) p_item = holder.createNewItem(p_resource); } else holder.addSlave(xml_getChildElementValue( p_resource, "res" ), SLAVE_TYPE_AUDIO); } } ixmlNodeList_free( p_resource_list ); if (!p_item) return false; holder.apply(p_item); input_item_CopyOptions( p_item, m_node->p_item ); input_item_node_AppendItem( m_node, p_item ); input_item_Release( p_item ); return true; }
/* * Fetches and parses the UPNP response */ bool MediaServer::_fetchContents( Container* p_parent ) { if (!p_parent) { msg_Err( _p_sd, "No parent" ); return false; } IXML_Document* p_response = _browseAction( p_parent->getObjectID(), "BrowseDirectChildren", "*", "0", "0", "" ); if ( !p_response ) { msg_Err( _p_sd, "No response from browse() action" ); return false; } IXML_Document* p_result = parseBrowseResult( p_response ); ixmlDocument_free( p_response ); if ( !p_result ) { msg_Err( _p_sd, "browse() response parsing failed" ); return false; } #ifndef NDEBUG else { msg_Dbg( _p_sd, "Got DIDL document: %s", ixmlPrintDocument( p_result ) ); } #endif IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( p_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* title = xml_getChildElementValue( containerElement, "dc:title" ); if ( !title ) continue; Container* container = new Container( p_parent, objectID, title ); p_parent->addContainer( container ); _fetchContents( container ); } ixmlNodeList_free( containerNodeList ); } IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_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; const char* psz_duration = xml_getChildElementAttributeValue( itemElement, "res", "duration" ); mtime_t i_duration = -1; int i_hours, i_minutes, i_seconds, i_decis; if ( psz_duration ) { if( sscanf( psz_duration, "%02d:%02d:%02d.%d", &i_hours, &i_minutes, &i_seconds, &i_decis )) i_duration = INT64_C(1000000) * ( i_hours*3600 + i_minutes*60 + i_seconds ) + INT64_C(100000) * i_decis; } Item* item = new Item( p_parent, objectID, title, resource, i_duration ); p_parent->addItem( item ); } ixmlNodeList_free( itemNodeList ); } ixmlDocument_free( p_result ); return true; }
void MediaServer::parseDeviceDescription( IXML_Document* p_doc, const char* p_location, services_discovery_t* p_sd ) { if ( !p_doc ) { msg_Err( p_sd, "Null IXML_Document" ); return; } if ( !p_location ) { msg_Err( p_sd, "Null location" ); return; } const char* psz_base_url = p_location; /* Try to extract baseURL */ IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( p_doc, "URLBase" ); if ( p_url_list ) { if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) ) { IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node ); if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node ); } ixmlNodeList_free( p_url_list ); } /* Get devices */ IXML_NodeList* p_device_list = ixmlDocument_getElementsByTagName( p_doc, "device" ); if ( p_device_list ) { for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ ) { IXML_Element* p_device_element = ( IXML_Element* ) ixmlNodeList_item( p_device_list, i ); const char* psz_device_type = xml_getChildElementValue( p_device_element, "deviceType" ); if ( !psz_device_type ) { msg_Warn( p_sd, "No deviceType found!" ); continue; } if ( strncmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type, strlen( MEDIA_SERVER_DEVICE_TYPE ) - 1 ) != 0 ) continue; const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" ); if ( !psz_udn ) { msg_Warn( p_sd, "No UDN!" ); continue; } /* Check if server is already added */ if ( p_sd->p_sys->p_server_list->getServer( psz_udn ) != 0 ) { msg_Warn( p_sd, "Server with uuid '%s' already exists.", psz_udn ); continue; } const char* psz_friendly_name = xml_getChildElementValue( p_device_element, "friendlyName" ); if ( !psz_friendly_name ) { msg_Dbg( p_sd, "No friendlyName!" ); continue; } MediaServer* p_server = new MediaServer( psz_udn, psz_friendly_name, p_sd ); if ( !p_sd->p_sys->p_server_list->addServer( p_server ) ) { delete p_server; p_server = 0; continue; } /* Check for ContentDirectory service. */ IXML_NodeList* p_service_list = ixmlElement_getElementsByTagName( p_device_element, "service" ); if ( p_service_list ) { for ( unsigned int j = 0; j < ixmlNodeList_length( p_service_list ); j++ ) { IXML_Element* p_service_element = ( IXML_Element* ) ixmlNodeList_item( p_service_list, j ); const char* psz_service_type = xml_getChildElementValue( p_service_element, "serviceType" ); if ( !psz_service_type ) { msg_Warn( p_sd, "No service type found." ); continue; } int k = strlen( CONTENT_DIRECTORY_SERVICE_TYPE ) - 1; if ( strncmp( CONTENT_DIRECTORY_SERVICE_TYPE, psz_service_type, k ) != 0 ) continue; p_server->_i_content_directory_service_version = psz_service_type[k]; const char* psz_event_sub_url = xml_getChildElementValue( p_service_element, "eventSubURL" ); if ( !psz_event_sub_url ) { msg_Warn( p_sd, "No event subscription url found." ); continue; } const char* psz_control_url = xml_getChildElementValue( p_service_element, "controlURL" ); if ( !psz_control_url ) { msg_Warn( p_sd, "No control url found." ); continue; } /* Try to subscribe to ContentDirectory service */ char* psz_url = ( char* ) malloc( strlen( psz_base_url ) + strlen( psz_event_sub_url ) + 1 ); if ( psz_url ) { if ( UpnpResolveURL( psz_base_url, psz_event_sub_url, psz_url ) == UPNP_E_SUCCESS ) { p_server->setContentDirectoryEventURL( psz_url ); p_server->subscribeToContentDirectory(); } free( psz_url ); } /* Try to browse content directory. */ psz_url = ( char* ) malloc( strlen( psz_base_url ) + strlen( psz_control_url ) + 1 ); if ( psz_url ) { if ( UpnpResolveURL( psz_base_url, psz_control_url, psz_url ) == UPNP_E_SUCCESS ) { p_server->setContentDirectoryControlURL( psz_url ); p_server->fetchContents(); } free( psz_url ); } } ixmlNodeList_free( p_service_list ); } } ixmlNodeList_free( p_device_list ); } }
input_item_t* MediaServer::getNextItem() { input_item_t *p_item = NULL; if( !xmlDocument_ ) { fetchContents(); if( !xmlDocument_ ) return NULL; } if ( containerNodeList_ ) { for ( ; !p_item && containerNodeIndex_ < ixmlNodeList_length( containerNodeList_ ) ; containerNodeIndex_++ ) { IXML_Element* containerElement = (IXML_Element*)ixmlNodeList_item( containerNodeList_, containerNodeIndex_ ); const char* objectID = ixmlElement_getAttribute( containerElement, "id" ); if ( !objectID ) continue; const char* title = xml_getChildElementValue( containerElement, "dc:title" ); if ( !title ) continue; p_item = newItem(objectID, title); } } if( itemNodeList_ ) { for ( ; !p_item && itemNodeIndex_ < ixmlNodeList_length( itemNodeList_ ) ; itemNodeIndex_++ ) { IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList_, itemNodeIndex_ ); const char* objectID = ixmlElement_getAttribute( itemElement, "id" ); if ( !objectID ) continue; const char* title = xml_getChildElementValue( itemElement, "dc:title" ); if ( !title ) continue; const char* psz_subtitles = xml_getChildElementValue( itemElement, "sec:CaptionInfo" ); if ( !psz_subtitles ) psz_subtitles = xml_getChildElementValue( itemElement, "sec:CaptionInfoEx" ); if ( !psz_subtitles ) psz_subtitles = xml_getChildElementValue( itemElement, "pv:subtitlefile" ); /* Try to extract all resources in DIDL */ IXML_NodeList* p_resource_list = ixmlDocument_getElementsByTagName( (IXML_Document*) itemElement, "res" ); if ( p_resource_list && ixmlNodeList_length( p_resource_list ) > 0 ) { mtime_t i_duration = -1; int i_hours, i_minutes, i_seconds; IXML_Element* p_resource = ( IXML_Element* ) ixmlNodeList_item( p_resource_list, 0 ); const char* psz_resource_url = xml_getChildElementValue( p_resource, "res" ); if( !psz_resource_url ) continue; const char* psz_duration = ixmlElement_getAttribute( p_resource, "duration" ); if ( psz_duration ) { if( sscanf( psz_duration, "%d:%02d:%02d", &i_hours, &i_minutes, &i_seconds ) ) i_duration = INT64_C(1000000) * ( i_hours*3600 + i_minutes*60 + i_seconds ); } p_item = newItem( title, objectID, psz_subtitles, i_duration, psz_resource_url ); } ixmlNodeList_free( p_resource_list ); } } return p_item; }
void MediaServerList::parseNewServer( IXML_Document *doc, const std::string &location ) { if ( !doc ) { msg_Err( p_sd_, "Null IXML_Document" ); return; } if ( location.empty() ) { msg_Err( p_sd_, "Empty location" ); return; } const char* psz_base_url = location.c_str(); /* Try to extract baseURL */ IXML_NodeList* p_url_list = ixmlDocument_getElementsByTagName( doc, "URLBase" ); if ( p_url_list ) { if ( IXML_Node* p_url_node = ixmlNodeList_item( p_url_list, 0 ) ) { IXML_Node* p_text_node = ixmlNode_getFirstChild( p_url_node ); if ( p_text_node ) psz_base_url = ixmlNode_getNodeValue( p_text_node ); } ixmlNodeList_free( p_url_list ); } /* Get devices */ IXML_NodeList* p_device_list = ixmlDocument_getElementsByTagName( doc, "device" ); if ( !p_device_list ) return; for ( unsigned int i = 0; i < ixmlNodeList_length( p_device_list ); i++ ) { IXML_Element* p_device_element = ( IXML_Element* ) ixmlNodeList_item( p_device_list, i ); if( !p_device_element ) continue; const char* psz_device_type = xml_getChildElementValue( p_device_element, "deviceType" ); if ( !psz_device_type ) { msg_Warn( p_sd_, "No deviceType found!" ); continue; } if ( strncmp( MEDIA_SERVER_DEVICE_TYPE, psz_device_type, strlen( MEDIA_SERVER_DEVICE_TYPE ) - 1 ) ) continue; const char* psz_udn = xml_getChildElementValue( p_device_element, "UDN" ); if ( !psz_udn ) { msg_Warn( p_sd_, "No UDN!" ); continue; } /* Check if server is already added */ if ( p_sd_->p_sys->p_server_list->getServer( psz_udn ) ) { msg_Warn( p_sd_, "Server with uuid '%s' already exists.", psz_udn ); continue; } const char* psz_friendly_name = xml_getChildElementValue( p_device_element, "friendlyName" ); if ( !psz_friendly_name ) { msg_Dbg( p_sd_, "No friendlyName!" ); continue; } // We now have basic info, we need to get the content browsing url // so the access module can browse without fetching the manifest again /* Check for ContentDirectory service. */ IXML_NodeList* p_service_list = ixmlElement_getElementsByTagName( p_device_element, "service" ); if ( !p_service_list ) continue; for ( unsigned int j = 0; j < ixmlNodeList_length( p_service_list ); j++ ) { IXML_Element* p_service_element = (IXML_Element*)ixmlNodeList_item( p_service_list, j ); const char* psz_service_type = xml_getChildElementValue( p_service_element, "serviceType" ); if ( !psz_service_type ) { msg_Warn( p_sd_, "No service type found." ); continue; } int k = strlen( CONTENT_DIRECTORY_SERVICE_TYPE ) - 1; if ( strncmp( CONTENT_DIRECTORY_SERVICE_TYPE, psz_service_type, k ) ) continue; const char* psz_control_url = xml_getChildElementValue( p_service_element, "controlURL" ); if ( !psz_control_url ) { msg_Warn( p_sd_, "No control url found." ); continue; } /* Try to browse content directory. */ char* psz_url = ( char* ) malloc( strlen( psz_base_url ) + strlen( psz_control_url ) + 1 ); if ( psz_url ) { if ( UpnpResolveURL( psz_base_url, psz_control_url, psz_url ) == UPNP_E_SUCCESS ) { SD::MediaServerDesc* p_server = new(std::nothrow) SD::MediaServerDesc( psz_udn, psz_friendly_name, psz_url ); free( psz_url ); if ( unlikely( !p_server ) ) break; if ( !addServer( p_server ) ) { delete p_server; continue; } } else free( psz_url ); } } ixmlNodeList_free( p_service_list ); } ixmlNodeList_free( p_device_list ); }
/* * Fetches and parses the UPNP response */ bool MediaServer::_fetchContents( Container* p_parent, int i_offset ) { if (!p_parent) { msg_Err( _p_sd, "No parent" ); return false; } char* psz_starting_index; if( asprintf( &psz_starting_index, "%d", i_offset ) < 0 ) { msg_Err( _p_sd, "asprintf error:%d", i_offset ); return false; } IXML_Document* p_response = _browseAction( p_parent->getObjectID(), "BrowseDirectChildren", "*", /* Filter */ psz_starting_index, /* StartingIndex */ "0", /* RequestedCount */ "" /* SortCriteria */ ); free( psz_starting_index ); if ( !p_response ) { msg_Err( _p_sd, "No response from browse() action" ); return false; } IXML_Document* p_result = parseBrowseResult( p_response ); int i_number_returned = xml_getNumber( p_response, "NumberReturned" ); int i_total_matches = xml_getNumber( p_response , "TotalMatches" ); #ifndef NDEBUG msg_Dbg( _p_sd, "i_offset[%d]i_number_returned[%d]_total_matches[%d]\n", i_offset, i_number_returned, i_total_matches ); #endif ixmlDocument_free( p_response ); if ( !p_result ) { msg_Err( _p_sd, "browse() response parsing failed" ); return false; } #ifndef NDEBUG msg_Dbg( _p_sd, "Got DIDL document: %s", ixmlPrintDocument( p_result ) ); #endif IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( p_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* title = xml_getChildElementValue( containerElement, "dc:title" ); if ( !title ) continue; Container* container = new Container( p_parent, objectID, title ); p_parent->addContainer( container ); _fetchContents( container, 0 ); } ixmlNodeList_free( containerNodeList ); } IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( p_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; const char* psz_duration = xml_getChildElementAttributeValue( itemElement, "res", "duration" ); mtime_t i_duration = -1; int i_hours, i_minutes, i_seconds, i_decis; if ( psz_duration ) { if( sscanf( psz_duration, "%02d:%02d:%02d.%d", &i_hours, &i_minutes, &i_seconds, &i_decis )) i_duration = INT64_C(1000000) * ( i_hours*3600 + i_minutes*60 + i_seconds ) + INT64_C(100000) * i_decis; } Item* item = new Item( p_parent, objectID, title, resource, i_duration ); p_parent->addItem( item ); } ixmlNodeList_free( itemNodeList ); } ixmlDocument_free( p_result ); if( i_offset + i_number_returned < i_total_matches ) return _fetchContents( p_parent, i_offset + i_number_returned ); return true; }