2009-12-01 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / pipeline-asf.cpp
blobe9625da2fde3688e995e9a72e0b814f6f00468e4
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 LOG_PIPELINE ("ASFDemuxer:Seek (%" G_GUINT64_FORMAT "): seek completed, reporting it\n", pts);
77 ReportSeekCompleted (pts);
78 } else if (result == MEDIA_NOT_ENOUGH_DATA) {
79 LOG_PIPELINE ("ASFDemuxer:Seek (%" G_GUINT64_FORMAT "): not enough data\n", pts);
80 EnqueueSeek ();
81 } else {
82 ReportErrorOccurred (result);
86 void
87 ASFDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *stream)
89 LOG_PIPELINE ("ASFDemuxer::SwitchMediaStreamAsyncInternal (%p). TODO.\n", stream);
92 void
93 ASFDemuxer::ReadMarkers ()
96 We can get markers from several places:
97 - The header of the file, read before starting to play
98 - As a SCRIPT_COMMAND
99 - As a MARKER
100 They are both treated the same way, added into the timeline marker collection when the media is loaded.
101 - As data in the file (a separate stream whose type is ASF_COMMAND_MEDIA)
102 These markers show up while playing the file, and they don't show up in the timeline marker collection,
103 they only get to raise the MarkerReached event.
104 currently the demuxer will call the streamed_marker_callback when it encounters any of these.
107 // Hookup to the marker (ASF_COMMAND_MEDIA) stream
108 MediaMarker *marker;
109 Media *media = GetMediaReffed ();
111 g_return_if_fail (media != NULL);
113 // Read the markers (if any)
114 List *markers = media->GetMarkers ();
115 const char *type;
116 guint64 pts;
117 guint64 preroll_pts = MilliSeconds_ToPts (parser->GetFileProperties ()->preroll);
118 char *text;
119 int i = -1;
121 // Read the SCRIPT COMMANDs
122 char **command_types = NULL;
123 asf_script_command_entry **commands = NULL;
124 asf_script_command *command = parser->script_command;
126 if (command != NULL) {
127 commands = command->get_commands (parser, &command_types);
129 if (command_types == NULL) {
130 //printf ("MediaElement::ReadASFMarkers (): No command types.\n");
131 goto cleanup;
135 if (commands != NULL) {
136 for (i = 0; commands[i]; i++) {
137 asf_script_command_entry *entry = commands [i];
139 text = entry->get_name ();
140 pts = MilliSeconds_ToPts (entry->pts) - preroll_pts;
142 if (entry->type_index + 1 <= command->command_type_count)
143 type = command_types [entry->type_index];
144 else
145 type = "";
147 marker = new MediaMarker (type, text, pts);
148 markers->Append (new MediaMarker::Node (marker));
149 marker->unref ();
151 //printf ("MediaElement::ReadMarkers () Added script command at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, type);
153 g_free (text);
157 // Read the MARKERs
158 asf_marker *asf_marker;
159 const asf_marker_entry* marker_entry;
161 asf_marker = parser->marker;
162 if (asf_marker != NULL) {
163 for (i = 0; i < (int) asf_marker->marker_count; i++) {
164 marker_entry = asf_marker->get_entry (i);
165 text = marker_entry->get_marker_description ();
167 pts = marker_entry->pts - preroll_pts;
169 marker = new MediaMarker ("Name", text, pts);
170 markers->Append (new MediaMarker::Node (marker));
171 marker->unref ();
173 //printf ("MediaElement::ReadMarkers () Added marker at %" G_GUINT64_FORMAT " (text: %s, type: %s)\n", pts, text, "Name");
175 g_free (text);
180 cleanup:
181 g_strfreev (command_types);
182 g_free (commands);
183 media->unref ();
186 void
187 ASFDemuxer::SetParser (ASFParser *parser)
189 if (this->parser)
190 this->parser->unref ();
191 this->parser = parser;
192 if (this->parser) {
193 this->parser->ref ();
194 this->parser->SetSource (source);
198 void
199 ASFDemuxer::OpenDemuxerAsyncInternal ()
201 MediaResult result;
203 LOG_PIPELINE ("ASFDemuxer::OpenDemuxerAsyncInternal ()\n");
205 result = Open ();
207 if (MEDIA_SUCCEEDED (result)) {
208 ReportOpenDemuxerCompleted ();
209 } else if (result == MEDIA_NOT_ENOUGH_DATA) {
210 EnqueueOpen ();
211 } else {
212 ReportErrorOccurred (result);
216 MediaResult
217 ASFDemuxer::Open ()
219 MediaResult result = MEDIA_SUCCESS;
220 ASFParser *asf_parser = NULL;
221 gint32 *stream_to_asf_index = NULL;
222 IMediaStream **streams = NULL;
223 Media *media = GetMediaReffed ();
224 int current_stream = 1;
225 int stream_count = 0;
226 int count;
228 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
230 if (parser != NULL) {
231 asf_parser = parser;
232 } else {
233 asf_parser = new ASFParser (source, media);
236 LOG_PIPELINE_ASF ("ASFDemuxer::ReadHeader ().\n");
238 result = asf_parser->ReadHeader ();
239 if (!MEDIA_SUCCEEDED (result)) {
240 if (result == MEDIA_NOT_ENOUGH_DATA) {
241 LOG_PIPELINE_ASF ("ASFDemuxer::ReadHeader (): ReadHeader failed due to not enough data being available.\n");
242 } else {
243 Media::Warning (MEDIA_INVALID_MEDIA, "asf_parser->ReadHeader () failed:");
244 Media::Warning (MEDIA_FAIL, "%s", asf_parser->GetLastErrorStr ());
246 goto failure;
249 // Count the number of streams
250 for (int i = 1; i <= 127; i++) {
251 if (asf_parser->IsValidStream (i))
252 stream_count++;
255 current_stream = 1;
256 streams = (IMediaStream **) g_malloc0 (sizeof (IMediaStream *) * (stream_count + 1)); // End with a NULL element.
257 stream_to_asf_index = (gint32 *) g_malloc0 (sizeof (gint32) * (stream_count + 1));
259 // keep count as a separate local since we can change its value (e.g. bad stream)
260 count = stream_count;
261 // Loop through all the streams and set stream-specific data
262 for (int i = 0; i < count; i++) {
263 while (current_stream <= 127 && !asf_parser->IsValidStream (current_stream))
264 current_stream++;
266 if (current_stream > 127) {
267 result = MEDIA_INVALID_STREAM;
268 Media::Warning (result, "Couldn't find all the claimed streams in the file.");
269 goto failure;
272 const asf_stream_properties* stream_properties = asf_parser->GetStream (current_stream);
273 IMediaStream* stream = NULL;
275 if (stream_properties == NULL) {
276 result = MEDIA_INVALID_STREAM;
277 Media::Warning (result, "Couldn't find all the claimed streams in the file.");
278 goto failure;
281 if (stream_properties->is_audio ()) {
282 AudioStream* audio = new AudioStream (media);
284 stream = audio;
286 const WAVEFORMATEX* wave = stream_properties->get_audio_data ();
287 if (wave == NULL) {
288 result = MEDIA_INVALID_STREAM;
289 Media::Warning (result, "Couldn't find audio data in the file.");
290 goto failure;
293 const WAVEFORMATEXTENSIBLE* wave_ex = wave->get_wave_format_extensible ();
294 int data_size = stream_properties->size - sizeof (asf_stream_properties) - sizeof (WAVEFORMATEX);
296 audio->SetChannels (wave->channels);
297 audio->SetSampleRate (wave->samples_per_second);
298 audio->SetBitRate (wave->bytes_per_second * 8);
299 audio->SetBlockAlign (wave->block_alignment);
300 audio->SetBitsPerSample (wave->bits_per_sample);
301 audio->SetExtraData (NULL);
302 audio->SetExtraDataSize (data_size > wave->codec_specific_data_size ? wave->codec_specific_data_size : data_size);
303 audio->SetCodecId (wave->codec_id);
305 if (wave_ex != NULL) {
306 audio->SetBitsPerSample (wave_ex->Samples.valid_bits_per_sample);
307 audio->SetExtraDataSize (audio->GetExtraDataSize () - (sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX)));
308 audio->SetCodecId (*((guint32*) &wave_ex->sub_format));
311 // Fill in any extra codec data
312 if (audio->GetExtraDataSize () > 0) {
313 audio->SetExtraData (g_malloc0 (audio->GetExtraDataSize ()));
314 char* src = ((char*) wave) + (wave_ex ? sizeof (WAVEFORMATEX) : sizeof (WAVEFORMATEX));
315 memcpy (audio->GetExtraData (), src, audio->GetExtraDataSize ());
317 } else if (stream_properties->is_video ()) {
318 VideoStream* video = new VideoStream (media);
319 stream = video;
321 const asf_video_stream_data* video_data = stream_properties->get_video_data ();
322 const BITMAPINFOHEADER* bmp;
323 const asf_extended_stream_properties* aesp;
325 if (video_data != NULL) {
326 bmp = video_data->get_bitmap_info_header ();
327 aesp = asf_parser->GetExtendedStream (current_stream);
328 if (bmp != NULL) {
329 video->width = bmp->image_width;
330 video->height = bmp->image_height;
332 // note: both height and width are unsigned
333 if ((video->height > MAX_VIDEO_HEIGHT) || (video->width > MAX_VIDEO_WIDTH)) {
334 result = MEDIA_INVALID_STREAM;
335 Media::Warning (result,
336 "Video stream size (width: %d, height: %d) outside limits (%d, %d)",
337 video->height, video->width, MAX_VIDEO_HEIGHT, MAX_VIDEO_WIDTH);
338 goto failure;
341 video->bits_per_sample = bmp->bits_per_pixel;
342 video->codec_id = bmp->compression_id;
343 video->extra_data_size = bmp->get_extra_data_size ();
344 if (video->extra_data_size > 0) {
345 video->extra_data = g_malloc0 (video->extra_data_size);
346 memcpy (video->extra_data, bmp->get_extra_data (), video->extra_data_size);
347 } else {
348 video->extra_data = NULL;
351 if (aesp != NULL) {
352 video->bit_rate = aesp->data_bitrate;
353 video->pts_per_frame = aesp->average_time_per_frame;
354 } else {
355 video->bit_rate = video->width*video->height;
356 video->pts_per_frame = 0;
359 } else if (stream_properties->is_command ()) {
360 MarkerStream* marker = new MarkerStream (media);
361 stream = marker;
362 stream->codec = g_strdup ("asf-marker");
363 } else {
364 // Unknown stream, don't include it in the count since it's NULL
365 stream_count--;
366 // also adjust indexes so we don't create a hole in the streams array
367 count--;
368 i--;
371 if (stream != NULL) {
372 if (stream_properties->is_video () || stream_properties->is_audio ()) {
373 switch (stream->codec_id) {
374 case CODEC_WMV1: stream->codec = g_strdup ("wmv1"); break;
375 case CODEC_WMV2: stream->codec = g_strdup ("wmv2"); break;
376 case CODEC_WMV3: stream->codec = g_strdup ("wmv3"); break;
377 case CODEC_WMVA: stream->codec = g_strdup ("wmva"); break;
378 case CODEC_WVC1: stream->codec = g_strdup ("vc1"); break;
379 case CODEC_MP3: stream->codec = g_strdup ("mp3"); break;
380 case CODEC_WMAV1: stream->codec = g_strdup ("wmav1"); break;
381 case CODEC_WMAV2: stream->codec = g_strdup ("wmav2"); break;
382 case CODEC_WMAV3: stream->codec = g_strdup ("wmav3"); break;
383 default:
384 char a = ((stream->codec_id & 0x000000FF));
385 char b = ((stream->codec_id & 0x0000FF00) >> 8);
386 char c = ((stream->codec_id & 0x00FF0000) >> 16);
387 char d = ((stream->codec_id & 0xFF000000) >> 24);
388 stream->codec = g_strdup_printf ("unknown (%c%c%c%c)", a ? a : ' ', b ? b : ' ', c ? c : ' ', d ? d : ' ');
389 break;
392 streams [i] = stream;
393 stream->index = i;
394 if (!asf_parser->file_properties->is_broadcast ()) {
395 stream->duration = asf_parser->file_properties->play_duration - MilliSeconds_ToPts (asf_parser->file_properties->preroll);
397 stream_to_asf_index [i] = current_stream;
400 current_stream++;
404 if (!MEDIA_SUCCEEDED (result)) {
405 goto failure;
408 SetStreams (streams, stream_count);
410 for (int i = 0; i < stream_count; i++)
411 streams [i]->unref ();
413 this->stream_to_asf_index = stream_to_asf_index;
414 this->parser = asf_parser;
416 reader = new ASFReader (parser, this);
418 ReadMarkers ();
420 media->unref ();
422 return result;
424 failure:
425 asf_parser->unref ();
426 asf_parser = NULL;
428 g_free (stream_to_asf_index);
429 stream_to_asf_index = NULL;
431 if (streams != NULL) {
432 for (int i = 0; i < stream_count; i++) {
433 if (streams [i] != NULL) {
434 streams [i]->unref ();
435 streams [i] = NULL;
438 g_free (streams);
439 streams = NULL;
442 media->unref ();
444 return result;
447 IMediaStream *
448 ASFDemuxer::GetStreamOfASFIndex (gint32 asf_index)
450 for (gint32 i = 0; i < GetStreamCount (); i++) {
451 if (stream_to_asf_index [i] == asf_index)
452 return GetStream (i);
454 return NULL;
457 MediaResult
458 ASFDemuxer::GetFrameCallback (MediaClosure *c)
460 MediaGetFrameClosure *closure = (MediaGetFrameClosure *) c;
461 ASFDemuxer *demuxer = (ASFDemuxer *) closure->GetDemuxer ();
462 demuxer->GetFrameAsyncInternal (closure->GetStream ());
464 return MEDIA_SUCCESS;
467 void
468 ASFDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
470 //printf ("ASFDemuxer::ReadFrame (%p).\n", frame);
471 ASFFrameReader *reader = NULL;
472 MediaFrame *frame;
473 MediaResult result;
475 g_return_if_fail (this->reader != NULL);
477 reader = this->reader->GetFrameReader (stream_to_asf_index [stream->index]);
479 g_return_if_fail (reader != NULL);
481 result = reader->Advance ();
483 if (result == MEDIA_NO_MORE_DATA) {
484 ReportGetFrameCompleted (NULL);
485 return;
488 if (result == MEDIA_BUFFER_UNDERFLOW || result == MEDIA_NOT_ENOUGH_DATA) {
489 Media *media = GetMediaReffed ();
490 g_return_if_fail (media != NULL);
491 MediaClosure *closure = new MediaGetFrameClosure (media, GetFrameCallback, this, stream);
492 media->EnqueueWork (closure, false); // TODO: use a timeout here, no need to try again immediately.
493 closure->unref ();
494 media->unref ();
495 return;
498 if (!MEDIA_SUCCEEDED (result)) {
499 ReportErrorOccurred (g_strdup_printf ("Error while advancing to the next frame (%d)", result));
500 return;
503 frame = new MediaFrame (stream);
504 frame->pts = reader->Pts ();
505 //frame->duration = reader->Duration ();
506 if (reader->IsKeyFrame ())
507 frame->AddState (MediaFrameKeyFrame);
508 frame->buflen = reader->Size ();
509 frame->buffer = (guint8 *) g_try_malloc (frame->buflen + frame->stream->min_padding);
511 if (frame->buffer == NULL) {
512 ReportErrorOccurred ( "Could not allocate memory for next frame.");
513 return;
516 //printf ("ASFDemuxer::ReadFrame (%p), min_padding = %i\n", frame, frame->stream->min_padding);
517 if (frame->stream->min_padding > 0)
518 memset (frame->buffer + frame->buflen, 0, frame->stream->min_padding);
520 if (!reader->Write (frame->buffer)) {
521 ReportErrorOccurred ("Error while copying the next frame.");
522 return;
525 frame->AddState (MediaFrameDemuxed);
527 ReportGetFrameCompleted (frame);
529 frame->unref ();
533 * ASFMarkerDecoder
536 ASFMarkerDecoder::ASFMarkerDecoder (Media *media, IMediaStream *stream)
537 : IMediaDecoder (Type::ASFMARKERDECODER, media, stream)
541 void
542 ASFMarkerDecoder::OpenDecoderAsyncInternal ()
544 ReportOpenDecoderCompleted ();
547 void
548 ASFMarkerDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
550 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame ()\n");
552 MediaResult result;
553 char *text;
554 char *type;
555 gunichar2 *data;
556 gunichar2 *uni_type = NULL;
557 gunichar2 *uni_text = NULL;
558 int text_length = 0;
559 int type_length = 0;
560 guint32 size = 0;
562 if (frame->buflen % 2 != 0 || frame->buflen == 0 || frame->buffer == NULL) {
563 ReportErrorOccurred (MEDIA_CORRUPTED_MEDIA);
564 return;
567 data = (gunichar2 *) frame->buffer;
568 uni_type = data;
569 size = frame->buflen;
571 // the data is two arrays of WCHARs (type and text), null terminated.
572 // loop through the data, counting characters and null characters
573 // there should be at least two null characters.
574 int null_count = 0;
576 for (guint32 i = 0; i < (size / sizeof (gunichar2)); i++) {
577 if (uni_text == NULL) {
578 type_length++;
579 } else {
580 text_length++;
582 if (*(data + i) == 0) {
583 null_count++;
584 if (uni_text == NULL) {
585 uni_text = data + i + 1;
586 } else {
587 break; // Found at least two nulls
592 if (null_count >= 2) {
593 text = wchar_to_utf8 (uni_text, text_length);
594 type = wchar_to_utf8 (uni_type, type_length);
596 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): sending script command type: '%s', text: '%s', pts: '%" G_GUINT64_FORMAT "'.\n", type, text, frame->pts);
598 frame->marker = new MediaMarker (type, text, frame->pts);
600 g_free (text);
601 g_free (type);
602 result = MEDIA_SUCCESS;
603 } else {
604 LOG_PIPELINE_ASF ("ASFMarkerDecoder::DecodeFrame (): didn't find 2 null characters in the data.\n");
605 result = MEDIA_CORRUPTED_MEDIA;
608 if (MEDIA_SUCCEEDED (result)) {
609 ReportDecodeFrameCompleted (frame);
610 } else {
611 ReportErrorOccurred (result);
616 * ASFMarkerDecoderInfo
619 IMediaDecoder *
620 ASFMarkerDecoderInfo::Create (Media *media, IMediaStream *stream)
622 return new ASFMarkerDecoder (media, stream);
625 bool
626 ASFMarkerDecoderInfo::Supports (const char *codec)
628 return !strcmp (codec, "asf-marker");
631 const char *
632 ASFMarkerDecoderInfo::GetName ()
634 return "ASFMarkerDecoder";
638 * ASFDemuxerInfo
641 MediaResult
642 ASFDemuxerInfo::Supports (IMediaSource *source)
644 guint8 buffer[16];
645 bool result;
647 LOG_PIPELINE_ASF ("ASFDemuxerInfo::Supports (%p) pos: %" G_GINT64_FORMAT ", avail pos: %" G_GINT64_FORMAT "\n", source, source->GetPosition (), source->GetLastAvailablePosition ());
649 #if DEBUG
650 bool eof = false;
651 if (!source->GetPosition () == 0)
652 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Trying to check if a media is supported, but the media isn't at position 0 (it's at position %" G_GINT64_FORMAT ")\n", source, source->GetPosition ());
653 if (!source->IsPositionAvailable (16, &eof)) // This shouldn't happen, we should have at least 1024 bytes (or eof).
654 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Not enough data! eof: %i\n", source, eof);
655 #endif
657 if (!source->Peek (buffer, 16)) {
658 fprintf (stderr, "ASFDemuxerInfo::Supports (%p): Peek failed.\n", source);
659 return MEDIA_FAIL;
662 result = asf_guid_compare (&asf_guids_header, (asf_guid *) buffer);
664 //printf ("ASFDemuxerInfo::Supports (%p): probing result: %s %s\n", source, source->ToString (),
665 // result ? "true" : "false");
667 return result ? MEDIA_SUCCESS : MEDIA_FAIL;
670 IMediaDemuxer *
671 ASFDemuxerInfo::Create (Media *media, IMediaSource *source)
673 return new ASFDemuxer (media, source);
677 * MmsSource
680 MmsSource::MmsSource (Media *media, Downloader *downloader)
681 : IMediaSource (Type::MMSSOURCE, media)
683 finished = false;
684 write_count = 0;
685 this->downloader = NULL;
686 current = NULL;
687 demuxer = NULL;
689 g_return_if_fail (downloader != NULL);
690 g_return_if_fail (downloader->GetInternalDownloader () != NULL);
691 g_return_if_fail (downloader->GetInternalDownloader ()->GetObjectType () == Type::MMSDOWNLOADER);
693 this->downloader = downloader;
694 this->downloader->ref ();
696 ReportStreamChange (0); // create the initial MmsPlaylistEntry
699 void
700 MmsSource::Dispose ()
702 // thread safe method
704 MmsPlaylistEntry *entry;
705 IMediaDemuxer *demux;
706 Downloader *dl;
708 // don't lock during unref, only while nulling out the local field
709 Lock ();
710 entry = this->current;
711 this->current = NULL;
712 dl = this->downloader;
713 this->downloader = NULL;
714 demux = this->demuxer;
715 this->demuxer = NULL;
716 Unlock ();
718 if (dl) {
719 dl->RemoveAllHandlers (this);
720 dl->unref ();
723 if (entry)
724 entry->unref ();
726 if (demux)
727 demux->unref ();
729 IMediaSource::Dispose ();
732 MediaResult
733 MmsSource::Initialize ()
735 Downloader *dl;
736 MmsDownloader *mms_dl;
738 VERIFY_MAIN_THREAD;
740 dl = GetDownloaderReffed ();
742 g_return_val_if_fail (dl != NULL, MEDIA_FAIL);
743 g_return_val_if_fail (!dl->Started (), MEDIA_FAIL);
745 // We must call MmsDownloader::SetSource before the downloader
746 // has actually received any data. Here we rely on the fact that
747 // firefox needs a tick before returning any data.
748 mms_dl = GetMmsDownloader (dl);
749 if (mms_dl != NULL) {
750 mms_dl->SetSource (this);
751 } else {
752 printf ("MmsSource::Initialize (): Could not get the MmsDownloader. Media won't play.\n");
755 dl->AddHandler (Downloader::DownloadFailedEvent, DownloadFailedCallback, this);
756 dl->AddHandler (Downloader::CompletedEvent, DownloadCompleteCallback, this);
757 dl->Send ();
759 dl->unref ();
761 return MEDIA_SUCCESS;
764 MmsDemuxer *
765 MmsSource::GetDemuxerReffed ()
767 MmsDemuxer *result;
768 Lock ();
769 result = demuxer;
770 if (result)
771 result->ref ();
772 Unlock ();
773 return result;
776 Downloader *
777 MmsSource::GetDownloaderReffed ()
779 Downloader *result;
780 Lock ();
781 result = downloader;
782 if (downloader)
783 downloader->ref ();
784 Unlock ();
785 return result;
788 MmsPlaylistEntry *
789 MmsSource::GetCurrentReffed ()
791 MmsPlaylistEntry *result;
793 // Thread safe
795 Lock ();
796 result = current;
797 if (result)
798 result->ref ();
799 Unlock ();
801 return result;
804 void
805 MmsSource::ReportDownloadFailure ()
807 Media *media;
809 LOG_MMS ("MmsSource::ReportDownloadFailure ()\n");
810 VERIFY_MAIN_THREAD;
812 media = GetMediaReffed ();
814 g_return_if_fail (media != NULL);
816 media->ReportErrorOccurred ("MmsDownloader failed");
817 media->unref ();
820 void
821 MmsSource::ReportStreamChange (gint32 reason)
823 Media *media;
824 PlaylistRoot *root;
825 Media *entry_media;
827 LOG_MMS ("MmsSource::ReportStreamChange (reason: %i)\n", reason);
829 VERIFY_MAIN_THREAD;
831 media = GetMediaReffed ();
833 g_return_if_fail (media != NULL);
835 root = media->GetPlaylistRoot ();
837 g_return_if_fail (root != NULL);
839 Lock ();
840 if (current != NULL) {
841 current->NotifyFinished ();
842 current->unref ();
845 entry_media = new Media (root);
846 current = new MmsPlaylistEntry (entry_media, this);
847 entry_media->unref ();
848 Unlock ();
850 media->unref ();
853 void
854 MmsSource::SetMmsMetadata (const char *playlist_gen_id, const char *broadcast_id, HttpStreamingFeatures features)
856 MmsPlaylistEntry *entry;
858 LOG_MMS ("MmsSource::SetMmsMetadata ('%s', '%s', %i)\n", playlist_gen_id, broadcast_id, (int) features);
860 VERIFY_MAIN_THREAD;
862 entry = GetCurrentReffed ();
864 g_return_if_fail (entry != NULL);
866 entry->SetPlaylistGenId (playlist_gen_id);
867 entry->SetBroadcastId (broadcast_id);
868 entry->SetHttpStreamingFeatures (features);
870 entry->unref ();
873 void
874 MmsSource::DownloadFailedHandler (Downloader *dl, EventArgs *args)
876 Media *media = GetMediaReffed ();
877 ErrorEventArgs *eea;
878 VERIFY_MAIN_THREAD;
880 g_return_if_fail (media != NULL);
881 eea = new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR"));
882 media->RetryHttp (eea);
883 media->unref ();
884 eea->unref ();
887 void
888 MmsSource::DownloadCompleteHandler (Downloader *dl, EventArgs *args)
890 VERIFY_MAIN_THREAD;
893 void
894 MmsSource::NotifyFinished (guint32 reason)
896 VERIFY_MAIN_THREAD;
898 LOG_MMS ("MmsSource::NotifyFinished (%i)\n", reason);
900 if (reason == 0) {
901 // The server has finished streaming and no more
902 // Data packets will be transmitted until the next Play request
903 finished = true;
904 } else if (reason == 1) {
905 // The server has finished streaming the current playlist entry. Other playlist
906 // entries still remain to be streamed. The server will transmit a stream change packet
907 // when it switches to the next entry.
908 MmsPlaylistEntry *entry = GetCurrentReffed ();
909 entry->NotifyFinished ();
910 entry->unref ();
911 } else {
912 // ?
916 MediaResult
917 MmsSource::SeekToPts (guint64 pts)
919 // thread safe
920 MediaResult result = true;
921 Downloader *dl;
922 MmsDownloader *mms_dl;
924 LOG_PIPELINE_ASF ("MmsSource::SeekToPts (%" G_GUINT64_FORMAT ")\n", pts);
926 dl = GetDownloaderReffed ();
928 g_return_val_if_fail (dl != NULL, MEDIA_FAIL);
930 mms_dl = GetMmsDownloader (dl);
932 if (mms_dl) {
933 mms_dl->SetRequestedPts (pts);
934 finished = false;
935 result = MEDIA_SUCCESS;
936 } else {
937 result = MEDIA_FAIL;
940 dl->unref ();
942 return result;
945 MmsDownloader *
946 MmsSource::GetMmsDownloader (Downloader *dl)
948 InternalDownloader *idl;
950 g_return_val_if_fail (dl != NULL, NULL);
952 idl = dl->GetInternalDownloader ();
954 if (idl == NULL)
955 return NULL;
957 if (idl->GetObjectType () != Type::MMSDOWNLOADER)
958 return NULL;
960 return (MmsDownloader *) idl;
963 IMediaDemuxer *
964 MmsSource::CreateDemuxer (Media *media)
966 // thread safe
967 MmsDemuxer *result = NULL;
969 g_return_val_if_fail (demuxer == NULL, NULL);
971 Lock ();
972 if (demuxer == NULL) {
973 result = new MmsDemuxer (media, this);
974 demuxer = result;
975 demuxer->ref ();
977 Unlock ();
979 return result;
982 void
983 MmsSource::WritePacket (void *buf, gint32 n)
985 MmsPlaylistEntry *entry;
987 VERIFY_MAIN_THREAD;
989 entry = GetCurrentReffed ();
991 g_return_if_fail (entry != NULL);
993 entry->WritePacket (buf, n);
994 entry->unref ();
997 ASFPacket *
998 MmsSource::Pop ()
1000 MmsPlaylistEntry *entry;
1001 ASFPacket *result;
1003 entry = GetCurrentReffed ();
1005 g_return_val_if_fail (entry != NULL, NULL);
1007 result = entry->Pop ();
1009 entry->unref ();
1011 return result;
1014 bool
1015 MmsSource::Eof ()
1017 // thread safe
1018 MmsPlaylistEntry *entry;
1019 bool result;
1021 if (!finished)
1022 return false;
1024 entry = GetCurrentReffed ();
1026 if (entry == NULL) {
1027 result = true;
1028 } else {
1029 result = entry->Eof ();
1030 entry->unref ();
1033 return result;
1037 * MmsPlaylistEntry
1040 MmsPlaylistEntry::MmsPlaylistEntry (Media *media, MmsSource *source)
1041 : IMediaSource (Type::MMSPLAYLISTENTRY, media)
1043 finished = false;
1044 parent = source;
1045 parser = NULL;
1046 write_count = 0;
1047 demuxer = NULL;
1048 playlist_gen_id = NULL;
1049 broadcast_id = NULL;
1050 features = HttpStreamingFeaturesNone;
1052 g_return_if_fail (parent != NULL);
1053 parent->ref ();
1056 MediaResult
1057 MmsPlaylistEntry::Initialize ()
1059 return MEDIA_SUCCESS;
1062 void
1063 MmsPlaylistEntry::Dispose ()
1065 // thread safe
1066 MmsSource *mms_source;
1067 ASFParser *asf_parser;
1068 IMediaDemuxer *demux;
1070 Lock ();
1071 mms_source = this->parent;
1072 this->parent = NULL;
1073 asf_parser = this->parser;
1074 this->parser = NULL;
1075 demux = this->demuxer;
1076 this->demuxer = NULL;
1077 g_free (playlist_gen_id);
1078 playlist_gen_id = NULL;
1079 g_free (broadcast_id);
1080 broadcast_id = NULL;
1081 Unlock ();
1083 if (mms_source != NULL)
1084 mms_source->unref ();
1086 if (asf_parser != NULL)
1087 asf_parser->unref ();
1089 if (demux != NULL)
1090 demux->unref ();
1092 queue.Clear (true);
1093 // This is a bit weird - in certain
1094 // we can end up with a circular dependency between
1095 // Media and MmsPlaylistEntry, where Media::Dispose
1096 // isn't called. So if Media::Dispose hasn't been
1097 // called, do it here, and only do it after our
1098 // instance copy of the media is cleared out to
1099 // prevent infinite loops.
1100 Media *m = GetMediaReffed ();
1102 IMediaSource::Dispose ();
1104 if (m != NULL) {
1105 if (!m->IsDisposed ())
1106 m->Dispose ();
1107 m->unref ();
1111 MediaResult
1112 MmsPlaylistEntry::SeekToPts (guint64 pts)
1114 MmsSource *ms = GetParentReffed ();
1115 if (ms) {
1116 ms->SeekToPts (pts);
1117 ms->unref ();
1118 queue.Clear (true);
1119 return MEDIA_SUCCESS;
1120 } else {
1121 fprintf (stderr, "MmsPlaylistEntry::SeekToPts (%" G_GUINT64_FORMAT "): Could not seek to pts, no parent.\n", pts);
1122 return MEDIA_FAIL;
1126 void
1127 MmsPlaylistEntry::NotifyFinished ()
1129 finished = true;
1132 void
1133 MmsPlaylistEntry::GetSelectedStreams (gint64 max_bitrate, gint8 streams [128])
1135 ASFParser *parser;
1136 asf_file_properties *properties;
1137 gint32 audio_bitrates [128];
1138 gint32 video_bitrates [128];
1140 memset (audio_bitrates, 0xff, 128 * 4);
1141 memset (video_bitrates, 0xff, 128 * 4);
1142 memset (streams, 0xff, 128);
1144 parser = GetParserReffed ();
1146 g_return_if_fail (parser != NULL);
1148 properties = parser->GetFileProperties ();
1150 g_return_if_fail (properties != NULL);
1152 for (int i = 1; i < 127; i++) {
1153 int current_stream;
1154 if (!parser->IsValidStream (i)) {
1155 streams [i] = -1; // inexistent
1156 continue;
1158 streams [i] = 0; // disabled
1159 current_stream = i;
1161 const asf_stream_properties *stream_properties = parser->GetStream (current_stream);
1162 const asf_extended_stream_properties *extended_stream_properties = parser->GetExtendedStream (current_stream);
1164 if (stream_properties == NULL) {
1165 printf ("MmsPlaylistEntry::GetSelectedStreams (): stream #%i doesn't have any stream properties.\n", current_stream);
1166 continue;
1169 if (stream_properties->is_audio ()) {
1170 const WAVEFORMATEX* wave = stream_properties->get_audio_data ();
1171 audio_bitrates [current_stream] = wave->bytes_per_second * 8;
1172 } else if (stream_properties->is_video ()) {
1173 int bit_rate = 0;
1174 const asf_video_stream_data* video_data = stream_properties->get_video_data ();
1175 const BITMAPINFOHEADER* bmp;
1177 if (extended_stream_properties != NULL) {
1178 bit_rate = extended_stream_properties->data_bitrate;
1179 } else if (video_data != NULL) {
1180 bmp = video_data->get_bitmap_info_header ();
1181 if (bmp != NULL) {
1182 bit_rate = bmp->image_width*bmp->image_height;
1186 video_bitrates [current_stream] = bit_rate;
1187 } else if (stream_properties->is_command ()) {
1188 // we select all marker streams
1189 streams [current_stream] = 1;
1193 // select the video stream
1194 int video_stream = 0;
1195 int video_rate = 0;
1197 for (int i = 0; i < 128; i++) {
1198 int stream_rate = video_bitrates [i];
1200 if (stream_rate == -1)
1201 continue;
1203 if (video_rate == 0) {
1204 video_rate = stream_rate;
1205 video_stream = i;
1208 if (stream_rate > video_rate && stream_rate < (max_bitrate * VIDEO_BITRATE_PERCENTAGE)) {
1209 video_rate = stream_rate;
1210 video_stream = i;
1213 streams [video_stream] = 1; // selected
1214 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected video stream %i of rate %i\n", video_stream, video_rate);
1217 // select audio stream
1218 int audio_stream = 0;
1219 int audio_rate = 0;
1221 for (int i = 0; i < 128; i++) {
1222 int stream_rate = audio_bitrates [i];
1224 if (stream_rate == -1)
1225 continue;
1227 if (audio_rate == 0) {
1228 audio_rate = stream_rate;
1229 audio_stream = i;
1232 if (stream_rate > audio_rate && stream_rate < (max_bitrate * AUDIO_BITRATE_PERCENTAGE)) {
1233 audio_rate = stream_rate;
1234 audio_stream = i;
1237 streams [audio_stream] = 1; // selected
1238 LOG_MMS ("MmsPlaylistEntry::GetSelectedStreams (): Selected audio stream %i of rate %i\n", audio_stream, audio_rate);
1240 parser->unref ();
1243 bool
1244 MmsPlaylistEntry::IsHeaderParsed ()
1246 bool result;
1247 Lock ();
1248 result = parser != NULL;
1249 Unlock ();
1250 return result;
1253 IMediaDemuxer *
1254 MmsPlaylistEntry::GetDemuxerReffed ()
1256 // thread safe
1257 IMediaDemuxer *result;
1259 Lock ();
1260 result = demuxer;
1261 if (result)
1262 result->ref ();
1263 Unlock ();
1265 return result;
1268 ASFParser *
1269 MmsPlaylistEntry::GetParserReffed ()
1271 // thread safe
1272 ASFParser *result;
1274 Lock ();
1275 result = parser;
1276 if (result)
1277 result->ref ();
1278 Unlock ();
1280 return result;
1283 MmsSource *
1284 MmsPlaylistEntry::GetParentReffed ()
1286 // thread safe
1287 MmsSource *result;
1289 Lock ();
1290 result = parent;
1291 if (result)
1292 result->ref ();
1293 Unlock ();
1295 return result;
1298 IMediaDemuxer *
1299 MmsPlaylistEntry::CreateDemuxer (Media *media)
1301 // thread safe
1302 ASFDemuxer *result;
1303 ASFParser *asf_parser;
1305 asf_parser = GetParserReffed ();
1307 g_return_val_if_fail (media != NULL, NULL);
1308 g_return_val_if_fail (asf_parser != NULL, NULL);
1309 g_return_val_if_fail (demuxer == NULL, NULL);
1311 result = new ASFDemuxer (media, this);
1312 result->SetParser (asf_parser);
1314 Lock ();
1315 if (demuxer != NULL)
1316 demuxer->unref ();
1317 demuxer = result;
1318 demuxer->ref ();
1319 Unlock ();
1321 asf_parser->unref ();
1323 return result;
1326 void
1327 MmsPlaylistEntry::SetPlaylistGenId (const char *value)
1329 // thread safe
1330 Lock ();
1331 g_free (playlist_gen_id);
1332 playlist_gen_id = g_strdup (value);
1333 Unlock ();
1336 char *
1337 MmsPlaylistEntry::GetPlaylistGenId ()
1339 // thread safe
1340 char *result;
1341 Lock ();
1342 result = g_strdup (playlist_gen_id);
1343 Unlock ();
1344 return result;
1347 void
1348 MmsPlaylistEntry::SetBroadcastId (const char *value)
1350 // thread safe
1351 Lock ();
1352 g_free (broadcast_id);
1353 broadcast_id = g_strdup (value);
1354 Unlock ();
1357 char *
1358 MmsPlaylistEntry::GetBroadcastId ()
1360 // thread safe
1361 char *result;
1362 Lock ();
1363 result = g_strdup (broadcast_id);
1364 Unlock ();
1365 return result;
1368 void
1369 MmsPlaylistEntry::SetHttpStreamingFeatures (HttpStreamingFeatures value)
1371 features = value;
1374 HttpStreamingFeatures
1375 MmsPlaylistEntry::GetHttpStreamingFeatures ()
1377 return features;
1380 void
1381 MmsPlaylistEntry::AddEntry ()
1383 VERIFY_MAIN_THREAD;
1385 Media *media = GetMediaReffed ();
1386 Playlist *playlist;
1387 PlaylistEntry *entry;
1388 MmsDemuxer *mms_demuxer = NULL;
1390 g_return_if_fail (media != NULL);
1392 if (parent == NULL)
1393 goto cleanup;
1395 mms_demuxer = parent->GetDemuxerReffed ();
1397 if (mms_demuxer == NULL)
1398 goto cleanup;
1400 playlist = mms_demuxer->GetPlaylist ();
1402 if (playlist == NULL)
1403 goto cleanup;
1405 entry = new PlaylistEntry (playlist);
1406 entry->SetIsLive (features & HttpStreamingBroadcast);
1408 playlist->AddEntry (entry);
1410 entry->InitializeWithSource (this);
1412 cleanup:
1413 if (media)
1414 media->unref ();
1415 if (mms_demuxer)
1416 mms_demuxer->unref ();
1419 MediaResult
1420 MmsPlaylistEntry::ParseHeader (void *buffer, gint32 size)
1422 VERIFY_MAIN_THREAD;
1424 LOG_MMS ("MmsPlaylistEntry::ParseHeader (%p, %i)\n", buffer, size);
1426 MediaResult result;
1427 MemorySource *asf_src = NULL;
1428 Media *media;
1429 ASFParser *asf_parser;
1431 // this method shouldn't get called more than once
1432 g_return_val_if_fail (parser == NULL, MEDIA_FAIL);
1434 media = GetMediaReffed ();
1435 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
1437 media->ReportDownloadProgress (1.0);
1439 asf_src = new MemorySource (media, buffer, size, 0, false);
1440 asf_parser = new ASFParser (asf_src, media);
1441 result = asf_parser->ReadHeader ();
1442 asf_src->unref ();
1443 media->unref ();
1445 if (MEDIA_SUCCEEDED (result)) {
1446 Lock ();
1447 if (parser)
1448 parser->unref ();
1449 parser = asf_parser;
1450 Unlock ();
1451 AddEntry ();
1452 } else {
1453 asf_parser->unref ();
1456 return result;
1459 ASFPacket *
1460 MmsPlaylistEntry::Pop ()
1462 // thread safe
1463 //LOG_MMS ("MmsSource::Pop (), there are %i packets in the queue, of a total of %" G_GINT64_FORMAT " packets written.\n", queue.Length (), write_count);
1465 QueueNode *node;
1466 ASFPacket *result = NULL;
1467 ASFParser *parser;
1469 trynext:
1470 node = (QueueNode *) queue.Pop ();
1472 if (node == NULL) {
1473 LOG_PIPELINE_ASF ("MmsSource::Pop (): No more packets (for now).\n");
1474 return NULL;
1477 parser = GetParserReffed ();
1479 if (node->packet == NULL) {
1480 if (parser == NULL) {
1481 g_warning ("MmsSource::Pop (): No parser to parse the packet.\n");
1482 goto cleanup;
1484 node->packet = new ASFPacket (parser, node->source);
1485 if (!MEDIA_SUCCEEDED (node->packet->Read ())) {
1486 LOG_PIPELINE_ASF ("MmsSource::Pop (): Error while parsing packet, getting a new packet\n");
1487 delete node;
1488 goto trynext;
1492 result = node->packet;
1493 result->ref ();
1495 cleanup:
1496 delete node;
1498 if (parser)
1499 parser->unref ();
1501 LOG_PIPELINE_ASF ("MmsSource::Pop (): popped 1 packet, there are %i packets left, of a total of %" G_GINT64_FORMAT " packets written\n", queue.Length (), write_count);
1503 return result;
1506 bool
1507 MmsPlaylistEntry::IsFinished ()
1509 bool result;
1510 return parent->IsFinished (); // REMOVE
1511 MmsSource *src = GetParentReffed ();
1513 g_return_val_if_fail (src != NULL, true);
1515 result = src->IsFinished ();
1516 src->unref ();
1518 return result;
1521 void
1522 MmsPlaylistEntry::WritePacket (void *buf, gint32 n)
1524 MemorySource *src;
1525 ASFPacket *packet;
1526 ASFParser *asf_parser;
1527 Media *media;
1528 bool added = false;
1530 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i), write_count: %" G_GINT64_FORMAT "\n", buf, n, write_count + 1);
1531 VERIFY_MAIN_THREAD;
1533 media = GetMediaReffed ();
1535 g_return_if_fail (media != NULL);
1537 write_count++;
1539 asf_parser = GetParserReffed ();
1541 if (asf_parser != NULL) {
1542 src = new MemorySource (media, buf, n, 0, false);
1543 packet = new ASFPacket (asf_parser, src);
1544 if (!MEDIA_SUCCEEDED (packet->Read ())) {
1545 LOG_PIPELINE_ASF ("MmsPlaylistEntry::WritePacket (%p, %i): Error while parsing packet, dropping packet.\n", buf, n);
1546 } else {
1547 queue.Push (new QueueNode (packet));
1548 added = true;
1550 packet->unref ();
1551 src->unref ();
1552 } else {
1553 src = new MemorySource (media, g_memdup (buf, n), n, 0);
1554 queue.Push (new QueueNode (src));
1555 src->unref ();
1556 added = true;
1559 if (added) {
1560 IMediaDemuxer *demuxer = GetDemuxerReffed ();
1561 if (demuxer) {
1562 demuxer->FillBuffers ();
1563 demuxer->unref ();
1566 if (asf_parser)
1567 asf_parser->unref ();
1569 if (media)
1570 media->unref ();
1574 * MmsPlaylistEntry::QueueNode
1577 MmsPlaylistEntry::QueueNode::QueueNode (MemorySource *source)
1579 if (source)
1580 source->ref ();
1581 this->source = source;
1582 packet = NULL;
1585 MmsPlaylistEntry::QueueNode::QueueNode (ASFPacket *packet)
1587 if (packet)
1588 packet->ref ();
1589 this->packet = packet;
1590 source = NULL;
1593 MmsPlaylistEntry::QueueNode::~QueueNode ()
1595 if (packet)
1596 packet->unref ();
1597 if (source)
1598 source->unref ();
1602 * MmsDemuxer
1605 MmsDemuxer::MmsDemuxer (Media *media, MmsSource *source)
1606 : IMediaDemuxer (Type::MMSDEMUXER, media, source)
1608 playlist = NULL;
1609 mms_source = source;
1610 if (mms_source)
1611 mms_source->ref ();
1614 void
1615 MmsDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
1617 printf ("MmsDemuxer::GetFrameAsyncInternal (%p): This method should never be called.\n", stream);
1620 void
1621 MmsDemuxer::OpenDemuxerAsyncInternal ()
1623 PlaylistRoot *root;
1624 Media *media;
1626 LOG_MMS ("MmsDemuxer::OpenDemuxerAsyncInternal ().\n");
1628 media = GetMediaReffed ();
1629 root = media ? media->GetPlaylistRoot () : NULL;
1631 g_return_if_fail (playlist == NULL);
1632 g_return_if_fail (media != NULL);
1633 g_return_if_fail (root != NULL);
1635 playlist = new Playlist (root, mms_source);
1636 ReportOpenDemuxerCompleted ();
1637 media->unref ();
1640 MediaResult
1641 MmsDemuxer::SeekInternal (guint64 pts)
1643 g_warning ("MmsDemuxer::SeekInternal (%" G_GINT64_FORMAT "): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", pts);
1644 print_stack_trace ();
1646 return MEDIA_FAIL;
1649 void
1650 MmsDemuxer::SeekAsyncInternal (guint64 seekToTime)
1652 printf ("MmsDemuxer::SeekAsyncInternal (%" G_GUINT64_FORMAT "): Not implemented.\n", seekToTime);
1655 void
1656 MmsDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *stream)
1658 printf ("MmsDemuxer::SwitchMediaStreamAsyncInternal (%p): Not implemented.\n", stream);
1661 void
1662 MmsDemuxer::Dispose ()
1664 Playlist *pl;
1665 MmsSource *src;
1667 mutex.Lock ();
1668 pl = this->playlist;
1669 this->playlist = NULL;
1670 src = this->mms_source;
1671 this->mms_source = NULL;
1672 mutex.Unlock ();
1674 if (pl)
1675 pl->unref ();
1677 if (src)
1678 src->unref ();
1680 IMediaDemuxer::Dispose ();