1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * pipeline-asf.cpp: ASF related parts of the pipeline
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
17 #include "pipeline-asf.h"
18 #include "mms-downloader.h"
22 #define VIDEO_BITRATE_PERCENTAGE 75
23 #define AUDIO_BITRATE_PERCENTAGE 25
29 ASFDemuxer::ASFDemuxer (Media
*media
, IMediaSource
*source
) : IMediaDemuxer (Type::ASFDEMUXER
, media
, source
)
31 stream_to_asf_index
= NULL
;
37 ASFDemuxer::Dispose ()
39 g_free (stream_to_asf_index
);
40 stream_to_asf_index
= NULL
;
51 IMediaDemuxer::Dispose ();
55 ASFDemuxer::UpdateSelected (IMediaStream
*stream
)
58 reader
->SelectStream (stream_to_asf_index
[stream
->index
], stream
->GetSelected ());
60 IMediaDemuxer::UpdateSelected (stream
);
64 ASFDemuxer::SeekAsyncInternal (guint64 pts
)
68 LOG_PIPELINE ("ASFDemuxer::Seek (%" G_GUINT64_FORMAT
")\n", pts
);
70 g_return_if_fail (reader
!= NULL
);
71 g_return_if_fail (InMediaThread ());
73 result
= reader
->Seek (pts
);
75 if (MEDIA_SUCCEEDED (result
)) {
76 ReportSeekCompleted (pts
);
77 } else if (result
== MEDIA_NOT_ENOUGH_DATA
) {
80 ReportErrorOccurred (result
);
85 ASFDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*stream
)
87 LOG_PIPELINE ("ASFDemuxer::SwitchMediaStreamAsyncInternal (%p). TODO.\n", stream
);
91 ASFDemuxer::ReadMarkers ()
94 We can get markers from several places:
95 - The header of the file, read before starting to play
98 They are both treated the same way, added into the timeline marker collection when the media is loaded.
99 - As data in the file (a separate stream whose type is ASF_COMMAND_MEDIA)
100 These markers show up while playing the file, and they don't show up in the timeline marker collection,
101 they only get to raise the MarkerReached event.
102 currently the demuxer will call the streamed_marker_callback when it encounters any of these.
105 // Hookup to the marker (ASF_COMMAND_MEDIA) stream
107 Media
*media
= GetMediaReffed ();
109 g_return_if_fail (media
!= NULL
);
111 // Read the markers (if any)
112 List
*markers
= media
->GetMarkers ();
115 guint64 preroll_pts
= MilliSeconds_ToPts (parser
->GetFileProperties ()->preroll
);
119 // Read the SCRIPT COMMANDs
120 char **command_types
= NULL
;
121 asf_script_command_entry
**commands
= NULL
;
122 asf_script_command
*command
= parser
->script_command
;
124 if (command
!= NULL
) {
125 commands
= command
->get_commands (parser
, &command_types
);
127 if (command_types
== NULL
) {
128 //printf ("MediaElement::ReadASFMarkers (): No command types.\n");
133 if (commands
!= NULL
) {
134 for (i
= 0; commands
[i
]; i
++) {
135 asf_script_command_entry
*entry
= commands
[i
];
137 text
= entry
->get_name ();
138 pts
= MilliSeconds_ToPts (entry
->pts
) - preroll_pts
;
140 if (entry
->type_index
+ 1 <= command
->command_type_count
)
141 type
= command_types
[entry
->type_index
];
145 marker
= new MediaMarker (type
, text
, pts
);
146 markers
->Append (new MediaMarker::Node (marker
));
149 //printf ("MediaElement::ReadMarkers () Added script command at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, type);
156 asf_marker
*asf_marker
;
157 const asf_marker_entry
* marker_entry
;
159 asf_marker
= parser
->marker
;
160 if (asf_marker
!= NULL
) {
161 for (i
= 0; i
< (int) asf_marker
->marker_count
; i
++) {
162 marker_entry
= asf_marker
->get_entry (i
);
163 text
= marker_entry
->get_marker_description ();
165 pts
= marker_entry
->pts
- preroll_pts
;
167 marker
= new MediaMarker ("Name", text
, pts
);
168 markers
->Append (new MediaMarker::Node (marker
));
171 //printf ("MediaElement::ReadMarkers () Added marker at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, "Name");
179 g_strfreev (command_types
);
185 ASFDemuxer::SetParser (ASFParser
*parser
)
188 this->parser
->unref ();
189 this->parser
= parser
;
191 this->parser
->ref ();
192 this->parser
->SetSource (source
);
197 ASFDemuxer::OpenDemuxerAsyncInternal ()
201 LOG_PIPELINE ("ASFDemuxer::OpenDemuxerAsyncInternal ()\n");
205 if (MEDIA_SUCCEEDED (result
)) {
206 ReportOpenDemuxerCompleted ();
207 } else if (result
== MEDIA_NOT_ENOUGH_DATA
) {
210 ReportErrorOccurred (result
);
217 MediaResult result
= MEDIA_SUCCESS
;
218 ASFParser
*asf_parser
= NULL
;
219 gint32
*stream_to_asf_index
= NULL
;
220 IMediaStream
**streams
= NULL
;
221 Media
*media
= GetMediaReffed ();
222 int current_stream
= 1;
223 int stream_count
= 0;
226 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
228 if (parser
!= NULL
) {
231 asf_parser
= new ASFParser (source
, media
);
234 LOG_PIPELINE_ASF ("ASFDemuxer::ReadHeader ().\n");
236 result
= asf_parser
->ReadHeader ();
237 if (!MEDIA_SUCCEEDED (result
)) {
238 if (result
== MEDIA_NOT_ENOUGH_DATA
) {
239 LOG_PIPELINE_ASF ("ASFDemuxer::ReadHeader (): ReadHeader failed due to not enough data being available.\n");
241 Media::Warning (MEDIA_INVALID_MEDIA
, "asf_parser->ReadHeader () failed:");
242 Media::Warning (MEDIA_FAIL
, "%s", asf_parser
->GetLastErrorStr ());
247 // Count the number of streams
248 for (int i
= 1; i
<= 127; i
++) {
249 if (asf_parser
->IsValidStream (i
))
254 streams
= (IMediaStream
**) g_malloc0 (sizeof (IMediaStream
*) * (stream_count
+ 1)); // End with a NULL element.
255 stream_to_asf_index
= (gint32
*) g_malloc0 (sizeof (gint32
) * (stream_count
+ 1));
257 // keep count as a separate local since we can change its value (e.g. bad stream)
258 count
= stream_count
;
259 // Loop through all the streams and set stream-specific data
260 for (int i
= 0; i
< count
; i
++) {
261 while (current_stream
<= 127 && !asf_parser
->IsValidStream (current_stream
))
264 if (current_stream
> 127) {
265 result
= MEDIA_INVALID_STREAM
;
266 Media::Warning (result
, "Couldn't find all the claimed streams in the file.");
270 const asf_stream_properties
* stream_properties
= asf_parser
->GetStream (current_stream
);
271 IMediaStream
* stream
= NULL
;
273 if (stream_properties
== NULL
) {
274 result
= MEDIA_INVALID_STREAM
;
275 Media::Warning (result
, "Couldn't find all the claimed streams in the file.");
279 if (stream_properties
->is_audio ()) {
280 AudioStream
* audio
= new AudioStream (media
);
284 const WAVEFORMATEX
* wave
= stream_properties
->get_audio_data ();
286 result
= MEDIA_INVALID_STREAM
;
287 Media::Warning (result
, "Couldn't find audio data in the file.");
291 const WAVEFORMATEXTENSIBLE
* wave_ex
= wave
->get_wave_format_extensible ();
292 int data_size
= stream_properties
->size
- sizeof (asf_stream_properties
) - sizeof (WAVEFORMATEX
);
294 audio
->SetChannels (wave
->channels
);
295 audio
->SetSampleRate (wave
->samples_per_second
);
296 audio
->SetBitRate (wave
->bytes_per_second
* 8);
297 audio
->SetBlockAlign (wave
->block_alignment
);
298 audio
->SetBitsPerSample (wave
->bits_per_sample
);
299 audio
->SetExtraData (NULL
);
300 audio
->SetExtraDataSize (data_size
> wave
->codec_specific_data_size
? wave
->codec_specific_data_size
: data_size
);
301 audio
->SetCodecId (wave
->codec_id
);
303 if (wave_ex
!= NULL
) {
304 audio
->SetBitsPerSample (wave_ex
->Samples
.valid_bits_per_sample
);
305 audio
->SetExtraDataSize (audio
->GetExtraDataSize () - (sizeof (WAVEFORMATEXTENSIBLE
) - sizeof (WAVEFORMATEX
)));
306 audio
->SetCodecId (*((guint32
*) &wave_ex
->sub_format
));
309 // Fill in any extra codec data
310 if (audio
->GetExtraDataSize () > 0) {
311 audio
->SetExtraData (g_malloc0 (audio
->GetExtraDataSize ()));
312 char* src
= ((char*) wave
) + (wave_ex
? sizeof (WAVEFORMATEX
) : sizeof (WAVEFORMATEX
));
313 memcpy (audio
->GetExtraData (), src
, audio
->GetExtraDataSize ());
315 } else if (stream_properties
->is_video ()) {
316 VideoStream
* video
= new VideoStream (media
);
319 const asf_video_stream_data
* video_data
= stream_properties
->get_video_data ();
320 const BITMAPINFOHEADER
* bmp
;
321 const asf_extended_stream_properties
* aesp
;
323 if (video_data
!= NULL
) {
324 bmp
= video_data
->get_bitmap_info_header ();
325 aesp
= asf_parser
->GetExtendedStream (current_stream
);
327 video
->width
= bmp
->image_width
;
328 video
->height
= bmp
->image_height
;
330 // note: both height and width are unsigned
331 if ((video
->height
> MAX_VIDEO_HEIGHT
) || (video
->width
> MAX_VIDEO_WIDTH
)) {
332 result
= MEDIA_INVALID_STREAM
;
333 Media::Warning (result
,
334 "Video stream size (width: %d, height: %d) outside limits (%d, %d)",
335 video
->height
, video
->width
, MAX_VIDEO_HEIGHT
, MAX_VIDEO_WIDTH
);
339 video
->bits_per_sample
= bmp
->bits_per_pixel
;
340 video
->codec_id
= bmp
->compression_id
;
341 video
->extra_data_size
= bmp
->get_extra_data_size ();
342 if (video
->extra_data_size
> 0) {
343 video
->extra_data
= g_malloc0 (video
->extra_data_size
);
344 memcpy (video
->extra_data
, bmp
->get_extra_data (), video
->extra_data_size
);
346 video
->extra_data
= NULL
;
350 video
->bit_rate
= aesp
->data_bitrate
;
351 video
->pts_per_frame
= aesp
->average_time_per_frame
;
353 video
->bit_rate
= video
->width
*video
->height
;
354 video
->pts_per_frame
= 0;
357 } else if (stream_properties
->is_command ()) {
358 MarkerStream
* marker
= new MarkerStream (media
);
360 stream
->codec
= g_strdup ("asf-marker");
362 // Unknown stream, don't include it in the count since it's NULL
364 // also adjust indexes so we don't create a hole in the streams array
369 if (stream
!= NULL
) {
370 if (stream_properties
->is_video () || stream_properties
->is_audio ()) {
371 switch (stream
->codec_id
) {
372 case CODEC_WMV1
: stream
->codec
= g_strdup ("wmv1"); break;
373 case CODEC_WMV2
: stream
->codec
= g_strdup ("wmv2"); break;
374 case CODEC_WMV3
: stream
->codec
= g_strdup ("wmv3"); break;
375 case CODEC_WMVA
: stream
->codec
= g_strdup ("wmva"); break;
376 case CODEC_WVC1
: stream
->codec
= g_strdup ("vc1"); break;
377 case CODEC_MP3
: stream
->codec
= g_strdup ("mp3"); break;
378 case CODEC_WMAV1
: stream
->codec
= g_strdup ("wmav1"); break;
379 case CODEC_WMAV2
: stream
->codec
= g_strdup ("wmav2"); break;
380 case CODEC_WMAV3
: stream
->codec
= g_strdup ("wmav3"); break;
382 char a
= ((stream
->codec_id
& 0x000000FF));
383 char b
= ((stream
->codec_id
& 0x0000FF00) >> 8);
384 char c
= ((stream
->codec_id
& 0x00FF0000) >> 16);
385 char d
= ((stream
->codec_id
& 0xFF000000) >> 24);
386 stream
->codec
= g_strdup_printf ("unknown (%c%c%c%c)", a
? a
: ' ', b
? b
: ' ', c
? c
: ' ', d
? d
: ' ');
390 streams
[i
] = stream
;
392 if (!asf_parser
->file_properties
->is_broadcast ()) {
393 stream
->duration
= asf_parser
->file_properties
->play_duration
- MilliSeconds_ToPts (asf_parser
->file_properties
->preroll
);
395 stream_to_asf_index
[i
] = current_stream
;
402 if (!MEDIA_SUCCEEDED (result
)) {
406 SetStreams (streams
, stream_count
);
408 for (int i
= 0; i
< stream_count
; i
++)
409 streams
[i
]->unref ();
411 this->stream_to_asf_index
= stream_to_asf_index
;
412 this->parser
= asf_parser
;
414 reader
= new ASFReader (parser
, this);
423 asf_parser
->unref ();
426 g_free (stream_to_asf_index
);
427 stream_to_asf_index
= NULL
;
429 if (streams
!= NULL
) {
430 for (int i
= 0; i
< stream_count
; i
++) {
431 if (streams
[i
] != NULL
) {
432 streams
[i
]->unref ();
446 ASFDemuxer::GetStreamOfASFIndex (gint32 asf_index
)
448 for (gint32 i
= 0; i
< GetStreamCount (); i
++) {
449 if (stream_to_asf_index
[i
] == asf_index
)
450 return GetStream (i
);
456 ASFDemuxer::GetFrameCallback (MediaClosure
*c
)
458 MediaGetFrameClosure
*closure
= (MediaGetFrameClosure
*) c
;
459 ASFDemuxer
*demuxer
= (ASFDemuxer
*) closure
->GetDemuxer ();
460 demuxer
->GetFrameAsyncInternal (closure
->GetStream ());
462 return MEDIA_SUCCESS
;
466 ASFDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
468 //printf ("ASFDemuxer::ReadFrame (%p).\n", frame);
469 ASFFrameReader
*reader
= this->reader
->GetFrameReader (stream_to_asf_index
[stream
->index
]);
473 g_return_if_fail (reader
!= NULL
);
475 result
= reader
->Advance ();
477 if (result
== MEDIA_NO_MORE_DATA
) {
478 ReportGetFrameCompleted (NULL
);
482 if (result
== MEDIA_BUFFER_UNDERFLOW
|| result
== MEDIA_NOT_ENOUGH_DATA
) {
483 Media
*media
= GetMediaReffed ();
484 g_return_if_fail (media
!= NULL
);
485 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
486 media
->EnqueueWork (closure
, false); // TODO: use a timeout here, no need to try again immediately.
492 if (!MEDIA_SUCCEEDED (result
)) {
493 ReportErrorOccurred ("Error while advancing to the next frame (%d)");
497 frame
= new MediaFrame (stream
);
498 frame
->pts
= reader
->Pts ();
499 //frame->duration = reader->Duration ();
500 if (reader
->IsKeyFrame ())
501 frame
->AddState (MediaFrameKeyFrame
);
502 frame
->buflen
= reader
->Size ();
503 frame
->buffer
= (guint8
*) g_try_malloc (frame
->buflen
+ frame
->stream
->min_padding
);
505 if (frame
->buffer
== NULL
) {
506 ReportErrorOccurred ( "Could not allocate memory for next frame.");
510 //printf ("ASFDemuxer::ReadFrame (%p), min_padding = %i\n", frame, frame->stream->min_padding);
511 if (frame
->stream
->min_padding
> 0)
512 memset (frame
->buffer
+ frame
->buflen
, 0, frame
->stream
->min_padding
);
514 if (!reader
->Write (frame
->buffer
)) {
515 ReportErrorOccurred ("Error while copying the next frame.");
519 frame
->AddState (MediaFrameDemuxed
);
521 ReportGetFrameCompleted (frame
);
530 ASFMarkerDecoder::ASFMarkerDecoder (Media
*media
, IMediaStream
*stream
)
531 : IMediaDecoder (Type::ASFMARKERDECODER
, media
, stream
)
536 ASFMarkerDecoder::OpenDecoderAsyncInternal ()
538 ReportOpenDecoderCompleted ();
542 ASFMarkerDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
544 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame ()\n");
550 gunichar2
*uni_type
= NULL
;
551 gunichar2
*uni_text
= NULL
;
556 if (frame
->buflen
% 2 != 0 || frame
->buflen
== 0 || frame
->buffer
== NULL
) {
557 ReportErrorOccurred (MEDIA_CORRUPTED_MEDIA
);
561 data
= (gunichar2
*) frame
->buffer
;
563 size
= frame
->buflen
;
565 // the data is two arrays of WCHARs (type and text), null terminated.
566 // loop through the data, counting characters and null characters
567 // there should be at least two null characters.
570 for (guint32 i
= 0; i
< (size
/ sizeof (gunichar2
)); i
++) {
571 if (uni_text
== NULL
) {
576 if (*(data
+ i
) == 0) {
578 if (uni_text
== NULL
) {
579 uni_text
= data
+ i
+ 1;
581 break; // Found at least two nulls
586 if (null_count
>= 2) {
587 text
= wchar_to_utf8 (uni_text
, text_length
);
588 type
= wchar_to_utf8 (uni_type
, type_length
);
590 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): sending script command type: '%s', text: '%s', pts: '%" G_GUINT64_FORMAT
"'.\n", type
, text
, frame
->pts
);
592 frame
->marker
= new MediaMarker (type
, text
, frame
->pts
);
596 result
= MEDIA_SUCCESS
;
598 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): didn't find 2 null characters in the data.\n");
599 result
= MEDIA_CORRUPTED_MEDIA
;
602 if (MEDIA_SUCCEEDED (result
)) {
603 ReportDecodeFrameCompleted (frame
);
605 ReportErrorOccurred (result
);
610 * ASFMarkerDecoderInfo
614 ASFMarkerDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
616 return new ASFMarkerDecoder (media
, stream
);
620 ASFMarkerDecoderInfo::Supports (const char *codec
)
622 return !strcmp (codec
, "asf-marker");
626 ASFMarkerDecoderInfo::GetName ()
628 return "ASFMarkerDecoder";
636 ASFDemuxerInfo::Supports (IMediaSource
*source
)
641 LOG_PIPELINE_ASF ("ASFDemuxerInfo::Supports (%p) pos: %lld, avail pos: %lld\n", source
, source
->GetPosition (), source
->GetLastAvailablePosition ());
645 if (!source
->GetPosition () == 0)
646 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Trying to check if a media is supported, but the media isn't at position 0 (it's at position %lld)\n", source
, source
->GetPosition ());
647 if (!source
->IsPositionAvailable (16, &eof
)) // This shouldn't happen, we should have at least 1024 bytes (or eof).
648 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Not enough data! eof: %i\n", source
, eof
);
651 if (!source
->Peek (buffer
, 16)) {
652 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Peek failed.\n", source
);
656 result
= asf_guid_compare (&asf_guids_header
, (asf_guid
*) buffer
);
658 //printf ("ASFDemuxerInfo::Supports (%p): probing result: %s %s\n", source, source->ToString (),
659 // result ? "true" : "false");
661 return result
? MEDIA_SUCCESS
: MEDIA_FAIL
;
665 ASFDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
667 return new ASFDemuxer (media
, source
);
674 MmsSource::MmsSource (Media
*media
, Downloader
*downloader
)
675 : IMediaSource (Type::MMSSOURCE
, media
)
679 this->downloader
= NULL
;
683 g_return_if_fail (downloader
!= NULL
);
684 g_return_if_fail (downloader
->GetInternalDownloader () != NULL
);
685 g_return_if_fail (downloader
->GetInternalDownloader ()->GetObjectType () == Type::MMSDOWNLOADER
);
687 this->downloader
= downloader
;
688 this->downloader
->ref ();
690 ReportStreamChange (0); // create the initial MmsPlaylistEntry
694 MmsSource::Dispose ()
696 // thread safe method
698 MmsPlaylistEntry
*entry
;
699 IMediaDemuxer
*demux
;
702 // don't lock during unref, only while nulling out the local field
704 entry
= this->current
;
705 this->current
= NULL
;
706 dl
= this->downloader
;
707 this->downloader
= NULL
;
708 demux
= this->demuxer
;
709 this->demuxer
= NULL
;
713 dl
->RemoveAllHandlers (this);
723 IMediaSource::Dispose ();
727 MmsSource::Initialize ()
730 MmsDownloader
*mms_dl
;
734 dl
= GetDownloaderReffed ();
736 g_return_val_if_fail (dl
!= NULL
, MEDIA_FAIL
);
737 g_return_val_if_fail (!dl
->Started (), MEDIA_FAIL
);
739 // We must call MmsDownloader::SetSource before the downloader
740 // has actually received any data. Here we rely on the fact that
741 // firefox needs a tick before returning any data.
742 mms_dl
= GetMmsDownloader (dl
);
743 if (mms_dl
!= NULL
) {
744 mms_dl
->SetSource (this);
746 printf ("MmsSource::Initialize (): Could not get the MmsDownloader. Media won't play.\n");
749 dl
->AddHandler (Downloader::DownloadFailedEvent
, DownloadFailedCallback
, this);
750 dl
->AddHandler (Downloader::CompletedEvent
, DownloadCompleteCallback
, this);
755 return MEDIA_SUCCESS
;
759 MmsSource::GetDemuxerReffed ()
771 MmsSource::GetDownloaderReffed ()
783 MmsSource::GetCurrentReffed ()
785 MmsPlaylistEntry
*result
;
799 MmsSource::ReportDownloadFailure ()
803 LOG_MMS ("MmsSource::ReportDownloadFailure ()\n");
806 media
= GetMediaReffed ();
808 g_return_if_fail (media
!= NULL
);
810 media
->ReportErrorOccurred ("MmsDownloader failed");
815 MmsSource::ReportStreamChange (gint32 reason
)
821 LOG_MMS ("MmsSource::ReportStreamChange (reason: %i)\n", reason
);
825 media
= GetMediaReffed ();
827 g_return_if_fail (media
!= NULL
);
829 root
= media
->GetPlaylistRoot ();
831 g_return_if_fail (root
!= NULL
);
834 if (current
!= NULL
) {
835 current
->NotifyFinished ();
839 entry_media
= new Media (root
);
840 current
= new MmsPlaylistEntry (entry_media
, this);
841 entry_media
->unref ();
846 MmsSource::SetMmsMetadata (const char *playlist_gen_id
, const char *broadcast_id
, HttpStreamingFeatures features
)
848 MmsPlaylistEntry
*entry
;
850 LOG_MMS ("MmsSource::SetMmsMetadata ('%s', '%s', %i)\n", playlist_gen_id
, broadcast_id
, (int) features
);
854 entry
= GetCurrentReffed ();
856 g_return_if_fail (entry
!= NULL
);
858 entry
->SetPlaylistGenId (playlist_gen_id
);
859 entry
->SetBroadcastId (broadcast_id
);
860 entry
->SetHttpStreamingFeatures (features
);
866 MmsSource::DownloadFailedHandler (Downloader
*dl
, EventArgs
*args
)
868 Media
*media
= GetMediaReffed ();
872 g_return_if_fail (media
!= NULL
);
874 media
->RetryHttp (new ErrorEventArgs (MediaError
, 4001, "AG_E_NETWORK_ERROR"));
879 MmsSource::DownloadCompleteHandler (Downloader
*dl
, EventArgs
*args
)
885 MmsSource::NotifyFinished (guint32 reason
)
889 LOG_MMS ("MmsSource::NotifyFinished (%i)\n", reason
);
892 // The server has finished streaming and no more
893 // Data packets will be transmitted until the next Play request
895 } else if (reason
== 1) {
896 // The server has finished streaming the current playlist entry. Other playlist
897 // entries still remain to be streamed. The server will transmit a stream change packet
898 // when it switches to the next entry.
899 MmsPlaylistEntry
*entry
= GetCurrentReffed ();
900 entry
->NotifyFinished ();
908 MmsSource::SeekToPts (guint64 pts
)
911 MediaResult result
= true;
913 MmsDownloader
*mms_dl
;
915 LOG_PIPELINE_ASF ("MmsSource::SeekToPts (%" G_GUINT64_FORMAT
")\n", pts
);
917 dl
= GetDownloaderReffed ();
919 g_return_val_if_fail (dl
!= NULL
, MEDIA_FAIL
);
921 mms_dl
= GetMmsDownloader (dl
);
924 mms_dl
->SetRequestedPts (pts
);
926 result
= MEDIA_SUCCESS
;
937 MmsSource::GetMmsDownloader (Downloader
*dl
)
939 InternalDownloader
*idl
;
941 g_return_val_if_fail (dl
!= NULL
, NULL
);
943 idl
= dl
->GetInternalDownloader ();
948 if (idl
->GetObjectType () != Type::MMSDOWNLOADER
)
951 return (MmsDownloader
*) idl
;
955 MmsSource::CreateDemuxer (Media
*media
)
960 g_return_val_if_fail (demuxer
== NULL
, NULL
);
963 if (demuxer
== NULL
) {
964 result
= new MmsDemuxer (media
, this);
974 MmsSource::WritePacket (void *buf
, gint32 n
)
976 MmsPlaylistEntry
*entry
;
980 entry
= GetCurrentReffed ();
982 g_return_if_fail (entry
!= NULL
);
984 entry
->WritePacket (buf
, n
);
991 MmsPlaylistEntry
*entry
;
994 entry
= GetCurrentReffed ();
996 g_return_val_if_fail (entry
!= NULL
, NULL
);
998 result
= entry
->Pop ();
1009 MmsPlaylistEntry
*entry
;
1015 entry
= GetCurrentReffed ();
1017 if (entry
== NULL
) {
1020 result
= entry
->Eof ();
1031 MmsPlaylistEntry::MmsPlaylistEntry (Media
*media
, MmsSource
*source
)
1032 : IMediaSource (Type::MMSPLAYLISTENTRY
, media
)
1039 playlist_gen_id
= NULL
;
1040 broadcast_id
= NULL
;
1041 features
= HttpStreamingFeaturesNone
;
1043 g_return_if_fail (parent
!= NULL
);
1048 MmsPlaylistEntry::Initialize ()
1050 return MEDIA_SUCCESS
;
1054 MmsPlaylistEntry::Dispose ()
1057 MmsSource
*mms_source
;
1058 ASFParser
*asf_parser
;
1059 IMediaDemuxer
*demux
;
1062 mms_source
= this->parent
;
1063 this->parent
= NULL
;
1064 asf_parser
= this->parser
;
1065 this->parser
= NULL
;
1066 demux
= this->demuxer
;
1067 this->demuxer
= NULL
;
1068 g_free (playlist_gen_id
);
1069 playlist_gen_id
= NULL
;
1070 g_free (broadcast_id
);
1071 broadcast_id
= NULL
;
1074 if (mms_source
!= NULL
)
1075 mms_source
->unref ();
1077 if (asf_parser
!= NULL
)
1078 asf_parser
->unref ();
1083 IMediaSource::Dispose ();
1087 MmsPlaylistEntry::SeekToPts (guint64 pts
)
1089 MmsSource
*ms
= GetParentReffed ();
1091 ms
->SeekToPts (pts
);
1093 return MEDIA_SUCCESS
;
1095 fprintf (stderr
, "MmsPlaylistEntry::SeekToPts (%llu): Could not seek to pts, no parent.\n", pts
);
1101 MmsPlaylistEntry::NotifyFinished ()
1107 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate
, gint8 streams
[128])
1110 asf_file_properties
*properties
;
1111 gint32 audio_bitrates
[128];
1112 gint32 video_bitrates
[128];
1114 memset (audio_bitrates
, 0xff, 128 * 4);
1115 memset (video_bitrates
, 0xff, 128 * 4);
1116 memset (streams
, 0xff, 128);
1118 parser
= GetParserReffed ();
1120 g_return_if_fail (parser
!= NULL
);
1122 properties
= parser
->GetFileProperties ();
1124 g_return_if_fail (properties
!= NULL
);
1126 for (int i
= 1; i
< 127; i
++) {
1128 if (!parser
->IsValidStream (i
)) {
1129 streams
[i
] = -1; // inexistent
1132 streams
[i
] = 0; // disabled
1135 const asf_stream_properties
*stream_properties
= parser
->GetStream (current_stream
);
1136 const asf_extended_stream_properties
*extended_stream_properties
= parser
->GetExtendedStream (current_stream
);
1138 if (stream_properties
== NULL
) {
1139 printf ("MmsPlaylistEntry::GetSelectedStreams (): stream #%i doesn't have any stream properties.\n", current_stream
);
1143 if (stream_properties
->is_audio ()) {
1144 const WAVEFORMATEX
* wave
= stream_properties
->get_audio_data ();
1145 audio_bitrates
[current_stream
] = wave
->bytes_per_second
* 8;
1146 } else if (stream_properties
->is_video ()) {
1148 const asf_video_stream_data
* video_data
= stream_properties
->get_video_data ();
1149 const BITMAPINFOHEADER
* bmp
;
1151 if (extended_stream_properties
!= NULL
) {
1152 bit_rate
= extended_stream_properties
->data_bitrate
;
1153 } else if (video_data
!= NULL
) {
1154 bmp
= video_data
->get_bitmap_info_header ();
1156 bit_rate
= bmp
->image_width
*bmp
->image_height
;
1160 video_bitrates
[current_stream
] = bit_rate
;
1161 } else if (stream_properties
->is_command ()) {
1162 // we select all marker streams
1163 streams
[current_stream
] = 1;
1167 // select the video stream
1168 int video_stream
= 0;
1171 for (int i
= 0; i
< 128; i
++) {
1172 int stream_rate
= video_bitrates
[i
];
1174 if (stream_rate
== -1)
1177 if (video_rate
== 0) {
1178 video_rate
= stream_rate
;
1182 if (stream_rate
> video_rate
&& stream_rate
< (max_bitrate
* VIDEO_BITRATE_PERCENTAGE
)) {
1183 video_rate
= stream_rate
;
1187 streams
[video_stream
] = 1; // selected
1188 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected video stream %i of rate %i\n", video_stream
, video_rate
);
1191 // select audio stream
1192 int audio_stream
= 0;
1195 for (int i
= 0; i
< 128; i
++) {
1196 int stream_rate
= audio_bitrates
[i
];
1198 if (stream_rate
== -1)
1201 if (audio_rate
== 0) {
1202 audio_rate
= stream_rate
;
1206 if (stream_rate
> audio_rate
&& stream_rate
< (max_bitrate
* AUDIO_BITRATE_PERCENTAGE
)) {
1207 audio_rate
= stream_rate
;
1211 streams
[audio_stream
] = 1; // selected
1212 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream
, audio_rate
);
1218 MmsPlaylistEntry::IsHeaderParsed ()
1222 result
= parser
!= NULL
;
1228 MmsPlaylistEntry::GetParserReffed ()
1243 MmsPlaylistEntry::GetParentReffed ()
1258 MmsPlaylistEntry::CreateDemuxer (Media
*media
)
1262 ASFParser
*asf_parser
;
1264 asf_parser
= GetParserReffed ();
1266 g_return_val_if_fail (media
!= NULL
, NULL
);
1267 g_return_val_if_fail (asf_parser
!= NULL
, NULL
);
1268 g_return_val_if_fail (demuxer
== NULL
, NULL
);
1270 result
= new ASFDemuxer (media
, this);
1271 result
->SetParser (asf_parser
);
1274 if (demuxer
!= NULL
)
1280 asf_parser
->unref ();
1286 MmsPlaylistEntry::SetPlaylistGenId (const char *value
)
1290 g_free (playlist_gen_id
);
1291 playlist_gen_id
= g_strdup (value
);
1296 MmsPlaylistEntry::GetPlaylistGenId ()
1301 result
= g_strdup (playlist_gen_id
);
1307 MmsPlaylistEntry::SetBroadcastId (const char *value
)
1311 g_free (broadcast_id
);
1312 broadcast_id
= g_strdup (value
);
1317 MmsPlaylistEntry::GetBroadcastId ()
1322 result
= g_strdup (broadcast_id
);
1328 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value
)
1333 HttpStreamingFeatures
1334 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1340 MmsPlaylistEntry::AddEntry ()
1344 Media
*media
= GetMediaReffed ();
1346 PlaylistEntry
*entry
;
1347 MmsDemuxer
*mms_demuxer
;
1349 g_return_if_fail (media
!= NULL
);
1350 g_return_if_fail (parent
!= NULL
);
1352 mms_demuxer
= parent
->GetDemuxerReffed ();
1354 g_return_if_fail (mms_demuxer
!= NULL
);
1356 playlist
= mms_demuxer
->GetPlaylist ();
1358 g_return_if_fail (playlist
!= NULL
);
1360 entry
= new PlaylistEntry (playlist
);
1361 entry
->SetIsLive (features
& HttpStreamingBroadcast
);
1363 playlist
->AddEntry (entry
);
1365 entry
->InitializeWithSource (this);
1369 mms_demuxer
->unref ();
1373 MmsPlaylistEntry::ParseHeader (void *buffer
, gint32 size
)
1377 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer
, size
);
1380 MemorySource
*asf_src
= NULL
;
1381 Media
*media
= GetMediaReffed ();
1382 ASFParser
*asf_parser
;
1384 // this method shouldn't get called more than once
1385 g_return_val_if_fail (parser
== NULL
, MEDIA_FAIL
);
1386 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
1388 media
->ReportDownloadProgress (1.0);
1390 asf_src
= new MemorySource (media
, buffer
, size
, 0, false);
1391 asf_parser
= new ASFParser (asf_src
, media
);
1392 result
= asf_parser
->ReadHeader ();
1396 if (MEDIA_SUCCEEDED (result
)) {
1400 parser
= asf_parser
;
1404 asf_parser
->unref ();
1411 MmsPlaylistEntry::Pop ()
1414 //LOG_MMS ("MmsSource::Pop (), there are %i packets in the queue, of a total of %lld packets written.\n", queue.Length (), write_count);
1417 ASFPacket
*result
= NULL
;
1421 node
= (QueueNode
*) queue
.Pop ();
1424 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1428 parser
= GetParserReffed ();
1430 if (node
->packet
== NULL
) {
1431 if (parser
== NULL
) {
1432 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1435 node
->packet
= new ASFPacket (parser
, node
->source
);
1436 if (!MEDIA_SUCCEEDED (node
->packet
->Read ())) {
1437 LOG_PIPELINE_ASF ("MmsSource::Pop (): Error while parsing packet, getting a new packet\n");
1443 result
= node
->packet
;
1452 LOG_PIPELINE_ASF ("MmsSource::Pop (): popped 1 packet, there are %i packets left, of a total of %lld packets written\n", queue
.Length (), write_count
);
1458 MmsPlaylistEntry::IsFinished ()
1461 return parent
->IsFinished (); // REMOVE
1462 MmsSource
*src
= GetParentReffed ();
1464 g_return_val_if_fail (src
!= NULL
, true);
1466 result
= src
->IsFinished ();
1473 MmsPlaylistEntry::WritePacket (void *buf
, gint32 n
)
1477 ASFParser
*asf_parser
;
1478 Media
*media
= GetMediaReffed ();
1480 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %lld\n", buf
, n
, write_count
+ 1);
1483 g_return_if_fail (media
!= NULL
);
1487 media
= GetMediaReffed ();
1489 g_return_if_fail (media
!= NULL
);
1491 asf_parser
= GetParserReffed ();
1493 if (asf_parser
!= NULL
) {
1494 src
= new MemorySource (media
, buf
, n
, 0, false);
1495 packet
= new ASFPacket (asf_parser
, src
);
1496 if (!MEDIA_SUCCEEDED (packet
->Read ())) {
1497 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i): Error while parsing packet, dropping packet.\n", buf
, n
);
1499 queue
.Push (new QueueNode (packet
));
1504 src
= new MemorySource (media
, g_memdup (buf
, n
), n
, 0);
1505 queue
.Push (new QueueNode (src
));
1510 asf_parser
->unref ();
1517 * MmsPlaylistEntry::QueueNode
1520 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource
*source
)
1524 this->source
= source
;
1528 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket
*packet
)
1532 this->packet
= packet
;
1536 MmsPlaylistEntry::QueueNode::~QueueNode ()
1548 MmsDemuxer::MmsDemuxer (Media
*media
, MmsSource
*source
)
1549 : IMediaDemuxer (Type::MMSDEMUXER
, media
, source
)
1552 mms_source
= source
;
1558 MmsDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
1560 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream
);
1564 MmsDemuxer::OpenDemuxerAsyncInternal ()
1569 LOG_MMS ("MmsDemuxer::OpenDemuxerAsyncInternal ().\n");
1571 media
= GetMediaReffed ();
1572 root
= media
? media
->GetPlaylistRoot () : NULL
;
1574 g_return_if_fail (playlist
== NULL
);
1575 g_return_if_fail (media
!= NULL
);
1576 g_return_if_fail (root
!= NULL
);
1578 playlist
= new Playlist (root
, mms_source
);
1579 ReportOpenDemuxerCompleted ();
1583 MmsDemuxer::SeekInternal (guint64 pts
)
1585 g_warning ("MmsDemuxer::SeekInternal (%lld): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", pts
);
1586 print_stack_trace ();
1592 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime
)
1594 printf ("MmsDemuxer::SeekAsyncInternal (%llu): Not implemented.\n", seekToTime
);
1598 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*stream
)
1600 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream
);
1604 MmsDemuxer::Dispose ()
1610 pl
= this->playlist
;
1611 this->playlist
= NULL
;
1612 src
= this->mms_source
;
1613 this->mms_source
= NULL
;
1622 IMediaDemuxer::Dispose ();