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 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
= NULL
;
473 g_return_if_fail (this->reader
!= NULL
);
475 reader
= this->reader
->GetFrameReader (stream_to_asf_index
[stream
->index
]);
477 g_return_if_fail (reader
!= NULL
);
479 result
= reader
->Advance ();
481 if (result
== MEDIA_NO_MORE_DATA
) {
482 ReportGetFrameCompleted (NULL
);
486 if (result
== MEDIA_BUFFER_UNDERFLOW
|| result
== MEDIA_NOT_ENOUGH_DATA
) {
487 Media
*media
= GetMediaReffed ();
488 g_return_if_fail (media
!= NULL
);
489 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
490 media
->EnqueueWork (closure
, false); // TODO: use a timeout here, no need to try again immediately.
496 if (!MEDIA_SUCCEEDED (result
)) {
497 ReportErrorOccurred ("Error while advancing to the next frame (%d)");
501 frame
= new MediaFrame (stream
);
502 frame
->pts
= reader
->Pts ();
503 //frame->duration = reader->Duration ();
504 if (reader
->IsKeyFrame ())
505 frame
->AddState (MediaFrameKeyFrame
);
506 frame
->buflen
= reader
->Size ();
507 frame
->buffer
= (guint8
*) g_try_malloc (frame
->buflen
+ frame
->stream
->min_padding
);
509 if (frame
->buffer
== NULL
) {
510 ReportErrorOccurred ( "Could not allocate memory for next frame.");
514 //printf ("ASFDemuxer::ReadFrame (%p), min_padding = %i\n", frame, frame->stream->min_padding);
515 if (frame
->stream
->min_padding
> 0)
516 memset (frame
->buffer
+ frame
->buflen
, 0, frame
->stream
->min_padding
);
518 if (!reader
->Write (frame
->buffer
)) {
519 ReportErrorOccurred ("Error while copying the next frame.");
523 frame
->AddState (MediaFrameDemuxed
);
525 ReportGetFrameCompleted (frame
);
534 ASFMarkerDecoder::ASFMarkerDecoder (Media
*media
, IMediaStream
*stream
)
535 : IMediaDecoder (Type::ASFMARKERDECODER
, media
, stream
)
540 ASFMarkerDecoder::OpenDecoderAsyncInternal ()
542 ReportOpenDecoderCompleted ();
546 ASFMarkerDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
548 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame ()\n");
554 gunichar2
*uni_type
= NULL
;
555 gunichar2
*uni_text
= NULL
;
560 if (frame
->buflen
% 2 != 0 || frame
->buflen
== 0 || frame
->buffer
== NULL
) {
561 ReportErrorOccurred (MEDIA_CORRUPTED_MEDIA
);
565 data
= (gunichar2
*) frame
->buffer
;
567 size
= frame
->buflen
;
569 // the data is two arrays of WCHARs (type and text), null terminated.
570 // loop through the data, counting characters and null characters
571 // there should be at least two null characters.
574 for (guint32 i
= 0; i
< (size
/ sizeof (gunichar2
)); i
++) {
575 if (uni_text
== NULL
) {
580 if (*(data
+ i
) == 0) {
582 if (uni_text
== NULL
) {
583 uni_text
= data
+ i
+ 1;
585 break; // Found at least two nulls
590 if (null_count
>= 2) {
591 text
= wchar_to_utf8 (uni_text
, text_length
);
592 type
= wchar_to_utf8 (uni_type
, type_length
);
594 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): sending script command type: '%s', text: '%s', pts: '%" G_GUINT64_FORMAT
"'.\n", type
, text
, frame
->pts
);
596 frame
->marker
= new MediaMarker (type
, text
, frame
->pts
);
600 result
= MEDIA_SUCCESS
;
602 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): didn't find 2 null characters in the data.\n");
603 result
= MEDIA_CORRUPTED_MEDIA
;
606 if (MEDIA_SUCCEEDED (result
)) {
607 ReportDecodeFrameCompleted (frame
);
609 ReportErrorOccurred (result
);
614 * ASFMarkerDecoderInfo
618 ASFMarkerDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
620 return new ASFMarkerDecoder (media
, stream
);
624 ASFMarkerDecoderInfo::Supports (const char *codec
)
626 return !strcmp (codec
, "asf-marker");
630 ASFMarkerDecoderInfo::GetName ()
632 return "ASFMarkerDecoder";
640 ASFDemuxerInfo::Supports (IMediaSource
*source
)
645 LOG_PIPELINE_ASF ("ASFDemuxerInfo::Supports (%p) pos: %lld, avail pos: %lld\n", source
, source
->GetPosition (), source
->GetLastAvailablePosition ());
649 if (!source
->GetPosition () == 0)
650 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 ());
651 if (!source
->IsPositionAvailable (16, &eof
)) // This shouldn't happen, we should have at least 1024 bytes (or eof).
652 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Not enough data! eof: %i\n", source
, eof
);
655 if (!source
->Peek (buffer
, 16)) {
656 fprintf (stderr
, "ASFDemuxerInfo::Supports (%p): Peek failed.\n", source
);
660 result
= asf_guid_compare (&asf_guids_header
, (asf_guid
*) buffer
);
662 //printf ("ASFDemuxerInfo::Supports (%p): probing result: %s %s\n", source, source->ToString (),
663 // result ? "true" : "false");
665 return result
? MEDIA_SUCCESS
: MEDIA_FAIL
;
669 ASFDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
671 return new ASFDemuxer (media
, source
);
678 MmsSource::MmsSource (Media
*media
, Downloader
*downloader
)
679 : IMediaSource (Type::MMSSOURCE
, media
)
683 this->downloader
= NULL
;
687 g_return_if_fail (downloader
!= NULL
);
688 g_return_if_fail (downloader
->GetInternalDownloader () != NULL
);
689 g_return_if_fail (downloader
->GetInternalDownloader ()->GetObjectType () == Type::MMSDOWNLOADER
);
691 this->downloader
= downloader
;
692 this->downloader
->ref ();
694 ReportStreamChange (0); // create the initial MmsPlaylistEntry
698 MmsSource::Dispose ()
700 // thread safe method
702 MmsPlaylistEntry
*entry
;
703 IMediaDemuxer
*demux
;
706 // don't lock during unref, only while nulling out the local field
708 entry
= this->current
;
709 this->current
= NULL
;
710 dl
= this->downloader
;
711 this->downloader
= NULL
;
712 demux
= this->demuxer
;
713 this->demuxer
= NULL
;
717 dl
->RemoveAllHandlers (this);
727 IMediaSource::Dispose ();
731 MmsSource::Initialize ()
734 MmsDownloader
*mms_dl
;
738 dl
= GetDownloaderReffed ();
740 g_return_val_if_fail (dl
!= NULL
, MEDIA_FAIL
);
741 g_return_val_if_fail (!dl
->Started (), MEDIA_FAIL
);
743 // We must call MmsDownloader::SetSource before the downloader
744 // has actually received any data. Here we rely on the fact that
745 // firefox needs a tick before returning any data.
746 mms_dl
= GetMmsDownloader (dl
);
747 if (mms_dl
!= NULL
) {
748 mms_dl
->SetSource (this);
750 printf ("MmsSource::Initialize (): Could not get the MmsDownloader. Media won't play.\n");
753 dl
->AddHandler (Downloader::DownloadFailedEvent
, DownloadFailedCallback
, this);
754 dl
->AddHandler (Downloader::CompletedEvent
, DownloadCompleteCallback
, this);
759 return MEDIA_SUCCESS
;
763 MmsSource::GetDemuxerReffed ()
775 MmsSource::GetDownloaderReffed ()
787 MmsSource::GetCurrentReffed ()
789 MmsPlaylistEntry
*result
;
803 MmsSource::ReportDownloadFailure ()
807 LOG_MMS ("MmsSource::ReportDownloadFailure ()\n");
810 media
= GetMediaReffed ();
812 g_return_if_fail (media
!= NULL
);
814 media
->ReportErrorOccurred ("MmsDownloader failed");
819 MmsSource::ReportStreamChange (gint32 reason
)
825 LOG_MMS ("MmsSource::ReportStreamChange (reason: %i)\n", reason
);
829 media
= GetMediaReffed ();
831 g_return_if_fail (media
!= NULL
);
833 root
= media
->GetPlaylistRoot ();
835 g_return_if_fail (root
!= NULL
);
838 if (current
!= NULL
) {
839 current
->NotifyFinished ();
843 entry_media
= new Media (root
);
844 current
= new MmsPlaylistEntry (entry_media
, this);
845 entry_media
->unref ();
852 MmsSource::SetMmsMetadata (const char *playlist_gen_id
, const char *broadcast_id
, HttpStreamingFeatures features
)
854 MmsPlaylistEntry
*entry
;
856 LOG_MMS ("MmsSource::SetMmsMetadata ('%s', '%s', %i)\n", playlist_gen_id
, broadcast_id
, (int) features
);
860 entry
= GetCurrentReffed ();
862 g_return_if_fail (entry
!= NULL
);
864 entry
->SetPlaylistGenId (playlist_gen_id
);
865 entry
->SetBroadcastId (broadcast_id
);
866 entry
->SetHttpStreamingFeatures (features
);
872 MmsSource::DownloadFailedHandler (Downloader
*dl
, EventArgs
*args
)
874 Media
*media
= GetMediaReffed ();
878 g_return_if_fail (media
!= NULL
);
879 eea
= new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR"));
880 media
->RetryHttp (eea
);
886 MmsSource::DownloadCompleteHandler (Downloader
*dl
, EventArgs
*args
)
892 MmsSource::NotifyFinished (guint32 reason
)
896 LOG_MMS ("MmsSource::NotifyFinished (%i)\n", reason
);
899 // The server has finished streaming and no more
900 // Data packets will be transmitted until the next Play request
902 } else if (reason
== 1) {
903 // The server has finished streaming the current playlist entry. Other playlist
904 // entries still remain to be streamed. The server will transmit a stream change packet
905 // when it switches to the next entry.
906 MmsPlaylistEntry
*entry
= GetCurrentReffed ();
907 entry
->NotifyFinished ();
915 MmsSource::SeekToPts (guint64 pts
)
918 MediaResult result
= true;
920 MmsDownloader
*mms_dl
;
922 LOG_PIPELINE_ASF ("MmsSource::SeekToPts (%" G_GUINT64_FORMAT
")\n", pts
);
924 dl
= GetDownloaderReffed ();
926 g_return_val_if_fail (dl
!= NULL
, MEDIA_FAIL
);
928 mms_dl
= GetMmsDownloader (dl
);
931 mms_dl
->SetRequestedPts (pts
);
933 result
= MEDIA_SUCCESS
;
944 MmsSource::GetMmsDownloader (Downloader
*dl
)
946 InternalDownloader
*idl
;
948 g_return_val_if_fail (dl
!= NULL
, NULL
);
950 idl
= dl
->GetInternalDownloader ();
955 if (idl
->GetObjectType () != Type::MMSDOWNLOADER
)
958 return (MmsDownloader
*) idl
;
962 MmsSource::CreateDemuxer (Media
*media
)
967 g_return_val_if_fail (demuxer
== NULL
, NULL
);
970 if (demuxer
== NULL
) {
971 result
= new MmsDemuxer (media
, this);
981 MmsSource::WritePacket (void *buf
, gint32 n
)
983 MmsPlaylistEntry
*entry
;
987 entry
= GetCurrentReffed ();
989 g_return_if_fail (entry
!= NULL
);
991 entry
->WritePacket (buf
, n
);
998 MmsPlaylistEntry
*entry
;
1001 entry
= GetCurrentReffed ();
1003 g_return_val_if_fail (entry
!= NULL
, NULL
);
1005 result
= entry
->Pop ();
1016 MmsPlaylistEntry
*entry
;
1022 entry
= GetCurrentReffed ();
1024 if (entry
== NULL
) {
1027 result
= entry
->Eof ();
1038 MmsPlaylistEntry::MmsPlaylistEntry (Media
*media
, MmsSource
*source
)
1039 : IMediaSource (Type::MMSPLAYLISTENTRY
, media
)
1046 playlist_gen_id
= NULL
;
1047 broadcast_id
= NULL
;
1048 features
= HttpStreamingFeaturesNone
;
1050 g_return_if_fail (parent
!= NULL
);
1055 MmsPlaylistEntry::Initialize ()
1057 return MEDIA_SUCCESS
;
1061 MmsPlaylistEntry::Dispose ()
1064 MmsSource
*mms_source
;
1065 ASFParser
*asf_parser
;
1066 IMediaDemuxer
*demux
;
1069 mms_source
= this->parent
;
1070 this->parent
= NULL
;
1071 asf_parser
= this->parser
;
1072 this->parser
= NULL
;
1073 demux
= this->demuxer
;
1074 this->demuxer
= NULL
;
1075 g_free (playlist_gen_id
);
1076 playlist_gen_id
= NULL
;
1077 g_free (broadcast_id
);
1078 broadcast_id
= NULL
;
1081 if (mms_source
!= NULL
)
1082 mms_source
->unref ();
1084 if (asf_parser
!= NULL
)
1085 asf_parser
->unref ();
1091 // This is a bit weird - in certain
1092 // we can end up with a circular dependency between
1093 // Media and MmsPlaylistEntry, where Media::Dispose
1094 // isn't called. So if Media::Dispose hasn't been
1095 // called, do it here, and only do it after our
1096 // instance copy of the media is cleared out to
1097 // prevent infinite loops.
1098 Media
*m
= GetMediaReffed ();
1100 IMediaSource::Dispose ();
1103 if (!m
->IsDisposed ())
1110 MmsPlaylistEntry::SeekToPts (guint64 pts
)
1112 MmsSource
*ms
= GetParentReffed ();
1114 ms
->SeekToPts (pts
);
1116 return MEDIA_SUCCESS
;
1118 fprintf (stderr
, "MmsPlaylistEntry::SeekToPts (%llu): Could not seek to pts, no parent.\n", pts
);
1124 MmsPlaylistEntry::NotifyFinished ()
1130 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate
, gint8 streams
[128])
1133 asf_file_properties
*properties
;
1134 gint32 audio_bitrates
[128];
1135 gint32 video_bitrates
[128];
1137 memset (audio_bitrates
, 0xff, 128 * 4);
1138 memset (video_bitrates
, 0xff, 128 * 4);
1139 memset (streams
, 0xff, 128);
1141 parser
= GetParserReffed ();
1143 g_return_if_fail (parser
!= NULL
);
1145 properties
= parser
->GetFileProperties ();
1147 g_return_if_fail (properties
!= NULL
);
1149 for (int i
= 1; i
< 127; i
++) {
1151 if (!parser
->IsValidStream (i
)) {
1152 streams
[i
] = -1; // inexistent
1155 streams
[i
] = 0; // disabled
1158 const asf_stream_properties
*stream_properties
= parser
->GetStream (current_stream
);
1159 const asf_extended_stream_properties
*extended_stream_properties
= parser
->GetExtendedStream (current_stream
);
1161 if (stream_properties
== NULL
) {
1162 printf ("MmsPlaylistEntry::GetSelectedStreams (): stream #%i doesn't have any stream properties.\n", current_stream
);
1166 if (stream_properties
->is_audio ()) {
1167 const WAVEFORMATEX
* wave
= stream_properties
->get_audio_data ();
1168 audio_bitrates
[current_stream
] = wave
->bytes_per_second
* 8;
1169 } else if (stream_properties
->is_video ()) {
1171 const asf_video_stream_data
* video_data
= stream_properties
->get_video_data ();
1172 const BITMAPINFOHEADER
* bmp
;
1174 if (extended_stream_properties
!= NULL
) {
1175 bit_rate
= extended_stream_properties
->data_bitrate
;
1176 } else if (video_data
!= NULL
) {
1177 bmp
= video_data
->get_bitmap_info_header ();
1179 bit_rate
= bmp
->image_width
*bmp
->image_height
;
1183 video_bitrates
[current_stream
] = bit_rate
;
1184 } else if (stream_properties
->is_command ()) {
1185 // we select all marker streams
1186 streams
[current_stream
] = 1;
1190 // select the video stream
1191 int video_stream
= 0;
1194 for (int i
= 0; i
< 128; i
++) {
1195 int stream_rate
= video_bitrates
[i
];
1197 if (stream_rate
== -1)
1200 if (video_rate
== 0) {
1201 video_rate
= stream_rate
;
1205 if (stream_rate
> video_rate
&& stream_rate
< (max_bitrate
* VIDEO_BITRATE_PERCENTAGE
)) {
1206 video_rate
= stream_rate
;
1210 streams
[video_stream
] = 1; // selected
1211 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected video stream %i of rate %i\n", video_stream
, video_rate
);
1214 // select audio stream
1215 int audio_stream
= 0;
1218 for (int i
= 0; i
< 128; i
++) {
1219 int stream_rate
= audio_bitrates
[i
];
1221 if (stream_rate
== -1)
1224 if (audio_rate
== 0) {
1225 audio_rate
= stream_rate
;
1229 if (stream_rate
> audio_rate
&& stream_rate
< (max_bitrate
* AUDIO_BITRATE_PERCENTAGE
)) {
1230 audio_rate
= stream_rate
;
1234 streams
[audio_stream
] = 1; // selected
1235 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream
, audio_rate
);
1241 MmsPlaylistEntry::IsHeaderParsed ()
1245 result
= parser
!= NULL
;
1251 MmsPlaylistEntry::GetParserReffed ()
1266 MmsPlaylistEntry::GetParentReffed ()
1281 MmsPlaylistEntry::CreateDemuxer (Media
*media
)
1285 ASFParser
*asf_parser
;
1287 asf_parser
= GetParserReffed ();
1289 g_return_val_if_fail (media
!= NULL
, NULL
);
1290 g_return_val_if_fail (asf_parser
!= NULL
, NULL
);
1291 g_return_val_if_fail (demuxer
== NULL
, NULL
);
1293 result
= new ASFDemuxer (media
, this);
1294 result
->SetParser (asf_parser
);
1297 if (demuxer
!= NULL
)
1303 asf_parser
->unref ();
1309 MmsPlaylistEntry::SetPlaylistGenId (const char *value
)
1313 g_free (playlist_gen_id
);
1314 playlist_gen_id
= g_strdup (value
);
1319 MmsPlaylistEntry::GetPlaylistGenId ()
1324 result
= g_strdup (playlist_gen_id
);
1330 MmsPlaylistEntry::SetBroadcastId (const char *value
)
1334 g_free (broadcast_id
);
1335 broadcast_id
= g_strdup (value
);
1340 MmsPlaylistEntry::GetBroadcastId ()
1345 result
= g_strdup (broadcast_id
);
1351 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value
)
1356 HttpStreamingFeatures
1357 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1363 MmsPlaylistEntry::AddEntry ()
1367 Media
*media
= GetMediaReffed ();
1369 PlaylistEntry
*entry
;
1370 MmsDemuxer
*mms_demuxer
= NULL
;
1372 g_return_if_fail (media
!= NULL
);
1377 mms_demuxer
= parent
->GetDemuxerReffed ();
1379 if (mms_demuxer
== NULL
)
1382 playlist
= mms_demuxer
->GetPlaylist ();
1384 if (playlist
== NULL
)
1387 entry
= new PlaylistEntry (playlist
);
1388 entry
->SetIsLive (features
& HttpStreamingBroadcast
);
1390 playlist
->AddEntry (entry
);
1392 entry
->InitializeWithSource (this);
1398 mms_demuxer
->unref ();
1402 MmsPlaylistEntry::ParseHeader (void *buffer
, gint32 size
)
1406 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer
, size
);
1409 MemorySource
*asf_src
= NULL
;
1411 ASFParser
*asf_parser
;
1413 // this method shouldn't get called more than once
1414 g_return_val_if_fail (parser
== NULL
, MEDIA_FAIL
);
1416 media
= GetMediaReffed ();
1417 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
1419 media
->ReportDownloadProgress (1.0);
1421 asf_src
= new MemorySource (media
, buffer
, size
, 0, false);
1422 asf_parser
= new ASFParser (asf_src
, media
);
1423 result
= asf_parser
->ReadHeader ();
1427 if (MEDIA_SUCCEEDED (result
)) {
1431 parser
= asf_parser
;
1435 asf_parser
->unref ();
1442 MmsPlaylistEntry::Pop ()
1445 //LOG_MMS ("MmsSource::Pop (), there are %i packets in the queue, of a total of %lld packets written.\n", queue.Length (), write_count);
1448 ASFPacket
*result
= NULL
;
1452 node
= (QueueNode
*) queue
.Pop ();
1455 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1459 parser
= GetParserReffed ();
1461 if (node
->packet
== NULL
) {
1462 if (parser
== NULL
) {
1463 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1466 node
->packet
= new ASFPacket (parser
, node
->source
);
1467 if (!MEDIA_SUCCEEDED (node
->packet
->Read ())) {
1468 LOG_PIPELINE_ASF ("MmsSource::Pop (): Error while parsing packet, getting a new packet\n");
1474 result
= node
->packet
;
1483 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
);
1489 MmsPlaylistEntry::IsFinished ()
1492 return parent
->IsFinished (); // REMOVE
1493 MmsSource
*src
= GetParentReffed ();
1495 g_return_val_if_fail (src
!= NULL
, true);
1497 result
= src
->IsFinished ();
1504 MmsPlaylistEntry::WritePacket (void *buf
, gint32 n
)
1508 ASFParser
*asf_parser
;
1511 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %lld\n", buf
, n
, write_count
+ 1);
1514 media
= GetMediaReffed ();
1516 g_return_if_fail (media
!= NULL
);
1520 asf_parser
= GetParserReffed ();
1522 if (asf_parser
!= NULL
) {
1523 src
= new MemorySource (media
, buf
, n
, 0, false);
1524 packet
= new ASFPacket (asf_parser
, src
);
1525 if (!MEDIA_SUCCEEDED (packet
->Read ())) {
1526 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i): Error while parsing packet, dropping packet.\n", buf
, n
);
1528 queue
.Push (new QueueNode (packet
));
1533 src
= new MemorySource (media
, g_memdup (buf
, n
), n
, 0);
1534 queue
.Push (new QueueNode (src
));
1539 asf_parser
->unref ();
1546 * MmsPlaylistEntry::QueueNode
1549 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource
*source
)
1553 this->source
= source
;
1557 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket
*packet
)
1561 this->packet
= packet
;
1565 MmsPlaylistEntry::QueueNode::~QueueNode ()
1577 MmsDemuxer::MmsDemuxer (Media
*media
, MmsSource
*source
)
1578 : IMediaDemuxer (Type::MMSDEMUXER
, media
, source
)
1581 mms_source
= source
;
1587 MmsDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
1589 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream
);
1593 MmsDemuxer::OpenDemuxerAsyncInternal ()
1598 LOG_MMS ("MmsDemuxer::OpenDemuxerAsyncInternal ().\n");
1600 media
= GetMediaReffed ();
1601 root
= media
? media
->GetPlaylistRoot () : NULL
;
1603 g_return_if_fail (playlist
== NULL
);
1604 g_return_if_fail (media
!= NULL
);
1605 g_return_if_fail (root
!= NULL
);
1607 playlist
= new Playlist (root
, mms_source
);
1608 ReportOpenDemuxerCompleted ();
1613 MmsDemuxer::SeekInternal (guint64 pts
)
1615 g_warning ("MmsDemuxer::SeekInternal (%lld): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", pts
);
1616 print_stack_trace ();
1622 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime
)
1624 printf ("MmsDemuxer::SeekAsyncInternal (%llu): Not implemented.\n", seekToTime
);
1628 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*stream
)
1630 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream
);
1634 MmsDemuxer::Dispose ()
1640 pl
= this->playlist
;
1641 this->playlist
= NULL
;
1642 src
= this->mms_source
;
1643 this->mms_source
= NULL
;
1652 IMediaDemuxer::Dispose ();