2009-10-20 Chris Toshok <toshok@ximian.com>
[moon.git] / src / mms-downloader.cpp
blob252b10b216fa3ba3e73e1c402d98d852d001cbb1
1 /*
2 * mms-downloader.cpp: MMS Downloader class.
4 * Contact:
5 * Moonlight List (moonlight-list@lists.ximian.com)
7 * Copyright 2008 Novell, Inc. (http://www.novell.com)
9 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include "mms-downloader.h"
16 #include "debug.h"
17 #include "timemanager.h"
18 #include "utils.h"
20 #define CLIENT_GUID "{c77e7400-738a-11d2-9add-0020af0a3278}"
21 #define CLIENT_SUPPORTED "com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.startupprofile, com.microsoft.wm.predstrm"
22 #define CLIENT_USER_AGENT "NSPlayer/11.08.0005.0000"
24 static inline bool
25 is_valid_mms_header (MmsHeader *header)
27 if (header->id != MMS_DATA && header->id != MMS_HEADER && header->id != MMS_METADATA && header->id != MMS_STREAM_C && header->id != MMS_END && header->id != MMS_PAIR_P)
28 return false;
30 return true;
34 * MmsDownloader
36 MmsDownloader::MmsDownloader (Downloader *dl) : InternalDownloader (dl, Type::MMSDOWNLOADER)
38 LOG_MMS ("MmsDownloader::MmsDownloader ()\n");
40 uri = NULL;
41 buffer = NULL;
42 client_id = NULL;
43 playlist_gen_id = NULL;
45 size = 0;
47 p_packet_count = 0;
49 p_packet_times [0] = 0;
50 p_packet_times [1] = 0;
51 p_packet_times [2] = 0;
53 max_bitrate = 0;
54 is_playing = false;
55 stream_switched = false;
57 source = NULL;
58 content_descriptions = NULL;
60 requested_pts = 0;
61 failure_reported = false;
63 dl->SetRequireCustomHeaderSupport (true);
64 dl->SetDisableCache (true);
67 MmsDownloader::~MmsDownloader ()
69 LOG_MMS ("MmsDownloader::~MmsDownloader ()\n");
71 g_free (uri);
72 g_free (buffer);
73 g_free (client_id);
74 g_free (playlist_gen_id);
75 delete content_descriptions;
77 if (source)
78 source->unref ();
81 void
82 MmsDownloader::SetSource (MmsSource *source)
84 VERIFY_MAIN_THREAD;
86 if (this->source)
87 this->source->unref ();
88 this->source = source;
89 if (this->source)
90 this->source->ref ();
93 void
94 MmsDownloader::SetRequestedPts (guint64 value)
96 // thread safe
97 LOG_MMS ("MmsDownloader::SetRequestedPts (%" G_GUINT64_FORMAT ")\n", value);
99 request_mutex.Lock ();
100 requested_pts = value;
101 request_mutex.Unlock ();
103 AddTickCallSafe (PlayCallback);
106 guint64
107 MmsDownloader::GetRequestedPts ()
109 // thread safe
110 guint64 result;
112 request_mutex.Lock ();
113 result = requested_pts;
114 request_mutex.Unlock ();
116 LOG_MMS ("MmsDownloader::GetRequestedPts (): %" G_GUINT64_FORMAT "\n", result);
118 return result;
121 static void
122 set_common_dl_headers (Downloader *dl, MmsDownloader *mms, GString *pragma)
124 dl->InternalSetHeader ("User-Agent", CLIENT_USER_AGENT);
125 dl->InternalSetHeader ("Pragma", "no-cache");
126 dl->InternalSetHeader ("Pragma", "xClientGUID=" CLIENT_GUID);
127 dl->InternalSetHeader ("Supported", CLIENT_SUPPORTED);
129 if (pragma != NULL && mms != NULL) {
130 const char *playlist_gen_id = mms->GetPlaylistGenId ();
131 const char *client_id = mms->GetClientId ();
133 if (playlist_gen_id != NULL)
134 g_string_append_printf (pragma, "Pragma: playlist-gen-id=%s\r\n", playlist_gen_id);
135 if (client_id != NULL)
136 g_string_append_printf (pragma, "Pragma: client-id=%s\r\n", client_id);
140 static void
141 set_stream_selection_headers (MmsDownloader *mms, GString *pragma, MmsPlaylistEntry *entry)
143 gint8 streams [128];
144 int count = 0;
147 * stream-switch-count && stream-switch-entry need to be on their own pragma lines
148 * we (ab)use SetBody for this
151 g_return_if_fail (mms != NULL);
152 g_return_if_fail (pragma != NULL);
153 g_return_if_fail (entry != NULL);
155 entry->GetSelectedStreams (mms->GetMaxBitrate (), streams);
157 g_string_append_printf (pragma, "Pragma: stream-switch-entry=");
158 for (int i = 0; i < 128; i++) {
159 switch (streams [i]) {
160 case -1: // invalid stream
161 break;
162 case 0: // not selected
163 count++;
164 g_string_append_printf (pragma, "%i:ffff:0 ", i);
165 break;
166 case 1: // selected
167 count++;
168 g_string_append_printf (pragma, "ffff:%i:0 ", i);
169 break;
170 default: // ?
171 printf ("MmsDownloader: invalid stream selection value (%i).\n", streams [i]);
172 break;
175 g_string_append_printf (pragma, "\r\n");
177 g_string_append_printf (pragma, "Pragma: stream-switch-count=%i\r\n", count);
180 void
181 MmsDownloader::Open (const char *verb, const char *uri)
183 LOG_MMS ("MmsDownloader::Open ('%s', '%s')\n", verb, uri);
185 VERIFY_MAIN_THREAD;
187 g_return_if_fail (this->uri == NULL);
188 g_return_if_fail (uri != NULL);
189 g_return_if_fail (strncmp (uri, "mms://", 6) == 0);
191 this->uri = g_strdup_printf ("http://%s", uri + 6);
193 dl->OpenInitialize ();
194 dl->SetRequireCustomHeaderSupport (true);
195 dl->SetDisableCache (true);
196 dl->InternalOpen (verb, this->uri);
198 set_common_dl_headers (dl, this, NULL);
199 dl->InternalSetHeader ("Pragma", "packet-pair-experiment=1");
200 dl->SetResponseHeaderCallback (ProcessResponseHeaderCallback, this);
203 void
204 MmsDownloader::PlayCallback (EventObject *sender)
206 ((MmsDownloader *) sender)->Play ();
209 void
210 MmsDownloader::Play ()
212 GString *pragma;
213 MmsPlaylistEntry *entry;
214 guint64 pts;
216 request_mutex.Lock ();
217 pts = requested_pts;
218 requested_pts = 0;
219 request_mutex.Unlock ();
221 LOG_MMS ("MmsDownloader::Play () requested_pts: %" G_GUINT64_FORMAT "\n", pts);
223 g_return_if_fail (source != NULL);
225 g_free (buffer);
226 buffer = NULL;
227 size = 0;
229 entry = source->GetCurrentReffed ();
231 g_return_if_fail (entry != NULL);
233 dl->InternalAbort ();
235 dl->OpenInitialize ();
236 dl->SetRequireCustomHeaderSupport (true);
237 dl->SetDisableCache (true);
238 dl->InternalOpen ("GET", uri);
240 pragma = g_string_new (NULL);
242 set_common_dl_headers (dl, this, pragma);
244 g_string_append_printf (pragma, "Pragma: rate=1.000000,stream-offset=0:0,max-duration=0\r\n");
245 g_string_append_printf (pragma, "Pragma: xPlayStrm=1\r\n");
246 g_string_append_printf (pragma, "Pragma: LinkBW=2147483647,rate=1.000, AccelDuration=20000, AccelBW=2147483647\r\n");
247 g_string_append_printf (pragma, "Pragma: stream-time=%" G_GINT64_FORMAT ", packet-num=4294967295\r\n", pts / 10000);
249 set_stream_selection_headers (this, pragma, entry);
251 g_string_append_printf (pragma, "\r\n"); // end of header
253 dl->InternalSetBody (pragma->str, pragma->len);
255 dl->Send ();
257 g_string_free (pragma, true);
258 entry->unref ();
260 is_playing = true;
263 void
264 MmsDownloader::ProcessResponseHeaderCallback (gpointer context, const char *header, const char *value)
266 MmsDownloader *dl = (MmsDownloader *) context;
267 g_return_if_fail (dl != NULL);
268 dl->SetCurrentDeployment ();
269 dl->ProcessResponseHeader (header, value);
272 void
273 MmsDownloader::ProcessResponseHeader (const char *header, const char *value)
275 char *h;
276 char *duped;
278 LOG_MMS ("MmsDownloader::ProcessResponseHeader ('%s', '%s')\n", header, value);
280 if (failure_reported)
281 return;
283 // check response code
284 DownloaderResponse *response = this->dl->GetResponse ();
285 if (response != NULL && response->GetResponseStatus () != 200) {
286 fprintf (stderr, "Moonlight: The MmsDownloader could not load the uri '%s', got response status: %i (expected 200)\n", uri, response->GetResponseStatus ());
287 failure_reported = true;
288 source->ReportDownloadFailure ();
289 return;
292 g_return_if_fail (header != NULL);
293 g_return_if_fail (value != NULL);
295 // we're only interested in the 'Pragma' header(s)
297 if (strcmp (header, "Pragma") != 0)
298 return;
300 h = g_strdup (value);
301 duped = h;
303 while (h != NULL && *h != 0) {
304 char *key = NULL;
305 char *val = NULL;
306 char c;
307 char *left;
309 key = parse_rfc_1945_token (h, &c, &left);
311 if (key == NULL)
312 break;
314 h = left;
316 if (key [0] == 0)
317 continue;
319 if (c == '=' && h != NULL) {
320 if (*h == '"') {
321 val = parse_rfc_1945_quoted_string (h + 1, &c, &left);
322 h = left;
323 } else if (*h != 0) {
324 val = parse_rfc_1945_token (h, &c, &left);
325 h = left;
329 // printf ("MmsDownloader::ResponseHeader (). processing 'Pragma', key='%s', value='%s'\n", key, val);
331 if (strcmp (key, "client-id") == 0) {
332 if (client_id != NULL)
333 g_free (client_id);
334 client_id = g_strdup (val);
338 g_free (duped);
341 void
342 MmsDownloader::Write (void *buf, gint32 off, gint32 n)
344 LOG_MMS ("MmsDownloader::Write (%p, %i, %i)\n", buf, off, n);
346 MmsHeader *header;
347 MmsPacket *packet;
348 char *payload;
349 guint32 offset = 0;
351 // Resize our internal buffer
352 if (buffer == NULL) {
353 buffer = (char *) g_malloc (n);
354 } else {
355 buffer = (char *) g_realloc (buffer, size + n);
358 // Populate the data into the buffer
359 memcpy (buffer + size, buf, n);
360 size += n;
362 // Check if we have an entire packet available.
364 while (size >= sizeof (MmsHeader)) {
366 header = (MmsHeader *) buffer;
368 if (!is_valid_mms_header (header)) {
369 LOG_MMS ("MmsDownloader::Write (): invalid mms header\n");
370 dl->Abort ();
371 dl->NotifyFailed ("invalid mms source");
372 return;
375 if (size < (header->length + sizeof (MmsHeader)))
376 return;
378 packet = (MmsPacket *) (buffer + sizeof (MmsHeader));
379 payload = (buffer + sizeof (MmsHeader) + sizeof (MmsDataPacket));
381 if (!ProcessPacket (header, packet, payload, &offset)) {
382 LOG_MMS ("MmsDownloader::Write (): packet processing failed\n");
383 break;
386 if (size > offset) {
387 // FIXME: We should refactor this to increment the buffer pointer to the new position
388 // but coalense the free / malloc / memcpy into batches to improve performance on big
389 // streams
390 char *new_buffer = (char *) g_malloc (size - offset);
391 memcpy (new_buffer, buffer + offset, size - offset);
392 g_free (buffer);
394 buffer = new_buffer;
395 size -= offset;
396 } else {
397 g_free (buffer);
398 buffer = NULL;
399 size = 0;
404 char *
405 MmsDownloader::GetDownloadedFilename (const char *partname)
407 LOG_MMS ("MmsDownloader::GetDownloadedFilename ('%s')\n", partname);
408 return NULL;
411 char *
412 MmsDownloader::GetResponseText (const char *partname, gint64 *size)
414 LOG_MMS ("MmsDownloader::GetResponseText ('%s', %p)\n", partname, size);
415 return NULL;
418 bool
419 MmsDownloader::ProcessPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
421 LOG_MMS ("MmsDownloader::ProcessPacket (%p, %p, %p, %p) length: %i\n", header, packet, payload, offset, header->length);
423 *offset = (header->length + sizeof (MmsHeader));
425 switch (header->id) {
426 case MMS_HEADER:
427 return ProcessHeaderPacket (header, packet, payload, offset);
428 case MMS_METADATA:
429 return ProcessMetadataPacket (header, packet, payload, offset);
430 case MMS_PAIR_P:
431 return ProcessPairPacket (header, packet, payload, offset);
432 case MMS_DATA:
433 return ProcessDataPacket (header, packet, payload, offset);
434 case MMS_END:
435 return ProcessEndPacket (header, packet, payload, offset);
436 case MMS_STREAM_C:
437 return ProcessStreamSwitchPacket (header, packet, payload, offset);
440 printf ("MmsDownloader::ProcessPacket received a unknown packet type %i.", (int) header->id);
442 return false;
445 bool
446 MmsDownloader::ProcessStreamSwitchPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
448 LOG_MMS ("MmsDownloader::ProcessStreamSwitchPacket ()\n");
449 VERIFY_MAIN_THREAD;
451 MmsHeaderReason *hr = (MmsHeaderReason *) header;
453 g_return_val_if_fail (source != NULL, false);
455 source->ReportStreamChange (hr->reason);
456 stream_switched = true;
458 return true;
461 bool
462 MmsDownloader::ProcessEndPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
464 LOG_MMS ("MmsDownloader::ProcessEndPacket ()\n");
465 VERIFY_MAIN_THREAD;
467 MmsHeaderReason *hr = (MmsHeaderReason *) header;
469 g_return_val_if_fail (source != NULL, false);
471 g_free (playlist_gen_id);
472 playlist_gen_id = NULL;
473 g_free (client_id);
474 client_id = NULL;
476 source->NotifyFinished (hr->reason);
478 // TODO: send log
480 return true;
483 MmsPlaylistEntry *
484 MmsDownloader::GetCurrentEntryReffed ()
486 VERIFY_MAIN_THREAD;
488 g_return_val_if_fail (source != NULL, NULL);
490 return source->GetCurrentReffed ();
493 bool
494 MmsDownloader::ProcessHeaderPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
496 bool success = true;
497 MmsPlaylistEntry *entry;
498 MediaResult result;
500 LOG_MMS ("MmsDownloader::ProcessHeaderPacket () is_playing: %i\n", is_playing);
502 entry = GetCurrentEntryReffed ();
504 g_return_val_if_fail (entry != NULL, false);
506 if (!entry->IsHeaderParsed ()) {
507 result = entry->ParseHeader (payload, header->length - sizeof (MmsDataPacket));
509 if (!MEDIA_SUCCEEDED (result)) {
510 LOG_MMS ("MmsDownloader::ProcessHeaderPacket (): failed to parse the asf header.\n");
511 success = false;
512 } else if (!is_playing) {
513 Play ();
514 } else if (stream_switched) {
515 MmsSecondDownloader *sdl = new MmsSecondDownloader (this);
516 sdl->SendStreamSwitch ();
517 sdl->SetKillTimeout (30 /* seconds */);
518 sdl->unref ();
520 } else {
521 // We've already parsed this header (in the Describe request).
522 // TODO: handle the xResetStream when the playlist changes
523 // TODO: can this be another header??
526 entry->unref ();
528 return success;
531 bool
532 MmsDownloader::ProcessMetadataPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
534 LOG_MMS ("MmsDownloader::ProcessMetadataPacket (%p, %p, %s, %p)\n", header, packet, payload, offset);
536 const char *playlist_gen_id = NULL;
537 const char *broadcast_id = NULL;
538 HttpStreamingFeatures features = HttpStreamingFeaturesNone;
540 char *start = payload;
541 char *key = NULL, *value = NULL;
542 char *state = NULL;
544 g_return_val_if_fail (source != NULL, false);
546 // format: key=value,key=value\0
547 // example:
548 // playlist-gen-id=1,broadcast-id=2,features="broadcast,seekable"\0
550 // Make sure payload is null-terminated
551 for (int i = 0; i < packet->packet.data.size; i++) {
552 if (payload [i] == 0)
553 break;
554 if (i == packet->packet.data.size - 1)
555 payload [i] = NULL;
558 // content description list
559 int payload_strlen = strlen (payload);
560 const char *cdl_start = NULL;
561 int cdl_length;
563 if (content_descriptions != NULL) {
564 delete content_descriptions;
565 content_descriptions = NULL;
568 if (packet->packet.data.size > payload_strlen + 1) {
569 cdl_start = payload + payload_strlen + 1;
570 cdl_length = packet->packet.data.size - payload_strlen - 2;
572 // parse content description list here
573 content_descriptions = new ContentDescriptionList ();
574 if (!content_descriptions->Parse (cdl_start, cdl_length)) {
575 delete content_descriptions;
576 content_descriptions = NULL;
580 do {
581 key = strtok_r (start, "=", &state);
582 start = NULL;
584 if (key == NULL)
585 break;
587 if (key [0] == ' ')
588 key++;
590 if (!strcmp (key, "features")) {
591 value = strtok_r (NULL, "\"", &state);
592 } else {
593 value = strtok_r (NULL, ",", &state);
596 if (value == NULL)
597 break;
599 LOG_MMS ("MmsDownloader::ProcessMetadataPacket (): %s=%s\n", key, value);
601 if (!strcmp (key, "playlist-gen-id")) {
602 playlist_gen_id = value;
603 } else if (!strcmp (key, "broadcast-id")) {
604 broadcast_id = value;
605 } else if (!strcmp (key, "features")) {
606 features = parse_http_streaming_features (value);
607 } else {
608 printf ("MmsDownloader::ProcessMetadataPacket (): Unexpected metadata: %s=%s\n", key, value);
610 } while (true);
612 if (this->playlist_gen_id != NULL)
613 g_free (this->playlist_gen_id);
614 this->playlist_gen_id = g_strdup (playlist_gen_id);
616 source->SetMmsMetadata (playlist_gen_id, broadcast_id, features);
618 LOG_MMS ("MmsDownloader::ProcessMetadataPacket (): playlist_gen_id: '%s', broadcast_id: '%s', features: %i\n", playlist_gen_id, broadcast_id, features);
620 return true;
623 bool
624 MmsDownloader::ProcessPairPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
626 LOG_MMS ("MmsDownloader::ProcessPairPacket ()\n");
628 if (p_packet_times [p_packet_count] == 0)
629 p_packet_times [p_packet_count] = get_now ();
631 // NOTE: If this is the 3rd $P packet, we need to increase the size reported in the header by
632 // the value in the reason field. This is a break from the normal behaviour of MMS packets
633 // so we need to guard against this occurnace here and ensure we actually have enough data
634 // buffered to consume
635 if (p_packet_count == 2 && size < (header->length + sizeof (MmsHeader) + packet->packet.reason))
636 return false;
638 // NOTE: We also need to account for the size of the reason field manually with our packet massaging.
639 *offset += 4;
641 // NOTE: If this is the first $P packet we've seen the reason is actually the amount of data
642 // that the header claims is in the payload, but is in fact not.
643 if (p_packet_count == 0) {
644 *offset -= packet->packet.reason;
647 // NOTE: If this is the third $P packet we've seen, reason is an amount of data that the packet
648 // is actually larger than the advertised packet size
649 if (p_packet_count == 2)
650 *offset += packet->packet.reason;
652 p_packet_sizes [p_packet_count] = *offset;
654 ++p_packet_count;
656 if (p_packet_times [0] == p_packet_times [2]) {
657 max_bitrate = 0; // prevent /0
658 } else {
659 max_bitrate = (gint64) (((p_packet_sizes [1] + p_packet_sizes [2]) * 8) / ((double) ((p_packet_times [2] - p_packet_times [0]) / (double) 10000000)));
662 return true;
665 bool
666 MmsDownloader::ProcessDataPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
668 LOG_MMS ("MmsDownloader::ProcessDataPacket ()\n");
670 g_return_val_if_fail (source != NULL, true);
672 source->WritePacket (payload, header->length - sizeof (MmsDataPacket));
674 return true;
678 * ContentDescriptionList
681 bool
682 ContentDescriptionList::Parse (const char *input, gint32 length)
684 bool result = false;
685 char *str;
686 char *duped;
687 int str_length = length;
688 char *end;
690 //LOG_MMS ("ContentDescriptionList::Parse ('%*s', %i)\n", (int) length, input, length);
692 // our input may contain embedded nulls or it may not have nulls at all
693 // (not even one at the end).
694 // since we use string parsing functions on the input, add a null at the
695 // end to not overrun anything.
697 str = (char *) g_malloc (str_length + 1);
698 memcpy (str, input, str_length);
699 str [str_length] = 0; // null terminate
700 duped = str; // save a copy of the allocated memory to free it later
701 end = str + str_length;
704 * The format is:
705 * <name length>,<name>,<value type>,<value length>,<value>
708 char *str_name_length;
709 char *str_name;
710 char *str_value_type;
711 char *str_value_length;
712 void *value;
713 char *comma;
715 gint64 name_length;
716 gint64 value_type;
717 gint64 value_length;
719 do {
720 // name length
721 comma = strchr (str, ',');
722 if (comma == NULL)
723 goto cleanup;
725 *comma = 0; // null terminate
726 str_name_length = str;
727 str = comma + 1;
729 name_length = strtoull (str_name_length, NULL, 10);
731 if (name_length < 0 || name_length > G_MAXINT32)
732 goto cleanup;
734 if (end - str < name_length + 1)
735 goto cleanup;
737 // name
738 str_name = str;
739 str_name [name_length] = 0; // null terminate
740 str += name_length + 1;
742 // value type
743 comma = strchr (str, ',');
744 if (comma == NULL)
745 goto cleanup;
747 *comma = 0; // null terminate
748 str_value_type = str;
749 str = comma + 1;
751 value_type = strtoull (str_value_type, NULL, 10);
753 if (value_type < 0 || value_type > G_MAXINT32)
754 goto cleanup;
756 // value length
757 comma = strchr (str, ',');
758 if (comma == NULL)
759 goto cleanup;
761 *comma = 0; // null terminate
762 str_value_length = str;
763 str = comma + 1;
765 value_length = strtoull (str_value_length, NULL, 10);
767 if (value_length < 0 || value_length > G_MAXINT32)
768 goto cleanup;
770 if (end - str < value_length)
771 goto cleanup;
773 value = str; // can't null terminate, we don't necessarily have a string
775 str += value_length;
777 ContentDescription *cd = new ContentDescription ();
778 cd->name = g_strndup (str_name, name_length);
779 cd->value_type = (ContentDescription::ValueType) value_type;
780 cd->value = g_malloc (value_length + 1);
781 memcpy (cd->value, value, value_length);
782 ((char *) cd->value) [value_length] = 0;
783 cd->value_length = value_length;
784 list.Append (cd);
786 // printf ("parsed: %*s = %*s\n", (int) name_length, str_name, (int) cd->value_length, (char *) cd->value);
788 // trailing commas
789 if (*str == ',') {
790 str++;
791 } else {
792 break;
794 } while (str < end);
796 result = true;
798 cleanup:
800 g_free (duped);
802 return result;
806 * ContentDescription
809 ContentDescription::~ContentDescription ()
811 g_free (name);
812 g_free (value);
816 * MmsSecondDownloader
819 MmsSecondDownloader::MmsSecondDownloader (MmsDownloader *mms)
821 VERIFY_MAIN_THREAD;
823 dl = NULL;
824 this->mms = mms;
825 this->mms->ref ();
826 kill_timeout = 0;
829 void
830 MmsSecondDownloader::Dispose ()
832 Deployment *deployment;
833 Surface *surface;
834 TimeManager *tm;
836 VERIFY_MAIN_THREAD;
838 if (dl != NULL) {
839 dl->RemoveAllHandlers (this);
840 dl->unref ();
841 dl = NULL;
844 if (mms != NULL) {
845 mms->unref ();
846 mms = NULL;
849 if (kill_timeout != 0) {
850 deployment = GetDeployment ();
851 surface = deployment ? deployment->GetSurface () : NULL;
852 tm = surface ? surface->GetTimeManager () : NULL;
853 if (tm != NULL) {
854 tm->RemoveTimeout (kill_timeout);
855 kill_timeout = 0;
856 unref ();
860 EventObject::Dispose ();
863 void
864 MmsSecondDownloader::DownloadFailedHandler (EventObject *sender, EventArgs *args)
866 LOG_MMS ("MmsLogger::DownloadFailedHandler ()\n");
867 VERIFY_MAIN_THREAD;
869 Dispose ();
872 void
873 MmsSecondDownloader::CompletedHandler (EventObject *sender, EventArgs *args)
875 LOG_MMS ("MmsLogger::CompletedHandler ()\n");
876 VERIFY_MAIN_THREAD;
878 Dispose ();
881 void
882 MmsSecondDownloader::data_write (void *data, gint32 offset, gint32 n, void *closure)
884 LOG_MMS ("((MmsLogger *) closure)->DataWrite (data = %p, offset = %i, n = %i);\n", data, offset, n);
887 void
888 MmsSecondDownloader::CreateDownloader ()
890 Deployment *deployment;
891 Surface *surface;
893 VERIFY_MAIN_THREAD;
895 deployment = GetDeployment ();
897 g_return_if_fail (dl == NULL);
898 g_return_if_fail (deployment != NULL);
900 surface = deployment->GetSurface ();
902 g_return_if_fail (surface != NULL);
904 dl = surface->CreateDownloader ();
906 dl->AddHandler (Downloader::DownloadFailedEvent, DownloadFailedCallback, this);
907 dl->AddHandler (Downloader::CompletedEvent, CompletedCallback, this);
908 dl->SetStreamFunctions (data_write, NULL, this);
910 dl->SetRequireCustomHeaderSupport (true);
911 // firefox will not download an uri equal to the mms uri simultaneously with the mms uri
912 // it tries to open a cache entry for writing, which fails since the cache entry is already in use
913 // sp we disable the cace
914 dl->SetDisableCache (true);
915 dl->Open ("POST", mms->GetUri (), StreamingPolicy);
918 void
919 MmsSecondDownloader::SetKillTimeout (guint seconds)
921 Deployment *deployment;
922 Surface *surface;
923 TimeManager *tm;
925 deployment = GetDeployment ();
926 surface = deployment ? deployment->GetSurface () : NULL;
927 tm = surface ? surface->GetTimeManager () : NULL;
929 g_return_if_fail (tm != NULL);
931 this->ref ();
932 tm->AddTimeout (MOON_PRIORITY_IDLE, seconds * 1000, KillTimeoutCallback, this);
935 gboolean
936 MmsSecondDownloader::KillTimeoutCallback (gpointer context)
938 ((MmsSecondDownloader *) context)->KillTimeoutHandler ();
939 return false;
942 void
943 MmsSecondDownloader::KillTimeoutHandler ()
945 LOG_MMS ("MmsSecondDownloader::KillTimeoutHandler (), dl: %p\n", dl);
946 kill_timeout = 0;
947 SetCurrentDeployment ();
948 unref ();
949 Deployment::SetCurrent (NULL);
952 void
953 MmsSecondDownloader::SendStreamSwitch ()
955 GString *pragma;
956 MmsPlaylistEntry *entry;
958 g_return_if_fail (mms != NULL);
960 CreateDownloader ();
962 g_return_if_fail (dl != NULL);
964 entry = mms->GetCurrentEntryReffed ();
966 pragma = g_string_new (NULL);
968 set_common_dl_headers (dl, mms, pragma);
969 set_stream_selection_headers (mms, pragma, entry);
971 g_string_append (pragma, "\r\n");
973 dl->InternalSetBody (pragma->str, pragma->len);
974 dl->Send ();
976 entry->unref ();
977 g_string_free (pragma, true);
979 LOG_MMS ("MmsSecondDownloader::SendStreamSwitch (): Sent.\n");
983 * Work in progress
985 void
986 MmsSecondDownloader::SendLog ()
988 CreateDownloader ();
990 g_return_if_fail (dl != NULL);
993 // POST /SSPLDrtOnDemandTest HTTP/1.0
994 // Host: moonlightmedia
995 // Content-Length: 2203
996 // User-Agent: NSPlayer/11.08.0005.0000
997 // Accept: * / *
998 // Accept-Language: en-us, *;q=0.1
999 // Connection: Keep-Alive
1000 // Content-Type: application/x-wms-Logstats
1001 // Pragma: client-id=3375607867
1002 // Pragma: playlist-gen-id=2769
1003 // Pragma: xClientGuid={00000000-0000-0000-0000-000000000000}
1004 // Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.startupprofile, com.microsoft.wm.predstrm
1007 dl->InternalSetHeader ("User-Agent", "NSPlayer/11.08.0005.0000");
1008 dl->InternalSetHeader ("Content-Type", "application/x-wms-Logstats");
1010 GString *header = g_string_new (NULL);
1012 set_common_dl_headers (dl, mms, header);
1014 GString *all = g_string_new (NULL);
1015 GString *xml = g_string_new (NULL);
1016 GString *summary = g_string_new (NULL);
1018 // "<c-ip>0.0.0.0</c-ip>"
1019 g_string_append (summary, "0.0.0.0 ");
1020 g_string_append (xml, "<c-ip>0.0.0.0</c-ip>");
1022 // "<date>%.4i-%.2i-%.2i</date>" // yyyy-MM-dd
1023 tm now;
1024 time_t time_now = time (NULL);
1025 gmtime_r (&time_now, &now);
1026 g_string_append_printf (summary, "%.4i-%.2i-%.2i ", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
1027 g_string_append_printf (xml, "<date>%.4i-%.2i-%.2i</date>", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
1029 // "<time>%.2i:%.2i:%.2i</time>" // HH:mm:ss
1030 g_string_append_printf (summary, "%.2i:%.2i:%.2i ", now.tm_hour, now.tm_min, now.tm_sec);
1031 g_string_append_printf (xml, "<time>%.2i:%.2i:%.2i</time>", now.tm_hour, now.tm_min, now.tm_sec);
1033 // "<c-dns>-</c-dns>"
1034 g_string_append_printf (summary, "- ");
1035 g_string_append_printf (xml, "<c-dns>-</c-dns>");
1037 // "<cs-uri-stem>-</cs-uri-stem>"
1038 g_string_append_printf (summary, "- ");
1039 g_string_append_printf (xml, "<cs-uri-stem>-</cs-uri-stem>");
1041 // "<c-starttime>0</c-starttime>"
1042 g_string_append_printf (summary, "0 ");
1043 g_string_append_printf (xml, "<c-starttime>0</c-starttime>");
1045 // "<x-duration>0</x-duration>"
1046 g_string_append_printf (summary, "0 ");
1047 g_string_append_printf (xml, "<x-duration>0</x-duration>");
1049 //"<c-rate>-</c-rate>"
1050 g_string_append_printf (summary, "- ");
1051 g_string_append_printf (xml, "<c-rate>-</c-rate>");
1053 //"<c-status>200</c-status>"
1054 g_string_append_printf (summary, "200 ");
1055 g_string_append_printf (xml, "<c-status>200</c-status>");
1057 // "<c-playerid>" CLIENT_GUID "</c-playerid>"
1058 g_string_append_printf (summary, "%s ", CLIENT_GUID);
1059 g_string_append_printf (xml, "<c-playerid>%s</c-playerid>", CLIENT_GUID);
1061 // "<c-playerversion>-</c-playerversion>"
1062 g_string_append_printf (summary, "- ");
1063 g_string_append_printf (xml, "<c-playerversion>-</c-playerversion>");
1065 // "<c-playerlanguage>-</c-playerlanguage>"
1066 g_string_append_printf (summary, "- ");
1067 g_string_append_printf (xml, "<c-playerlanguage>-</c-playerlanguage>");
1069 // "<cs-User-Agent>%s</cs-User-Agent>"
1070 const char *user_agent = "Mozilla/5.0_(Windows;_U;_Windows_NT_5.1;_en-GB;_rv:1.9.0.9)_Gecko/2009040821_Firefox/3.0.9_(.NET_CLR_3.5.30729)_NSPlayer/11.08.0005.0000_Silverlight/2.0.40115.0"; //"Firefox";
1071 g_string_append_printf (summary, "%s ", user_agent);
1072 g_string_append_printf (xml, "<cs-User-Agent>%s</cs-User-Agent>", user_agent);
1074 // "<cs-Referer>%s</cs-Referer>"
1075 const char *referrer = "http://192.168.1.4:8080/media/video/test-server-side-playlist.html";//"http://example.com/";
1076 g_string_append_printf (summary, "%s ", referrer);
1077 g_string_append_printf (xml, "<cs-Referer>%s</cs-Referer>", referrer);
1079 // "<c-hostexe>-</c-hostexe>"
1080 g_string_append_printf (summary, "- ");
1081 g_string_append_printf (xml, "<c-hostexe>-</c-hostexe>");
1083 // "<c-hostexever>-</c-hostexever>"
1084 g_string_append_printf (summary, "- ");
1085 g_string_append_printf (xml, "<c-hostexever>-</c-hostexever>");
1087 // "<c-os>Linux</c-os>"
1088 g_string_append_printf (summary, "Windows_XP ");
1089 g_string_append_printf (xml, "<c-os>Windows_XP</c-os>");
1091 // "<c-osversion>-</c-osversion>"
1092 g_string_append_printf (summary, "- ");
1093 g_string_append_printf (xml, "<c-osversion>-</c-osversion>");
1095 // "<c-cpu>-</c-cpu>"
1096 g_string_append_printf (summary, "- ");
1097 g_string_append_printf (xml, "<c-cpu>-</c-cpu>");
1099 // "<filelength>-</filelength>"
1100 g_string_append_printf (summary, "- ");
1101 g_string_append_printf (xml, "<filelength>-</filelength>");
1103 // "<filesize>-</filesize>"
1104 g_string_append_printf (summary, "- ");
1105 g_string_append_printf (xml, "<filesize>-</filesize>");
1107 // "<avgbandwidth>-</avgbandwidth>"
1108 g_string_append_printf (summary, "- ");
1109 g_string_append_printf (xml, "<avgbandwidth>-</avgbandwidth>");
1111 // "<protocol>http</protocol>"
1112 g_string_append_printf (summary, "http ");
1113 g_string_append_printf (xml, "<protocol>http</protocol>");
1115 // "<transport>TCP</transport>"
1116 g_string_append_printf (summary, "TCP ");
1117 g_string_append_printf (xml, "<transport>TCP</transport>");
1119 // "<audiocodec>-</audiocodec>"
1120 g_string_append_printf (summary, "- ");
1121 g_string_append_printf (xml, "<audiocodec>-</audiocodec>");
1123 // "<videocodec>-</videocodec>"
1124 g_string_append_printf (summary, "- ");
1125 g_string_append_printf (xml, "<videocodec>-</videocodec>");
1127 // "<c-channelURL>-</c-channelURL>"
1128 g_string_append_printf (summary, "- ");
1129 g_string_append_printf (xml, "<c-channelURL>-</c-channelURL>");
1131 // "<sc-bytes>-</sc-bytes>"
1132 g_string_append_printf (summary, "- ");
1133 g_string_append_printf (xml, "<sc-bytes>-</sc-bytes>");
1135 // "<c-bytes>-</c-bytes>"
1136 g_string_append_printf (summary, "- ");
1137 g_string_append_printf (xml, "<c-bytes>-</c-bytes>");
1139 // "<s-pkts-sent>-</s-pkts-sent>"
1140 g_string_append_printf (summary, "- ");
1141 g_string_append_printf (xml, "<s-pkts-sent>-</s-pkts-sent>");
1143 // "<c-pkts-received>-</c-pkts-received>"
1144 g_string_append_printf (summary, "- ");
1145 g_string_append_printf (xml, "<c-pkts-received>-</c-pkts-received>");
1147 // "<c-pkts-lost-client>-</c-pkts-lost-client>"
1148 g_string_append_printf (summary, "- ");
1149 g_string_append_printf (xml, "<c-pkts-lost-client>-</c-pkts-lost-client>");
1151 // "<c-pkts-lost-net>-</c-pkts-lost-net>"+
1152 g_string_append_printf (summary, "- ");
1153 g_string_append_printf (xml, "<c-pkts-lost-net>-</c-pkts-lost-net>");
1155 // "<c-pkts-lost-cont-net>-</c-pkts-lost-cont-net>"
1156 g_string_append_printf (summary, "- ");
1157 g_string_append_printf (xml, "<c-pkts-lost-cont-net>-</c-pkts-lost-cont-net>");
1159 // "<c-resendreqs>-</c-resendreqs>"
1160 g_string_append_printf (summary, "- ");
1161 g_string_append_printf (xml, "<c-resendreqs>-</c-resendreqs>");
1163 // "<c-pkts-recovered-ECC>-</c-pkts-recovered-ECC>"
1164 g_string_append_printf (summary, "- ");
1165 g_string_append_printf (xml, "<c-pkts-recovered-ECC>-</c-pkts-recovered-ECC>");
1167 // "<c-pkts-recovered-resent>-</c-pkts-recovered-resent>"
1168 g_string_append_printf (summary, "- ");
1169 g_string_append_printf (xml, "<c-pkts-recovered-resent>-</c-pkts-recovered-resent>");
1171 // "<c-buffercount>-</c-buffercount>"
1172 g_string_append_printf (summary, "- ");
1173 g_string_append_printf (xml, "<c-buffercount>-</c-buffercount>");
1175 // "<c-totalbuffertime>-</c-totalbuffertime>"
1176 g_string_append_printf (summary, "- ");
1177 g_string_append_printf (xml, "<c-totalbuffertime>-</c-totalbuffertime>");
1179 // "<c-quality>-</c-quality>"
1180 g_string_append_printf (summary, "- ");
1181 g_string_append_printf (xml, "<c-quality>-</c-quality>");
1183 // "<s-ip>-</s-ip><s-dns>-</s-dns>"
1184 g_string_append_printf (summary, "- ");
1185 g_string_append_printf (xml, "<s-ip>-</s-ip><s-dns>-</s-dns>");
1187 // "<s-totalclients>-</s-totalclients>"
1188 g_string_append_printf (summary, "- ");
1189 g_string_append_printf (xml, "<s-totalclients>-</s-totalclients>");
1191 // "<s-cpu-util>-</s-cpu-util>"
1192 g_string_append_printf (summary, "- ");
1193 g_string_append_printf (xml, "<s-cpu-util>-</s-cpu-util>");
1195 // "<cs-url>%s</cs-url>"
1196 g_string_append_printf (summary, "%s ", mms->GetUri ());
1197 g_string_append_printf (xml, "<cs-url>%s</cs-url>", mms->GetUri ());
1199 // "<cs-media-name>-</cs-media-name>"
1200 g_string_append_printf (summary, "- ");
1201 g_string_append_printf (xml, "<cs-media-name>-</cs-media-name>");
1203 // "<cs-media-role>-</cs-media-role>"
1204 g_string_append_printf (summary, "-"); // skip the last space
1205 g_string_append_printf (xml, "<cs-media-role>-</cs-media-role>");
1207 g_string_append_printf (header, "Content-Length: %i\r\n", summary->len + xml->len + 11 + 19); // length of <XML></XML> + <Summary/>
1209 g_string_append_printf (header, "\r\n"); // end of header
1211 g_string_append (all, header->str);
1212 g_string_append (all, "<XML><Summary>");
1213 g_string_append (all, summary->str);
1214 g_string_append (all, "</Summary>");
1215 g_string_append (all, xml->str);
1216 g_string_append (all, "</XML>");
1218 dl->InternalSetBody (all->str, all->len);
1220 g_string_free (all, true);
1221 g_string_free (xml, true);
1222 g_string_free (summary, true);
1223 g_string_free (header, true);
1225 dl->Send ();
1227 LOG_MMS ("MmsDownloader: sent log.\n");