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 ("Error while advancing to the next frame (%d)");
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
);
1118 return MEDIA_SUCCESS
;
1120 fprintf (stderr
, "MmsPlaylistEntry::SeekToPts (%" G_GUINT64_FORMAT
"): Could not seek to pts, no parent.\n", pts
);
1126 MmsPlaylistEntry::NotifyFinished ()
1132 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate
, gint8 streams
[128])
1135 asf_file_properties
*properties
;
1136 gint32 audio_bitrates
[128];
1137 gint32 video_bitrates
[128];
1139 memset (audio_bitrates
, 0xff, 128 * 4);
1140 memset (video_bitrates
, 0xff, 128 * 4);
1141 memset (streams
, 0xff, 128);
1143 parser
= GetParserReffed ();
1145 g_return_if_fail (parser
!= NULL
);
1147 properties
= parser
->GetFileProperties ();
1149 g_return_if_fail (properties
!= NULL
);
1151 for (int i
= 1; i
< 127; i
++) {
1153 if (!parser
->IsValidStream (i
)) {
1154 streams
[i
] = -1; // inexistent
1157 streams
[i
] = 0; // disabled
1160 const asf_stream_properties
*stream_properties
= parser
->GetStream (current_stream
);
1161 const asf_extended_stream_properties
*extended_stream_properties
= parser
->GetExtendedStream (current_stream
);
1163 if (stream_properties
== NULL
) {
1164 printf ("MmsPlaylistEntry::GetSelectedStreams (): stream #%i doesn't have any stream properties.\n", current_stream
);
1168 if (stream_properties
->is_audio ()) {
1169 const WAVEFORMATEX
* wave
= stream_properties
->get_audio_data ();
1170 audio_bitrates
[current_stream
] = wave
->bytes_per_second
* 8;
1171 } else if (stream_properties
->is_video ()) {
1173 const asf_video_stream_data
* video_data
= stream_properties
->get_video_data ();
1174 const BITMAPINFOHEADER
* bmp
;
1176 if (extended_stream_properties
!= NULL
) {
1177 bit_rate
= extended_stream_properties
->data_bitrate
;
1178 } else if (video_data
!= NULL
) {
1179 bmp
= video_data
->get_bitmap_info_header ();
1181 bit_rate
= bmp
->image_width
*bmp
->image_height
;
1185 video_bitrates
[current_stream
] = bit_rate
;
1186 } else if (stream_properties
->is_command ()) {
1187 // we select all marker streams
1188 streams
[current_stream
] = 1;
1192 // select the video stream
1193 int video_stream
= 0;
1196 for (int i
= 0; i
< 128; i
++) {
1197 int stream_rate
= video_bitrates
[i
];
1199 if (stream_rate
== -1)
1202 if (video_rate
== 0) {
1203 video_rate
= stream_rate
;
1207 if (stream_rate
> video_rate
&& stream_rate
< (max_bitrate
* VIDEO_BITRATE_PERCENTAGE
)) {
1208 video_rate
= stream_rate
;
1212 streams
[video_stream
] = 1; // selected
1213 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected video stream %i of rate %i\n", video_stream
, video_rate
);
1216 // select audio stream
1217 int audio_stream
= 0;
1220 for (int i
= 0; i
< 128; i
++) {
1221 int stream_rate
= audio_bitrates
[i
];
1223 if (stream_rate
== -1)
1226 if (audio_rate
== 0) {
1227 audio_rate
= stream_rate
;
1231 if (stream_rate
> audio_rate
&& stream_rate
< (max_bitrate
* AUDIO_BITRATE_PERCENTAGE
)) {
1232 audio_rate
= stream_rate
;
1236 streams
[audio_stream
] = 1; // selected
1237 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream
, audio_rate
);
1243 MmsPlaylistEntry::IsHeaderParsed ()
1247 result
= parser
!= NULL
;
1253 MmsPlaylistEntry::GetDemuxerReffed ()
1256 IMediaDemuxer
*result
;
1268 MmsPlaylistEntry::GetParserReffed ()
1283 MmsPlaylistEntry::GetParentReffed ()
1298 MmsPlaylistEntry::CreateDemuxer (Media
*media
)
1302 ASFParser
*asf_parser
;
1304 asf_parser
= GetParserReffed ();
1306 g_return_val_if_fail (media
!= NULL
, NULL
);
1307 g_return_val_if_fail (asf_parser
!= NULL
, NULL
);
1308 g_return_val_if_fail (demuxer
== NULL
, NULL
);
1310 result
= new ASFDemuxer (media
, this);
1311 result
->SetParser (asf_parser
);
1314 if (demuxer
!= NULL
)
1320 asf_parser
->unref ();
1326 MmsPlaylistEntry::SetPlaylistGenId (const char *value
)
1330 g_free (playlist_gen_id
);
1331 playlist_gen_id
= g_strdup (value
);
1336 MmsPlaylistEntry::GetPlaylistGenId ()
1341 result
= g_strdup (playlist_gen_id
);
1347 MmsPlaylistEntry::SetBroadcastId (const char *value
)
1351 g_free (broadcast_id
);
1352 broadcast_id
= g_strdup (value
);
1357 MmsPlaylistEntry::GetBroadcastId ()
1362 result
= g_strdup (broadcast_id
);
1368 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value
)
1373 HttpStreamingFeatures
1374 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1380 MmsPlaylistEntry::AddEntry ()
1384 Media
*media
= GetMediaReffed ();
1386 PlaylistEntry
*entry
;
1387 MmsDemuxer
*mms_demuxer
= NULL
;
1389 g_return_if_fail (media
!= NULL
);
1394 mms_demuxer
= parent
->GetDemuxerReffed ();
1396 if (mms_demuxer
== NULL
)
1399 playlist
= mms_demuxer
->GetPlaylist ();
1401 if (playlist
== NULL
)
1404 entry
= new PlaylistEntry (playlist
);
1405 entry
->SetIsLive (features
& HttpStreamingBroadcast
);
1407 playlist
->AddEntry (entry
);
1409 entry
->InitializeWithSource (this);
1415 mms_demuxer
->unref ();
1419 MmsPlaylistEntry::ParseHeader (void *buffer
, gint32 size
)
1423 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer
, size
);
1426 MemorySource
*asf_src
= NULL
;
1428 ASFParser
*asf_parser
;
1430 // this method shouldn't get called more than once
1431 g_return_val_if_fail (parser
== NULL
, MEDIA_FAIL
);
1433 media
= GetMediaReffed ();
1434 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
1436 media
->ReportDownloadProgress (1.0);
1438 asf_src
= new MemorySource (media
, buffer
, size
, 0, false);
1439 asf_parser
= new ASFParser (asf_src
, media
);
1440 result
= asf_parser
->ReadHeader ();
1444 if (MEDIA_SUCCEEDED (result
)) {
1448 parser
= asf_parser
;
1452 asf_parser
->unref ();
1459 MmsPlaylistEntry::Pop ()
1462 //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);
1465 ASFPacket
*result
= NULL
;
1469 node
= (QueueNode
*) queue
.Pop ();
1472 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1476 parser
= GetParserReffed ();
1478 if (node
->packet
== NULL
) {
1479 if (parser
== NULL
) {
1480 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1483 node
->packet
= new ASFPacket (parser
, node
->source
);
1484 if (!MEDIA_SUCCEEDED (node
->packet
->Read ())) {
1485 LOG_PIPELINE_ASF ("MmsSource::Pop (): Error while parsing packet, getting a new packet\n");
1491 result
= node
->packet
;
1500 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
);
1506 MmsPlaylistEntry::IsFinished ()
1509 return parent
->IsFinished (); // REMOVE
1510 MmsSource
*src
= GetParentReffed ();
1512 g_return_val_if_fail (src
!= NULL
, true);
1514 result
= src
->IsFinished ();
1521 MmsPlaylistEntry::WritePacket (void *buf
, gint32 n
)
1525 ASFParser
*asf_parser
;
1529 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %" G_GINT64_FORMAT
"\n", buf
, n
, write_count
+ 1);
1532 media
= GetMediaReffed ();
1534 g_return_if_fail (media
!= NULL
);
1538 asf_parser
= GetParserReffed ();
1540 if (asf_parser
!= NULL
) {
1541 src
= new MemorySource (media
, buf
, n
, 0, false);
1542 packet
= new ASFPacket (asf_parser
, src
);
1543 if (!MEDIA_SUCCEEDED (packet
->Read ())) {
1544 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i): Error while parsing packet, dropping packet.\n", buf
, n
);
1546 queue
.Push (new QueueNode (packet
));
1552 src
= new MemorySource (media
, g_memdup (buf
, n
), n
, 0);
1553 queue
.Push (new QueueNode (src
));
1559 IMediaDemuxer
*demuxer
= GetDemuxerReffed ();
1561 demuxer
->FillBuffers ();
1566 asf_parser
->unref ();
1573 * MmsPlaylistEntry::QueueNode
1576 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource
*source
)
1580 this->source
= source
;
1584 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket
*packet
)
1588 this->packet
= packet
;
1592 MmsPlaylistEntry::QueueNode::~QueueNode ()
1604 MmsDemuxer::MmsDemuxer (Media
*media
, MmsSource
*source
)
1605 : IMediaDemuxer (Type::MMSDEMUXER
, media
, source
)
1608 mms_source
= source
;
1614 MmsDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
1616 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream
);
1620 MmsDemuxer::OpenDemuxerAsyncInternal ()
1625 LOG_MMS ("MmsDemuxer::OpenDemuxerAsyncInternal ().\n");
1627 media
= GetMediaReffed ();
1628 root
= media
? media
->GetPlaylistRoot () : NULL
;
1630 g_return_if_fail (playlist
== NULL
);
1631 g_return_if_fail (media
!= NULL
);
1632 g_return_if_fail (root
!= NULL
);
1634 playlist
= new Playlist (root
, mms_source
);
1635 ReportOpenDemuxerCompleted ();
1640 MmsDemuxer::SeekInternal (guint64 pts
)
1642 g_warning ("MmsDemuxer::SeekInternal (%" G_GINT64_FORMAT
"): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", pts
);
1643 print_stack_trace ();
1649 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime
)
1651 printf ("MmsDemuxer::SeekAsyncInternal (%" G_GUINT64_FORMAT
"): Not implemented.\n", seekToTime
);
1655 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*stream
)
1657 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream
);
1661 MmsDemuxer::Dispose ()
1667 pl
= this->playlist
;
1668 this->playlist
= NULL
;
1669 src
= this->mms_source
;
1670 this->mms_source
= NULL
;
1679 IMediaDemuxer::Dispose ();