added MouseWheel event support for Silverlight 3.0
[moon.git] / src / pipeline.cpp
blob8a8305f07c0699faf736361a10c4711a842f3bd3
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;
93 LOG_PIPELINE ("Media::Dispose (), id: %i\n", GET_OBJ_ID (this));
95 #if SANITY
96 if (!MediaThreadPool::IsThreadPoolThread ()) {
97 g_warning ("Media::Dispose (): Not in thread-pool thread.\n");
99 #endif
101 mutex.Lock ();
102 is_disposed = true;
103 mutex.Unlock ();
105 ClearQueue ();
108 * We're on a media thread, and there is no other work in the queue: we can ensure that nothing
109 * more will ever execute on the media thread related to this Media instance.
112 g_free (file);
113 file = NULL;
114 g_free (uri);
115 uri = NULL;
117 src = this->source;
118 this->source = NULL;
119 if (src) {
120 src->Dispose ();
121 src->unref ();
124 dmx = this->demuxer;
125 this->demuxer = NULL;
126 if (dmx) {
127 dmx->Dispose ();
128 dmx->unref ();
131 delete markers;
132 markers = NULL;
134 IMediaObject::Dispose ();
136 GetDeployment ()->UnregisterMedia (this);
139 bool
140 Media::IsMSCodecsInstalled ()
142 return registered_ms_codecs;
145 void
146 Media::RegisterMSCodecs (void)
148 register_codec reg;
149 void *dl;
150 char *libmscodecs_path = NULL;
151 const char *functions [] = {"register_codec_pack", NULL};
152 const gchar *home = g_get_home_dir ();
153 registering_ms_codecs = true;
155 if (!(moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS)) {
156 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
157 return;
160 if (home != NULL)
161 libmscodecs_path = g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME, NULL);
163 if (!(g_file_test (libmscodecs_path, G_FILE_TEST_EXISTS) && g_file_test (libmscodecs_path, G_FILE_TEST_IS_REGULAR))) {
164 if (libmscodecs_path)
165 g_free (libmscodecs_path);
166 libmscodecs_path = g_strdup (CODEC_LIBRARY_NAME);
169 dl = dlopen (libmscodecs_path, RTLD_LAZY);
170 if (dl != NULL) {
171 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path);
173 for (int i = 0; functions [i] != NULL; i++) {
174 reg = (register_codec) dlsym (dl, functions [i]);
175 if (reg != NULL) {
176 (*reg) (MOONLIGHT_CODEC_ABI_VERSION);
177 } else {
178 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions [i], libmscodecs_path);
181 registered_ms_codecs = true;
182 } else {
183 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path, dlerror ());
185 g_free (libmscodecs_path);
187 registering_ms_codecs = false;
190 void
191 Media::SetBufferingEnabled (bool value)
193 buffering_enabled = value;
194 WakeUp ();
197 void
198 Media::SetBufferingTime (guint64 buffering_time)
200 mutex.Lock ();
201 this->buffering_time = buffering_time;
202 mutex.Unlock ();
204 if (demuxer != NULL)
205 demuxer->FillBuffers ();
208 guint64
209 Media::GetBufferingTime ()
211 guint64 result;
212 mutex.Lock ();
213 result = buffering_time;
214 mutex.Unlock ();
215 return result;
218 PlaylistRoot *
219 Media::GetPlaylistRoot ()
221 return playlist;
224 List *
225 Media::GetMarkers ()
227 if (markers == NULL)
228 markers = new List ();
230 return markers;
233 void
234 Media::RegisterDemuxer (DemuxerInfo *info)
236 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
237 info->next = NULL;
238 if (registered_demuxers == NULL) {
239 registered_demuxers = info;
240 } else {
241 MediaInfo* current = registered_demuxers;
242 while (current->next != NULL)
243 current = current->next;
244 current->next = info;
248 void
249 Media::RegisterConverter (ConverterInfo *info)
251 //printf ("Media::RegisterConverter (%p)\n", info);
252 info->next = NULL;
253 if (registered_converters == NULL) {
254 registered_converters = info;
255 } else {
256 MediaInfo *current = registered_converters;
257 while (current->next != NULL)
258 current = current->next;
259 current->next = info;
263 void
264 Media::RegisterDecoder (DecoderInfo *info)
266 MediaInfo *current;
268 //printf ("Media::RegisterDecoder (%p)\n", info);
269 info->next = NULL;
270 if (registered_decoders == NULL) {
271 registered_decoders = info;
272 } else {
273 if (registering_ms_codecs) {
274 // MS codecs might get registered after all other codecs (right after installing them),
275 // which means after the null codecs so if they don't get special treatment, they won't
276 // get used until the next browser restart (when they're registered normally).
277 // So instead of appending them, we prepend them.
278 info->next = registered_decoders;
279 registered_decoders = info;
280 } else {
281 current = registered_decoders;
282 while (current->next != NULL)
283 current = current->next;
284 current->next = info;
287 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info->GetName ());
290 void
291 Media::Initialize ()
293 LOG_PIPELINE ("Media::Initialize ()\n");
295 // demuxers
296 Media::RegisterDemuxer (new ASFDemuxerInfo ());
297 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
298 Media::RegisterDemuxer (new ASXDemuxerInfo ());
300 // converters
301 if (!(moonlight_flags & RUNTIME_INIT_FFMPEG_YUV_CONVERTER))
302 Media::RegisterConverter (new YUVConverterInfo ());
304 // decoders
305 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
306 if (moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS) {
307 RegisterMSCodecs ();
309 #ifdef INCLUDE_FFMPEG
310 if (!(moonlight_flags & RUNTIME_INIT_DISABLE_FFMPEG_CODECS)) {
311 register_ffmpeg ();
313 #endif
315 Media::RegisterDecoder (new PassThroughDecoderInfo ());
316 Media::RegisterDecoder (new NullDecoderInfo ());
318 MediaThreadPool::Initialize ();
321 void
322 Media::Shutdown ()
324 LOG_PIPELINE ("Media::Shutdown ()\n");
326 MediaInfo *current;
327 MediaInfo *next;
329 // Make sure all threads are stopped
330 AudioPlayer::Shutdown ();
331 MediaThreadPool::Shutdown ();
333 current = registered_decoders;
334 while (current != NULL) {
335 next = current->next;
336 delete current;
337 current = next;
339 registered_decoders = NULL;
341 current = registered_demuxers;
342 while (current != NULL) {
343 next = current->next;
344 delete current;
345 current = next;
347 registered_demuxers = NULL;
349 current = registered_converters;
350 while (current != NULL) {
351 next = current->next;
352 delete current;
353 current = next;
355 registered_converters = NULL;
357 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
360 void
361 Media::Warning (MediaResult result, const char *format, ...)
363 va_list args;
365 if (MEDIA_SUCCEEDED (result))
366 return;
368 fprintf (stderr, "Moonlight: MediaResult = %d; ", result);
370 va_start (args, format);
371 vfprintf (stderr, format, args);
372 va_end (args);
374 fputc ('\n', stderr);
377 bool
378 Media::InMediaThread ()
380 return MediaThreadPool::IsThreadPoolThread ();
383 void
384 Media::ReportBufferingProgress (double progress)
386 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress, buffering_progress);
388 progress = MAX (MIN (progress, 1.0), 0.0);
390 if (progress == buffering_progress)
391 return;
393 if (progress < buffering_progress || progress > (buffering_progress + 0.005) || progress == 1.0 || progress == 0.0) {
394 buffering_progress = progress;
395 EmitSafe (BufferingProgressChangedEvent, new ProgressEventArgs (progress));
399 void
400 Media::ReportDownloadProgress (double progress)
402 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress, download_progress);
404 progress = MAX (MIN (progress, 1.0), 0.0);
406 // download progress can only go up
407 g_return_if_fail (progress >= download_progress);
409 if (progress == download_progress)
410 return;
412 if (progress > (download_progress + 0.005) || progress == 1.0 || progress == 0.0) {
413 download_progress = progress;
414 EmitSafe (DownloadProgressChangedEvent, new ProgressEventArgs (progress));
418 void
419 Media::SeekAsync (guint64 pts)
421 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
423 if (demuxer == NULL) {
424 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
425 return;
428 demuxer->SeekAsync (pts);
431 void
432 Media::ReportSeekCompleted (guint64 pts)
434 LOG_PIPELINE ("Media::ReportSeekCompleted (%llu), id: %i\n", pts, GET_OBJ_ID (this));
436 buffering_progress = 0;
437 ClearQueue ();
438 EmitSafe (SeekCompletedEvent);
441 void
442 Media::ReportOpenCompleted ()
444 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
446 EmitSafe (OpenCompletedEvent);
449 void
450 Media::ReportOpenDemuxerCompleted ()
452 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
454 OpenInternal ();
457 void
458 Media::ReportOpenDecoderCompleted (IMediaDecoder *decoder)
460 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder, GET_OBJ_ID (this));
462 g_return_if_fail (decoder != NULL);
464 OpenInternal ();
467 void
468 Media::ReportErrorOccurred (ErrorEventArgs *args)
470 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args, args == NULL ? NULL : args->GetErrorMessage());
472 if (args) {
473 fprintf (stderr, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args->GetErrorType()), args->GetErrorCode(), args->GetErrorMessage(), args->GetExtendedMessage());
474 } else {
475 fprintf (stderr, "Moonlight: Unspecified media error.\n");
478 if (!error_reported) {
479 error_reported = true;
480 EmitSafe (MediaErrorEvent, args);
484 void
485 Media::ReportErrorOccurred (const char *message)
487 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message);
489 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, message)));
492 void
493 Media::ReportErrorOccurred (MediaResult result)
495 char *msg = g_strdup_printf ("Media error: %i.", result);
496 ReportErrorOccurred (msg);
497 g_free (msg);
500 void
501 Media::PlayAsync ()
503 LOG_PIPELINE ("Media::PlayAsync ()\n");
505 MediaClosure *closure = new MediaClosure (this, PlayCallback, this, "Media::PlayAsync");
506 EnqueueWork (closure);
507 closure->unref ();
510 void
511 Media::PauseAsync ()
513 LOG_PIPELINE ("Media::PauseAsync ()\n");
516 void
517 Media::StopAsync ()
519 LOG_PIPELINE ("Media::StopAsync ()\n");
521 MediaClosure *closure = new MediaClosure (this, StopCallback, this, "Media::StopAsync");
522 EnqueueWork (closure);
523 closure->unref ();
526 MediaResult
527 Media::StopCallback (MediaClosure *closure)
529 closure->GetMedia ()->Stop ();
530 return MEDIA_SUCCESS;
533 MediaResult
534 Media::PlayCallback (MediaClosure *closure)
536 closure->GetMedia ()->Play ();
537 return MEDIA_SUCCESS;
540 void
541 Media::Stop ()
543 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
545 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
547 stopped = true;
549 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
550 /* ClearQueue (); */
552 if (demuxer != NULL)
553 demuxer->ClearBuffers ();
556 void
557 Media::Play ()
559 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
561 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
563 stopped = false;
564 if (demuxer != NULL)
565 demuxer->FillBuffers ();
568 void
569 Media::Initialize (Downloader *downloader, const char *PartName)
571 IMediaSource *source;
573 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader, PartName, GET_OBJ_ID (this));
575 g_return_if_fail (downloader != NULL);
576 g_return_if_fail (file == NULL);
577 g_return_if_fail (uri != NULL || PartName != NULL);
578 g_return_if_fail (initialized == false);
579 g_return_if_fail (error_reported == false);
580 g_return_if_fail (this->source == NULL);
582 if (downloader->Completed ()) {
583 file = downloader->GetDownloadedFilename (PartName);
585 if (file == NULL) {
586 ReportErrorOccurred ("Couldn't get downloaded filename.");
587 return;
591 if (file == NULL && PartName != NULL && PartName [0] != 0) {
592 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)");
593 return;
596 if (file == NULL) {
597 InternalDownloader *idl = downloader->GetInternalDownloader ();
598 MmsDownloader *mms_dl = (idl && idl->GetObjectType () == Type::MMSDOWNLOADER) ? (MmsDownloader *) idl : NULL;
600 if (mms_dl == NULL) {
601 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
602 return;
605 source = new MmsSource (this, downloader);
606 } else {
607 source = new FileSource (this, file);
610 Initialize (source);
611 source->unref ();
614 void
615 Media::Initialize (IMediaSource *source)
617 MediaResult result;
619 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source, GET_OBJ_ID (this));
621 g_return_if_fail (source != NULL);
622 g_return_if_fail (this->source == NULL);
623 g_return_if_fail (initialized == false);
625 result = source->Initialize ();
626 if (!MEDIA_SUCCEEDED (result)) {
627 ReportErrorOccurred (result);
628 return;
631 initialized = true;
632 this->source = source;
633 this->source->ref ();
636 void
637 Media::Initialize (const char *uri)
639 Downloader *dl;
640 IMediaSource *source = NULL;
642 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri, GET_OBJ_ID (this));
644 g_return_if_fail (uri != NULL);
645 g_return_if_fail (file == NULL);
646 g_return_if_fail (uri != NULL);
647 g_return_if_fail (initialized == false);
648 g_return_if_fail (error_reported == false);
649 g_return_if_fail (source == NULL);
650 g_return_if_fail (this->source == NULL);
652 this->uri = g_strdup (uri);
655 if (g_str_has_prefix (uri, "mms://") || g_str_has_prefix (uri, "rtsp://") || g_str_has_prefix (uri, "rtsps://")) {
656 dl = Surface::CreateDownloader (this);
657 if (dl == NULL) {
658 ReportErrorOccurred ("Couldn't create downloader.");
659 return;
662 dl->Open ("GET", uri, StreamingPolicy);
664 if (dl->GetFailedMessage () == NULL) {
665 Initialize (dl, NULL);
666 } else {
667 ReportErrorOccurred (new ErrorEventArgs (MediaError,
668 MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
671 dl->unref ();
673 return;
676 source = new ProgressiveSource (this, uri);
677 Initialize (source);
678 source->unref ();
681 void
682 Media::Initialize (IMediaDemuxer *demuxer)
684 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer, GET_OBJ_ID (this));
686 g_return_if_fail (demuxer != NULL);
687 g_return_if_fail (this->demuxer == NULL);
688 g_return_if_fail (initialized == false);
690 this->demuxer = demuxer;
691 this->demuxer->ref ();
693 initialized = true;
697 void
698 Media::RetryHttp (ErrorEventArgs *args)
700 char *http_uri = NULL;
702 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri);
704 g_return_if_fail (uri != NULL);
705 g_return_if_fail (source != NULL);
707 if (http_retried) {
708 ReportErrorOccurred (args);
709 return;
712 // CHECK: If the current protocolo is rtsps, should we retry http or https?
714 if (g_str_has_prefix (uri, "mms://")) {
715 http_uri = g_strdup_printf ("http://%s", uri + 6);
716 } else if (g_str_has_prefix (uri, "rtsp://")) {
717 http_uri = g_strdup_printf ("http://%s", uri + 7);
718 } else if (g_str_has_prefix (uri, "rtsps://")) {
719 http_uri = g_strdup_printf ("http://%s", uri + 8);
720 } else {
721 ReportErrorOccurred (args);
722 return;
725 http_retried = true;
727 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri);
729 g_free (uri);
730 uri = NULL;
731 source->Dispose ();
732 source->unref ();
733 source = NULL;
734 initialized = false;
736 Initialize (http_uri);
738 g_free (http_uri);
740 if (!error_reported)
741 OpenAsync ();
744 void
745 Media::OpenAsync ()
747 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
749 g_return_if_fail (initialized == true);
751 EmitSafe (OpeningEvent);
753 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenAsync");
754 EnqueueWork (closure);
755 closure->unref ();
758 void
759 Media::OpenInternal ()
761 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
763 g_return_if_fail (initialized == true);
765 if (opened) {
766 // This may happen due to the recursion detection below
767 // Example: we try open a demuxer, the demuxer opens successfully
768 // right away and calls ReportDemuxerOpenComplete which will call
769 // us. Due to the recursion detection we'll enqueue a call to
770 // OpenInternal, while the first OpenInternal may succeed and
771 // set opened to true.
772 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
773 return;
776 // detect recursive calls.
778 if (in_open_internal) {
779 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
780 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
781 EnqueueWork (closure);
782 closure->unref ();
783 return;
786 in_open_internal = true;
788 if (error_reported)
789 goto cleanup;
791 if (!SelectDemuxerAsync ()) {
792 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
793 goto cleanup;
796 if (error_reported)
797 goto cleanup;
799 if (!SelectDecodersAsync ()) {
800 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
801 goto cleanup;
804 demuxer->FillBuffers ();
806 opened = true;
807 opening = false;
809 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
811 EmitSafe (OpenCompletedEvent);
813 cleanup:
814 in_open_internal = false;
817 MediaResult
818 Media::OpenInternal (MediaClosure *closure)
820 Media *media = (Media *) closure->GetContext ();
822 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
824 media->OpenInternal ();
826 return MEDIA_SUCCESS;
829 bool
830 Media::SelectDemuxerAsync ()
832 DemuxerInfo *demuxerInfo;
833 MediaResult support;
834 MediaResult result;
835 bool eof;
837 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);
839 g_return_val_if_fail (error_reported == false, false);
840 g_return_val_if_fail (initialized == true, false);
842 // Check if demuxer already is open
843 if (demuxer != NULL) {
844 if (demuxer->IsOpened ())
845 return true;
846 if (!demuxer->IsOpening ())
847 demuxer->OpenDemuxerAsync ();
848 return demuxer->IsOpened ();
851 g_return_val_if_fail (source != NULL, false);
853 // Check if the source knows how to create the demuxer
854 demuxer = source->CreateDemuxer (this);
856 if (demuxer == NULL) { // No demuxer created, we need to find it ourselves.
857 // Check if we have at least 1024 bytes or eof
858 if (!source->IsPositionAvailable (16, &eof)) {
859 if (!eof) {
860 // We need to try again later.
861 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
863 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
864 EnqueueWork (closure, false);
865 closure->unref ();
867 return false;
871 // Select a demuxer
872 demuxerInfo = registered_demuxers;
873 while (demuxer == NULL && demuxerInfo != NULL) {
874 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo->GetName ());
875 support = demuxerInfo->Supports (source);
877 if (support == MEDIA_SUCCESS)
878 break;
880 result = support;
882 if (result == MEDIA_NOT_ENOUGH_DATA) {
883 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 ());
885 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
886 EnqueueWork (closure, false);
887 closure->unref ();
889 return false;
892 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo->GetName ());
893 demuxerInfo = (DemuxerInfo *) demuxerInfo->next;
896 if (demuxerInfo == NULL) {
897 // No demuxer found, report an error
898 const char *source_name = file ? file : uri;
900 if (!source_name) {
901 switch (source->GetType ()) {
902 case MediaSourceTypeProgressive:
903 case MediaSourceTypeFile:
904 source_name = ((FileSource *) source)->GetFileName ();
905 break;
906 case MediaSourceTypeMms:
907 case MediaSourceTypeMmsEntry:
908 source_name = "live source";
909 break;
910 default:
911 source_name = "unknown source";
912 break;
915 char *msg = g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name);
916 ReportErrorOccurred (new ErrorEventArgs (MediaError,
917 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT"),
918 MEDIA_UNKNOWN_CODEC, msg));
919 g_free (msg);
920 return false;
923 // Found a demuxer
924 demuxer = demuxerInfo->Create (this, source);
925 } else {
926 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer->GetTypeName ());
929 if (demuxer->IsOpened ())
930 return true;
932 if (demuxer->IsOpening ())
933 return false;
935 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
937 demuxer->OpenDemuxerAsync ();
939 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
941 return demuxer != NULL && demuxer->IsOpened ();
944 bool
945 Media::SelectDecodersAsync ()
947 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
949 g_return_val_if_fail (error_reported == false, false);
950 g_return_val_if_fail (initialized == true, false);
952 if (demuxer == NULL) {
953 ReportErrorOccurred ("No demuxer to select decoders from.");
954 return false;
957 // If the demuxer has no streams (ASXDemuxer for instance)
958 // then just return success.
959 if (demuxer->GetStreamCount () == 0)
960 return true;
962 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
964 // Select codecs for each stream
965 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
966 IMediaStream *stream = demuxer->GetStream (i);
969 if (stream == NULL) {
970 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
971 return false;
974 if (stream->GetDecoder () != NULL)
975 continue;
977 const char *codec = stream->GetCodec ();
978 IMediaDecoder *decoder = NULL;
980 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec);
982 DecoderInfo *current_decoder = registered_decoders;
983 while (current_decoder != NULL && !current_decoder->Supports (codec)) {
984 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder->GetName (), codec);
985 current_decoder = (DecoderInfo*) current_decoder->next;
988 if (current_decoder == NULL) {
989 Media::Warning (MEDIA_UNKNOWN_CODEC, "Unknown codec: '%s'.", codec);
990 continue;
993 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder->GetName (), codec);
994 decoder = current_decoder->Create (this, stream);
996 stream->SetDecoder (decoder);
997 decoder->unref ();
1000 if (error_reported)
1001 return false;
1003 // Open the codecs
1004 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1006 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1007 IMediaStream *stream = demuxer->GetStream (i);
1008 IMediaDecoder *decoder;
1010 if (stream == NULL)
1011 continue;
1013 decoder = stream->GetDecoder ();
1015 if (decoder == NULL) {
1016 ReportErrorOccurred (new ErrorEventArgs (MediaError,
1017 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT")));
1018 return false;
1021 if (decoder->IsOpening () || decoder->IsOpened ())
1022 continue;
1024 decoder->OpenDecoderAsync ();
1027 if (error_reported)
1028 return false;
1030 // Wait until all the codecs have opened
1031 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1033 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1034 IMediaStream *stream = demuxer->GetStream (i);
1035 IMediaDecoder *decoder;
1037 if (stream == NULL)
1038 continue;
1040 decoder = stream->GetDecoder ();
1042 if (decoder == NULL) {
1043 ReportErrorOccurred (MEDIA_FAIL);
1044 return false;
1047 if (decoder->IsOpening ()) {
1048 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
1049 EnqueueWork (closure, false);
1050 closure->unref ();
1051 return false;
1054 if (!decoder->IsOpened ()) {
1055 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1056 ReportErrorOccurred (MEDIA_FAIL);
1057 return false;
1062 // All the codecs have been opened now.
1063 // Find converters for each of them (whenever required).
1065 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1067 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1068 IMediaStream *stream = demuxer->GetStream (i);
1069 IMediaDecoder *decoder;
1071 if (stream == NULL)
1072 continue;
1074 decoder = stream->GetDecoder ();
1076 if (decoder == NULL) {
1077 ReportErrorOccurred (MEDIA_FAIL);
1078 return false;
1081 if (stream->GetType () != MediaTypeVideo)
1082 continue; // Only video streams need converters
1084 if (decoder->GetPixelFormat () == MoonPixelFormatRGB32 || decoder->GetPixelFormat () == MoonPixelFormatRGBA32)
1085 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1087 // Select converter for this stream
1088 VideoStream *vs = (VideoStream *) stream;
1090 ConverterInfo* current_conv = registered_converters;
1091 while (current_conv != NULL && !current_conv->Supports (decoder->GetPixelFormat (), MoonPixelFormatRGB32)) {
1092 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1093 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1094 current_conv = (ConverterInfo*) current_conv->next;
1098 if (current_conv == NULL) {
1099 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER);
1100 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1101 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1102 return false;
1105 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1106 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1108 vs->converter = current_conv->Create (this, vs);
1109 vs->converter->input_format = decoder->GetPixelFormat ();
1110 vs->converter->output_format = MoonPixelFormatRGB32;
1111 if (!MEDIA_SUCCEEDED (vs->converter->Open ())) {
1112 vs->converter->unref ();
1113 vs->converter = NULL;
1114 ReportErrorOccurred (MEDIA_FAIL);
1115 return false;
1119 // Loop through all the streams, return true if at least one has a codec.
1121 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1122 IMediaStream *stream = demuxer->GetStream (i);
1124 if (stream == NULL)
1125 continue;
1127 if (stream->GetDecoder () != NULL)
1128 return true;
1131 // No codecs found for no stream, report an error.
1132 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1133 return false;
1136 bool
1137 Media::EnqueueWork (MediaClosure *closure, bool wakeup)
1139 bool result = false;
1140 bool disposed;
1142 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure);
1144 g_return_val_if_fail (closure != NULL, false);
1145 g_return_val_if_fail (!IsDisposed (), false);
1147 mutex.Lock ();
1148 disposed = this->is_disposed;
1149 if (disposed) {
1150 result = false;
1151 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed);
1152 } else {
1153 MediaThreadPool::AddWork (closure, wakeup);
1154 result = true;
1156 mutex.Unlock ();
1158 return result;
1161 MediaResult
1162 Media::DisposeObjectInternal (MediaClosure *closure)
1164 closure->GetContext ()->Dispose ();
1165 return MEDIA_SUCCESS;
1168 void
1169 Media::DisposeObject (EventObject *obj)
1171 MediaDisposeObjectClosure *closure = new MediaDisposeObjectClosure (this, DisposeObjectInternal, obj);
1172 if (!EnqueueWork (closure, true)) {
1173 #if DEBUG && SANITY
1174 printf ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj);
1175 #endif
1176 obj->Dispose ();
1178 closure->unref ();
1181 void
1182 Media::WakeUp ()
1184 MediaThreadPool::WakeUp ();
1187 void
1188 Media::ClearQueue ()
1190 LOG_PIPELINE ("Media::ClearQueue ().\n");
1191 MediaThreadPool::RemoveWork (this);
1195 * ASXDemuxer
1198 ASXDemuxer::ASXDemuxer (Media *media, IMediaSource *source)
1199 : IMediaDemuxer (Type::ASXDEMUXER, media, source)
1201 playlist = NULL;
1204 ASXDemuxer::~ASXDemuxer ()
1208 void
1209 ASXDemuxer::Dispose ()
1211 if (playlist) {
1212 playlist->unref ();
1213 playlist = NULL;
1215 IMediaDemuxer::Dispose ();
1218 void
1219 ASXDemuxer::OpenDemuxerAsyncInternal ()
1221 MediaResult result;
1222 PlaylistRoot *root;
1223 ErrorEventArgs *args = NULL;
1224 Media *media = GetMediaReffed ();
1226 g_return_if_fail (media != NULL);
1228 root = media->GetPlaylistRoot ();
1230 g_return_if_fail (root != NULL);
1232 PlaylistParser *parser = new PlaylistParser (root, source);
1234 if (MEDIA_SUCCEEDED (parser->Parse ())) {
1235 result = MEDIA_SUCCESS;
1236 playlist = parser->GetPlaylist ();
1237 playlist->ref ();
1238 } else {
1239 result = MEDIA_FAIL;
1240 args = parser->GetErrorEventArgs ();
1241 if (args != NULL)
1242 args->ref ();
1245 delete parser;
1247 if (MEDIA_SUCCEEDED (result)) {
1248 ReportOpenDemuxerCompleted ();
1249 } else if (args != NULL) {
1250 args->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1251 ReportErrorOccurred (args);
1252 } else {
1253 ReportErrorOccurred (result);
1255 if (args)
1256 args->unref ();
1258 media->unref ();
1262 * ASXDemuxerInfo
1265 MediaResult
1266 ASXDemuxerInfo::Supports (IMediaSource *source)
1268 if (PlaylistParser::IsASX2 (source) || PlaylistParser::IsASX3 (source)) {
1269 return MEDIA_SUCCESS;
1270 } else {
1271 return MEDIA_FAIL;
1275 IMediaDemuxer *
1276 ASXDemuxerInfo::Create (Media *media, IMediaSource *source)
1278 return new ASXDemuxer (media, source);
1282 * ManagedStreamSource
1285 ManagedStreamSource::ManagedStreamSource (Media *media, ManagedStreamCallbacks *stream) : IMediaSource (Type::MANAGEDSTREAMSOURCE, media)
1287 memcpy (&this->stream, stream, sizeof (this->stream));
1290 ManagedStreamSource::~ManagedStreamSource ()
1292 stream.handle = NULL;
1295 gint32
1296 ManagedStreamSource::ReadInternal (void *buf, guint32 n)
1298 return stream.Read (stream.handle, buf, 0, n);
1301 gint32
1302 ManagedStreamSource::PeekInternal (void *buf, guint32 n)
1304 int read;
1306 read = stream.Read (stream.handle, buf, 0, n);
1307 stream.Seek (stream.handle, -read, 1 /* SeekOrigin.Current */);
1308 return read;
1311 bool
1312 ManagedStreamSource::SeekInternal (gint64 offset, int mode)
1314 stream.Seek (stream.handle, offset, mode /* FIXME: check if mode values matches SeekOrigin values */);
1315 return true;
1318 gint64
1319 ManagedStreamSource::GetPositionInternal ()
1321 return stream.Position (stream.handle);
1324 gint64
1325 ManagedStreamSource::GetSizeInternal ()
1327 return stream.Length (stream.handle);
1331 * FileSource
1334 FileSource::FileSource (Media *media, const char *filename) : IMediaSource (Type::FILESOURCE, media)
1336 this->filename = g_strdup (filename);
1337 fd = NULL;
1338 size = 0;
1339 temp_file = false;
1342 FileSource::FileSource (Media *media, bool temp_file) : IMediaSource (Type::FILESOURCE, media)
1344 filename = NULL;
1345 fd = NULL;
1346 size = 0;
1347 this->temp_file = temp_file;
1350 FileSource::~FileSource ()
1354 void
1355 FileSource::Dispose ()
1357 g_free (filename);
1358 filename = NULL;
1359 if (fd != NULL) {
1360 fclose (fd);
1361 fd = NULL;
1363 IMediaSource::Dispose ();
1366 MediaResult
1367 FileSource::Initialize ()
1369 int tmp_fd;
1371 LOG_PIPELINE ("FileSource::Initialize ()\n");
1373 if (fd != NULL)
1374 return MEDIA_SUCCESS;
1376 if (temp_file) {
1377 if (filename != NULL)
1378 return MEDIA_FILE_ERROR;
1380 filename = g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL);
1382 if ((tmp_fd = g_mkstemp (filename)) == -1) {
1383 g_free (filename);
1384 filename = NULL;
1386 return MEDIA_FAIL;
1389 fd = fdopen (tmp_fd, "r");
1391 setvbuf (fd, buffer, _IOFBF, sizeof (buffer));
1392 } else {
1393 if (filename == NULL)
1394 return MEDIA_FILE_ERROR;
1396 fd = g_fopen (filename, "r");
1399 if (fd == NULL)
1400 return MEDIA_FILE_ERROR;
1402 UpdateSize ();
1404 return MEDIA_SUCCESS;
1407 MediaResult
1408 FileSource::Open (const char *filename)
1410 g_return_val_if_fail (filename != NULL, MEDIA_FAIL);
1412 g_free (this->filename);
1413 this->filename = g_strdup (filename);
1415 if (fd != NULL) {
1416 fclose (fd);
1417 fd = NULL;
1420 fd = fopen (filename, "r");
1422 if (fd == NULL)
1423 return MEDIA_FAIL;
1425 UpdateSize ();
1427 return MEDIA_SUCCESS;
1430 void
1431 FileSource::UpdateSize ()
1433 struct stat st;
1435 g_return_if_fail (fd != NULL);
1437 if (fstat (fileno (fd), &st) != -1) {
1438 size = st.st_size;
1439 } else {
1440 size = 0;
1444 gint64
1445 FileSource::GetSizeInternal ()
1447 return size;
1450 gint64
1451 FileSource::GetPositionInternal ()
1453 gint64 result;
1455 if (fd == NULL)
1456 return -1;
1458 result = ftell (fd);
1460 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %lld\n", result);
1462 return result;
1465 bool
1466 FileSource::SeekInternal (gint64 offset, int mode)
1468 gint64 n;
1470 if (fd == NULL)
1471 return false;
1473 LOG_PIPELINE ("FileSource::SeekInternal (%lld, %i)\n", offset, mode);
1475 clearerr (fd);
1476 n = fseek (fd, offset, mode);
1478 return n != -1;
1481 gint32
1482 FileSource::ReadInternal (void *buf, guint32 n)
1484 ssize_t nread = 0;
1486 if (fd == NULL) {
1487 errno = EINVAL;
1488 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf, n);
1489 return -1;
1492 clearerr (fd);
1493 nread = fread (buf, 1, n, fd);
1495 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n, (int) nread);
1497 return nread;
1500 gint32
1501 FileSource::PeekInternal (void *buf, guint32 n)
1503 gint32 result;
1505 result = ReadSome (buf, n);
1507 Seek (-result, SEEK_CUR);
1509 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %lld [Done]\n", GET_OBJ_ID (this), buf, n, GetPosition ());
1511 return result;
1514 bool
1515 FileSource::Eof ()
1517 if (fd == NULL)
1518 return false;
1520 return feof (fd);
1524 * ProgressiveSource
1527 ProgressiveSource::ProgressiveSource (Media *media, const char *uri) : FileSource (media, true)
1529 write_pos = 0;
1530 size = -1;
1531 write_fd = NULL;
1532 cancellable = NULL;
1533 this->uri = g_strdup (uri);
1536 void
1537 ProgressiveSource::Dispose ()
1539 g_free (uri);
1540 uri = NULL;
1542 if (cancellable) {
1543 if (Surface::InMainThread ()) {
1544 delete_cancellable (this);
1545 } else {
1546 // we have to cancel/delete he cancellable on the main thread
1547 // it may end up doing a lot of stuff, including calling into
1548 // mozilla.
1550 // The tick call will ref us until the callback has been called.
1551 // Note that it may cause a warning to be printed
1552 // in ref () (reffing an object with a refcount of 0).
1553 // TODO: find a way to avoid the warning in this case, imho this is
1554 // a valid case of reffing an object with a refcount of 0.
1555 AddTickCallSafe (delete_cancellable);
1559 CloseWriteFile ();
1561 FileSource::Dispose ();
1564 void
1565 ProgressiveSource::delete_cancellable (EventObject *data)
1567 ProgressiveSource *src = (ProgressiveSource *) data;
1568 if (src->cancellable) {
1569 src->cancellable->Cancel ();
1570 delete src->cancellable;
1571 src->cancellable = NULL;
1575 MediaResult
1576 ProgressiveSource::Initialize ()
1578 MediaResult result = MEDIA_SUCCESS;
1579 Application *application;
1581 application = GetDeployment ()->GetCurrentApplication ();
1583 g_return_val_if_fail (application != NULL, MEDIA_FAIL);
1584 g_return_val_if_fail (filename == NULL, MEDIA_FAIL);
1585 g_return_val_if_fail (cancellable == NULL, MEDIA_FAIL);
1587 result = FileSource::Initialize ();
1589 if (!MEDIA_SUCCEEDED (result))
1590 return result;
1592 write_fd = g_fopen (filename, "w");
1593 if (write_fd == NULL) {
1594 char *msg = g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename);
1595 ReportErrorOccurred (msg);
1596 g_free (msg);
1597 return MEDIA_FAIL;
1600 // unlink the file right away so that it'll be deleted even if we crash.
1601 if (moonlight_flags & RUNTIME_INIT_KEEP_MEDIA) {
1602 printf ("Moonlight: The media file %s will not deleted.\n", filename);
1603 } else {
1604 g_unlink (filename);
1607 cancellable = new Cancellable ();
1608 Uri *u = new Uri ();
1609 if (u->Parse (uri)) {
1610 application->GetResource (NULL, u, notify_func, data_write, MediaPolicy, cancellable, (gpointer) this);
1611 } else {
1612 result = MEDIA_FAIL;
1613 char *msg = g_strdup_printf ("Could not parse the uri '%s'", uri);
1614 ReportErrorOccurred (msg);
1615 g_free (msg);
1617 delete u;
1619 return result;
1622 void
1623 ProgressiveSource::notify_func (NotifyType type, gint64 args, void *closure)
1625 g_return_if_fail (closure != NULL);
1626 ((ProgressiveSource *) closure)->Notify (type, args);
1629 void
1630 ProgressiveSource::Notify (NotifyType type, gint64 args)
1632 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT ")\n",
1633 type,
1634 type == ::NotifySize ? "NotifySize" :
1635 (type == NotifyCompleted ? "NotifyCompleted" :
1636 (type == NotifyFailed ? "NotifyFailed" :
1637 (type == NotifyStarted ? "NotifyStarted" :
1638 (type == NotifyProgressChanged ? "NotifyProgressChanged" : "unknown")))),
1639 args);
1641 switch (type) {
1642 case ::NotifySize:
1643 NotifySize (args);
1644 break;
1645 case NotifyCompleted:
1646 DownloadComplete ();
1647 break;
1648 case NotifyFailed:
1649 DownloadFailed ();
1650 break;
1651 case NotifyStarted:
1652 case NotifyProgressChanged:
1653 default:
1654 break;
1658 void
1659 ProgressiveSource::data_write (void *data, gint32 offset, gint32 n, void *closure)
1661 g_return_if_fail (closure != NULL);
1662 ((ProgressiveSource *) closure)->DataWrite (data, offset, n);
1665 void
1666 ProgressiveSource::DataWrite (void *buf, gint32 offset, gint32 n)
1668 size_t nwritten;
1669 Media *media = GetMediaReffed ();
1671 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf, offset, n, media, filename);
1673 g_return_if_fail (write_fd != NULL);
1675 if (n == 0) {
1676 // We've got the entire file, update the size
1677 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.
1679 // Close our write handle, we won't write more now
1680 CloseWriteFile ();
1682 goto cleanup;
1685 nwritten = fwrite (buf, 1, n, write_fd);
1686 fflush (write_fd);
1688 Lock ();
1689 write_pos += nwritten;
1690 Unlock ();
1692 cleanup:
1693 if (media) {
1694 media->WakeUp ();
1695 media->ReportDownloadProgress ((double) (offset + n) / (double) size);
1696 media->unref ();
1700 void
1701 ProgressiveSource::NotifySize (gint64 size)
1703 LOG_PIPELINE ("ProgressiveSource::NotifySize (%lld)\n", size);
1705 Lock ();
1706 this->size = size;
1707 Unlock ();
1710 void
1711 ProgressiveSource::DownloadComplete ()
1713 MediaResult result = MEDIA_SUCCESS;
1714 Media *media = GetMediaReffed ();
1716 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1718 Lock ();
1719 if (write_pos != size && size != -1) { // what happend here?
1720 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT ") != the reported size (%" G_GINT64_FORMAT ")\n", write_pos, size);
1723 this->size = write_pos;
1725 // Close our write handle, we won't write more now
1726 CloseWriteFile ();
1728 Unlock ();
1730 if (!MEDIA_SUCCEEDED (result))
1731 ReportErrorOccurred (result);
1733 if (media) {
1734 media->ReportDownloadProgress (1.0);
1735 media->WakeUp ();
1736 media->unref ();
1740 void
1741 ProgressiveSource::DownloadFailed ()
1743 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1745 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
1748 void
1749 ProgressiveSource::CloseWriteFile ()
1751 if (write_fd == NULL)
1752 return;
1754 fclose (write_fd);
1755 write_fd = NULL;
1759 * MemorySource
1762 MemorySource::MemorySource (Media *media, void *memory, gint32 size, gint64 start, bool owner)
1763 : IMediaSource (Type::MEMORYSOURCE, media)
1765 this->memory = memory;
1766 this->size = size;
1767 this->start = start;
1768 this->pos = 0;
1769 this->owner = owner;
1772 MemorySource::~MemorySource ()
1774 if (owner)
1775 g_free (memory);
1778 bool
1779 MemorySource::SeekInternal (gint64 offset, int mode)
1781 gint64 real_offset;
1783 switch (mode) {
1784 case SEEK_SET:
1785 real_offset = offset - start;
1786 if (real_offset < 0 || real_offset >= size)
1787 return false;
1788 pos = real_offset;
1789 return true;
1790 case SEEK_CUR:
1791 if (pos + offset > size || pos + offset < 0)
1792 return false;
1793 pos += offset;
1794 return true;
1795 case SEEK_END:
1796 if (size - offset > size || size - offset < 0)
1797 return false;
1798 pos = size - offset;
1799 return true;
1800 default:
1801 return false;
1803 return true;
1806 gint32
1807 MemorySource::ReadInternal (void *buffer, guint32 n)
1809 guint32 k = MIN (n, size - pos);
1810 memcpy (buffer, ((char*) memory) + pos, k);
1811 pos += k;
1812 return k;
1815 gint32
1816 MemorySource::PeekInternal (void *buffer, guint32 n)
1818 gint64 start = this->start + pos;
1820 if (this->start > start)
1821 return 0;
1823 if ((this->start + size) < (start + n))
1824 return 0;
1826 memcpy (buffer, ((char*) memory) + this->start - start, n);
1827 return n;
1831 * MediaThreadPool
1834 pthread_mutex_t MediaThreadPool::mutex = PTHREAD_MUTEX_INITIALIZER;
1835 pthread_cond_t MediaThreadPool::condition = PTHREAD_COND_INITIALIZER;
1836 pthread_cond_t MediaThreadPool::completed_condition = PTHREAD_COND_INITIALIZER;
1837 int MediaThreadPool::count = 0;
1838 pthread_t MediaThreadPool::threads [max_threads];
1839 Media *MediaThreadPool::medias [max_threads];
1840 Deployment *MediaThreadPool::deployments [max_threads];
1841 bool MediaThreadPool::shutting_down = false;
1842 List *MediaThreadPool::queue = NULL;
1843 bool MediaThreadPool::valid [max_threads];
1845 void
1846 MediaThreadPool::AddWork (MediaClosure *closure, bool wakeup)
1848 pthread_attr_t attribs;
1849 int result = 0;
1851 pthread_mutex_lock (&mutex);
1853 if (shutting_down) {
1854 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1855 } else {
1856 if (queue == NULL)
1857 queue = new List ();
1858 queue->Append (new MediaWork (closure));
1860 // check if all threads are busy with other Media objects
1861 bool spawn = true;
1862 if (count == 0) {
1863 spawn = true;
1864 } else if (count < max_threads) {
1865 Media *media = closure->GetMedia ();
1866 for (int i = 0; i < count; i++) {
1867 if (medias [i] == NULL || medias [i] == media) {
1868 spawn = false; // there is a thread working on this media or not working at all.
1869 break;
1872 } else {
1873 spawn = false;
1876 if (spawn) {
1877 int prev_count = count;
1879 count++; // start up another thread.
1881 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count);
1883 for (int i = prev_count; i < count && result == 0; i++) {
1884 valid [i] = false;
1885 medias [i] = NULL;
1886 deployments [i] = NULL;
1888 pthread_attr_init (&attribs);
1889 pthread_attr_setdetachstate (&attribs, PTHREAD_CREATE_JOINABLE);
1890 result = pthread_create (&threads [i], &attribs, WorkerLoop, NULL);
1891 pthread_attr_destroy (&attribs);
1893 if (result != 0) {
1894 fprintf (stderr, "Moonlight: could not create media thread: %s (%i)\n", strerror (result), result);
1895 } else {
1896 valid [i] = true;
1901 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1902 closure->GetDescription (), closure, closure->GetMedia (), GET_OBJ_ID (closure->GetMedia ()), closure->GetDeployment (), queue ? queue->Length () : -1);
1904 if (wakeup)
1905 pthread_cond_signal (&condition);
1907 pthread_mutex_unlock (&mutex);
1910 void
1911 MediaThreadPool::WaitForCompletion (Deployment *deployment)
1913 bool waiting = false;
1914 MediaWork *current = NULL;
1916 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment);
1918 VERIFY_MAIN_THREAD;
1920 pthread_mutex_lock (&mutex);
1921 do {
1922 waiting = false;
1924 /* check if the deployment is being worked on */
1925 for (int i = 0; i < count; i++) {
1926 if (deployments [i] == deployment) {
1927 waiting = true;
1928 break;
1931 /* check if the deployment is in the queue */
1932 if (!waiting && queue != NULL) {
1933 current = (MediaWork *) queue->First ();
1934 while (current != NULL) {
1935 if (current->closure->GetDeployment () == deployment) {
1936 waiting = true;
1937 break;
1939 current = (MediaWork *) current->next;
1942 if (waiting) {
1943 timespec ts;
1944 ts.tv_sec = 0;
1945 ts.tv_nsec = 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
1946 pthread_cond_timedwait (&completed_condition, &mutex, &ts);
1948 } while (waiting);
1949 pthread_mutex_unlock (&mutex);
1952 void
1953 MediaThreadPool::RemoveWork (Media *media)
1955 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media, GET_OBJ_ID (media));
1957 List::Node *next;
1958 List::Node *first = NULL;
1959 List::Node *last = NULL;
1960 List::Node *current = NULL;
1961 int counter = 0;
1963 pthread_mutex_lock (&mutex);
1965 // create a list of nodes to delete
1966 current = queue != NULL ? queue->First () : NULL;
1967 while (current != NULL) {
1968 next = current->next; // retrieve next before Unlinking
1969 MediaWork *mw = (MediaWork *) current;
1970 if (mw->closure->GetMedia () == media) {
1971 queue->Unlink (current);
1972 if (first == NULL) {
1973 first = current;
1974 } else {
1975 last->next = current;
1977 last = current;
1978 counter++;
1979 break;
1981 current = next;
1984 pthread_mutex_unlock (&mutex);
1986 // We have to delete the list nodes with the
1987 // queue mutex unlocked, due to refcounting
1988 // (our node's (MediaWork) dtor will cause unrefs,
1989 // which may cause other dtors to be called,
1990 // eventually ending up wanting to lock the mutex
1991 // again).
1993 current = first;
1994 while (current != NULL) {
1995 next = current->next;
1996 delete current;
1997 current = next;
2001 void
2002 MediaThreadPool::WakeUp ()
2004 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2006 pthread_mutex_lock (&mutex);
2007 pthread_cond_signal (&condition);
2008 pthread_mutex_unlock (&mutex);
2011 bool
2012 MediaThreadPool::IsThreadPoolThread ()
2014 bool result = false;
2015 pthread_mutex_lock (&mutex);
2016 for (int i = 0; i < count; i++) {
2017 if (pthread_equal (pthread_self (), threads [i])) {
2018 result = true;
2019 break;
2022 pthread_mutex_unlock (&mutex);
2023 return true;
2026 void
2027 MediaThreadPool::Initialize ()
2029 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2030 VERIFY_MAIN_THREAD;
2032 shutting_down = false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2035 void
2036 MediaThreadPool::Shutdown ()
2038 List::Node *current = NULL;
2039 List::Node *next = NULL;
2041 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count);
2043 VERIFY_MAIN_THREAD;
2045 g_return_if_fail (!shutting_down);
2047 pthread_mutex_lock (&mutex);
2049 shutting_down = true;
2050 pthread_cond_broadcast (&condition);
2052 for (int i = 0; i < count; i++) {
2053 if (!valid [i])
2054 continue;
2056 pthread_mutex_unlock (&mutex);
2057 pthread_join (threads [i], NULL);
2058 pthread_mutex_lock (&mutex);
2061 if (queue != NULL) {
2062 current = queue->First ();
2063 queue->Clear (false);
2064 delete queue;
2065 queue = NULL;
2067 count = 0;
2069 pthread_mutex_unlock (&mutex);
2071 // deleting a node can have side-effects, so we first copy the list of nodes,
2072 // clear the original and loop over the copy while deleting the nodes.
2073 // this prevents any reentering issues while deleting nodes.
2074 while (current != NULL) {
2075 next = current->next;
2076 delete current;
2077 current = next;
2080 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2083 void *
2084 MediaThreadPool::WorkerLoop (void *data)
2086 MediaWork *node = NULL;
2087 Media *media = NULL;
2088 int self_index = -1;
2090 pthread_mutex_lock (&mutex);
2091 for (int i = 0; i < count; i++) {
2092 if (pthread_equal (threads [i], pthread_self ())) {
2093 self_index = i;
2094 break;
2097 pthread_mutex_unlock (&mutex);
2099 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index);
2101 g_return_val_if_fail (self_index >= 0, NULL);
2103 while (!shutting_down) {
2104 pthread_mutex_lock (&mutex);
2106 medias [self_index] = NULL;
2107 deployments [self_index] = NULL;
2108 /* if anybody was waiting for us to finish working, notify them */
2109 if (media != NULL)
2110 pthread_cond_signal (&completed_condition);
2112 media = NULL;
2113 node = (MediaWork *) (queue != NULL ? queue->First () : NULL);
2115 while (node != NULL) {
2116 media = node->closure->GetMedia ();
2118 for (int i = 0; i < count; i++) {
2119 if (medias [i] == media) {
2120 // another thread is working for the same media object.
2121 // we need to find something else to do.
2122 media = NULL;
2123 break;
2127 if (media != NULL)
2128 break;
2130 node = (MediaWork *) node->next;
2133 if (node == NULL) {
2134 pthread_cond_wait (&condition, &mutex);
2135 } else {
2136 queue->Unlink (node);
2139 if (node != NULL) {
2140 medias [self_index] = media;
2141 /* At this point the current deployment might be wrong, so avoid
2142 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2143 * here, since it might end up doing a lot of work with the mutex
2144 * locked. */
2145 deployments [self_index] = media->GetUnsafeDeployment ();
2148 pthread_mutex_unlock (&mutex);
2150 if (node == NULL)
2151 continue;
2153 media->SetCurrentDeployment (true, true);
2155 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);
2157 node->closure->Call ();
2159 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node);
2161 delete node;
2164 pthread_mutex_lock (&mutex);
2165 deployments [self_index] = NULL;
2166 medias [self_index] = NULL;
2167 /* if anybody was waiting for us to finish working, notify them */
2168 if (media != NULL)
2169 pthread_cond_signal (&completed_condition);
2170 pthread_mutex_unlock (&mutex);
2172 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index);
2174 return NULL;
2179 * MediaClosure
2182 MediaClosure::MediaClosure (Media *media, MediaCallback *callback, EventObject *context, const char *description)
2183 : EventObject (Type::MEDIACLOSURE, true)
2185 Init (media, callback, context);
2186 this->description = description;
2189 MediaClosure::MediaClosure (Type::Kind object_kind, Media *media, MediaCallback *callback, EventObject *context)
2190 : EventObject (object_kind, true)
2192 Init (media, callback, context);
2195 void
2196 MediaClosure::Init (Media *media, MediaCallback *callback, EventObject *context)
2198 result = MEDIA_INVALID;
2199 description = NULL;
2200 this->callback = callback;
2201 this->context = context;
2202 if (this->context)
2203 this->context->ref ();
2204 this->media = media;
2205 if (this->media)
2206 this->media->ref ();
2208 // put checks at the end so that fields are still initialized, since we can't abort construction.
2209 g_return_if_fail (callback != NULL);
2210 g_return_if_fail (media != NULL);
2213 void
2214 MediaClosure::Dispose ()
2216 if (context) {
2217 context->unref ();
2218 context = NULL;
2221 if (media) {
2222 media->unref ();
2223 media = NULL;
2226 callback = NULL;
2228 EventObject::Dispose ();
2231 void
2232 MediaClosure::Call ()
2234 if (callback) {
2235 result = callback (this);
2236 } else {
2237 result = MEDIA_NO_CALLBACK;
2242 * MediaDisposeObjectClosure
2244 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media *media, MediaCallback *callback, EventObject *context)
2245 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE, media, callback, context)
2249 void
2250 MediaDisposeObjectClosure::Dispose ()
2252 if (!CallExecuted ()) {
2253 // we haven't been executed. do it now.
2254 #if SANITY && DEBUG
2255 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2256 #endif
2257 Call ();
2260 MediaClosure::Dispose ();
2264 * MediaSeekClosure
2266 MediaSeekClosure::MediaSeekClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2267 : MediaClosure (Type::MEDIASEEKCLOSURE, media, callback, context)
2269 this->pts = pts;
2273 * MediaReportSeekCompletedClosure
2276 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2277 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE, media, callback, context)
2279 g_return_if_fail (context != NULL);
2281 this->pts = pts;
2284 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2288 void
2289 MediaReportSeekCompletedClosure::Dispose ()
2291 MediaClosure::Dispose ();
2295 * MediaGetFrameClosure
2298 MediaGetFrameClosure::MediaGetFrameClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, IMediaStream *stream)
2299 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2301 this->stream = NULL;
2303 g_return_if_fail (context != NULL);
2304 g_return_if_fail (stream != NULL);
2306 this->stream = stream;
2307 // this->stream->ref ();
2309 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2312 MediaGetFrameClosure::~MediaGetFrameClosure ()
2314 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2317 void
2318 MediaGetFrameClosure::Dispose ()
2320 if (stream) {
2321 // stream->unref ();
2322 stream = NULL;
2325 MediaClosure::Dispose ();
2326 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2330 * IMediaStream
2333 IMediaStream::IMediaStream (Type::Kind kind, Media *media) : IMediaObject (kind, media)
2335 context = NULL;
2337 extra_data_size = 0;
2338 extra_data = NULL;
2340 duration = 0;
2342 decoder = NULL;
2343 codec_id = 0;
2344 codec = NULL;
2346 min_padding = 0;
2347 index = -1;
2348 selected = false;
2349 input_ended = false;
2350 output_ended = false;
2352 first_pts = G_MAXUINT64; // The first pts in the stream, initialized to G_MAXUINT64
2353 last_popped_pts = G_MAXUINT64; // The pts of the last frame returned, initialized to G_MAXUINT64
2354 last_enqueued_pts = G_MAXUINT64; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2355 last_available_pts = 0; // The pts of the last available frame, initialized to 0
2358 void
2359 IMediaStream::Dispose ()
2361 if (decoder) {
2362 IMediaDecoder *d = decoder;
2363 decoder = NULL;
2364 d->Dispose ();
2365 d->unref ();
2367 g_free (extra_data);
2368 extra_data = NULL;
2369 g_free (codec);
2370 codec = NULL;
2372 ClearQueue ();
2373 IMediaObject::Dispose ();
2376 char *
2377 IMediaStream::CreateCodec (int codec_id)
2379 switch (codec_id) {
2380 case CODEC_WMV1: return g_strdup ("wmv1");
2381 case CODEC_WMV2: return g_strdup ("wmv2");
2382 case CODEC_WMV3: return g_strdup ("wmv3");
2383 case CODEC_WMVA: return g_strdup ("wmva");
2384 case CODEC_WVC1: return g_strdup ("vc1");
2385 case CODEC_RGBA: return g_strdup ("rgba");
2386 case CODEC_YV12: return g_strdup ("yv12");
2387 case CODEC_MP3: return g_strdup ("mp3");
2388 case CODEC_WMAV1: return g_strdup ("wmav1");
2389 case CODEC_WMAV2: return g_strdup ("wmav2");
2390 case CODEC_WMAV3: return g_strdup ("wmav3");
2391 case CODEC_PCM: return g_strdup ("pcm");
2392 default:
2393 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id);
2395 /* This algorithm needs testing.
2396 char *result;
2397 int size, current;
2398 int a = (codec_id & 0x000000FF);
2399 int b = (codec_id & 0x0000FF00) >> 8;
2400 int c = (codec_id & 0x00FF0000) >> 16;
2401 int d = (codec_id & 0xFF000000) >> 24;
2403 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2405 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2407 result = (char *) g_malloc (size + 1);
2408 current = 0;
2409 if (a)
2410 result [current++] = (char) a;
2411 if (b)
2412 result [current++] = (char) b;
2413 if (c)
2414 result [current++] = (char) c;
2415 if (d)
2416 result [current++] = (char) d;
2417 result [current] = 0;
2419 return g_strdup ("<unknown>");
2424 bool
2425 IMediaStream::IsQueueEmpty ()
2427 return queue.IsEmpty ();
2430 const char *
2431 IMediaStream::GetStreamTypeName ()
2433 switch (GetType ()) {
2434 case MediaTypeVideo: return "Video";
2435 case MediaTypeAudio: return "Audio";
2436 case MediaTypeMarker: return "Marker";
2437 default: return "Unknown";
2441 void
2442 IMediaStream::ReportSeekCompleted ()
2444 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2445 input_ended = false;
2446 output_ended = false;
2447 ClearQueue ();
2448 if (decoder != NULL)
2449 decoder->ReportSeekCompleted ();
2452 IMediaDemuxer *
2453 IMediaStream::GetDemuxer ()
2455 Media *media;
2456 IMediaDemuxer *result;
2458 if (IsDisposed ())
2459 return NULL;
2461 media = GetMediaReffed ();
2463 g_return_val_if_fail (media != NULL, NULL);
2465 result = media->GetDemuxer ();
2467 media->unref ();
2469 return result;
2472 IMediaDecoder *
2473 IMediaStream::GetDecoder ()
2475 return decoder;
2478 void
2479 IMediaStream::SetDecoder (IMediaDecoder *value)
2481 if (decoder)
2482 decoder->unref ();
2483 decoder = value;
2484 if (decoder)
2485 decoder->ref ();
2488 bool
2489 IMediaStream::GetOutputEnded ()
2491 return output_ended;
2494 void
2495 IMediaStream::SetOutputEnded (bool value)
2497 output_ended = value;
2500 bool
2501 IMediaStream::GetInputEnded ()
2503 return input_ended;
2506 void
2507 IMediaStream::SetInputEnded (bool value)
2509 input_ended = value;
2510 if (GetDecoder () != NULL)
2511 GetDecoder ()->ReportInputEnded ();
2514 guint64
2515 IMediaStream::GetBufferedSize ()
2517 guint64 result;
2519 queue.Lock ();
2520 if (first_pts == G_MAXUINT64 || last_enqueued_pts == G_MAXUINT64)
2521 result = 0;
2522 else if (last_popped_pts == G_MAXUINT64)
2523 result = last_enqueued_pts - first_pts;
2524 else
2525 result = last_enqueued_pts - last_popped_pts;
2526 queue.Unlock ();
2528 LOG_BUFFERING ("IMediaStream::GetBufferedSize (): id: %i, codec: %s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %llu ms, result: %llu ms\n",
2529 GET_OBJ_ID (this), codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts), MilliSeconds_FromPts (result));
2531 return result;
2535 #if DEBUG
2536 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2538 void
2539 IMediaStream::PrintBufferInformation ()
2541 guint64 buffer_size = GetBufferedSize ();
2543 printf (" <%s: ", codec);
2545 if (GetSelected ()) {
2546 printf ("size: %.4llu, first: %.4lli, last popped: %.4lli, last enq: %.4lli, frames enq: %i>",
2547 TO_MS (buffer_size), TO_MS (first_pts), TO_MS (last_popped_pts),
2548 TO_MS (last_enqueued_pts), queue.Length ());
2549 } else {
2550 printf ("(not selected) >");
2553 #endif
2555 void
2556 IMediaStream::EnqueueFrame (MediaFrame *frame)
2558 bool first = false;
2559 Media *media;
2561 g_return_if_fail (Media::InMediaThread ());
2563 media = GetMediaReffed ();
2564 g_return_if_fail (media != NULL);
2566 if (media->IsStopped ()) {
2567 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame.\n", frame);
2568 goto cleanup;
2571 queue.Lock ();
2572 if (first_pts == G_MAXUINT64)
2573 first_pts = frame->pts;
2575 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
2577 #if 0
2578 if (last_enqueued_pts > frame->pts && last_enqueued_pts != G_MAXUINT64 && frame->event != FrameEventEOF && frame->buflen > 0) {
2579 g_warning ("IMediaStream::EnqueueFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %llu ms, "
2580 "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",
2581 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2582 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), frame, frame->buflen, MilliSeconds_FromPts (frame->pts));
2584 #endif
2586 last_enqueued_pts = frame->pts;
2587 first = queue.LinkedList ()->Length () == 0;
2588 queue.LinkedList ()->Append (new StreamNode (frame));
2589 queue.Unlock ();
2591 SetLastAvailablePts (frame->pts);
2593 if (first)
2594 EmitSafe (FirstFrameEnqueuedEvent);
2596 FrameEnqueued ();
2598 cleanup:
2599 media->unref ();
2601 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",
2602 codec, first, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2603 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts - first_pts), frame, frame->buflen);
2606 MediaFrame *
2607 IMediaStream::PopFrame ()
2609 MediaFrame *result = NULL;
2610 StreamNode *node = NULL;
2612 // We use the queue lock to synchronize access to
2613 // last_popped_pts/last_enqueued_pts/first_pts
2615 queue.Lock ();
2616 node = (StreamNode *) queue.LinkedList ()->First ();
2617 if (node != NULL) {
2618 result = node->GetFrame ();
2619 result->ref ();
2620 queue.LinkedList ()->Remove (node);
2621 last_popped_pts = result->pts;
2623 queue.Unlock ();
2625 LOG_BUFFERING ("IMediaStream::PopFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %llu ms, buffer: %llu ms, frame: %p, frame->buflen: %i\n",
2626 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2627 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), result, result ? result->buflen : 0);
2629 if (!input_ended && !output_ended && result != NULL) {
2630 IMediaDemuxer *demuxer = GetDemuxer ();
2631 if (demuxer != NULL)
2632 demuxer->FillBuffers ();
2635 return result;
2638 void
2639 IMediaStream::ClearQueue ()
2641 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2642 queue.Lock ();
2643 queue.LinkedList ()->Clear (true);
2644 first_pts = G_MAXUINT64;
2645 last_popped_pts = G_MAXUINT64;
2646 last_enqueued_pts = G_MAXUINT64;
2647 queue.Unlock ();
2650 void
2651 IMediaStream::SetSelected (bool value)
2653 Media *media = GetMediaReffed ();
2655 selected = value;
2656 if (media) {
2657 if (media->GetDemuxer ())
2658 media->GetDemuxer ()->UpdateSelected (this);
2659 media->unref ();
2664 * IMediaStream.StreamNode
2667 IMediaStream::StreamNode::StreamNode (MediaFrame *f)
2669 frame = f;
2670 frame->ref ();
2673 IMediaStream::StreamNode::~StreamNode ()
2675 frame->unref ();
2676 frame = NULL;
2679 * IMediaDemuxer
2682 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media, IMediaSource *source) : IMediaObject (kind, media)
2684 this->source = source;
2685 this->source->ref ();
2686 stream_count = 0;
2687 streams = NULL;
2688 opened = false;
2689 opening = false;
2690 pending_stream = NULL;
2691 pending_fill_buffers = false;
2694 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media)
2695 : IMediaObject (kind, media)
2697 source = NULL;
2698 stream_count = 0;
2699 streams = NULL;
2700 opened = false;
2701 opening = false;
2702 pending_stream = NULL;
2705 void
2706 IMediaDemuxer::Dispose ()
2708 if (streams != NULL) {
2709 IMediaStream **tmp = streams;
2710 int stream_count = this->stream_count;
2711 streams = NULL;
2712 for (int i = 0; i < stream_count; i++) {
2713 tmp [i]->Dispose ();
2714 tmp [i]->unref ();
2716 g_free (tmp);
2718 if (source) {
2719 source->unref ();
2720 source = NULL;
2722 if (pending_stream != NULL) {
2723 pending_stream->unref ();
2724 pending_stream = NULL;
2726 opened = false;
2727 IMediaObject::Dispose ();
2730 MediaResult
2731 IMediaDemuxer::OpenCallback (MediaClosure *closure)
2733 IMediaDemuxer *demuxer;
2735 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure);
2737 demuxer = (IMediaDemuxer *) closure->GetContext ();
2738 demuxer->OpenDemuxerAsync ();
2740 return MEDIA_SUCCESS;
2743 void
2744 IMediaDemuxer::EnqueueOpen ()
2746 MediaClosure *closure;
2747 Media *media = GetMediaReffed ();
2749 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2751 closure = new MediaClosure (media, OpenCallback, this, "IMediaDemuxer::OpenCallback");
2752 media->EnqueueWork (closure, false);
2753 closure->unref ();
2754 media->unref ();
2757 void
2758 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2760 Media *media = GetMediaReffed ();
2762 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media);
2764 opened = true;
2765 opening = false;
2767 // Media might be null if we got disposed for some reason.
2768 if (!media)
2769 return;
2771 media->ReportOpenDemuxerCompleted ();
2772 media->unref ();
2775 void
2776 IMediaDemuxer::ReportGetFrameProgress (double progress)
2778 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress);
2781 void
2782 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream *stream)
2784 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream);
2787 void
2788 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind, gint64 value)
2790 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %lld)\n", kind, value);
2793 void
2794 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame *frame)
2796 Media *media = GetMediaReffed ();
2798 g_return_if_fail (media != NULL);
2800 if (media->IsStopped ())
2801 goto cleanup; /* if we're stopped, just drop what we're doing. */
2803 g_return_if_fail (frame == NULL || (frame != NULL && frame->stream != NULL));
2804 g_return_if_fail (pending_stream != NULL); // we must be waiting for a frame ...
2806 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p) %i %s %llu ms\n", frame, GET_OBJ_ID (this), frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : (guint64) -1);
2808 if (frame == NULL) {
2809 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame, pending_stream->GetStreamTypeName ());
2810 // No more data for this stream
2811 pending_stream->SetInputEnded (true);
2812 } else if (!frame->stream->IsDisposed ()) {
2813 IMediaDecoder *decoder = frame->stream->GetDecoder ();
2814 if (decoder != NULL)
2815 decoder->DecodeFrameAsync (frame, true /* always enqueue */);
2818 pending_stream->unref ();
2819 pending_stream = NULL; // not waiting for anything more
2821 // enqueue some more
2822 FillBuffers ();
2824 cleanup:
2825 if (media)
2826 media->unref ();
2829 MediaResult
2830 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure *c)
2832 MediaReportSeekCompletedClosure *closure = (MediaReportSeekCompletedClosure *) c;
2833 IMediaDemuxer *demuxer;
2835 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
2836 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
2838 demuxer = (IMediaDemuxer *) closure->GetContext ();
2839 demuxer->ReportSeekCompleted (closure->GetPts ());
2841 return MEDIA_SUCCESS;
2844 void
2845 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts)
2847 Media *media = GetMediaReffed ();
2848 MediaClosure *closure = new MediaReportSeekCompletedClosure (media, ReportSeekCompletedCallback, this, pts);
2849 media->EnqueueWork (closure);
2850 closure->unref ();
2851 media->unref ();
2854 void
2855 IMediaDemuxer::ReportSeekCompleted (guint64 pts)
2857 Media *media;
2859 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%llu)\n", pts);
2861 if (!Media::InMediaThread ()) {
2862 EnqueueReportSeekCompleted (pts);
2863 return;
2866 media = GetMediaReffed ();
2868 g_return_if_fail (media != NULL);
2871 for (int i = 0; i < GetStreamCount (); i++) {
2872 IMediaStream *stream = GetStream (i);
2874 if (stream == NULL)
2875 continue;
2877 stream->ReportSeekCompleted ();
2880 media->ReportSeekCompleted (pts);
2881 media->unref ();
2883 if (pending_stream) {
2884 pending_stream->unref ();
2885 pending_stream = NULL;
2888 pending_fill_buffers = false;
2889 FillBuffers ();
2891 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%llu) [Done]\n", pts);
2894 void
2895 IMediaDemuxer::OpenDemuxerAsync ()
2897 g_return_if_fail (opened == false);
2899 opening = true;
2900 opened = false;
2901 OpenDemuxerAsyncInternal ();
2904 MediaResult
2905 IMediaDemuxer::GetFrameCallback (MediaClosure *c)
2907 MediaGetFrameClosure *closure = (MediaGetFrameClosure *) c;
2908 IMediaDemuxer *demuxer;
2910 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
2911 g_return_val_if_fail (closure->GetStream () != NULL, MEDIA_FAIL);
2912 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
2914 demuxer = (IMediaDemuxer *) closure->GetContext ();
2915 demuxer->GetFrameAsync (closure->GetStream ());
2917 return MEDIA_SUCCESS;
2920 void
2921 IMediaDemuxer::EnqueueGetFrame (IMediaStream *stream)
2923 g_return_if_fail (pending_stream == NULL); // we can't be waiting for another frame.
2925 Media *media = GetMediaReffed ();
2926 MediaClosure *closure = new MediaGetFrameClosure (media, GetFrameCallback, this, stream);
2927 media->EnqueueWork (closure);
2928 closure->unref ();
2929 media->unref ();
2932 void
2933 IMediaDemuxer::GetFrameAsync (IMediaStream *stream)
2935 Media *media;
2937 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream, stream->GetStreamTypeName (), Media::InMediaThread ());
2939 if (!Media::InMediaThread ()) {
2940 EnqueueGetFrame (stream);
2941 return;
2944 media = GetMediaReffed ();
2946 g_return_if_fail (media != NULL);
2948 if (media->IsStopped ())
2949 goto cleanup;
2951 if (stream != NULL && pending_stream == NULL) {
2952 pending_stream = stream;
2953 pending_stream->ref ();
2954 GetFrameAsyncInternal (stream);
2957 cleanup:
2958 media->unref ();
2961 MediaResult
2962 IMediaDemuxer::SeekCallback (MediaClosure *closure)
2964 MediaSeekClosure *seek = (MediaSeekClosure *) closure;
2965 seek->GetDemuxer ()->SeekAsyncInternal (seek->GetPts ());
2966 return MEDIA_SUCCESS;
2969 void
2970 IMediaDemuxer::EnqueueSeek (guint64 pts)
2972 Media *media = GetMediaReffed ();
2973 MediaSeekClosure *closure;
2975 g_return_if_fail (media != NULL);
2977 closure = new MediaSeekClosure (media, SeekCallback, this, pts);
2978 media->EnqueueWork (closure, true);
2979 closure->unref ();
2980 media->unref ();
2983 void
2984 IMediaDemuxer::SeekAsync (guint64 pts)
2986 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%llu)\n", pts);
2988 if (IsDisposed ())
2989 return;
2991 // we need to run this on the media thread, not the main thread.
2992 EnqueueSeek (pts);
2995 void
2996 IMediaDemuxer::ClearBuffers ()
2998 pending_fill_buffers = false;
3000 /* Clear all the buffer queues */
3001 for (int i = 0; i < GetStreamCount (); i++) {
3002 IMediaStream *stream = GetStream (i);
3004 if (stream == NULL)
3005 continue;
3007 stream->ClearQueue ();
3011 MediaResult
3012 IMediaDemuxer::FillBuffersCallback (MediaClosure *closure)
3014 IMediaDemuxer *demuxer = (IMediaDemuxer *) closure->GetContext ();
3015 demuxer->FillBuffersInternal ();
3016 return MEDIA_SUCCESS;
3019 void
3020 IMediaDemuxer::FillBuffers ()
3022 Media *media = NULL;
3023 MediaClosure *closure;
3024 bool enqueue = true;
3026 mutex.Lock ();
3027 if (pending_fill_buffers) {
3028 // there's already a FillBuffers request enqueued
3029 enqueue = false;
3030 } else {
3031 media = GetMediaReffed ();
3032 if (media == NULL) {
3033 enqueue = false;
3034 } else {
3035 enqueue = true;
3036 pending_fill_buffers = true;
3039 mutex.Unlock ();
3041 if (enqueue) {
3042 closure = new MediaClosure (media, FillBuffersCallback, this, "IMediaDemuxer::FillBuffersCallback");
3043 media->EnqueueWork (closure);
3044 closure->unref ();
3047 if (media != NULL)
3048 media->unref ();
3051 void
3052 IMediaDemuxer::FillBuffersInternal ()
3054 IMediaStream *stream;
3055 IMediaStream *request_stream = NULL;
3056 guint64 min_buffered_size = G_MAXUINT64;
3057 MediaResult result = MEDIA_SUCCESS;
3058 Media *media = GetMediaReffed ();
3059 guint64 buffering_time = 0;
3060 guint64 buffered_size = 0;
3061 int ended = 0;
3062 int media_streams = 0;
3064 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (), %i %s buffering time: %llu = %llu 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");
3066 mutex.Lock ();
3067 pending_fill_buffers = false;
3068 mutex.Unlock ();
3070 if (IsDisposed ())
3071 goto cleanup;
3073 // If we're waiting for something, there's nothing to do here.
3074 if (pending_stream != NULL)
3075 goto cleanup;
3077 // Find the stream with the smallest buffered size, and request a frame from that stream.
3078 g_return_if_fail (media != NULL);
3080 // If we're stopped there is nothing to do here.
3081 if (media->IsStopped ()) {
3082 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped\n");
3083 goto cleanup;
3086 buffering_time = media->GetBufferingTime ();
3088 if (buffering_time == 0) {
3089 // Play as soon as possible.
3090 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3091 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3092 // all streams have 1 frame queued.
3093 buffering_time = 1;
3096 for (int i = 0; i < GetStreamCount (); i++) {
3097 IMediaDecoder *decoder = NULL;
3099 stream = GetStream (i);
3100 if (!stream->GetSelected ())
3101 continue;
3103 if (stream->GetType () != MediaTypeVideo &&
3104 stream->GetType () != MediaTypeAudio)
3105 continue;
3107 media_streams++;
3108 if (stream->GetOutputEnded ()) {
3109 ended++;
3110 continue; // this stream has ended.
3113 decoder = stream->GetDecoder ();
3114 if (decoder == NULL) {
3115 fprintf (stderr, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream->GetStreamTypeName (), GET_OBJ_ID (stream), stream->GetRefCount ());
3116 continue; // no decoder??
3119 buffered_size = stream->GetBufferedSize ();
3120 min_buffered_size = MIN (min_buffered_size, buffered_size);
3122 if (buffered_size >= buffering_time)
3123 continue; // this stream has enough data buffered.
3125 if (!decoder->IsDecoderQueueEmpty ())
3126 continue; // this stream is waiting for data to be decoded.
3128 if (buffered_size <= min_buffered_size)
3129 request_stream = stream;
3131 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: %llu ms\n",
3132 stream->codec, GET_OBJ_ID (stream), result, MilliSeconds_FromPts (buffered_size), MilliSeconds_FromPts (buffering_time), MilliSeconds_FromPts (stream->GetLastPoppedPts ()));
3135 if (request_stream != NULL) {
3136 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));
3137 GetFrameAsync (request_stream);
3140 if (media_streams > 0) {
3141 if (ended == media_streams) {
3142 media->ReportBufferingProgress (1.0);
3143 } else {
3144 if (min_buffered_size > 0 && buffering_time > 0) {
3145 double progress = ((double) min_buffered_size / (double) buffering_time);
3146 media->ReportBufferingProgress (progress);
3151 cleanup:
3152 if (media)
3153 media->unref ();
3155 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT " ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3158 guint64
3159 IMediaDemuxer::GetBufferedSize ()
3161 guint64 result = G_MAXUINT64;
3162 IMediaStream *stream;
3164 for (int i = 0; i < GetStreamCount (); i++) {
3165 stream = GetStream (i);
3166 if (!stream->GetSelected ())
3167 continue;
3169 if (stream->GetType () != MediaTypeVideo && stream->GetType () != MediaTypeAudio)
3170 continue;
3172 result = MIN (result, stream->GetBufferedSize ());
3175 return result;
3178 guint64
3179 IMediaDemuxer::GetLastAvailablePts ()
3181 guint64 result = G_MAXUINT64;
3182 IMediaStream *stream;
3184 for (int i = 0; i < GetStreamCount (); i++) {
3185 stream = GetStream (i);
3187 if (stream == NULL || !stream->GetSelected ())
3188 continue;
3190 result = MIN (result, stream->GetLastAvailablePts ());
3193 if (result == G_MAXUINT64)
3194 result = 0;
3196 return result;
3199 #if DEBUG
3200 void
3201 IMediaDemuxer::PrintBufferInformation ()
3203 printf ("Buffer: %lld", MilliSeconds_FromPts (GetBufferedSize ()));
3204 for (int i = 0; i < GetStreamCount (); i++) {
3205 GetStream (i)->PrintBufferInformation ();
3207 printf ("\n");
3209 #endif
3211 guint64
3212 IMediaDemuxer::GetDuration ()
3214 guint64 result = 0;
3215 for (int i = 0; i < GetStreamCount (); i++)
3216 result = MAX (result, GetStream (i)->duration);
3217 return result;
3220 IMediaStream*
3221 IMediaDemuxer::GetStream (int index)
3223 return (index < 0 || index >= stream_count) ? NULL : streams [index];
3227 * MediaFrame
3230 MediaFrame::MediaFrame (IMediaStream *stream)
3231 : EventObject (Type::MEDIAFRAME, true)
3233 Initialize ();
3235 g_return_if_fail (stream != NULL);
3237 this->stream = stream;
3238 this->stream->ref ();
3241 MediaFrame::MediaFrame (IMediaStream *stream, guint8 *buffer, guint32 buflen, guint64 pts, bool keyframe)
3242 : EventObject (Type::MEDIAFRAME, true)
3244 Initialize ();
3246 g_return_if_fail (stream != NULL);
3248 this->stream = stream;
3249 this->stream->ref ();
3250 this->buffer = buffer;
3251 this->buflen = buflen;
3252 this->pts = pts;
3254 #if 0
3255 if (buflen > 4 && false) {
3256 printf ("MediaFrame::MediaFrame () %s buffer: ", stream->GetStreamTypeName ());
3257 for (int i = 0; i < 4; i++)
3258 printf (" 0x%x", buffer [i]);
3259 printf ("\n");
3261 #endif
3263 if (keyframe)
3264 AddState (MediaFrameKeyFrame);
3267 void
3268 MediaFrame::Initialize ()
3270 decoder_specific_data = NULL;
3271 stream = NULL;
3272 marker = NULL;
3274 duration = 0;
3275 pts = 0;
3277 buffer = NULL;
3278 buflen = 0;
3279 state = 0;
3280 event = 0;
3282 for (int i = 0; i < 4; i++) {
3283 data_stride[i] = 0;
3284 srcStride[i] = 0;
3287 srcSlideY = 0;
3288 srcSlideH = 0;
3289 width = 0;
3290 height = 0;
3293 MediaFrame::~MediaFrame ()
3297 void
3298 MediaFrame::Dispose ()
3300 IMediaDecoder *decoder;
3302 #if SANITY
3303 // We can be called either on the main thread just before destruction
3304 // (in which case there are no races since the code which unreffed us
3305 // is the only code which knows about us), or at any time from the
3306 // media thread.
3308 if (GetRefCount () != 0 && stream != NULL) {
3309 if (!Media::InMediaThread ()) {
3310 // if refcount != 0 we're not being called just before destruction, in which case we should
3311 // only be on the media thread.
3312 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3315 #endif
3317 if (decoder_specific_data != NULL && stream != NULL) {
3318 decoder = stream->GetDecoder ();
3319 if (decoder != NULL)
3320 decoder->Cleanup (this);
3322 g_free (buffer);
3323 buffer = NULL;
3324 if (marker) {
3325 marker->unref ();
3326 marker = NULL;
3328 if (stream) {
3329 stream->unref ();
3330 stream = NULL;
3333 EventObject::Dispose ();
3336 void
3337 MediaFrame::SetSrcSlideY (int value)
3339 srcSlideY = value;
3342 void
3343 MediaFrame::SetSrcSlideH (int value)
3345 srcSlideH = value;
3348 void
3349 MediaFrame::SetSrcStride (int a, int b, int c, int d)
3351 srcStride [0] = a;
3352 srcStride [1] = b;
3353 srcStride [2] = c;
3354 srcStride [3] = d;
3357 void
3358 MediaFrame::SetDataStride (guint8* a, guint8* b, guint8* c, guint8* d)
3360 data_stride [0] = a;
3361 data_stride [1] = b;
3362 data_stride [2] = c;
3363 data_stride [3] = d;
3367 * IMediaObject.EventData
3370 IMediaObject::EventData::EventData (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3372 this->event_id = event_id;
3373 this->handler = handler;
3374 this->context = context;
3375 this->context->ref ();
3376 this->invoke_on_main_thread = invoke_on_main_thread;
3379 IMediaObject::EventData::~EventData ()
3381 context->unref ();
3382 context = NULL;
3386 * IMediaObject.EmitData
3389 IMediaObject::EmitData::EmitData (int event_id, EventHandler handler, EventObject *context, EventArgs *args)
3391 this->event_id = event_id;
3392 this->handler = handler;
3393 this->context = context;
3394 this->context->ref ();
3395 this->args = args;
3396 if (this->args)
3397 this->args->ref ();
3400 IMediaObject::EmitData::~EmitData ()
3402 context->unref ();
3403 context = NULL;
3404 if (args) {
3405 args->unref ();
3406 args = NULL;
3411 * IMediaObject
3414 IMediaObject::IMediaObject (Type::Kind kind, Media *media)
3415 : EventObject (kind, true)
3417 this->media = media;
3418 if (this->media)
3419 this->media->ref ();
3420 g_return_if_fail (media != NULL);
3421 events = NULL;
3422 emit_on_main_thread = NULL;
3425 void
3426 IMediaObject::Dispose ()
3429 #if SANITY
3430 // We can be called either on the main thread just before destruction
3431 // (in which case there are no races since the code which unreffed us
3432 // is the only code which knows about us), or at any time from the
3433 // media thread.
3434 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3435 // if refcount != 0 we're not being called just before destruction, in which case we should
3436 // only be on the media thread.
3437 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3439 #endif
3441 media_mutex.Lock ();
3442 if (media) {
3443 media->unref ();
3444 media = NULL;
3446 media_mutex.Unlock ();
3448 event_mutex.Lock ();
3449 delete events;
3450 events = NULL;
3451 if (emit_on_main_thread != NULL) {
3452 delete emit_on_main_thread;
3453 emit_on_main_thread = NULL;
3455 event_mutex.Unlock ();
3457 EventObject::Dispose ();
3460 void
3461 IMediaObject::AddSafeHandler (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3463 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id, handler, context, invoke_on_main_thread);
3464 EventData *ed;
3466 if (!IsDisposed ()) {
3467 ed = new EventData (event_id, handler, context, invoke_on_main_thread);
3468 event_mutex.Lock ();
3469 if (events == NULL)
3470 events = new List ();
3471 events->Append (ed);
3472 event_mutex.Unlock ();
3476 void
3477 IMediaObject::RemoveSafeHandlers (EventObject *context)
3479 EventData *ed;
3480 EventData *next;
3482 event_mutex.Lock ();
3483 if (events != NULL) {
3484 ed = (EventData *) events->First ();
3485 while (ed != NULL) {
3486 next = (EventData *) ed->next;
3487 if (ed->context == context)
3488 events->Remove (ed);
3489 ed = next;
3492 event_mutex.Unlock ();
3495 void
3496 IMediaObject::EmitSafe (int event_id, EventArgs *args)
3498 List *emits = NULL; // The events to emit on this thread.
3499 EventData *ed;
3500 EmitData *emit;
3502 if (events == NULL)
3503 goto cleanup;
3505 // Create a list of all the events to emit
3506 // don't keep the lock while emitting.
3507 event_mutex.Lock ();
3508 if (events != NULL) {
3509 ed = (EventData *) events->First ();
3510 while (ed != NULL) {
3511 if (ed->event_id == event_id) {
3512 emit = new EmitData (event_id, ed->handler, ed->context, args);
3513 if (ed->invoke_on_main_thread) {
3514 if (emit_on_main_thread == NULL)
3515 emit_on_main_thread = new List ();
3516 emit_on_main_thread->Append (emit);
3517 } else {
3518 if (emits == NULL)
3519 emits = new List ();
3520 emits->Append (emit);
3523 ed = (EventData *) ed->next;
3526 event_mutex.Unlock ();
3528 // emit the events to be emitted on this thread
3529 EmitList (emits);
3531 if (Surface::InMainThread ()) {
3532 // if we're already on the main thread,
3533 // we can the events to be emitted
3534 // on the main thread
3535 List *tmp;
3536 event_mutex.Lock ();
3537 tmp = emit_on_main_thread;
3538 emit_on_main_thread = NULL;
3539 event_mutex.Unlock ();
3540 EmitList (tmp);
3541 } else {
3542 AddTickCallSafe (EmitListCallback);
3545 cleanup:
3546 if (args)
3547 args->unref ();
3550 void
3551 IMediaObject::EmitListMain ()
3553 VERIFY_MAIN_THREAD;
3555 List *list;
3556 event_mutex.Lock ();
3557 list = emit_on_main_thread;
3558 emit_on_main_thread = NULL;
3559 event_mutex.Unlock ();
3560 EmitList (list);
3563 void
3564 IMediaObject::EmitListCallback (EventObject *obj)
3566 IMediaObject *media_obj = (IMediaObject *) obj;
3567 media_obj->EmitListMain ();
3570 void
3571 IMediaObject::EmitList (List *list)
3573 EmitData *emit;
3575 if (list == NULL)
3576 return;
3578 emit = (EmitData *) list->First ();
3579 while (emit != NULL) {
3580 emit->handler (this, emit->args, emit->context);
3581 emit = (EmitData *) emit->next;
3584 delete list;
3587 Media *
3588 IMediaObject::GetMediaReffed ()
3590 Media *result;
3591 media_mutex.Lock ();
3592 result = media;
3593 if (result)
3594 result->ref ();
3595 media_mutex.Unlock ();
3596 return result;
3599 void
3600 IMediaObject::ReportErrorOccurred (char const *message)
3602 g_return_if_fail (media != NULL);
3604 media->ReportErrorOccurred (message);
3607 void
3608 IMediaObject::ReportErrorOccurred (MediaResult result)
3610 g_return_if_fail (media != NULL);
3612 media->ReportErrorOccurred (result);
3615 void
3616 IMediaObject::ReportErrorOccurred (ErrorEventArgs *args)
3618 g_return_if_fail (media != NULL);
3620 media->ReportErrorOccurred (args);
3623 void
3624 IMediaObject::SetMedia (Media *value)
3626 media_mutex.Lock ();
3627 if (media)
3628 media->unref ();
3629 media = value;
3630 if (media)
3631 media->ref ();
3632 media_mutex.Unlock ();
3636 * IMediaSource
3639 IMediaSource::IMediaSource (Type::Kind kind, Media *media)
3640 : IMediaObject (kind, media)
3642 pthread_mutexattr_t attribs;
3643 pthread_mutexattr_init (&attribs);
3644 pthread_mutexattr_settype (&attribs, PTHREAD_MUTEX_RECURSIVE);
3645 pthread_mutex_init (&mutex, &attribs);
3646 pthread_mutexattr_destroy (&attribs);
3648 pthread_cond_init (&condition, NULL);
3651 IMediaSource::~IMediaSource ()
3653 pthread_mutex_destroy (&mutex);
3654 pthread_cond_destroy (&condition);
3657 void
3658 IMediaSource::Dispose ()
3660 IMediaObject::Dispose ();
3663 void
3664 IMediaSource::Lock ()
3666 pthread_mutex_lock (&mutex);
3669 void
3670 IMediaSource::Unlock ()
3672 pthread_mutex_unlock (&mutex);
3675 gint32
3676 IMediaSource::ReadSome (void *buf, guint32 n)
3678 gint32 result;
3680 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf, n);
3682 Lock ();
3684 result = ReadInternal (buf, n);
3686 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %lld\n", GET_OBJ_ID (this), buf, n, result, GetPosition ());
3688 Unlock ();
3690 return result;
3693 bool
3694 IMediaSource::ReadAll (void *buf, guint32 n)
3696 gint32 read;
3697 gint64 prev = GetPosition ();
3698 gint64 avail = GetLastAvailablePosition ();
3700 //printf ("IMediaSource::ReadAll (%p, %u), position: %lld\n", buf, n, prev);
3702 read = ReadSome (buf, n);
3704 if ((gint64) read != (gint64) n) {
3705 FileSource *fs = NULL;
3707 if (GetType () == MediaSourceTypeFile)
3708 fs = (FileSource *) this;
3709 g_warning ("IMediaSource::ReadInternal (%i): Read failed, read %i bytes. available size: %lld, size: %lld, pos: %lld, prev pos: %lld, position not available: %lld, feof: %i, ferror: %i, strerror: %s\n",
3710 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>");
3711 print_stack_trace ();
3714 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf, n, read);
3716 return (gint64) read == (gint64) n;
3719 bool
3720 IMediaSource::Peek (void *buf, guint32 n)
3722 bool result;
3723 gint64 read;
3725 Lock ();
3727 read = PeekInternal (buf, n);
3728 result = read == (gint64) n;
3730 Unlock ();
3732 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %lld bytes.\n", buf, n, result, read);
3734 return result;
3737 bool
3738 IMediaSource::Seek (gint64 offset, int mode)
3740 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%lld, %d = %s)\n",
3741 GET_OBJ_ID (this), ToString (), offset, mode, mode == SEEK_SET ? "SEEK_SET"
3742 : (mode == SEEK_CUR ? "SEEK_CUR" : (mode == SEEK_END ? "SEEK_END" : "<invalid value>")));
3744 bool result;
3745 Lock ();
3746 result = SeekInternal (offset, mode);
3747 Unlock ();
3748 return result;
3751 bool
3752 IMediaSource::IsPositionAvailable (gint64 position, bool *eof)
3754 gint64 available = GetLastAvailablePosition ();
3755 gint64 size = GetSize ();
3757 *eof = false;
3759 if (size != -1 && size < position) {
3760 // Size is known and smaller than the requested position
3761 *eof = true;
3762 return false;
3765 if (available != -1 && available < position) {
3766 // Not everything is available and the available position is smaller than the requested position
3767 *eof = false;
3768 return false;
3771 if (size == -1 && available == -1) {
3772 // Size is not known, but everything is available??
3773 // This is probably due to a bug in the derived *Source class
3774 *eof = false;
3775 fprintf (stderr, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
3776 return false;
3779 return true;
3782 gint64
3783 IMediaSource::GetLastAvailablePosition ()
3785 gint64 result;
3786 Lock ();
3787 result = GetLastAvailablePositionInternal ();
3788 Unlock ();
3789 return result;
3792 gint64
3793 IMediaSource::GetPositionInternal ()
3795 // This method should be overridden (or never called for the classes which doesn't override it).
3796 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3797 print_stack_trace ();
3799 return -1;
3801 bool
3802 IMediaSource::SeekInternal (gint64 offset, int mode)
3804 g_warning ("IMediaSource (%s)::SeekInternal (%lld, %i): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), offset, mode);
3805 print_stack_trace ();
3807 return false;
3810 gint32
3811 IMediaSource::ReadInternal (void *buffer, guint32 n)
3813 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);
3814 print_stack_trace ();
3816 return 0;
3819 gint32
3820 IMediaSource::PeekInternal (void *buffer, guint32 n)
3822 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);
3823 print_stack_trace ();
3825 return 0;
3828 gint64
3829 IMediaSource::GetSizeInternal ()
3831 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3832 print_stack_trace ();
3834 return 0;
3837 gint64
3838 IMediaSource::GetPosition ()
3840 gint64 result;
3841 Lock ();
3842 result = GetPositionInternal ();
3843 Unlock ();
3844 return result;
3847 gint64
3848 IMediaSource::GetSize ()
3850 gint64 result;
3851 Lock ();
3852 result = GetSizeInternal ();
3853 Unlock ();
3854 return result;
3858 * IMediaDemuxer
3861 void
3862 IMediaDemuxer::SetStreams (IMediaStream** streams, int count)
3864 this->streams = streams;
3865 this->stream_count = count;
3867 for (int i = 0; i < count; i++)
3868 this->streams [i]->ref ();
3871 gint32
3872 IMediaDemuxer::AddStream (IMediaStream *stream)
3874 g_return_val_if_fail (stream != NULL, -1);
3876 stream_count++;
3877 streams = (IMediaStream **) g_realloc (streams, stream_count * sizeof (IMediaStream *));
3878 streams [stream_count - 1] = stream;
3879 stream->ref ();
3881 return stream_count - 1;
3885 * IMediaDecoder
3888 IMediaDecoder::IMediaDecoder (Type::Kind kind, Media *media, IMediaStream *stream) : IMediaObject (kind, media)
3890 this->stream = NULL;
3892 g_return_if_fail (stream != NULL);
3894 this->stream = stream;
3895 this->stream->ref ();
3897 opening = false;
3898 opened = false;
3899 input_ended = false;
3902 void
3903 IMediaDecoder::Dispose ()
3905 if (stream != NULL) {
3906 IMediaStream *s = stream;
3907 stream = NULL;
3908 s->Dispose ();
3909 s->unref ();
3910 s = NULL;
3913 queue.Clear (true);
3915 IMediaObject::Dispose ();
3918 void
3919 IMediaDecoder::ReportSeekCompleted ()
3921 queue.Clear (true);
3922 input_ended = false;
3923 CleanState ();
3926 void
3927 IMediaDecoder::ReportInputEnded ()
3929 input_ended = true;
3930 if (IsDecoderQueueEmpty ()) {
3931 InputEnded ();
3935 void
3936 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame *frame)
3938 IMediaDemuxer *demuxer;
3939 IMediaStream *stream;
3940 Media *media = NULL;
3942 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %llu ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
3944 g_return_if_fail (frame != NULL);
3946 media = GetMediaReffed ();
3947 g_return_if_fail (media != NULL);
3949 stream = frame->stream;
3950 if (stream == NULL)
3951 goto cleanup;
3953 frame->stream->EnqueueFrame (frame);
3955 demuxer = stream->GetDemuxer ();
3956 if (demuxer != NULL)
3957 demuxer->FillBuffers ();
3959 if (input_ended && IsDecoderQueueEmpty ())
3960 InputEnded ();
3962 cleanup:
3963 if (media)
3964 media->unref ();
3967 MediaResult
3968 IMediaDecoder::DecodeFrameCallback (MediaClosure *closure)
3971 IMediaDecoder *decoder = (IMediaDecoder *) closure->GetContext ();
3972 IMediaDecoder::FrameNode *node = (IMediaDecoder::FrameNode *) decoder->queue.Pop ();
3974 if (node != NULL) {
3975 decoder->DecodeFrameAsync (node->frame, false);
3976 delete node;
3979 return MEDIA_SUCCESS;
3982 void
3983 IMediaDecoder::DecodeFrameAsync (MediaFrame *frame, bool enqueue_always)
3985 Media *media;
3987 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame, (frame && frame->stream) ? frame->stream->GetStreamTypeName () : NULL);
3989 if (IsDisposed ())
3990 return;
3992 g_return_if_fail (frame != NULL);
3994 media = GetMediaReffed ();
3996 g_return_if_fail (media != NULL);
3998 if (enqueue_always || !Media::InMediaThread ()) {
3999 MediaClosure *closure = new MediaClosure (media, DecodeFrameCallback, this, "IMediaDecoder::DecodeFrameCallback");
4000 queue.Push (new FrameNode (frame));
4001 media->EnqueueWork (closure);
4002 closure->unref ();
4003 goto cleanup;
4006 if (media->IsStopped ())
4007 goto cleanup;
4009 DecodeFrameAsyncInternal (frame);
4011 cleanup:
4012 media->unref ();
4015 void
4016 IMediaDecoder::OpenDecoderAsync ()
4018 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4020 g_return_if_fail (opening == false);
4021 g_return_if_fail (opened == false);
4023 opening = true;
4024 OpenDecoderAsyncInternal ();
4027 void
4028 IMediaDecoder::ReportOpenDecoderCompleted ()
4030 Media *media = GetMediaReffed ();
4032 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4034 opening = false;
4035 opened = true;
4037 g_return_if_fail (media != NULL);
4039 media->ReportOpenDecoderCompleted (this);
4040 media->unref ();
4044 * IImageConverter
4047 IImageConverter::IImageConverter (Type::Kind kind, Media *media, VideoStream *stream) : IMediaObject (kind, media)
4049 output_format = MoonPixelFormatNone;
4050 input_format = MoonPixelFormatNone;
4051 this->stream = stream;
4055 * VideoStream
4058 VideoStream::VideoStream (Media *media) : IMediaStream (Type::VIDEOSTREAM, media)
4060 converter = NULL;
4061 bits_per_sample = 0;
4062 pts_per_frame = 0;
4063 initial_pts = 0;
4064 height = 0;
4065 width = 0;
4068 VideoStream::VideoStream (Media *media, int codec_id, guint32 width, guint32 height, guint64 duration, gpointer extra_data, guint32 extra_data_size)
4069 : IMediaStream (Type::VIDEOSTREAM, media)
4071 converter = NULL;
4072 bits_per_sample = 0;
4073 pts_per_frame = 0;
4074 initial_pts = 0;
4075 this->height = height;
4076 this->width = width;
4077 this->duration = duration;
4078 this->codec_id = codec_id;
4079 this->codec = CreateCodec (codec_id);
4080 this->extra_data = extra_data;
4081 this->extra_data_size = extra_data_size;
4084 VideoStream::~VideoStream ()
4088 void
4089 VideoStream::Dispose ()
4091 if (converter) {
4092 converter->Dispose ();
4093 converter->unref ();
4094 converter = NULL;
4096 IMediaStream::Dispose ();
4100 * MediaMarkerFoundClosure
4103 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media *media, MediaCallback *callback, MediaElement *context)
4104 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE, media, callback, context)
4106 marker = NULL;
4109 void
4110 MediaMarkerFoundClosure::Dispose ()
4112 if (marker) {
4113 marker->unref ();
4114 marker = NULL;
4116 MediaClosure::Dispose ();
4119 void
4120 MediaMarkerFoundClosure::SetMarker (MediaMarker *marker)
4122 if (this->marker)
4123 this->marker->unref ();
4124 this->marker = marker;
4125 if (this->marker)
4126 this->marker->ref ();
4130 * MediaMarker
4133 MediaMarker::MediaMarker (const char *type, const char *text, guint64 pts)
4134 : EventObject (Type::MEDIAMARKER)
4136 this->type = g_strdup (type);
4137 this->text = g_strdup (text);
4138 this->pts = pts;
4141 MediaMarker::~MediaMarker ()
4143 g_free (type);
4144 g_free (text);
4148 * MarkerStream
4151 MarkerStream::MarkerStream (Media *media) : IMediaStream (Type::MARKERSTREAM, media)
4153 closure = NULL;
4156 void
4157 MarkerStream::Dispose ()
4159 if (closure) {
4160 closure->unref ();
4161 closure = NULL;
4164 IMediaStream::Dispose ();
4167 void
4168 MarkerStream::MarkerFound (MediaFrame *frame)
4170 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4172 if (GetDecoder () == NULL) {
4173 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4174 return;
4177 GetDecoder ()->DecodeFrameAsync (frame, false);
4180 void
4181 MarkerStream::FrameEnqueued ()
4183 MediaFrame *frame;
4185 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4187 frame = PopFrame ();
4189 if (frame == NULL) {
4190 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4191 return;
4194 if (closure != NULL) {
4195 closure->SetMarker (frame->marker);
4196 closure->Call ();
4197 closure->SetMarker (NULL);
4198 } else {
4199 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4200 mutex.Lock ();
4201 list.Append (new MediaMarker::Node (frame->marker));
4202 mutex.Unlock ();
4205 frame->unref ();
4208 MediaMarker *
4209 MarkerStream::Pop ()
4211 MediaMarker *result = NULL;
4212 MediaMarker::Node *node;
4214 mutex.Lock ();
4215 node = (MediaMarker::Node *) list.First ();
4216 if (node != NULL) {
4217 result = node->marker;
4218 result->ref ();
4219 list.Remove (node);
4221 mutex.Unlock ();
4223 return result;
4226 void
4227 MarkerStream::SetCallback (MediaMarkerFoundClosure *closure)
4229 if (this->closure)
4230 this->closure->unref ();
4231 this->closure = closure;
4232 if (this->closure)
4233 this->closure->ref ();
4237 * MediaWork
4239 MediaWork::MediaWork (MediaClosure *c)
4241 g_return_if_fail (c != NULL);
4243 closure = c;
4244 closure->ref ();
4247 MediaWork::~MediaWork ()
4249 g_return_if_fail (closure != NULL);
4251 closure->unref ();
4252 closure = NULL;
4256 * PassThroughDecoderInfo
4259 bool
4260 PassThroughDecoderInfo::Supports (const char *codec)
4262 const char *video_fourccs [] = { "yv12", "rgba", NULL };
4263 const char *audio_fourccs [] = { "pcm", NULL };
4265 for (int i = 0; video_fourccs [i] != NULL; i++)
4266 if (!strcmp (codec, video_fourccs [i]))
4267 return true;
4269 for (int i = 0; audio_fourccs [i] != NULL; i++)
4270 if (!strcmp (codec, audio_fourccs [i]))
4271 return true;
4273 return false;
4277 * PassThroughDecoder
4280 PassThroughDecoder::PassThroughDecoder (Media *media, IMediaStream *stream)
4281 : IMediaDecoder (Type::PASSTHROUGHDECODER, media, stream)
4285 void
4286 PassThroughDecoder::Dispose ()
4288 IMediaDecoder::Dispose ();
4291 void
4292 PassThroughDecoder::OpenDecoderAsyncInternal ()
4294 const char *fourcc = GetStream ()->GetCodec ();
4296 if (!strcmp (fourcc, "yv12")) {
4297 SetPixelFormat (MoonPixelFormatYUV420P);
4298 } else if (!strcmp (fourcc, "rgba")) {
4299 SetPixelFormat (MoonPixelFormatRGBA32);
4300 } else if (!strcmp (fourcc, "pcm")) {
4301 // nothing to do here
4302 } else {
4303 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc));
4304 return;
4307 ReportOpenDecoderCompleted ();
4310 void
4311 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4313 frame->AddState (MediaFrameDecoded);
4314 if (GetPixelFormat () == MoonPixelFormatYUV420P) {
4315 VideoStream *vs = (VideoStream *) GetStream ();
4317 frame->width = vs->width;
4318 frame->height = vs->height;
4320 frame->data_stride[0] = frame->buffer;
4321 frame->data_stride[1] = frame->buffer + (frame->width*frame->height);
4322 frame->data_stride[2] = frame->buffer + (frame->width*frame->height)+(frame->width/2*frame->height/2);
4323 frame->buffer = NULL;
4324 frame->srcStride[0] = frame->width;
4325 frame->srcSlideY = frame->width;
4326 frame->srcSlideH = frame->height;
4328 frame->AddState (MediaFramePlanar);
4330 ReportDecodeFrameCompleted (frame);
4334 * NullDecoderInfo
4337 bool
4338 NullDecoderInfo::Supports (const char *codec)
4340 const char *video_fourccs [] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL };
4341 const char *audio_fourccs [] = { "wmav1","wmav2", "wmav3", "mp3", NULL};
4343 for (int i = 0; video_fourccs [i] != NULL; i++)
4344 if (!strcmp (codec, video_fourccs [i]))
4345 return true;
4347 for (int i = 0; audio_fourccs [i] != NULL; i++)
4348 if (!strcmp (codec, audio_fourccs [i]))
4349 return true;
4352 return false;
4356 * NullDecoder
4359 NullDecoder::NullDecoder (Media *media, IMediaStream *stream) : IMediaDecoder (Type::NULLDECODER, media, stream)
4361 logo = NULL;
4362 logo_size = 0;
4363 prev_pts = G_MAXUINT64;
4366 void
4367 NullDecoder::Dispose ()
4369 g_free (logo);
4370 logo = NULL;
4372 IMediaDecoder::Dispose ();
4375 MediaResult
4376 NullDecoder::DecodeVideoFrame (MediaFrame *frame)
4378 // free encoded buffer and alloc a new one for our image
4379 g_free (frame->buffer);
4380 frame->buflen = logo_size;
4381 frame->buffer = (guint8*) g_malloc (frame->buflen);
4382 memcpy (frame->buffer, logo, frame->buflen);
4383 frame->AddState (MediaFrameDecoded);
4385 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4387 return MEDIA_SUCCESS;
4390 MediaResult
4391 NullDecoder::DecodeAudioFrame (MediaFrame *frame)
4393 AudioStream *as = (AudioStream *) GetStream ();
4394 guint32 samples;
4395 guint32 data_size;
4396 guint64 diff_pts;
4398 // discard encoded data
4399 g_free (frame->buffer);
4401 // We have no idea here how long the encoded audio data is
4402 // for the first frame we use 0.1 seconds, for the rest
4403 // we calculate the time since the last frame
4405 if (prev_pts == G_MAXUINT64 || frame->pts <= prev_pts) {
4406 samples = as->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4407 } else {
4408 diff_pts = frame->pts - prev_pts;
4409 samples = (float) as->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT / (float) diff_pts);
4411 prev_pts = frame->pts;
4413 data_size = samples * as->GetChannels () * 2 /* 16 bit audio */;
4415 frame->buflen = data_size;
4416 frame->buffer = (guint8 *) g_malloc0 (frame->buflen);
4418 frame->AddState (MediaFrameDecoded);
4420 return MEDIA_SUCCESS;
4423 void
4424 NullDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4426 MediaResult result = MEDIA_FAIL;
4427 IMediaStream *stream = GetStream ();
4429 if (stream->GetType () == MediaTypeAudio) {
4430 result = DecodeAudioFrame (frame);
4431 } else if (stream->GetType () == MediaTypeVideo) {
4432 result = DecodeVideoFrame (frame);
4435 if (MEDIA_SUCCEEDED (result)) {
4436 ReportDecodeFrameCompleted (frame);
4437 } else {
4438 ReportErrorOccurred (result);
4442 void
4443 NullDecoder::OpenDecoderAsyncInternal ()
4445 MediaResult result;
4446 IMediaStream *stream = GetStream ();
4448 if (stream->GetType () == MediaTypeAudio)
4449 result = OpenAudio ();
4450 else if (stream->GetType () == MediaTypeVideo)
4451 result = OpenVideo ();
4452 else
4453 result = MEDIA_FAIL;
4455 if (MEDIA_SUCCEEDED (result)) {
4456 ReportOpenDecoderCompleted ();
4457 } else {
4458 ReportErrorOccurred (result);
4462 MediaResult
4463 NullDecoder::OpenAudio ()
4465 return MEDIA_SUCCESS;
4468 MediaResult
4469 NullDecoder::OpenVideo ()
4471 VideoStream *vs = (VideoStream *) GetStream ();
4472 guint32 dest_height = vs->height;
4473 guint32 dest_width = vs->width;
4474 guint32 dest_i = 0;
4476 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4477 extern const char moonlight_logo [];
4478 const char *image = moonlight_logo;
4480 guint32 img_offset = *((guint32*)(image + 10));
4481 guint32 img_width = *((guint32*)(image + 18));
4482 guint32 img_height = *((guint32*)(image + 22));
4483 guint32 img_stride = (img_width * 3 + 3) & ~3; // in bytes
4484 guint32 img_i, img_h, img_w;
4485 guint32 start_w = (dest_width-img_width)/2;
4486 guint32 end_w = start_w + img_width;
4487 guint32 start_h = (dest_height-img_height)/2;
4488 guint32 end_h = start_h + img_height;
4490 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);
4492 // create the buffer for our image
4493 logo_size = dest_height * dest_width * 4;
4494 logo = (guint8*) g_malloc (logo_size);
4495 memset (logo, 0x00, logo_size);
4497 // write our image centered into the destination rectangle, flipped horizontally
4498 dest_i = 4;
4499 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4500 for (guint32 dest_w = 0; dest_w < dest_width; dest_w++) {
4501 if (dest_w >= start_w && dest_w < end_w && dest_h >= start_h && dest_h < end_h) {
4502 img_h = (dest_h - start_h) % img_height;
4503 img_w = (dest_w - start_w) % img_width;
4504 img_i = img_h * img_stride + img_w * 3;
4506 logo [logo_size - dest_i + 0] = image [img_offset + img_i + 0];
4507 logo [logo_size - dest_i + 1] = image [img_offset + img_i + 1];
4508 logo [logo_size - dest_i + 2] = image [img_offset + img_i + 2];
4510 logo [logo_size - dest_i + 3] = 0xff;
4512 dest_i += 4;
4516 // Flip the image vertically
4517 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4518 for (guint32 dest_w = 0; dest_w < dest_width / 2; dest_w++) {
4519 guint32 tmp;
4520 guint32 a = (dest_h * dest_width + dest_w) * 4;
4521 guint32 b = (dest_h * dest_width + dest_width - dest_w) * 4 - 4;
4522 for (guint32 c = 0; c < 3; c++) {
4523 tmp = logo [a + c];
4524 logo [a + c] = logo [b + c];
4525 logo [b + c] = tmp;
4530 SetPixelFormat (MoonPixelFormatRGB32);
4532 return MEDIA_SUCCESS;
4536 * ExternalDemuxer
4539 ExternalDemuxer::ExternalDemuxer (Media *media, void *instance, CloseDemuxerCallback close_demuxer,
4540 GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample, OpenDemuxerAsyncCallback open_demuxer,
4541 SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
4542 : IMediaDemuxer (Type::EXTERNALDEMUXER, media)
4544 this->close_demuxer_callback = close_demuxer;
4545 this->get_diagnostic_async_callback = get_diagnostic;
4546 this->get_sample_async_callback = get_sample;
4547 this->open_demuxer_async_callback = open_demuxer;
4548 this->seek_async_callback = seek;
4549 this->switch_media_stream_async_callback = switch_media_stream;
4550 this->instance = instance;
4552 pthread_rwlock_init (&rwlock, NULL);
4554 g_return_if_fail (instance != NULL);
4555 g_return_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL);
4558 ExternalDemuxer::~ExternalDemuxer ()
4560 pthread_rwlock_destroy (&rwlock);
4563 void
4564 ExternalDemuxer::Dispose ()
4566 ClearCallbacks ();
4567 IMediaDemuxer::Dispose ();
4570 void
4571 ExternalDemuxer::ClearCallbacks ()
4573 pthread_rwlock_wrlock (&rwlock);
4574 close_demuxer_callback = NULL;
4575 get_diagnostic_async_callback = NULL;
4576 get_sample_async_callback = NULL;
4577 open_demuxer_async_callback = NULL;
4578 seek_async_callback = NULL;
4579 switch_media_stream_async_callback = NULL;
4580 instance = NULL;
4581 pthread_rwlock_unlock (&rwlock);
4584 void
4585 ExternalDemuxer::SetCanSeek (bool value)
4587 g_warning ("TODO: ExternalDemuxer::SetCanSeek ()");
4590 gint32
4591 ExternalDemuxer::AddStream (IMediaStream *stream)
4593 return IMediaDemuxer::AddStream (stream);
4596 void
4597 ExternalDemuxer::CloseDemuxerInternal ()
4599 pthread_rwlock_rdlock (&rwlock);
4600 if (close_demuxer_callback != NULL) {
4601 close_demuxer_callback (instance);
4602 } else {
4603 #if SANITY
4604 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4605 #endif
4607 pthread_rwlock_unlock (&rwlock);
4610 void
4611 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind)
4613 pthread_rwlock_rdlock (&rwlock);
4614 if (get_diagnostic_async_callback != NULL) {
4615 get_diagnostic_async_callback (instance, diagnosticsKind);
4616 } else {
4617 #if SANITY
4618 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4619 #endif
4621 pthread_rwlock_unlock (&rwlock);
4624 void
4625 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
4627 g_return_if_fail (stream != NULL);
4629 pthread_rwlock_rdlock (&rwlock);
4630 if (get_sample_async_callback != NULL) {
4631 get_sample_async_callback (instance, stream->GetStreamType ());
4632 } else {
4633 #if SANITY
4634 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4635 #endif
4637 pthread_rwlock_unlock (&rwlock);
4640 void
4641 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4643 pthread_rwlock_rdlock (&rwlock);
4644 if (open_demuxer_async_callback != NULL) {
4645 open_demuxer_async_callback (instance, this);
4646 } else {
4647 #if SANITY
4648 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4649 #endif
4651 pthread_rwlock_unlock (&rwlock);
4654 void
4655 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime)
4657 pthread_rwlock_rdlock (&rwlock);
4658 if (seek_async_callback != NULL) {
4659 seek_async_callback (instance, seekToTime);
4660 } else {
4661 #if SANITY
4662 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4663 #endif
4665 pthread_rwlock_unlock (&rwlock);
4668 void
4669 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *mediaStreamDescription)
4671 g_return_if_fail (mediaStreamDescription != NULL);
4673 pthread_rwlock_rdlock (&rwlock);
4674 if (switch_media_stream_async_callback != NULL) {
4675 switch_media_stream_async_callback (instance, mediaStreamDescription);
4676 } else {
4677 #if SANITY
4678 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4679 #endif
4681 pthread_rwlock_unlock (&rwlock);
4686 * AudioStream
4689 AudioStream::AudioStream (Media *media)
4690 : IMediaStream (Type::AUDIOSTREAM, media)
4694 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)
4695 : IMediaStream (Type::AUDIOSTREAM, media)
4697 this->codec_id = codec_id;
4698 this->codec = CreateCodec (codec_id);
4699 this->extra_data = extra_data;
4700 this->extra_data_size = extra_data_size;
4701 input_bits_per_sample = bits_per_sample;
4702 output_bits_per_sample = bits_per_sample;
4703 input_block_align = block_align;
4704 output_block_align = block_align;
4705 input_sample_rate = sample_rate;
4706 output_sample_rate = sample_rate;
4707 input_channels = channels;
4708 output_channels = channels;
4709 input_bit_rate = bit_rate;
4710 output_bit_rate = bit_rate;
4714 * ExternalDecoder
4717 ExternalDecoder::ExternalDecoder (Media *media, IMediaStream *stream, void *instance, const char *name,
4718 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async,
4719 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async,
4720 ExternalDecoder_CleanupCallback cleanup,
4721 ExternalDecoder_CleanStateCallback clean_state,
4722 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame,
4723 ExternalDecoder_DisposeCallback dispose,
4724 ExternalDecoder_DtorCallback dtor)
4725 : IMediaDecoder (Type::EXTERNALDECODER, media, stream)
4727 this->instance = instance;
4728 this->name = g_strdup (name);
4729 this->decode_frame_async = decode_frame_async;
4730 this->open_decoder_async = open_decoder_async;
4731 this->cleanup = cleanup;
4732 this->clean_state = clean_state;
4733 this->has_delayed_frame = has_delayed_frame;
4734 this->dispose = dispose;
4735 this->dtor = dtor;
4738 ExternalDecoder::~ExternalDecoder ()
4740 dtor (instance);
4741 g_free (name);
4744 void
4745 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4747 decode_frame_async (instance, frame);
4750 void
4751 ExternalDecoder::OpenDecoderAsyncInternal ()
4753 open_decoder_async (instance);
4756 void
4757 ExternalDecoder::Dispose ()
4759 dispose (instance);
4761 IMediaDecoder::Dispose ();
4764 void
4765 ExternalDecoder::Cleanup (MediaFrame *frame)
4767 cleanup (instance, frame);
4770 void
4771 ExternalDecoder::CleanState ()
4773 clean_state (instance);
4776 bool
4777 ExternalDecoder::HasDelayedFrame ()
4779 return has_delayed_frame (instance);
4782 void
4783 ExternalDecoder::InputEnded ()
4785 GetStream ()->SetOutputEnded (true);
4789 * ExternalDecoderInfo
4792 ExternalDecoderInfo::ExternalDecoderInfo (void *instance, const char *name, ExternalDecoderInfo_SupportsCallback supports, ExternalDecoderInfo_Create create, ExternalDecoderInfo_dtor dtor)
4794 this->instance = instance;
4795 this->supports = supports;
4796 this->create = create;
4797 this->dtor = dtor;
4798 this->name = g_strdup (name);
4801 bool
4802 ExternalDecoderInfo::Supports (const char *codec)
4804 return supports (instance, codec);
4807 IMediaDecoder *
4808 ExternalDecoderInfo::Create (Media *media, IMediaStream *stream)
4810 return create (instance, media, stream);
4813 ExternalDecoderInfo::~ExternalDecoderInfo ()
4815 if (dtor != NULL)
4816 dtor (instance);
4817 g_free (name);