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 AddTickCall (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
)
185 LOG_MMS ("MmsDownloader::Open ('%s', '%s')\n", verb
, uri
);
189 g_return_if_fail (this->uri
== NULL
);
190 g_return_if_fail (uri
!= NULL
);
192 if (strncmp (uri
, "mms://", 6) == 0) {
194 } else if (strncmp (uri
, "rtsp://", 7) == 0) {
196 } else if (strncmp (uri
, "rtsps://", 8) == 0) {
199 fprintf (stderr
, "Moonlight: streaming scheme must be either mms, rtsp or rtsps, got uri: %s\n", uri
);
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);
216 MmsDownloader::PlayCallback (EventObject
*sender
)
218 ((MmsDownloader
*) sender
)->Play ();
222 MmsDownloader::Play ()
225 MmsPlaylistEntry
*entry
;
228 request_mutex
.Lock ();
231 request_mutex
.Unlock ();
233 LOG_MMS ("MmsDownloader::Play () requested_pts: %" G_GUINT64_FORMAT
"\n", pts
);
235 g_return_if_fail (source
!= NULL
);
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
);
269 g_string_free (pragma
, true);
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
);
285 MmsDownloader::ProcessResponseHeader (const char *header
, const char *value
)
290 LOG_MMS ("MmsDownloader::ProcessResponseHeader ('%s', '%s')\n", header
, value
);
292 if (failure_reported
)
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;
301 source
->ReportDownloadFailure ();
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)
313 h
= g_strdup (value
);
316 while (h
!= NULL
&& *h
!= 0) {
322 key
= parse_rfc_1945_token (h
, &c
, &left
);
332 if (c
== '=' && h
!= NULL
) {
334 val
= parse_rfc_1945_quoted_string (h
+ 1, &c
, &left
);
336 } else if (*h
!= 0) {
337 val
= parse_rfc_1945_token (h
, &c
, &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
)
347 client_id
= g_strdup (val
);
355 MmsDownloader::Write (void *buf
, gint32 off
, gint32 n
)
357 LOG_MMS ("MmsDownloader::Write (%p, %i, %i)\n", buf
, off
, n
);
364 // Resize our internal buffer
365 if (buffer
== NULL
) {
366 buffer
= (char *) g_malloc (n
);
368 buffer
= (char *) g_realloc (buffer
, size
+ n
);
371 // Populate the data into the buffer
372 memcpy (buffer
+ size
, buf
, 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");
384 dl
->NotifyFailed ("invalid mms source");
388 if (size
< (header
->length
+ sizeof (MmsHeader
)))
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");
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
403 char *new_buffer
= (char *) g_malloc (size
- offset
);
404 memcpy (new_buffer
, buffer
+ offset
, size
- offset
);
418 MmsDownloader::GetDownloadedFilename (const char *partname
)
420 LOG_MMS ("MmsDownloader::GetDownloadedFilename ('%s')\n", partname
);
425 MmsDownloader::GetResponseText (const char *partname
, gint64
*size
)
427 LOG_MMS ("MmsDownloader::GetResponseText ('%s', %p)\n", partname
, size
);
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
) {
440 return ProcessHeaderPacket (header
, packet
, payload
, offset
);
442 return ProcessMetadataPacket (header
, packet
, payload
, offset
);
444 return ProcessPairPacket (header
, packet
, payload
, offset
);
446 return ProcessDataPacket (header
, packet
, payload
, offset
);
448 return ProcessEndPacket (header
, packet
, payload
, offset
);
450 return ProcessStreamSwitchPacket (header
, packet
, payload
, offset
);
453 printf ("MmsDownloader::ProcessPacket received a unknown packet type %i.", (int) header
->id
);
459 MmsDownloader::ProcessStreamSwitchPacket (MmsHeader
*header
, MmsPacket
*packet
, char *payload
, guint32
*offset
)
461 LOG_MMS ("MmsDownloader::ProcessStreamSwitchPacket ()\n");
464 MmsHeaderReason
*hr
= (MmsHeaderReason
*) header
;
466 g_return_val_if_fail (source
!= NULL
, false);
468 source
->ReportStreamChange (hr
->reason
);
469 stream_switched
= true;
475 MmsDownloader::ProcessEndPacket (MmsHeader
*header
, MmsPacket
*packet
, char *payload
, guint32
*offset
)
477 LOG_MMS ("MmsDownloader::ProcessEndPacket ()\n");
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
;
489 source
->NotifyFinished (hr
->reason
);
497 MmsDownloader::GetCurrentEntryReffed ()
501 g_return_val_if_fail (source
!= NULL
, NULL
);
503 return source
->GetCurrentReffed ();
507 MmsDownloader::ProcessHeaderPacket (MmsHeader
*header
, MmsPacket
*packet
, char *payload
, guint32
*offset
)
510 MmsPlaylistEntry
*entry
;
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");
525 } else if (!is_playing
) {
527 } else if (stream_switched
) {
528 MmsSecondDownloader
*sdl
= new MmsSecondDownloader (this);
529 sdl
->SendStreamSwitch ();
530 sdl
->SetKillTimeout (30 /* seconds */);
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??
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
;
557 g_return_val_if_fail (source
!= NULL
, false);
559 // format: key=value,key=value\0
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)
567 if (i
== packet
->packet
.data
.size
- 1)
571 // content description list
572 int payload_strlen
= strlen (payload
);
573 const char *cdl_start
= NULL
;
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
;
594 key
= strtok_r (start
, "=", &state
);
603 if (!strcmp (key
, "features")) {
604 value
= strtok_r (NULL
, "\"", &state
);
606 value
= strtok_r (NULL
, ",", &state
);
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
);
621 printf ("MmsDownloader::ProcessMetadataPacket (): Unexpected metadata: %s=%s\n", key
, value
);
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
);
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
))
651 // NOTE: We also need to account for the size of the reason field manually with our packet massaging.
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
;
669 if (p_packet_times
[0] == p_packet_times
[2]) {
670 max_bitrate
= 0; // prevent /0
672 max_bitrate
= (gint64
) (((p_packet_sizes
[1] + p_packet_sizes
[2]) * 8) / ((double) ((p_packet_times
[2] - p_packet_times
[0]) / (double) 10000000)));
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
));
691 * ContentDescriptionList
695 ContentDescriptionList::Parse (const char *input
, gint32 length
)
700 int str_length
= length
;
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
;
718 * <name length>,<name>,<value type>,<value length>,<value>
721 char *str_name_length
;
723 char *str_value_type
;
724 char *str_value_length
;
734 comma
= strchr (str
, ',');
738 *comma
= 0; // null terminate
739 str_name_length
= str
;
742 name_length
= strtoull (str_name_length
, NULL
, 10);
744 if (name_length
< 0 || name_length
> G_MAXINT32
)
747 if (end
- str
< name_length
+ 1)
752 str_name
[name_length
] = 0; // null terminate
753 str
+= name_length
+ 1;
756 comma
= strchr (str
, ',');
760 *comma
= 0; // null terminate
761 str_value_type
= str
;
764 value_type
= strtoull (str_value_type
, NULL
, 10);
766 if (value_type
< 0 || value_type
> G_MAXINT32
)
770 comma
= strchr (str
, ',');
774 *comma
= 0; // null terminate
775 str_value_length
= str
;
778 value_length
= strtoull (str_value_length
, NULL
, 10);
780 if (value_length
< 0 || value_length
> G_MAXINT32
)
783 if (end
- str
< value_length
)
786 value
= str
; // can't null terminate, we don't necessarily have a string
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
;
799 // printf ("parsed: %*s = %*s\n", (int) name_length, str_name, (int) cd->value_length, (char *) cd->value);
822 ContentDescription::~ContentDescription ()
829 * MmsSecondDownloader
832 MmsSecondDownloader::MmsSecondDownloader (MmsDownloader
*mms
)
843 MmsSecondDownloader::Dispose ()
845 Deployment
*deployment
;
852 dl
->RemoveAllHandlers (this);
862 if (kill_timeout
!= 0) {
863 deployment
= GetDeployment ();
864 surface
= deployment
? deployment
->GetSurface () : NULL
;
865 tm
= surface
? surface
->GetTimeManager () : NULL
;
867 tm
->RemoveTimeout (kill_timeout
);
873 EventObject::Dispose ();
877 MmsSecondDownloader::DownloadFailedHandler (EventObject
*sender
, EventArgs
*args
)
879 LOG_MMS ("MmsLogger::DownloadFailedHandler ()\n");
886 MmsSecondDownloader::CompletedHandler (EventObject
*sender
, EventArgs
*args
)
888 LOG_MMS ("MmsLogger::CompletedHandler ()\n");
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
);
901 MmsSecondDownloader::CreateDownloader ()
903 Deployment
*deployment
;
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
);
932 MmsSecondDownloader::SetKillTimeout (guint seconds
)
934 Deployment
*deployment
;
938 deployment
= GetDeployment ();
939 surface
= deployment
? deployment
->GetSurface () : NULL
;
940 tm
= surface
? surface
->GetTimeManager () : NULL
;
942 g_return_if_fail (tm
!= NULL
);
945 tm
->AddTimeout (MOON_PRIORITY_IDLE
, seconds
* 1000, KillTimeoutCallback
, this);
949 MmsSecondDownloader::KillTimeoutCallback (gpointer context
)
951 ((MmsSecondDownloader
*) context
)->KillTimeoutHandler ();
956 MmsSecondDownloader::KillTimeoutHandler ()
958 LOG_MMS ("MmsSecondDownloader::KillTimeoutHandler (), dl: %p\n", dl
);
960 SetCurrentDeployment ();
962 Deployment::SetCurrent (NULL
);
966 MmsSecondDownloader::SendStreamSwitch ()
969 MmsPlaylistEntry
*entry
;
971 g_return_if_fail (mms
!= NULL
);
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
);
990 g_string_free (pragma
, true);
992 LOG_MMS ("MmsSecondDownloader::SendStreamSwitch (): Sent.\n");
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
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
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);
1240 LOG_MMS ("MmsDownloader: sent log.\n");