/*---------------------------------------------------------------------- | PLT_DeviceData::SetDescription +---------------------------------------------------------------------*/ NPT_Result PLT_DeviceData::SetDescription(PLT_DeviceDataReference& root_device, NPT_TimeInterval leasetime, NPT_HttpUrl description_url, const char* description, const NPT_HttpRequestContext& context) { NPT_XmlParser parser; NPT_XmlNode* tree = NULL; NPT_Result res; NPT_XmlElementNode* root = NULL; NPT_String URLBase; // create new device if none passed if (root_device.IsNull()) { root_device = new PLT_DeviceData(description_url, "", leasetime); } res = parser.Parse(description, tree); NPT_CHECK_LABEL_SEVERE(res, cleanup); root = tree->AsElementNode(); if (!root || root->GetTag() != "root" || !root->GetNamespace() || *root->GetNamespace() != "urn:schemas-upnp-org:device-1-0") { NPT_LOG_INFO_1("root namespace is invalid: %s", (root&&root->GetNamespace())?root->GetNamespace()->GetChars():"null"); NPT_CHECK_LABEL_SEVERE(NPT_FAILURE, cleanup); } // look for optional URLBase element if (NPT_SUCCEEDED(PLT_XmlHelper::GetChildText(root, "URLBase", URLBase))) { NPT_HttpUrl url(URLBase); // Some devices like Connect360 try to be funny - not so if (url.GetHost().ToLowercase() == "localhost" || url.GetHost().ToLowercase() == "127.0.0.1") { url.SetHost(context.GetRemoteAddress().GetIpAddress().ToString()); } root_device->SetURLBase(url); } else { // No URLBase, derive from description url root_device->SetURLBase(description_url); } // at least one root device child element is required NPT_XmlElementNode* device; if (!(device = PLT_XmlHelper::GetChild(root, "device"))) { NPT_CHECK_LABEL_SEVERE(NPT_FAILURE, cleanup); } res = SetDescriptionDevice(root_device, device, context); cleanup: // delete the tree delete tree; return res; }
/*---------------------------------------------------------------------- | PLT_DeviceData::GetDescription +---------------------------------------------------------------------*/ NPT_Result PLT_DeviceData::GetDescription(NPT_String& desc) { NPT_Result res; NPT_XmlElementNode* spec = NULL; NPT_XmlElementNode* root = new NPT_XmlElementNode("root"); NPT_CHECK_LABEL_SEVERE(res = root->SetNamespaceUri("", "urn:schemas-upnp-org:device-1-0"), cleanup); NPT_CHECK_LABEL_SEVERE(res = root->SetNamespaceUri("dlna", "urn:schemas-dlna-org:device-1-0"), cleanup); NPT_CHECK_LABEL_SEVERE(res = root->SetAttribute("", "configId", NPT_String::FromInteger(m_ConfigId)), cleanup); // add spec version spec = new NPT_XmlElementNode("specVersion"); NPT_CHECK_LABEL_SEVERE(res = root->AddChild(spec), cleanup); NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::AddChildText(spec, "major", "1"), cleanup); NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::AddChildText(spec, "minor", "1"), cleanup); // get device xml NPT_CHECK_LABEL_SEVERE(res = GetDescription(root), cleanup); // serialize node NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::Serialize(*root, desc, true, 2), cleanup); cleanup: delete root; return res; }
bool CUPnPPlayer::QueueNextFile(const CFileItem& file) { CFileItem item(file); NPT_Reference<CThumbLoader> thumb_loader; NPT_Reference<PLT_MediaObject> obj; NPT_String path(file.GetPath().c_str()); NPT_String tmp; if (file.IsVideoDb()) thumb_loader = NPT_Reference<CThumbLoader>(new CVideoThumbLoader()); else if (item.IsMusicDb()) thumb_loader = NPT_Reference<CThumbLoader>(new CMusicThumbLoader()); obj = BuildObject(item, path, 0, thumb_loader, NULL, CUPnP::GetServer(), UPnPPlayer); if(!obj.IsNull()) { NPT_CHECK_LABEL_SEVERE(PLT_Didl::ToDidl(*obj, "", tmp), failed); tmp.Insert(didl_header, 0); tmp.Append(didl_footer); } NPT_CHECK_LABEL_WARNING(m_control->SetNextAVTransportURI(m_delegate->m_device , m_delegate->m_instance , file.GetPath().c_str() , (const char*)tmp , m_delegate), failed); if(!m_delegate->m_resevent.WaitMSec(10000)) goto failed; NPT_CHECK_LABEL_WARNING(m_delegate->m_resstatus, failed); return true; failed: CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::QueueNextFile - unable to queue file %s", file.GetPath().c_str()); return false; }
bool CUPnPPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options) { CGUIDialogBusy* dialog = NULL; XbmcThreads::EndTime timeout(10000); /* if no path we want to attach to a already playing player */ if(file.GetPath() == "") { NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed); /* make sure the attached player is actually playing */ { CSingleLock lock(m_delegate->m_section); if(m_delegate->m_trainfo.cur_transport_state != "PLAYING" && m_delegate->m_trainfo.cur_transport_state != "PAUSED_PLAYBACK") goto failed; } } else NPT_CHECK_LABEL_SEVERE(PlayFile(file, options, dialog, timeout), failed); m_stopremote = true; m_started = true; m_callback.OnPlayBackStarted(); NPT_CHECK_LABEL_SEVERE(m_control->GetPositionInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); NPT_CHECK_LABEL_SEVERE(m_control->GetMediaInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); if(dialog) dialog->Close(); return true; failed: CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - unable to open file %s", file.GetPath().c_str()); if(dialog) dialog->Close(); return false; }
/*---------------------------------------------------------------------- | PLT_HttpServerSocketTask::DoRun +---------------------------------------------------------------------*/ void PLT_HttpServerSocketTask::DoRun() { NPT_BufferedInputStreamReference buffered_input_stream; NPT_HttpRequestContext context; NPT_Result res = NPT_SUCCESS; bool headers_only; bool keep_alive = false; // create a buffered input stream to parse HTTP request NPT_InputStreamReference input_stream; NPT_CHECK_LABEL_SEVERE(GetInputStream(input_stream), done); NPT_CHECK_POINTER_LABEL_FATAL(input_stream.AsPointer(), done); buffered_input_stream = new NPT_BufferedInputStream(input_stream); while (!IsAborting(0)) { NPT_HttpRequest* request = NULL; NPT_HttpResponse* response = NULL; // reset keep-alive to exit task on read failure keep_alive = false; // wait for a request res = Read(buffered_input_stream, request, &context); if (NPT_FAILED(res) || (request == NULL)) goto cleanup; // process request and setup response res = RespondToClient(*request, context, response); if (NPT_FAILED(res) || (response == NULL)) goto cleanup; // check if client requested keep-alive keep_alive = PLT_HttpHelper::IsConnectionKeepAlive(*request); headers_only = request->GetMethod() == NPT_HTTP_METHOD_HEAD; // send response, pass keep-alive request from client // (it can be overridden if response handler did not allow it) res = Write(response, keep_alive, headers_only); // on write error, reset keep_alive so we can close this connection if (NPT_FAILED(res)) keep_alive = false; cleanup: // cleanup delete request; delete response; if (!keep_alive && !m_StayAliveForever) { return; } } done: return; }
/*---------------------------------------------------------------------- | PLT_MediaServer::ParseTagList +---------------------------------------------------------------------*/ NPT_Result PLT_MediaServer::ParseTagList(const NPT_String& updates, NPT_Map<NPT_String,NPT_String>& tags) { // reset output params first tags.Clear(); NPT_List<NPT_String> split = updates.Split(","); NPT_XmlNode* node = NULL; NPT_XmlElementNode* didl_partial = NULL; NPT_XmlParser parser; // as these are single name value pairs, separated by commas we wrap in a tag // to create a valid tree NPT_String xml("<TagValueList>"); for (NPT_List<NPT_String>::Iterator entry = split.GetFirstItem(); entry; entry++) { NPT_String& element = (*entry); if (element.IsEmpty()) xml.Append("<empty>empty</empty>"); else xml.Append(element); } xml.Append("</TagValueList>"); NPT_LOG_FINE("Parsing TagList..."); NPT_CHECK_LABEL_SEVERE(parser.Parse(xml, node), cleanup); if (!node || !node->AsElementNode()) { NPT_LOG_SEVERE("Invalid node type"); goto cleanup; } didl_partial = node->AsElementNode(); if (didl_partial->GetTag().Compare("TagValueList", true)) { NPT_LOG_SEVERE("Invalid node tag"); goto cleanup; } for (NPT_List<NPT_XmlNode*>::Iterator children = didl_partial->GetChildren().GetFirstItem(); children; children++) { NPT_XmlElementNode* child = (*children)->AsElementNode(); if (!child) continue; tags[child->GetTag()] = *child->GetText(); } return NPT_SUCCESS; cleanup: if (node) delete node; return NPT_FAILURE; }
/*---------------------------------------------------------------------- | PLT_Didl::FromDidl +---------------------------------------------------------------------*/ NPT_Result PLT_Didl::FromDidl(const char* xml, PLT_MediaObjectListReference& objects) { NPT_String str; PLT_MediaObject* object = NULL; NPT_XmlNode* node = NULL; NPT_XmlElementNode* didl = NULL; NPT_XmlParser parser; NPT_LOG_FINE("Parsing Didl..."); NPT_CHECK_LABEL_SEVERE(parser.Parse(xml, node), cleanup); if (!node || !node->AsElementNode()) { NPT_LOG_SEVERE("Invalid node type"); goto cleanup; } didl = node->AsElementNode(); if (didl->GetTag().Compare("DIDL-Lite", true)) { NPT_LOG_SEVERE("Invalid node tag"); goto cleanup; } // create entry list objects = new PLT_MediaObjectList(); // for each child, find out if it's a container or not // and then invoke the FromDidl on it for (NPT_List<NPT_XmlNode*>::Iterator children = didl->GetChildren().GetFirstItem(); children; children++) { NPT_XmlElementNode* child = (*children)->AsElementNode(); if (!child) continue; if (child->GetTag().Compare("Container", true) == 0) { object = new PLT_MediaContainer(); } else if (child->GetTag().Compare("item", true) == 0) { object = new PLT_MediaItem(); } else { NPT_LOG_WARNING("Invalid node tag"); continue; } if (NPT_FAILED(object->FromDidl(child))) { NPT_LOG_WARNING_1("Invalid didl for object: %s", (const char*) PLT_XmlHelper::Serialize(*child, false)); continue; } objects->Add(object); object = NULL; // reset to make sure it doesn't get deleted twice in case of error } delete node; return NPT_SUCCESS; cleanup: objects = NULL; delete node; delete object; return NPT_FAILURE; }
/*---------------------------------------------------------------------- | PLT_HttpServerSocketTask::DoRun +---------------------------------------------------------------------*/ void PLT_SsdpSearchTask::DoRun() { NPT_HttpResponse* response = NULL; PLT_HttpClient client; NPT_Timeout timeout = 30; NPT_HttpRequestContext context; do { // get the address of the server NPT_IpAddress server_address; NPT_CHECK_LABEL_SEVERE(server_address.ResolveName( m_Request->GetUrl().GetHost(), timeout), done); NPT_SocketAddress address(server_address, m_Request->GetUrl().GetPort()); // send 2 requests in a row NPT_OutputStreamReference output_stream( new PLT_OutputDatagramStream(m_Socket, 4096, &address)); NPT_CHECK_LABEL_SEVERE(client.SendRequest( output_stream, *m_Request), done); NPT_CHECK_LABEL_SEVERE(client.SendRequest( output_stream, *m_Request), done); output_stream = NULL; // keep track of when we sent the request NPT_TimeStamp last_send; NPT_System::GetCurrentTimeStamp(last_send); while (!IsAborting(0)) { // read response PLT_InputDatagramStreamReference input_stream( new PLT_InputDatagramStream(m_Socket)); NPT_InputStreamReference stream = input_stream; NPT_Result res = client.WaitForResponse(stream, *m_Request, context, response); // callback to process response if (NPT_SUCCEEDED(res)) { // get source info NPT_SocketInfo info; input_stream->GetInfo(info); context.SetLocalAddress(info.local_address); context.SetRemoteAddress(info.remote_address); // process response ProcessResponse(NPT_SUCCESS, m_Request, context, response); delete response; response = NULL; } else if (res != NPT_ERROR_TIMEOUT) { NPT_LOG_WARNING_1("PLT_SsdpSearchTask got an error (%d) waiting for response", res); } input_stream = NULL; // check if it's time to resend request NPT_TimeStamp now; NPT_System::GetCurrentTimeStamp(now); if (now >= last_send + (long)m_Timeout/1000) break; } } while (!IsAborting(0) && m_Repeat); done: return; }
int CUPnPPlayer::PlayFile(const CFileItem& file, const CPlayerOptions& options, CGUIDialogBusy*& dialog, XbmcThreads::EndTime& timeout) { CFileItem item(file); NPT_Reference<CThumbLoader> thumb_loader; NPT_Reference<PLT_MediaObject> obj; NPT_String path(file.GetPath().c_str()); NPT_String tmp, resource; EMediaControllerQuirks quirks = EMEDIACONTROLLERQUIRKS_NONE; NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed); if (file.IsVideoDb()) thumb_loader = NPT_Reference<CThumbLoader>(new CVideoThumbLoader()); else if (item.IsMusicDb()) thumb_loader = NPT_Reference<CThumbLoader>(new CMusicThumbLoader()); obj = BuildObject(item, path, false, thumb_loader, NULL, CUPnP::GetServer(), UPnPPlayer); if(obj.IsNull()) goto failed; NPT_CHECK_LABEL_SEVERE(PLT_Didl::ToDidl(*obj, "", tmp), failed_todidl); tmp.Insert(didl_header, 0); tmp.Append(didl_footer); quirks = GetMediaControllerQuirks(m_delegate->m_device.AsPointer()); if (quirks & EMEDIACONTROLLERQUIRKS_X_MKV) { for (NPT_Cardinal i=0; i< obj->m_Resources.GetItemCount(); i++) { if (obj->m_Resources[i].m_ProtocolInfo.GetContentType().Compare("video/x-matroska") == 0) { CLog::Log(LOGDEBUG, "CUPnPPlayer::PlayFile(%s): applying video/x-mkv quirk", file.GetPath().c_str()); NPT_String protocolInfo = obj->m_Resources[i].m_ProtocolInfo.ToString(); protocolInfo.Replace(":video/x-matroska:", ":video/x-mkv:"); obj->m_Resources[i].m_ProtocolInfo = PLT_ProtocolInfo(protocolInfo); } } } /* The resource uri's are stored in the Didl. We must choose the best resource * for the playback device */ NPT_Cardinal res_index; NPT_CHECK_LABEL_SEVERE(m_control->FindBestResource(m_delegate->m_device, *obj, res_index), failed_findbestresource); // get the transport info to evaluate the TransportState to be able to // determine whether we first need to call Stop() timeout.Set(timeout.GetInitialTimeoutValue()); NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed_gettransportinfo); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed_gettransportinfo); if (m_delegate->m_trainfo.cur_transport_state != "NO_MEDIA_PRESENT" && m_delegate->m_trainfo.cur_transport_state != "STOPPED") { timeout.Set(timeout.GetInitialTimeoutValue()); NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed_stop); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed_stop); NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_stop); } timeout.Set(timeout.GetInitialTimeoutValue()); NPT_CHECK_LABEL_SEVERE(m_control->SetAVTransportURI(m_delegate->m_device , m_delegate->m_instance , obj->m_Resources[res_index].m_Uri , (const char*)tmp , m_delegate), failed_setavtransporturi); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed_setavtransporturi); NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_setavtransporturi); timeout.Set(timeout.GetInitialTimeoutValue()); NPT_CHECK_LABEL_SEVERE(m_control->Play(m_delegate->m_device , m_delegate->m_instance , "1" , m_delegate), failed_play); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed_play); NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_play); /* wait for PLAYING state */ timeout.Set(timeout.GetInitialTimeoutValue()); do { NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed_waitplaying); { CSingleLock lock(m_delegate->m_section); if(m_delegate->m_trainfo.cur_transport_state == "PLAYING" || m_delegate->m_trainfo.cur_transport_state == "PAUSED_PLAYBACK") break; if(m_delegate->m_trainfo.cur_transport_state == "STOPPED" && m_delegate->m_trainfo.cur_transport_status != "OK") { CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - remote player signalled error %s", file.GetPath().c_str()); return NPT_FAILURE; } } NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed_waitplaying); } while(!timeout.IsTimePast()); if(options.starttime > 0) { /* many upnp units won't load file properly until after play (including xbmc) */ NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device , m_delegate->m_instance , "REL_TIME" , PLT_Didl::FormatTimeStamp((NPT_UInt32)options.starttime) , m_delegate), failed_seek); } return NPT_SUCCESS; failed_todidl: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to serialize item into DIDL-Lite", file.GetPath().c_str()); return NPT_FAILURE; failed_findbestresource: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to find a matching resource", file.GetPath().c_str()); return NPT_FAILURE; failed_gettransportinfo: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s): call to GetTransportInfo failed", file.GetPath().c_str()); return NPT_FAILURE; failed_stop: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to stop current playback", file.GetPath().c_str()); return NPT_FAILURE; failed_setavtransporturi: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to set the playback URI", file.GetPath().c_str()); return NPT_FAILURE; failed_play: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to start playback", file.GetPath().c_str()); return NPT_FAILURE; failed_waitplaying: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to wait for PLAYING state", file.GetPath().c_str()); return NPT_FAILURE; failed_seek: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to seek to start offset", file.GetPath().c_str()); return NPT_FAILURE; failed: CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed", file.GetPath().c_str()); return NPT_FAILURE; }
/*---------------------------------------------------------------------- | PLT_FileMediaServer::BuildFromFilePath +---------------------------------------------------------------------*/ PLT_MediaObject* PLT_FileMediaServer::BuildFromFilePath(const NPT_String& filepath, bool with_count /* = true */, const NPT_SocketAddress* req_local_address /* = NULL */, bool keep_extension_in_title /* = false */) { NPT_String root = m_Path; PLT_MediaItemResource resource; PLT_MediaObject* object = NULL; /* retrieve the entry type (directory or file) */ NPT_FileInfo info; NPT_CHECK_LABEL_FATAL(NPT_File::GetInfo(filepath, &info), failure); if (info.m_Type == NPT_FileInfo::FILE_TYPE_REGULAR) { object = new PLT_MediaItem(); /* Set the title using the filename for now */ object->m_Title = NPT_FilePath::BaseName(filepath, keep_extension_in_title); if (object->m_Title.GetLength() == 0) goto failure; /* Set the protocol Info from the extension */ resource.m_ProtocolInfo = PLT_MediaItem::GetProtInfoFromExt(NPT_FilePath::FileExtension(filepath)); if (resource.m_ProtocolInfo.GetLength() == 0) goto failure; /* Set the resource file size */ resource.m_Size = info.m_Size; /* format the resource URI */ NPT_String url = filepath.SubString(root.GetLength()+1); // get list of ip addresses NPT_List<NPT_String> ips; NPT_CHECK_LABEL_SEVERE(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure); // if we're passed an interface where we received the request from // move the ip to the top if (req_local_address && req_local_address->GetIpAddress().ToString() != "0.0.0.0") { ips.Remove(req_local_address->GetIpAddress().ToString()); ips.Insert(ips.GetFirstItem(), req_local_address->GetIpAddress().ToString()); } // iterate through list and build list of resources NPT_List<NPT_String>::Iterator ip = ips.GetFirstItem(); while (ip) { /* prepend the base URI and url encode it */ //resource.m_Uri = NPT_Uri::Encode(uri.ToString(), NPT_Uri::UnsafeCharsToEncode); resource.m_Uri = BuildResourceUri(m_FileBaseUri, *ip, url); /* Look to see if a metadatahandler exists for this extension */ PLT_MetadataHandler* handler = NULL; NPT_Result res = NPT_ContainerFind( m_MetadataHandlers, PLT_MetadataHandlerFinder(NPT_FilePath::FileExtension(filepath)), handler); if (NPT_SUCCEEDED(res) && handler) { /* if it failed loading data, reset the metadatahandler so we don't use it */ if (NPT_SUCCEEDED(handler->LoadFile(filepath))) { /* replace the title with the one from the Metadata */ NPT_String newTitle; if (handler->GetTitle(newTitle) != NULL) { object->m_Title = newTitle; } /* assign description */ handler->GetDescription(object->m_Description.long_description); /* assign album art uri if we haven't yet */ /* prepend the album art base URI and url encode it */ if (object->m_ExtraInfo.album_art_uri.GetLength() == 0) { object->m_ExtraInfo.album_art_uri = NPT_Uri::PercentEncode(BuildResourceUri(m_AlbumArtBaseUri, *ip, url), NPT_Uri::UnsafeCharsToEncode); } /* duration */ handler->GetDuration(resource.m_Duration); /* protection */ handler->GetProtection(resource.m_Protection); } } object->m_ObjectClass.type = PLT_MediaItem::GetUPnPClassFromExt(NPT_FilePath::FileExtension(filepath)); object->m_Resources.Add(resource); ++ip; } } else { object = new PLT_MediaContainer; /* Assign a title for this container */ if (filepath.Compare(root, true) == 0) { object->m_Title = "Root"; } else { object->m_Title = NPT_FilePath::BaseName(filepath, keep_extension_in_title); if (object->m_Title.GetLength() == 0) goto failure; } /* Get the number of children for this container */ NPT_Cardinal count = 0; if (with_count && NPT_SUCCEEDED(NPT_File::GetCount(filepath, count))) { ((PLT_MediaContainer*)object)->m_ChildrenCount = count; } object->m_ObjectClass.type = "object.container"; } /* is it the root? */ if (filepath.Compare(root, true) == 0) { object->m_ParentID = "-1"; object->m_ObjectID = "0"; } else { NPT_String directory = NPT_FilePath::DirectoryName(filepath); /* is the parent path the root? */ if (directory.GetLength() == root.GetLength()) { object->m_ParentID = "0"; } else { object->m_ParentID = "0" + filepath.SubString(root.GetLength(), directory.GetLength() - root.GetLength()); } object->m_ObjectID = "0" + filepath.SubString(root.GetLength()); } return object; failure: delete object; return NULL; }
bool CUPnPPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options) { CFileItem item(file); NPT_Reference<CThumbLoader> thumb_loader; NPT_Reference<PLT_MediaObject> obj; NPT_String path(file.GetPath().c_str()); NPT_String tmp, resource; XbmcThreads::EndTime timeout; CGUIDialogBusy* dialog = NULL; NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed); timeout.Set(10000); /* if no path we want to attach to a already playing player */ if(path != "") { if (file.IsVideoDb()) thumb_loader = NPT_Reference<CThumbLoader>(new CVideoThumbLoader()); else if (item.IsMusicDb()) thumb_loader = NPT_Reference<CThumbLoader>(new CMusicThumbLoader()); obj = BuildObject(item, path, false, thumb_loader, NULL, CUPnP::GetServer()); if(obj.IsNull()) goto failed; NPT_CHECK_LABEL_SEVERE(PLT_Didl::ToDidl(*obj, "", tmp), failed); tmp.Insert(didl_header, 0); tmp.Append(didl_footer); /* The resource uri's are stored in the Didl. We must choose the best resource * for the playback device */ NPT_Cardinal res_index; NPT_CHECK_LABEL_SEVERE(m_control->FindBestResource(m_delegate->m_device, *obj, res_index), failed); /* dlna specifies that a return code of 705 should be returned * if TRANSPORT_STATE is not STOPPED or NO_MEDIA_PRESENT */ NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed); NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed); NPT_CHECK_LABEL_SEVERE(m_control->SetAVTransportURI(m_delegate->m_device , m_delegate->m_instance , obj->m_Resources[res_index].m_Uri , (const char*)tmp , m_delegate), failed); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed); NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed); NPT_CHECK_LABEL_SEVERE(m_control->Play(m_delegate->m_device , m_delegate->m_instance , "1" , m_delegate), failed); NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed); NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed); } /* wait for PLAYING state */ do { NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); { CSingleLock lock(m_delegate->m_section); if(m_delegate->m_trainfo.cur_transport_state == "PLAYING" || m_delegate->m_trainfo.cur_transport_state == "PAUSED_PLAYBACK") break; if(m_delegate->m_trainfo.cur_transport_state == "STOPPED" && m_delegate->m_trainfo.cur_transport_status != "OK") { CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - remote player signalled error %s", file.GetPath().c_str()); goto failed; } } NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed); } while(!timeout.IsTimePast()); if(options.starttime > 0) { /* many upnp units won't load file properly until after play (including xbmc) */ NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device , m_delegate->m_instance , "REL_TIME" , PLT_Didl::FormatTimeStamp((NPT_UInt32)options.starttime) , m_delegate), failed); } m_started = true; m_callback.OnPlayBackStarted(); NPT_CHECK_LABEL_SEVERE(m_control->GetPositionInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); NPT_CHECK_LABEL_SEVERE(m_control->GetMediaInfo(m_delegate->m_device , m_delegate->m_instance , m_delegate), failed); if(dialog) dialog->Close(); return true; failed: CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - unable to open file %s", file.GetPath().c_str()); if(dialog) dialog->Close(); return false; }
/*---------------------------------------------------------------------- | PLT_MediaObject::FromDidl +---------------------------------------------------------------------*/ NPT_Result PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry) { NPT_String str, xml; NPT_Array<NPT_XmlElementNode*> children; NPT_Result res; // check if item is restricted (is default true?) if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str))) { m_Restricted = PLT_Service::IsTrue(str); } res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID); NPT_CHECK_LABEL_SEVERE(res, cleanup); res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID); NPT_CHECK_LABEL_SEVERE(res, cleanup); res = PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID); res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc); NPT_CHECK_LABEL_SEVERE(res, cleanup); res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp); NPT_CHECK_LABEL_SEVERE(res, cleanup); // read non-required elements PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc); PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc); PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp); m_People.artists.FromDidl(children); PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp); PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp); PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp); PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp); NPT_UInt32 value; if (NPT_FAILED(str.ToInteger(value))) value = 0; m_Recorded.episode_number = value; children.Clear(); PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp); for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { if (children[i]->GetText()) { m_Affiliation.genres.Add(*children[i]->GetText()); } } PLT_XmlHelper::GetChildText(entry, "albumArtURI", m_ExtraInfo.album_art_uri, didl_namespace_upnp); PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp); PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp); PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp); if (NPT_FAILED(str.ToInteger(value))) value = 0; m_MiscInfo.original_track_number = value; children.Clear(); PLT_XmlHelper::GetChildren(entry, children, "res"); for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) { PLT_MediaItemResource resource; // extract url if (children[i]->GetText() == NULL) { NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i])); } else { resource.m_Uri = *children[i]->GetText(); // basic uri validation, ignoring scheme (could be rtsp) NPT_HttpUrl url(resource.m_Uri, true); if (!url.IsValid()) { NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri); } } // extract protocol info NPT_String protocol_info; res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info); if (NPT_FAILED(res)) { NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i])); } else { resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info); if (!resource.m_ProtocolInfo.IsValid()) { NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info); } } // extract known attributes PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection); PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution); if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str))) { if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1; } if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str))) { if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) { // if error while converting, ignore and set to -1 to indicate we don't know the duration resource.m_Duration = (NPT_UInt32)-1; PLT_XmlHelper::RemoveAttribute(children[i], "duration"); } else { // DLNA: reformat duration in case it was not compliant str = PLT_Didl::FormatTimeStamp(resource.m_Duration); PLT_XmlHelper::SetAttribute(children[i], "duration", str); } } m_Resources.Add(resource); } // re serialize the entry didl as a we might need to pass it to a renderer // we may have modified the tree to "fix" issues, so as not to break a renderer // (don't write xml prefix as this didl could be part of a larger document) res = PLT_XmlHelper::Serialize(*entry, xml, false); NPT_CHECK_LABEL_SEVERE(res, cleanup); m_Didl = didl_header + xml + didl_footer; return NPT_SUCCESS; cleanup: return res; }
/*---------------------------------------------------------------------- | PLT_FileMediaServer::BuildFromFilePath +---------------------------------------------------------------------*/ PLT_MediaObject* PLT_FileMediaServer::BuildFromFilePath(const NPT_String& filepath, bool with_count /* = true */, NPT_SocketInfo* info /* = NULL */, bool keep_extension_in_title /* = false */) { NPT_String delimiter = m_DirDelimiter; NPT_String root = m_Path; PLT_MediaItemResource resource; PLT_MediaObject* object = NULL; int dir_delim_index; /* make sure this is a valid entry */ /* and retrieve the entry type (directory or file) */ NPT_DirectoryEntryInfo entry_info; if (!ProceedWithEntry(filepath, entry_info)) goto failure; /* find the last directory delimiter */ dir_delim_index = filepath.ReverseFind(delimiter); if (dir_delim_index < 0) goto failure; if (entry_info.type == NPT_FILE_TYPE) { object = new PLT_MediaItem(); /* we need a valid extension to retrieve the mimetype for the protocol info */ int ext_index = filepath.ReverseFind('.'); if (ext_index <= 0 || ext_index < dir_delim_index) { ext_index = filepath.GetLength(); } /* Set the title using the filename for now */ object->m_Title = filepath.SubString(dir_delim_index+1, keep_extension_in_title?filepath.GetLength():ext_index - dir_delim_index -1); if (object->m_Title.GetLength() == 0) goto failure; /* Set the protocol Info from the extension */ const char* ext = ((const char*)filepath) + ext_index; resource.m_ProtocolInfo = PLT_MediaItem::GetProtInfoFromExt(ext); if (resource.m_ProtocolInfo.GetLength() == 0) goto failure; /* Set the resource file size */ resource.m_Size = entry_info.size; /* format the resource URI */ NPT_String url = filepath.SubString(root.GetLength()); // get list of ip addresses NPT_List<NPT_String> ips; NPT_CHECK_LABEL_SEVERE(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure); // if we're passed an interface where we received the request from // move the ip to the top if (info && info->local_address.GetIpAddress().ToString() != "0.0.0.0") { ips.Remove(info->local_address.GetIpAddress().ToString()); ips.Insert(ips.GetFirstItem(), info->local_address.GetIpAddress().ToString()); } // iterate through list and build list of resources NPT_List<NPT_String>::Iterator ip = ips.GetFirstItem(); while (ip) { NPT_HttpUrl uri = m_FileBaseUri; NPT_HttpUrlQuery query; query.AddField("path", url); uri.SetHost(*ip); uri.SetQuery(query.ToString()); //uri.SetPath(uri.GetPath() + url); /* prepend the base URI and url encode it */ //resource.m_Uri = NPT_Uri::Encode(uri.ToString(), NPT_Uri::UnsafeCharsToEncode); resource.m_Uri = uri.ToString(); /* Look to see if a metadatahandler exists for this extension */ PLT_MetadataHandler* handler = NULL; NPT_Result res = NPT_ContainerFind(m_MetadataHandlers, PLT_MetadataHandlerFinder(ext), handler); if (NPT_SUCCEEDED(res) && handler) { /* if it failed loading data, reset the metadatahandler so we don't use it */ if (NPT_SUCCEEDED(handler->LoadFile(filepath))) { /* replace the title with the one from the Metadata */ NPT_String newTitle; if (handler->GetTitle(newTitle) != NULL) { object->m_Title = newTitle; } /* assign description */ handler->GetDescription(object->m_Description.long_description); /* assign album art uri if we haven't yet */ /* prepend the album art base URI and url encode it */ if (object->m_ExtraInfo.album_art_uri.GetLength() == 0) { NPT_HttpUrl uri = m_AlbumArtBaseUri; NPT_HttpUrlQuery query; query.AddField("path", url); uri.SetHost(*ip); uri.SetQuery(query.ToString()); //uri.SetPath(uri.GetPath() + url); object->m_ExtraInfo.album_art_uri = NPT_Uri::PercentEncode(uri.ToString(), NPT_Uri::UnsafeCharsToEncode); } /* duration */ handler->GetDuration((NPT_UInt32&)resource.m_Duration); /* protection */ handler->GetProtection(resource.m_Protection); } } object->m_ObjectClass.type = PLT_MediaItem::GetUPnPClassFromExt(ext); object->m_Resources.Add(resource); ++ip; } } else { object = new PLT_MediaContainer; /* Assign a title for this container */ if (filepath.Compare(root, true) == 0) { object->m_Title = "Root"; } else { object->m_Title = filepath.SubString(dir_delim_index+1, filepath.GetLength() - dir_delim_index -1); if (object->m_Title.GetLength() == 0) goto failure; } /* Get the number of children for this container */ if (with_count) { NPT_Cardinal count = 0; NPT_CHECK_LABEL_SEVERE(GetEntryCount(filepath, count), failure); ((PLT_MediaContainer*)object)->m_ChildrenCount = count; } object->m_ObjectClass.type = "object.container"; } /* is it the root? */ if (filepath.Compare(root, true) == 0) { object->m_ParentID = "-1"; object->m_ObjectID = "0"; } else { /* is the parent path the root? */ if (dir_delim_index == (int)root.GetLength() - 1) { object->m_ParentID = "0"; } else { object->m_ParentID = "0" + delimiter + filepath.SubString(root.GetLength(), dir_delim_index - root.GetLength()); } object->m_ObjectID = "0" + delimiter + filepath.SubString(root.GetLength()); } return object; failure: delete object; return NULL; }