in plugin/:
[moon.git] / src / pipeline-asf.cpp
blob94b4254e54461680b1608327047b440a4afa8cfc
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * pipeline-asf.cpp: ASF related parts of the pipeline
5 * Contact:
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.
15 #include <config.h>
17 #include "pipeline-asf.h"
18 #include "mms-downloader.h"
19 #include "debug.h"
20 #include "playlist.h"
22 #define VIDEO_BITRATE_PERCENTAGE 75
23 #define AUDIO_BITRATE_PERCENTAGE 25
26 * ASFDemuxer
29 ASFDemuxer::ASFDemuxer (Media *media, IMediaSource *source) : IMediaDemuxer (Type::ASFDEMUXER, media, source)
31 stream_to_asf_index = NULL;
32 reader = NULL;
33 parser = NULL;
36 void
37 ASFDemuxer::Dispose ()
39 g_free (stream_to_asf_index);
40 stream_to_asf_index = NULL;
42 delete reader;
43 reader = NULL;
45 if (parser) {
46 parser->Dispose ();
47 parser->unref ();
48 parser = NULL;
51 IMediaDemuxer::Dispose ();
54 void
55 ASFDemuxer::UpdateSelected (IMediaStream *stream)
57 if (reader)
58 reader->SelectStream (stream_to_asf_index [stream->index], stream->GetSelected ());
60 IMediaDemuxer::UpdateSelected (stream);
63 void
64 ASFDemuxer::SeekAsyncInternal (guint64 pts)
66 MediaResult result;
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) {
78 EnqueueSeek (pts);
79 } else {
80 ReportErrorOccurred (result);
84 void
85 ASFDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *stream)
87 LOG_PIPELINE ("ASFDemuxer::SwitchMediaStreamAsyncInternal (%p). TODO.\n", stream);
90 void
91 ASFDemuxer::ReadMarkers ()
94 We can get markers from several places:
95 - The header of the file, read before starting to play
96 - As a SCRIPT_COMMAND
97 - As a MARKER
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
106 MediaMarker *marker;
107 Media *media = GetMediaReffed ();
109 g_return_if_fail (media != NULL);
111 // Read the markers (if any)
112 List *markers = media->GetMarkers ();
113 const char *type;
114 guint64 pts;
115 guint64 preroll_pts = MilliSeconds_ToPts (parser->GetFileProperties ()->preroll);
116 char *text;
117 int i = -1;
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");
129 goto cleanup;
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];
142 else
143 type = "";
145 marker = new MediaMarker (type, text, pts);
146 markers->Append (new MediaMarker::Node (marker));
147 marker->unref ();
149 //printf ("MediaElement::ReadMarkers () Added script command at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, type);
151 g_free (text);
155 // Read the MARKERs
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));
169 marker->unref ();
171 //printf ("MediaElement::ReadMarkers () Added marker at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, "Name");
173 g_free (text);
178 cleanup:
179 g_strfreev (command_types);
180 g_free (commands);
181 media->unref ();
184 void
185 ASFDemuxer::SetParser (ASFParser *parser)
187 if (this->parser)
188 this->parser->unref ();
189 this->parser = parser;
190 if (this->parser) {
191 this->parser->ref ();
192 this->parser->SetSource (source);
196 void
197 ASFDemuxer::OpenDemuxerAsyncInternal ()
199 MediaResult result;
201 LOG_PIPELINE ("ASFDemuxer::OpenDemuxerAsyncInternal ()\n");
203 result = Open ();
205 if (MEDIA_SUCCEEDED (result)) {
206 ReportOpenDemuxerCompleted ();
207 } else if (result == MEDIA_NOT_ENOUGH_DATA) {
208 EnqueueOpen ();
209 } else {
210 ReportErrorOccurred (result);
214 MediaResult
215 ASFDemuxer::Open ()
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;
224 int count;
226 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
228 if (parser != NULL) {
229 asf_parser = parser;
230 } else {
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");
240 } else {
241 Media::Warning (MEDIA_INVALID_MEDIA, "asf_parser->ReadHeader () failed:");
242 Media::Warning (MEDIA_FAIL, "%s", asf_parser->GetLastErrorStr ());
244 goto failure;
247 // Count the number of streams
248 for (int i = 1; i <= 127; i++) {
249 if (asf_parser->IsValidStream (i))
250 stream_count++;
253 current_stream = 1;
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))
262 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.");
267 goto failure;
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.");
276 goto failure;
279 if (stream_properties->is_audio ()) {
280 AudioStream* audio = new AudioStream (media);
282 stream = audio;
284 const WAVEFORMATEX* wave = stream_properties->get_audio_data ();
285 if (wave == NULL) {
286 result = MEDIA_INVALID_STREAM;
287 Media::Warning (result, "Couldn't find audio data in the file.");
288 goto failure;
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);
317 stream = video;
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);
326 if (bmp != NULL) {
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);
336 goto failure;
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);
345 } else {
346 video->extra_data = NULL;
349 if (aesp != NULL) {
350 video->bit_rate = aesp->data_bitrate;
351 video->pts_per_frame = aesp->average_time_per_frame;
352 } else {
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);
359 stream = marker;
360 stream->codec = g_strdup ("asf-marker");
361 } else {
362 // Unknown stream, don't include it in the count since it's NULL
363 stream_count--;
364 // also adjust indexes so we don't create a hole in the streams array
365 count--;
366 i--;
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;
381 default:
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 : ' ');
387 break;
390 streams [i] = stream;
391 stream->index = i;
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;
398 current_stream++;
402 if (!MEDIA_SUCCEEDED (result)) {
403 goto failure;
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);
416 ReadMarkers ();
418 media->unref ();
420 return result;
422 failure:
423 asf_parser->unref ();
424 asf_parser = NULL;
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 ();
433 streams [i] = NULL;
436 g_free (streams);
437 streams = NULL;
440 media->unref ();
442 return result;
445 IMediaStream *
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);
452 return NULL;
455 MediaResult
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;
465 void
466 ASFDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
468 //printf ("ASFDemuxer::ReadFrame (%p).\n", frame);
469 ASFFrameReader *reader = NULL;
470 MediaFrame *frame;
471 MediaResult result;
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);
483 return;
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.
491 closure->unref ();
492 media->unref ();
493 return;
496 if (!MEDIA_SUCCEEDED (result)) {
497 ReportErrorOccurred ("Error while advancing to the next frame (%d)");
498 return;
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.");
511 return;
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.");
520 return;
523 frame->AddState (MediaFrameDemuxed);
525 ReportGetFrameCompleted (frame);
527 frame->unref ();
531 * ASFMarkerDecoder
534 ASFMarkerDecoder::ASFMarkerDecoder (Media *media, IMediaStream *stream)
535 : IMediaDecoder (Type::ASFMARKERDECODER, media, stream)
539 void
540 ASFMarkerDecoder::OpenDecoderAsyncInternal ()
542 ReportOpenDecoderCompleted ();
545 void
546 ASFMarkerDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
548 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame ()\n");
550 MediaResult result;
551 char *text;
552 char *type;
553 gunichar2 *data;
554 gunichar2 *uni_type = NULL;
555 gunichar2 *uni_text = NULL;
556 int text_length = 0;
557 int type_length = 0;
558 guint32 size = 0;
560 if (frame->buflen % 2 != 0 || frame->buflen == 0 || frame->buffer == NULL) {
561 ReportErrorOccurred (MEDIA_CORRUPTED_MEDIA);
562 return;
565 data = (gunichar2 *) frame->buffer;
566 uni_type = data;
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.
572 int null_count = 0;
574 for (guint32 i = 0; i < (size / sizeof (gunichar2)); i++) {
575 if (uni_text == NULL) {
576 type_length++;
577 } else {
578 text_length++;
580 if (*(data + i) == 0) {
581 null_count++;
582 if (uni_text == NULL) {
583 uni_text = data + i + 1;
584 } else {
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);
598 g_free (text);
599 g_free (type);
600 result = MEDIA_SUCCESS;
601 } else {
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);
608 } else {
609 ReportErrorOccurred (result);
614 * ASFMarkerDecoderInfo
617 IMediaDecoder *
618 ASFMarkerDecoderInfo::Create (Media *media, IMediaStream *stream)
620 return new ASFMarkerDecoder (media, stream);
623 bool
624 ASFMarkerDecoderInfo::Supports (const char *codec)
626 return !strcmp (codec, "asf-marker");
629 const char *
630 ASFMarkerDecoderInfo::GetName ()
632 return "ASFMarkerDecoder";
636 * ASFDemuxerInfo
639 MediaResult
640 ASFDemuxerInfo::Supports (IMediaSource *source)
642 guint8 buffer[16];
643 bool result;
645 LOG_PIPELINE_ASF ("ASFDemuxerInfo::Supports (%p) pos: %lld, avail pos: %lld\n", source, source->GetPosition (), source->GetLastAvailablePosition ());
647 #if DEBUG
648 bool eof = false;
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);
653 #endif
655 if (!source->Peek (buffer, 16)) {
656 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Peek failed.\n", source);
657 return MEDIA_FAIL;
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;
668 IMediaDemuxer *
669 ASFDemuxerInfo::Create (Media *media, IMediaSource *source)
671 return new ASFDemuxer (media, source);
675 * MmsSource
678 MmsSource::MmsSource (Media *media, Downloader *downloader)
679 : IMediaSource (Type::MMSSOURCE, media)
681 finished = false;
682 write_count = 0;
683 this->downloader = NULL;
684 current = NULL;
685 demuxer = 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
697 void
698 MmsSource::Dispose ()
700 // thread safe method
702 MmsPlaylistEntry *entry;
703 IMediaDemuxer *demux;
704 Downloader *dl;
706 // don't lock during unref, only while nulling out the local field
707 Lock ();
708 entry = this->current;
709 this->current = NULL;
710 dl = this->downloader;
711 this->downloader = NULL;
712 demux = this->demuxer;
713 this->demuxer = NULL;
714 Unlock ();
716 if (dl) {
717 dl->RemoveAllHandlers (this);
718 dl->unref ();
721 if (entry)
722 entry->unref ();
724 if (demux)
725 demux->unref ();
727 IMediaSource::Dispose ();
730 MediaResult
731 MmsSource::Initialize ()
733 Downloader *dl;
734 MmsDownloader *mms_dl;
736 VERIFY_MAIN_THREAD;
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);
749 } else {
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);
755 dl->Send ();
757 dl->unref ();
759 return MEDIA_SUCCESS;
762 MmsDemuxer *
763 MmsSource::GetDemuxerReffed ()
765 MmsDemuxer *result;
766 Lock ();
767 result = demuxer;
768 if (result)
769 result->ref ();
770 Unlock ();
771 return result;
774 Downloader *
775 MmsSource::GetDownloaderReffed ()
777 Downloader *result;
778 Lock ();
779 result = downloader;
780 if (downloader)
781 downloader->ref ();
782 Unlock ();
783 return result;
786 MmsPlaylistEntry *
787 MmsSource::GetCurrentReffed ()
789 MmsPlaylistEntry *result;
791 // Thread safe
793 Lock ();
794 result = current;
795 if (result)
796 result->ref ();
797 Unlock ();
799 return result;
802 void
803 MmsSource::ReportDownloadFailure ()
805 Media *media;
807 LOG_MMS ("MmsSource::ReportDownloadFailure ()\n");
808 VERIFY_MAIN_THREAD;
810 media = GetMediaReffed ();
812 g_return_if_fail (media != NULL);
814 media->ReportErrorOccurred ("MmsDownloader failed");
815 media->unref ();
818 void
819 MmsSource::ReportStreamChange (gint32 reason)
821 Media *media;
822 PlaylistRoot *root;
823 Media *entry_media;
825 LOG_MMS ("MmsSource::ReportStreamChange (reason: %i)\n", reason);
827 VERIFY_MAIN_THREAD;
829 media = GetMediaReffed ();
831 g_return_if_fail (media != NULL);
833 root = media->GetPlaylistRoot ();
835 g_return_if_fail (root != NULL);
837 Lock ();
838 if (current != NULL) {
839 current->NotifyFinished ();
840 current->unref ();
843 entry_media = new Media (root);
844 current = new MmsPlaylistEntry (entry_media, this);
845 entry_media->unref ();
846 Unlock ();
848 media->unref ();
851 void
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);
858 VERIFY_MAIN_THREAD;
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);
868 entry->unref ();
871 void
872 MmsSource::DownloadFailedHandler (Downloader *dl, EventArgs *args)
874 Media *media = GetMediaReffed ();
875 ErrorEventArgs *eea;
876 VERIFY_MAIN_THREAD;
878 g_return_if_fail (media != NULL);
879 eea = new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR"));
880 media->RetryHttp (eea);
881 media->unref ();
882 eea->unref ();
885 void
886 MmsSource::DownloadCompleteHandler (Downloader *dl, EventArgs *args)
888 VERIFY_MAIN_THREAD;
891 void
892 MmsSource::NotifyFinished (guint32 reason)
894 VERIFY_MAIN_THREAD;
896 LOG_MMS ("MmsSource::NotifyFinished (%i)\n", reason);
898 if (reason == 0) {
899 // The server has finished streaming and no more
900 // Data packets will be transmitted until the next Play request
901 finished = true;
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 ();
908 entry->unref ();
909 } else {
910 // ?
914 MediaResult
915 MmsSource::SeekToPts (guint64 pts)
917 // thread safe
918 MediaResult result = true;
919 Downloader *dl;
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);
930 if (mms_dl) {
931 mms_dl->SetRequestedPts (pts);
932 finished = false;
933 result = MEDIA_SUCCESS;
934 } else {
935 result = MEDIA_FAIL;
938 dl->unref ();
940 return result;
943 MmsDownloader *
944 MmsSource::GetMmsDownloader (Downloader *dl)
946 InternalDownloader *idl;
948 g_return_val_if_fail (dl != NULL, NULL);
950 idl = dl->GetInternalDownloader ();
952 if (idl == NULL)
953 return NULL;
955 if (idl->GetObjectType () != Type::MMSDOWNLOADER)
956 return NULL;
958 return (MmsDownloader *) idl;
961 IMediaDemuxer *
962 MmsSource::CreateDemuxer (Media *media)
964 // thread safe
965 MmsDemuxer *result;
967 g_return_val_if_fail (demuxer == NULL, NULL);
969 Lock ();
970 if (demuxer == NULL) {
971 result = new MmsDemuxer (media, this);
972 demuxer = result;
973 demuxer->ref ();
975 Unlock ();
977 return result;
980 void
981 MmsSource::WritePacket (void *buf, gint32 n)
983 MmsPlaylistEntry *entry;
985 VERIFY_MAIN_THREAD;
987 entry = GetCurrentReffed ();
989 g_return_if_fail (entry != NULL);
991 entry->WritePacket (buf, n);
992 entry->unref ();
995 ASFPacket *
996 MmsSource::Pop ()
998 MmsPlaylistEntry *entry;
999 ASFPacket *result;
1001 entry = GetCurrentReffed ();
1003 g_return_val_if_fail (entry != NULL, NULL);
1005 result = entry->Pop ();
1007 entry->unref ();
1009 return result;
1012 bool
1013 MmsSource::Eof ()
1015 // thread safe
1016 MmsPlaylistEntry *entry;
1017 bool result;
1019 if (!finished)
1020 return false;
1022 entry = GetCurrentReffed ();
1024 if (entry == NULL) {
1025 result = true;
1026 } else {
1027 result = entry->Eof ();
1028 entry->unref ();
1031 return result;
1035 * MmsPlaylistEntry
1038 MmsPlaylistEntry::MmsPlaylistEntry (Media *media, MmsSource *source)
1039 : IMediaSource (Type::MMSPLAYLISTENTRY, media)
1041 finished = false;
1042 parent = source;
1043 parser = NULL;
1044 write_count = 0;
1045 demuxer = NULL;
1046 playlist_gen_id = NULL;
1047 broadcast_id = NULL;
1048 features = HttpStreamingFeaturesNone;
1050 g_return_if_fail (parent != NULL);
1051 parent->ref ();
1054 MediaResult
1055 MmsPlaylistEntry::Initialize ()
1057 return MEDIA_SUCCESS;
1060 void
1061 MmsPlaylistEntry::Dispose ()
1063 // thread safe
1064 MmsSource *mms_source;
1065 ASFParser *asf_parser;
1066 IMediaDemuxer *demux;
1068 Lock ();
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;
1079 Unlock ();
1081 if (mms_source != NULL)
1082 mms_source->unref ();
1084 if (asf_parser != NULL)
1085 asf_parser->unref ();
1087 if (demux != NULL)
1088 demux->unref ();
1090 queue.Clear (true);
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 ();
1102 if (m != NULL) {
1103 if (!m->IsDisposed ())
1104 m->Dispose ();
1105 m->unref ();
1109 MediaResult
1110 MmsPlaylistEntry::SeekToPts (guint64 pts)
1112 MmsSource *ms = GetParentReffed ();
1113 if (ms) {
1114 ms->SeekToPts (pts);
1115 ms->unref ();
1116 return MEDIA_SUCCESS;
1117 } else {
1118 fprintf (stderr, "MmsPlaylistEntry::SeekToPts (%llu): Could not seek to pts, no parent.\n", pts);
1119 return MEDIA_FAIL;
1123 void
1124 MmsPlaylistEntry::NotifyFinished ()
1126 finished = true;
1129 void
1130 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate, gint8 streams [128])
1132 ASFParser *parser;
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++) {
1150 int current_stream;
1151 if (!parser->IsValidStream (i)) {
1152 streams [i] = -1; // inexistent
1153 continue;
1155 streams [i] = 0; // disabled
1156 current_stream = i;
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);
1163 continue;
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 ()) {
1170 int bit_rate = 0;
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 ();
1178 if (bmp != NULL) {
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;
1192 int video_rate = 0;
1194 for (int i = 0; i < 128; i++) {
1195 int stream_rate = video_bitrates [i];
1197 if (stream_rate == -1)
1198 continue;
1200 if (video_rate == 0) {
1201 video_rate = stream_rate;
1202 video_stream = i;
1205 if (stream_rate > video_rate && stream_rate < (max_bitrate * VIDEO_BITRATE_PERCENTAGE)) {
1206 video_rate = stream_rate;
1207 video_stream = i;
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;
1216 int audio_rate = 0;
1218 for (int i = 0; i < 128; i++) {
1219 int stream_rate = audio_bitrates [i];
1221 if (stream_rate == -1)
1222 continue;
1224 if (audio_rate == 0) {
1225 audio_rate = stream_rate;
1226 audio_stream = i;
1229 if (stream_rate > audio_rate && stream_rate < (max_bitrate * AUDIO_BITRATE_PERCENTAGE)) {
1230 audio_rate = stream_rate;
1231 audio_stream = i;
1234 streams [audio_stream] = 1; // selected
1235 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream, audio_rate);
1237 parser->unref ();
1240 bool
1241 MmsPlaylistEntry::IsHeaderParsed ()
1243 bool result;
1244 Lock ();
1245 result = parser != NULL;
1246 Unlock ();
1247 return result;
1250 ASFParser *
1251 MmsPlaylistEntry::GetParserReffed ()
1253 // thread safe
1254 ASFParser *result;
1256 Lock ();
1257 result = parser;
1258 if (result)
1259 result->ref ();
1260 Unlock ();
1262 return result;
1265 MmsSource *
1266 MmsPlaylistEntry::GetParentReffed ()
1268 // thread safe
1269 MmsSource *result;
1271 Lock ();
1272 result = parent;
1273 if (result)
1274 result->ref ();
1275 Unlock ();
1277 return result;
1280 IMediaDemuxer *
1281 MmsPlaylistEntry::CreateDemuxer (Media *media)
1283 // thread safe
1284 ASFDemuxer *result;
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);
1296 Lock ();
1297 if (demuxer != NULL)
1298 demuxer->unref ();
1299 demuxer = result;
1300 demuxer->ref ();
1301 Unlock ();
1303 asf_parser->unref ();
1305 return result;
1308 void
1309 MmsPlaylistEntry::SetPlaylistGenId (const char *value)
1311 // thread safe
1312 Lock ();
1313 g_free (playlist_gen_id);
1314 playlist_gen_id = g_strdup (value);
1315 Unlock ();
1318 char *
1319 MmsPlaylistEntry::GetPlaylistGenId ()
1321 // thread safe
1322 char *result;
1323 Lock ();
1324 result = g_strdup (playlist_gen_id);
1325 Unlock ();
1326 return result;
1329 void
1330 MmsPlaylistEntry::SetBroadcastId (const char *value)
1332 // thread safe
1333 Lock ();
1334 g_free (broadcast_id);
1335 broadcast_id = g_strdup (value);
1336 Unlock ();
1339 char *
1340 MmsPlaylistEntry::GetBroadcastId ()
1342 // thread safe
1343 char *result;
1344 Lock ();
1345 result = g_strdup (broadcast_id);
1346 Unlock ();
1347 return result;
1350 void
1351 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value)
1353 features = value;
1356 HttpStreamingFeatures
1357 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1359 return features;
1362 void
1363 MmsPlaylistEntry::AddEntry ()
1365 VERIFY_MAIN_THREAD;
1367 Media *media = GetMediaReffed ();
1368 Playlist *playlist;
1369 PlaylistEntry *entry;
1370 MmsDemuxer *mms_demuxer = NULL;
1372 g_return_if_fail (media != NULL);
1374 if (parent == NULL)
1375 goto cleanup;
1377 mms_demuxer = parent->GetDemuxerReffed ();
1379 if (mms_demuxer == NULL)
1380 goto cleanup;
1382 playlist = mms_demuxer->GetPlaylist ();
1384 if (playlist == NULL)
1385 goto cleanup;
1387 entry = new PlaylistEntry (playlist);
1388 entry->SetIsLive (features & HttpStreamingBroadcast);
1390 playlist->AddEntry (entry);
1392 entry->InitializeWithSource (this);
1394 cleanup:
1395 if (media)
1396 media->unref ();
1397 if (mms_demuxer)
1398 mms_demuxer->unref ();
1401 MediaResult
1402 MmsPlaylistEntry::ParseHeader (void *buffer, gint32 size)
1404 VERIFY_MAIN_THREAD;
1406 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer, size);
1408 MediaResult result;
1409 MemorySource *asf_src = NULL;
1410 Media *media;
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 ();
1424 asf_src->unref ();
1425 media->unref ();
1427 if (MEDIA_SUCCEEDED (result)) {
1428 Lock ();
1429 if (parser)
1430 parser->unref ();
1431 parser = asf_parser;
1432 Unlock ();
1433 AddEntry ();
1434 } else {
1435 asf_parser->unref ();
1438 return result;
1441 ASFPacket *
1442 MmsPlaylistEntry::Pop ()
1444 // thread safe
1445 //LOG_MMS ("MmsSource::Pop (), there are %i packets in the queue, of a total of %lld packets written.\n", queue.Length (), write_count);
1447 QueueNode *node;
1448 ASFPacket *result = NULL;
1449 ASFParser *parser;
1451 trynext:
1452 node = (QueueNode *) queue.Pop ();
1454 if (node == NULL) {
1455 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1456 return NULL;
1459 parser = GetParserReffed ();
1461 if (node->packet == NULL) {
1462 if (parser == NULL) {
1463 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1464 goto cleanup;
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");
1469 delete node;
1470 goto trynext;
1474 result = node->packet;
1475 result->ref ();
1477 cleanup:
1478 delete node;
1480 if (parser)
1481 parser->unref ();
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);
1485 return result;
1488 bool
1489 MmsPlaylistEntry::IsFinished ()
1491 bool result;
1492 return parent->IsFinished (); // REMOVE
1493 MmsSource *src = GetParentReffed ();
1495 g_return_val_if_fail (src != NULL, true);
1497 result = src->IsFinished ();
1498 src->unref ();
1500 return result;
1503 void
1504 MmsPlaylistEntry::WritePacket (void *buf, gint32 n)
1506 MemorySource *src;
1507 ASFPacket *packet;
1508 ASFParser *asf_parser;
1509 Media *media;
1511 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %lld\n", buf, n, write_count + 1);
1512 VERIFY_MAIN_THREAD;
1514 media = GetMediaReffed ();
1516 g_return_if_fail (media != NULL);
1518 write_count++;
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);
1527 } else {
1528 queue.Push (new QueueNode (packet));
1530 packet->unref ();
1531 src->unref ();
1532 } else {
1533 src = new MemorySource (media, g_memdup (buf, n), n, 0);
1534 queue.Push (new QueueNode (src));
1535 src->unref ();
1538 if (asf_parser)
1539 asf_parser->unref ();
1541 if (media)
1542 media->unref ();
1546 * MmsPlaylistEntry::QueueNode
1549 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource *source)
1551 if (source)
1552 source->ref ();
1553 this->source = source;
1554 packet = NULL;
1557 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket *packet)
1559 if (packet)
1560 packet->ref ();
1561 this->packet = packet;
1562 source = NULL;
1565 MmsPlaylistEntry::QueueNode::~QueueNode ()
1567 if (packet)
1568 packet->unref ();
1569 if (source)
1570 source->unref ();
1574 * MmsDemuxer
1577 MmsDemuxer::MmsDemuxer (Media *media, MmsSource *source)
1578 : IMediaDemuxer (Type::MMSDEMUXER, media, source)
1580 playlist = NULL;
1581 mms_source = source;
1582 if (mms_source)
1583 mms_source->ref ();
1586 void
1587 MmsDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
1589 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream);
1592 void
1593 MmsDemuxer::OpenDemuxerAsyncInternal ()
1595 PlaylistRoot *root;
1596 Media *media;
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 ();
1609 media->unref ();
1612 MediaResult
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 ();
1618 return MEDIA_FAIL;
1621 void
1622 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime)
1624 printf ("MmsDemuxer::SeekAsyncInternal (%llu): Not implemented.\n", seekToTime);
1627 void
1628 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *stream)
1630 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream);
1633 void
1634 MmsDemuxer::Dispose ()
1636 Playlist *pl;
1637 MmsSource *src;
1639 mutex.Lock ();
1640 pl = this->playlist;
1641 this->playlist = NULL;
1642 src = this->mms_source;
1643 this->mms_source = NULL;
1644 mutex.Unlock ();
1646 if (pl)
1647 pl->unref ();
1649 if (src)
1650 src->unref ();
1652 IMediaDemuxer::Dispose ();