2 * mms-downloader.cpp: MMS Downloader class.
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.
15 #include "mms-downloader.h"
17 #include "timemanager.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"
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
)
36 MmsDownloader::MmsDownloader (Downloader
*dl
) : InternalDownloader (dl
, Type::MMSDOWNLOADER
)
38 LOG_MMS ("MmsDownloader::MmsDownloader ()\n");
43 playlist_gen_id
= NULL
;
49 p_packet_times
[0] = 0;
50 p_packet_times
[1] = 0;
51 p_packet_times
[2] = 0;
55 stream_switched
= false;
58 content_descriptions
= NULL
;
61 failure_reported
= false;
63 dl
->SetRequireCustomHeaderSupport (true);
64 dl
->SetDisableCache (true);
67 MmsDownloader::~MmsDownloader ()
69 LOG_MMS ("MmsDownloader::~MmsDownloader ()\n");
74 g_free (playlist_gen_id
);
75 delete content_descriptions
;
82 MmsDownloader::SetSource (MmsSource
*source
)
87 this->source
->unref ();
88 this->source
= source
;
94 MmsDownloader::SetRequestedPts (guint64 value
)
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
);
107 MmsDownloader::GetRequestedPts ()
112 request_mutex
.Lock ();
113 result
= requested_pts
;
114 request_mutex
.Unlock ();
116 LOG_MMS ("MmsDownloader::GetRequestedPts (): %" G_GUINT64_FORMAT
"\n", result
);
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
);
141 set_stream_selection_headers (MmsDownloader
*mms
, GString
*pragma
, MmsPlaylistEntry
*entry
)
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
162 case 0: // not selected
164 g_string_append_printf (pragma
, "%i:ffff:0 ", i
);
168 g_string_append_printf (pragma
, "ffff:%i:0 ", i
);
171 printf ("MmsDownloader: invalid stream selection value (%i).\n", streams
[i
]);
175 g_string_append_printf (pragma
, "\r\n");
177 g_string_append_printf (pragma
, "Pragma: stream-switch-count=%i\r\n", count
);
181 MmsDownloader::Open (const char *verb
, const char *uri
)
183 LOG_MMS ("MmsDownloader::Open ('%s', '%s')\n", verb
, uri
);
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);
204 MmsDownloader::PlayCallback (EventObject
*sender
)
206 ((MmsDownloader
*) sender
)->Play ();
210 MmsDownloader::Play ()
213 MmsPlaylistEntry
*entry
;
216 request_mutex
.Lock ();
219 request_mutex
.Unlock ();
221 LOG_MMS ("MmsDownloader::Play () requested_pts: %" G_GUINT64_FORMAT
"\n", pts
);
223 g_return_if_fail (source
!= NULL
);
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
);
257 g_string_free (pragma
, true);
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
);
273 MmsDownloader::ProcessResponseHeader (const char *header
, const char *value
)
278 LOG_MMS ("MmsDownloader::ProcessResponseHeader ('%s', '%s')\n", header
, value
);
280 if (failure_reported
)
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 ();
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)
300 h
= g_strdup (value
);
303 while (h
!= NULL
&& *h
!= 0) {
309 key
= parse_rfc_1945_token (h
, &c
, &left
);
319 if (c
== '=' && h
!= NULL
) {
321 val
= parse_rfc_1945_quoted_string (h
+ 1, &c
, &left
);
323 } else if (*h
!= 0) {
324 val
= parse_rfc_1945_token (h
, &c
, &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
)
334 client_id
= g_strdup (val
);
342 MmsDownloader::Write (void *buf
, gint32 off
, gint32 n
)
344 LOG_MMS ("MmsDownloader::Write (%p, %i, %i)\n", buf
, off
, n
);
351 // Resize our internal buffer
352 if (buffer
== NULL
) {
353 buffer
= (char *) g_malloc (n
);
355 buffer
= (char *) g_realloc (buffer
, size
+ n
);
358 // Populate the data into the buffer
359 memcpy (buffer
+ size
, buf
, 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");
371 dl
->NotifyFailed ("invalid mms source");
375 if (size
< (header
->length
+ sizeof (MmsHeader
)))
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");
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
390 char *new_buffer
= (char *) g_malloc (size
- offset
);
391 memcpy (new_buffer
, buffer
+ offset
, size
- offset
);
405 MmsDownloader::GetDownloadedFilename (const char *partname
)
407 LOG_MMS ("MmsDownloader::GetDownloadedFilename ('%s')\n", partname
);
412 MmsDownloader::GetResponseText (const char *partname
, gint64
*size
)
414 LOG_MMS ("MmsDownloader::GetResponseText ('%s', %p)\n", partname
, size
);
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
) {
427 return ProcessHeaderPacket (header
, packet
, payload
, offset
);
429 return ProcessMetadataPacket (header
, packet
, payload
, offset
);
431 return ProcessPairPacket (header
, packet
, payload
, offset
);
433 return ProcessDataPacket (header
, packet
, payload
, offset
);
435 return ProcessEndPacket (header
, packet
, payload
, offset
);
437 return ProcessStreamSwitchPacket (header
, packet
, payload
, offset
);
440 printf ("MmsDownloader::ProcessPacket received a unknown packet type %i.", (int) header
->id
);
446 MmsDownloader::ProcessStreamSwitchPacket (MmsHeader
*header
, MmsPacket
*packet
, char *payload
, guint32
*offset
)
448 LOG_MMS ("MmsDownloader::ProcessStreamSwitchPacket ()\n");
451 MmsHeaderReason
*hr
= (MmsHeaderReason
*) header
;
453 g_return_val_if_fail (source
!= NULL
, false);
455 source
->ReportStreamChange (hr
->reason
);
456 stream_switched
= true;
462 MmsDownloader::ProcessEndPacket (MmsHeader
*header
, MmsPacket
*packet
, char *payload
, guint32
*offset
)
464 LOG_MMS ("MmsDownloader::ProcessEndPacket ()\n");
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
;
476 source
->NotifyFinished (hr
->reason
);
484 MmsDownloader::GetCurrentEntryReffed ()
488 g_return_val_if_fail (source
!= NULL
, NULL
);
490 return source
->GetCurrentReffed ();
494 MmsDownloader::ProcessHeaderPacket (MmsHeader
*header
, MmsPacket
*packet
, char *payload
, guint32
*offset
)
497 MmsPlaylistEntry
*entry
;
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");
512 } else if (!is_playing
) {
514 } else if (stream_switched
) {
515 MmsSecondDownloader
*sdl
= new MmsSecondDownloader (this);
516 sdl
->SendStreamSwitch ();
517 sdl
->SetKillTimeout (30 /* seconds */);
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??
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
;
544 g_return_val_if_fail (source
!= NULL
, false);
546 // format: key=value,key=value\0
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)
554 if (i
== packet
->packet
.data
.size
- 1)
558 // content description list
559 int payload_strlen
= strlen (payload
);
560 const char *cdl_start
= NULL
;
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
;
581 key
= strtok_r (start
, "=", &state
);
590 if (!strcmp (key
, "features")) {
591 value
= strtok_r (NULL
, "\"", &state
);
593 value
= strtok_r (NULL
, ",", &state
);
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
);
608 printf ("MmsDownloader::ProcessMetadataPacket (): Unexpected metadata: %s=%s\n", key
, value
);
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
);
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
))
638 // NOTE: We also need to account for the size of the reason field manually with our packet massaging.
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
;
656 if (p_packet_times
[0] == p_packet_times
[2]) {
657 max_bitrate
= 0; // prevent /0
659 max_bitrate
= (gint64
) (((p_packet_sizes
[1] + p_packet_sizes
[2]) * 8) / ((double) ((p_packet_times
[2] - p_packet_times
[0]) / (double) 10000000)));
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
));
678 * ContentDescriptionList
682 ContentDescriptionList::Parse (const char *input
, gint32 length
)
687 int str_length
= length
;
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
;
705 * <name length>,<name>,<value type>,<value length>,<value>
708 char *str_name_length
;
710 char *str_value_type
;
711 char *str_value_length
;
721 comma
= strchr (str
, ',');
725 *comma
= 0; // null terminate
726 str_name_length
= str
;
729 name_length
= strtoull (str_name_length
, NULL
, 10);
731 if (name_length
< 0 || name_length
> G_MAXINT32
)
734 if (end
- str
< name_length
+ 1)
739 str_name
[name_length
] = 0; // null terminate
740 str
+= name_length
+ 1;
743 comma
= strchr (str
, ',');
747 *comma
= 0; // null terminate
748 str_value_type
= str
;
751 value_type
= strtoull (str_value_type
, NULL
, 10);
753 if (value_type
< 0 || value_type
> G_MAXINT32
)
757 comma
= strchr (str
, ',');
761 *comma
= 0; // null terminate
762 str_value_length
= str
;
765 value_length
= strtoull (str_value_length
, NULL
, 10);
767 if (value_length
< 0 || value_length
> G_MAXINT32
)
770 if (end
- str
< value_length
)
773 value
= str
; // can't null terminate, we don't necessarily have a string
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
;
786 // printf ("parsed: %*s = %*s\n", (int) name_length, str_name, (int) cd->value_length, (char *) cd->value);
809 ContentDescription::~ContentDescription ()
816 * MmsSecondDownloader
819 MmsSecondDownloader::MmsSecondDownloader (MmsDownloader
*mms
)
830 MmsSecondDownloader::Dispose ()
832 Deployment
*deployment
;
839 dl
->RemoveAllHandlers (this);
849 if (kill_timeout
!= 0) {
850 deployment
= GetDeployment ();
851 surface
= deployment
? deployment
->GetSurface () : NULL
;
852 tm
= surface
? surface
->GetTimeManager () : NULL
;
854 tm
->RemoveTimeout (kill_timeout
);
860 EventObject::Dispose ();
864 MmsSecondDownloader::DownloadFailedHandler (EventObject
*sender
, EventArgs
*args
)
866 LOG_MMS ("MmsLogger::DownloadFailedHandler ()\n");
873 MmsSecondDownloader::CompletedHandler (EventObject
*sender
, EventArgs
*args
)
875 LOG_MMS ("MmsLogger::CompletedHandler ()\n");
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
);
888 MmsSecondDownloader::CreateDownloader ()
890 Deployment
*deployment
;
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
);
919 MmsSecondDownloader::SetKillTimeout (guint seconds
)
921 Deployment
*deployment
;
925 deployment
= GetDeployment ();
926 surface
= deployment
? deployment
->GetSurface () : NULL
;
927 tm
= surface
? surface
->GetTimeManager () : NULL
;
929 g_return_if_fail (tm
!= NULL
);
932 tm
->AddTimeout (MOON_PRIORITY_IDLE
, seconds
* 1000, KillTimeoutCallback
, this);
936 MmsSecondDownloader::KillTimeoutCallback (gpointer context
)
938 ((MmsSecondDownloader
*) context
)->KillTimeoutHandler ();
943 MmsSecondDownloader::KillTimeoutHandler ()
945 LOG_MMS ("MmsSecondDownloader::KillTimeoutHandler (), dl: %p\n", dl
);
947 SetCurrentDeployment ();
949 Deployment::SetCurrent (NULL
);
953 MmsSecondDownloader::SendStreamSwitch ()
956 MmsPlaylistEntry
*entry
;
958 g_return_if_fail (mms
!= NULL
);
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
);
977 g_string_free (pragma
, true);
979 LOG_MMS ("MmsSecondDownloader::SendStreamSwitch (): Sent.\n");
986 MmsSecondDownloader::SendLog ()
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
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
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);
1227 LOG_MMS ("MmsDownloader: sent log.\n");