1 /*****************************************************************
3 | Platinum - Synchronous Media Browser
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
7 | http://www.plutinosoft.com
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.
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
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.
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
33 ****************************************************************/
35 /*----------------------------------------------------------------------
37 +---------------------------------------------------------------------*/
38 #include "PltSyncMediaBrowser.h"
41 NPT_SET_LOCAL_LOGGER("platinum.media.server.syncbrowser")
43 /*----------------------------------------------------------------------
44 | PLT_SyncMediaBrowser::PLT_SyncMediaBrowser
45 +---------------------------------------------------------------------*/
46 PLT_SyncMediaBrowser::PLT_SyncMediaBrowser(PLT_CtrlPointReference
& ctrlPoint
,
47 bool use_cache
/* = false */,
48 PLT_MediaContainerChangesListener
* listener
/* = NULL */) :
49 PLT_MediaBrowser(ctrlPoint
),
50 m_ContainerListener(listener
),
56 /*----------------------------------------------------------------------
57 | PLT_SyncMediaBrowser::~PLT_SyncMediaBrowser
58 +---------------------------------------------------------------------*/
59 PLT_SyncMediaBrowser::~PLT_SyncMediaBrowser()
63 /* Blocks forever waiting for a response from a request
64 * It is expected the request to succeed or to timeout and return an error eventually
66 /*----------------------------------------------------------------------
67 | PLT_SyncMediaBrowser::WaitForResponse
68 +---------------------------------------------------------------------*/
70 PLT_SyncMediaBrowser::WaitForResponse(NPT_SharedVariable
& shared_var
)
72 return shared_var
.WaitUntilEquals(1, 30000);
75 /*----------------------------------------------------------------------
76 | PLT_SyncMediaBrowser::OnDeviceAdded
77 +---------------------------------------------------------------------*/
79 PLT_SyncMediaBrowser::OnDeviceAdded(PLT_DeviceDataReference
& device
)
81 NPT_String uuid
= device
->GetUUID();
83 // test if it's a media server
85 if (NPT_SUCCEEDED(device
->FindServiceByType("urn:schemas-upnp-org:service:ContentDirectory:*", service
))) {
86 NPT_AutoLock
lock(m_MediaServers
);
87 m_MediaServers
.Put(uuid
, device
);
90 return PLT_MediaBrowser::OnDeviceAdded(device
);
93 /*----------------------------------------------------------------------
94 | PLT_SyncMediaBrowser::OnDeviceRemoved
95 +---------------------------------------------------------------------*/
97 PLT_SyncMediaBrowser::OnDeviceRemoved(PLT_DeviceDataReference
& device
)
99 NPT_String uuid
= device
->GetUUID();
101 // Remove from our list of servers first if found
103 NPT_AutoLock
lock(m_MediaServers
);
104 m_MediaServers
.Erase(uuid
);
107 // clear cache for that device
108 if (m_UseCache
) m_Cache
.Clear(device
.AsPointer()->GetUUID());
110 return PLT_MediaBrowser::OnDeviceRemoved(device
);
113 /*----------------------------------------------------------------------
114 | PLT_SyncMediaBrowser::Find
115 +---------------------------------------------------------------------*/
117 PLT_SyncMediaBrowser::Find(const char* ip
, PLT_DeviceDataReference
& device
)
119 NPT_AutoLock
lock(m_MediaServers
);
120 const NPT_List
<PLT_DeviceMapEntry
*>::Iterator it
=
121 m_MediaServers
.GetEntries().Find(PLT_DeviceMapFinderByIp(ip
));
123 device
= (*it
)->GetValue();
129 static void OnResult(NPT_Result res
,
130 PLT_DeviceDataReference
& device
,
131 PLT_BrowseInfo
* info
,
134 NPT_COMPILER_UNUSED(device
);
136 if (!userdata
) return;
138 PLT_BrowseDataReference
* data
= (PLT_BrowseDataReference
*) userdata
;
140 if (NPT_SUCCEEDED(res
) && info
) {
141 (*data
)->info
= *info
;
143 (*data
)->shared_var
.SetValue(1);
147 /*----------------------------------------------------------------------
148 | PLT_SyncMediaBrowser::OnBrowseResult
149 +---------------------------------------------------------------------*/
151 PLT_SyncMediaBrowser::OnBrowseResult(NPT_Result res
,
152 PLT_DeviceDataReference
& device
,
153 PLT_BrowseInfo
* info
,
156 OnResult(res
, device
, info
, userdata
);
159 /*----------------------------------------------------------------------
160 | PLT_SyncMediaBrowser::OnSearchResult
161 +---------------------------------------------------------------------*/
163 PLT_SyncMediaBrowser::OnSearchResult(NPT_Result res
,
164 PLT_DeviceDataReference
& device
,
165 PLT_BrowseInfo
* info
,
168 OnResult(res
, device
, info
, userdata
);
171 /*----------------------------------------------------------------------
172 | PLT_SyncMediaBrowser::OnGetSearchCapabilitiesResult
173 +---------------------------------------------------------------------*/
175 PLT_SyncMediaBrowser::OnGetSearchCapabilitiesResult(NPT_Result res
,
176 PLT_DeviceDataReference
& device
,
177 NPT_String searchCapabilities
,
180 NPT_COMPILER_UNUSED(device
);
182 if (!userdata
) return;
184 PLT_CapabilitiesDataReference
* data
= (PLT_CapabilitiesDataReference
*) userdata
;
186 if (NPT_SUCCEEDED(res
)) {
187 (*data
)->capabilities
= searchCapabilities
;
189 (*data
)->shared_var
.SetValue(1);
193 /*----------------------------------------------------------------------
194 | PLT_SyncMediaBrowser::OnGetSortCapabilitiesResult
195 +---------------------------------------------------------------------*/
197 PLT_SyncMediaBrowser::OnGetSortCapabilitiesResult(NPT_Result res
,
198 PLT_DeviceDataReference
& device
,
199 NPT_String sortCapabilities
,
202 NPT_COMPILER_UNUSED(device
);
204 if (!userdata
) return;
206 PLT_CapabilitiesDataReference
* data
= (PLT_CapabilitiesDataReference
*) userdata
;
208 if (NPT_SUCCEEDED(res
)) {
209 (*data
)->capabilities
= sortCapabilities
;
211 (*data
)->shared_var
.SetValue(1);
215 /*----------------------------------------------------------------------
216 | PLT_SyncMediaBrowser::OnMSStateVariablesChanged
217 +---------------------------------------------------------------------*/
219 PLT_SyncMediaBrowser::OnMSStateVariablesChanged(PLT_Service
* service
,
220 NPT_List
<PLT_StateVariable
*>* vars
)
222 NPT_AutoLock
lock(m_MediaServers
);
224 PLT_DeviceDataReference device
;
225 const NPT_List
<PLT_DeviceMapEntry
*>::Iterator it
=
226 m_MediaServers
.GetEntries().Find(PLT_DeviceMapFinderByUUID(service
->GetDevice()->GetUUID()));
227 if (!it
) return; // device with this service has gone away
229 device
= (*it
)->GetValue();
230 PLT_StateVariable
* var
= PLT_StateVariable::Find(*vars
, "ContainerUpdateIDs");
232 // variable found, parse value
233 NPT_String value
= var
->GetValue();
234 NPT_String item_id
, update_id
;
237 while (value
.GetLength()) {
238 // look for container id
239 index
= value
.Find(',');
240 if (index
< 0) break;
241 item_id
= value
.Left(index
);
242 value
= value
.SubString(index
+1);
244 // look for update id
245 if (value
.GetLength()) {
246 index
= value
.Find(',');
247 update_id
= (index
<0)?value
:value
.Left(index
);
248 value
= (index
< 0) ? "" : value
.SubString(index
+ 1).GetChars();
250 // clear cache for that device
251 if (m_UseCache
) m_Cache
.Clear(device
->GetUUID(), item_id
);
254 if (m_ContainerListener
) m_ContainerListener
->OnContainerChanged(device
, item_id
, update_id
);
260 /*----------------------------------------------------------------------
261 | PLT_SyncMediaBrowser::BrowseSync
262 +---------------------------------------------------------------------*/
264 PLT_SyncMediaBrowser::BrowseSync(PLT_BrowseDataReference
& browse_data
,
265 PLT_DeviceDataReference
& device
,
266 const char* object_id
,
269 bool browse_metadata
,
275 browse_data
->shared_var
.SetValue(0);
276 browse_data
->info
.si
= index
;
278 // send off the browse packet. Note that this will
279 // not block. There is a call to WaitForResponse in order
280 // to block until the response comes back.
281 res
= PLT_MediaBrowser::Browse(device
,
282 (const char*)object_id
,
288 new PLT_BrowseDataReference(browse_data
));
289 NPT_CHECK_SEVERE(res
);
291 return WaitForResponse(browse_data
->shared_var
);
294 /*----------------------------------------------------------------------
295 | PLT_SyncMediaBrowser::SearchSync
296 +---------------------------------------------------------------------*/
298 PLT_SyncMediaBrowser::SearchSync(PLT_BrowseDataReference
& browse_data
,
299 PLT_DeviceDataReference
& device
,
300 const char* container_id
,
301 const char* search_criteria
,
308 browse_data
->shared_var
.SetValue(0);
309 browse_data
->info
.si
= index
;
311 // send off the search packet. Note that this will
312 // not block. There is a call to WaitForResponse in order
313 // to block until the response comes back.
314 res
= PLT_MediaBrowser::Search(device
,
320 new PLT_BrowseDataReference(browse_data
));
321 NPT_CHECK_SEVERE(res
);
323 return WaitForResponse(browse_data
->shared_var
);
326 /*----------------------------------------------------------------------
327 | PLT_SyncMediaBrowser::GetSearchCapabilitiesSync
328 +---------------------------------------------------------------------*/
330 PLT_SyncMediaBrowser::GetSearchCapabilitiesSync(PLT_DeviceDataReference
& device
,
331 NPT_String
& searchCapabilities
)
335 PLT_CapabilitiesDataReference
capabilities_data(new PLT_CapabilitiesData(), true);
336 capabilities_data
->shared_var
.SetValue(0);
338 // send of the GetSearchCapabilities packet. Note that this will
339 // not block. There is a call to WaitForResponse in order
340 // to block until the response comes back.
341 res
= PLT_MediaBrowser::GetSearchCapabilities(device
,
342 new PLT_CapabilitiesDataReference(capabilities_data
));
343 NPT_CHECK_SEVERE(res
);
345 res
= WaitForResponse(capabilities_data
->shared_var
);
346 NPT_CHECK_LABEL_WARNING(res
, done
);
348 if (NPT_FAILED(capabilities_data
->res
)) {
349 res
= capabilities_data
->res
;
350 NPT_CHECK_LABEL_WARNING(res
, done
);
353 searchCapabilities
= capabilities_data
->capabilities
;
359 /*----------------------------------------------------------------------
360 | PLT_SyncMediaBrowser::GetSortCapabilitiesSync
361 +---------------------------------------------------------------------*/
363 PLT_SyncMediaBrowser::GetSortCapabilitiesSync(PLT_DeviceDataReference
& device
,
364 NPT_String
& sortCapabilities
)
368 PLT_CapabilitiesDataReference
capabilities_data(new PLT_CapabilitiesData(), true);
369 capabilities_data
->shared_var
.SetValue(0);
371 // send of the GetSortCapabilities packet. Note that this will
372 // not block. There is a call to WaitForResponse in order
373 // to block until the response comes back.
374 res
= PLT_MediaBrowser::GetSortCapabilities(device
,
375 new PLT_CapabilitiesDataReference(capabilities_data
));
376 NPT_CHECK_SEVERE(res
);
378 res
= WaitForResponse(capabilities_data
->shared_var
);
379 NPT_CHECK_LABEL_WARNING(res
, done
);
381 if (NPT_FAILED(capabilities_data
->res
)) {
382 res
= capabilities_data
->res
;
383 NPT_CHECK_LABEL_WARNING(res
, done
);
386 sortCapabilities
= capabilities_data
->capabilities
;
392 /*----------------------------------------------------------------------
393 | PLT_SyncMediaBrowser::BrowseSync
394 +---------------------------------------------------------------------*/
396 PLT_SyncMediaBrowser::BrowseSync(PLT_DeviceDataReference
& device
,
397 const char* object_id
,
398 PLT_MediaObjectListReference
& list
,
399 bool metadata
, /* = false */
400 NPT_Int32 start
, /* = 0 */
401 NPT_Cardinal max_results
/* = 0 */)
403 NPT_Result res
= NPT_FAILURE
;
404 NPT_Int32 index
= start
;
405 NPT_UInt32 count
= 0;
407 // only cache metadata or if starting from 0 and asking for maximum
408 bool cache
= m_UseCache
&& (metadata
|| (start
== 0 && max_results
== 0));
410 // reset output params
413 // look into cache first
414 if (cache
&& NPT_SUCCEEDED(m_Cache
.Get(device
->GetUUID(), object_id
, list
))) return NPT_SUCCESS
;
417 PLT_BrowseDataReference
browse_data(new PLT_BrowseData());
419 // send off the browse packet. Note that this will
420 // not block. There is a call to WaitForResponse in order
421 // to block until the response comes back.
425 (const char*)object_id
,
427 metadata
?1:200, // DLNA recommendations for browsing children is no more than 30 at a time
429 NPT_CHECK_LABEL_WARNING(res
, done
);
431 if (NPT_FAILED(browse_data
->res
)) {
432 res
= browse_data
->res
;
433 NPT_CHECK_LABEL_WARNING(res
, done
);
436 // server returned no more, bail now
437 if (browse_data
->info
.nr
== 0)
440 if (browse_data
->info
.nr
!= browse_data
->info
.items
->GetItemCount()) {
441 NPT_LOG_WARNING_2("Server returned unexpected number of items (%d vs %d)",
442 browse_data
->info
.nr
, browse_data
->info
.items
->GetItemCount());
444 count
+= std::max
<NPT_UInt32
>(browse_data
->info
.nr
, browse_data
->info
.items
->GetItemCount());
447 list
= browse_data
->info
.items
;
449 list
->Add(*browse_data
->info
.items
);
450 // clear the list items so that the data inside is not
451 // cleaned up by PLT_MediaItemList dtor since we copied
452 // each pointer into the new list.
453 browse_data
->info
.items
->Clear();
456 // stop now if our list contains exactly what the server said it had.
457 // Note that the server could return 0 if it didn't know how many items were
458 // available. In this case we have to continue browsing until
459 // nothing is returned back by the server.
460 // Unless we were told to stop after reaching a certain amount to avoid
462 // (some servers may return a total matches out of whack at some point too)
463 if ((browse_data
->info
.tm
&& browse_data
->info
.tm
<= count
) ||
464 (max_results
&& count
>= max_results
))
467 // ask for the next chunk of entries
473 if (cache
&& NPT_SUCCEEDED(res
) && !list
.IsNull() && list
->GetItemCount()) {
474 m_Cache
.Put(device
->GetUUID(), object_id
, list
);
477 // clear entire cache data for device if failed, the device could be gone
478 if (NPT_FAILED(res
) && cache
) m_Cache
.Clear(device
->GetUUID());
483 /*----------------------------------------------------------------------
484 | PLT_SyncMediaBrowser::SearchSync
485 +---------------------------------------------------------------------*/
487 PLT_SyncMediaBrowser::SearchSync(PLT_DeviceDataReference
& device
,
488 const char* container_id
,
489 const char* search_criteria
,
490 PLT_MediaObjectListReference
& list
,
491 NPT_Int32 start
, /* = 0 */
492 NPT_Cardinal max_results
/* = 0 */)
494 NPT_Result res
= NPT_FAILURE
;
495 NPT_Int32 index
= start
;
496 NPT_UInt32 count
= 0;
498 // reset output params
502 PLT_BrowseDataReference
browse_data(new PLT_BrowseData(), true);
504 // send off the search packet. Note that this will
505 // not block. There is a call to WaitForResponse in order
506 // to block until the response comes back.
513 200); // DLNA recommendations for browsing children is no more than 30 at a time
515 NPT_CHECK_LABEL_WARNING(res
, done
);
517 if (NPT_FAILED(browse_data
->res
)) {
518 res
= browse_data
->res
;
519 NPT_CHECK_LABEL_WARNING(res
, done
);
522 // server returned no more, bail now
523 if (browse_data
->info
.nr
== 0)
526 if (browse_data
->info
.nr
!= browse_data
->info
.items
->GetItemCount()) {
527 NPT_LOG_WARNING_2("Server returned unexpected number of items (%d vs %d)",
528 browse_data
->info
.nr
, browse_data
->info
.items
->GetItemCount());
530 count
+= std::max
<NPT_UInt32
>(browse_data
->info
.nr
, browse_data
->info
.items
->GetItemCount());
533 list
= browse_data
->info
.items
;
535 list
->Add(*browse_data
->info
.items
);
536 // clear the list items so that the data inside is not
537 // cleaned up by PLT_MediaItemList dtor since we copied
538 // each pointer into the new list.
539 browse_data
->info
.items
->Clear();
542 // stop now if our list contains exactly what the server said it had.
543 // Note that the server could return 0 if it didn't know how many items were
544 // available. In this case we have to continue browsing until
545 // nothing is returned back by the server.
546 // Unless we were told to stop after reaching a certain amount to avoid
548 // (some servers may return a total matches out of whack at some point too)
549 if ((browse_data
->info
.tm
&& browse_data
->info
.tm
<= count
) ||
550 (max_results
&& count
>= max_results
))
553 // ask for the next chunk of entries
561 /*----------------------------------------------------------------------
562 | PLT_SyncMediaBrowser::IsCached
563 +---------------------------------------------------------------------*/
565 PLT_SyncMediaBrowser::IsCached(const char* uuid
, const char* object_id
)
567 NPT_AutoLock
lock(m_MediaServers
);
568 const NPT_List
<PLT_DeviceMapEntry
*>::Iterator it
=
569 m_MediaServers
.GetEntries().Find(PLT_DeviceMapFinderByUUID(uuid
));
572 return false; // device with this service has gone away
575 PLT_MediaObjectListReference list
;
576 return NPT_SUCCEEDED(m_Cache
.Get(uuid
, object_id
, list
))?true:false;