1 /*****************************************************************
2 |
3 |   Platinum - Synchronous Media Browser
4 |
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
8 |
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
13 |
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
21 |
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 | GNU General Public License for more details.
26 |
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
32 |
33 ****************************************************************/
34 
35 /*----------------------------------------------------------------------
36 |   includes
37 +---------------------------------------------------------------------*/
38 #include "PltSyncMediaBrowser.h"
39 
40 NPT_SET_LOCAL_LOGGER("platinum.media.server.syncbrowser")
41 
42 /*----------------------------------------------------------------------
43 |   PLT_SyncMediaBrowser::PLT_SyncMediaBrowser
44 +---------------------------------------------------------------------*/
PLT_SyncMediaBrowser(PLT_CtrlPointReference & ctrlPoint,bool use_cache,PLT_MediaContainerChangesListener * listener)45 PLT_SyncMediaBrowser::PLT_SyncMediaBrowser(PLT_CtrlPointReference&            ctrlPoint,
46                                            bool                               use_cache /* = false */,
47                                            PLT_MediaContainerChangesListener* listener /* = NULL */) :
48     PLT_MediaBrowser(ctrlPoint),
49     m_ContainerListener(listener),
50     m_UseCache(use_cache)
51 {
52     SetDelegate(this);
53 }
54 
55 /*----------------------------------------------------------------------
56 |   PLT_SyncMediaBrowser::~PLT_SyncMediaBrowser
57 +---------------------------------------------------------------------*/
~PLT_SyncMediaBrowser()58 PLT_SyncMediaBrowser::~PLT_SyncMediaBrowser()
59 {
60 }
61 
62 /*  Blocks forever waiting for a response from a request
63  *  It is expected the request to succeed or to timeout and return an error eventually
64  */
65 /*----------------------------------------------------------------------
66 |   PLT_SyncMediaBrowser::WaitForResponse
67 +---------------------------------------------------------------------*/
68 NPT_Result
WaitForResponse(NPT_SharedVariable & shared_var)69 PLT_SyncMediaBrowser::WaitForResponse(NPT_SharedVariable& shared_var)
70 {
71     return shared_var.WaitUntilEquals(1, 30000);
72 }
73 
74 /*----------------------------------------------------------------------
75 |   PLT_SyncMediaBrowser::OnDeviceAdded
76 +---------------------------------------------------------------------*/
77 NPT_Result
OnDeviceAdded(PLT_DeviceDataReference & device)78 PLT_SyncMediaBrowser::OnDeviceAdded(PLT_DeviceDataReference& device)
79 {
80     NPT_String uuid = device->GetUUID();
81 
82     // test if it's a media server
83     PLT_Service* service;
84     if (NPT_SUCCEEDED(device->FindServiceByType("urn:schemas-upnp-org:service:ContentDirectory:*", service))) {
85         NPT_AutoLock lock(m_MediaServers);
86         m_MediaServers.Put(uuid, device);
87     }
88 
89     return PLT_MediaBrowser::OnDeviceAdded(device);
90 }
91 
92 /*----------------------------------------------------------------------
93 |   PLT_SyncMediaBrowser::OnDeviceRemoved
94 +---------------------------------------------------------------------*/
95 NPT_Result
OnDeviceRemoved(PLT_DeviceDataReference & device)96 PLT_SyncMediaBrowser::OnDeviceRemoved(PLT_DeviceDataReference& device)
97 {
98     NPT_String uuid = device->GetUUID();
99 
100     // Remove from our list of servers first if found
101     {
102         NPT_AutoLock lock(m_MediaServers);
103         m_MediaServers.Erase(uuid);
104     }
105 
106     // clear cache for that device
107     if (m_UseCache) m_Cache.Clear(device.AsPointer()->GetUUID());
108 
109     return PLT_MediaBrowser::OnDeviceRemoved(device);
110 }
111 
112 /*----------------------------------------------------------------------
113 |   PLT_SyncMediaBrowser::Find
114 +---------------------------------------------------------------------*/
115 NPT_Result
Find(const char * ip,PLT_DeviceDataReference & device)116 PLT_SyncMediaBrowser::Find(const char* ip, PLT_DeviceDataReference& device)
117 {
118     NPT_AutoLock lock(m_MediaServers);
119     const NPT_List<PLT_DeviceMapEntry*>::Iterator it =
120         m_MediaServers.GetEntries().Find(PLT_DeviceMapFinderByIp(ip));
121     if (it) {
122         device = (*it)->GetValue();
123         return NPT_SUCCESS;
124     }
125     return NPT_FAILURE;
126 }
127 
128 /*----------------------------------------------------------------------
129 |   PLT_SyncMediaBrowser::OnBrowseResult
130 +---------------------------------------------------------------------*/
131 void
OnBrowseResult(NPT_Result res,PLT_DeviceDataReference & device,PLT_BrowseInfo * info,void * userdata)132 PLT_SyncMediaBrowser::OnBrowseResult(NPT_Result               res,
133                                      PLT_DeviceDataReference& device,
134                                      PLT_BrowseInfo*          info,
135                                      void*                    userdata)
136 {
137     NPT_COMPILER_UNUSED(device);
138 
139     if (!userdata) return;
140 
141     PLT_BrowseDataReference* data = (PLT_BrowseDataReference*) userdata;
142     (*data)->res = res;
143     if (NPT_SUCCEEDED(res) && info) {
144         (*data)->info = *info;
145     }
146     (*data)->shared_var.SetValue(1);
147     delete data;
148 }
149 
150 /*----------------------------------------------------------------------
151 |   PLT_SyncMediaBrowser::OnMSStateVariablesChanged
152 +---------------------------------------------------------------------*/
153 void
OnMSStateVariablesChanged(PLT_Service * service,NPT_List<PLT_StateVariable * > * vars)154 PLT_SyncMediaBrowser::OnMSStateVariablesChanged(PLT_Service*                  service,
155                                                 NPT_List<PLT_StateVariable*>* vars)
156 {
157     NPT_AutoLock lock(m_MediaServers);
158 
159     PLT_DeviceDataReference device;
160     const NPT_List<PLT_DeviceMapEntry*>::Iterator it =
161         m_MediaServers.GetEntries().Find(PLT_DeviceMapFinderByUUID(service->GetDevice()->GetUUID()));
162     if (!it) return; // device with this service has gone away
163 
164     device = (*it)->GetValue();
165     PLT_StateVariable* var = PLT_StateVariable::Find(*vars, "ContainerUpdateIDs");
166     if (var) {
167         // variable found, parse value
168         NPT_String value = var->GetValue();
169         NPT_String item_id, update_id;
170         int index;
171 
172         while (value.GetLength()) {
173             // look for container id
174             index = value.Find(',');
175             if (index < 0) break;
176             item_id = value.Left(index);
177             value = value.SubString(index+1);
178 
179             // look for update id
180             if (value.GetLength()) {
181                 index = value.Find(',');
182                 update_id = (index<0)?value:value.Left(index);
183                 value = (index<0)?"":value.SubString(index+1);
184 
185                 // clear cache for that device
186                 if (m_UseCache) m_Cache.Clear(device->GetUUID(), item_id);
187 
188                 // notify listener
189                 if (m_ContainerListener) m_ContainerListener->OnContainerChanged(device, item_id, update_id);
190             }
191         }
192     }
193 }
194 
195 /*----------------------------------------------------------------------
196 |   PLT_SyncMediaBrowser::BrowseSync
197 +---------------------------------------------------------------------*/
198 NPT_Result
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)199 PLT_SyncMediaBrowser::BrowseSync(PLT_BrowseDataReference& browse_data,
200                                  PLT_DeviceDataReference& device,
201                                  const char*              object_id,
202                                  NPT_Int32                index,
203                                  NPT_Int32                count,
204                                  bool                     browse_metadata,
205                                  const char*              filter,
206                                  const char*              sort)
207 {
208     NPT_Result res;
209 
210     browse_data->shared_var.SetValue(0);
211     browse_data->info.si = index;
212 
213     // send off the browse packet.  Note that this will
214     // not block.  There is a call to WaitForResponse in order
215     // to block until the response comes back.
216     res = PLT_MediaBrowser::Browse(device,
217         (const char*)object_id,
218         index,
219         count,
220         browse_metadata,
221         filter,
222         sort,
223         new PLT_BrowseDataReference(browse_data));
224     NPT_CHECK_SEVERE(res);
225 
226     return WaitForResponse(browse_data->shared_var);
227 }
228 
229 /*----------------------------------------------------------------------
230 |   PLT_SyncMediaBrowser::BrowseSync
231 +---------------------------------------------------------------------*/
232 NPT_Result
BrowseSync(PLT_DeviceDataReference & device,const char * object_id,PLT_MediaObjectListReference & list,bool metadata,NPT_Int32 start,NPT_Cardinal max_results,const char * filter,const char * sort)233 PLT_SyncMediaBrowser::BrowseSync(PLT_DeviceDataReference&      device,
234                                  const char*                   object_id,
235                                  PLT_MediaObjectListReference& list,
236                                  bool                          metadata, /* = false */
237                                  NPT_Int32                     start, /* = 0 */
238                                  NPT_Cardinal                  max_results, /* = 0 */
239                                  const char*                   filter, /* = PLT_DEFAULT_FILTER */
240                                  const char*                   sort /* = "" */)
241 {
242     NPT_Result res = NPT_FAILURE;
243     NPT_Int32  index = start;
244 
245     // only cache metadata or if starting from 0 and asking for maximum
246     bool cache = m_UseCache && (metadata || (start == 0 && max_results == 0));
247 
248     // reset output params
249     list = NULL;
250 
251     // look into cache first
252     if (cache && NPT_SUCCEEDED(m_Cache.Get(device->GetUUID(), object_id, list))) return NPT_SUCCESS;
253 
254     do {
255         PLT_BrowseDataReference browse_data(new PLT_BrowseData());
256 
257         // send off the browse packet.  Note that this will
258         // block until server responds or times out.
259         res = BrowseSync(
260             browse_data,
261             device,
262             (const char*)object_id,
263             index,
264             metadata?1:30, // DLNA recommendations for browsing children is no more than 30 at a time
265             metadata,
266             filter,
267             sort);
268         NPT_CHECK_LABEL_WARNING(res, done);
269 
270         if (NPT_FAILED(browse_data->res)) {
271             res = browse_data->res;
272             NPT_CHECK_LABEL_WARNING(res, done);
273         }
274 
275         // server returned no more, bail now
276         if (browse_data->info.items->GetItemCount() == 0)
277             break;
278 
279         if (list.IsNull()) {
280             list = browse_data->info.items;
281         } else {
282             list->Add(*browse_data->info.items);
283             // clear the list items so that the data inside is not
284             // cleaned up by PLT_MediaItemList destructor since we copied
285             // each pointer into the new list.
286             browse_data->info.items->Clear();
287         }
288 
289         // stop now if our list contains exactly what the server said it had.
290         // Note that the server could return 0 if it didn't know how many items were
291         // available. In this case we have to continue browsing until
292         // nothing is returned back by the server.
293         // Unless we were told to stop after reaching a certain amount to avoid
294         // lengthy delays.
295         // (some servers may return a total matches out of whack at some point too)
296         if ((browse_data->info.tm && browse_data->info.tm <= list->GetItemCount()) ||
297             (max_results && list->GetItemCount() >= max_results))
298             break;
299 
300         // ask for the next chunk of entries
301         index = list->GetItemCount();
302     } while(1);
303 
304 done:
305     // cache the result
306     if (cache && NPT_SUCCEEDED(res) && !list.IsNull() && list->GetItemCount()) {
307         m_Cache.Put(device->GetUUID(), object_id, list);
308     }
309 
310     // clear entire cache data for device if failed, the device could be gone
311     if (NPT_FAILED(res) && cache) m_Cache.Clear(device->GetUUID());
312 
313     return res;
314 }
315 
316 /*----------------------------------------------------------------------
317 |   PLT_SyncMediaBrowser::IsCached
318 +---------------------------------------------------------------------*/
319 bool
IsCached(const char * uuid,const char * object_id)320 PLT_SyncMediaBrowser::IsCached(const char* uuid, const char* object_id)
321 {
322     NPT_AutoLock lock(m_MediaServers);
323     const NPT_List<PLT_DeviceMapEntry*>::Iterator it =
324         m_MediaServers.GetEntries().Find(PLT_DeviceMapFinderByUUID(uuid));
325     if (!it) {
326         m_Cache.Clear(uuid);
327         return false; // device with this service has gone away
328     }
329 
330     PLT_MediaObjectListReference list;
331     return NPT_SUCCEEDED(m_Cache.Get(uuid, object_id, list))?true:false;
332 }
333 
334