sbMetadataChannel.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 // INCLUDES ===================================================================
28 #include <nspr.h>
29 #include <nscore.h>
30 
31 #include <nsStringGlue.h>
32 #include <nsIInputStream.h>
33 #include <nsIResumableChannel.h>
34 #include <nsIChannel.h>
35 #include <nsIURI.h>
36 #include <nsIFileStreams.h>
37 #include <nsIIOService.h>
38 #include <nsNetUtil.h>
39 
40 #include "sbMetadataChannel.h"
41 
42 #include "prlog.h"
43 #ifdef PR_LOGGING
44 extern PRLogModuleInfo* gMetadataLog;
45 #endif
46 
47 // DEFINES ====================================================================
48 
49 // FUNCTIONS ==================================================================
50 
51 // CLASSES ====================================================================
52 NS_IMPL_THREADSAFE_ISUPPORTS3(sbMetadataChannel, sbIMetadataChannel, nsIStreamListener, nsIRequestObserver)
53 
54 //-----------------------------------------------------------------------------
56  m_Pos( 0 ),
57  m_Buf( 0 ),
58  m_BufDeadZoneStart( 0 ),
59  m_BufDeadZoneEnd( 0 ),
60  m_Blocks(),
61  m_Completed( PR_FALSE )
62 {
63 }
64 
65 //-----------------------------------------------------------------------------
66 sbMetadataChannel::~sbMetadataChannel()
67 {
68  Close();
69 }
70 
71 
72 /* void Open (in nsIChannel channel); */
73 NS_IMETHODIMP sbMetadataChannel::Open(nsIChannel *channel, sbIMetadataHandler *handler)
74 {
75  nsresult rv;
76  if ( ! channel || ! handler )
77  return NS_ERROR_NULL_POINTER;
78 
79  // Dump anything that might be there.
80  Close();
81 
82  // Open the channel with ourselves as the listener and the handler as the context.
83  m_Channel = channel;
84  m_Handler = handler;
85 
86  {
87  nsCOMPtr<nsIRequest> request = do_QueryInterface(m_Channel);
88  PRUint32 loadFlags = nsIRequest::INHIBIT_CACHING |
89  nsIRequest::INHIBIT_PERSISTENT_CACHING |
90  nsIRequest::LOAD_BYPASS_CACHE;
91  rv = request->SetLoadFlags(loadFlags);
92  NS_ASSERTION(NS_SUCCEEDED(rv), "Setting load flags failed :( Watch out, app will deadlock.");
93  if(NS_FAILED(rv))
94  return rv;
95  }
96 
97  nsCOMPtr<nsIInterfaceRequestor> ir = new sbMetadataChannelEventSink(this);
98  NS_ENSURE_TRUE(ir, NS_ERROR_OUT_OF_MEMORY);
99 
100  rv = m_Channel->SetNotificationCallbacks(ir);
101  NS_ENSURE_SUCCESS(rv, rv);
102 
103  return m_Channel->AsyncOpen( this, handler );
104 }
105 
106 /* void Close (); */
107 NS_IMETHODIMP sbMetadataChannel::Close()
108 {
109  if(m_Channel)
110  {
111  PRBool pendingRequest = PR_FALSE;
112  m_Channel->IsPending(&pendingRequest);
113  if(pendingRequest)
114  m_Channel->Cancel(NS_ERROR_ABORT);
115  }
116 
117  m_Pos = 0;
118  m_Buf = 0;
119  m_BufDeadZoneStart = 0;
120  m_BufDeadZoneEnd = 0;
121  m_Blocks.clear();
122  m_Channel = nsnull;
123  m_Handler = nsnull;
124  return NS_OK;
125 }
126 
127 /* void SetPos (in PRUint64 pos); */
128 NS_IMETHODIMP sbMetadataChannel::SetPos(PRUint64 pos)
129 {
130  // For now, disallow seeks past our buffering point.
131  if ( pos > m_Buf )
132  {
133  // NOTE: This block of code is neat and everything, but it doesn't work.
134  // When I attempt to do it, I just get an OnStopRequest without any data.
135 
136  // We should only ever see one overflowing seek.
137  if (m_BufDeadZoneStart)
138  return NS_ERROR_UNEXPECTED;
139 
140  nsresult rv;
141 
142  // See if this is a "resumable" channel.
143  nsCOMPtr<nsIResumableChannel> testing_to_see_if_this_exists_but_never_going_to_use( do_QueryInterface(m_Channel, &rv) );
144  if ( NS_FAILED( rv ) ) return NS_ERROR_INVALID_ARG; // this is totally okay to fail.
145 
146  // Remember what our target file is.
147  nsCOMPtr<nsIURI> pURI;
148  rv = m_Channel->GetURI( getter_AddRefs(pURI) );
149  NS_ENSURE_SUCCESS( rv, rv );
150 
151  // Shutdown to prepare for opening a new one.
152  // Read this: http://developer.mozilla.org/en/docs/Implementing_Download_Resuming
153  if(m_Channel)
154  m_Channel->Cancel(NS_ERROR_ABORT);
155  m_Channel = nsnull;
156  // Apparently, this interface isn't REALLY meant for "read from the end of a file online"
157 
158  // Get a new channel.
159  nsCOMPtr<nsIIOService> pIOService = do_GetIOService(&rv);
160  NS_ENSURE_SUCCESS( rv, rv );
161  rv = pIOService->NewChannelFromURI(pURI, getter_AddRefs(m_Channel));
162  NS_ENSURE_SUCCESS( rv, rv );
163 
164  // See if this is a "resumable" channel. Again.
165  nsCOMPtr<nsIResumableChannel> resume( do_QueryInterface(m_Channel, &rv) );
166  NS_ENSURE_SUCCESS( rv, rv );
167 
168  // Reopen everybody and rock out.
169  rv = resume->ResumeAt( pos, NS_LITERAL_CSTRING("") );
170  NS_ENSURE_SUCCESS( rv, rv );
171  rv = m_Channel->AsyncOpen( this, m_Handler );
172  NS_ENSURE_SUCCESS( rv, rv );
173 
174  // Now we start reading from pos and remember we have a hole in the buffer.
175  m_BufDeadZoneStart = m_Buf;
176  m_BufDeadZoneEnd = m_Buf = pos;
177 
178  // Tell the code to abort this time and start over when new data comes in.
180  // Hopefully none of those functions up there return error abort.
181  }
182 
183  // No, you can't seek into the deadzone.
184  if ( m_BufDeadZoneStart && pos >= m_BufDeadZoneStart && pos < m_BufDeadZoneEnd )
185  {
186  NS_WARNING("****** METADATACHANNEL ****** Can't seek into the deadzone");
187  return NS_ERROR_UNEXPECTED;
188  }
189 
190  m_Pos = pos;
191 
192  return NS_OK;
193 }
194 
195 /* PRUint64 GetPos (); */
196 NS_IMETHODIMP sbMetadataChannel::GetPos(PRUint64 *_retval)
197 {
198  if ( ! _retval )
199  return NS_ERROR_NULL_POINTER;
200 
201  *_retval = m_Pos;
202 
203  return NS_OK;
204 }
205 
206 /* PRUint64 GetBuf (); */
207 NS_IMETHODIMP sbMetadataChannel::GetBuf(PRUint64 *_retval)
208 {
209  if ( ! _retval )
210  return NS_ERROR_NULL_POINTER;
211 
212  *_retval = m_Buf;
213 
214  return NS_OK;
215 }
216 
217 /* PRUint64 GetSize (); */
218 NS_IMETHODIMP sbMetadataChannel::GetSize(PRUint64 *_retval)
219 {
220  if ( ! _retval )
221  return NS_ERROR_NULL_POINTER;
222 
223  PRInt32 ret = 0;
224 
225  NS_ASSERTION(m_Channel, "sbMetadataChannel::GetSize called without m_Channel set!");
226 
227  if(m_Channel)
228  m_Channel->GetContentLength( &ret );
229 
230  *_retval = (PRInt64)ret; // preserve sign.
231 
232  return NS_OK;
233 }
234 
235 /* void Skip (in PRUint64 skip); */
236 NS_IMETHODIMP sbMetadataChannel::Skip(PRUint64 skip)
237 {
238  return SetPos( m_Pos + skip );
239 }
240 
241 /* PRUint32 Read (in charPtr buf, in PRUint32 len); */
242 NS_IMETHODIMP sbMetadataChannel::Read(char * out_buf, PRUint32 len, PRUint32 *_retval)
243 {
244  // Sanity check
245  if ( ! out_buf )
246  return NS_ERROR_NULL_POINTER;
247  if ( m_Pos + len >= m_Buf )
248  return NS_ERROR_UNEXPECTED;
249 
250  *_retval = 0;
251  // Write <len> bytes of data to the output buffer, from possibly more than one block.
252  for ( PRUint32 remaining = len, count = (PRUint32)-1; remaining && count; remaining -= count, m_Pos += count, out_buf += count, *_retval += count )
253  {
254  // Either to the end of the incoming read or the end of the current block.
255  PRUint32 left = (PRUint32)( BLOCK_SIZE - POS(m_Pos) );
256  count = std::min( remaining, left );
257  char *buf = BUF(m_Pos); // Magic pointer-to-position method
258  memcpy( out_buf, buf, count );
259  }
260 
261  return NS_OK;
262 }
263 
264 NS_IMETHODIMP sbMetadataChannel::ReadChar(char *_retval)
265 {
266  // Sanity check
267  if ( m_Pos + 1 >= m_Buf )
268  return NS_ERROR_UNEXPECTED;
269 
270  PRUint32 count;
271  Read( _retval, 1, &count );
272 
273  return NS_OK;
274 }
275 
276 NS_IMETHODIMP sbMetadataChannel::ReadInt32(PRInt32 *_retval)
277 {
278  // Sanity check
279  if ( m_Pos + 4 >= m_Buf )
280  return NS_ERROR_UNEXPECTED;
281 
282  PRUint32 count;
283  Read( (char *)_retval, 4, &count );
284 
285 #ifdef IS_BIG_ENDIAN
286  // HMMMMM. Endianness?
287  *_retval = ( ( *_retval & 0xFF000000 ) >> 24 ) |
288  ( ( *_retval & 0x00FF0000 ) >> 8 ) |
289  ( ( *_retval & 0x0000FF00 ) << 8 ) |
290  ( ( *_retval & 0x000000FF ) << 24 );
291 #endif
292 
293  return NS_OK;
294 }
295 
296 NS_IMETHODIMP sbMetadataChannel::ReadInt64(PRInt64 *_retval)
297 {
298  // Sanity check
299  if ( m_Pos + 8 >= m_Buf )
300  return NS_ERROR_UNEXPECTED;
301 
302  PRUint32 count;
303  Read( (char *)_retval, 8, &count );
304 
305 #if 0
306 #ifdef IS_BIG_ENDIAN
307  // HMMMMM. Endianness?
308  *_retval = ( ( *_retval & (long long)0xFF00000000000000 ) >> 56 ) |
309  ( ( *_retval & (long long)0x00FF000000000000 ) >> 40 ) |
310  ( ( *_retval & (long long)0x0000FF0000000000 ) >> 24 ) |
311  ( ( *_retval & (long long)0x000000FF00000000 ) >> 8 ) |
312  ( ( *_retval & 0x00000000FF000000 ) << 8 ) |
313  ( ( *_retval & 0x0000000000FF0000 ) << 24 ) |
314  ( ( *_retval & 0x000000000000FF00 ) << 40 ) |
315  ( ( *_retval & 0x00000000000000FF ) << 56 );
316 #endif
317 #endif
318 
319  return NS_OK;
320 }
321 
322 /* PRBool GetSeekable (); */
323 NS_IMETHODIMP sbMetadataChannel::GetSeekable(PRBool *_retval)
324 {
325  if ( ! _retval )
326  return NS_ERROR_NULL_POINTER;
327 
328  *_retval = PR_FALSE;
329 
330  return NS_OK;
331 }
332 
333 /* PRBool GetSeekable (); */
334 NS_IMETHODIMP sbMetadataChannel::GetCompleted(PRBool *_retval)
335 {
336  if ( ! _retval )
337  return NS_ERROR_NULL_POINTER;
338 
339  *_retval = m_Completed;
340 
341  return NS_OK;
342 }
343 
344 NS_IMETHODIMP
345 sbMetadataChannel::OnDataAvailable(nsIRequest *aRequest,
346  nsISupports *ctxt,
347  nsIInputStream *inStr,
348  PRUint32 sourceOffset,
349  PRUint32 count)
350 {
351  // Sanity check
352  if ( ! aRequest || ! ctxt || ! inStr )
353  return NS_ERROR_NULL_POINTER;
354  if ( m_Buf != sourceOffset )
355  return NS_ERROR_UNEXPECTED;
356 
357  // Read <count> bytes of data from the input stream, into possibly more than one block.
358  for ( PRUint32 remaining = count, read = (PRUint32)-1; remaining && read; remaining -= read, m_Buf += read )
359  {
360  // Either to the end of the incoming read or the end of the current block.
361  PRUint32 left = (PRUint32)( BLOCK_SIZE - POS(m_Buf) );
362  PRUint32 len = std::min( remaining, left );
363  char *buf = BUF(m_Buf); // Magic pointer-to-position method
364  inStr->Read( buf, len, &read );
365  buf++;
366  }
367 
368  PRUint64 size;
369  GetSize( &size );
370  // Don't send until you get to the end or are over the block size or its broke or something
371  if ( m_Buf >= BLOCK_SIZE )
372  {
373  // Inform the handler that we read data.
374  nsCOMPtr<sbIMetadataHandler> handler( do_QueryInterface(ctxt) );
375  if ( handler.get() )
376  {
377  handler->OnChannelData( this );
378  PRBool complete = PR_FALSE;
379  nsresult rv = handler->GetCompleted( &complete );
380  if ( NS_FAILED( rv ) || complete )
381  {
382  Close();
383  }
384  }
385  }
386  return NS_OK;
387 }
388 
391 NS_IMETHODIMP
392 sbMetadataChannel::OnStartRequest(nsIRequest *aRequest,
393  nsISupports *ctxt)
394 {
395  nsresult result;
396  aRequest->GetStatus( &result );
397  return NS_OK;
398 }
399 
400 NS_IMETHODIMP
401 sbMetadataChannel::OnStopRequest(nsIRequest *aRequest,
402  nsISupports *ctxt,
403  nsresult status)
404 {
405  // Ignore the stop something we're aborting
406  nsresult rv, request_status;
407  rv = aRequest->GetStatus( &request_status );
408  NS_ENSURE_SUCCESS(rv, rv);
409  if ( request_status == NS_ERROR_ABORT )
410  return NS_OK;
411 
412  // Okay, we're done.
413  m_Completed = PR_TRUE;
414  // Inform the handler that we read data.
415  nsCOMPtr<sbIMetadataHandler> handler( do_QueryInterface(ctxt, &rv) );
416  NS_ENSURE_SUCCESS(rv, rv);
417  if ( handler.get() )
418  {
419  handler->OnChannelData( this );
420  }
421  return NS_OK;
422 }
423 
424 NS_IMETHODIMP
425 sbMetadataChannel::SetRedirectedChannel(nsIChannel* aChannel)
426 {
427  m_Channel = aChannel;
428 
429  return NS_OK;
430 }
431 
433  nsIChannelEventSink,
435 
437 {
438  mMetadataChannel = aMetadataChannel;
439 }
440 
441 sbMetadataChannelEventSink::~sbMetadataChannelEventSink()
442 {
443 }
444 
445 NS_IMETHODIMP
446 sbMetadataChannelEventSink::OnChannelRedirect(nsIChannel* oldChannel,
447  nsIChannel* newChannel,
448  PRUint32 flags)
449 {
450  nsresult rv;
451 
452  rv = mMetadataChannel->SetRedirectedChannel(newChannel);
453  NS_ENSURE_SUCCESS(rv, rv);
454 
455  return NS_OK;
456 }
457 
458 NS_IMETHODIMP
459 sbMetadataChannelEventSink::GetInterface(const nsIID& uuid, void** aResult)
460 {
461  return QueryInterface(uuid, aResult);
462 }
463 
NS_IMPL_THREADSAFE_ISUPPORTS2(sbMetadataChannelEventSink, nsIChannelEventSink, nsIInterfaceRequestor) sbMetadataChannelEventSink
return NS_OK
_dialogDatepicker pos
sbDeviceFirmwareAutoCheckForUpdate prototype flags
A wrapper for an nsIChannel that buffers the incoming data.
const NS_ERROR_ABORT
attribute PRUInt64 pos
The current read position (for the read methods)
readonly attribute PRUInt64 size
The size of the file targeted by the nsIChannel.
sbOSDControlService prototype QueryInterface
PRUint32 read(in charPtr aBuffer, in PRUint32 aSize)
Read from the buffer.
var uuid
function skip(aMessage)
readonly attribute PRUInt64 buf
The current internal buffer position (everything the channel has sent)
var count
Definition: test_bug7406.js:32
const nsIChannel
An object capable of manipulating the metadata tags for a media file.
return ret
#define NS_ERROR_SONGBIRD_METADATA_CHANNEL_RESTART
#define min(a, b)
const nsIInterfaceRequestor
NS_IMPL_THREADSAFE_ISUPPORTS3(sbAlbumArtFetcherSet, sbIAlbumArtFetcherSet, sbIAlbumArtFetcher, sbIAlbumArtListener) NS_IMETHODIMP sbAlbumArtFetcherSet
If true, only attempt to fetch album art from local sources.
GstMessage gpointer data sbGStreamerMessageHandler * handler