revert jeff's last commit since it breaks the build
[moon.git] / src / mms-downloader.cpp
blob287907a397eb009f11e106e396d9d660e7969c9f
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 AddTickCall (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 int offset = 0;
185 LOG_MMS ("MmsDownloader::Open ('%s', '%s')\n", verb, uri);
187 VERIFY_MAIN_THREAD;
189 g_return_if_fail (this->uri == NULL);
190 g_return_if_fail (uri != NULL);
192 if (strncmp (uri, "mms://", 6) == 0) {
193 offset = 6;
194 } else if (strncmp (uri, "rtsp://", 7) == 0) {
195 offset = 7;
196 } else if (strncmp (uri, "rtsps://", 8) == 0) {
197 offset = 8;
198 } else {
199 fprintf (stderr, "Moonlight: streaming scheme must be either mms, rtsp or rtsps, got uri: %s\n", uri);
200 return;
203 this->uri = g_strdup_printf ("http://%s", uri + offset);
205 dl->OpenInitialize ();
206 dl->SetRequireCustomHeaderSupport (true);
207 dl->SetDisableCache (true);
208 dl->InternalOpen (verb, this->uri);
210 set_common_dl_headers (dl, this, NULL);
211 dl->InternalSetHeader ("Pragma", "packet-pair-experiment=1");
212 dl->SetResponseHeaderCallback (ProcessResponseHeaderCallback, this);
215 void
216 MmsDownloader::PlayCallback (EventObject *sender)
218 ((MmsDownloader *) sender)->Play ();
221 void
222 MmsDownloader::Play ()
224 GString *pragma;
225 MmsPlaylistEntry *entry;
226 guint64 pts;
228 request_mutex.Lock ();
229 pts = requested_pts;
230 requested_pts = 0;
231 request_mutex.Unlock ();
233 LOG_MMS ("MmsDownloader::Play () requested_pts: %" G_GUINT64_FORMAT "\n", pts);
235 g_return_if_fail (source != NULL);
237 g_free (buffer);
238 buffer = NULL;
239 size = 0;
241 entry = source->GetCurrentReffed ();
243 g_return_if_fail (entry != NULL);
245 dl->InternalAbort ();
247 dl->OpenInitialize ();
248 dl->SetRequireCustomHeaderSupport (true);
249 dl->SetDisableCache (true);
250 dl->InternalOpen ("GET", uri);
252 pragma = g_string_new (NULL);
254 set_common_dl_headers (dl, this, pragma);
256 g_string_append_printf (pragma, "Pragma: rate=1.000000,stream-offset=0:0,max-duration=0\r\n");
257 g_string_append_printf (pragma, "Pragma: xPlayStrm=1\r\n");
258 g_string_append_printf (pragma, "Pragma: LinkBW=2147483647,rate=1.000, AccelDuration=20000, AccelBW=2147483647\r\n");
259 g_string_append_printf (pragma, "Pragma: stream-time=%" G_GINT64_FORMAT ", packet-num=4294967295\r\n", pts / 10000);
261 set_stream_selection_headers (this, pragma, entry);
263 g_string_append_printf (pragma, "\r\n"); // end of header
265 dl->InternalSetBody (pragma->str, pragma->len);
267 dl->Send ();
269 g_string_free (pragma, true);
270 entry->unref ();
272 is_playing = true;
275 void
276 MmsDownloader::ProcessResponseHeaderCallback (gpointer context, const char *header, const char *value)
278 MmsDownloader *dl = (MmsDownloader *) context;
279 g_return_if_fail (dl != NULL);
280 dl->SetCurrentDeployment ();
281 dl->ProcessResponseHeader (header, value);
284 void
285 MmsDownloader::ProcessResponseHeader (const char *header, const char *value)
287 char *h;
288 char *duped;
290 LOG_MMS ("MmsDownloader::ProcessResponseHeader ('%s', '%s')\n", header, value);
292 if (failure_reported)
293 return;
295 // check response code
296 DownloaderResponse *response = this->dl->GetResponse ();
297 if (response != NULL && response->GetResponseStatus () != 200) {
298 fprintf (stderr, "Moonlight: The MmsDownloader could not load the uri '%s', got response status: %i (expected 200)\n", uri, response->GetResponseStatus ());
299 failure_reported = true;
300 if (source)
301 source->ReportDownloadFailure ();
302 return;
305 g_return_if_fail (header != NULL);
306 g_return_if_fail (value != NULL);
308 // we're only interested in the 'Pragma' header(s)
310 if (strcmp (header, "Pragma") != 0)
311 return;
313 h = g_strdup (value);
314 duped = h;
316 while (h != NULL && *h != 0) {
317 char *key = NULL;
318 char *val = NULL;
319 char c;
320 char *left;
322 key = parse_rfc_1945_token (h, &c, &left);
324 if (key == NULL)
325 break;
327 h = left;
329 if (key [0] == 0)
330 continue;
332 if (c == '=' && h != NULL) {
333 if (*h == '"') {
334 val = parse_rfc_1945_quoted_string (h + 1, &c, &left);
335 h = left;
336 } else if (*h != 0) {
337 val = parse_rfc_1945_token (h, &c, &left);
338 h = left;
342 // printf ("MmsDownloader::ResponseHeader (). processing 'Pragma', key='%s', value='%s'\n", key, val);
344 if (strcmp (key, "client-id") == 0) {
345 if (client_id != NULL)
346 g_free (client_id);
347 client_id = g_strdup (val);
351 g_free (duped);
354 void
355 MmsDownloader::Write (void *buf, gint32 off, gint32 n)
357 LOG_MMS ("MmsDownloader::Write (%p, %i, %i)\n", buf, off, n);
359 MmsHeader *header;
360 MmsPacket *packet;
361 char *payload;
362 guint32 offset = 0;
364 // Resize our internal buffer
365 if (buffer == NULL) {
366 buffer = (char *) g_malloc (n);
367 } else {
368 buffer = (char *) g_realloc (buffer, size + n);
371 // Populate the data into the buffer
372 memcpy (buffer + size, buf, n);
373 size += n;
375 // Check if we have an entire packet available.
377 while (size >= sizeof (MmsHeader)) {
379 header = (MmsHeader *) buffer;
381 if (!is_valid_mms_header (header)) {
382 LOG_MMS ("MmsDownloader::Write (): invalid mms header\n");
383 dl->Abort ();
384 dl->NotifyFailed ("invalid mms source");
385 return;
388 if (size < (header->length + sizeof (MmsHeader)))
389 return;
391 packet = (MmsPacket *) (buffer + sizeof (MmsHeader));
392 payload = (buffer + sizeof (MmsHeader) + sizeof (MmsDataPacket));
394 if (!ProcessPacket (header, packet, payload, &offset)) {
395 LOG_MMS ("MmsDownloader::Write (): packet processing failed\n");
396 break;
399 if (size > offset) {
400 // FIXME: We should refactor this to increment the buffer pointer to the new position
401 // but coalense the free / malloc / memcpy into batches to improve performance on big
402 // streams
403 char *new_buffer = (char *) g_malloc (size - offset);
404 memcpy (new_buffer, buffer + offset, size - offset);
405 g_free (buffer);
407 buffer = new_buffer;
408 size -= offset;
409 } else {
410 g_free (buffer);
411 buffer = NULL;
412 size = 0;
417 char *
418 MmsDownloader::GetDownloadedFilename (const char *partname)
420 LOG_MMS ("MmsDownloader::GetDownloadedFilename ('%s')\n", partname);
421 return NULL;
424 char *
425 MmsDownloader::GetResponseText (const char *partname, gint64 *size)
427 LOG_MMS ("MmsDownloader::GetResponseText ('%s', %p)\n", partname, size);
428 return NULL;
431 bool
432 MmsDownloader::ProcessPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
434 LOG_MMS ("MmsDownloader::ProcessPacket (%p, %p, %p, %p) length: %i\n", header, packet, payload, offset, header->length);
436 *offset = (header->length + sizeof (MmsHeader));
438 switch (header->id) {
439 case MMS_HEADER:
440 return ProcessHeaderPacket (header, packet, payload, offset);
441 case MMS_METADATA:
442 return ProcessMetadataPacket (header, packet, payload, offset);
443 case MMS_PAIR_P:
444 return ProcessPairPacket (header, packet, payload, offset);
445 case MMS_DATA:
446 return ProcessDataPacket (header, packet, payload, offset);
447 case MMS_END:
448 return ProcessEndPacket (header, packet, payload, offset);
449 case MMS_STREAM_C:
450 return ProcessStreamSwitchPacket (header, packet, payload, offset);
453 printf ("MmsDownloader::ProcessPacket received a unknown packet type %i.", (int) header->id);
455 return false;
458 bool
459 MmsDownloader::ProcessStreamSwitchPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
461 LOG_MMS ("MmsDownloader::ProcessStreamSwitchPacket ()\n");
462 VERIFY_MAIN_THREAD;
464 MmsHeaderReason *hr = (MmsHeaderReason *) header;
466 g_return_val_if_fail (source != NULL, false);
468 source->ReportStreamChange (hr->reason);
469 stream_switched = true;
471 return true;
474 bool
475 MmsDownloader::ProcessEndPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
477 LOG_MMS ("MmsDownloader::ProcessEndPacket ()\n");
478 VERIFY_MAIN_THREAD;
480 MmsHeaderReason *hr = (MmsHeaderReason *) header;
482 g_return_val_if_fail (source != NULL, false);
484 g_free (playlist_gen_id);
485 playlist_gen_id = NULL;
486 g_free (client_id);
487 client_id = NULL;
489 source->NotifyFinished (hr->reason);
491 // TODO: send log
493 return true;
496 MmsPlaylistEntry *
497 MmsDownloader::GetCurrentEntryReffed ()
499 VERIFY_MAIN_THREAD;
501 g_return_val_if_fail (source != NULL, NULL);
503 return source->GetCurrentReffed ();
506 bool
507 MmsDownloader::ProcessHeaderPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
509 bool success = true;
510 MmsPlaylistEntry *entry;
511 MediaResult result;
513 LOG_MMS ("MmsDownloader::ProcessHeaderPacket () is_playing: %i\n", is_playing);
515 entry = GetCurrentEntryReffed ();
517 g_return_val_if_fail (entry != NULL, false);
519 if (!entry->IsHeaderParsed ()) {
520 result = entry->ParseHeader (payload, header->length - sizeof (MmsDataPacket));
522 if (!MEDIA_SUCCEEDED (result)) {
523 LOG_MMS ("MmsDownloader::ProcessHeaderPacket (): failed to parse the asf header.\n");
524 success = false;
525 } else if (!is_playing) {
526 Play ();
527 } else if (stream_switched) {
528 MmsSecondDownloader *sdl = new MmsSecondDownloader (this);
529 sdl->SendStreamSwitch ();
530 sdl->SetKillTimeout (30 /* seconds */);
531 sdl->unref ();
533 } else {
534 // We've already parsed this header (in the Describe request).
535 // TODO: handle the xResetStream when the playlist changes
536 // TODO: can this be another header??
539 entry->unref ();
541 return success;
544 bool
545 MmsDownloader::ProcessMetadataPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
547 LOG_MMS ("MmsDownloader::ProcessMetadataPacket (%p, %p, %s, %p)\n", header, packet, payload, offset);
549 const char *playlist_gen_id = NULL;
550 const char *broadcast_id = NULL;
551 HttpStreamingFeatures features = HttpStreamingFeaturesNone;
553 char *start = payload;
554 char *key = NULL, *value = NULL;
555 char *state = NULL;
557 g_return_val_if_fail (source != NULL, false);
559 // format: key=value,key=value\0
560 // example:
561 // playlist-gen-id=1,broadcast-id=2,features="broadcast,seekable"\0
563 // Make sure payload is null-terminated
564 for (int i = 0; i < packet->packet.data.size; i++) {
565 if (payload [i] == 0)
566 break;
567 if (i == packet->packet.data.size - 1)
568 payload [i] = NULL;
571 // content description list
572 int payload_strlen = strlen (payload);
573 const char *cdl_start = NULL;
574 int cdl_length;
576 if (content_descriptions != NULL) {
577 delete content_descriptions;
578 content_descriptions = NULL;
581 if (packet->packet.data.size > payload_strlen + 1) {
582 cdl_start = payload + payload_strlen + 1;
583 cdl_length = packet->packet.data.size - payload_strlen - 2;
585 // parse content description list here
586 content_descriptions = new ContentDescriptionList ();
587 if (!content_descriptions->Parse (cdl_start, cdl_length)) {
588 delete content_descriptions;
589 content_descriptions = NULL;
593 do {
594 key = strtok_r (start, "=", &state);
595 start = NULL;
597 if (key == NULL)
598 break;
600 if (key [0] == ' ')
601 key++;
603 if (!strcmp (key, "features")) {
604 value = strtok_r (NULL, "\"", &state);
605 } else {
606 value = strtok_r (NULL, ",", &state);
609 if (value == NULL)
610 break;
612 LOG_MMS ("MmsDownloader::ProcessMetadataPacket (): %s=%s\n", key, value);
614 if (!strcmp (key, "playlist-gen-id")) {
615 playlist_gen_id = value;
616 } else if (!strcmp (key, "broadcast-id")) {
617 broadcast_id = value;
618 } else if (!strcmp (key, "features")) {
619 features = parse_http_streaming_features (value);
620 } else {
621 printf ("MmsDownloader::ProcessMetadataPacket (): Unexpected metadata: %s=%s\n", key, value);
623 } while (true);
625 if (this->playlist_gen_id != NULL)
626 g_free (this->playlist_gen_id);
627 this->playlist_gen_id = g_strdup (playlist_gen_id);
629 source->SetMmsMetadata (playlist_gen_id, broadcast_id, features);
631 LOG_MMS ("MmsDownloader::ProcessMetadataPacket (): playlist_gen_id: '%s', broadcast_id: '%s', features: %i\n", playlist_gen_id, broadcast_id, features);
633 return true;
636 bool
637 MmsDownloader::ProcessPairPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
639 LOG_MMS ("MmsDownloader::ProcessPairPacket ()\n");
641 if (p_packet_times [p_packet_count] == 0)
642 p_packet_times [p_packet_count] = get_now ();
644 // NOTE: If this is the 3rd $P packet, we need to increase the size reported in the header by
645 // the value in the reason field. This is a break from the normal behaviour of MMS packets
646 // so we need to guard against this occurnace here and ensure we actually have enough data
647 // buffered to consume
648 if (p_packet_count == 2 && size < (header->length + sizeof (MmsHeader) + packet->packet.reason))
649 return false;
651 // NOTE: We also need to account for the size of the reason field manually with our packet massaging.
652 *offset += 4;
654 // NOTE: If this is the first $P packet we've seen the reason is actually the amount of data
655 // that the header claims is in the payload, but is in fact not.
656 if (p_packet_count == 0) {
657 *offset -= packet->packet.reason;
660 // NOTE: If this is the third $P packet we've seen, reason is an amount of data that the packet
661 // is actually larger than the advertised packet size
662 if (p_packet_count == 2)
663 *offset += packet->packet.reason;
665 p_packet_sizes [p_packet_count] = *offset;
667 ++p_packet_count;
669 if (p_packet_times [0] == p_packet_times [2]) {
670 max_bitrate = 0; // prevent /0
671 } else {
672 max_bitrate = (gint64) (((p_packet_sizes [1] + p_packet_sizes [2]) * 8) / ((double) ((p_packet_times [2] - p_packet_times [0]) / (double) 10000000)));
675 return true;
678 bool
679 MmsDownloader::ProcessDataPacket (MmsHeader *header, MmsPacket *packet, char *payload, guint32 *offset)
681 LOG_MMS ("MmsDownloader::ProcessDataPacket ()\n");
683 g_return_val_if_fail (source != NULL, true);
685 source->WritePacket (payload, header->length - sizeof (MmsDataPacket));
687 return true;
691 * ContentDescriptionList
694 bool
695 ContentDescriptionList::Parse (const char *input, gint32 length)
697 bool result = false;
698 char *str;
699 char *duped;
700 int str_length = length;
701 char *end;
703 //LOG_MMS ("ContentDescriptionList::Parse ('%*s', %i)\n", (int) length, input, length);
705 // our input may contain embedded nulls or it may not have nulls at all
706 // (not even one at the end).
707 // since we use string parsing functions on the input, add a null at the
708 // end to not overrun anything.
710 str = (char *) g_malloc (str_length + 1);
711 memcpy (str, input, str_length);
712 str [str_length] = 0; // null terminate
713 duped = str; // save a copy of the allocated memory to free it later
714 end = str + str_length;
717 * The format is:
718 * <name length>,<name>,<value type>,<value length>,<value>
721 char *str_name_length;
722 char *str_name;
723 char *str_value_type;
724 char *str_value_length;
725 void *value;
726 char *comma;
728 gint64 name_length;
729 gint64 value_type;
730 gint64 value_length;
732 do {
733 // name length
734 comma = strchr (str, ',');
735 if (comma == NULL)
736 goto cleanup;
738 *comma = 0; // null terminate
739 str_name_length = str;
740 str = comma + 1;
742 name_length = strtoull (str_name_length, NULL, 10);
744 if (name_length < 0 || name_length > G_MAXINT32)
745 goto cleanup;
747 if (end - str < name_length + 1)
748 goto cleanup;
750 // name
751 str_name = str;
752 str_name [name_length] = 0; // null terminate
753 str += name_length + 1;
755 // value type
756 comma = strchr (str, ',');
757 if (comma == NULL)
758 goto cleanup;
760 *comma = 0; // null terminate
761 str_value_type = str;
762 str = comma + 1;
764 value_type = strtoull (str_value_type, NULL, 10);
766 if (value_type < 0 || value_type > G_MAXINT32)
767 goto cleanup;
769 // value length
770 comma = strchr (str, ',');
771 if (comma == NULL)
772 goto cleanup;
774 *comma = 0; // null terminate
775 str_value_length = str;
776 str = comma + 1;
778 value_length = strtoull (str_value_length, NULL, 10);
780 if (value_length < 0 || value_length > G_MAXINT32)
781 goto cleanup;
783 if (end - str < value_length)
784 goto cleanup;
786 value = str; // can't null terminate, we don't necessarily have a string
788 str += value_length;
790 ContentDescription *cd = new ContentDescription ();
791 cd->name = g_strndup (str_name, name_length);
792 cd->value_type = (ContentDescription::ValueType) value_type;
793 cd->value = g_malloc (value_length + 1);
794 memcpy (cd->value, value, value_length);
795 ((char *) cd->value) [value_length] = 0;
796 cd->value_length = value_length;
797 list.Append (cd);
799 // printf ("parsed: %*s = %*s\n", (int) name_length, str_name, (int) cd->value_length, (char *) cd->value);
801 // trailing commas
802 if (*str == ',') {
803 str++;
804 } else {
805 break;
807 } while (str < end);
809 result = true;
811 cleanup:
813 g_free (duped);
815 return result;
819 * ContentDescription
822 ContentDescription::~ContentDescription ()
824 g_free (name);
825 g_free (value);
829 * MmsSecondDownloader
832 MmsSecondDownloader::MmsSecondDownloader (MmsDownloader *mms)
834 VERIFY_MAIN_THREAD;
836 dl = NULL;
837 this->mms = mms;
838 this->mms->ref ();
839 kill_timeout = 0;
842 void
843 MmsSecondDownloader::Dispose ()
845 Deployment *deployment;
846 Surface *surface;
847 TimeManager *tm;
849 VERIFY_MAIN_THREAD;
851 if (dl != NULL) {
852 dl->RemoveAllHandlers (this);
853 dl->unref ();
854 dl = NULL;
857 if (mms != NULL) {
858 mms->unref ();
859 mms = NULL;
862 if (kill_timeout != 0) {
863 deployment = GetDeployment ();
864 surface = deployment ? deployment->GetSurface () : NULL;
865 tm = surface ? surface->GetTimeManager () : NULL;
866 if (tm != NULL) {
867 tm->RemoveTimeout (kill_timeout);
868 kill_timeout = 0;
869 unref ();
873 EventObject::Dispose ();
876 void
877 MmsSecondDownloader::DownloadFailedHandler (EventObject *sender, EventArgs *args)
879 LOG_MMS ("MmsLogger::DownloadFailedHandler ()\n");
880 VERIFY_MAIN_THREAD;
882 Dispose ();
885 void
886 MmsSecondDownloader::CompletedHandler (EventObject *sender, EventArgs *args)
888 LOG_MMS ("MmsLogger::CompletedHandler ()\n");
889 VERIFY_MAIN_THREAD;
891 Dispose ();
894 void
895 MmsSecondDownloader::data_write (void *data, gint32 offset, gint32 n, void *closure)
897 LOG_MMS ("((MmsLogger *) closure)->DataWrite (data = %p, offset = %i, n = %i);\n", data, offset, n);
900 void
901 MmsSecondDownloader::CreateDownloader ()
903 Deployment *deployment;
904 Surface *surface;
906 VERIFY_MAIN_THREAD;
908 deployment = GetDeployment ();
910 g_return_if_fail (dl == NULL);
911 g_return_if_fail (deployment != NULL);
913 surface = deployment->GetSurface ();
915 g_return_if_fail (surface != NULL);
917 dl = surface->CreateDownloader ();
919 dl->AddHandler (Downloader::DownloadFailedEvent, DownloadFailedCallback, this);
920 dl->AddHandler (Downloader::CompletedEvent, CompletedCallback, this);
921 dl->SetStreamFunctions (data_write, NULL, this);
923 dl->SetRequireCustomHeaderSupport (true);
924 // firefox will not download an uri equal to the mms uri simultaneously with the mms uri
925 // it tries to open a cache entry for writing, which fails since the cache entry is already in use
926 // sp we disable the cace
927 dl->SetDisableCache (true);
928 dl->Open ("POST", mms->GetUri (), NoPolicy);
931 void
932 MmsSecondDownloader::SetKillTimeout (guint seconds)
934 Deployment *deployment;
935 Surface *surface;
936 TimeManager *tm;
938 deployment = GetDeployment ();
939 surface = deployment ? deployment->GetSurface () : NULL;
940 tm = surface ? surface->GetTimeManager () : NULL;
942 g_return_if_fail (tm != NULL);
944 this->ref ();
945 tm->AddTimeout (MOON_PRIORITY_IDLE, seconds * 1000, KillTimeoutCallback, this);
948 gboolean
949 MmsSecondDownloader::KillTimeoutCallback (gpointer context)
951 ((MmsSecondDownloader *) context)->KillTimeoutHandler ();
952 return false;
955 void
956 MmsSecondDownloader::KillTimeoutHandler ()
958 LOG_MMS ("MmsSecondDownloader::KillTimeoutHandler (), dl: %p\n", dl);
959 kill_timeout = 0;
960 SetCurrentDeployment ();
961 unref ();
962 Deployment::SetCurrent (NULL);
965 void
966 MmsSecondDownloader::SendStreamSwitch ()
968 GString *pragma;
969 MmsPlaylistEntry *entry;
971 g_return_if_fail (mms != NULL);
973 CreateDownloader ();
975 g_return_if_fail (dl != NULL);
977 entry = mms->GetCurrentEntryReffed ();
979 pragma = g_string_new (NULL);
981 set_common_dl_headers (dl, mms, pragma);
982 set_stream_selection_headers (mms, pragma, entry);
984 g_string_append (pragma, "\r\n");
986 dl->InternalSetBody (pragma->str, pragma->len);
987 dl->Send ();
989 entry->unref ();
990 g_string_free (pragma, true);
992 LOG_MMS ("MmsSecondDownloader::SendStreamSwitch (): Sent.\n");
996 * Work in progress
998 void
999 MmsSecondDownloader::SendLog ()
1001 CreateDownloader ();
1003 g_return_if_fail (dl != NULL);
1006 // POST /SSPLDrtOnDemandTest HTTP/1.0
1007 // Host: moonlightmedia
1008 // Content-Length: 2203
1009 // User-Agent: NSPlayer/11.08.0005.0000
1010 // Accept: * / *
1011 // Accept-Language: en-us, *;q=0.1
1012 // Connection: Keep-Alive
1013 // Content-Type: application/x-wms-Logstats
1014 // Pragma: client-id=3375607867
1015 // Pragma: playlist-gen-id=2769
1016 // Pragma: xClientGuid={00000000-0000-0000-0000-000000000000}
1017 // Supported: com.microsoft.wm.srvppair, com.microsoft.wm.sswitch, com.microsoft.wm.startupprofile, com.microsoft.wm.predstrm
1020 dl->InternalSetHeader ("User-Agent", "NSPlayer/11.08.0005.0000");
1021 dl->InternalSetHeader ("Content-Type", "application/x-wms-Logstats");
1023 GString *header = g_string_new (NULL);
1025 set_common_dl_headers (dl, mms, header);
1027 GString *all = g_string_new (NULL);
1028 GString *xml = g_string_new (NULL);
1029 GString *summary = g_string_new (NULL);
1031 // "<c-ip>0.0.0.0</c-ip>"
1032 g_string_append (summary, "0.0.0.0 ");
1033 g_string_append (xml, "<c-ip>0.0.0.0</c-ip>");
1035 // "<date>%.4i-%.2i-%.2i</date>" // yyyy-MM-dd
1036 tm now;
1037 time_t time_now = time (NULL);
1038 gmtime_r (&time_now, &now);
1039 g_string_append_printf (summary, "%.4i-%.2i-%.2i ", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
1040 g_string_append_printf (xml, "<date>%.4i-%.2i-%.2i</date>", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
1042 // "<time>%.2i:%.2i:%.2i</time>" // HH:mm:ss
1043 g_string_append_printf (summary, "%.2i:%.2i:%.2i ", now.tm_hour, now.tm_min, now.tm_sec);
1044 g_string_append_printf (xml, "<time>%.2i:%.2i:%.2i</time>", now.tm_hour, now.tm_min, now.tm_sec);
1046 // "<c-dns>-</c-dns>"
1047 g_string_append_printf (summary, "- ");
1048 g_string_append_printf (xml, "<c-dns>-</c-dns>");
1050 // "<cs-uri-stem>-</cs-uri-stem>"
1051 g_string_append_printf (summary, "- ");
1052 g_string_append_printf (xml, "<cs-uri-stem>-</cs-uri-stem>");
1054 // "<c-starttime>0</c-starttime>"
1055 g_string_append_printf (summary, "0 ");
1056 g_string_append_printf (xml, "<c-starttime>0</c-starttime>");
1058 // "<x-duration>0</x-duration>"
1059 g_string_append_printf (summary, "0 ");
1060 g_string_append_printf (xml, "<x-duration>0</x-duration>");
1062 //"<c-rate>-</c-rate>"
1063 g_string_append_printf (summary, "- ");
1064 g_string_append_printf (xml, "<c-rate>-</c-rate>");
1066 //"<c-status>200</c-status>"
1067 g_string_append_printf (summary, "200 ");
1068 g_string_append_printf (xml, "<c-status>200</c-status>");
1070 // "<c-playerid>" CLIENT_GUID "</c-playerid>"
1071 g_string_append_printf (summary, "%s ", CLIENT_GUID);
1072 g_string_append_printf (xml, "<c-playerid>%s</c-playerid>", CLIENT_GUID);
1074 // "<c-playerversion>-</c-playerversion>"
1075 g_string_append_printf (summary, "- ");
1076 g_string_append_printf (xml, "<c-playerversion>-</c-playerversion>");
1078 // "<c-playerlanguage>-</c-playerlanguage>"
1079 g_string_append_printf (summary, "- ");
1080 g_string_append_printf (xml, "<c-playerlanguage>-</c-playerlanguage>");
1082 // "<cs-User-Agent>%s</cs-User-Agent>"
1083 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";
1084 g_string_append_printf (summary, "%s ", user_agent);
1085 g_string_append_printf (xml, "<cs-User-Agent>%s</cs-User-Agent>", user_agent);
1087 // "<cs-Referer>%s</cs-Referer>"
1088 const char *referrer = "http://192.168.1.4:8080/media/video/test-server-side-playlist.html";//"http://example.com/";
1089 g_string_append_printf (summary, "%s ", referrer);
1090 g_string_append_printf (xml, "<cs-Referer>%s</cs-Referer>", referrer);
1092 // "<c-hostexe>-</c-hostexe>"
1093 g_string_append_printf (summary, "- ");
1094 g_string_append_printf (xml, "<c-hostexe>-</c-hostexe>");
1096 // "<c-hostexever>-</c-hostexever>"
1097 g_string_append_printf (summary, "- ");
1098 g_string_append_printf (xml, "<c-hostexever>-</c-hostexever>");
1100 // "<c-os>Linux</c-os>"
1101 g_string_append_printf (summary, "Windows_XP ");
1102 g_string_append_printf (xml, "<c-os>Windows_XP</c-os>");
1104 // "<c-osversion>-</c-osversion>"
1105 g_string_append_printf (summary, "- ");
1106 g_string_append_printf (xml, "<c-osversion>-</c-osversion>");
1108 // "<c-cpu>-</c-cpu>"
1109 g_string_append_printf (summary, "- ");
1110 g_string_append_printf (xml, "<c-cpu>-</c-cpu>");
1112 // "<filelength>-</filelength>"
1113 g_string_append_printf (summary, "- ");
1114 g_string_append_printf (xml, "<filelength>-</filelength>");
1116 // "<filesize>-</filesize>"
1117 g_string_append_printf (summary, "- ");
1118 g_string_append_printf (xml, "<filesize>-</filesize>");
1120 // "<avgbandwidth>-</avgbandwidth>"
1121 g_string_append_printf (summary, "- ");
1122 g_string_append_printf (xml, "<avgbandwidth>-</avgbandwidth>");
1124 // "<protocol>http</protocol>"
1125 g_string_append_printf (summary, "http ");
1126 g_string_append_printf (xml, "<protocol>http</protocol>");
1128 // "<transport>TCP</transport>"
1129 g_string_append_printf (summary, "TCP ");
1130 g_string_append_printf (xml, "<transport>TCP</transport>");
1132 // "<audiocodec>-</audiocodec>"
1133 g_string_append_printf (summary, "- ");
1134 g_string_append_printf (xml, "<audiocodec>-</audiocodec>");
1136 // "<videocodec>-</videocodec>"
1137 g_string_append_printf (summary, "- ");
1138 g_string_append_printf (xml, "<videocodec>-</videocodec>");
1140 // "<c-channelURL>-</c-channelURL>"
1141 g_string_append_printf (summary, "- ");
1142 g_string_append_printf (xml, "<c-channelURL>-</c-channelURL>");
1144 // "<sc-bytes>-</sc-bytes>"
1145 g_string_append_printf (summary, "- ");
1146 g_string_append_printf (xml, "<sc-bytes>-</sc-bytes>");
1148 // "<c-bytes>-</c-bytes>"
1149 g_string_append_printf (summary, "- ");
1150 g_string_append_printf (xml, "<c-bytes>-</c-bytes>");
1152 // "<s-pkts-sent>-</s-pkts-sent>"
1153 g_string_append_printf (summary, "- ");
1154 g_string_append_printf (xml, "<s-pkts-sent>-</s-pkts-sent>");
1156 // "<c-pkts-received>-</c-pkts-received>"
1157 g_string_append_printf (summary, "- ");
1158 g_string_append_printf (xml, "<c-pkts-received>-</c-pkts-received>");
1160 // "<c-pkts-lost-client>-</c-pkts-lost-client>"
1161 g_string_append_printf (summary, "- ");
1162 g_string_append_printf (xml, "<c-pkts-lost-client>-</c-pkts-lost-client>");
1164 // "<c-pkts-lost-net>-</c-pkts-lost-net>"+
1165 g_string_append_printf (summary, "- ");
1166 g_string_append_printf (xml, "<c-pkts-lost-net>-</c-pkts-lost-net>");
1168 // "<c-pkts-lost-cont-net>-</c-pkts-lost-cont-net>"
1169 g_string_append_printf (summary, "- ");
1170 g_string_append_printf (xml, "<c-pkts-lost-cont-net>-</c-pkts-lost-cont-net>");
1172 // "<c-resendreqs>-</c-resendreqs>"
1173 g_string_append_printf (summary, "- ");
1174 g_string_append_printf (xml, "<c-resendreqs>-</c-resendreqs>");
1176 // "<c-pkts-recovered-ECC>-</c-pkts-recovered-ECC>"
1177 g_string_append_printf (summary, "- ");
1178 g_string_append_printf (xml, "<c-pkts-recovered-ECC>-</c-pkts-recovered-ECC>");
1180 // "<c-pkts-recovered-resent>-</c-pkts-recovered-resent>"
1181 g_string_append_printf (summary, "- ");
1182 g_string_append_printf (xml, "<c-pkts-recovered-resent>-</c-pkts-recovered-resent>");
1184 // "<c-buffercount>-</c-buffercount>"
1185 g_string_append_printf (summary, "- ");
1186 g_string_append_printf (xml, "<c-buffercount>-</c-buffercount>");
1188 // "<c-totalbuffertime>-</c-totalbuffertime>"
1189 g_string_append_printf (summary, "- ");
1190 g_string_append_printf (xml, "<c-totalbuffertime>-</c-totalbuffertime>");
1192 // "<c-quality>-</c-quality>"
1193 g_string_append_printf (summary, "- ");
1194 g_string_append_printf (xml, "<c-quality>-</c-quality>");
1196 // "<s-ip>-</s-ip><s-dns>-</s-dns>"
1197 g_string_append_printf (summary, "- ");
1198 g_string_append_printf (xml, "<s-ip>-</s-ip><s-dns>-</s-dns>");
1200 // "<s-totalclients>-</s-totalclients>"
1201 g_string_append_printf (summary, "- ");
1202 g_string_append_printf (xml, "<s-totalclients>-</s-totalclients>");
1204 // "<s-cpu-util>-</s-cpu-util>"
1205 g_string_append_printf (summary, "- ");
1206 g_string_append_printf (xml, "<s-cpu-util>-</s-cpu-util>");
1208 // "<cs-url>%s</cs-url>"
1209 g_string_append_printf (summary, "%s ", mms->GetUri ());
1210 g_string_append_printf (xml, "<cs-url>%s</cs-url>", mms->GetUri ());
1212 // "<cs-media-name>-</cs-media-name>"
1213 g_string_append_printf (summary, "- ");
1214 g_string_append_printf (xml, "<cs-media-name>-</cs-media-name>");
1216 // "<cs-media-role>-</cs-media-role>"
1217 g_string_append_printf (summary, "-"); // skip the last space
1218 g_string_append_printf (xml, "<cs-media-role>-</cs-media-role>");
1220 g_string_append_printf (header, "Content-Length: %i\r\n", summary->len + xml->len + 11 + 19); // length of <XML></XML> + <Summary/>
1222 g_string_append_printf (header, "\r\n"); // end of header
1224 g_string_append (all, header->str);
1225 g_string_append (all, "<XML><Summary>");
1226 g_string_append (all, summary->str);
1227 g_string_append (all, "</Summary>");
1228 g_string_append (all, xml->str);
1229 g_string_append (all, "</XML>");
1231 dl->InternalSetBody (all->str, all->len);
1233 g_string_free (all, true);
1234 g_string_free (xml, true);
1235 g_string_free (summary, true);
1236 g_string_free (header, true);
1238 dl->Send ();
1240 LOG_MMS ("MmsDownloader: sent log.\n");