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