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 (Media::InMediaThread ());
73 result
= reader
->Seek (pts
);
75 if (MEDIA_SUCCEEDED (result
)) {
76 LOG_PIPELINE ("ASFDemuxer:Seek (%" G_GUINT64_FORMAT
"): seek completed, reporting it\n", pts
);
77 ReportSeekCompleted (pts
);
78 } else if (result
== MEDIA_NOT_ENOUGH_DATA
) {
79 LOG_PIPELINE ("ASFDemuxer:Seek (%" G_GUINT64_FORMAT
"): not enough data\n", pts
);
82 ReportErrorOccurred (result
);
87 ASFDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*stream
)
89 LOG_PIPELINE ("ASFDemuxer::SwitchMediaStreamAsyncInternal (%p). TODO.\n", stream
);
93 ASFDemuxer::ReadMarkers ()
96 We can get markers from several places:
97 - The header of the file, read before starting to play
100 They are both treated the same way, added into the timeline marker collection when the media is loaded.
101 - As data in the file (a separate stream whose type is ASF_COMMAND_MEDIA)
102 These markers show up while playing the file, and they don't show up in the timeline marker collection,
103 they only get to raise the MarkerReached event.
104 currently the demuxer will call the streamed_marker_callback when it encounters any of these.
107 // Hookup to the marker (ASF_COMMAND_MEDIA) stream
109 Media
*media
= GetMediaReffed ();
111 g_return_if_fail (media
!= NULL
);
113 // Read the markers (if any)
114 List
*markers
= media
->GetMarkers ();
117 guint64 preroll_pts
= MilliSeconds_ToPts (parser
->GetFileProperties ()->preroll
);
121 // Read the SCRIPT COMMANDs
122 char **command_types
= NULL
;
123 asf_script_command_entry
**commands
= NULL
;
124 asf_script_command
*command
= parser
->script_command
;
126 if (command
!= NULL
) {
127 commands
= command
->get_commands (parser
, &command_types
);
129 if (command_types
== NULL
) {
130 //printf ("MediaElement::ReadASFMarkers (): No command types.\n");
135 if (commands
!= NULL
) {
136 for (i
= 0; commands
[i
]; i
++) {
137 asf_script_command_entry
*entry
= commands
[i
];
139 text
= entry
->get_name ();
140 pts
= MilliSeconds_ToPts (entry
->pts
) - preroll_pts
;
142 if (entry
->type_index
+ 1 <= command
->command_type_count
)
143 type
= command_types
[entry
->type_index
];
147 marker
= new MediaMarker (type
, text
, pts
);
148 markers
->Append (new MediaMarker::Node (marker
));
151 //printf ("MediaElement::ReadMarkers () Added script command at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, type);
158 asf_marker
*asf_marker
;
159 const asf_marker_entry
* marker_entry
;
161 asf_marker
= parser
->marker
;
162 if (asf_marker
!= NULL
) {
163 for (i
= 0; i
< (int) asf_marker
->marker_count
; i
++) {
164 marker_entry
= asf_marker
->get_entry (i
);
165 text
= marker_entry
->get_marker_description ();
167 pts
= marker_entry
->pts
- preroll_pts
;
169 marker
= new MediaMarker ("Name", text
, pts
);
170 markers
->Append (new MediaMarker::Node (marker
));
173 //printf ("MediaElement::ReadMarkers () Added marker at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, "Name");
181 g_strfreev (command_types
);
187 ASFDemuxer::SetParser (ASFParser
*parser
)
190 this->parser
->unref ();
191 this->parser
= parser
;
193 this->parser
->ref ();
194 this->parser
->SetSource (source
);
199 ASFDemuxer::OpenDemuxerAsyncInternal ()
203 LOG_PIPELINE ("ASFDemuxer::OpenDemuxerAsyncInternal ()\n");
207 if (MEDIA_SUCCEEDED (result
)) {
208 ReportOpenDemuxerCompleted ();
209 } else if (result
== MEDIA_NOT_ENOUGH_DATA
) {
212 ReportErrorOccurred (result
);
219 MediaResult result
= MEDIA_SUCCESS
;
220 ASFParser
*asf_parser
= NULL
;
221 gint32
*stream_to_asf_index
= NULL
;
222 IMediaStream
**streams
= NULL
;
223 Media
*media
= GetMediaReffed ();
224 int current_stream
= 1;
225 int stream_count
= 0;
228 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
230 if (parser
!= NULL
) {
233 asf_parser
= new ASFParser (source
, media
);
236 LOG_PIPELINE_ASF ("ASFDemuxer::ReadHeader ().\n");
238 result
= asf_parser
->ReadHeader ();
239 if (!MEDIA_SUCCEEDED (result
)) {
240 if (result
== MEDIA_NOT_ENOUGH_DATA
) {
241 LOG_PIPELINE_ASF ("ASFDemuxer::ReadHeader (): ReadHeader failed due to not enough data being available.\n");
243 Media::Warning (MEDIA_INVALID_MEDIA
, "asf_parser->ReadHeader () failed:");
244 Media::Warning (MEDIA_FAIL
, "%s", asf_parser
->GetLastErrorStr ());
249 // Count the number of streams
250 for (int i
= 1; i
<= 127; i
++) {
251 if (asf_parser
->IsValidStream (i
))
256 streams
= (IMediaStream
**) g_malloc0 (sizeof (IMediaStream
*) * (stream_count
+ 1)); // End with a NULL element.
257 stream_to_asf_index
= (gint32
*) g_malloc0 (sizeof (gint32
) * (stream_count
+ 1));
259 // keep count as a separate local since we can change its value (e.g. bad stream)
260 count
= stream_count
;
261 // Loop through all the streams and set stream-specific data
262 for (int i
= 0; i
< count
; i
++) {
263 while (current_stream
<= 127 && !asf_parser
->IsValidStream (current_stream
))
266 if (current_stream
> 127) {
267 result
= MEDIA_INVALID_STREAM
;
268 Media::Warning (result
, "Couldn't find all the claimed streams in the file.");
272 const asf_stream_properties
* stream_properties
= asf_parser
->GetStream (current_stream
);
273 IMediaStream
* stream
= NULL
;
275 if (stream_properties
== NULL
) {
276 result
= MEDIA_INVALID_STREAM
;
277 Media::Warning (result
, "Couldn't find all the claimed streams in the file.");
281 if (stream_properties
->is_audio ()) {
282 AudioStream
* audio
= new AudioStream (media
);
286 const WAVEFORMATEX
* wave
= stream_properties
->get_audio_data ();
288 result
= MEDIA_INVALID_STREAM
;
289 Media::Warning (result
, "Couldn't find audio data in the file.");
293 const WAVEFORMATEXTENSIBLE
* wave_ex
= wave
->get_wave_format_extensible ();
294 int data_size
= stream_properties
->size
- sizeof (asf_stream_properties
) - sizeof (WAVEFORMATEX
);
296 audio
->SetChannels (wave
->channels
);
297 audio
->SetSampleRate (wave
->samples_per_second
);
298 audio
->SetBitRate (wave
->bytes_per_second
* 8);
299 audio
->SetBlockAlign (wave
->block_alignment
);
300 audio
->SetBitsPerSample (wave
->bits_per_sample
);
301 audio
->SetExtraData (NULL
);
302 audio
->SetExtraDataSize (data_size
> wave
->codec_specific_data_size
? wave
->codec_specific_data_size
: data_size
);
303 audio
->SetCodecId (wave
->codec_id
);
305 if (wave_ex
!= NULL
) {
306 audio
->SetBitsPerSample (wave_ex
->Samples
.valid_bits_per_sample
);
307 audio
->SetExtraDataSize (audio
->GetExtraDataSize () - (sizeof (WAVEFORMATEXTENSIBLE
) - sizeof (WAVEFORMATEX
)));
308 audio
->SetCodecId (*((guint32
*) &wave_ex
->sub_format
));
311 // Fill in any extra codec data
312 if (audio
->GetExtraDataSize () > 0) {
313 audio
->SetExtraData (g_malloc0 (audio
->GetExtraDataSize ()));
314 char* src
= ((char*) wave
) + (wave_ex
? sizeof (WAVEFORMATEX
) : sizeof (WAVEFORMATEX
));
315 memcpy (audio
->GetExtraData (), src
, audio
->GetExtraDataSize ());
317 } else if (stream_properties
->is_video ()) {
318 VideoStream
* video
= new VideoStream (media
);
321 const asf_video_stream_data
* video_data
= stream_properties
->get_video_data ();
322 const BITMAPINFOHEADER
* bmp
;
323 const asf_extended_stream_properties
* aesp
;
325 if (video_data
!= NULL
) {
326 bmp
= video_data
->get_bitmap_info_header ();
327 aesp
= asf_parser
->GetExtendedStream (current_stream
);
329 video
->width
= bmp
->image_width
;
330 video
->height
= bmp
->image_height
;
332 // note: both height and width are unsigned
333 if ((video
->height
> MAX_VIDEO_HEIGHT
) || (video
->width
> MAX_VIDEO_WIDTH
)) {
334 result
= MEDIA_INVALID_STREAM
;
335 Media::Warning (result
,
336 "Video stream size (width: %d, height: %d) outside limits (%d, %d)",
337 video
->height
, video
->width
, MAX_VIDEO_HEIGHT
, MAX_VIDEO_WIDTH
);
341 video
->bits_per_sample
= bmp
->bits_per_pixel
;
342 video
->codec_id
= bmp
->compression_id
;
343 video
->extra_data_size
= bmp
->get_extra_data_size ();
344 if (video
->extra_data_size
> 0) {
345 video
->extra_data
= g_malloc0 (video
->extra_data_size
);
346 memcpy (video
->extra_data
, bmp
->get_extra_data (), video
->extra_data_size
);
348 video
->extra_data
= NULL
;
352 video
->bit_rate
= aesp
->data_bitrate
;
353 video
->pts_per_frame
= aesp
->average_time_per_frame
;
355 video
->bit_rate
= video
->width
*video
->height
;
356 video
->pts_per_frame
= 0;
359 } else if (stream_properties
->is_command ()) {
360 MarkerStream
* marker
= new MarkerStream (media
);
362 stream
->codec
= g_strdup ("asf-marker");
364 // Unknown stream, don't include it in the count since it's NULL
366 // also adjust indexes so we don't create a hole in the streams array
371 if (stream
!= NULL
) {
372 if (stream_properties
->is_video () || stream_properties
->is_audio ()) {
373 switch (stream
->codec_id
) {
374 case CODEC_WMV1
: stream
->codec
= g_strdup ("wmv1"); break;
375 case CODEC_WMV2
: stream
->codec
= g_strdup ("wmv2"); break;
376 case CODEC_WMV3
: stream
->codec
= g_strdup ("wmv3"); break;
377 case CODEC_WMVA
: stream
->codec
= g_strdup ("wmva"); break;
378 case CODEC_WVC1
: stream
->codec
= g_strdup ("vc1"); break;
379 case CODEC_MP3
: stream
->codec
= g_strdup ("mp3"); break;
380 case CODEC_WMAV1
: stream
->codec
= g_strdup ("wmav1"); break;
381 case CODEC_WMAV2
: stream
->codec
= g_strdup ("wmav2"); break;
382 case CODEC_WMAV3
: stream
->codec
= g_strdup ("wmav3"); break;
384 char a
= ((stream
->codec_id
& 0x000000FF));
385 char b
= ((stream
->codec_id
& 0x0000FF00) >> 8);
386 char c
= ((stream
->codec_id
& 0x00FF0000) >> 16);
387 char d
= ((stream
->codec_id
& 0xFF000000) >> 24);
388 stream
->codec
= g_strdup_printf ("unknown (%c%c%c%c)", a
? a
: ' ', b
? b
: ' ', c
? c
: ' ', d
? d
: ' ');
392 streams
[i
] = stream
;
394 if (!asf_parser
->file_properties
->is_broadcast ()) {
395 stream
->duration
= asf_parser
->file_properties
->play_duration
- MilliSeconds_ToPts (asf_parser
->file_properties
->preroll
);
397 stream_to_asf_index
[i
] = current_stream
;
404 if (!MEDIA_SUCCEEDED (result
)) {
408 SetStreams (streams
, stream_count
);
410 for (int i
= 0; i
< stream_count
; i
++)
411 streams
[i
]->unref ();
413 this->stream_to_asf_index
= stream_to_asf_index
;
414 this->parser
= asf_parser
;
416 reader
= new ASFReader (parser
, this);
425 asf_parser
->unref ();
428 g_free (stream_to_asf_index
);
429 stream_to_asf_index
= NULL
;
431 if (streams
!= NULL
) {
432 for (int i
= 0; i
< stream_count
; i
++) {
433 if (streams
[i
] != NULL
) {
434 streams
[i
]->unref ();
448 ASFDemuxer::GetStreamOfASFIndex (gint32 asf_index
)
450 for (gint32 i
= 0; i
< GetStreamCount (); i
++) {
451 if (stream_to_asf_index
[i
] == asf_index
)
452 return GetStream (i
);
458 ASFDemuxer::GetFrameCallback (MediaClosure
*c
)
460 MediaGetFrameClosure
*closure
= (MediaGetFrameClosure
*) c
;
461 ASFDemuxer
*demuxer
= (ASFDemuxer
*) closure
->GetDemuxer ();
462 demuxer
->GetFrameAsyncInternal (closure
->GetStream ());
464 return MEDIA_SUCCESS
;
468 ASFDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
470 //printf ("ASFDemuxer::ReadFrame (%p).\n", frame);
471 ASFFrameReader
*reader
= NULL
;
475 g_return_if_fail (this->reader
!= NULL
);
477 reader
= this->reader
->GetFrameReader (stream_to_asf_index
[stream
->index
]);
479 g_return_if_fail (reader
!= NULL
);
481 result
= reader
->Advance ();
483 if (result
== MEDIA_NO_MORE_DATA
) {
484 ReportGetFrameCompleted (NULL
);
488 if (result
== MEDIA_BUFFER_UNDERFLOW
|| result
== MEDIA_NOT_ENOUGH_DATA
) {
489 Media
*media
= GetMediaReffed ();
490 g_return_if_fail (media
!= NULL
);
491 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
492 media
->EnqueueWork (closure
, false); // TODO: use a timeout here, no need to try again immediately.
498 if (!MEDIA_SUCCEEDED (result
)) {
499 ReportErrorOccurred (g_strdup_printf ("Error while advancing to the next frame (%d)", result
));
503 frame
= new MediaFrame (stream
);
504 frame
->pts
= reader
->Pts ();
505 //frame->duration = reader->Duration ();
506 if (reader
->IsKeyFrame ())
507 frame
->AddState (MediaFrameKeyFrame
);
508 frame
->buflen
= reader
->Size ();
509 frame
->buffer
= (guint8
*) g_try_malloc (frame
->buflen
+ frame
->stream
->min_padding
);
511 if (frame
->buffer
== NULL
) {
512 ReportErrorOccurred ( "Could not allocate memory for next frame.");
516 //printf ("ASFDemuxer::ReadFrame (%p), min_padding = %i\n", frame, frame->stream->min_padding);
517 if (frame
->stream
->min_padding
> 0)
518 memset (frame
->buffer
+ frame
->buflen
, 0, frame
->stream
->min_padding
);
520 if (!reader
->Write (frame
->buffer
)) {
521 ReportErrorOccurred ("Error while copying the next frame.");
525 frame
->AddState (MediaFrameDemuxed
);
527 ReportGetFrameCompleted (frame
);
536 ASFMarkerDecoder::ASFMarkerDecoder (Media
*media
, IMediaStream
*stream
)
537 : IMediaDecoder (Type::ASFMARKERDECODER
, media
, stream
)
542 ASFMarkerDecoder::OpenDecoderAsyncInternal ()
544 ReportOpenDecoderCompleted ();
548 ASFMarkerDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
550 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame ()\n");
556 gunichar2
*uni_type
= NULL
;
557 gunichar2
*uni_text
= NULL
;
562 if (frame
->buflen
% 2 != 0 || frame
->buflen
== 0 || frame
->buffer
== NULL
) {
563 ReportErrorOccurred (MEDIA_CORRUPTED_MEDIA
);
567 data
= (gunichar2
*) frame
->buffer
;
569 size
= frame
->buflen
;
571 // the data is two arrays of WCHARs (type and text), null terminated.
572 // loop through the data, counting characters and null characters
573 // there should be at least two null characters.
576 for (guint32 i
= 0; i
< (size
/ sizeof (gunichar2
)); i
++) {
577 if (uni_text
== NULL
) {
582 if (*(data
+ i
) == 0) {
584 if (uni_text
== NULL
) {
585 uni_text
= data
+ i
+ 1;
587 break; // Found at least two nulls
592 if (null_count
>= 2) {
593 text
= wchar_to_utf8 (uni_text
, text_length
);
594 type
= wchar_to_utf8 (uni_type
, type_length
);
596 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): sending script command type: '%s', text: '%s', pts: '%" G_GUINT64_FORMAT
"'.\n", type
, text
, frame
->pts
);
598 frame
->marker
= new MediaMarker (type
, text
, frame
->pts
);
602 result
= MEDIA_SUCCESS
;
604 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): didn't find 2 null characters in the data.\n");
605 result
= MEDIA_CORRUPTED_MEDIA
;
608 if (MEDIA_SUCCEEDED (result
)) {
609 ReportDecodeFrameCompleted (frame
);
611 ReportErrorOccurred (result
);
616 * ASFMarkerDecoderInfo
620 ASFMarkerDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
622 return new ASFMarkerDecoder (media
, stream
);
626 ASFMarkerDecoderInfo::Supports (const char *codec
)
628 return !strcmp (codec
, "asf-marker");
632 ASFMarkerDecoderInfo::GetName ()
634 return "ASFMarkerDecoder";
642 ASFDemuxerInfo::Supports (IMediaSource
*source
)
647 LOG_PIPELINE_ASF ("ASFDemuxerInfo::Supports (%p) pos: %" G_GINT64_FORMAT
", avail pos: %" G_GINT64_FORMAT
"\n", source
, source
->GetPosition (), source
->GetLastAvailablePosition ());
651 if (!source
->GetPosition () == 0)
652 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 %" G_GINT64_FORMAT
")\n", source
, source
->GetPosition ());
653 if (!source
->IsPositionAvailable (16, &eof
)) // This shouldn't happen, we should have at least 1024 bytes (or eof).
654 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Not enough data! eof: %i\n", source
, eof
);
657 if (!source
->Peek (buffer
, 16)) {
658 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Peek failed.\n", source
);
662 result
= asf_guid_compare (&asf_guids_header
, (asf_guid
*) buffer
);
664 //printf ("ASFDemuxerInfo::Supports (%p): probing result: %s %s\n", source, source->ToString (),
665 // result ? "true" : "false");
667 return result
? MEDIA_SUCCESS
: MEDIA_FAIL
;
671 ASFDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
673 return new ASFDemuxer (media
, source
);
680 MmsSource::MmsSource (Media
*media
, Downloader
*downloader
)
681 : IMediaSource (Type::MMSSOURCE
, media
)
685 this->downloader
= NULL
;
689 g_return_if_fail (downloader
!= NULL
);
690 g_return_if_fail (downloader
->GetInternalDownloader () != NULL
);
691 g_return_if_fail (downloader
->GetInternalDownloader ()->GetObjectType () == Type::MMSDOWNLOADER
);
693 this->downloader
= downloader
;
694 this->downloader
->ref ();
696 ReportStreamChange (0); // create the initial MmsPlaylistEntry
700 MmsSource::Dispose ()
702 // thread safe method
704 MmsPlaylistEntry
*entry
;
705 IMediaDemuxer
*demux
;
708 // don't lock during unref, only while nulling out the local field
710 entry
= this->current
;
711 this->current
= NULL
;
712 dl
= this->downloader
;
713 this->downloader
= NULL
;
714 demux
= this->demuxer
;
715 this->demuxer
= NULL
;
719 dl
->RemoveAllHandlers (this);
729 IMediaSource::Dispose ();
733 MmsSource::Initialize ()
736 MmsDownloader
*mms_dl
;
740 dl
= GetDownloaderReffed ();
742 g_return_val_if_fail (dl
!= NULL
, MEDIA_FAIL
);
743 g_return_val_if_fail (!dl
->Started (), MEDIA_FAIL
);
745 // We must call MmsDownloader::SetSource before the downloader
746 // has actually received any data. Here we rely on the fact that
747 // firefox needs a tick before returning any data.
748 mms_dl
= GetMmsDownloader (dl
);
749 if (mms_dl
!= NULL
) {
750 mms_dl
->SetSource (this);
752 printf ("MmsSource::Initialize (): Could not get the MmsDownloader. Media won't play.\n");
755 dl
->AddHandler (Downloader::DownloadFailedEvent
, DownloadFailedCallback
, this);
756 dl
->AddHandler (Downloader::CompletedEvent
, DownloadCompleteCallback
, this);
761 return MEDIA_SUCCESS
;
765 MmsSource::GetDemuxerReffed ()
777 MmsSource::GetDownloaderReffed ()
789 MmsSource::GetCurrentReffed ()
791 MmsPlaylistEntry
*result
;
805 MmsSource::ReportDownloadFailure ()
809 LOG_MMS ("MmsSource::ReportDownloadFailure ()\n");
812 media
= GetMediaReffed ();
814 g_return_if_fail (media
!= NULL
);
816 media
->ReportErrorOccurred ("MmsDownloader failed");
821 MmsSource::ReportStreamChange (gint32 reason
)
827 LOG_MMS ("MmsSource::ReportStreamChange (reason: %i)\n", reason
);
831 media
= GetMediaReffed ();
833 g_return_if_fail (media
!= NULL
);
835 root
= media
->GetPlaylistRoot ();
837 g_return_if_fail (root
!= NULL
);
840 if (current
!= NULL
) {
841 current
->NotifyFinished ();
845 entry_media
= new Media (root
);
846 current
= new MmsPlaylistEntry (entry_media
, this);
847 entry_media
->unref ();
854 MmsSource::SetMmsMetadata (const char *playlist_gen_id
, const char *broadcast_id
, HttpStreamingFeatures features
)
856 MmsPlaylistEntry
*entry
;
858 LOG_MMS ("MmsSource::SetMmsMetadata ('%s', '%s', %i)\n", playlist_gen_id
, broadcast_id
, (int) features
);
862 entry
= GetCurrentReffed ();
864 g_return_if_fail (entry
!= NULL
);
866 entry
->SetPlaylistGenId (playlist_gen_id
);
867 entry
->SetBroadcastId (broadcast_id
);
868 entry
->SetHttpStreamingFeatures (features
);
874 MmsSource::DownloadFailedHandler (Downloader
*dl
, EventArgs
*args
)
876 Media
*media
= GetMediaReffed ();
880 g_return_if_fail (media
!= NULL
);
881 eea
= new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR"));
882 media
->RetryHttp (eea
);
888 MmsSource::DownloadCompleteHandler (Downloader
*dl
, EventArgs
*args
)
894 MmsSource::NotifyFinished (guint32 reason
)
898 LOG_MMS ("MmsSource::NotifyFinished (%i)\n", reason
);
901 // The server has finished streaming and no more
902 // Data packets will be transmitted until the next Play request
904 } else if (reason
== 1) {
905 // The server has finished streaming the current playlist entry. Other playlist
906 // entries still remain to be streamed. The server will transmit a stream change packet
907 // when it switches to the next entry.
908 MmsPlaylistEntry
*entry
= GetCurrentReffed ();
909 entry
->NotifyFinished ();
917 MmsSource::SeekToPts (guint64 pts
)
920 MediaResult result
= true;
922 MmsDownloader
*mms_dl
;
924 LOG_PIPELINE_ASF ("MmsSource::SeekToPts (%" G_GUINT64_FORMAT
")\n", pts
);
926 dl
= GetDownloaderReffed ();
928 g_return_val_if_fail (dl
!= NULL
, MEDIA_FAIL
);
930 mms_dl
= GetMmsDownloader (dl
);
933 mms_dl
->SetRequestedPts (pts
);
935 result
= MEDIA_SUCCESS
;
946 MmsSource::GetMmsDownloader (Downloader
*dl
)
948 InternalDownloader
*idl
;
950 g_return_val_if_fail (dl
!= NULL
, NULL
);
952 idl
= dl
->GetInternalDownloader ();
957 if (idl
->GetObjectType () != Type::MMSDOWNLOADER
)
960 return (MmsDownloader
*) idl
;
964 MmsSource::CreateDemuxer (Media
*media
)
967 MmsDemuxer
*result
= NULL
;
969 g_return_val_if_fail (demuxer
== NULL
, NULL
);
972 if (demuxer
== NULL
) {
973 result
= new MmsDemuxer (media
, this);
983 MmsSource::WritePacket (void *buf
, gint32 n
)
985 MmsPlaylistEntry
*entry
;
989 entry
= GetCurrentReffed ();
991 g_return_if_fail (entry
!= NULL
);
993 entry
->WritePacket (buf
, n
);
1000 MmsPlaylistEntry
*entry
;
1003 entry
= GetCurrentReffed ();
1005 g_return_val_if_fail (entry
!= NULL
, NULL
);
1007 result
= entry
->Pop ();
1018 MmsPlaylistEntry
*entry
;
1024 entry
= GetCurrentReffed ();
1026 if (entry
== NULL
) {
1029 result
= entry
->Eof ();
1040 MmsPlaylistEntry::MmsPlaylistEntry (Media
*media
, MmsSource
*source
)
1041 : IMediaSource (Type::MMSPLAYLISTENTRY
, media
)
1048 playlist_gen_id
= NULL
;
1049 broadcast_id
= NULL
;
1050 features
= HttpStreamingFeaturesNone
;
1052 g_return_if_fail (parent
!= NULL
);
1057 MmsPlaylistEntry::Initialize ()
1059 return MEDIA_SUCCESS
;
1063 MmsPlaylistEntry::Dispose ()
1066 MmsSource
*mms_source
;
1067 ASFParser
*asf_parser
;
1068 IMediaDemuxer
*demux
;
1071 mms_source
= this->parent
;
1072 this->parent
= NULL
;
1073 asf_parser
= this->parser
;
1074 this->parser
= NULL
;
1075 demux
= this->demuxer
;
1076 this->demuxer
= NULL
;
1077 g_free (playlist_gen_id
);
1078 playlist_gen_id
= NULL
;
1079 g_free (broadcast_id
);
1080 broadcast_id
= NULL
;
1083 if (mms_source
!= NULL
)
1084 mms_source
->unref ();
1086 if (asf_parser
!= NULL
)
1087 asf_parser
->unref ();
1093 // This is a bit weird - in certain
1094 // we can end up with a circular dependency between
1095 // Media and MmsPlaylistEntry, where Media::Dispose
1096 // isn't called. So if Media::Dispose hasn't been
1097 // called, do it here, and only do it after our
1098 // instance copy of the media is cleared out to
1099 // prevent infinite loops.
1100 Media
*m
= GetMediaReffed ();
1102 IMediaSource::Dispose ();
1105 if (!m
->IsDisposed ())
1112 MmsPlaylistEntry::SeekToPts (guint64 pts
)
1114 MmsSource
*ms
= GetParentReffed ();
1116 ms
->SeekToPts (pts
);
1119 return MEDIA_SUCCESS
;
1121 fprintf (stderr
, "MmsPlaylistEntry::SeekToPts (%" G_GUINT64_FORMAT
"): Could not seek to pts, no parent.\n", pts
);
1127 MmsPlaylistEntry::NotifyFinished ()
1133 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate
, gint8 streams
[128])
1136 asf_file_properties
*properties
;
1137 gint32 audio_bitrates
[128];
1138 gint32 video_bitrates
[128];
1140 memset (audio_bitrates
, 0xff, 128 * 4);
1141 memset (video_bitrates
, 0xff, 128 * 4);
1142 memset (streams
, 0xff, 128);
1144 parser
= GetParserReffed ();
1146 g_return_if_fail (parser
!= NULL
);
1148 properties
= parser
->GetFileProperties ();
1150 g_return_if_fail (properties
!= NULL
);
1152 for (int i
= 1; i
< 127; i
++) {
1154 if (!parser
->IsValidStream (i
)) {
1155 streams
[i
] = -1; // inexistent
1158 streams
[i
] = 0; // disabled
1161 const asf_stream_properties
*stream_properties
= parser
->GetStream (current_stream
);
1162 const asf_extended_stream_properties
*extended_stream_properties
= parser
->GetExtendedStream (current_stream
);
1164 if (stream_properties
== NULL
) {
1165 printf ("MmsPlaylistEntry::GetSelectedStreams (): stream #%i doesn't have any stream properties.\n", current_stream
);
1169 if (stream_properties
->is_audio ()) {
1170 const WAVEFORMATEX
* wave
= stream_properties
->get_audio_data ();
1171 audio_bitrates
[current_stream
] = wave
->bytes_per_second
* 8;
1172 } else if (stream_properties
->is_video ()) {
1174 const asf_video_stream_data
* video_data
= stream_properties
->get_video_data ();
1175 const BITMAPINFOHEADER
* bmp
;
1177 if (extended_stream_properties
!= NULL
) {
1178 bit_rate
= extended_stream_properties
->data_bitrate
;
1179 } else if (video_data
!= NULL
) {
1180 bmp
= video_data
->get_bitmap_info_header ();
1182 bit_rate
= bmp
->image_width
*bmp
->image_height
;
1186 video_bitrates
[current_stream
] = bit_rate
;
1187 } else if (stream_properties
->is_command ()) {
1188 // we select all marker streams
1189 streams
[current_stream
] = 1;
1193 // select the video stream
1194 int video_stream
= 0;
1197 for (int i
= 0; i
< 128; i
++) {
1198 int stream_rate
= video_bitrates
[i
];
1200 if (stream_rate
== -1)
1203 if (video_rate
== 0) {
1204 video_rate
= stream_rate
;
1208 if (stream_rate
> video_rate
&& stream_rate
< (max_bitrate
* VIDEO_BITRATE_PERCENTAGE
)) {
1209 video_rate
= stream_rate
;
1213 streams
[video_stream
] = 1; // selected
1214 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected video stream %i of rate %i\n", video_stream
, video_rate
);
1217 // select audio stream
1218 int audio_stream
= 0;
1221 for (int i
= 0; i
< 128; i
++) {
1222 int stream_rate
= audio_bitrates
[i
];
1224 if (stream_rate
== -1)
1227 if (audio_rate
== 0) {
1228 audio_rate
= stream_rate
;
1232 if (stream_rate
> audio_rate
&& stream_rate
< (max_bitrate
* AUDIO_BITRATE_PERCENTAGE
)) {
1233 audio_rate
= stream_rate
;
1237 streams
[audio_stream
] = 1; // selected
1238 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream
, audio_rate
);
1244 MmsPlaylistEntry::IsHeaderParsed ()
1248 result
= parser
!= NULL
;
1254 MmsPlaylistEntry::GetDemuxerReffed ()
1257 IMediaDemuxer
*result
;
1269 MmsPlaylistEntry::GetParserReffed ()
1284 MmsPlaylistEntry::GetParentReffed ()
1299 MmsPlaylistEntry::CreateDemuxer (Media
*media
)
1303 ASFParser
*asf_parser
;
1305 asf_parser
= GetParserReffed ();
1307 g_return_val_if_fail (media
!= NULL
, NULL
);
1308 g_return_val_if_fail (asf_parser
!= NULL
, NULL
);
1309 g_return_val_if_fail (demuxer
== NULL
, NULL
);
1311 result
= new ASFDemuxer (media
, this);
1312 result
->SetParser (asf_parser
);
1315 if (demuxer
!= NULL
)
1321 asf_parser
->unref ();
1327 MmsPlaylistEntry::SetPlaylistGenId (const char *value
)
1331 g_free (playlist_gen_id
);
1332 playlist_gen_id
= g_strdup (value
);
1337 MmsPlaylistEntry::GetPlaylistGenId ()
1342 result
= g_strdup (playlist_gen_id
);
1348 MmsPlaylistEntry::SetBroadcastId (const char *value
)
1352 g_free (broadcast_id
);
1353 broadcast_id
= g_strdup (value
);
1358 MmsPlaylistEntry::GetBroadcastId ()
1363 result
= g_strdup (broadcast_id
);
1369 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value
)
1374 HttpStreamingFeatures
1375 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1381 MmsPlaylistEntry::AddEntry ()
1385 Media
*media
= GetMediaReffed ();
1387 PlaylistEntry
*entry
;
1388 MmsDemuxer
*mms_demuxer
= NULL
;
1390 g_return_if_fail (media
!= NULL
);
1395 mms_demuxer
= parent
->GetDemuxerReffed ();
1397 if (mms_demuxer
== NULL
)
1400 playlist
= mms_demuxer
->GetPlaylist ();
1402 if (playlist
== NULL
)
1405 entry
= new PlaylistEntry (playlist
);
1406 entry
->SetIsLive (features
& HttpStreamingBroadcast
);
1408 playlist
->AddEntry (entry
);
1410 entry
->InitializeWithSource (this);
1416 mms_demuxer
->unref ();
1420 MmsPlaylistEntry::ParseHeader (void *buffer
, gint32 size
)
1424 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer
, size
);
1427 MemorySource
*asf_src
= NULL
;
1429 ASFParser
*asf_parser
;
1431 // this method shouldn't get called more than once
1432 g_return_val_if_fail (parser
== NULL
, MEDIA_FAIL
);
1434 media
= GetMediaReffed ();
1435 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
1437 media
->ReportDownloadProgress (1.0);
1439 asf_src
= new MemorySource (media
, buffer
, size
, 0, false);
1440 asf_parser
= new ASFParser (asf_src
, media
);
1441 result
= asf_parser
->ReadHeader ();
1445 if (MEDIA_SUCCEEDED (result
)) {
1449 parser
= asf_parser
;
1453 asf_parser
->unref ();
1460 MmsPlaylistEntry::Pop ()
1463 //LOG_MMS ("MmsSource::Pop (), there are %i packets in the queue, of a total of %" G_GINT64_FORMAT " packets written.\n", queue.Length (), write_count);
1466 ASFPacket
*result
= NULL
;
1470 node
= (QueueNode
*) queue
.Pop ();
1473 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1477 parser
= GetParserReffed ();
1479 if (node
->packet
== NULL
) {
1480 if (parser
== NULL
) {
1481 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1484 node
->packet
= new ASFPacket (parser
, node
->source
);
1485 if (!MEDIA_SUCCEEDED (node
->packet
->Read ())) {
1486 LOG_PIPELINE_ASF ("MmsSource::Pop (): Error while parsing packet, getting a new packet\n");
1492 result
= node
->packet
;
1501 LOG_PIPELINE_ASF ("MmsSource::Pop (): popped 1 packet, there are %i packets left, of a total of %" G_GINT64_FORMAT
" packets written\n", queue
.Length (), write_count
);
1507 MmsPlaylistEntry::IsFinished ()
1510 return parent
->IsFinished (); // REMOVE
1511 MmsSource
*src
= GetParentReffed ();
1513 g_return_val_if_fail (src
!= NULL
, true);
1515 result
= src
->IsFinished ();
1522 MmsPlaylistEntry::WritePacket (void *buf
, gint32 n
)
1526 ASFParser
*asf_parser
;
1530 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %" G_GINT64_FORMAT
"\n", buf
, n
, write_count
+ 1);
1533 media
= GetMediaReffed ();
1535 g_return_if_fail (media
!= NULL
);
1539 asf_parser
= GetParserReffed ();
1541 if (asf_parser
!= NULL
) {
1542 src
= new MemorySource (media
, buf
, n
, 0, false);
1543 packet
= new ASFPacket (asf_parser
, src
);
1544 if (!MEDIA_SUCCEEDED (packet
->Read ())) {
1545 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i): Error while parsing packet, dropping packet.\n", buf
, n
);
1547 queue
.Push (new QueueNode (packet
));
1553 src
= new MemorySource (media
, g_memdup (buf
, n
), n
, 0);
1554 queue
.Push (new QueueNode (src
));
1560 IMediaDemuxer
*demuxer
= GetDemuxerReffed ();
1562 demuxer
->FillBuffers ();
1567 asf_parser
->unref ();
1574 * MmsPlaylistEntry::QueueNode
1577 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource
*source
)
1581 this->source
= source
;
1585 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket
*packet
)
1589 this->packet
= packet
;
1593 MmsPlaylistEntry::QueueNode::~QueueNode ()
1605 MmsDemuxer::MmsDemuxer (Media
*media
, MmsSource
*source
)
1606 : IMediaDemuxer (Type::MMSDEMUXER
, media
, source
)
1609 mms_source
= source
;
1615 MmsDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
1617 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream
);
1621 MmsDemuxer::OpenDemuxerAsyncInternal ()
1626 LOG_MMS ("MmsDemuxer::OpenDemuxerAsyncInternal ().\n");
1628 media
= GetMediaReffed ();
1629 root
= media
? media
->GetPlaylistRoot () : NULL
;
1631 g_return_if_fail (playlist
== NULL
);
1632 g_return_if_fail (media
!= NULL
);
1633 g_return_if_fail (root
!= NULL
);
1635 playlist
= new Playlist (root
, mms_source
);
1636 ReportOpenDemuxerCompleted ();
1641 MmsDemuxer::SeekInternal (guint64 pts
)
1643 g_warning ("MmsDemuxer::SeekInternal (%" G_GINT64_FORMAT
"): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", pts
);
1644 print_stack_trace ();
1650 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime
)
1652 printf ("MmsDemuxer::SeekAsyncInternal (%" G_GUINT64_FORMAT
"): Not implemented.\n", seekToTime
);
1656 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*stream
)
1658 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream
);
1662 MmsDemuxer::Dispose ()
1668 pl
= this->playlist
;
1669 this->playlist
= NULL
;
1670 src
= this->mms_source
;
1671 this->mms_source
= NULL
;
1680 IMediaDemuxer::Dispose ();