Index: src/pipeline.cpp =================================================================== --- src/pipeline.cpp (revision 98038) +++ src/pipeline.cpp (working copy) @@ -2780,6 +2780,352 @@ /* + * LiveSource + */ + +#define LIVE_SOURCE_BUFFER_SIZE 1000000 +#define LIVE_SOURCE_PEEK_BUFFER_SIZE 64000 + + + +LiveSource::LiveSource (Media *media) : IMediaSource (media) +{ + write_pos = 0; + read_pos = 0; + peek_buffer_size = 0; + requested_pos = -1; + size = -1; + + pthread_mutex_init (&write_mutex, NULL); + pthread_cond_init (&write_cond, NULL); + cancel_wait = false; + wait_count = 0; + wait_size = 0; + +} + +LiveSource::~LiveSource () +{ + delete buffer; + g_free (peek_buffer); + + pthread_mutex_destroy (&write_mutex); + pthread_cond_destroy (&write_cond); +} + +MediaResult +LiveSource::Initialize () +{ + + buffer = new RingBuffer (LIVE_SOURCE_BUFFER_SIZE); + peek_buffer = (char*) g_malloc (LIVE_SOURCE_PEEK_BUFFER_SIZE); + return MEDIA_SUCCESS; +} + + +bool +LiveSource::IsWaiting () +{ + return g_atomic_int_get (&wait_count) != 0; +} + +bool +LiveSource::WakeUp () +{ + return WakeUp (true); +} + +bool +LiveSource::WakeUp (bool lock) +{ + if (lock) + Lock (); + pthread_cond_signal (&write_cond); + if (lock) + Unlock (); + return true; +} + +void +LiveSource::Lock () +{ + pthread_mutex_lock (&write_mutex); +} + +void +LiveSource::Unlock () +{ + pthread_mutex_unlock (&write_mutex); +} + +bool +LiveSource::WaitForData (int size) +{ + bool result = false; + + g_atomic_int_inc (&wait_count); + + Lock (); + + if (size > wait_size) + wait_size = size; + + while (true) { + if (cancel_wait) { + goto cleanup; + } + + if (buffer->DataAvailable() > size) { + result = true; + goto cleanup; + } + + pthread_cond_wait (&write_cond, &write_mutex); + } + +cleanup: + if (cancel_wait) { + cancel_wait = false; + // Wake up CancelWait (), which is waiting for us to finish. + WakeUp (false); + } + wait_size = 0; + Unlock (); + + g_atomic_int_dec_and_test (&wait_count); + + return result; +} + +void +LiveSource::CancelWait () +{ + //printf ("%p ProgressiveSource::CancelWait () wait_pos: %lld.\n", this, wait_pos); + Lock (); + if (IsWaiting ()) { + cancel_wait = true; + WakeUp (false); + while (cancel_wait) + pthread_cond_wait (&write_cond, &write_mutex); + } + Unlock (); +} + + + +void +LiveSource::Write (void *buf, int64_t offset, int32_t n) +{ + int nwritten; + + if (n == 0) { + return; + } + + Lock (); + + + nwritten = buffer->Write ((char*)buf, n); + write_pos += nwritten; + if (nwritten < n) { + /* re-try once */ + int nwritten2 = buffer->Write ((char*)buf + nwritten, n - nwritten); + write_pos += nwritten2; + if (nwritten2 < n - nwritten) { + g_print ("LiveSource::Write: RingBuffer full write does not fit, should wait\n"); + /* Pause the downloader */ + requested_pos = -2; + Unlock (); + return; + } + } + + if (IsWaiting ()) + WakeUp (false); + + /* Keep a copy for future Peeks */ + if (peek_buffer_size < LIVE_SOURCE_PEEK_BUFFER_SIZE && + LIVE_SOURCE_PEEK_BUFFER_SIZE - peek_buffer_size > n) { + memcpy (peek_buffer + peek_buffer_size, buf, n); + peek_buffer_size += n; + } + + Unlock (); + +} + +void +LiveSource::NotifySize (int64_t size) +{ + this->size = size; +} + + +bool +LiveSource::Seek (int64_t offset, int mode) +{ + g_print ("Ask to seek to %lld and readpos is %lld (writepos:%lld)\n", offset, read_pos, write_pos); + + switch (mode) { + case SEEK_SET: + // no-op, offset is already relative to BOF + break; + case SEEK_CUR: + offset += read_pos; + break; + case SEEK_END: + if (size == -1) { + // we don't know the stream size, so cannot perform this seek + return false; + } + + if (offset > 0) { + // attempting to seek beyond eof + return false; + } + + offset += size; + break; + default: + // invalid 'whence' argument + return false; + } + + + if (offset < 0 || (size != -1 && offset > size)) { + // attempting to seek to an invalid position + return false; + } + if (offset == read_pos) + return true; + + if (offset > read_pos) { + /* FIXME: Implement skip on RingBuffer */ + if (offset < write_pos) { + int nread; + char stupid_buffer[offset - read_pos]; + nread = Read(stupid_buffer, offset - read_pos); + return true; + } else if (offset - write_pos < 10000) { + if (!(WaitForData (10000))) + return false; + int nread; + char stupid_buffer[offset - read_pos]; + nread = Read(stupid_buffer, offset - read_pos); + return true; + } + + } + + if (offset < read_pos) { + int nrewind; + nrewind = buffer->Rewind (read_pos - offset); + if (nrewind == read_pos - offset) { + read_pos = offset; + return true; + } + + } + + g_print ("Requesting remote SEEK\n"); + + /* Need to get the stream in the new position */ + requested_pos = offset; + /* Clear the buffer */ + Lock (); + buffer->Clean(); + /* Set it here and wait for data */ + read_pos = offset; + write_pos = offset; + Unlock (); + + /* Fill the buffer a little bit before continue (avoid extra seeks) */ + + if (!(WaitForData (10000))) + return false; + + return true; + +} + + +bool +LiveSource::Seek (int64_t offset) +{ + return Seek (offset, SEEK_SET); +} + +int64_t +LiveSource::GetPosition () +{ + return read_pos; +} + +int64_t +LiveSource::GetWritePosition () +{ + return write_pos; +} + + + + +int32_t +LiveSource::Read (void *buf, uint32_t n) +{ + int nread; + int available; + + Lock (); + available = buffer->DataAvailable(); + Unlock (); + + if (n > (uint32_t) available ) + if (!(WaitForData (n))) + return 0; + + nread = buffer->Read ((char*)buf, n); + if ((uint32_t)nread < n) { + g_print ("LiveSource::Read: Not enough data in the RingBuffer\n"); + } + + read_pos += nread; + + return nread; +} + +bool +LiveSource::ReadAll (void *buf, uint32_t n) +{ + /* Should be without the while in the read */ + int nread = Read (buf, n); + return (uint32_t) nread == n; +} + +bool +LiveSource::Peek (void *buf, uint32_t n) +{ + if (n > (uint32_t) peek_buffer_size) { + /* we can't handle a peek of this size */ + return false; + } + + memcpy (buf, peek_buffer, n); + return true; +} + +void +LiveSource::RequestPosition (int64_t *pos) +{ + if (requested_pos != -1) { + *pos = requested_pos; + requested_pos = -1; + } +} + + + +/* * MediaClosure */ Index: src/downloader.cpp =================================================================== --- src/downloader.cpp (revision 98038) +++ src/downloader.cpp (working copy) @@ -293,6 +293,13 @@ } void +Downloader::RequestPosition (int64_t *pos) +{ + if (request_position) + request_position (pos, consumer_closure); +} + +void Downloader::NotifyFinished (const char *fname) { if (aborted) @@ -363,7 +370,14 @@ this->consumer_closure = data; } + void +Downloader::SetRequestPositionFunc (downloader_request_position_func request_position) +{ + this->request_position = request_position; +} + +void Downloader::SetFunctions (downloader_create_state_func create_state, downloader_destroy_state_func destroy_state, downloader_open_func open, @@ -468,6 +482,12 @@ dl->NotifySize (size); } +void +downloader_request_position (Downloader *dl, int64_t *pos) +{ + dl->RequestPosition (pos); +} + void dummy_downloader_write (void *buf, int32_t offset, int32_t n, gpointer cb_data) { Index: src/media.h =================================================================== --- src/media.h (revision 98038) +++ src/media.h (working copy) @@ -186,6 +186,7 @@ // downloader methods/data ProgressiveSource *downloaded_file; + LiveSource *live_stream; Downloader *downloader; char *part_name; uint64_t buffering_start; // The last write position in the file when we started buffering @@ -195,6 +196,7 @@ int advance_frame_timeout_id; void DataWrite (void *data, int32_t offset, int32_t n); + void DataRequestPosition (int64_t *pos); void DownloaderAbort (); void DownloaderComplete (); void BufferingComplete (); @@ -211,6 +213,7 @@ bool MediaOpened (Media *media); static void data_write (void *data, int32_t offset, int32_t n, void *closure); + static void data_request_position (int64_t *pos, void *closure); static void downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure); static void size_notify (int64_t size, gpointer data); Index: src/downloader.h =================================================================== --- src/downloader.h (revision 98038) +++ src/downloader.h (working copy) @@ -25,6 +25,7 @@ typedef void (*downloader_write_func) (void *buf, int32_t offset, int32_t n, gpointer cb_data); typedef void (*downloader_notify_size_func) (int64_t size, gpointer cb_data); +typedef void (*downloader_request_position_func) (int64_t *pos, gpointer cb_data); typedef gpointer (*downloader_create_state_func) (Downloader *dl); typedef void (*downloader_destroy_state_func) (gpointer state); @@ -68,11 +69,14 @@ void NotifyFailed (const char *msg); void NotifySize (int64_t size); + void RequestPosition (int64_t *pos); + // This is called by the consumer of the downloaded data (the // Image class for instance) void SetWriteFunc (downloader_write_func write, downloader_notify_size_func notify_size, gpointer closure); + void SetRequestPositionFunc (downloader_request_position_func request_position); // This is called by the supplier of the downloaded data (the // managed framework, the browser plugin, the demo test) @@ -102,9 +106,10 @@ bool aborted; // Set by the consumer - downloader_write_func write; - downloader_notify_size_func notify_size; + downloader_write_func write; + downloader_notify_size_func notify_size; gpointer consumer_closure; + downloader_request_position_func request_position; // Set by the supplier. gpointer downloader_state; @@ -146,6 +151,8 @@ void downloader_notify_finished (Downloader *dl, const char *filename); void downloader_notify_error (Downloader *dl, const char *msg); +void downloader_request_position (Downloader *dl, int64_t *pos); + void downloader_init (void); G_END_DECLS Index: src/pipeline.h =================================================================== --- src/pipeline.h (revision 98038) +++ src/pipeline.h (working copy) @@ -650,25 +650,152 @@ void NotifySize (int64_t size); }; +class RingBuffer { +private: + int size; + char *data; + char *end; + char *load, *consume; + int max_load, max_consume, data_in_buffer; + void Update () { + if (load == end) + load = data; + if (consume == end) + consume = data; + + if (load == consume) { + if (data_in_buffer > 0) { + max_consume = end - consume; + max_load = 0; + } else { + max_load = end - load; + max_consume = 0; + } + } else if (load > consume) { + max_load = end - load; + max_consume = load - consume; + } else { + max_load = consume - load; + max_consume = end - consume; + } + } + +public: + RingBuffer (int size) { + data = (char*) g_malloc (size); + this->size = size; + end = data + size; + load = data; + consume = data; + max_load = size; + max_consume = 0; + data_in_buffer = 0; + + } + ~RingBuffer () { g_free (data); } + + void Clean () { + end = data + size; + load = data; + consume = data; + max_load = size; + max_consume = 0; + data_in_buffer = 0; + } + + int GetSize () { return size; } + int Write (const char *data, int size) { + if (size > max_load) + size = max_load; + memcpy (load, data, size); + data_in_buffer += size; + load += size; + Update(); + return size; + } + + int Read (char *data, int size) { + if (size > max_consume) + size = max_consume; + memcpy (data, consume, size); + consume += size; + data_in_buffer -= size; + Update(); + return size; + } + + int Rewind (int size) { + /* FIXME do the real checks */ + if (size > max_load) + size = max_load; + consume -= size; + data_in_buffer += size; + Update(); + return size; + } + + int DataAvailable () { + return max_consume; + } +}; + class LiveSource : public IMediaSource { +private: + int64_t write_pos; + int64_t read_pos; + int64_t requested_pos; + int64_t size; + bool can_seek; + RingBuffer *buffer; + char *peek_buffer; + int peek_buffer_size; + + pthread_mutex_t write_mutex; + pthread_cond_t write_cond; + bool cancel_wait; + int wait_count; // Counter of how many threads are waiting in WaitForPosition + int wait_size; + + + static void write (void *buf, int32_t offset, int32_t n, gpointer cb_data); + static void position (int64_t *pos, gpointer cb_data); + protected: - virtual ~LiveSource () {} + virtual ~LiveSource (); public: - LiveSource (Media *media) : IMediaSource (media) {} + LiveSource (Media *media); - virtual MediaResult Initialize () { return MEDIA_FAIL; } + virtual MediaResult Initialize (); virtual MediaSourceType GetType () { return MediaSourceTypeLive; } + + // Blocks until the position have data + // Returns false if failure (one possibility being that the requested position is beyond the end of the file) + bool WaitForData (int size); + // Wakes up WaitForPosition to check if the position has been reached, or if the wait should be cancelled + bool WakeUp (); + bool WakeUp (bool lock); // lock: set to false if the write_lock is already acquired by the caller + bool IsWaiting (); + // Cancels any pending waits + void CancelWait (); + void Lock (); + void Unlock (); - virtual bool IsSeekable () { return false; } - virtual int64_t GetPosition () { return 0; } - virtual bool Seek (int64_t position) { return false; } - virtual bool Seek (int64_t position, int mode) { return false; } - virtual int32_t Read (void *buffer, uint32_t n) { return -1; } - virtual bool ReadAll (void *buffer, uint32_t n) { return false; } - virtual bool Peek (void *buffer, uint32_t n) { return false; } - virtual int64_t GetSize () { return -1; } - virtual bool Eof () { return false; } + + virtual bool IsSeekable () { return true; } // TEMPORAL + virtual int64_t GetPosition (); + virtual bool Seek (int64_t position); + virtual bool Seek (int64_t position, int mode); + virtual int32_t Read (void *buffer, uint32_t n); + virtual bool ReadAll (void *buffer, uint32_t n); + virtual bool Peek (void *buffer, uint32_t n); + virtual int64_t GetSize () { return size; } + virtual bool Eof () { return write_pos == size; } + + int64_t GetWritePosition (); + void Write (void *buf, int64_t offset, int32_t n); + void NotifySize (int64_t size); + void RequestPosition (int64_t *pos); }; class VideoStream : public IMediaStream { Index: src/media.cpp =================================================================== --- src/media.cpp (revision 98038) +++ src/media.cpp (working copy) @@ -335,6 +335,7 @@ streamed_markers = NULL; downloaded_file = NULL; + live_stream = NULL; downloader = NULL; part_name = NULL; mplayer = NULL; @@ -413,6 +414,10 @@ downloaded_file->unref (); downloaded_file = NULL; } + if (live_stream) { + live_stream->unref (); + live_stream = NULL; + } flags |= UpdatingPosition; SetValue (MediaElement::PositionProperty, Value (0, Type::TIMESPAN)); @@ -668,9 +673,17 @@ // CHECK: if buffering, will DownloadCompletedEvent be emitted? if (IsBuffering ()) { - int64_t size = downloaded_file->GetSize (); + int64_t size; + if (downloaded_file != NULL) + size = downloaded_file->GetSize (); + if (live_stream != NULL); + size = live_stream->GetSize (); int64_t buffering_time = TimeSpan_ToPts (GetValue (MediaElement::BufferingTimeProperty)->AsTimeSpan ()); - int64_t write_pos = downloaded_file->GetWritePosition (); + int64_t write_pos; + if (downloaded_file != NULL) + write_pos = downloaded_file->GetWritePosition (); + if (live_stream != NULL) + write_pos = live_stream->GetWritePosition (); int64_t wait_pos = media == NULL ? 0 : media->GetDemuxer ()->EstimatePtsPosition (buffering_pts + buffering_time); int64_t buffer_size = wait_pos - buffering_start; int64_t buffered_size = write_pos - buffering_start; @@ -755,6 +768,12 @@ if (!(flags & BufferingFailed) && IsOpening () && offset > 1024 && (part_name == NULL || part_name[0] == 0)) TryOpen (); } + + if (live_stream != NULL) { + live_stream->Write (buf, (int64_t) offset, n); + if (!(flags & BufferingFailed) && IsOpening () && offset > 1024) + TryOpen (); + } // Delay the propogating progress 1.0 until // the downloader has notified us it is done. @@ -764,6 +783,13 @@ UpdateProgress (); } +void +MediaElement::DataRequestPosition (int64_t *position) +{ + if (live_stream != NULL) + live_stream->RequestPosition (position); +} + void MediaElement::data_write (void *buf, int32_t offset, int32_t n, gpointer data) { @@ -777,6 +803,8 @@ if (element->downloaded_file != NULL) element->downloaded_file->NotifySize (size); + if (element->live_stream != NULL) + element->live_stream->NotifySize (size); } void @@ -785,6 +813,14 @@ ((MediaElement *) closure)->DownloaderComplete (); } +void +MediaElement::data_request_position (int64_t *position, gpointer data) +{ + ((MediaElement *) data)->DataRequestPosition (position); +} + + + void MediaElement::BufferingComplete () { @@ -853,7 +889,8 @@ } else { element->flags |= BufferingFailed; // Seek back to the beginning of the file - element->downloaded_file->Seek (0, SEEK_SET); + if (element->downloaded_file != NULL) + element->downloaded_file->Seek (0, SEEK_SET); } delete closure; return false; @@ -898,16 +935,23 @@ LOG_MEDIAELEMENT ("MediaElement::TryOpen ()\n"); if (flags & DownloadComplete) { - IMediaSource *current_downloaded_file = downloaded_file; - char *filename = downloader_get_response_file (downloader, part_name); Media *media = new Media (this); IMediaSource *source; + IMediaSource *current_downloaded_file = NULL; + + if (downloaded_file != NULL) { + current_downloaded_file = downloaded_file; + char *filename = downloader_get_response_file (downloader, part_name); - if (current_downloaded_file) - current_downloaded_file->ref (); + if (current_downloaded_file) + current_downloaded_file->ref (); - source = new FileSource (media, filename); - g_free (filename); + source = new FileSource (media, filename); + g_free (filename); + } + if (live_stream != NULL) { + source = live_stream; + } if (MEDIA_SUCCEEDED (source->Initialize ()) && MEDIA_SUCCEEDED (media->Open (source))) { if (MediaOpened (media)) { @@ -942,7 +986,11 @@ MediaClosure *closure = new MediaClosure (media_element_open_callback); closure->SetContext (this); - media->OpenAsync (downloaded_file, closure); + if (downloaded_file != NULL) { + media->OpenAsync (downloaded_file, closure); + } else if (live_stream != NULL) { + media->OpenAsync (live_stream, closure); + } } // FIXME: specify which audio stream index the player should use @@ -990,10 +1038,13 @@ void MediaElement::SetSourceInternal (Downloader *dl, const char *PartName) { + bool is_live = false; + if (g_str_has_prefix (dl->GetValue (Downloader::UriProperty)->AsString (), "mms")) { + is_live = true; #if DEBUG - if (g_str_has_prefix (dl->GetValue (Downloader::UriProperty)->AsString (), "mms")) printf ("MediaElement is trying to load an mms stream, which isn't supported yet (%s)\n", dl->GetValue (Downloader::UriProperty)->AsString ()); #endif + } LOG_MEDIAELEMENT ("MediaElement::SetSourceInternal (%p, '%s'), uri: %s\n", dl, PartName, dl->GetValue (Downloader::UriProperty)->AsString ()); @@ -1013,19 +1064,25 @@ TryOpen (); } else { - downloaded_file = new ProgressiveSource (mplayer->media); + if (is_live) { + live_stream = new LiveSource (mplayer->media); + live_stream->Initialize (); + downloader->SetWriteFunc (data_write, size_notify, this); + downloader->SetRequestPositionFunc (data_request_position); + } else { + downloaded_file = new ProgressiveSource (mplayer->media); + // FIXME: error check Initialize() + downloaded_file->Initialize (); + downloader->SetWriteFunc (data_write, size_notify, this); + } - // FIXME: error check Initialize() - downloaded_file->Initialize (); - - downloader->SetWriteFunc (data_write, size_notify, this); } if (!(flags & DownloadComplete)) downloader->AddHandler (downloader->CompletedEvent, downloader_complete, this); // This is what actually triggers the download - if (downloaded_file != NULL) + if (downloaded_file != NULL || live_stream != NULL) downloader->Send (); Invalidate (); Index: plugin/plugin-downloader.h =================================================================== --- plugin/plugin-downloader.h (revision 98038) +++ plugin/plugin-downloader.h (working copy) @@ -33,6 +33,8 @@ this->npp = NULL; this->stream = NULL; + ignore_non_data = false; + // The Downloader will call destroy_state from it's destructor, // but if we ref the Downloader, its destructor will never get called. // No need to keep a ref, since this instance will never live longer @@ -60,6 +62,7 @@ bool mmsh; NPStream *stream; NPP npp; + bool ignore_non_data; }; void downloader_set_stream_data (Downloader *downloader, NPP npp, NPStream *stream); Index: plugin/browser-mmsh.cpp =================================================================== --- plugin/browser-mmsh.cpp (revision 98038) +++ plugin/browser-mmsh.cpp (working copy) @@ -11,6 +11,7 @@ */ #include "browser-mmsh.h" +#include "plugin-downloader.h" #include #define LE_16(val) (GINT16_FROM_LE (*((u_int16_t*)(val)))) @@ -139,6 +140,7 @@ BrowserMmshResponse::Abort () { this->channel->Cancel (NS_BINDING_ABORTED); + this->aborted = true; } typedef struct { @@ -347,6 +349,7 @@ { PRUint32 length; char *read_buffer; + PluginDownloader *pd = (PluginDownloader*) this->context; if (tmp_buffer) { read_buffer = (char *) NS_Realloc (tmp_buffer, tmp_size + count); @@ -392,13 +395,16 @@ asf_header_parse (mms_packet+8, packet_size - 8, &file_size, &asf_packet_size); if (file_size && notifier) notifier (this, this->context, NULL, file_size); - reader (this, this->context, mms_packet+8, this->size, packet_size - 8); - this->size += packet_size - 8; + if (!pd->ignore_non_data) { + reader (this, this->context, mms_packet+8, this->size, packet_size - 8); + this->size += packet_size - 8; + } } else if (type == MMS_DATA) { char *new_data = (char*)g_malloc0 (asf_packet_size); memcpy (new_data, mms_packet+8, packet_size - 8); reader (this, this->context, new_data, this->size, asf_packet_size); this->size += asf_packet_size; + g_free (new_data); } length -= size + 4; @@ -460,7 +466,7 @@ nsresult rs = NS_OK; AsyncBrowserMmshResponse *response; - response = new AsyncBrowserMmshResponse (channel, reader, notifier, finisher, context); + response = new AsyncBrowserMmshResponse (channel, this->uri, reader, notifier, finisher, context); rs = channel->AsyncOpen (response, (BrowserMmshResponse *) response); return !NS_FAILED (rs); } Index: plugin/plugin-downloader.cpp =================================================================== --- plugin/plugin-downloader.cpp (revision 98038) +++ plugin/plugin-downloader.cpp (working copy) @@ -15,6 +15,8 @@ bool downloader_shutdown = false; +static NPError p_downloader_mmsh_send (PluginDownloader *pd, int64_t offset); + static gpointer p_downloader_create_state (Downloader *dl) { @@ -48,26 +50,38 @@ static void p_downloader_mmsh_reader (BrowserMmshResponse *response, gpointer context, char *buffer, int offset, PRUint32 length) { - StreamNotify *notify = (StreamNotify*) context; - Downloader *dl = (Downloader *) notify->pdata; + int64_t requested_pos = -1; + PluginDownloader *pd = (PluginDownloader*) context; if (downloader_shutdown) { response->Abort (); return; } - dl->Write (buffer, offset, length); + if (response->aborted) { + g_print ("Dude! we had aborted!\n"); + return; + } + pd->dl->RequestPosition (&requested_pos); + if (requested_pos != -1 ) { + + response->Abort (); + p_downloader_mmsh_send (pd, requested_pos); + return; + } + + pd->dl->Write (buffer, offset, length); + } static void p_downloader_mmsh_notifier (BrowserMmshResponse *response, gpointer context, char *name, int64_t size) { - StreamNotify *notify = (StreamNotify*) context; - Downloader *dl = (Downloader *) notify->pdata; + PluginDownloader *pd = (PluginDownloader*) context; g_print ("Notifing a size of %d\n", size); - dl->NotifySize (size); + pd->dl->NotifySize (size); } @@ -75,30 +89,34 @@ p_downloader_mmsh_finished (BrowserMmshResponse *response, gpointer context) { char *fname; - StreamNotify *notify = (StreamNotify*) context; - Downloader *dl = (Downloader *) notify->pdata; - + PluginDownloader *pd = (PluginDownloader*) context; if (downloader_shutdown) { return; } - fname = dl->GetResponseFile (NULL); - dl->NotifyFinished (fname); + fname = pd->dl->GetResponseFile (NULL); + pd->dl->NotifyFinished (fname); } static NPError -p_downloader_mmsh_send (NPP_t *plugin, const char *uri, StreamNotify *notify) +p_downloader_mmsh_send (PluginDownloader *pd, int64_t offset) { - BrowserMmshRequest *mmsh_request = new BrowserMmshRequest ("GET", uri); + BrowserMmshRequest *mmsh_request = new BrowserMmshRequest ("GET", pd->uri); mmsh_request->SetHttpHeader ("User-Agent", "NSPlayer/11.1.0.3856"); - mmsh_request->SetHttpHeader ("Pragma", "no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,max-duration=0"); + mmsh_request->SetHttpHeader ("Pragma", "no-cache,rate=1.000000,stream-time=0,max-duration=0"); mmsh_request->SetHttpHeader ("Pragma", "xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}"); mmsh_request->SetHttpHeader ("Pragma", "xPlayStrm=1"); mmsh_request->SetHttpHeader ("Pragma", "stream-switch-count=0"); mmsh_request->SetHttpHeader ("Pragma", "stream-switch-entry=ffff:1:0 ffff:4:0"); - return mmsh_request->GetAsyncResponse (p_downloader_mmsh_reader, p_downloader_mmsh_notifier, p_downloader_mmsh_finished, notify); + if (offset != -1) { + char *header = g_strdup_printf ("stream-offset=%d:%d", (unsigned int)(offset >> 32), (unsigned int)offset); + mmsh_request->SetHttpHeader ("Pragma", header); + g_free (header); + pd->ignore_non_data = true; + } + return mmsh_request->GetAsyncResponse (p_downloader_mmsh_reader, p_downloader_mmsh_notifier, p_downloader_mmsh_finished, pd); } @@ -126,7 +144,7 @@ NPError err; if (pd->mmsh) { - err = p_downloader_mmsh_send (plugin, pd->uri, notify); + err = p_downloader_mmsh_send (pd, -1); } else { err = NPN_GetURLNotify (plugin, pd->uri, NULL, notify); } Index: plugin/browser-mmsh.h =================================================================== --- plugin/browser-mmsh.h (revision 98038) +++ plugin/browser-mmsh.h (working copy) @@ -41,25 +41,32 @@ class BrowserMmshResponse : public nsIHttpHeaderVisitor { nsCOMPtr channel; HttpHeaderHandler handler; + char *uri; protected: NS_DECL_NSIHTTPHEADERVISITOR public: NS_DECL_ISUPPORTS + bool aborted; BrowserMmshResponse (nsCOMPtr channel) : handler (NULL) { + aborted = false; + this->uri = NULL; this->channel = channel; } virtual ~BrowserMmshResponse () { + g_free (uri); } void Abort (); void VisitHeaders (HttpHeaderHandler handler); char *GetStatus (int *code); + void SetUri (const char *uri) { this->uri = g_strdup (uri); } + const char *GetUri () { return this->uri; } //virtual void *Read (int *length) = 0; }; @@ -90,6 +97,7 @@ NS_DECL_ISUPPORTS AsyncBrowserMmshResponse (nsCOMPtr channel, + const char *uri, AsyncMmshResponseDataAvailableHandler reader, AsyncMmshResponseNotifierHandler notifier, AsyncMmshResponseFinishedHandler finisher, @@ -107,11 +115,14 @@ this->reader = reader; this->finisher = finisher; this->context = context; + SetUri (uri); + } void MmsMetadataParse (int, const char*); virtual ~AsyncBrowserMmshResponse () { + g_free (tmp_buffer); } };