diff options
Diffstat (limited to 'lib/libUPnP/Platinum/Source/Devices/MediaServer/PltSyncMediaBrowser.cpp')
-rw-r--r-- | lib/libUPnP/Platinum/Source/Devices/MediaServer/PltSyncMediaBrowser.cpp | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltSyncMediaBrowser.cpp b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltSyncMediaBrowser.cpp new file mode 100644 index 0000000..794b56b --- /dev/null +++ b/lib/libUPnP/Platinum/Source/Devices/MediaServer/PltSyncMediaBrowser.cpp @@ -0,0 +1,578 @@ +/***************************************************************** +| +| Platinum - Synchronous Media Browser +| +| Copyright (c) 2004-2010, Plutinosoft, LLC. +| All rights reserved. +| http://www.plutinosoft.com +| +| 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. +| +| OEMs, ISVs, VARs and other distributors that combine and +| distribute commercially licensed software with Platinum software +| and do not wish to distribute the source code for the commercially +| licensed software under version 2, or (at your option) any later +| version, of the GNU General Public License (the "GPL") must enter +| into a commercial license agreement with Plutinosoft, LLC. +| licensing@plutinosoft.com +| +| 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; see the file LICENSE.txt. If not, write to +| the Free Software Foundation, Inc., +| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +| http://www.gnu.org/licenses/gpl-2.0.html +| +****************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include "PltSyncMediaBrowser.h" +#include <algorithm> + +NPT_SET_LOCAL_LOGGER("platinum.media.server.syncbrowser") + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::PLT_SyncMediaBrowser ++---------------------------------------------------------------------*/ +PLT_SyncMediaBrowser::PLT_SyncMediaBrowser(PLT_CtrlPointReference& ctrlPoint, + bool use_cache /* = false */, + PLT_MediaContainerChangesListener* listener /* = NULL */) : + PLT_MediaBrowser(ctrlPoint), + m_ContainerListener(listener), + m_UseCache(use_cache) +{ + SetDelegate(this); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::~PLT_SyncMediaBrowser ++---------------------------------------------------------------------*/ +PLT_SyncMediaBrowser::~PLT_SyncMediaBrowser() +{ +} + +/* Blocks forever waiting for a response from a request + * It is expected the request to succeed or to timeout and return an error eventually + */ +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::WaitForResponse ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::WaitForResponse(NPT_SharedVariable& shared_var) +{ + return shared_var.WaitUntilEquals(1, 30000); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnDeviceAdded ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::OnDeviceAdded(PLT_DeviceDataReference& device) +{ + NPT_String uuid = device->GetUUID(); + + // test if it's a media server + PLT_Service* service; + if (NPT_SUCCEEDED(device->FindServiceByType("urn:schemas-upnp-org:service:ContentDirectory:*", service))) { + NPT_AutoLock lock(m_MediaServers); + m_MediaServers.Put(uuid, device); + } + + return PLT_MediaBrowser::OnDeviceAdded(device); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnDeviceRemoved ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::OnDeviceRemoved(PLT_DeviceDataReference& device) +{ + NPT_String uuid = device->GetUUID(); + + // Remove from our list of servers first if found + { + NPT_AutoLock lock(m_MediaServers); + m_MediaServers.Erase(uuid); + } + + // clear cache for that device + if (m_UseCache) m_Cache.Clear(device.AsPointer()->GetUUID()); + + return PLT_MediaBrowser::OnDeviceRemoved(device); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::Find ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::Find(const char* ip, PLT_DeviceDataReference& device) +{ + NPT_AutoLock lock(m_MediaServers); + const NPT_List<PLT_DeviceMapEntry*>::Iterator it = + m_MediaServers.GetEntries().Find(PLT_DeviceMapFinderByIp(ip)); + if (it) { + device = (*it)->GetValue(); + return NPT_SUCCESS; + } + return NPT_FAILURE; +} + +static void OnResult(NPT_Result res, + PLT_DeviceDataReference& device, + PLT_BrowseInfo* info, + void* userdata) +{ + NPT_COMPILER_UNUSED(device); + + if (!userdata) return; + + PLT_BrowseDataReference* data = (PLT_BrowseDataReference*) userdata; + (*data)->res = res; + if (NPT_SUCCEEDED(res) && info) { + (*data)->info = *info; + } + (*data)->shared_var.SetValue(1); + delete data; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnBrowseResult ++---------------------------------------------------------------------*/ +void +PLT_SyncMediaBrowser::OnBrowseResult(NPT_Result res, + PLT_DeviceDataReference& device, + PLT_BrowseInfo* info, + void* userdata) +{ + OnResult(res, device, info, userdata); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnSearchResult ++---------------------------------------------------------------------*/ +void +PLT_SyncMediaBrowser::OnSearchResult(NPT_Result res, + PLT_DeviceDataReference& device, + PLT_BrowseInfo* info, + void* userdata) +{ + OnResult(res, device, info, userdata); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnGetSearchCapabilitiesResult ++---------------------------------------------------------------------*/ +void +PLT_SyncMediaBrowser::OnGetSearchCapabilitiesResult(NPT_Result res, + PLT_DeviceDataReference& device, + NPT_String searchCapabilities, + void* userdata) +{ + NPT_COMPILER_UNUSED(device); + + if (!userdata) return; + + PLT_CapabilitiesDataReference* data = (PLT_CapabilitiesDataReference*) userdata; + (*data)->res = res; + if (NPT_SUCCEEDED(res)) { + (*data)->capabilities = searchCapabilities; + } + (*data)->shared_var.SetValue(1); + delete data; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnGetSortCapabilitiesResult ++---------------------------------------------------------------------*/ +void +PLT_SyncMediaBrowser::OnGetSortCapabilitiesResult(NPT_Result res, + PLT_DeviceDataReference& device, + NPT_String sortCapabilities, + void* userdata) +{ + NPT_COMPILER_UNUSED(device); + + if (!userdata) return; + + PLT_CapabilitiesDataReference* data = (PLT_CapabilitiesDataReference*) userdata; + (*data)->res = res; + if (NPT_SUCCEEDED(res)) { + (*data)->capabilities = sortCapabilities; + } + (*data)->shared_var.SetValue(1); + delete data; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::OnMSStateVariablesChanged ++---------------------------------------------------------------------*/ +void +PLT_SyncMediaBrowser::OnMSStateVariablesChanged(PLT_Service* service, + NPT_List<PLT_StateVariable*>* vars) +{ + NPT_AutoLock lock(m_MediaServers); + + PLT_DeviceDataReference device; + const NPT_List<PLT_DeviceMapEntry*>::Iterator it = + m_MediaServers.GetEntries().Find(PLT_DeviceMapFinderByUUID(service->GetDevice()->GetUUID())); + if (!it) return; // device with this service has gone away + + device = (*it)->GetValue(); + PLT_StateVariable* var = PLT_StateVariable::Find(*vars, "ContainerUpdateIDs"); + if (var) { + // variable found, parse value + NPT_String value = var->GetValue(); + NPT_String item_id, update_id; + int index; + + while (value.GetLength()) { + // look for container id + index = value.Find(','); + if (index < 0) break; + item_id = value.Left(index); + value = value.SubString(index+1); + + // look for update id + if (value.GetLength()) { + index = value.Find(','); + update_id = (index<0)?value:value.Left(index); + value = (index < 0) ? "" : value.SubString(index + 1).GetChars(); + + // clear cache for that device + if (m_UseCache) m_Cache.Clear(device->GetUUID(), item_id); + + // notify listener + if (m_ContainerListener) m_ContainerListener->OnContainerChanged(device, item_id, update_id); + } + } + } +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::BrowseSync ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::BrowseSync(PLT_BrowseDataReference& browse_data, + PLT_DeviceDataReference& device, + const char* object_id, + NPT_Int32 index, + NPT_Int32 count, + bool browse_metadata, + const char* filter, + const char* sort) +{ + NPT_Result res; + + browse_data->shared_var.SetValue(0); + browse_data->info.si = index; + + // send off the browse packet. Note that this will + // not block. There is a call to WaitForResponse in order + // to block until the response comes back. + res = PLT_MediaBrowser::Browse(device, + (const char*)object_id, + index, + count, + browse_metadata, + filter, + sort, + new PLT_BrowseDataReference(browse_data)); + NPT_CHECK_SEVERE(res); + + return WaitForResponse(browse_data->shared_var); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::SearchSync ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::SearchSync(PLT_BrowseDataReference& browse_data, + PLT_DeviceDataReference& device, + const char* container_id, + const char* search_criteria, + NPT_Int32 index, + NPT_Int32 count, + const char* filter) +{ + NPT_Result res; + + browse_data->shared_var.SetValue(0); + browse_data->info.si = index; + + // send off the search packet. Note that this will + // not block. There is a call to WaitForResponse in order + // to block until the response comes back. + res = PLT_MediaBrowser::Search(device, + container_id, + search_criteria, + index, + count, + filter, + new PLT_BrowseDataReference(browse_data)); + NPT_CHECK_SEVERE(res); + + return WaitForResponse(browse_data->shared_var); +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::GetSearchCapabilitiesSync ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::GetSearchCapabilitiesSync(PLT_DeviceDataReference& device, + NPT_String& searchCapabilities) +{ + NPT_Result res; + + PLT_CapabilitiesDataReference capabilities_data(new PLT_CapabilitiesData(), true); + capabilities_data->shared_var.SetValue(0); + + // send of the GetSearchCapabilities packet. Note that this will + // not block. There is a call to WaitForResponse in order + // to block until the response comes back. + res = PLT_MediaBrowser::GetSearchCapabilities(device, + new PLT_CapabilitiesDataReference(capabilities_data)); + NPT_CHECK_SEVERE(res); + + res = WaitForResponse(capabilities_data->shared_var); + NPT_CHECK_LABEL_WARNING(res, done); + + if (NPT_FAILED(capabilities_data->res)) { + res = capabilities_data->res; + NPT_CHECK_LABEL_WARNING(res, done); + } + + searchCapabilities = capabilities_data->capabilities; + +done: + return res; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::GetSortCapabilitiesSync ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::GetSortCapabilitiesSync(PLT_DeviceDataReference& device, + NPT_String& sortCapabilities) +{ + NPT_Result res; + + PLT_CapabilitiesDataReference capabilities_data(new PLT_CapabilitiesData(), true); + capabilities_data->shared_var.SetValue(0); + + // send of the GetSortCapabilities packet. Note that this will + // not block. There is a call to WaitForResponse in order + // to block until the response comes back. + res = PLT_MediaBrowser::GetSortCapabilities(device, + new PLT_CapabilitiesDataReference(capabilities_data)); + NPT_CHECK_SEVERE(res); + + res = WaitForResponse(capabilities_data->shared_var); + NPT_CHECK_LABEL_WARNING(res, done); + + if (NPT_FAILED(capabilities_data->res)) { + res = capabilities_data->res; + NPT_CHECK_LABEL_WARNING(res, done); + } + + sortCapabilities = capabilities_data->capabilities; + +done: + return res; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::BrowseSync ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::BrowseSync(PLT_DeviceDataReference& device, + const char* object_id, + PLT_MediaObjectListReference& list, + bool metadata, /* = false */ + NPT_Int32 start, /* = 0 */ + NPT_Cardinal max_results /* = 0 */) +{ + NPT_Result res = NPT_FAILURE; + NPT_Int32 index = start; + NPT_UInt32 count = 0; + + // only cache metadata or if starting from 0 and asking for maximum + bool cache = m_UseCache && (metadata || (start == 0 && max_results == 0)); + + // reset output params + list = NULL; + + // look into cache first + if (cache && NPT_SUCCEEDED(m_Cache.Get(device->GetUUID(), object_id, list))) return NPT_SUCCESS; + + do { + PLT_BrowseDataReference browse_data(new PLT_BrowseData()); + + // send off the browse packet. Note that this will + // not block. There is a call to WaitForResponse in order + // to block until the response comes back. + res = BrowseSync( + browse_data, + device, + (const char*)object_id, + index, + metadata?1:200, // DLNA recommendations for browsing children is no more than 30 at a time + metadata); + NPT_CHECK_LABEL_WARNING(res, done); + + if (NPT_FAILED(browse_data->res)) { + res = browse_data->res; + NPT_CHECK_LABEL_WARNING(res, done); + } + + // server returned no more, bail now + if (browse_data->info.nr == 0) + break; + + if (browse_data->info.nr != browse_data->info.items->GetItemCount()) { + NPT_LOG_WARNING_2("Server returned unexpected number of items (%d vs %d)", + browse_data->info.nr, browse_data->info.items->GetItemCount()); + } + count += std::max<NPT_UInt32>(browse_data->info.nr, browse_data->info.items->GetItemCount()); + + if (list.IsNull()) { + list = browse_data->info.items; + } else { + list->Add(*browse_data->info.items); + // clear the list items so that the data inside is not + // cleaned up by PLT_MediaItemList dtor since we copied + // each pointer into the new list. + browse_data->info.items->Clear(); + } + + // stop now if our list contains exactly what the server said it had. + // Note that the server could return 0 if it didn't know how many items were + // available. In this case we have to continue browsing until + // nothing is returned back by the server. + // Unless we were told to stop after reaching a certain amount to avoid + // length delays + // (some servers may return a total matches out of whack at some point too) + if ((browse_data->info.tm && browse_data->info.tm <= count) || + (max_results && count >= max_results)) + break; + + // ask for the next chunk of entries + index = count; + } while(1); + +done: + // cache the result + if (cache && NPT_SUCCEEDED(res) && !list.IsNull() && list->GetItemCount()) { + m_Cache.Put(device->GetUUID(), object_id, list); + } + + // clear entire cache data for device if failed, the device could be gone + if (NPT_FAILED(res) && cache) m_Cache.Clear(device->GetUUID()); + + return res; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::SearchSync ++---------------------------------------------------------------------*/ +NPT_Result +PLT_SyncMediaBrowser::SearchSync(PLT_DeviceDataReference& device, + const char* container_id, + const char* search_criteria, + PLT_MediaObjectListReference& list, + NPT_Int32 start, /* = 0 */ + NPT_Cardinal max_results /* = 0 */) +{ + NPT_Result res = NPT_FAILURE; + NPT_Int32 index = start; + NPT_UInt32 count = 0; + + // reset output params + list = NULL; + + do { + PLT_BrowseDataReference browse_data(new PLT_BrowseData(), true); + + // send off the search packet. Note that this will + // not block. There is a call to WaitForResponse in order + // to block until the response comes back. + res = SearchSync( + browse_data, + device, + container_id, + search_criteria, + index, + 200); // DLNA recommendations for browsing children is no more than 30 at a time + + NPT_CHECK_LABEL_WARNING(res, done); + + if (NPT_FAILED(browse_data->res)) { + res = browse_data->res; + NPT_CHECK_LABEL_WARNING(res, done); + } + + // server returned no more, bail now + if (browse_data->info.nr == 0) + break; + + if (browse_data->info.nr != browse_data->info.items->GetItemCount()) { + NPT_LOG_WARNING_2("Server returned unexpected number of items (%d vs %d)", + browse_data->info.nr, browse_data->info.items->GetItemCount()); + } + count += std::max<NPT_UInt32>(browse_data->info.nr, browse_data->info.items->GetItemCount()); + + if (list.IsNull()) { + list = browse_data->info.items; + } else { + list->Add(*browse_data->info.items); + // clear the list items so that the data inside is not + // cleaned up by PLT_MediaItemList dtor since we copied + // each pointer into the new list. + browse_data->info.items->Clear(); + } + + // stop now if our list contains exactly what the server said it had. + // Note that the server could return 0 if it didn't know how many items were + // available. In this case we have to continue browsing until + // nothing is returned back by the server. + // Unless we were told to stop after reaching a certain amount to avoid + // length delays + // (some servers may return a total matches out of whack at some point too) + if ((browse_data->info.tm && browse_data->info.tm <= count) || + (max_results && count >= max_results)) + break; + + // ask for the next chunk of entries + index = count; + } while(1); + +done: + return res; +} + +/*---------------------------------------------------------------------- +| PLT_SyncMediaBrowser::IsCached ++---------------------------------------------------------------------*/ +bool +PLT_SyncMediaBrowser::IsCached(const char* uuid, const char* object_id) +{ + NPT_AutoLock lock(m_MediaServers); + const NPT_List<PLT_DeviceMapEntry*>::Iterator it = + m_MediaServers.GetEntries().Find(PLT_DeviceMapFinderByUUID(uuid)); + if (!it) { + m_Cache.Clear(uuid); + return false; // device with this service has gone away + } + + PLT_MediaObjectListReference list; + return NPT_SUCCEEDED(m_Cache.Get(uuid, object_id, list))?true:false; +} + |