add the 2.1-bootstrap dir to MONO_PATH when running smcs
[moon.git] / src / pipeline-asf.cpp
blob6176a736534b3838b78f83df0eb881917e02994b
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 (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 = this->reader->GetFrameReader (stream_to_asf_index [stream->index]);
470 MediaFrame *frame;
471 MediaResult result;
473 g_return_if_fail (reader != NULL);
475 result = reader->Advance ();
477 if (result == MEDIA_NO_MORE_DATA) {
478 ReportGetFrameCompleted (NULL);
479 return;
482 if (result == MEDIA_BUFFER_UNDERFLOW || result == MEDIA_NOT_ENOUGH_DATA) {
483 Media *media = GetMediaReffed ();
484 g_return_if_fail (media != NULL);
485 MediaClosure *closure = new MediaGetFrameClosure (media, GetFrameCallback, this, stream);
486 media->EnqueueWork (closure, false); // TODO: use a timeout here, no need to try again immediately.
487 closure->unref ();
488 media->unref ();
489 return;
492 if (!MEDIA_SUCCEEDED (result)) {
493 ReportErrorOccurred ("Error while advancing to the next frame (%d)");
494 return;
497 frame = new MediaFrame (stream);
498 frame->pts = reader->Pts ();
499 //frame->duration = reader->Duration ();
500 if (reader->IsKeyFrame ())
501 frame->AddState (MediaFrameKeyFrame);
502 frame->buflen = reader->Size ();
503 frame->buffer = (guint8 *) g_try_malloc (frame->buflen + frame->stream->min_padding);
505 if (frame->buffer == NULL) {
506 ReportErrorOccurred ( "Could not allocate memory for next frame.");
507 return;
510 //printf ("ASFDemuxer::ReadFrame (%p), min_padding = %i\n", frame, frame->stream->min_padding);
511 if (frame->stream->min_padding > 0)
512 memset (frame->buffer + frame->buflen, 0, frame->stream->min_padding);
514 if (!reader->Write (frame->buffer)) {
515 ReportErrorOccurred ("Error while copying the next frame.");
516 return;
519 frame->AddState (MediaFrameDemuxed);
521 ReportGetFrameCompleted (frame);
523 frame->unref ();
527 * ASFMarkerDecoder
530 ASFMarkerDecoder::ASFMarkerDecoder (Media *media, IMediaStream *stream)
531 : IMediaDecoder (Type::ASFMARKERDECODER, media, stream)
535 void
536 ASFMarkerDecoder::OpenDecoderAsyncInternal ()
538 ReportOpenDecoderCompleted ();
541 void
542 ASFMarkerDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
544 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame ()\n");
546 MediaResult result;
547 char *text;
548 char *type;
549 gunichar2 *data;
550 gunichar2 *uni_type = NULL;
551 gunichar2 *uni_text = NULL;
552 int text_length = 0;
553 int type_length = 0;
554 guint32 size = 0;
556 if (frame->buflen % 2 != 0 || frame->buflen == 0 || frame->buffer == NULL) {
557 ReportErrorOccurred (MEDIA_CORRUPTED_MEDIA);
558 return;
561 data = (gunichar2 *) frame->buffer;
562 uni_type = data;
563 size = frame->buflen;
565 // the data is two arrays of WCHARs (type and text), null terminated.
566 // loop through the data, counting characters and null characters
567 // there should be at least two null characters.
568 int null_count = 0;
570 for (guint32 i = 0; i < (size / sizeof (gunichar2)); i++) {
571 if (uni_text == NULL) {
572 type_length++;
573 } else {
574 text_length++;
576 if (*(data + i) == 0) {
577 null_count++;
578 if (uni_text == NULL) {
579 uni_text = data + i + 1;
580 } else {
581 break; // Found at least two nulls
586 if (null_count >= 2) {
587 text = wchar_to_utf8 (uni_text, text_length);
588 type = wchar_to_utf8 (uni_type, type_length);
590 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): sending script command type: '%s', text: '%s', pts: '%" G_GUINT64_FORMAT "'.\n", type, text, frame->pts);
592 frame->marker = new MediaMarker (type, text, frame->pts);
594 g_free (text);
595 g_free (type);
596 result = MEDIA_SUCCESS;
597 } else {
598 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): didn't find 2 null characters in the data.\n");
599 result = MEDIA_CORRUPTED_MEDIA;
602 if (MEDIA_SUCCEEDED (result)) {
603 ReportDecodeFrameCompleted (frame);
604 } else {
605 ReportErrorOccurred (result);
610 * ASFMarkerDecoderInfo
613 IMediaDecoder *
614 ASFMarkerDecoderInfo::Create (Media *media, IMediaStream *stream)
616 return new ASFMarkerDecoder (media, stream);
619 bool
620 ASFMarkerDecoderInfo::Supports (const char *codec)
622 return !strcmp (codec, "asf-marker");
625 const char *
626 ASFMarkerDecoderInfo::GetName ()
628 return "ASFMarkerDecoder";
632 * ASFDemuxerInfo
635 MediaResult
636 ASFDemuxerInfo::Supports (IMediaSource *source)
638 guint8 buffer[16];
639 bool result;
641 LOG_PIPELINE_ASF ("ASFDemuxerInfo::Supports (%p) pos: %lld, avail pos: %lld\n", source, source->GetPosition (), source->GetLastAvailablePosition ());
643 #if DEBUG
644 bool eof = false;
645 if (!source->GetPosition () == 0)
646 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Trying to check if a media is supported, but the media isn't at position 0 (it's at position %lld)\n", source, source->GetPosition ());
647 if (!source->IsPositionAvailable (16, &eof)) // This shouldn't happen, we should have at least 1024 bytes (or eof).
648 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Not enough data! eof: %i\n", source, eof);
649 #endif
651 if (!source->Peek (buffer, 16)) {
652 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Peek failed.\n", source);
653 return MEDIA_FAIL;
656 result = asf_guid_compare (&asf_guids_header, (asf_guid *) buffer);
658 //printf ("ASFDemuxerInfo::Supports (%p): probing result: %s %s\n", source, source->ToString (),
659 // result ? "true" : "false");
661 return result ? MEDIA_SUCCESS : MEDIA_FAIL;
664 IMediaDemuxer *
665 ASFDemuxerInfo::Create (Media *media, IMediaSource *source)
667 return new ASFDemuxer (media, source);
671 * MmsSource
674 MmsSource::MmsSource (Media *media, Downloader *downloader)
675 : IMediaSource (Type::MMSSOURCE, media)
677 finished = false;
678 write_count = 0;
679 this->downloader = NULL;
680 current = NULL;
681 demuxer = NULL;
683 g_return_if_fail (downloader != NULL);
684 g_return_if_fail (downloader->GetInternalDownloader () != NULL);
685 g_return_if_fail (downloader->GetInternalDownloader ()->GetObjectType () == Type::MMSDOWNLOADER);
687 this->downloader = downloader;
688 this->downloader->ref ();
690 ReportStreamChange (0); // create the initial MmsPlaylistEntry
693 void
694 MmsSource::Dispose ()
696 // thread safe method
698 MmsPlaylistEntry *entry;
699 IMediaDemuxer *demux;
700 Downloader *dl;
702 // don't lock during unref, only while nulling out the local field
703 Lock ();
704 entry = this->current;
705 this->current = NULL;
706 dl = this->downloader;
707 this->downloader = NULL;
708 demux = this->demuxer;
709 this->demuxer = NULL;
710 Unlock ();
712 if (dl) {
713 dl->RemoveAllHandlers (this);
714 dl->unref ();
717 if (entry)
718 entry->unref ();
720 if (demux)
721 demux->unref ();
723 IMediaSource::Dispose ();
726 MediaResult
727 MmsSource::Initialize ()
729 Downloader *dl;
730 MmsDownloader *mms_dl;
732 VERIFY_MAIN_THREAD;
734 dl = GetDownloaderReffed ();
736 g_return_val_if_fail (dl != NULL, MEDIA_FAIL);
737 g_return_val_if_fail (!dl->Started (), MEDIA_FAIL);
739 // We must call MmsDownloader::SetSource before the downloader
740 // has actually received any data. Here we rely on the fact that
741 // firefox needs a tick before returning any data.
742 mms_dl = GetMmsDownloader (dl);
743 if (mms_dl != NULL) {
744 mms_dl->SetSource (this);
745 } else {
746 printf ("MmsSource::Initialize (): Could not get the MmsDownloader. Media won't play.\n");
749 dl->AddHandler (Downloader::DownloadFailedEvent, DownloadFailedCallback, this);
750 dl->AddHandler (Downloader::CompletedEvent, DownloadCompleteCallback, this);
751 dl->Send ();
753 dl->unref ();
755 return MEDIA_SUCCESS;
758 MmsDemuxer *
759 MmsSource::GetDemuxerReffed ()
761 MmsDemuxer *result;
762 Lock ();
763 result = demuxer;
764 if (result)
765 result->ref ();
766 Unlock ();
767 return result;
770 Downloader *
771 MmsSource::GetDownloaderReffed ()
773 Downloader *result;
774 Lock ();
775 result = downloader;
776 if (downloader)
777 downloader->ref ();
778 Unlock ();
779 return result;
782 MmsPlaylistEntry *
783 MmsSource::GetCurrentReffed ()
785 MmsPlaylistEntry *result;
787 // Thread safe
789 Lock ();
790 result = current;
791 if (result)
792 result->ref ();
793 Unlock ();
795 return result;
798 void
799 MmsSource::ReportDownloadFailure ()
801 Media *media;
803 LOG_MMS ("MmsSource::ReportDownloadFailure ()\n");
804 VERIFY_MAIN_THREAD;
806 media = GetMediaReffed ();
808 g_return_if_fail (media != NULL);
810 media->ReportErrorOccurred ("MmsDownloader failed");
811 media->unref ();
814 void
815 MmsSource::ReportStreamChange (gint32 reason)
817 Media *media;
818 PlaylistRoot *root;
819 Media *entry_media;
821 LOG_MMS ("MmsSource::ReportStreamChange (reason: %i)\n", reason);
823 VERIFY_MAIN_THREAD;
825 media = GetMediaReffed ();
827 g_return_if_fail (media != NULL);
829 root = media->GetPlaylistRoot ();
831 g_return_if_fail (root != NULL);
833 Lock ();
834 if (current != NULL) {
835 current->NotifyFinished ();
836 current->unref ();
839 entry_media = new Media (root);
840 current = new MmsPlaylistEntry (entry_media, this);
841 entry_media->unref ();
842 Unlock ();
845 void
846 MmsSource::SetMmsMetadata (const char *playlist_gen_id, const char *broadcast_id, HttpStreamingFeatures features)
848 MmsPlaylistEntry *entry;
850 LOG_MMS ("MmsSource::SetMmsMetadata ('%s', '%s', %i)\n", playlist_gen_id, broadcast_id, (int) features);
852 VERIFY_MAIN_THREAD;
854 entry = GetCurrentReffed ();
856 g_return_if_fail (entry != NULL);
858 entry->SetPlaylistGenId (playlist_gen_id);
859 entry->SetBroadcastId (broadcast_id);
860 entry->SetHttpStreamingFeatures (features);
862 entry->unref ();
865 void
866 MmsSource::DownloadFailedHandler (Downloader *dl, EventArgs *args)
868 Media *media = GetMediaReffed ();
870 VERIFY_MAIN_THREAD;
872 g_return_if_fail (media != NULL);
874 media->RetryHttp (new ErrorEventArgs (MediaError, 4001, "AG_E_NETWORK_ERROR"));
875 media->unref ();
878 void
879 MmsSource::DownloadCompleteHandler (Downloader *dl, EventArgs *args)
881 VERIFY_MAIN_THREAD;
884 void
885 MmsSource::NotifyFinished (guint32 reason)
887 VERIFY_MAIN_THREAD;
889 LOG_MMS ("MmsSource::NotifyFinished (%i)\n", reason);
891 if (reason == 0) {
892 // The server has finished streaming and no more
893 // Data packets will be transmitted until the next Play request
894 finished = true;
895 } else if (reason == 1) {
896 // The server has finished streaming the current playlist entry. Other playlist
897 // entries still remain to be streamed. The server will transmit a stream change packet
898 // when it switches to the next entry.
899 MmsPlaylistEntry *entry = GetCurrentReffed ();
900 entry->NotifyFinished ();
901 entry->unref ();
902 } else {
903 // ?
907 MediaResult
908 MmsSource::SeekToPts (guint64 pts)
910 // thread safe
911 MediaResult result = true;
912 Downloader *dl;
913 MmsDownloader *mms_dl;
915 LOG_PIPELINE_ASF ("MmsSource::SeekToPts (%" G_GUINT64_FORMAT ")\n", pts);
917 dl = GetDownloaderReffed ();
919 g_return_val_if_fail (dl != NULL, MEDIA_FAIL);
921 mms_dl = GetMmsDownloader (dl);
923 if (mms_dl) {
924 mms_dl->SetRequestedPts (pts);
925 finished = false;
926 result = MEDIA_SUCCESS;
927 } else {
928 result = MEDIA_FAIL;
931 dl->unref ();
933 return result;
936 MmsDownloader *
937 MmsSource::GetMmsDownloader (Downloader *dl)
939 InternalDownloader *idl;
941 g_return_val_if_fail (dl != NULL, NULL);
943 idl = dl->GetInternalDownloader ();
945 if (idl == NULL)
946 return NULL;
948 if (idl->GetObjectType () != Type::MMSDOWNLOADER)
949 return NULL;
951 return (MmsDownloader *) idl;
954 IMediaDemuxer *
955 MmsSource::CreateDemuxer (Media *media)
957 // thread safe
958 MmsDemuxer *result;
960 g_return_val_if_fail (demuxer == NULL, NULL);
962 Lock ();
963 if (demuxer == NULL) {
964 result = new MmsDemuxer (media, this);
965 demuxer = result;
966 demuxer->ref ();
968 Unlock ();
970 return result;
973 void
974 MmsSource::WritePacket (void *buf, gint32 n)
976 MmsPlaylistEntry *entry;
978 VERIFY_MAIN_THREAD;
980 entry = GetCurrentReffed ();
982 g_return_if_fail (entry != NULL);
984 entry->WritePacket (buf, n);
985 entry->unref ();
988 ASFPacket *
989 MmsSource::Pop ()
991 MmsPlaylistEntry *entry;
992 ASFPacket *result;
994 entry = GetCurrentReffed ();
996 g_return_val_if_fail (entry != NULL, NULL);
998 result = entry->Pop ();
1000 entry->unref ();
1002 return result;
1005 bool
1006 MmsSource::Eof ()
1008 // thread safe
1009 MmsPlaylistEntry *entry;
1010 bool result;
1012 if (!finished)
1013 return false;
1015 entry = GetCurrentReffed ();
1017 if (entry == NULL) {
1018 result = true;
1019 } else {
1020 result = entry->Eof ();
1021 entry->unref ();
1024 return result;
1028 * MmsPlaylistEntry
1031 MmsPlaylistEntry::MmsPlaylistEntry (Media *media, MmsSource *source)
1032 : IMediaSource (Type::MMSPLAYLISTENTRY, media)
1034 finished = false;
1035 parent = source;
1036 parser = NULL;
1037 write_count = 0;
1038 demuxer = NULL;
1039 playlist_gen_id = NULL;
1040 broadcast_id = NULL;
1041 features = HttpStreamingFeaturesNone;
1043 g_return_if_fail (parent != NULL);
1044 parent->ref ();
1047 MediaResult
1048 MmsPlaylistEntry::Initialize ()
1050 return MEDIA_SUCCESS;
1053 void
1054 MmsPlaylistEntry::Dispose ()
1056 // thread safe
1057 MmsSource *mms_source;
1058 ASFParser *asf_parser;
1059 IMediaDemuxer *demux;
1061 Lock ();
1062 mms_source = this->parent;
1063 this->parent = NULL;
1064 asf_parser = this->parser;
1065 this->parser = NULL;
1066 demux = this->demuxer;
1067 this->demuxer = NULL;
1068 g_free (playlist_gen_id);
1069 playlist_gen_id = NULL;
1070 g_free (broadcast_id);
1071 broadcast_id = NULL;
1072 Unlock ();
1074 if (mms_source != NULL)
1075 mms_source->unref ();
1077 if (asf_parser != NULL)
1078 asf_parser->unref ();
1080 if (demux != NULL)
1081 demux->unref ();
1083 IMediaSource::Dispose ();
1086 MediaResult
1087 MmsPlaylistEntry::SeekToPts (guint64 pts)
1089 MmsSource *ms = GetParentReffed ();
1090 if (ms) {
1091 ms->SeekToPts (pts);
1092 ms->unref ();
1093 return MEDIA_SUCCESS;
1094 } else {
1095 fprintf (stderr, "MmsPlaylistEntry::SeekToPts (%llu): Could not seek to pts, no parent.\n", pts);
1096 return MEDIA_FAIL;
1100 void
1101 MmsPlaylistEntry::NotifyFinished ()
1103 finished = true;
1106 void
1107 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate, gint8 streams [128])
1109 ASFParser *parser;
1110 asf_file_properties *properties;
1111 gint32 audio_bitrates [128];
1112 gint32 video_bitrates [128];
1114 memset (audio_bitrates, 0xff, 128 * 4);
1115 memset (video_bitrates, 0xff, 128 * 4);
1116 memset (streams, 0xff, 128);
1118 parser = GetParserReffed ();
1120 g_return_if_fail (parser != NULL);
1122 properties = parser->GetFileProperties ();
1124 g_return_if_fail (properties != NULL);
1126 for (int i = 1; i < 127; i++) {
1127 int current_stream;
1128 if (!parser->IsValidStream (i)) {
1129 streams [i] = -1; // inexistent
1130 continue;
1132 streams [i] = 0; // disabled
1133 current_stream = i;
1135 const asf_stream_properties *stream_properties = parser->GetStream (current_stream);
1136 const asf_extended_stream_properties *extended_stream_properties = parser->GetExtendedStream (current_stream);
1138 if (stream_properties == NULL) {
1139 printf ("MmsPlaylistEntry::GetSelectedStreams (): stream #%i doesn't have any stream properties.\n", current_stream);
1140 continue;
1143 if (stream_properties->is_audio ()) {
1144 const WAVEFORMATEX* wave = stream_properties->get_audio_data ();
1145 audio_bitrates [current_stream] = wave->bytes_per_second * 8;
1146 } else if (stream_properties->is_video ()) {
1147 int bit_rate = 0;
1148 const asf_video_stream_data* video_data = stream_properties->get_video_data ();
1149 const BITMAPINFOHEADER* bmp;
1151 if (extended_stream_properties != NULL) {
1152 bit_rate = extended_stream_properties->data_bitrate;
1153 } else if (video_data != NULL) {
1154 bmp = video_data->get_bitmap_info_header ();
1155 if (bmp != NULL) {
1156 bit_rate = bmp->image_width*bmp->image_height;
1160 video_bitrates [current_stream] = bit_rate;
1161 } else if (stream_properties->is_command ()) {
1162 // we select all marker streams
1163 streams [current_stream] = 1;
1167 // select the video stream
1168 int video_stream = 0;
1169 int video_rate = 0;
1171 for (int i = 0; i < 128; i++) {
1172 int stream_rate = video_bitrates [i];
1174 if (stream_rate == -1)
1175 continue;
1177 if (video_rate == 0) {
1178 video_rate = stream_rate;
1179 video_stream = i;
1182 if (stream_rate > video_rate && stream_rate < (max_bitrate * VIDEO_BITRATE_PERCENTAGE)) {
1183 video_rate = stream_rate;
1184 video_stream = i;
1187 streams [video_stream] = 1; // selected
1188 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected video stream %i of rate %i\n", video_stream, video_rate);
1191 // select audio stream
1192 int audio_stream = 0;
1193 int audio_rate = 0;
1195 for (int i = 0; i < 128; i++) {
1196 int stream_rate = audio_bitrates [i];
1198 if (stream_rate == -1)
1199 continue;
1201 if (audio_rate == 0) {
1202 audio_rate = stream_rate;
1203 audio_stream = i;
1206 if (stream_rate > audio_rate && stream_rate < (max_bitrate * AUDIO_BITRATE_PERCENTAGE)) {
1207 audio_rate = stream_rate;
1208 audio_stream = i;
1211 streams [audio_stream] = 1; // selected
1212 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream, audio_rate);
1214 parser->unref ();
1217 bool
1218 MmsPlaylistEntry::IsHeaderParsed ()
1220 bool result;
1221 Lock ();
1222 result = parser != NULL;
1223 Unlock ();
1224 return result;
1227 ASFParser *
1228 MmsPlaylistEntry::GetParserReffed ()
1230 // thread safe
1231 ASFParser *result;
1233 Lock ();
1234 result = parser;
1235 if (result)
1236 result->ref ();
1237 Unlock ();
1239 return result;
1242 MmsSource *
1243 MmsPlaylistEntry::GetParentReffed ()
1245 // thread safe
1246 MmsSource *result;
1248 Lock ();
1249 result = parent;
1250 if (result)
1251 result->ref ();
1252 Unlock ();
1254 return result;
1257 IMediaDemuxer *
1258 MmsPlaylistEntry::CreateDemuxer (Media *media)
1260 // thread safe
1261 ASFDemuxer *result;
1262 ASFParser *asf_parser;
1264 asf_parser = GetParserReffed ();
1266 g_return_val_if_fail (media != NULL, NULL);
1267 g_return_val_if_fail (asf_parser != NULL, NULL);
1268 g_return_val_if_fail (demuxer == NULL, NULL);
1270 result = new ASFDemuxer (media, this);
1271 result->SetParser (asf_parser);
1273 Lock ();
1274 if (demuxer != NULL)
1275 demuxer->unref ();
1276 demuxer = result;
1277 demuxer->ref ();
1278 Unlock ();
1280 asf_parser->unref ();
1282 return result;
1285 void
1286 MmsPlaylistEntry::SetPlaylistGenId (const char *value)
1288 // thread safe
1289 Lock ();
1290 g_free (playlist_gen_id);
1291 playlist_gen_id = g_strdup (value);
1292 Unlock ();
1295 char *
1296 MmsPlaylistEntry::GetPlaylistGenId ()
1298 // thread safe
1299 char *result;
1300 Lock ();
1301 result = g_strdup (playlist_gen_id);
1302 Unlock ();
1303 return result;
1306 void
1307 MmsPlaylistEntry::SetBroadcastId (const char *value)
1309 // thread safe
1310 Lock ();
1311 g_free (broadcast_id);
1312 broadcast_id = g_strdup (value);
1313 Unlock ();
1316 char *
1317 MmsPlaylistEntry::GetBroadcastId ()
1319 // thread safe
1320 char *result;
1321 Lock ();
1322 result = g_strdup (broadcast_id);
1323 Unlock ();
1324 return result;
1327 void
1328 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value)
1330 features = value;
1333 HttpStreamingFeatures
1334 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1336 return features;
1339 void
1340 MmsPlaylistEntry::AddEntry ()
1342 VERIFY_MAIN_THREAD;
1344 Media *media = GetMediaReffed ();
1345 Playlist *playlist;
1346 PlaylistEntry *entry;
1347 MmsDemuxer *mms_demuxer;
1349 g_return_if_fail (media != NULL);
1350 g_return_if_fail (parent != NULL);
1352 mms_demuxer = parent->GetDemuxerReffed ();
1354 g_return_if_fail (mms_demuxer != NULL);
1356 playlist = mms_demuxer->GetPlaylist ();
1358 g_return_if_fail (playlist != NULL);
1360 entry = new PlaylistEntry (playlist);
1361 entry->SetIsLive (features & HttpStreamingBroadcast);
1363 playlist->AddEntry (entry);
1365 entry->InitializeWithSource (this);
1368 media->unref ();
1369 mms_demuxer->unref ();
1372 MediaResult
1373 MmsPlaylistEntry::ParseHeader (void *buffer, gint32 size)
1375 VERIFY_MAIN_THREAD;
1377 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer, size);
1379 MediaResult result;
1380 MemorySource *asf_src = NULL;
1381 Media *media = GetMediaReffed ();
1382 ASFParser *asf_parser;
1384 // this method shouldn't get called more than once
1385 g_return_val_if_fail (parser == NULL, MEDIA_FAIL);
1386 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
1388 media->ReportDownloadProgress (1.0);
1390 asf_src = new MemorySource (media, buffer, size, 0, false);
1391 asf_parser = new ASFParser (asf_src, media);
1392 result = asf_parser->ReadHeader ();
1393 asf_src->unref ();
1394 media->unref ();
1396 if (MEDIA_SUCCEEDED (result)) {
1397 Lock ();
1398 if (parser)
1399 parser->unref ();
1400 parser = asf_parser;
1401 Unlock ();
1402 AddEntry ();
1403 } else {
1404 asf_parser->unref ();
1407 return result;
1410 ASFPacket *
1411 MmsPlaylistEntry::Pop ()
1413 // thread safe
1414 //LOG_MMS ("MmsSource::Pop (), there are %i packets in the queue, of a total of %lld packets written.\n", queue.Length (), write_count);
1416 QueueNode *node;
1417 ASFPacket *result = NULL;
1418 ASFParser *parser;
1420 trynext:
1421 node = (QueueNode *) queue.Pop ();
1423 if (node == NULL) {
1424 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1425 return NULL;
1428 parser = GetParserReffed ();
1430 if (node->packet == NULL) {
1431 if (parser == NULL) {
1432 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1433 goto cleanup;
1435 node->packet = new ASFPacket (parser, node->source);
1436 if (!MEDIA_SUCCEEDED (node->packet->Read ())) {
1437 LOG_PIPELINE_ASF ("MmsSource::Pop (): Error while parsing packet, getting a new packet\n");
1438 delete node;
1439 goto trynext;
1443 result = node->packet;
1444 result->ref ();
1446 cleanup:
1447 delete node;
1449 if (parser)
1450 parser->unref ();
1452 LOG_PIPELINE_ASF ("MmsSource::Pop (): popped 1 packet, there are %i packets left, of a total of %lld packets written\n", queue.Length (), write_count);
1454 return result;
1457 bool
1458 MmsPlaylistEntry::IsFinished ()
1460 bool result;
1461 return parent->IsFinished (); // REMOVE
1462 MmsSource *src = GetParentReffed ();
1464 g_return_val_if_fail (src != NULL, true);
1466 result = src->IsFinished ();
1467 src->unref ();
1469 return result;
1472 void
1473 MmsPlaylistEntry::WritePacket (void *buf, gint32 n)
1475 MemorySource *src;
1476 ASFPacket *packet;
1477 ASFParser *asf_parser;
1478 Media *media = GetMediaReffed ();
1480 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %lld\n", buf, n, write_count + 1);
1481 VERIFY_MAIN_THREAD;
1483 g_return_if_fail (media != NULL);
1485 write_count++;
1487 media = GetMediaReffed ();
1489 g_return_if_fail (media != NULL);
1491 asf_parser = GetParserReffed ();
1493 if (asf_parser != NULL) {
1494 src = new MemorySource (media, buf, n, 0, false);
1495 packet = new ASFPacket (asf_parser, src);
1496 if (!MEDIA_SUCCEEDED (packet->Read ())) {
1497 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i): Error while parsing packet, dropping packet.\n", buf, n);
1498 } else {
1499 queue.Push (new QueueNode (packet));
1501 packet->unref ();
1502 src->unref ();
1503 } else {
1504 src = new MemorySource (media, g_memdup (buf, n), n, 0);
1505 queue.Push (new QueueNode (src));
1506 src->unref ();
1509 if (asf_parser)
1510 asf_parser->unref ();
1512 if (media)
1513 media->unref ();
1517 * MmsPlaylistEntry::QueueNode
1520 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource *source)
1522 if (source)
1523 source->ref ();
1524 this->source = source;
1525 packet = NULL;
1528 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket *packet)
1530 if (packet)
1531 packet->ref ();
1532 this->packet = packet;
1533 source = NULL;
1536 MmsPlaylistEntry::QueueNode::~QueueNode ()
1538 if (packet)
1539 packet->unref ();
1540 if (source)
1541 source->unref ();
1545 * MmsDemuxer
1548 MmsDemuxer::MmsDemuxer (Media *media, MmsSource *source)
1549 : IMediaDemuxer (Type::MMSDEMUXER, media, source)
1551 playlist = NULL;
1552 mms_source = source;
1553 if (mms_source)
1554 mms_source->ref ();
1557 void
1558 MmsDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
1560 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream);
1563 void
1564 MmsDemuxer::OpenDemuxerAsyncInternal ()
1566 PlaylistRoot *root;
1567 Media *media;
1569 LOG_MMS ("MmsDemuxer::OpenDemuxerAsyncInternal ().\n");
1571 media = GetMediaReffed ();
1572 root = media ? media->GetPlaylistRoot () : NULL;
1574 g_return_if_fail (playlist == NULL);
1575 g_return_if_fail (media != NULL);
1576 g_return_if_fail (root != NULL);
1578 playlist = new Playlist (root, mms_source);
1579 ReportOpenDemuxerCompleted ();
1582 MediaResult
1583 MmsDemuxer::SeekInternal (guint64 pts)
1585 g_warning ("MmsDemuxer::SeekInternal (%lld): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", pts);
1586 print_stack_trace ();
1588 return MEDIA_FAIL;
1591 void
1592 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime)
1594 printf ("MmsDemuxer::SeekAsyncInternal (%llu): Not implemented.\n", seekToTime);
1597 void
1598 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *stream)
1600 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream);
1603 void
1604 MmsDemuxer::Dispose ()
1606 Playlist *pl;
1607 MmsSource *src;
1609 mutex.Lock ();
1610 pl = this->playlist;
1611 this->playlist = NULL;
1612 src = this->mms_source;
1613 this->mms_source = NULL;
1614 mutex.Unlock ();
1616 if (pl)
1617 pl->unref ();
1619 if (src)
1620 src->unref ();
1622 IMediaDemuxer::Dispose ();