2009-10-20 Chris Toshok <toshok@ximian.com>
[moon.git] / src / pipeline.cpp
blob4d659b08c8e3ad20fa78ff115543fa26f7e3d96f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * pipeline.cpp: Pipeline for the media
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 <glib/gstdio.h>
18 #include <fcntl.h>
19 #include <errno.h>
21 #include <dlfcn.h>
23 #include "audio.h"
24 #include "pipeline.h"
25 #include "codec-version.h"
26 #include "pipeline-ffmpeg.h"
27 #include "mp3.h"
28 #include "uri.h"
29 #include "media.h"
30 #include "mediaelement.h"
31 #include "asf/asf.h"
32 #include "asf/asf-structures.h"
33 #include "yuv-converter.h"
34 #include "runtime.h"
35 #include "mms-downloader.h"
36 #include "pipeline-ui.h"
37 #include "pipeline-asf.h"
38 #include "playlist.h"
39 #include "deployment.h"
40 #include "timesource.h"
43 * Media
46 bool Media::registering_ms_codecs = false;
47 bool Media::registered_ms_codecs = false;
49 DemuxerInfo *Media::registered_demuxers = NULL;
50 DecoderInfo *Media::registered_decoders = NULL;
51 ConverterInfo *Media::registered_converters = NULL;
53 Media::Media (PlaylistRoot *root)
54 : IMediaObject (Type::MEDIA, this)
56 LOG_PIPELINE ("Media::Media (), id: %i\n", GET_OBJ_ID (this));
58 playlist = root;
59 buffering_time = 0;
60 file = NULL;
61 uri = NULL;
62 source = NULL;
63 demuxer = NULL;
64 markers = NULL;
66 is_disposed = false;
67 initialized = false;
68 opened = false;
69 opening = false;
70 stopped = false;
71 error_reported = false;
72 buffering_enabled = false;
73 in_open_internal = false;
74 http_retried = false;
75 download_progress = 0.0;
76 buffering_progress = 0.0;
78 if (!GetDeployment ()->RegisterMedia (this))
79 Dispose ();
82 Media::~Media ()
84 LOG_PIPELINE ("Media::~Media (), id: %i\n", GET_OBJ_ID (this));
87 void
88 Media::Dispose ()
90 IMediaSource *src;
91 IMediaDemuxer *dmx;
92 bool was_disposed = false;
94 LOG_PIPELINE ("Media::Dispose (), id: %i\n", GET_OBJ_ID (this));
96 mutex.Lock ();
97 was_disposed = is_disposed;
98 is_disposed = true;
99 mutex.Unlock ();
102 * Don't run our dispose code more than once, we may end up deleted on the main thread
103 * which will cause Dispose to be called on the main thread too. Since Dispose must already
104 * have been called on the media thread, we're safe.
106 if (was_disposed) {
107 IMediaObject::Dispose ();
108 return;
111 #if SANITY
112 if (!MediaThreadPool::IsThreadPoolThread ()) {
113 g_warning ("Media::Dispose (): Not in thread-pool thread, and we haven't been disposed already.\n");
115 #endif
117 ClearQueue ();
120 * We're on a media thread, and there is no other work in the queue: we can ensure that nothing
121 * more will ever execute on the media thread related to this Media instance.
124 g_free (file);
125 file = NULL;
126 g_free (uri);
127 uri = NULL;
129 src = this->source;
130 this->source = NULL;
131 if (src) {
132 src->Dispose ();
133 src->unref ();
136 dmx = this->demuxer;
137 this->demuxer = NULL;
138 if (dmx) {
139 dmx->Dispose ();
140 dmx->unref ();
143 delete markers;
144 markers = NULL;
146 IMediaObject::Dispose ();
148 GetDeployment ()->UnregisterMedia (this);
151 bool
152 Media::IsMSCodecsInstalled ()
154 return registered_ms_codecs;
157 void
158 Media::RegisterMSCodecs (void)
160 register_codec reg;
161 void *dl;
162 char *libmscodecs_path = NULL;
163 const char *functions [] = {"register_codec_pack", NULL};
164 const gchar *home = g_get_home_dir ();
165 registering_ms_codecs = true;
167 if (!(moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS)) {
168 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
169 return;
172 if (home != NULL)
173 libmscodecs_path = g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME, NULL);
175 if (!(g_file_test (libmscodecs_path, G_FILE_TEST_EXISTS) && g_file_test (libmscodecs_path, G_FILE_TEST_IS_REGULAR))) {
176 if (libmscodecs_path)
177 g_free (libmscodecs_path);
178 libmscodecs_path = g_strdup (CODEC_LIBRARY_NAME);
181 dl = dlopen (libmscodecs_path, RTLD_LAZY);
182 if (dl != NULL) {
183 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path);
185 for (int i = 0; functions [i] != NULL; i++) {
186 reg = (register_codec) dlsym (dl, functions [i]);
187 if (reg != NULL) {
188 (*reg) (MOONLIGHT_CODEC_ABI_VERSION);
189 } else {
190 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions [i], libmscodecs_path);
193 registered_ms_codecs = true;
194 } else {
195 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path, dlerror ());
197 g_free (libmscodecs_path);
199 registering_ms_codecs = false;
202 void
203 Media::SetBufferingEnabled (bool value)
205 buffering_enabled = value;
206 WakeUp ();
209 void
210 Media::SetBufferingTime (guint64 buffering_time)
212 mutex.Lock ();
213 this->buffering_time = buffering_time;
214 mutex.Unlock ();
216 if (demuxer != NULL)
217 demuxer->FillBuffers ();
220 guint64
221 Media::GetBufferingTime ()
223 guint64 result;
224 mutex.Lock ();
225 result = buffering_time;
226 mutex.Unlock ();
227 return result;
230 PlaylistRoot *
231 Media::GetPlaylistRoot ()
233 return playlist;
236 List *
237 Media::GetMarkers ()
239 if (markers == NULL)
240 markers = new List ();
242 return markers;
245 void
246 Media::RegisterDemuxer (DemuxerInfo *info)
248 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
249 info->next = NULL;
250 if (registered_demuxers == NULL) {
251 registered_demuxers = info;
252 } else {
253 MediaInfo* current = registered_demuxers;
254 while (current->next != NULL)
255 current = current->next;
256 current->next = info;
260 void
261 Media::RegisterConverter (ConverterInfo *info)
263 //printf ("Media::RegisterConverter (%p)\n", info);
264 info->next = NULL;
265 if (registered_converters == NULL) {
266 registered_converters = info;
267 } else {
268 MediaInfo *current = registered_converters;
269 while (current->next != NULL)
270 current = current->next;
271 current->next = info;
275 void
276 Media::RegisterDecoder (DecoderInfo *info)
278 MediaInfo *current;
280 //printf ("Media::RegisterDecoder (%p)\n", info);
281 info->next = NULL;
282 if (registered_decoders == NULL) {
283 registered_decoders = info;
284 } else {
285 if (registering_ms_codecs) {
286 // MS codecs might get registered after all other codecs (right after installing them),
287 // which means after the null codecs so if they don't get special treatment, they won't
288 // get used until the next browser restart (when they're registered normally).
289 // So instead of appending them, we prepend them.
290 info->next = registered_decoders;
291 registered_decoders = info;
292 } else {
293 current = registered_decoders;
294 while (current->next != NULL)
295 current = current->next;
296 current->next = info;
299 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info->GetName ());
302 void
303 Media::Initialize ()
305 LOG_PIPELINE ("Media::Initialize ()\n");
307 // demuxers
308 Media::RegisterDemuxer (new ASFDemuxerInfo ());
309 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
310 Media::RegisterDemuxer (new ASXDemuxerInfo ());
312 // converters
313 if (!(moonlight_flags & RUNTIME_INIT_FFMPEG_YUV_CONVERTER))
314 Media::RegisterConverter (new YUVConverterInfo ());
316 // decoders
317 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
318 if (moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS) {
319 RegisterMSCodecs ();
321 #ifdef INCLUDE_FFMPEG
322 if (!(moonlight_flags & RUNTIME_INIT_DISABLE_FFMPEG_CODECS)) {
323 register_ffmpeg ();
325 #endif
327 Media::RegisterDecoder (new PassThroughDecoderInfo ());
328 Media::RegisterDecoder (new NullDecoderInfo ());
330 MediaThreadPool::Initialize ();
333 void
334 Media::Shutdown ()
336 LOG_PIPELINE ("Media::Shutdown ()\n");
338 MediaInfo *current;
339 MediaInfo *next;
341 // Make sure all threads are stopped
342 AudioPlayer::Shutdown ();
343 MediaThreadPool::Shutdown ();
345 current = registered_decoders;
346 while (current != NULL) {
347 next = current->next;
348 delete current;
349 current = next;
351 registered_decoders = NULL;
353 current = registered_demuxers;
354 while (current != NULL) {
355 next = current->next;
356 delete current;
357 current = next;
359 registered_demuxers = NULL;
361 current = registered_converters;
362 while (current != NULL) {
363 next = current->next;
364 delete current;
365 current = next;
367 registered_converters = NULL;
369 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
372 void
373 Media::Warning (MediaResult result, const char *format, ...)
375 va_list args;
377 if (MEDIA_SUCCEEDED (result))
378 return;
380 fprintf (stderr, "Moonlight: MediaResult = %d; ", result);
382 va_start (args, format);
383 vfprintf (stderr, format, args);
384 va_end (args);
386 fputc ('\n', stderr);
389 bool
390 Media::InMediaThread ()
392 return MediaThreadPool::IsThreadPoolThread ();
395 void
396 Media::ReportBufferingProgress (double progress)
398 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress, buffering_progress);
400 progress = MAX (MIN (progress, 1.0), 0.0);
402 if (progress == buffering_progress)
403 return;
405 if (progress < buffering_progress || progress > (buffering_progress + 0.005) || progress == 1.0 || progress == 0.0) {
406 buffering_progress = progress;
407 EmitSafe (BufferingProgressChangedEvent, new ProgressEventArgs (progress));
411 void
412 Media::ReportDownloadProgress (double progress)
414 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress, download_progress);
416 progress = MAX (MIN (progress, 1.0), 0.0);
418 if (progress <= download_progress) {
420 * Download progress percentage can actually go down - if the file size
421 * goes up. Yes, the file size can go up.
423 return;
426 if (progress > (download_progress + 0.005) || progress == 1.0 || progress == 0.0) {
427 download_progress = progress;
428 EmitSafe (DownloadProgressChangedEvent, new ProgressEventArgs (progress));
432 void
433 Media::SeekAsync (guint64 pts)
435 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
437 if (demuxer == NULL) {
438 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
439 return;
442 demuxer->SeekAsync (pts);
445 void
446 Media::ReportSeekCompleted (guint64 pts)
448 LOG_PIPELINE ("Media::ReportSeekCompleted (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
450 buffering_progress = 0;
451 ClearQueue ();
452 EmitSafe (SeekCompletedEvent);
455 void
456 Media::ReportOpenCompleted ()
458 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
460 EmitSafe (OpenCompletedEvent);
463 void
464 Media::ReportOpenDemuxerCompleted ()
466 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
468 OpenInternal ();
471 void
472 Media::ReportOpenDecoderCompleted (IMediaDecoder *decoder)
474 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder, GET_OBJ_ID (this));
476 g_return_if_fail (decoder != NULL);
478 OpenInternal ();
481 void
482 Media::ReportErrorOccurred (ErrorEventArgs *args)
484 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args, args == NULL ? NULL : args->GetErrorMessage());
486 if (args) {
487 fprintf (stderr, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args->GetErrorType()), args->GetErrorCode(), args->GetErrorMessage(), args->GetExtendedMessage());
488 } else {
489 fprintf (stderr, "Moonlight: Unspecified media error.\n");
492 if (!error_reported) {
493 error_reported = true;
494 EmitSafe (MediaErrorEvent, args);
498 void
499 Media::ReportErrorOccurred (const char *message)
501 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message);
503 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, message)));
506 void
507 Media::ReportErrorOccurred (MediaResult result)
509 char *msg = g_strdup_printf ("Media error: %i.", result);
510 ReportErrorOccurred (msg);
511 g_free (msg);
514 void
515 Media::PlayAsync ()
517 LOG_PIPELINE ("Media::PlayAsync ()\n");
519 MediaClosure *closure = new MediaClosure (this, PlayCallback, this, "Media::PlayAsync");
520 EnqueueWork (closure);
521 closure->unref ();
524 void
525 Media::PauseAsync ()
527 LOG_PIPELINE ("Media::PauseAsync ()\n");
530 void
531 Media::StopAsync ()
533 LOG_PIPELINE ("Media::StopAsync ()\n");
535 MediaClosure *closure = new MediaClosure (this, StopCallback, this, "Media::StopAsync");
536 EnqueueWork (closure);
537 closure->unref ();
540 MediaResult
541 Media::StopCallback (MediaClosure *closure)
543 closure->GetMedia ()->Stop ();
544 return MEDIA_SUCCESS;
547 MediaResult
548 Media::PlayCallback (MediaClosure *closure)
550 closure->GetMedia ()->Play ();
551 return MEDIA_SUCCESS;
554 void
555 Media::Stop ()
557 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
559 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
561 stopped = true;
563 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
564 /* ClearQueue (); */
566 if (demuxer != NULL)
567 demuxer->ClearBuffers ();
570 void
571 Media::Play ()
573 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
575 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
577 stopped = false;
578 if (demuxer != NULL)
579 demuxer->FillBuffers ();
582 void
583 Media::Initialize (Downloader *downloader, const char *PartName)
585 IMediaSource *source;
587 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader, PartName, GET_OBJ_ID (this));
589 g_return_if_fail (downloader != NULL);
590 g_return_if_fail (file == NULL);
591 g_return_if_fail (uri != NULL || PartName != NULL);
592 g_return_if_fail (initialized == false);
593 g_return_if_fail (error_reported == false);
594 g_return_if_fail (this->source == NULL);
596 if (downloader->Completed ()) {
597 file = downloader->GetDownloadedFilename (PartName);
599 if (file == NULL) {
600 ReportErrorOccurred ("Couldn't get downloaded filename.");
601 return;
605 if (file == NULL && PartName != NULL && PartName [0] != 0) {
606 ReportErrorOccurred ("We don't support using media in zip files which haven't been downloaded yet (i.e. calling MediaElement.SetSource (dl, 'foo') with a dl which hasn't downloaded the file yet)");
607 return;
610 if (file == NULL) {
611 InternalDownloader *idl = downloader->GetInternalDownloader ();
612 MmsDownloader *mms_dl = (idl && idl->GetObjectType () == Type::MMSDOWNLOADER) ? (MmsDownloader *) idl : NULL;
614 if (mms_dl == NULL) {
615 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
616 return;
619 source = new MmsSource (this, downloader);
620 } else {
621 source = new FileSource (this, file);
624 Initialize (source);
625 source->unref ();
628 void
629 Media::Initialize (IMediaSource *source)
631 MediaResult result;
633 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source, GET_OBJ_ID (this));
635 g_return_if_fail (source != NULL);
636 g_return_if_fail (this->source == NULL);
637 g_return_if_fail (initialized == false);
639 result = source->Initialize ();
640 if (!MEDIA_SUCCEEDED (result)) {
641 ReportErrorOccurred (result);
642 return;
645 initialized = true;
646 this->source = source;
647 this->source->ref ();
650 void
651 Media::Initialize (const char *uri)
653 Downloader *dl;
654 IMediaSource *source = NULL;
656 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri, GET_OBJ_ID (this));
658 g_return_if_fail (uri != NULL);
659 g_return_if_fail (file == NULL);
660 g_return_if_fail (uri != NULL);
661 g_return_if_fail (initialized == false);
662 g_return_if_fail (error_reported == false);
663 g_return_if_fail (source == NULL);
664 g_return_if_fail (this->source == NULL);
666 this->uri = g_strdup (uri);
669 if (g_str_has_prefix (uri, "mms://") || g_str_has_prefix (uri, "rtsp://") || g_str_has_prefix (uri, "rtsps://")) {
670 dl = Surface::CreateDownloader (this);
671 if (dl == NULL) {
672 ReportErrorOccurred ("Couldn't create downloader.");
673 return;
676 dl->Open ("GET", uri, StreamingPolicy);
678 if (dl->GetFailedMessage () == NULL) {
679 Initialize (dl, NULL);
680 } else {
681 ReportErrorOccurred (new ErrorEventArgs (MediaError,
682 MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
685 dl->unref ();
687 return;
690 source = new ProgressiveSource (this, uri);
691 Initialize (source);
692 source->unref ();
695 void
696 Media::Initialize (IMediaDemuxer *demuxer)
698 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer, GET_OBJ_ID (this));
700 g_return_if_fail (demuxer != NULL);
701 g_return_if_fail (this->demuxer == NULL);
702 g_return_if_fail (initialized == false);
704 this->demuxer = demuxer;
705 this->demuxer->ref ();
707 initialized = true;
711 void
712 Media::RetryHttp (ErrorEventArgs *args)
714 char *http_uri = NULL;
716 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri);
718 g_return_if_fail (uri != NULL);
719 g_return_if_fail (source != NULL);
721 if (http_retried) {
722 ReportErrorOccurred (args);
723 return;
726 // CHECK: If the current protocolo is rtsps, should we retry http or https?
728 if (g_str_has_prefix (uri, "mms://")) {
729 http_uri = g_strdup_printf ("http://%s", uri + 6);
730 } else if (g_str_has_prefix (uri, "rtsp://")) {
731 http_uri = g_strdup_printf ("http://%s", uri + 7);
732 } else if (g_str_has_prefix (uri, "rtsps://")) {
733 http_uri = g_strdup_printf ("http://%s", uri + 8);
734 } else {
735 ReportErrorOccurred (args);
736 return;
739 http_retried = true;
741 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri);
743 g_free (uri);
744 uri = NULL;
745 /* this method is called on the main thread, ensure Dispose is called on the source on the media thread */
746 DisposeObject (source);
747 source->unref ();
748 source = NULL;
749 initialized = false;
750 error_reported = false;
752 Initialize (http_uri);
754 g_free (http_uri);
756 if (!error_reported)
757 OpenAsync ();
760 void
761 Media::OpenAsync ()
763 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
765 g_return_if_fail (initialized == true);
767 EmitSafe (OpeningEvent);
769 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenAsync");
770 EnqueueWork (closure);
771 closure->unref ();
774 void
775 Media::OpenInternal ()
777 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
779 g_return_if_fail (initialized == true);
781 if (opened) {
782 // This may happen due to the recursion detection below
783 // Example: we try open a demuxer, the demuxer opens successfully
784 // right away and calls ReportDemuxerOpenComplete which will call
785 // us. Due to the recursion detection we'll enqueue a call to
786 // OpenInternal, while the first OpenInternal may succeed and
787 // set opened to true.
788 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
789 return;
792 // detect recursive calls.
794 if (in_open_internal) {
795 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
796 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
797 EnqueueWork (closure);
798 closure->unref ();
799 return;
802 in_open_internal = true;
804 if (error_reported)
805 goto cleanup;
807 if (!SelectDemuxerAsync ()) {
808 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
809 goto cleanup;
812 if (error_reported)
813 goto cleanup;
815 if (!SelectDecodersAsync ()) {
816 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
817 goto cleanup;
820 demuxer->FillBuffers ();
822 opened = true;
823 opening = false;
825 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
827 EmitSafe (OpenCompletedEvent);
829 cleanup:
830 in_open_internal = false;
833 MediaResult
834 Media::OpenInternal (MediaClosure *closure)
836 Media *media = (Media *) closure->GetContext ();
838 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
840 media->OpenInternal ();
842 return MEDIA_SUCCESS;
845 bool
846 Media::SelectDemuxerAsync ()
848 DemuxerInfo *demuxerInfo;
849 MediaResult support;
850 MediaResult result;
851 bool eof;
853 LOG_PIPELINE ("Media::SelectDemuxer () id: %i, demuxer: %p, IsOpened: %i, IsOpening: %i\n", GET_OBJ_ID (this), demuxer, demuxer ? demuxer->IsOpened () : -1, demuxer ? demuxer->IsOpening () : -1);
855 g_return_val_if_fail (error_reported == false, false);
856 g_return_val_if_fail (initialized == true, false);
858 // Check if demuxer already is open
859 if (demuxer != NULL) {
860 if (demuxer->IsOpened ())
861 return true;
862 if (!demuxer->IsOpening ())
863 demuxer->OpenDemuxerAsync ();
864 return demuxer->IsOpened ();
867 g_return_val_if_fail (source != NULL, false);
869 // Check if the source knows how to create the demuxer
870 demuxer = source->CreateDemuxer (this);
872 if (demuxer == NULL) { // No demuxer created, we need to find it ourselves.
873 // Check if we have at least 1024 bytes or eof
874 if (!source->IsPositionAvailable (16, &eof)) {
875 if (!eof) {
876 // We need to try again later.
877 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
879 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
880 EnqueueWork (closure, false);
881 closure->unref ();
883 return false;
887 // Select a demuxer
888 demuxerInfo = registered_demuxers;
889 while (demuxer == NULL && demuxerInfo != NULL) {
890 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo->GetName ());
891 support = demuxerInfo->Supports (source);
893 if (support == MEDIA_SUCCESS)
894 break;
896 result = support;
898 if (result == MEDIA_NOT_ENOUGH_DATA) {
899 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't determine whether it can handle the media or not due to not enough data being available yet.\n", demuxerInfo->GetName ());
901 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
902 EnqueueWork (closure, false);
903 closure->unref ();
905 return false;
908 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo->GetName ());
909 demuxerInfo = (DemuxerInfo *) demuxerInfo->next;
912 if (demuxerInfo == NULL) {
913 // No demuxer found, report an error
914 const char *source_name = file ? file : uri;
916 if (!source_name) {
917 switch (source->GetType ()) {
918 case MediaSourceTypeProgressive:
919 case MediaSourceTypeFile:
920 source_name = ((FileSource *) source)->GetFileName ();
921 break;
922 case MediaSourceTypeMms:
923 case MediaSourceTypeMmsEntry:
924 source_name = "live source";
925 break;
926 default:
927 source_name = "unknown source";
928 break;
931 char *msg = g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name);
932 ReportErrorOccurred (new ErrorEventArgs (MediaError,
933 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT"),
934 MEDIA_UNKNOWN_CODEC, msg));
935 g_free (msg);
936 return false;
939 // Found a demuxer
940 demuxer = demuxerInfo->Create (this, source);
941 } else {
942 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer->GetTypeName ());
945 if (demuxer->IsOpened ())
946 return true;
948 if (demuxer->IsOpening ())
949 return false;
951 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
953 demuxer->OpenDemuxerAsync ();
955 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
957 return demuxer != NULL && demuxer->IsOpened ();
960 bool
961 Media::SelectDecodersAsync ()
963 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
965 g_return_val_if_fail (error_reported == false, false);
966 g_return_val_if_fail (initialized == true, false);
968 if (demuxer == NULL) {
969 ReportErrorOccurred ("No demuxer to select decoders from.");
970 return false;
973 // If the demuxer has no streams (ASXDemuxer for instance)
974 // then just return success.
975 if (demuxer->GetStreamCount () == 0)
976 return true;
978 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
980 // Select codecs for each stream
981 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
982 IMediaStream *stream = demuxer->GetStream (i);
985 if (stream == NULL) {
986 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
987 return false;
990 if (stream->GetDecoder () != NULL)
991 continue;
993 const char *codec = stream->GetCodec ();
994 IMediaDecoder *decoder = NULL;
996 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec);
998 DecoderInfo *current_decoder = registered_decoders;
999 while (current_decoder != NULL && !current_decoder->Supports (codec)) {
1000 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder->GetName (), codec);
1001 current_decoder = (DecoderInfo*) current_decoder->next;
1004 if (current_decoder == NULL) {
1005 Media::Warning (MEDIA_UNKNOWN_CODEC, "Unknown codec: '%s'.", codec);
1006 continue;
1009 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder->GetName (), codec);
1010 decoder = current_decoder->Create (this, stream);
1012 stream->SetDecoder (decoder);
1013 decoder->unref ();
1016 if (error_reported)
1017 return false;
1019 // Open the codecs
1020 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1022 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1023 IMediaStream *stream = demuxer->GetStream (i);
1024 IMediaDecoder *decoder;
1026 if (stream == NULL)
1027 continue;
1029 decoder = stream->GetDecoder ();
1031 if (decoder == NULL) {
1032 ReportErrorOccurred (new ErrorEventArgs (MediaError,
1033 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT")));
1034 return false;
1037 if (decoder->IsOpening () || decoder->IsOpened ())
1038 continue;
1040 decoder->OpenDecoderAsync ();
1043 if (error_reported)
1044 return false;
1046 // Wait until all the codecs have opened
1047 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1049 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1050 IMediaStream *stream = demuxer->GetStream (i);
1051 IMediaDecoder *decoder;
1053 if (stream == NULL)
1054 continue;
1056 decoder = stream->GetDecoder ();
1058 if (decoder == NULL) {
1059 ReportErrorOccurred (MEDIA_FAIL);
1060 return false;
1063 if (decoder->IsOpening ()) {
1064 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
1065 EnqueueWork (closure, false);
1066 closure->unref ();
1067 return false;
1070 if (!decoder->IsOpened ()) {
1071 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1072 ReportErrorOccurred (MEDIA_FAIL);
1073 return false;
1078 // All the codecs have been opened now.
1079 // Find converters for each of them (whenever required).
1081 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1083 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1084 IMediaStream *stream = demuxer->GetStream (i);
1085 IMediaDecoder *decoder;
1087 if (stream == NULL)
1088 continue;
1090 decoder = stream->GetDecoder ();
1092 if (decoder == NULL) {
1093 ReportErrorOccurred (MEDIA_FAIL);
1094 return false;
1097 if (stream->GetType () != MediaTypeVideo)
1098 continue; // Only video streams need converters
1100 if (decoder->GetPixelFormat () == MoonPixelFormatRGB32 || decoder->GetPixelFormat () == MoonPixelFormatRGBA32)
1101 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1103 // Select converter for this stream
1104 VideoStream *vs = (VideoStream *) stream;
1106 ConverterInfo* current_conv = registered_converters;
1107 while (current_conv != NULL && !current_conv->Supports (decoder->GetPixelFormat (), MoonPixelFormatRGB32)) {
1108 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1109 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1110 current_conv = (ConverterInfo*) current_conv->next;
1114 if (current_conv == NULL) {
1115 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER);
1116 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1117 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1118 return false;
1121 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1122 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1124 vs->converter = current_conv->Create (this, vs);
1125 vs->converter->input_format = decoder->GetPixelFormat ();
1126 vs->converter->output_format = MoonPixelFormatRGB32;
1127 if (!MEDIA_SUCCEEDED (vs->converter->Open ())) {
1128 vs->converter->unref ();
1129 vs->converter = NULL;
1130 ReportErrorOccurred (MEDIA_FAIL);
1131 return false;
1135 // Loop through all the streams, return true if at least one has a codec.
1137 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1138 IMediaStream *stream = demuxer->GetStream (i);
1140 if (stream == NULL)
1141 continue;
1143 if (stream->GetDecoder () != NULL)
1144 return true;
1147 // No codecs found for no stream, report an error.
1148 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1149 return false;
1152 bool
1153 Media::EnqueueWork (MediaClosure *closure, bool wakeup)
1155 bool result = false;
1156 bool disposed;
1158 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure);
1160 g_return_val_if_fail (closure != NULL, false);
1162 if (IsDisposed ())
1163 return false;
1165 mutex.Lock ();
1166 disposed = this->is_disposed;
1167 if (disposed) {
1168 result = false;
1169 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed);
1170 } else {
1171 MediaThreadPool::AddWork (closure, wakeup);
1172 result = true;
1174 mutex.Unlock ();
1176 return result;
1179 MediaResult
1180 Media::DisposeObjectInternal (MediaClosure *closure)
1182 closure->GetContext ()->Dispose ();
1183 return MEDIA_SUCCESS;
1186 void
1187 Media::DisposeObject (EventObject *obj)
1189 MediaDisposeObjectClosure *closure = new MediaDisposeObjectClosure (this, DisposeObjectInternal, obj);
1190 if (!EnqueueWork (closure, true)) {
1191 LOG_PIPELINE ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj);
1192 obj->Dispose ();
1194 closure->unref ();
1197 void
1198 Media::WakeUp ()
1200 MediaThreadPool::WakeUp ();
1203 void
1204 Media::ClearQueue ()
1206 LOG_PIPELINE ("Media::ClearQueue ().\n");
1207 MediaThreadPool::RemoveWork (this);
1211 * ASXDemuxer
1214 ASXDemuxer::ASXDemuxer (Media *media, IMediaSource *source)
1215 : IMediaDemuxer (Type::ASXDEMUXER, media, source)
1217 playlist = NULL;
1220 ASXDemuxer::~ASXDemuxer ()
1224 void
1225 ASXDemuxer::Dispose ()
1227 if (playlist) {
1228 playlist->unref ();
1229 playlist = NULL;
1231 IMediaDemuxer::Dispose ();
1234 void
1235 ASXDemuxer::OpenDemuxerAsyncInternal ()
1237 MediaResult result;
1238 PlaylistRoot *root;
1239 ErrorEventArgs *args = NULL;
1240 Media *media = GetMediaReffed ();
1242 g_return_if_fail (media != NULL);
1244 root = media->GetPlaylistRoot ();
1246 g_return_if_fail (root != NULL);
1248 PlaylistParser *parser = new PlaylistParser (root, source);
1250 if (MEDIA_SUCCEEDED (parser->Parse ())) {
1251 result = MEDIA_SUCCESS;
1252 playlist = parser->GetPlaylist ();
1253 playlist->ref ();
1254 } else {
1255 result = MEDIA_FAIL;
1256 args = parser->GetErrorEventArgs ();
1257 if (args != NULL)
1258 args->ref ();
1261 delete parser;
1263 if (MEDIA_SUCCEEDED (result)) {
1264 ReportOpenDemuxerCompleted ();
1265 } else if (args != NULL) {
1266 args->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1267 ReportErrorOccurred (args);
1268 } else {
1269 ReportErrorOccurred (result);
1271 if (args)
1272 args->unref ();
1274 media->unref ();
1278 * ASXDemuxerInfo
1281 MediaResult
1282 ASXDemuxerInfo::Supports (IMediaSource *source)
1284 if (PlaylistParser::IsASX2 (source) || PlaylistParser::IsASX3 (source)) {
1285 return MEDIA_SUCCESS;
1286 } else {
1287 return MEDIA_FAIL;
1291 IMediaDemuxer *
1292 ASXDemuxerInfo::Create (Media *media, IMediaSource *source)
1294 return new ASXDemuxer (media, source);
1298 * ManagedStreamSource
1301 ManagedStreamSource::ManagedStreamSource (Media *media, ManagedStreamCallbacks *stream) : IMediaSource (Type::MANAGEDSTREAMSOURCE, media)
1303 memcpy (&this->stream, stream, sizeof (this->stream));
1306 ManagedStreamSource::~ManagedStreamSource ()
1308 stream.handle = NULL;
1311 gint32
1312 ManagedStreamSource::ReadInternal (void *buf, guint32 n)
1314 return stream.Read (stream.handle, buf, 0, n);
1317 gint32
1318 ManagedStreamSource::PeekInternal (void *buf, guint32 n)
1320 int read;
1322 read = stream.Read (stream.handle, buf, 0, n);
1323 stream.Seek (stream.handle, -read, 1 /* SeekOrigin.Current */);
1324 return read;
1327 bool
1328 ManagedStreamSource::SeekInternal (gint64 offset, int mode)
1330 stream.Seek (stream.handle, offset, mode /* FIXME: check if mode values matches SeekOrigin values */);
1331 return true;
1334 gint64
1335 ManagedStreamSource::GetPositionInternal ()
1337 return stream.Position (stream.handle);
1340 gint64
1341 ManagedStreamSource::GetSizeInternal ()
1343 return stream.Length (stream.handle);
1347 * FileSource
1350 FileSource::FileSource (Media *media, const char *filename) : IMediaSource (Type::FILESOURCE, media)
1352 this->filename = g_strdup (filename);
1353 fd = NULL;
1354 size = 0;
1355 temp_file = false;
1358 FileSource::FileSource (Media *media, bool temp_file) : IMediaSource (Type::FILESOURCE, media)
1360 filename = NULL;
1361 fd = NULL;
1362 size = 0;
1363 this->temp_file = temp_file;
1366 FileSource::~FileSource ()
1370 void
1371 FileSource::Dispose ()
1373 g_free (filename);
1374 filename = NULL;
1375 if (fd != NULL) {
1376 fclose (fd);
1377 fd = NULL;
1379 IMediaSource::Dispose ();
1382 MediaResult
1383 FileSource::Initialize ()
1385 int tmp_fd;
1387 LOG_PIPELINE ("FileSource::Initialize ()\n");
1389 if (fd != NULL)
1390 return MEDIA_SUCCESS;
1392 if (temp_file) {
1393 if (filename != NULL)
1394 return MEDIA_FILE_ERROR;
1396 filename = g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL);
1398 if ((tmp_fd = g_mkstemp (filename)) == -1) {
1399 g_free (filename);
1400 filename = NULL;
1402 return MEDIA_FAIL;
1405 fd = fdopen (tmp_fd, "r");
1407 setvbuf (fd, buffer, _IOFBF, sizeof (buffer));
1408 } else {
1409 if (filename == NULL)
1410 return MEDIA_FILE_ERROR;
1412 fd = g_fopen (filename, "r");
1415 if (fd == NULL)
1416 return MEDIA_FILE_ERROR;
1418 UpdateSize ();
1420 return MEDIA_SUCCESS;
1423 MediaResult
1424 FileSource::Open (const char *filename)
1426 g_return_val_if_fail (filename != NULL, MEDIA_FAIL);
1428 g_free (this->filename);
1429 this->filename = g_strdup (filename);
1431 if (fd != NULL) {
1432 fclose (fd);
1433 fd = NULL;
1436 fd = fopen (filename, "r");
1438 if (fd == NULL)
1439 return MEDIA_FAIL;
1441 UpdateSize ();
1443 return MEDIA_SUCCESS;
1446 void
1447 FileSource::UpdateSize ()
1449 struct stat st;
1451 g_return_if_fail (fd != NULL);
1453 if (fstat (fileno (fd), &st) != -1) {
1454 size = st.st_size;
1455 } else {
1456 size = 0;
1460 gint64
1461 FileSource::GetSizeInternal ()
1463 return size;
1466 gint64
1467 FileSource::GetPositionInternal ()
1469 gint64 result;
1471 if (fd == NULL)
1472 return -1;
1474 result = ftell (fd);
1476 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %" G_GINT64_FORMAT "\n", result);
1478 return result;
1481 bool
1482 FileSource::SeekInternal (gint64 offset, int mode)
1484 gint64 n;
1486 if (fd == NULL)
1487 return false;
1489 LOG_PIPELINE ("FileSource::SeekInternal (%" G_GINT64_FORMAT ", %i)\n", offset, mode);
1491 clearerr (fd);
1492 n = fseek (fd, offset, mode);
1494 return n != -1;
1497 gint32
1498 FileSource::ReadInternal (void *buf, guint32 n)
1500 ssize_t nread = 0;
1502 if (fd == NULL) {
1503 errno = EINVAL;
1504 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf, n);
1505 return -1;
1508 clearerr (fd);
1509 nread = fread (buf, 1, n, fd);
1511 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n, (int) nread);
1513 return nread;
1516 gint32
1517 FileSource::PeekInternal (void *buf, guint32 n)
1519 gint32 result;
1521 result = ReadSome (buf, n);
1523 Seek (-result, SEEK_CUR);
1525 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %" G_GINT64_FORMAT " [Done]\n", GET_OBJ_ID (this), buf, n, GetPosition ());
1527 return result;
1530 bool
1531 FileSource::Eof ()
1533 if (fd == NULL)
1534 return false;
1536 return feof (fd);
1540 * ProgressiveSource
1543 ProgressiveSource::ProgressiveSource (Media *media, const char *uri) : FileSource (media, true)
1545 write_pos = 0;
1546 size = -1;
1547 write_fd = NULL;
1548 cancellable = NULL;
1549 this->uri = g_strdup (uri);
1552 void
1553 ProgressiveSource::Dispose ()
1555 g_free (uri);
1556 uri = NULL;
1558 if (cancellable) {
1559 if (Surface::InMainThread ()) {
1560 delete_cancellable (this);
1561 } else {
1562 // we have to cancel/delete he cancellable on the main thread
1563 // it may end up doing a lot of stuff, including calling into
1564 // mozilla.
1566 // The tick call will ref us until the callback has been called.
1567 // Note that it may cause a warning to be printed
1568 // in ref () (reffing an object with a refcount of 0).
1569 // TODO: find a way to avoid the warning in this case, imho this is
1570 // a valid case of reffing an object with a refcount of 0.
1571 AddTickCallSafe (delete_cancellable);
1575 CloseWriteFile ();
1577 FileSource::Dispose ();
1580 void
1581 ProgressiveSource::delete_cancellable (EventObject *data)
1583 ProgressiveSource *src = (ProgressiveSource *) data;
1584 if (src->cancellable) {
1585 src->cancellable->Cancel ();
1586 delete src->cancellable;
1587 src->cancellable = NULL;
1591 MediaResult
1592 ProgressiveSource::Initialize ()
1594 MediaResult result = MEDIA_SUCCESS;
1595 Application *application;
1597 application = GetDeployment ()->GetCurrentApplication ();
1599 g_return_val_if_fail (application != NULL, MEDIA_FAIL);
1600 g_return_val_if_fail (filename == NULL, MEDIA_FAIL);
1601 g_return_val_if_fail (cancellable == NULL, MEDIA_FAIL);
1603 result = FileSource::Initialize ();
1605 if (!MEDIA_SUCCEEDED (result))
1606 return result;
1608 write_fd = g_fopen (filename, "w");
1609 if (write_fd == NULL) {
1610 char *msg = g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename);
1611 ReportErrorOccurred (msg);
1612 g_free (msg);
1613 return MEDIA_FAIL;
1616 // unlink the file right away so that it'll be deleted even if we crash.
1617 if (moonlight_flags & RUNTIME_INIT_KEEP_MEDIA) {
1618 printf ("Moonlight: The media file %s will not deleted.\n", filename);
1619 } else {
1620 g_unlink (filename);
1623 cancellable = new Cancellable ();
1624 Uri *u = new Uri ();
1625 if (u->Parse (uri)) {
1626 application->GetResource (NULL, u, notify_func, data_write, MediaPolicy, cancellable, (gpointer) this);
1627 } else {
1628 result = MEDIA_FAIL;
1629 char *msg = g_strdup_printf ("Could not parse the uri '%s'", uri);
1630 ReportErrorOccurred (msg);
1631 g_free (msg);
1633 delete u;
1635 return result;
1638 void
1639 ProgressiveSource::notify_func (NotifyType type, gint64 args, void *closure)
1641 g_return_if_fail (closure != NULL);
1642 ((ProgressiveSource *) closure)->Notify (type, args);
1645 void
1646 ProgressiveSource::Notify (NotifyType type, gint64 args)
1648 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT ")\n",
1649 type,
1650 type == ::NotifySize ? "NotifySize" :
1651 (type == NotifyCompleted ? "NotifyCompleted" :
1652 (type == NotifyFailed ? "NotifyFailed" :
1653 (type == NotifyStarted ? "NotifyStarted" :
1654 (type == NotifyProgressChanged ? "NotifyProgressChanged" : "unknown")))),
1655 args);
1657 switch (type) {
1658 case ::NotifySize:
1659 NotifySize (args);
1660 break;
1661 case NotifyCompleted:
1662 DownloadComplete ();
1663 break;
1664 case NotifyFailed:
1665 DownloadFailed ();
1666 break;
1667 case NotifyStarted:
1668 case NotifyProgressChanged:
1669 default:
1670 break;
1674 void
1675 ProgressiveSource::data_write (void *data, gint32 offset, gint32 n, void *closure)
1677 g_return_if_fail (closure != NULL);
1678 ((ProgressiveSource *) closure)->DataWrite (data, offset, n);
1681 void
1682 ProgressiveSource::DataWrite (void *buf, gint32 offset, gint32 n)
1684 size_t nwritten;
1685 Media *media = NULL;
1687 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf, offset, n, media, filename);
1689 if (IsDisposed ())
1690 return;
1692 g_return_if_fail (write_fd != NULL);
1694 media = GetMediaReffed ();
1696 if (n == 0) {
1697 // We've got the entire file, update the size
1698 size = write_pos; // Since this method is the only method that writes to write_pos, and we're not reentrant, there is no need to lock here.
1700 // Close our write handle, we won't write more now
1701 CloseWriteFile ();
1703 goto cleanup;
1706 nwritten = fwrite (buf, 1, n, write_fd);
1707 fflush (write_fd);
1709 Lock ();
1710 write_pos += nwritten;
1711 Unlock ();
1713 cleanup:
1714 if (media) {
1715 media->WakeUp ();
1716 media->ReportDownloadProgress ((double) (offset + n) / (double) size);
1717 media->unref ();
1721 void
1722 ProgressiveSource::NotifySize (gint64 size)
1724 LOG_PIPELINE ("ProgressiveSource::NotifySize (%" G_GINT64_FORMAT ")\n", size);
1726 Lock ();
1727 this->size = size;
1728 Unlock ();
1731 void
1732 ProgressiveSource::DownloadComplete ()
1734 MediaResult result = MEDIA_SUCCESS;
1735 Media *media = GetMediaReffed ();
1737 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1739 Lock ();
1740 if (write_pos != size && size != -1) { // what happend here?
1741 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT ") != the reported size (%" G_GINT64_FORMAT ")\n", write_pos, size);
1744 this->size = write_pos;
1746 // Close our write handle, we won't write more now
1747 CloseWriteFile ();
1749 Unlock ();
1751 if (!MEDIA_SUCCEEDED (result))
1752 ReportErrorOccurred (result);
1754 if (media) {
1755 media->ReportDownloadProgress (1.0);
1756 media->WakeUp ();
1757 media->unref ();
1761 void
1762 ProgressiveSource::DownloadFailed ()
1764 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1766 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
1769 void
1770 ProgressiveSource::CloseWriteFile ()
1772 if (write_fd == NULL)
1773 return;
1775 fclose (write_fd);
1776 write_fd = NULL;
1780 * MemorySource
1783 MemorySource::MemorySource (Media *media, void *memory, gint32 size, gint64 start, bool owner)
1784 : IMediaSource (Type::MEMORYSOURCE, media)
1786 this->memory = memory;
1787 this->size = size;
1788 this->start = start;
1789 this->pos = 0;
1790 this->owner = owner;
1793 MemorySource::~MemorySource ()
1795 if (owner)
1796 g_free (memory);
1799 bool
1800 MemorySource::SeekInternal (gint64 offset, int mode)
1802 gint64 real_offset;
1804 switch (mode) {
1805 case SEEK_SET:
1806 real_offset = offset - start;
1807 if (real_offset < 0 || real_offset >= size)
1808 return false;
1809 pos = real_offset;
1810 return true;
1811 case SEEK_CUR:
1812 if (pos + offset > size || pos + offset < 0)
1813 return false;
1814 pos += offset;
1815 return true;
1816 case SEEK_END:
1817 if (size - offset > size || size - offset < 0)
1818 return false;
1819 pos = size - offset;
1820 return true;
1821 default:
1822 return false;
1824 return true;
1827 gint32
1828 MemorySource::ReadInternal (void *buffer, guint32 n)
1830 guint32 k = MIN (n, size - pos);
1831 memcpy (buffer, ((char*) memory) + pos, k);
1832 pos += k;
1833 return k;
1836 gint32
1837 MemorySource::PeekInternal (void *buffer, guint32 n)
1839 gint64 start = this->start + pos;
1841 if (this->start > start)
1842 return 0;
1844 if ((this->start + size) < (start + n))
1845 return 0;
1847 memcpy (buffer, ((char*) memory) + this->start - start, n);
1848 return n;
1852 * MediaThreadPool
1855 pthread_mutex_t MediaThreadPool::mutex = PTHREAD_MUTEX_INITIALIZER;
1856 pthread_cond_t MediaThreadPool::condition = PTHREAD_COND_INITIALIZER;
1857 pthread_cond_t MediaThreadPool::completed_condition = PTHREAD_COND_INITIALIZER;
1858 int MediaThreadPool::count = 0;
1859 pthread_t MediaThreadPool::threads [max_threads];
1860 Media *MediaThreadPool::medias [max_threads];
1861 Deployment *MediaThreadPool::deployments [max_threads];
1862 bool MediaThreadPool::shutting_down = false;
1863 List *MediaThreadPool::queue = NULL;
1864 bool MediaThreadPool::valid [max_threads];
1866 void
1867 MediaThreadPool::AddWork (MediaClosure *closure, bool wakeup)
1869 pthread_attr_t attribs;
1870 int result = 0;
1872 pthread_mutex_lock (&mutex);
1874 if (shutting_down) {
1875 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1876 } else {
1877 if (queue == NULL)
1878 queue = new List ();
1879 queue->Append (new MediaWork (closure));
1881 // check if all threads are busy with other Media objects
1882 bool spawn = true;
1883 if (count == 0) {
1884 spawn = true;
1885 } else if (count < max_threads) {
1886 Media *media = closure->GetMedia ();
1887 for (int i = 0; i < count; i++) {
1888 if (medias [i] == NULL || medias [i] == media) {
1889 spawn = false; // there is a thread working on this media or not working at all.
1890 break;
1893 } else {
1894 spawn = false;
1897 if (spawn) {
1898 int prev_count = count;
1900 count++; // start up another thread.
1902 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count);
1904 for (int i = prev_count; i < count && result == 0; i++) {
1905 valid [i] = false;
1906 medias [i] = NULL;
1907 deployments [i] = NULL;
1909 pthread_attr_init (&attribs);
1910 pthread_attr_setdetachstate (&attribs, PTHREAD_CREATE_JOINABLE);
1911 result = pthread_create (&threads [i], &attribs, WorkerLoop, NULL);
1912 pthread_attr_destroy (&attribs);
1914 if (result != 0) {
1915 fprintf (stderr, "Moonlight: could not create media thread: %s (%i)\n", strerror (result), result);
1916 } else {
1917 valid [i] = true;
1922 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1923 closure->GetDescription (), closure, closure->GetMedia (), GET_OBJ_ID (closure->GetMedia ()), closure->GetDeployment (), queue ? queue->Length () : -1);
1925 if (wakeup)
1926 pthread_cond_signal (&condition);
1928 pthread_mutex_unlock (&mutex);
1931 void
1932 MediaThreadPool::WaitForCompletion (Deployment *deployment)
1934 bool waiting = false;
1935 MediaWork *current = NULL;
1937 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment);
1939 VERIFY_MAIN_THREAD;
1941 pthread_mutex_lock (&mutex);
1942 do {
1943 waiting = false;
1945 /* check if the deployment is being worked on */
1946 for (int i = 0; i < count; i++) {
1947 if (deployments [i] == deployment) {
1948 waiting = true;
1949 break;
1952 /* check if the deployment is in the queue */
1953 if (!waiting && queue != NULL) {
1954 current = (MediaWork *) queue->First ();
1955 while (current != NULL) {
1956 if (current->closure->GetDeployment () == deployment) {
1957 waiting = true;
1958 break;
1960 current = (MediaWork *) current->next;
1963 if (waiting) {
1964 timespec ts;
1965 ts.tv_sec = 0;
1966 ts.tv_nsec = 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
1967 pthread_cond_timedwait (&completed_condition, &mutex, &ts);
1969 } while (waiting);
1970 pthread_mutex_unlock (&mutex);
1973 void
1974 MediaThreadPool::RemoveWork (Media *media)
1976 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media, GET_OBJ_ID (media));
1978 List::Node *next;
1979 List::Node *first = NULL;
1980 List::Node *last = NULL;
1981 List::Node *current = NULL;
1982 int counter = 0;
1984 pthread_mutex_lock (&mutex);
1986 // create a list of nodes to delete
1987 current = queue != NULL ? queue->First () : NULL;
1988 while (current != NULL) {
1989 next = current->next; // retrieve next before Unlinking
1990 MediaWork *mw = (MediaWork *) current;
1991 if (mw->closure->GetMedia () == media) {
1992 queue->Unlink (current);
1993 if (first == NULL) {
1994 first = current;
1995 } else {
1996 last->next = current;
1998 last = current;
1999 counter++;
2000 break;
2002 current = next;
2005 pthread_mutex_unlock (&mutex);
2007 // We have to delete the list nodes with the
2008 // queue mutex unlocked, due to refcounting
2009 // (our node's (MediaWork) dtor will cause unrefs,
2010 // which may cause other dtors to be called,
2011 // eventually ending up wanting to lock the mutex
2012 // again).
2014 current = first;
2015 while (current != NULL) {
2016 next = current->next;
2017 delete current;
2018 current = next;
2022 void
2023 MediaThreadPool::WakeUp ()
2025 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2027 pthread_mutex_lock (&mutex);
2028 pthread_cond_signal (&condition);
2029 pthread_mutex_unlock (&mutex);
2032 bool
2033 MediaThreadPool::IsThreadPoolThread ()
2035 bool result = false;
2036 pthread_mutex_lock (&mutex);
2037 for (int i = 0; i < count; i++) {
2038 if (pthread_equal (pthread_self (), threads [i])) {
2039 result = true;
2040 break;
2043 pthread_mutex_unlock (&mutex);
2044 return result;
2047 void
2048 MediaThreadPool::Initialize ()
2050 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2051 VERIFY_MAIN_THREAD;
2053 shutting_down = false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2056 void
2057 MediaThreadPool::Shutdown ()
2059 List::Node *current = NULL;
2060 List::Node *next = NULL;
2062 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count);
2064 VERIFY_MAIN_THREAD;
2066 g_return_if_fail (!shutting_down);
2068 pthread_mutex_lock (&mutex);
2070 shutting_down = true;
2071 pthread_cond_broadcast (&condition);
2073 for (int i = 0; i < count; i++) {
2074 if (!valid [i])
2075 continue;
2077 pthread_mutex_unlock (&mutex);
2078 pthread_join (threads [i], NULL);
2079 pthread_mutex_lock (&mutex);
2082 if (queue != NULL) {
2083 current = queue->First ();
2084 queue->Clear (false);
2085 delete queue;
2086 queue = NULL;
2088 count = 0;
2090 pthread_mutex_unlock (&mutex);
2092 // deleting a node can have side-effects, so we first copy the list of nodes,
2093 // clear the original and loop over the copy while deleting the nodes.
2094 // this prevents any reentering issues while deleting nodes.
2095 while (current != NULL) {
2096 next = current->next;
2097 delete current;
2098 current = next;
2101 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2104 void *
2105 MediaThreadPool::WorkerLoop (void *data)
2107 MediaWork *node = NULL;
2108 Media *media = NULL;
2109 int self_index = -1;
2111 pthread_mutex_lock (&mutex);
2112 for (int i = 0; i < count; i++) {
2113 if (pthread_equal (threads [i], pthread_self ())) {
2114 self_index = i;
2115 break;
2118 pthread_mutex_unlock (&mutex);
2120 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index);
2122 g_return_val_if_fail (self_index >= 0, NULL);
2124 while (!shutting_down) {
2125 pthread_mutex_lock (&mutex);
2127 medias [self_index] = NULL;
2128 deployments [self_index] = NULL;
2129 /* if anybody was waiting for us to finish working, notify them */
2130 if (media != NULL)
2131 pthread_cond_signal (&completed_condition);
2133 media = NULL;
2134 node = (MediaWork *) (queue != NULL ? queue->First () : NULL);
2136 while (node != NULL) {
2137 media = node->closure->GetMedia ();
2139 for (int i = 0; i < count; i++) {
2140 if (medias [i] == media) {
2141 // another thread is working for the same media object.
2142 // we need to find something else to do.
2143 media = NULL;
2144 break;
2148 if (media != NULL)
2149 break;
2151 node = (MediaWork *) node->next;
2154 if (node == NULL) {
2155 pthread_cond_wait (&condition, &mutex);
2156 } else {
2157 queue->Unlink (node);
2160 if (node != NULL) {
2161 medias [self_index] = media;
2162 /* At this point the current deployment might be wrong, so avoid
2163 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2164 * here, since it might end up doing a lot of work with the mutex
2165 * locked. */
2166 deployments [self_index] = media->GetUnsafeDeployment ();
2169 pthread_mutex_unlock (&mutex);
2171 if (node == NULL)
2172 continue;
2174 media->SetCurrentDeployment (true, true);
2176 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: got %s %p for media %p on deployment %p, there are %d nodes left.\n", (int) pthread_self (), node->closure->GetDescription (), node, media, media->GetDeployment (), queue ? queue->Length () : -1);
2178 node->closure->Call ();
2180 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node);
2182 delete node;
2185 pthread_mutex_lock (&mutex);
2186 deployments [self_index] = NULL;
2187 medias [self_index] = NULL;
2188 /* if anybody was waiting for us to finish working, notify them */
2189 if (media != NULL)
2190 pthread_cond_signal (&completed_condition);
2191 pthread_mutex_unlock (&mutex);
2193 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index);
2195 return NULL;
2200 * MediaClosure
2203 MediaClosure::MediaClosure (Media *media, MediaCallback *callback, EventObject *context, const char *description)
2204 : EventObject (Type::MEDIACLOSURE, true)
2206 Init (media, callback, context);
2207 this->description = description;
2210 MediaClosure::MediaClosure (Type::Kind object_kind, Media *media, MediaCallback *callback, EventObject *context)
2211 : EventObject (object_kind, true)
2213 Init (media, callback, context);
2216 void
2217 MediaClosure::Init (Media *media, MediaCallback *callback, EventObject *context)
2219 result = MEDIA_INVALID;
2220 description = NULL;
2221 this->callback = callback;
2222 this->context = context;
2223 if (this->context)
2224 this->context->ref ();
2225 this->media = media;
2226 if (this->media)
2227 this->media->ref ();
2229 // put checks at the end so that fields are still initialized, since we can't abort construction.
2230 g_return_if_fail (callback != NULL);
2231 g_return_if_fail (media != NULL);
2234 void
2235 MediaClosure::Dispose ()
2237 if (context) {
2238 context->unref ();
2239 context = NULL;
2242 if (media) {
2243 media->unref ();
2244 media = NULL;
2247 callback = NULL;
2249 EventObject::Dispose ();
2252 void
2253 MediaClosure::Call ()
2255 if (callback) {
2256 result = callback (this);
2257 } else {
2258 result = MEDIA_NO_CALLBACK;
2263 * MediaDisposeObjectClosure
2265 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media *media, MediaCallback *callback, EventObject *context)
2266 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE, media, callback, context)
2270 void
2271 MediaDisposeObjectClosure::Dispose ()
2273 if (!CallExecuted ()) {
2274 // we haven't been executed. do it now.
2275 #if SANITY && DEBUG
2276 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2277 #endif
2278 Call ();
2281 MediaClosure::Dispose ();
2285 * MediaSeekClosure
2287 MediaSeekClosure::MediaSeekClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2288 : MediaClosure (Type::MEDIASEEKCLOSURE, media, callback, context)
2290 this->pts = pts;
2294 * MediaReportSeekCompletedClosure
2297 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2298 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE, media, callback, context)
2300 g_return_if_fail (context != NULL);
2302 this->pts = pts;
2305 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2309 void
2310 MediaReportSeekCompletedClosure::Dispose ()
2312 MediaClosure::Dispose ();
2316 * MediaGetFrameClosure
2319 MediaGetFrameClosure::MediaGetFrameClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, IMediaStream *stream)
2320 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2322 this->stream = NULL;
2324 g_return_if_fail (context != NULL);
2325 g_return_if_fail (stream != NULL);
2327 this->stream = stream;
2328 // this->stream->ref ();
2330 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2333 MediaGetFrameClosure::~MediaGetFrameClosure ()
2335 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2338 void
2339 MediaGetFrameClosure::Dispose ()
2341 if (stream) {
2342 // stream->unref ();
2343 stream = NULL;
2346 MediaClosure::Dispose ();
2347 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2351 * MediaReportFrameCompletedClosure
2354 MediaReportFrameCompletedClosure::MediaReportFrameCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, MediaFrame *frame)
2355 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2357 this->frame = NULL;
2359 g_return_if_fail (context != NULL);
2360 g_return_if_fail (frame != NULL);
2362 this->frame = frame;
2363 this->frame->ref ();
2366 void
2367 MediaReportFrameCompletedClosure::Dispose ()
2369 if (frame) {
2370 frame->unref ();
2371 frame = NULL;
2374 MediaClosure::Dispose ();
2378 * IMediaStream
2381 IMediaStream::IMediaStream (Type::Kind kind, Media *media) : IMediaObject (kind, media)
2383 context = NULL;
2385 extra_data_size = 0;
2386 extra_data = NULL;
2388 duration = 0;
2390 decoder = NULL;
2391 codec_id = 0;
2392 codec = NULL;
2394 min_padding = 0;
2395 index = -1;
2396 selected = false;
2397 input_ended = false;
2398 output_ended = false;
2400 first_pts = G_MAXUINT64; // The first pts in the stream, initialized to G_MAXUINT64
2401 last_popped_pts = G_MAXUINT64; // The pts of the last frame returned, initialized to G_MAXUINT64
2402 last_enqueued_pts = G_MAXUINT64; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2403 last_available_pts = 0; // The pts of the last available frame, initialized to 0
2406 void
2407 IMediaStream::Dispose ()
2409 if (decoder) {
2410 IMediaDecoder *d = decoder;
2411 decoder = NULL;
2412 d->Dispose ();
2413 d->unref ();
2415 g_free (extra_data);
2416 extra_data = NULL;
2417 g_free (codec);
2418 codec = NULL;
2420 ClearQueue ();
2421 IMediaObject::Dispose ();
2424 char *
2425 IMediaStream::CreateCodec (int codec_id)
2427 switch (codec_id) {
2428 case CODEC_WMV1: return g_strdup ("wmv1");
2429 case CODEC_WMV2: return g_strdup ("wmv2");
2430 case CODEC_WMV3: return g_strdup ("wmv3");
2431 case CODEC_WMVA: return g_strdup ("wmva");
2432 case CODEC_WVC1: return g_strdup ("vc1");
2433 case CODEC_RGBA: return g_strdup ("rgba");
2434 case CODEC_YV12: return g_strdup ("yv12");
2435 case CODEC_MP3: return g_strdup ("mp3");
2436 case CODEC_WMAV1: return g_strdup ("wmav1");
2437 case CODEC_WMAV2: return g_strdup ("wmav2");
2438 case CODEC_WMAV3: return g_strdup ("wmav3");
2439 case CODEC_PCM: return g_strdup ("pcm");
2440 default:
2441 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id);
2443 /* This algorithm needs testing.
2444 char *result;
2445 int size, current;
2446 int a = (codec_id & 0x000000FF);
2447 int b = (codec_id & 0x0000FF00) >> 8;
2448 int c = (codec_id & 0x00FF0000) >> 16;
2449 int d = (codec_id & 0xFF000000) >> 24;
2451 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2453 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2455 result = (char *) g_malloc (size + 1);
2456 current = 0;
2457 if (a)
2458 result [current++] = (char) a;
2459 if (b)
2460 result [current++] = (char) b;
2461 if (c)
2462 result [current++] = (char) c;
2463 if (d)
2464 result [current++] = (char) d;
2465 result [current] = 0;
2467 return g_strdup ("<unknown>");
2472 bool
2473 IMediaStream::IsQueueEmpty ()
2475 return queue.IsEmpty ();
2478 const char *
2479 IMediaStream::GetStreamTypeName ()
2481 switch (GetType ()) {
2482 case MediaTypeVideo: return "Video";
2483 case MediaTypeAudio: return "Audio";
2484 case MediaTypeMarker: return "Marker";
2485 default: return "Unknown";
2489 void
2490 IMediaStream::ReportSeekCompleted ()
2492 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2493 input_ended = false;
2494 output_ended = false;
2495 ClearQueue ();
2496 if (decoder != NULL)
2497 decoder->ReportSeekCompleted ();
2500 IMediaDemuxer *
2501 IMediaStream::GetDemuxer ()
2503 Media *media;
2504 IMediaDemuxer *result;
2506 if (IsDisposed ())
2507 return NULL;
2509 media = GetMediaReffed ();
2511 g_return_val_if_fail (media != NULL, NULL);
2513 result = media->GetDemuxer ();
2515 media->unref ();
2517 return result;
2520 IMediaDecoder *
2521 IMediaStream::GetDecoder ()
2523 return decoder;
2526 void
2527 IMediaStream::SetDecoder (IMediaDecoder *value)
2529 if (decoder)
2530 decoder->unref ();
2531 decoder = value;
2532 if (decoder)
2533 decoder->ref ();
2536 bool
2537 IMediaStream::GetOutputEnded ()
2539 return output_ended;
2542 void
2543 IMediaStream::SetOutputEnded (bool value)
2545 output_ended = value;
2548 bool
2549 IMediaStream::GetInputEnded ()
2551 return input_ended;
2554 void
2555 IMediaStream::SetInputEnded (bool value)
2557 input_ended = value;
2558 if (GetDecoder () != NULL)
2559 GetDecoder ()->ReportInputEnded ();
2562 guint64
2563 IMediaStream::GetBufferedSize ()
2565 guint64 result;
2567 queue.Lock ();
2568 if (first_pts == G_MAXUINT64 || last_enqueued_pts == G_MAXUINT64)
2569 result = 0;
2570 else if (last_popped_pts == G_MAXUINT64)
2571 result = last_enqueued_pts - first_pts;
2572 else
2573 result = last_enqueued_pts - last_popped_pts;
2574 queue.Unlock ();
2576 LOG_BUFFERING ("IMediaStream::GetBufferedSize (): id: %i, codec: %s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, result: %" G_GUINT64_FORMAT " ms\n",
2577 GET_OBJ_ID (this), codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts), MilliSeconds_FromPts (result));
2579 return result;
2583 #if DEBUG
2584 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2586 void
2587 IMediaStream::PrintBufferInformation ()
2589 guint64 buffer_size = GetBufferedSize ();
2591 printf (" <%s: ", codec);
2593 if (GetSelected ()) {
2594 printf ("size: %.4" G_GINT64_FORMAT ", first: %.4" G_GINT64_FORMAT ", last popped: %.4" G_GINT64_FORMAT ", last enq: %.4" G_GINT64_FORMAT ", frames enq: %i>",
2595 TO_MS (buffer_size), TO_MS (first_pts), TO_MS (last_popped_pts),
2596 TO_MS (last_enqueued_pts), queue.Length ());
2597 } else {
2598 printf ("(not selected) >");
2601 #endif
2603 void
2604 IMediaStream::EnqueueFrame (MediaFrame *frame)
2606 bool first = false;
2607 Media *media;
2609 g_return_if_fail (Media::InMediaThread ());
2611 media = GetMediaReffed ();
2612 g_return_if_fail (media != NULL);
2614 if (media->IsStopped ()) {
2615 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame.\n", frame);
2616 goto cleanup;
2619 if (frame->buffer == NULL) {
2620 /* for some reason there is no output from the decoder, possibly because it needs more data from the demuxer before outputting anything */
2621 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No data in frame, not storing it.\n", frame);
2622 goto cleanup;
2625 queue.Lock ();
2626 if (first_pts == G_MAXUINT64)
2627 first_pts = frame->pts;
2629 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
2631 #if 0
2632 if (last_enqueued_pts > frame->pts && last_enqueued_pts != G_MAXUINT64 && frame->event != FrameEventEOF && frame->buflen > 0) {
2633 g_warning ("IMediaStream::EnqueueFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, "
2634 "buffer: %" G_GUINT64_FORMAT " ms, frame: %p, frame->buflen: %i, frame->pts: %" G_GUINT64_FORMAT " ms (the last enqueued frame's pts is below the current frame's pts)\n",
2635 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2636 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), frame, frame->buflen, MilliSeconds_FromPts (frame->pts));
2638 #endif
2640 last_enqueued_pts = frame->pts;
2641 first = queue.LinkedList ()->Length () == 0;
2642 queue.LinkedList ()->Append (new StreamNode (frame));
2643 queue.Unlock ();
2645 SetLastAvailablePts (frame->pts);
2647 if (first)
2648 EmitSafe (FirstFrameEnqueuedEvent);
2650 FrameEnqueued ();
2652 cleanup:
2653 media->unref ();
2655 LOG_BUFFERING ("IMediaStream::EnqueueFrame (): codec: %.5s, first: %i, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, buffer: %" G_GUINT64_FORMAT " ms, frame: %p, frame->buflen: %i\n",
2656 codec, first, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2657 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts - first_pts), frame, frame->buflen);
2660 MediaFrame *
2661 IMediaStream::PopFrame ()
2663 MediaFrame *result = NULL;
2664 StreamNode *node = NULL;
2666 // We use the queue lock to synchronize access to
2667 // last_popped_pts/last_enqueued_pts/first_pts
2669 queue.Lock ();
2670 node = (StreamNode *) queue.LinkedList ()->First ();
2671 if (node != NULL) {
2672 result = node->GetFrame ();
2673 result->ref ();
2674 queue.LinkedList ()->Remove (node);
2675 last_popped_pts = result->pts;
2677 queue.Unlock ();
2679 LOG_BUFFERING ("IMediaStream::PopFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, buffer: %" G_GUINT64_FORMAT " ms, frame: %p, frame->buflen: %i\n",
2680 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2681 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), result, result ? result->buflen : 0);
2683 if (!input_ended && !output_ended && result != NULL) {
2684 IMediaDemuxer *demuxer = GetDemuxer ();
2685 if (demuxer != NULL)
2686 demuxer->FillBuffers ();
2689 return result;
2692 void
2693 IMediaStream::ClearQueue ()
2695 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2696 queue.Lock ();
2697 queue.LinkedList ()->Clear (true);
2698 first_pts = G_MAXUINT64;
2699 last_popped_pts = G_MAXUINT64;
2700 last_enqueued_pts = G_MAXUINT64;
2701 queue.Unlock ();
2704 void
2705 IMediaStream::SetSelected (bool value)
2707 Media *media = GetMediaReffed ();
2709 selected = value;
2710 if (media) {
2711 if (media->GetDemuxer ())
2712 media->GetDemuxer ()->UpdateSelected (this);
2713 media->unref ();
2718 * IMediaStream.StreamNode
2721 IMediaStream::StreamNode::StreamNode (MediaFrame *f)
2723 frame = f;
2724 frame->ref ();
2727 IMediaStream::StreamNode::~StreamNode ()
2729 frame->unref ();
2730 frame = NULL;
2733 * IMediaDemuxer
2736 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media, IMediaSource *source) : IMediaObject (kind, media)
2738 this->source = source;
2739 this->source->ref ();
2740 stream_count = 0;
2741 streams = NULL;
2742 opened = false;
2743 opening = false;
2744 seeking = false;
2745 seeking_pts = G_MAXUINT64;
2746 pending_stream = NULL;
2747 pending_fill_buffers = false;
2750 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media)
2751 : IMediaObject (kind, media)
2753 source = NULL;
2754 stream_count = 0;
2755 streams = NULL;
2756 opened = false;
2757 opening = false;
2758 seeking = false;
2759 seeking_pts = G_MAXUINT64;
2760 pending_stream = NULL;
2761 pending_fill_buffers = false;
2764 void
2765 IMediaDemuxer::Dispose ()
2767 if (streams != NULL) {
2768 IMediaStream **tmp = streams;
2769 int stream_count = this->stream_count;
2770 streams = NULL;
2771 for (int i = 0; i < stream_count; i++) {
2772 tmp [i]->Dispose ();
2773 tmp [i]->unref ();
2775 g_free (tmp);
2777 if (source) {
2778 source->unref ();
2779 source = NULL;
2781 if (pending_stream != NULL) {
2782 pending_stream->unref ();
2783 pending_stream = NULL;
2785 opened = false;
2786 IMediaObject::Dispose ();
2789 MediaResult
2790 IMediaDemuxer::OpenCallback (MediaClosure *closure)
2792 IMediaDemuxer *demuxer;
2794 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure);
2796 demuxer = (IMediaDemuxer *) closure->GetContext ();
2797 demuxer->OpenDemuxerAsync ();
2799 return MEDIA_SUCCESS;
2802 void
2803 IMediaDemuxer::EnqueueOpen ()
2805 MediaClosure *closure;
2806 Media *media = GetMediaReffed ();
2808 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2810 closure = new MediaClosure (media, OpenCallback, this, "IMediaDemuxer::OpenCallback");
2811 media->EnqueueWork (closure, false);
2812 closure->unref ();
2813 media->unref ();
2816 void
2817 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2819 Media *media = GetMediaReffed ();
2821 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media);
2823 opened = true;
2824 opening = false;
2826 // Media might be null if we got disposed for some reason.
2827 if (!media)
2828 return;
2830 media->ReportOpenDemuxerCompleted ();
2831 media->unref ();
2834 void
2835 IMediaDemuxer::ReportGetFrameProgress (double progress)
2837 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress);
2840 void
2841 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream *stream)
2843 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream);
2846 void
2847 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind, gint64 value)
2849 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %" G_GINT64_FORMAT ")\n", kind, value);
2852 void
2853 IMediaDemuxer::EnqueueReportGetFrameCompleted (MediaFrame *frame)
2855 Media *media = GetMediaReffed ();
2856 MediaClosure *closure = new MediaReportFrameCompletedClosure (media, ReportGetFrameCompletedCallback, this, frame);
2857 media->EnqueueWork (closure);
2858 closure->unref ();
2859 media->unref ();
2862 MediaResult
2863 IMediaDemuxer::ReportGetFrameCompletedCallback (MediaClosure *closure)
2865 MediaReportFrameCompletedClosure *c = (MediaReportFrameCompletedClosure *) closure;
2867 g_return_val_if_fail (c != NULL, MEDIA_FAIL);
2868 g_return_val_if_fail (c->GetDemuxer () != NULL, MEDIA_FAIL);
2870 c->GetDemuxer ()->ReportGetFrameCompleted (c->GetFrame ());
2872 return MEDIA_SUCCESS;
2875 void
2876 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame *frame)
2878 Media *media;
2880 g_return_if_fail (frame == NULL || (frame != NULL && frame->stream != NULL));
2881 g_return_if_fail (pending_stream != NULL);
2883 media = GetMediaReffed ();
2885 g_return_if_fail (media != NULL);
2887 if (media->IsStopped ()) {
2888 pending_stream->unref ();
2889 pending_stream = NULL; // not waiting for anything more
2890 goto cleanup; /* if we're stopped, just drop what we're doing. */
2893 /* ensure we're on a media thread */
2894 if (!Media::InMediaThread ()) {
2895 EnqueueReportGetFrameCompleted (frame);
2896 goto cleanup;
2899 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p) %i %s %" G_GUINT64_FORMAT " ms\n", frame, GET_OBJ_ID (this), frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : (guint64) -1);
2901 if (frame == NULL) {
2902 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame, pending_stream->GetStreamTypeName ());
2903 // No more data for this stream
2904 pending_stream->SetInputEnded (true);
2905 } else if (!frame->stream->IsDisposed ()) {
2906 IMediaDecoder *decoder = frame->stream->GetDecoder ();
2907 if (decoder != NULL)
2908 decoder->DecodeFrameAsync (frame, true /* always enqueue */);
2911 pending_stream->unref ();
2912 pending_stream = NULL; // not waiting for anything more
2914 // enqueue some more
2915 FillBuffers ();
2917 cleanup:
2918 if (media)
2919 media->unref ();
2922 MediaResult
2923 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure *c)
2925 MediaReportSeekCompletedClosure *closure = (MediaReportSeekCompletedClosure *) c;
2926 IMediaDemuxer *demuxer;
2928 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
2929 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
2931 demuxer = (IMediaDemuxer *) closure->GetContext ();
2932 demuxer->ReportSeekCompleted (closure->GetPts ());
2934 return MEDIA_SUCCESS;
2937 void
2938 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts)
2940 Media *media = GetMediaReffed ();
2941 MediaClosure *closure = new MediaReportSeekCompletedClosure (media, ReportSeekCompletedCallback, this, pts);
2942 media->EnqueueWork (closure);
2943 closure->unref ();
2944 media->unref ();
2947 void
2948 IMediaDemuxer::ReportSeekCompleted (guint64 pts)
2950 Media *media;
2952 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT ")\n", pts);
2954 g_return_if_fail (seeking);
2956 if (!Media::InMediaThread ()) {
2957 EnqueueReportSeekCompleted (pts);
2958 return;
2961 #if SANITY
2962 if (pending_stream != NULL)
2963 printf ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT "): we can't be waiting for a frame now.\n", pts);
2964 #endif
2966 media = GetMediaReffed ();
2968 g_return_if_fail (media != NULL);
2971 for (int i = 0; i < GetStreamCount (); i++) {
2972 IMediaStream *stream = GetStream (i);
2974 if (stream == NULL)
2975 continue;
2977 stream->ReportSeekCompleted ();
2980 media->ReportSeekCompleted (pts);
2981 media->unref ();
2983 seeking = false;
2984 pending_fill_buffers = false;
2985 FillBuffers ();
2987 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT ") [Done]\n", pts);
2990 void
2991 IMediaDemuxer::OpenDemuxerAsync ()
2993 g_return_if_fail (opened == false);
2995 opening = true;
2996 opened = false;
2997 OpenDemuxerAsyncInternal ();
3000 MediaResult
3001 IMediaDemuxer::GetFrameCallback (MediaClosure *c)
3003 MediaGetFrameClosure *closure = (MediaGetFrameClosure *) c;
3004 IMediaDemuxer *demuxer;
3006 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
3007 g_return_val_if_fail (closure->GetStream () != NULL, MEDIA_FAIL);
3008 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
3010 demuxer = (IMediaDemuxer *) closure->GetContext ();
3011 demuxer->GetFrameAsync (closure->GetStream ());
3013 return MEDIA_SUCCESS;
3016 void
3017 IMediaDemuxer::EnqueueGetFrame (IMediaStream *stream)
3019 g_return_if_fail (pending_stream == NULL); // we can't be waiting for another frame.
3021 Media *media = GetMediaReffed ();
3022 MediaClosure *closure = new MediaGetFrameClosure (media, GetFrameCallback, this, stream);
3023 media->EnqueueWork (closure);
3024 closure->unref ();
3025 media->unref ();
3028 void
3029 IMediaDemuxer::GetFrameAsync (IMediaStream *stream)
3031 Media *media = NULL;
3033 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream, stream->GetStreamTypeName (), Media::InMediaThread ());
3035 if (!Media::InMediaThread ()) {
3036 EnqueueGetFrame (stream);
3037 return;
3040 if (seeking) {
3041 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (): delayed since we're waiting for a seek.\n");
3042 goto cleanup;
3045 if (pending_stream != NULL) {
3046 /* we're already waiting for a frame */
3047 goto cleanup;
3050 media = GetMediaReffed ();
3052 g_return_if_fail (media != NULL);
3054 if (media->IsStopped ())
3055 goto cleanup;
3057 if (stream != NULL) {
3058 pending_stream = stream;
3059 pending_stream->ref ();
3060 GetFrameAsyncInternal (stream);
3063 cleanup:
3064 if (media)
3065 media->unref ();
3068 MediaResult
3069 IMediaDemuxer::SeekCallback (MediaClosure *closure)
3071 MediaSeekClosure *seek = (MediaSeekClosure *) closure;
3072 seek->GetDemuxer ()->SeekAsync ();
3073 return MEDIA_SUCCESS;
3076 void
3077 IMediaDemuxer::EnqueueSeek ()
3079 Media *media = GetMediaReffed ();
3080 MediaSeekClosure *closure;
3082 g_return_if_fail (media != NULL);
3084 closure = new MediaSeekClosure (media, SeekCallback, this, 0);
3085 media->EnqueueWork (closure, true);
3086 closure->unref ();
3087 media->unref ();
3090 void
3091 IMediaDemuxer::SeekAsync ()
3093 guint64 pts;
3095 LOG_PIPELINE ("IMediaDemuxer::SeekAsync ()\n");
3097 g_return_if_fail (Media::InMediaThread ());
3099 seeking = true; /* this ensures that we stop demuxing frames asap */
3101 mutex.Lock ();
3102 pts = seeking_pts;
3103 mutex.Unlock ();
3105 if (pts == G_MAXUINT64) {
3107 * Two (or more) seeks was enqueued before the first one was processed
3108 * - this is the callback for the second (or subsequent) seek. Since we
3109 * keep track only of the last seeked-to position we've already seeked to
3110 * this position.
3112 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): Nothing to do, seek has been executed already.\n");
3113 return;
3116 if (pending_stream != NULL) {
3117 /* we're waiting for the decoder to decode a frame, wait a bit with the seek */
3118 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i waiting for a frame, postponing seek\n", GET_OBJ_ID (this));
3119 EnqueueSeek ();
3120 return;
3123 /* as the demuxer to seek */
3124 /* at this point the pipeline shouldn't be doing anything else (for this media) */
3125 SeekAsyncInternal (pts);
3128 void
3129 IMediaDemuxer::SeekAsync (guint64 pts)
3131 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%" G_GUINT64_FORMAT ")\n", pts);
3132 VERIFY_MAIN_THREAD;
3134 if (IsDisposed ())
3135 return;
3137 mutex.Lock ();
3138 seeking_pts = pts;
3139 mutex.Unlock ();
3141 EnqueueSeek ();
3144 void
3145 IMediaDemuxer::ClearBuffers ()
3147 pending_fill_buffers = false;
3149 /* Clear all the buffer queues */
3150 for (int i = 0; i < GetStreamCount (); i++) {
3151 IMediaStream *stream = GetStream (i);
3153 if (stream == NULL)
3154 continue;
3156 stream->ClearQueue ();
3160 MediaResult
3161 IMediaDemuxer::FillBuffersCallback (MediaClosure *closure)
3163 IMediaDemuxer *demuxer = (IMediaDemuxer *) closure->GetContext ();
3164 demuxer->FillBuffersInternal ();
3165 return MEDIA_SUCCESS;
3168 void
3169 IMediaDemuxer::FillBuffers ()
3171 Media *media = NULL;
3172 MediaClosure *closure;
3173 bool enqueue = true;
3175 mutex.Lock ();
3176 if (pending_fill_buffers) {
3177 // there's already a FillBuffers request enqueued
3178 enqueue = false;
3179 } else {
3180 media = GetMediaReffed ();
3181 if (media == NULL) {
3182 enqueue = false;
3183 } else {
3184 enqueue = true;
3185 pending_fill_buffers = true;
3188 mutex.Unlock ();
3190 if (enqueue) {
3191 closure = new MediaClosure (media, FillBuffersCallback, this, "IMediaDemuxer::FillBuffersCallback");
3192 media->EnqueueWork (closure);
3193 closure->unref ();
3196 if (media != NULL)
3197 media->unref ();
3200 void
3201 IMediaDemuxer::FillBuffersInternal ()
3203 IMediaStream *stream;
3204 IMediaStream *request_stream = NULL;
3205 guint64 min_buffered_size = G_MAXUINT64;
3206 MediaResult result = MEDIA_SUCCESS;
3207 Media *media = GetMediaReffed ();
3208 guint64 buffering_time = 0;
3209 guint64 buffered_size = 0;
3210 int ended = 0;
3211 int media_streams = 0;
3213 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (), %i %s buffering time: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, pending_stream: %i %s\n", GET_OBJ_ID (this), GetTypeName (), buffering_time, media != NULL ? MilliSeconds_FromPts (media->GetBufferingTime ()) : -1, GET_OBJ_ID (pending_stream), pending_stream ? pending_stream->GetStreamTypeName () : "NULL");
3215 mutex.Lock ();
3216 pending_fill_buffers = false;
3217 mutex.Unlock ();
3219 if (IsDisposed ())
3220 goto cleanup;
3222 // If we're waiting for something, there's nothing to do here.
3223 if (pending_stream != NULL)
3224 goto cleanup;
3226 // Find the stream with the smallest buffered size, and request a frame from that stream.
3227 g_return_if_fail (media != NULL);
3229 // If we're stopped there is nothing to do here.
3230 if (media->IsStopped ()) {
3231 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped\n");
3232 goto cleanup;
3235 buffering_time = media->GetBufferingTime ();
3237 if (buffering_time == 0) {
3238 // Play as soon as possible.
3239 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3240 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3241 // all streams have 1 frame queued.
3242 buffering_time = 1;
3245 for (int i = 0; i < GetStreamCount (); i++) {
3246 IMediaDecoder *decoder = NULL;
3248 stream = GetStream (i);
3249 if (!stream->GetSelected ())
3250 continue;
3252 if (stream->GetType () != MediaTypeVideo &&
3253 stream->GetType () != MediaTypeAudio)
3254 continue;
3256 media_streams++;
3257 if (stream->GetOutputEnded ()) {
3258 ended++;
3259 continue; // this stream has ended.
3262 decoder = stream->GetDecoder ();
3263 if (decoder == NULL) {
3264 fprintf (stderr, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream->GetStreamTypeName (), GET_OBJ_ID (stream), stream->GetRefCount ());
3265 continue; // no decoder??
3268 buffered_size = stream->GetBufferedSize ();
3269 min_buffered_size = MIN (min_buffered_size, buffered_size);
3271 if (buffered_size >= buffering_time)
3272 continue; // this stream has enough data buffered.
3274 if (!decoder->IsDecoderQueueEmpty ())
3275 continue; // this stream is waiting for data to be decoded.
3277 if (buffered_size <= min_buffered_size)
3278 request_stream = stream;
3280 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): codec: %s, stream id: %i, result: %i, buffered size: %" G_GUINT64_FORMAT " ms, buffering time: %" G_GUINT64_FORMAT " ms, last popped time: %" G_GUINT64_FORMAT " ms\n",
3281 stream->codec, GET_OBJ_ID (stream), result, MilliSeconds_FromPts (buffered_size), MilliSeconds_FromPts (buffering_time), MilliSeconds_FromPts (stream->GetLastPoppedPts ()));
3284 if (request_stream != NULL) {
3285 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): requesting frame from %s stream (%i), min_buffered_size: %" G_GUINT64_FORMAT " ms\n", request_stream->GetStreamTypeName (), GET_OBJ_ID (stream), MilliSeconds_FromPts (min_buffered_size));
3286 GetFrameAsync (request_stream);
3289 if (media_streams > 0) {
3290 if (ended == media_streams) {
3291 media->ReportBufferingProgress (1.0);
3292 } else {
3293 if (min_buffered_size > 0 && buffering_time > 0) {
3294 double progress = ((double) min_buffered_size / (double) buffering_time);
3295 media->ReportBufferingProgress (progress);
3300 cleanup:
3301 if (media)
3302 media->unref ();
3304 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT " ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3307 guint64
3308 IMediaDemuxer::GetBufferedSize ()
3310 guint64 result = G_MAXUINT64;
3311 IMediaStream *stream;
3313 for (int i = 0; i < GetStreamCount (); i++) {
3314 stream = GetStream (i);
3315 if (!stream->GetSelected ())
3316 continue;
3318 if (stream->GetType () != MediaTypeVideo && stream->GetType () != MediaTypeAudio)
3319 continue;
3321 result = MIN (result, stream->GetBufferedSize ());
3324 return result;
3327 guint64
3328 IMediaDemuxer::GetLastAvailablePts ()
3330 guint64 result = G_MAXUINT64;
3331 IMediaStream *stream;
3333 for (int i = 0; i < GetStreamCount (); i++) {
3334 stream = GetStream (i);
3336 if (stream == NULL || !stream->GetSelected ())
3337 continue;
3339 result = MIN (result, stream->GetLastAvailablePts ());
3342 if (result == G_MAXUINT64)
3343 result = 0;
3345 return result;
3348 #if DEBUG
3349 void
3350 IMediaDemuxer::PrintBufferInformation ()
3352 printf ("Buffer: %" G_GINT64_FORMAT "", MilliSeconds_FromPts (GetBufferedSize ()));
3353 for (int i = 0; i < GetStreamCount (); i++) {
3354 GetStream (i)->PrintBufferInformation ();
3356 printf ("\n");
3358 #endif
3360 guint64
3361 IMediaDemuxer::GetDuration ()
3363 guint64 result = 0;
3364 for (int i = 0; i < GetStreamCount (); i++)
3365 result = MAX (result, GetStream (i)->duration);
3366 return result;
3369 IMediaStream*
3370 IMediaDemuxer::GetStream (int index)
3372 return (index < 0 || index >= stream_count) ? NULL : streams [index];
3376 * MediaFrame
3379 MediaFrame::MediaFrame (IMediaStream *stream)
3380 : EventObject (Type::MEDIAFRAME, true)
3382 Initialize ();
3384 g_return_if_fail (stream != NULL);
3386 this->stream = stream;
3387 this->stream->ref ();
3390 MediaFrame::MediaFrame (IMediaStream *stream, guint8 *buffer, guint32 buflen, guint64 pts, bool keyframe)
3391 : EventObject (Type::MEDIAFRAME, true)
3393 Initialize ();
3395 g_return_if_fail (stream != NULL);
3397 this->stream = stream;
3398 this->stream->ref ();
3399 this->buffer = buffer;
3400 this->buflen = buflen;
3401 this->pts = pts;
3403 #if 0
3404 if (buflen > 4 && false) {
3405 printf ("MediaFrame::MediaFrame () %s buffer: ", stream->GetStreamTypeName ());
3406 for (int i = 0; i < 4; i++)
3407 printf (" 0x%x", buffer [i]);
3408 printf ("\n");
3410 #endif
3412 if (keyframe)
3413 AddState (MediaFrameKeyFrame);
3416 void
3417 MediaFrame::Initialize ()
3419 decoder_specific_data = NULL;
3420 stream = NULL;
3421 marker = NULL;
3423 duration = 0;
3424 pts = 0;
3426 buffer = NULL;
3427 buflen = 0;
3428 state = 0;
3429 event = 0;
3431 for (int i = 0; i < 4; i++) {
3432 data_stride[i] = 0;
3433 srcStride[i] = 0;
3436 srcSlideY = 0;
3437 srcSlideH = 0;
3438 width = 0;
3439 height = 0;
3442 MediaFrame::~MediaFrame ()
3446 void
3447 MediaFrame::Dispose ()
3449 IMediaDecoder *decoder;
3451 #if SANITY
3452 // We can be called either on the main thread just before destruction
3453 // (in which case there are no races since the code which unreffed us
3454 // is the only code which knows about us), or at any time from the
3455 // media thread.
3457 if (GetRefCount () != 0 && stream != NULL) {
3458 if (!Media::InMediaThread ()) {
3459 // if refcount != 0 we're not being called just before destruction, in which case we should
3460 // only be on the media thread.
3461 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3464 #endif
3466 if (decoder_specific_data != NULL && stream != NULL) {
3467 decoder = stream->GetDecoder ();
3468 if (decoder != NULL)
3469 decoder->Cleanup (this);
3471 g_free (buffer);
3472 buffer = NULL;
3473 if (marker) {
3474 marker->unref ();
3475 marker = NULL;
3477 if (stream) {
3478 stream->unref ();
3479 stream = NULL;
3482 EventObject::Dispose ();
3485 void
3486 MediaFrame::SetSrcSlideY (int value)
3488 srcSlideY = value;
3491 void
3492 MediaFrame::SetSrcSlideH (int value)
3494 srcSlideH = value;
3497 void
3498 MediaFrame::SetSrcStride (int a, int b, int c, int d)
3500 srcStride [0] = a;
3501 srcStride [1] = b;
3502 srcStride [2] = c;
3503 srcStride [3] = d;
3506 void
3507 MediaFrame::SetDataStride (guint8* a, guint8* b, guint8* c, guint8* d)
3509 data_stride [0] = a;
3510 data_stride [1] = b;
3511 data_stride [2] = c;
3512 data_stride [3] = d;
3516 * IMediaObject.EventData
3519 IMediaObject::EventData::EventData (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3521 this->event_id = event_id;
3522 this->handler = handler;
3523 this->context = context;
3524 this->context->ref ();
3525 this->invoke_on_main_thread = invoke_on_main_thread;
3528 IMediaObject::EventData::~EventData ()
3530 context->unref ();
3531 context = NULL;
3535 * IMediaObject.EmitData
3538 IMediaObject::EmitData::EmitData (int event_id, EventHandler handler, EventObject *context, EventArgs *args)
3540 this->event_id = event_id;
3541 this->handler = handler;
3542 this->context = context;
3543 this->context->ref ();
3544 this->args = args;
3545 if (this->args)
3546 this->args->ref ();
3549 IMediaObject::EmitData::~EmitData ()
3551 context->unref ();
3552 context = NULL;
3553 if (args) {
3554 args->unref ();
3555 args = NULL;
3560 * IMediaObject
3563 IMediaObject::IMediaObject (Type::Kind kind, Media *media)
3564 : EventObject (kind, true)
3566 this->media = media;
3567 if (this->media)
3568 this->media->ref ();
3569 g_return_if_fail (media != NULL);
3570 events = NULL;
3571 emit_on_main_thread = NULL;
3574 void
3575 IMediaObject::Dispose ()
3578 #if SANITY
3579 // We can be called either on the main thread just before destruction
3580 // (in which case there are no races since the code which unreffed us
3581 // is the only code which knows about us), or at any time from the
3582 // media thread.
3583 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3584 // if refcount != 0 we're not being called just before destruction, in which case we should
3585 // only be on the media thread.
3586 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3588 #endif
3590 media_mutex.Lock ();
3591 if (media) {
3592 media->unref ();
3593 media = NULL;
3595 media_mutex.Unlock ();
3597 event_mutex.Lock ();
3598 delete events;
3599 events = NULL;
3600 if (emit_on_main_thread != NULL) {
3601 delete emit_on_main_thread;
3602 emit_on_main_thread = NULL;
3604 event_mutex.Unlock ();
3606 EventObject::Dispose ();
3609 void
3610 IMediaObject::AddSafeHandler (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3612 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id, handler, context, invoke_on_main_thread);
3613 EventData *ed;
3615 if (!IsDisposed ()) {
3616 ed = new EventData (event_id, handler, context, invoke_on_main_thread);
3617 event_mutex.Lock ();
3618 if (events == NULL)
3619 events = new List ();
3620 events->Append (ed);
3621 event_mutex.Unlock ();
3625 void
3626 IMediaObject::RemoveSafeHandlers (EventObject *context)
3628 EventData *ed;
3629 EventData *next;
3631 event_mutex.Lock ();
3632 if (events != NULL) {
3633 ed = (EventData *) events->First ();
3634 while (ed != NULL) {
3635 next = (EventData *) ed->next;
3636 if (ed->context == context)
3637 events->Remove (ed);
3638 ed = next;
3641 event_mutex.Unlock ();
3644 void
3645 IMediaObject::EmitSafe (int event_id, EventArgs *args)
3647 List *emits = NULL; // The events to emit on this thread.
3648 EventData *ed;
3649 EmitData *emit;
3651 if (events == NULL)
3652 goto cleanup;
3654 // Create a list of all the events to emit
3655 // don't keep the lock while emitting.
3656 event_mutex.Lock ();
3657 if (events != NULL) {
3658 ed = (EventData *) events->First ();
3659 while (ed != NULL) {
3660 if (ed->event_id == event_id) {
3661 emit = new EmitData (event_id, ed->handler, ed->context, args);
3662 if (ed->invoke_on_main_thread) {
3663 if (emit_on_main_thread == NULL)
3664 emit_on_main_thread = new List ();
3665 emit_on_main_thread->Append (emit);
3666 } else {
3667 if (emits == NULL)
3668 emits = new List ();
3669 emits->Append (emit);
3672 ed = (EventData *) ed->next;
3675 event_mutex.Unlock ();
3677 // emit the events to be emitted on this thread
3678 EmitList (emits);
3680 if (Surface::InMainThread ()) {
3681 // if we're already on the main thread,
3682 // we can the events to be emitted
3683 // on the main thread
3684 List *tmp;
3685 event_mutex.Lock ();
3686 tmp = emit_on_main_thread;
3687 emit_on_main_thread = NULL;
3688 event_mutex.Unlock ();
3689 EmitList (tmp);
3690 } else {
3691 AddTickCallSafe (EmitListCallback);
3694 cleanup:
3695 if (args)
3696 args->unref ();
3699 void
3700 IMediaObject::EmitListMain ()
3702 VERIFY_MAIN_THREAD;
3704 List *list;
3705 event_mutex.Lock ();
3706 list = emit_on_main_thread;
3707 emit_on_main_thread = NULL;
3708 event_mutex.Unlock ();
3709 EmitList (list);
3712 void
3713 IMediaObject::EmitListCallback (EventObject *obj)
3715 IMediaObject *media_obj = (IMediaObject *) obj;
3716 media_obj->EmitListMain ();
3719 void
3720 IMediaObject::EmitList (List *list)
3722 EmitData *emit;
3724 if (list == NULL)
3725 return;
3727 emit = (EmitData *) list->First ();
3728 while (emit != NULL) {
3729 emit->handler (this, emit->args, emit->context);
3730 emit = (EmitData *) emit->next;
3733 delete list;
3736 Media *
3737 IMediaObject::GetMediaReffed ()
3739 Media *result;
3740 media_mutex.Lock ();
3741 result = media;
3742 if (result)
3743 result->ref ();
3744 media_mutex.Unlock ();
3745 return result;
3748 void
3749 IMediaObject::ReportErrorOccurred (char const *message)
3751 g_return_if_fail (media != NULL);
3753 media->ReportErrorOccurred (message);
3756 void
3757 IMediaObject::ReportErrorOccurred (MediaResult result)
3759 g_return_if_fail (media != NULL);
3761 media->ReportErrorOccurred (result);
3764 void
3765 IMediaObject::ReportErrorOccurred (ErrorEventArgs *args)
3767 g_return_if_fail (media != NULL);
3769 media->ReportErrorOccurred (args);
3772 void
3773 IMediaObject::SetMedia (Media *value)
3775 media_mutex.Lock ();
3776 if (media)
3777 media->unref ();
3778 media = value;
3779 if (media)
3780 media->ref ();
3781 media_mutex.Unlock ();
3785 * IMediaSource
3788 IMediaSource::IMediaSource (Type::Kind kind, Media *media)
3789 : IMediaObject (kind, media)
3791 pthread_mutexattr_t attribs;
3792 pthread_mutexattr_init (&attribs);
3793 pthread_mutexattr_settype (&attribs, PTHREAD_MUTEX_RECURSIVE);
3794 pthread_mutex_init (&mutex, &attribs);
3795 pthread_mutexattr_destroy (&attribs);
3797 pthread_cond_init (&condition, NULL);
3800 IMediaSource::~IMediaSource ()
3802 pthread_mutex_destroy (&mutex);
3803 pthread_cond_destroy (&condition);
3806 void
3807 IMediaSource::Dispose ()
3809 IMediaObject::Dispose ();
3812 void
3813 IMediaSource::Lock ()
3815 pthread_mutex_lock (&mutex);
3818 void
3819 IMediaSource::Unlock ()
3821 pthread_mutex_unlock (&mutex);
3824 gint32
3825 IMediaSource::ReadSome (void *buf, guint32 n)
3827 gint32 result;
3829 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf, n);
3831 Lock ();
3833 result = ReadInternal (buf, n);
3835 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %" G_GINT64_FORMAT "\n", GET_OBJ_ID (this), buf, n, result, GetPosition ());
3837 Unlock ();
3839 return result;
3842 bool
3843 IMediaSource::ReadAll (void *buf, guint32 n)
3845 gint32 read;
3846 gint64 prev = GetPosition ();
3847 gint64 avail = GetLastAvailablePosition ();
3849 //printf ("IMediaSource::ReadAll (%p, %u), position: %" G_GINT64_FORMAT "\n", buf, n, prev);
3851 read = ReadSome (buf, n);
3853 if ((gint64) read != (gint64) n) {
3854 FileSource *fs = NULL;
3856 if (GetType () == MediaSourceTypeFile)
3857 fs = (FileSource *) this;
3858 g_warning ("IMediaSource::ReadInternal (%i): Read failed, read %i bytes. available size: %" G_GINT64_FORMAT ", size: %" G_GINT64_FORMAT ", pos: %" G_GINT64_FORMAT ", prev pos: %" G_GINT64_FORMAT ", position not available: %" G_GINT64_FORMAT ", feof: %i, ferror: %i, strerror: %s\n",
3859 n, read, avail, GetSize (), GetPosition (), prev, prev + n, fs ? feof (fs->fd) : -1, fs ? ferror (fs->fd) : -1, fs ? strerror (ferror (fs->fd)) : "<N/A>");
3860 print_stack_trace ();
3863 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf, n, read);
3865 return (gint64) read == (gint64) n;
3868 bool
3869 IMediaSource::Peek (void *buf, guint32 n)
3871 bool result;
3872 gint64 read;
3874 Lock ();
3876 read = PeekInternal (buf, n);
3877 result = read == (gint64) n;
3879 Unlock ();
3881 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %" G_GINT64_FORMAT " bytes.\n", buf, n, result, read);
3883 return result;
3886 bool
3887 IMediaSource::Seek (gint64 offset, int mode)
3889 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%" G_GINT64_FORMAT ", %d = %s)\n",
3890 GET_OBJ_ID (this), ToString (), offset, mode, mode == SEEK_SET ? "SEEK_SET"
3891 : (mode == SEEK_CUR ? "SEEK_CUR" : (mode == SEEK_END ? "SEEK_END" : "<invalid value>")));
3893 bool result;
3894 Lock ();
3895 result = SeekInternal (offset, mode);
3896 Unlock ();
3897 return result;
3900 bool
3901 IMediaSource::IsPositionAvailable (gint64 position, bool *eof)
3903 gint64 available = GetLastAvailablePosition ();
3904 gint64 size = GetSize ();
3906 *eof = false;
3908 if (size != -1 && size < position) {
3909 // Size is known and smaller than the requested position
3910 *eof = true;
3911 return false;
3914 if (available != -1 && available < position) {
3915 // Not everything is available and the available position is smaller than the requested position
3916 *eof = false;
3917 return false;
3920 if (size == -1 && available == -1) {
3921 // Size is not known, but everything is available??
3922 // This is probably due to a bug in the derived *Source class
3923 *eof = false;
3924 fprintf (stderr, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
3925 return false;
3928 return true;
3931 gint64
3932 IMediaSource::GetLastAvailablePosition ()
3934 gint64 result;
3935 Lock ();
3936 result = GetLastAvailablePositionInternal ();
3937 Unlock ();
3938 return result;
3941 gint64
3942 IMediaSource::GetPositionInternal ()
3944 // This method should be overridden (or never called for the classes which doesn't override it).
3945 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3946 print_stack_trace ();
3948 return -1;
3950 bool
3951 IMediaSource::SeekInternal (gint64 offset, int mode)
3953 g_warning ("IMediaSource (%s)::SeekInternal (%" G_GINT64_FORMAT ", %i): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), offset, mode);
3954 print_stack_trace ();
3956 return false;
3959 gint32
3960 IMediaSource::ReadInternal (void *buffer, guint32 n)
3962 g_warning ("IMediaSource (%s)::ReadInternal (%p, %u): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), buffer, n);
3963 print_stack_trace ();
3965 return 0;
3968 gint32
3969 IMediaSource::PeekInternal (void *buffer, guint32 n)
3971 g_warning ("IMediaSource (%s)::PeekInternal (%p, %u): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), buffer, n);
3972 print_stack_trace ();
3974 return 0;
3977 gint64
3978 IMediaSource::GetSizeInternal ()
3980 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3981 print_stack_trace ();
3983 return 0;
3986 gint64
3987 IMediaSource::GetPosition ()
3989 gint64 result;
3990 Lock ();
3991 result = GetPositionInternal ();
3992 Unlock ();
3993 return result;
3996 gint64
3997 IMediaSource::GetSize ()
3999 gint64 result;
4000 Lock ();
4001 result = GetSizeInternal ();
4002 Unlock ();
4003 return result;
4007 * IMediaDemuxer
4010 void
4011 IMediaDemuxer::SetStreams (IMediaStream** streams, int count)
4013 this->streams = streams;
4014 this->stream_count = count;
4016 for (int i = 0; i < count; i++)
4017 this->streams [i]->ref ();
4020 gint32
4021 IMediaDemuxer::AddStream (IMediaStream *stream)
4023 g_return_val_if_fail (stream != NULL, -1);
4025 stream_count++;
4026 streams = (IMediaStream **) g_realloc (streams, stream_count * sizeof (IMediaStream *));
4027 streams [stream_count - 1] = stream;
4028 stream->ref ();
4030 return stream_count - 1;
4034 * IMediaDecoder
4037 IMediaDecoder::IMediaDecoder (Type::Kind kind, Media *media, IMediaStream *stream) : IMediaObject (kind, media)
4039 this->stream = NULL;
4041 g_return_if_fail (stream != NULL);
4043 this->stream = stream;
4044 this->stream->ref ();
4046 opening = false;
4047 opened = false;
4048 input_ended = false;
4051 void
4052 IMediaDecoder::Dispose ()
4054 if (stream != NULL) {
4055 IMediaStream *s = stream;
4056 stream = NULL;
4057 s->Dispose ();
4058 s->unref ();
4059 s = NULL;
4062 queue.Clear (true);
4064 IMediaObject::Dispose ();
4067 void
4068 IMediaDecoder::ReportSeekCompleted ()
4070 queue.Clear (true);
4071 input_ended = false;
4072 CleanState ();
4075 void
4076 IMediaDecoder::ReportInputEnded ()
4078 input_ended = true;
4079 if (IsDecoderQueueEmpty ()) {
4080 InputEnded ();
4084 void
4085 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame *frame)
4087 IMediaDemuxer *demuxer;
4088 IMediaStream *stream;
4089 Media *media = NULL;
4091 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
4093 g_return_if_fail (frame != NULL);
4095 media = GetMediaReffed ();
4096 g_return_if_fail (media != NULL);
4098 stream = frame->stream;
4099 if (stream == NULL)
4100 goto cleanup;
4102 frame->stream->EnqueueFrame (frame);
4104 demuxer = stream->GetDemuxer ();
4105 if (demuxer != NULL)
4106 demuxer->FillBuffers ();
4108 if (input_ended && IsDecoderQueueEmpty ())
4109 InputEnded ();
4111 cleanup:
4112 if (media)
4113 media->unref ();
4116 MediaResult
4117 IMediaDecoder::DecodeFrameCallback (MediaClosure *closure)
4120 IMediaDecoder *decoder = (IMediaDecoder *) closure->GetContext ();
4121 IMediaDecoder::FrameNode *node = (IMediaDecoder::FrameNode *) decoder->queue.Pop ();
4123 if (node != NULL) {
4124 decoder->DecodeFrameAsync (node->frame, false);
4125 delete node;
4128 return MEDIA_SUCCESS;
4131 void
4132 IMediaDecoder::DecodeFrameAsync (MediaFrame *frame, bool enqueue_always)
4134 Media *media;
4136 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame, (frame && frame->stream) ? frame->stream->GetStreamTypeName () : NULL);
4138 if (IsDisposed ())
4139 return;
4141 g_return_if_fail (frame != NULL);
4143 media = GetMediaReffed ();
4145 g_return_if_fail (media != NULL);
4147 if (enqueue_always || !Media::InMediaThread ()) {
4148 MediaClosure *closure = new MediaClosure (media, DecodeFrameCallback, this, "IMediaDecoder::DecodeFrameCallback");
4149 queue.Push (new FrameNode (frame));
4150 media->EnqueueWork (closure);
4151 closure->unref ();
4152 goto cleanup;
4155 if (media->IsStopped ())
4156 goto cleanup;
4158 DecodeFrameAsyncInternal (frame);
4160 cleanup:
4161 media->unref ();
4164 void
4165 IMediaDecoder::OpenDecoderAsync ()
4167 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4169 g_return_if_fail (opening == false);
4170 g_return_if_fail (opened == false);
4172 opening = true;
4173 OpenDecoderAsyncInternal ();
4176 void
4177 IMediaDecoder::ReportOpenDecoderCompleted ()
4179 Media *media = GetMediaReffed ();
4181 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4183 opening = false;
4184 opened = true;
4186 g_return_if_fail (media != NULL);
4188 media->ReportOpenDecoderCompleted (this);
4189 media->unref ();
4193 * IImageConverter
4196 IImageConverter::IImageConverter (Type::Kind kind, Media *media, VideoStream *stream) : IMediaObject (kind, media)
4198 output_format = MoonPixelFormatNone;
4199 input_format = MoonPixelFormatNone;
4200 this->stream = stream;
4204 * VideoStream
4207 VideoStream::VideoStream (Media *media) : IMediaStream (Type::VIDEOSTREAM, media)
4209 converter = NULL;
4210 bits_per_sample = 0;
4211 pts_per_frame = 0;
4212 initial_pts = 0;
4213 height = 0;
4214 width = 0;
4217 VideoStream::VideoStream (Media *media, int codec_id, guint32 width, guint32 height, guint64 duration, gpointer extra_data, guint32 extra_data_size)
4218 : IMediaStream (Type::VIDEOSTREAM, media)
4220 converter = NULL;
4221 bits_per_sample = 0;
4222 pts_per_frame = 0;
4223 initial_pts = 0;
4224 this->height = height;
4225 this->width = width;
4226 this->duration = duration;
4227 this->codec_id = codec_id;
4228 this->codec = CreateCodec (codec_id);
4229 this->extra_data = extra_data;
4230 this->extra_data_size = extra_data_size;
4233 VideoStream::~VideoStream ()
4237 void
4238 VideoStream::Dispose ()
4240 if (converter) {
4241 converter->Dispose ();
4242 converter->unref ();
4243 converter = NULL;
4245 IMediaStream::Dispose ();
4249 * MediaMarkerFoundClosure
4252 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media *media, MediaCallback *callback, MediaElement *context)
4253 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE, media, callback, context)
4255 marker = NULL;
4258 void
4259 MediaMarkerFoundClosure::Dispose ()
4261 if (marker) {
4262 marker->unref ();
4263 marker = NULL;
4265 MediaClosure::Dispose ();
4268 void
4269 MediaMarkerFoundClosure::SetMarker (MediaMarker *marker)
4271 if (this->marker)
4272 this->marker->unref ();
4273 this->marker = marker;
4274 if (this->marker)
4275 this->marker->ref ();
4279 * MediaMarker
4282 MediaMarker::MediaMarker (const char *type, const char *text, guint64 pts)
4283 : EventObject (Type::MEDIAMARKER)
4285 this->type = g_strdup (type);
4286 this->text = g_strdup (text);
4287 this->pts = pts;
4290 MediaMarker::~MediaMarker ()
4292 g_free (type);
4293 g_free (text);
4297 * MarkerStream
4300 MarkerStream::MarkerStream (Media *media) : IMediaStream (Type::MARKERSTREAM, media)
4302 closure = NULL;
4305 void
4306 MarkerStream::Dispose ()
4308 if (closure) {
4309 closure->unref ();
4310 closure = NULL;
4313 IMediaStream::Dispose ();
4316 void
4317 MarkerStream::MarkerFound (MediaFrame *frame)
4319 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4321 if (GetDecoder () == NULL) {
4322 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4323 return;
4326 GetDecoder ()->DecodeFrameAsync (frame, false);
4329 void
4330 MarkerStream::FrameEnqueued ()
4332 MediaFrame *frame;
4334 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4336 frame = PopFrame ();
4338 if (frame == NULL) {
4339 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4340 return;
4343 if (closure != NULL) {
4344 closure->SetMarker (frame->marker);
4345 closure->Call ();
4346 closure->SetMarker (NULL);
4347 } else {
4348 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4349 mutex.Lock ();
4350 list.Append (new MediaMarker::Node (frame->marker));
4351 mutex.Unlock ();
4354 frame->unref ();
4357 MediaMarker *
4358 MarkerStream::Pop ()
4360 MediaMarker *result = NULL;
4361 MediaMarker::Node *node;
4363 mutex.Lock ();
4364 node = (MediaMarker::Node *) list.First ();
4365 if (node != NULL) {
4366 result = node->marker;
4367 result->ref ();
4368 list.Remove (node);
4370 mutex.Unlock ();
4372 return result;
4375 void
4376 MarkerStream::SetCallback (MediaMarkerFoundClosure *closure)
4378 if (this->closure)
4379 this->closure->unref ();
4380 this->closure = closure;
4381 if (this->closure)
4382 this->closure->ref ();
4386 * MediaWork
4388 MediaWork::MediaWork (MediaClosure *c)
4390 g_return_if_fail (c != NULL);
4392 closure = c;
4393 closure->ref ();
4396 MediaWork::~MediaWork ()
4398 g_return_if_fail (closure != NULL);
4400 closure->unref ();
4401 closure = NULL;
4405 * PassThroughDecoderInfo
4408 bool
4409 PassThroughDecoderInfo::Supports (const char *codec)
4411 const char *video_fourccs [] = { "yv12", "rgba", NULL };
4412 const char *audio_fourccs [] = { "pcm", NULL };
4414 for (int i = 0; video_fourccs [i] != NULL; i++)
4415 if (!strcmp (codec, video_fourccs [i]))
4416 return true;
4418 for (int i = 0; audio_fourccs [i] != NULL; i++)
4419 if (!strcmp (codec, audio_fourccs [i]))
4420 return true;
4422 return false;
4426 * PassThroughDecoder
4429 PassThroughDecoder::PassThroughDecoder (Media *media, IMediaStream *stream)
4430 : IMediaDecoder (Type::PASSTHROUGHDECODER, media, stream)
4434 void
4435 PassThroughDecoder::Dispose ()
4437 IMediaDecoder::Dispose ();
4440 void
4441 PassThroughDecoder::OpenDecoderAsyncInternal ()
4443 const char *fourcc = GetStream ()->GetCodec ();
4445 if (!strcmp (fourcc, "yv12")) {
4446 SetPixelFormat (MoonPixelFormatYUV420P);
4447 } else if (!strcmp (fourcc, "rgba")) {
4448 SetPixelFormat (MoonPixelFormatRGBA32);
4449 } else if (!strcmp (fourcc, "pcm")) {
4450 // nothing to do here
4451 } else {
4452 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc));
4453 return;
4456 ReportOpenDecoderCompleted ();
4459 void
4460 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4462 frame->AddState (MediaFrameDecoded);
4463 if (GetPixelFormat () == MoonPixelFormatYUV420P) {
4464 VideoStream *vs = (VideoStream *) GetStream ();
4466 frame->width = vs->width;
4467 frame->height = vs->height;
4469 frame->data_stride[0] = frame->buffer;
4470 frame->data_stride[1] = frame->buffer + (frame->width*frame->height);
4471 frame->data_stride[2] = frame->buffer + (frame->width*frame->height)+(frame->width/2*frame->height/2);
4472 frame->buffer = NULL;
4473 frame->srcStride[0] = frame->width;
4474 frame->srcSlideY = frame->width;
4475 frame->srcSlideH = frame->height;
4477 frame->AddState (MediaFramePlanar);
4479 ReportDecodeFrameCompleted (frame);
4483 * NullDecoderInfo
4486 bool
4487 NullDecoderInfo::Supports (const char *codec)
4489 const char *video_fourccs [] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL };
4490 const char *audio_fourccs [] = { "wmav1","wmav2", "wmav3", "mp3", NULL};
4492 for (int i = 0; video_fourccs [i] != NULL; i++)
4493 if (!strcmp (codec, video_fourccs [i]))
4494 return true;
4496 for (int i = 0; audio_fourccs [i] != NULL; i++)
4497 if (!strcmp (codec, audio_fourccs [i]))
4498 return true;
4501 return false;
4505 * NullDecoder
4508 NullDecoder::NullDecoder (Media *media, IMediaStream *stream) : IMediaDecoder (Type::NULLDECODER, media, stream)
4510 logo = NULL;
4511 logo_size = 0;
4512 prev_pts = G_MAXUINT64;
4515 void
4516 NullDecoder::Dispose ()
4518 g_free (logo);
4519 logo = NULL;
4521 IMediaDecoder::Dispose ();
4524 MediaResult
4525 NullDecoder::DecodeVideoFrame (MediaFrame *frame)
4527 // free encoded buffer and alloc a new one for our image
4528 g_free (frame->buffer);
4529 frame->buflen = logo_size;
4530 frame->buffer = (guint8*) g_malloc (frame->buflen);
4531 memcpy (frame->buffer, logo, frame->buflen);
4532 frame->AddState (MediaFrameDecoded);
4534 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4536 return MEDIA_SUCCESS;
4539 MediaResult
4540 NullDecoder::DecodeAudioFrame (MediaFrame *frame)
4542 AudioStream *as = (AudioStream *) GetStream ();
4543 guint32 samples;
4544 guint32 data_size;
4545 guint64 diff_pts;
4547 // discard encoded data
4548 g_free (frame->buffer);
4550 // We have no idea here how long the encoded audio data is
4551 // for the first frame we use 0.1 seconds, for the rest
4552 // we calculate the time since the last frame
4554 if (prev_pts == G_MAXUINT64 || frame->pts <= prev_pts) {
4555 samples = as->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4556 } else {
4557 diff_pts = frame->pts - prev_pts;
4558 samples = (float) as->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT / (float) diff_pts);
4560 prev_pts = frame->pts;
4562 data_size = samples * as->GetChannels () * 2 /* 16 bit audio */;
4564 frame->buflen = data_size;
4565 frame->buffer = (guint8 *) g_malloc0 (frame->buflen);
4567 frame->AddState (MediaFrameDecoded);
4569 return MEDIA_SUCCESS;
4572 void
4573 NullDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4575 MediaResult result = MEDIA_FAIL;
4576 IMediaStream *stream = GetStream ();
4578 if (stream->GetType () == MediaTypeAudio) {
4579 result = DecodeAudioFrame (frame);
4580 } else if (stream->GetType () == MediaTypeVideo) {
4581 result = DecodeVideoFrame (frame);
4584 if (MEDIA_SUCCEEDED (result)) {
4585 ReportDecodeFrameCompleted (frame);
4586 } else {
4587 ReportErrorOccurred (result);
4591 void
4592 NullDecoder::OpenDecoderAsyncInternal ()
4594 MediaResult result;
4595 IMediaStream *stream = GetStream ();
4597 if (stream->GetType () == MediaTypeAudio)
4598 result = OpenAudio ();
4599 else if (stream->GetType () == MediaTypeVideo)
4600 result = OpenVideo ();
4601 else
4602 result = MEDIA_FAIL;
4604 if (MEDIA_SUCCEEDED (result)) {
4605 ReportOpenDecoderCompleted ();
4606 } else {
4607 ReportErrorOccurred (result);
4611 MediaResult
4612 NullDecoder::OpenAudio ()
4614 return MEDIA_SUCCESS;
4617 MediaResult
4618 NullDecoder::OpenVideo ()
4620 VideoStream *vs = (VideoStream *) GetStream ();
4621 guint32 dest_height = vs->height;
4622 guint32 dest_width = vs->width;
4623 guint32 dest_i = 0;
4625 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4626 extern const char moonlight_logo [];
4627 const char *image = moonlight_logo;
4629 guint32 img_offset = *((guint32*)(image + 10));
4630 guint32 img_width = *((guint32*)(image + 18));
4631 guint32 img_height = *((guint32*)(image + 22));
4632 guint32 img_stride = (img_width * 3 + 3) & ~3; // in bytes
4633 guint32 img_i, img_h, img_w;
4634 guint32 start_w = (dest_width-img_width)/2;
4635 guint32 end_w = start_w + img_width;
4636 guint32 start_h = (dest_height-img_height)/2;
4637 guint32 end_h = start_h + img_height;
4639 LOG_PIPELINE ("offset: %i, width: 0x%x = %i, height: 0x%x = %i, stride: %i\n", img_offset, img_width, img_width, img_height, img_height, img_stride);
4641 // create the buffer for our image
4642 logo_size = dest_height * dest_width * 4;
4643 logo = (guint8*) g_malloc (logo_size);
4644 memset (logo, 0x00, logo_size);
4646 // write our image centered into the destination rectangle, flipped horizontally
4647 dest_i = 4;
4648 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4649 for (guint32 dest_w = 0; dest_w < dest_width; dest_w++) {
4650 if (dest_w >= start_w && dest_w < end_w && dest_h >= start_h && dest_h < end_h) {
4651 img_h = (dest_h - start_h) % img_height;
4652 img_w = (dest_w - start_w) % img_width;
4653 img_i = img_h * img_stride + img_w * 3;
4655 logo [logo_size - dest_i + 0] = image [img_offset + img_i + 0];
4656 logo [logo_size - dest_i + 1] = image [img_offset + img_i + 1];
4657 logo [logo_size - dest_i + 2] = image [img_offset + img_i + 2];
4659 logo [logo_size - dest_i + 3] = 0xff;
4661 dest_i += 4;
4665 // Flip the image vertically
4666 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4667 for (guint32 dest_w = 0; dest_w < dest_width / 2; dest_w++) {
4668 guint32 tmp;
4669 guint32 a = (dest_h * dest_width + dest_w) * 4;
4670 guint32 b = (dest_h * dest_width + dest_width - dest_w) * 4 - 4;
4671 for (guint32 c = 0; c < 3; c++) {
4672 tmp = logo [a + c];
4673 logo [a + c] = logo [b + c];
4674 logo [b + c] = tmp;
4679 SetPixelFormat (MoonPixelFormatRGB32);
4681 return MEDIA_SUCCESS;
4685 * ExternalDemuxer
4688 ExternalDemuxer::ExternalDemuxer (Media *media, void *instance, CloseDemuxerCallback close_demuxer,
4689 GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample, OpenDemuxerAsyncCallback open_demuxer,
4690 SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
4691 : IMediaDemuxer (Type::EXTERNALDEMUXER, media)
4693 this->close_demuxer_callback = close_demuxer;
4694 this->get_diagnostic_async_callback = get_diagnostic;
4695 this->get_sample_async_callback = get_sample;
4696 this->open_demuxer_async_callback = open_demuxer;
4697 this->seek_async_callback = seek;
4698 this->switch_media_stream_async_callback = switch_media_stream;
4699 this->instance = instance;
4701 pthread_rwlock_init (&rwlock, NULL);
4703 g_return_if_fail (instance != NULL);
4704 g_return_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL);
4707 ExternalDemuxer::~ExternalDemuxer ()
4709 pthread_rwlock_destroy (&rwlock);
4712 void
4713 ExternalDemuxer::Dispose ()
4715 ClearCallbacks ();
4716 IMediaDemuxer::Dispose ();
4719 void
4720 ExternalDemuxer::ClearCallbacks ()
4722 pthread_rwlock_wrlock (&rwlock);
4723 close_demuxer_callback = NULL;
4724 get_diagnostic_async_callback = NULL;
4725 get_sample_async_callback = NULL;
4726 open_demuxer_async_callback = NULL;
4727 seek_async_callback = NULL;
4728 switch_media_stream_async_callback = NULL;
4729 instance = NULL;
4730 pthread_rwlock_unlock (&rwlock);
4733 void
4734 ExternalDemuxer::SetCanSeek (bool value)
4736 g_warning ("TODO: ExternalDemuxer::SetCanSeek ()");
4739 gint32
4740 ExternalDemuxer::AddStream (IMediaStream *stream)
4742 return IMediaDemuxer::AddStream (stream);
4745 void
4746 ExternalDemuxer::CloseDemuxerInternal ()
4748 pthread_rwlock_rdlock (&rwlock);
4749 if (close_demuxer_callback != NULL) {
4750 close_demuxer_callback (instance);
4751 } else {
4752 #if SANITY
4753 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4754 #endif
4756 pthread_rwlock_unlock (&rwlock);
4759 void
4760 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind)
4762 pthread_rwlock_rdlock (&rwlock);
4763 if (get_diagnostic_async_callback != NULL) {
4764 get_diagnostic_async_callback (instance, diagnosticsKind);
4765 } else {
4766 #if SANITY
4767 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4768 #endif
4770 pthread_rwlock_unlock (&rwlock);
4773 void
4774 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
4776 g_return_if_fail (stream != NULL);
4778 pthread_rwlock_rdlock (&rwlock);
4779 if (get_sample_async_callback != NULL) {
4780 get_sample_async_callback (instance, stream->GetStreamType ());
4781 } else {
4782 #if SANITY
4783 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4784 #endif
4786 pthread_rwlock_unlock (&rwlock);
4789 void
4790 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4792 pthread_rwlock_rdlock (&rwlock);
4793 if (open_demuxer_async_callback != NULL) {
4794 open_demuxer_async_callback (instance, this);
4795 } else {
4796 #if SANITY
4797 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4798 #endif
4800 pthread_rwlock_unlock (&rwlock);
4803 void
4804 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime)
4806 pthread_rwlock_rdlock (&rwlock);
4807 if (seek_async_callback != NULL) {
4808 seek_async_callback (instance, seekToTime);
4809 } else {
4810 #if SANITY
4811 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4812 #endif
4814 pthread_rwlock_unlock (&rwlock);
4817 void
4818 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *mediaStreamDescription)
4820 g_return_if_fail (mediaStreamDescription != NULL);
4822 pthread_rwlock_rdlock (&rwlock);
4823 if (switch_media_stream_async_callback != NULL) {
4824 switch_media_stream_async_callback (instance, mediaStreamDescription);
4825 } else {
4826 #if SANITY
4827 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4828 #endif
4830 pthread_rwlock_unlock (&rwlock);
4835 * AudioStream
4838 AudioStream::AudioStream (Media *media)
4839 : IMediaStream (Type::AUDIOSTREAM, media)
4843 AudioStream::AudioStream (Media *media, int codec_id, int bits_per_sample, int block_align, int sample_rate, int channels, int bit_rate, gpointer extra_data, guint32 extra_data_size)
4844 : IMediaStream (Type::AUDIOSTREAM, media)
4846 this->codec_id = codec_id;
4847 this->codec = CreateCodec (codec_id);
4848 this->extra_data = extra_data;
4849 this->extra_data_size = extra_data_size;
4850 input_bits_per_sample = bits_per_sample;
4851 output_bits_per_sample = bits_per_sample;
4852 input_block_align = block_align;
4853 output_block_align = block_align;
4854 input_sample_rate = sample_rate;
4855 output_sample_rate = sample_rate;
4856 input_channels = channels;
4857 output_channels = channels;
4858 input_bit_rate = bit_rate;
4859 output_bit_rate = bit_rate;
4863 * ExternalDecoder
4866 ExternalDecoder::ExternalDecoder (Media *media, IMediaStream *stream, void *instance, const char *name,
4867 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async,
4868 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async,
4869 ExternalDecoder_CleanupCallback cleanup,
4870 ExternalDecoder_CleanStateCallback clean_state,
4871 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame,
4872 ExternalDecoder_DisposeCallback dispose,
4873 ExternalDecoder_DtorCallback dtor)
4874 : IMediaDecoder (Type::EXTERNALDECODER, media, stream)
4876 this->instance = instance;
4877 this->name = g_strdup (name);
4878 this->decode_frame_async = decode_frame_async;
4879 this->open_decoder_async = open_decoder_async;
4880 this->cleanup = cleanup;
4881 this->clean_state = clean_state;
4882 this->has_delayed_frame = has_delayed_frame;
4883 this->dispose = dispose;
4884 this->dtor = dtor;
4887 ExternalDecoder::~ExternalDecoder ()
4889 dtor (instance);
4890 g_free (name);
4893 void
4894 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4896 decode_frame_async (instance, frame);
4899 void
4900 ExternalDecoder::OpenDecoderAsyncInternal ()
4902 open_decoder_async (instance);
4905 void
4906 ExternalDecoder::Dispose ()
4908 dispose (instance);
4910 IMediaDecoder::Dispose ();
4913 void
4914 ExternalDecoder::Cleanup (MediaFrame *frame)
4916 cleanup (instance, frame);
4919 void
4920 ExternalDecoder::CleanState ()
4922 clean_state (instance);
4925 bool
4926 ExternalDecoder::HasDelayedFrame ()
4928 return has_delayed_frame (instance);
4931 void
4932 ExternalDecoder::InputEnded ()
4934 GetStream ()->SetOutputEnded (true);
4938 * ExternalDecoderInfo
4941 ExternalDecoderInfo::ExternalDecoderInfo (void *instance, const char *name, ExternalDecoderInfo_SupportsCallback supports, ExternalDecoderInfo_Create create, ExternalDecoderInfo_dtor dtor)
4943 this->instance = instance;
4944 this->supports = supports;
4945 this->create = create;
4946 this->dtor = dtor;
4947 this->name = g_strdup (name);
4950 bool
4951 ExternalDecoderInfo::Supports (const char *codec)
4953 return supports (instance, codec);
4956 IMediaDecoder *
4957 ExternalDecoderInfo::Create (Media *media, IMediaStream *stream)
4959 return create (instance, media, stream);
4962 ExternalDecoderInfo::~ExternalDecoderInfo ()
4964 if (dtor != NULL)
4965 dtor (instance);
4966 g_free (name);