2009-11-12 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / pipeline.cpp
blob15b4cdd8a2ee07af4b4b8ee70617e6b33b61d830
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>
22 #include <signal.h>
24 #include "audio.h"
25 #include "pipeline.h"
26 #include "codec-version.h"
27 #include "pipeline-ffmpeg.h"
28 #include "mp3.h"
29 #include "uri.h"
30 #include "media.h"
31 #include "mediaelement.h"
32 #include "asf/asf.h"
33 #include "asf/asf-structures.h"
34 #include "yuv-converter.h"
35 #include "runtime.h"
36 #include "mms-downloader.h"
37 #include "pipeline-ui.h"
38 #include "pipeline-asf.h"
39 #include "playlist.h"
40 #include "deployment.h"
41 #include "timesource.h"
44 * Media
47 bool Media::registering_ms_codecs = false;
48 bool Media::registered_ms_codecs = false;
50 DemuxerInfo *Media::registered_demuxers = NULL;
51 DecoderInfo *Media::registered_decoders = NULL;
52 ConverterInfo *Media::registered_converters = NULL;
54 Media::Media (PlaylistRoot *root)
55 : IMediaObject (Type::MEDIA, this)
57 LOG_PIPELINE ("Media::Media (), id: %i\n", GET_OBJ_ID (this));
59 playlist = root;
60 buffering_time = 0;
61 file = NULL;
62 uri = NULL;
63 source = NULL;
64 demuxer = NULL;
65 markers = NULL;
67 is_disposed = false;
68 initialized = false;
69 opened = false;
70 opening = false;
71 stopped = false;
72 error_reported = false;
73 buffering_enabled = false;
74 in_open_internal = false;
75 http_retried = false;
76 download_progress = 0.0;
77 buffering_progress = 0.0;
79 if (!GetDeployment ()->RegisterMedia (this))
80 Dispose ();
83 Media::~Media ()
85 LOG_PIPELINE ("Media::~Media (), id: %i\n", GET_OBJ_ID (this));
88 void
89 Media::Dispose ()
91 IMediaSource *src;
92 IMediaDemuxer *dmx;
93 bool was_disposed = false;
95 LOG_PIPELINE ("Media::Dispose (), id: %i\n", GET_OBJ_ID (this));
97 mutex.Lock ();
98 was_disposed = is_disposed;
99 is_disposed = true;
100 mutex.Unlock ();
103 * Don't run our dispose code more than once, we may end up deleted on the main thread
104 * which will cause Dispose to be called on the main thread too. Since Dispose must already
105 * have been called on the media thread, we're safe.
107 if (was_disposed) {
108 IMediaObject::Dispose ();
109 return;
112 #if SANITY
113 if (!MediaThreadPool::IsThreadPoolThread ()) {
114 g_warning ("Media::Dispose (): Not in thread-pool thread, and we haven't been disposed already.\n");
116 #endif
118 ClearQueue ();
121 * We're on a media thread, and there is no other work in the queue: we can ensure that nothing
122 * more will ever execute on the media thread related to this Media instance.
125 g_free (file);
126 file = NULL;
127 g_free (uri);
128 uri = NULL;
130 src = this->source;
131 this->source = NULL;
132 if (src) {
133 src->Dispose ();
134 src->unref ();
137 dmx = this->demuxer;
138 this->demuxer = NULL;
139 if (dmx) {
140 dmx->Dispose ();
141 dmx->unref ();
144 delete markers;
145 markers = NULL;
147 IMediaObject::Dispose ();
149 GetDeployment ()->UnregisterMedia (this);
152 bool
153 Media::IsMSCodecsInstalled ()
155 return registered_ms_codecs;
158 void
159 Media::RegisterMSCodecs (void)
161 register_codec reg;
162 void *dl;
163 char *libmscodecs_path = NULL;
164 const char *functions [] = {"register_codec_pack", NULL};
165 const gchar *home = g_get_home_dir ();
166 registering_ms_codecs = true;
168 if (!(moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS)) {
169 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
170 return;
173 if (home != NULL)
174 libmscodecs_path = g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME, NULL);
176 if (!(g_file_test (libmscodecs_path, G_FILE_TEST_EXISTS) && g_file_test (libmscodecs_path, G_FILE_TEST_IS_REGULAR))) {
177 if (libmscodecs_path)
178 g_free (libmscodecs_path);
179 libmscodecs_path = g_strdup (CODEC_LIBRARY_NAME);
182 dl = dlopen (libmscodecs_path, RTLD_LAZY);
183 if (dl != NULL) {
184 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path);
186 int pre_decoders = 0;
187 int post_decoders = 0;
189 /* Count the number of current decoders */
190 MediaInfo *current;
191 current = registered_decoders;
192 while (current != NULL) {
193 pre_decoders++;
194 current = current->next;
197 for (int i = 0; functions [i] != NULL; i++) {
198 reg = (register_codec) dlsym (dl, functions [i]);
199 if (reg != NULL) {
200 (*reg) (MOONLIGHT_CODEC_ABI_VERSION);
201 } else {
202 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions [i], libmscodecs_path);
206 /* Count the number of decoders after registering the ms codecs */
207 current = registered_decoders;
208 while (current != NULL) {
209 post_decoders++;
210 current = current->next;
213 /* We could only load the codecs if the codec pack actually registered any decoders
214 * This ensures that if the user has invalid codecs for whatever reason, we request
215 * a new download. */
216 registered_ms_codecs = post_decoders > pre_decoders;
217 } else {
218 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path, dlerror ());
220 g_free (libmscodecs_path);
222 registering_ms_codecs = false;
225 void
226 Media::SetBufferingEnabled (bool value)
228 buffering_enabled = value;
229 WakeUp ();
232 void
233 Media::SetBufferingTime (guint64 buffering_time)
235 mutex.Lock ();
236 this->buffering_time = buffering_time;
237 mutex.Unlock ();
240 guint64
241 Media::GetBufferingTime ()
243 guint64 result;
244 mutex.Lock ();
245 result = buffering_time;
246 mutex.Unlock ();
247 return result;
250 PlaylistRoot *
251 Media::GetPlaylistRoot ()
253 return playlist;
256 List *
257 Media::GetMarkers ()
259 if (markers == NULL)
260 markers = new List ();
262 return markers;
265 void
266 Media::RegisterDemuxer (DemuxerInfo *info)
268 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
269 info->next = NULL;
270 if (registered_demuxers == NULL) {
271 registered_demuxers = info;
272 } else {
273 MediaInfo* current = registered_demuxers;
274 while (current->next != NULL)
275 current = current->next;
276 current->next = info;
280 void
281 Media::RegisterConverter (ConverterInfo *info)
283 //printf ("Media::RegisterConverter (%p)\n", info);
284 info->next = NULL;
285 if (registered_converters == NULL) {
286 registered_converters = info;
287 } else {
288 MediaInfo *current = registered_converters;
289 while (current->next != NULL)
290 current = current->next;
291 current->next = info;
295 void
296 Media::RegisterDecoder (DecoderInfo *info)
298 MediaInfo *current;
300 //printf ("Media::RegisterDecoder (%p)\n", info);
301 info->next = NULL;
302 if (registered_decoders == NULL) {
303 registered_decoders = info;
304 } else {
305 if (registering_ms_codecs) {
306 // MS codecs might get registered after all other codecs (right after installing them),
307 // which means after the null codecs so if they don't get special treatment, they won't
308 // get used until the next browser restart (when they're registered normally).
309 // So instead of appending them, we prepend them.
310 info->next = registered_decoders;
311 registered_decoders = info;
312 } else {
313 current = registered_decoders;
314 while (current->next != NULL)
315 current = current->next;
316 current->next = info;
319 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info->GetName ());
322 void
323 Media::Initialize ()
325 LOG_PIPELINE ("Media::Initialize ()\n");
327 // demuxers
328 Media::RegisterDemuxer (new ASFDemuxerInfo ());
329 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
330 Media::RegisterDemuxer (new ASXDemuxerInfo ());
332 // converters
333 if (!(moonlight_flags & RUNTIME_INIT_FFMPEG_YUV_CONVERTER))
334 Media::RegisterConverter (new YUVConverterInfo ());
336 // decoders
337 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
338 if (moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS) {
339 RegisterMSCodecs ();
341 #ifdef INCLUDE_FFMPEG
342 if (!(moonlight_flags & RUNTIME_INIT_DISABLE_FFMPEG_CODECS)) {
343 register_ffmpeg ();
345 #endif
347 Media::RegisterDecoder (new PassThroughDecoderInfo ());
348 Media::RegisterDecoder (new NullDecoderInfo ());
350 MediaThreadPool::Initialize ();
353 void
354 Media::Shutdown ()
356 LOG_PIPELINE ("Media::Shutdown ()\n");
358 MediaInfo *current;
359 MediaInfo *next;
361 // Make sure all threads are stopped
362 AudioPlayer::Shutdown ();
363 MediaThreadPool::Shutdown ();
365 current = registered_decoders;
366 while (current != NULL) {
367 next = current->next;
368 delete current;
369 current = next;
371 registered_decoders = NULL;
373 current = registered_demuxers;
374 while (current != NULL) {
375 next = current->next;
376 delete current;
377 current = next;
379 registered_demuxers = NULL;
381 current = registered_converters;
382 while (current != NULL) {
383 next = current->next;
384 delete current;
385 current = next;
387 registered_converters = NULL;
389 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
392 void
393 Media::Warning (MediaResult result, const char *format, ...)
395 va_list args;
397 if (MEDIA_SUCCEEDED (result))
398 return;
400 fprintf (stderr, "Moonlight: MediaResult = %d; ", result);
402 va_start (args, format);
403 vfprintf (stderr, format, args);
404 va_end (args);
406 fputc ('\n', stderr);
409 bool
410 Media::InMediaThread ()
412 return MediaThreadPool::IsThreadPoolThread ();
415 void
416 Media::ReportBufferingProgress (double progress)
418 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress, buffering_progress);
420 progress = MAX (MIN (progress, 1.0), 0.0);
422 if (progress == buffering_progress)
423 return;
425 if (progress < buffering_progress || progress > (buffering_progress + 0.005) || progress == 1.0 || progress == 0.0) {
426 buffering_progress = progress;
427 EmitSafe (BufferingProgressChangedEvent, new ProgressEventArgs (progress));
431 void
432 Media::ReportDownloadProgress (double progress)
434 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress, download_progress);
436 progress = MAX (MIN (progress, 1.0), 0.0);
438 if (progress <= download_progress) {
440 * Download progress percentage can actually go down - if the file size
441 * goes up. Yes, the file size can go up.
443 return;
446 if (progress > (download_progress + 0.005) || progress == 1.0 || progress == 0.0) {
447 download_progress = progress;
448 EmitSafe (DownloadProgressChangedEvent, new ProgressEventArgs (progress));
452 void
453 Media::SeekAsync (guint64 pts)
455 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
457 if (demuxer == NULL) {
458 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
459 return;
462 demuxer->SeekAsync (pts);
465 void
466 Media::ReportSeekCompleted (guint64 pts)
468 LOG_PIPELINE ("Media::ReportSeekCompleted (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
470 buffering_progress = 0;
471 ClearQueue ();
472 EmitSafe (SeekCompletedEvent);
475 void
476 Media::ReportOpenCompleted ()
478 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
480 EmitSafe (OpenCompletedEvent);
483 void
484 Media::ReportOpenDemuxerCompleted ()
486 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
488 OpenInternal ();
491 void
492 Media::ReportOpenDecoderCompleted (IMediaDecoder *decoder)
494 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder, GET_OBJ_ID (this));
496 g_return_if_fail (decoder != NULL);
498 OpenInternal ();
501 void
502 Media::ReportErrorOccurred (ErrorEventArgs *args)
504 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args, args == NULL ? NULL : args->GetErrorMessage());
506 if (args) {
507 fprintf (stderr, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args->GetErrorType()), args->GetErrorCode(), args->GetErrorMessage(), args->GetExtendedMessage());
508 } else {
509 fprintf (stderr, "Moonlight: Unspecified media error.\n");
512 if (!error_reported) {
513 error_reported = true;
514 EmitSafe (MediaErrorEvent, args);
518 void
519 Media::ReportErrorOccurred (const char *message)
521 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message);
523 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, message)));
526 void
527 Media::ReportErrorOccurred (MediaResult result)
529 char *msg = g_strdup_printf ("Media error: %i.", result);
530 ReportErrorOccurred (msg);
531 g_free (msg);
534 void
535 Media::PlayAsync ()
537 LOG_PIPELINE ("Media::PlayAsync ()\n");
539 MediaClosure *closure = new MediaClosure (this, PlayCallback, this, "Media::PlayAsync");
540 EnqueueWork (closure);
541 closure->unref ();
544 void
545 Media::PauseAsync ()
547 LOG_PIPELINE ("Media::PauseAsync ()\n");
550 void
551 Media::StopAsync ()
553 LOG_PIPELINE ("Media::StopAsync ()\n");
555 MediaClosure *closure = new MediaClosure (this, StopCallback, this, "Media::StopAsync");
556 EnqueueWork (closure);
557 closure->unref ();
560 MediaResult
561 Media::StopCallback (MediaClosure *closure)
563 closure->GetMedia ()->Stop ();
564 return MEDIA_SUCCESS;
567 MediaResult
568 Media::PlayCallback (MediaClosure *closure)
570 closure->GetMedia ()->Play ();
571 return MEDIA_SUCCESS;
574 void
575 Media::Stop ()
577 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
579 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
581 stopped = true;
583 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
584 /* ClearQueue (); */
586 if (demuxer != NULL)
587 demuxer->ClearBuffers ();
590 void
591 Media::Play ()
593 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
595 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
597 stopped = false;
598 if (demuxer != NULL)
599 demuxer->FillBuffers ();
602 void
603 Media::Initialize (Downloader *downloader, const char *PartName)
605 IMediaSource *source;
607 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader, PartName, GET_OBJ_ID (this));
609 g_return_if_fail (downloader != NULL);
610 g_return_if_fail (file == NULL);
611 g_return_if_fail (uri != NULL || PartName != NULL);
612 g_return_if_fail (initialized == false);
613 g_return_if_fail (error_reported == false);
614 g_return_if_fail (this->source == NULL);
616 if (downloader->Completed ()) {
617 file = downloader->GetDownloadedFilename (PartName);
619 if (file == NULL) {
620 ReportErrorOccurred ("Couldn't get downloaded filename.");
621 return;
625 if (file == NULL && PartName != NULL && PartName [0] != 0) {
626 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)");
627 return;
630 if (file == NULL) {
631 InternalDownloader *idl = downloader->GetInternalDownloader ();
632 MmsDownloader *mms_dl = (idl && idl->GetObjectType () == Type::MMSDOWNLOADER) ? (MmsDownloader *) idl : NULL;
634 if (mms_dl == NULL) {
635 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
636 return;
639 source = new MmsSource (this, downloader);
640 } else {
641 source = new FileSource (this, file);
644 Initialize (source);
645 source->unref ();
648 void
649 Media::Initialize (IMediaSource *source)
651 MediaResult result;
653 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source, GET_OBJ_ID (this));
655 g_return_if_fail (source != NULL);
656 g_return_if_fail (this->source == NULL);
657 g_return_if_fail (initialized == false);
659 result = source->Initialize ();
660 if (!MEDIA_SUCCEEDED (result)) {
661 ReportErrorOccurred (result);
662 return;
665 initialized = true;
666 this->source = source;
667 this->source->ref ();
670 void
671 Media::Initialize (const char *uri)
673 Downloader *dl;
674 IMediaSource *source = NULL;
676 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri, GET_OBJ_ID (this));
678 g_return_if_fail (uri != NULL);
679 g_return_if_fail (file == NULL);
680 g_return_if_fail (uri != NULL);
681 g_return_if_fail (initialized == false);
682 g_return_if_fail (error_reported == false);
683 g_return_if_fail (source == NULL);
684 g_return_if_fail (this->source == NULL);
686 this->uri = g_strdup (uri);
689 if (g_str_has_prefix (uri, "mms://") || g_str_has_prefix (uri, "rtsp://") || g_str_has_prefix (uri, "rtsps://")) {
690 dl = Surface::CreateDownloader (this);
691 if (dl == NULL) {
692 ReportErrorOccurred ("Couldn't create downloader.");
693 return;
696 dl->Open ("GET", uri, StreamingPolicy);
698 if (dl->GetFailedMessage () == NULL) {
699 Initialize (dl, NULL);
700 } else {
701 ReportErrorOccurred (new ErrorEventArgs (MediaError,
702 MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
705 dl->unref ();
707 return;
710 source = new ProgressiveSource (this, uri);
711 Initialize (source);
712 source->unref ();
715 void
716 Media::Initialize (IMediaDemuxer *demuxer)
718 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer, GET_OBJ_ID (this));
720 g_return_if_fail (demuxer != NULL);
721 g_return_if_fail (this->demuxer == NULL);
722 g_return_if_fail (initialized == false);
724 this->demuxer = demuxer;
725 this->demuxer->ref ();
727 initialized = true;
731 void
732 Media::RetryHttp (ErrorEventArgs *args)
734 char *http_uri = NULL;
736 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri);
738 g_return_if_fail (uri != NULL);
739 g_return_if_fail (source != NULL);
741 if (http_retried) {
742 ReportErrorOccurred (args);
743 return;
746 // CHECK: If the current protocolo is rtsps, should we retry http or https?
748 if (g_str_has_prefix (uri, "mms://")) {
749 http_uri = g_strdup_printf ("http://%s", uri + 6);
750 } else if (g_str_has_prefix (uri, "rtsp://")) {
751 http_uri = g_strdup_printf ("http://%s", uri + 7);
752 } else if (g_str_has_prefix (uri, "rtsps://")) {
753 http_uri = g_strdup_printf ("http://%s", uri + 8);
754 } else {
755 ReportErrorOccurred (args);
756 return;
759 http_retried = true;
761 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri);
763 g_free (uri);
764 uri = NULL;
765 /* this method is called on the main thread, ensure Dispose is called on the source on the media thread */
766 DisposeObject (source);
767 source->unref ();
768 source = NULL;
769 initialized = false;
770 error_reported = false;
772 Initialize (http_uri);
774 g_free (http_uri);
776 if (!error_reported)
777 OpenAsync ();
780 void
781 Media::OpenAsync ()
783 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
785 g_return_if_fail (initialized == true);
787 EmitSafe (OpeningEvent);
789 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenAsync");
790 EnqueueWork (closure);
791 closure->unref ();
794 void
795 Media::OpenInternal ()
797 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
799 g_return_if_fail (initialized == true);
801 if (opened) {
802 // This may happen due to the recursion detection below
803 // Example: we try open a demuxer, the demuxer opens successfully
804 // right away and calls ReportDemuxerOpenComplete which will call
805 // us. Due to the recursion detection we'll enqueue a call to
806 // OpenInternal, while the first OpenInternal may succeed and
807 // set opened to true.
808 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
809 return;
812 // detect recursive calls.
814 if (in_open_internal) {
815 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
816 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
817 EnqueueWork (closure);
818 closure->unref ();
819 return;
822 in_open_internal = true;
824 if (error_reported)
825 goto cleanup;
827 if (!SelectDemuxerAsync ()) {
828 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
829 goto cleanup;
832 if (error_reported)
833 goto cleanup;
835 if (!SelectDecodersAsync ()) {
836 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
837 goto cleanup;
840 opened = true;
841 opening = false;
843 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
845 EmitSafe (OpenCompletedEvent);
847 cleanup:
848 in_open_internal = false;
851 MediaResult
852 Media::OpenInternal (MediaClosure *closure)
854 Media *media = (Media *) closure->GetContext ();
856 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
858 media->OpenInternal ();
860 return MEDIA_SUCCESS;
863 bool
864 Media::SelectDemuxerAsync ()
866 DemuxerInfo *demuxerInfo;
867 MediaResult support;
868 MediaResult result;
869 bool eof;
871 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);
873 g_return_val_if_fail (error_reported == false, false);
874 g_return_val_if_fail (initialized == true, false);
876 // Check if demuxer already is open
877 if (demuxer != NULL) {
878 if (demuxer->IsOpened ())
879 return true;
880 if (!demuxer->IsOpening ())
881 demuxer->OpenDemuxerAsync ();
882 return demuxer->IsOpened ();
885 g_return_val_if_fail (source != NULL, false);
887 // Check if the source knows how to create the demuxer
888 demuxer = source->CreateDemuxer (this);
890 if (demuxer == NULL) { // No demuxer created, we need to find it ourselves.
891 if (source->CanSeek () && source->GetPosition () > 0) {
892 if (!source->Seek (0, SEEK_SET)) {
893 LOG_PIPELINE ("Media::SelectDemuxer (): could not seek to position 0 of the input stream. Will try to continue anyway.\n");
896 // Check if we have at least 1024 bytes or eof
897 if (!source->IsPositionAvailable (16, &eof)) {
898 if (!eof) {
899 // We need to try again later.
900 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
902 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
903 EnqueueWork (closure, false);
904 closure->unref ();
906 return false;
910 // Select a demuxer
911 demuxerInfo = registered_demuxers;
912 while (demuxer == NULL && demuxerInfo != NULL) {
913 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo->GetName ());
914 support = demuxerInfo->Supports (source);
916 if (support == MEDIA_SUCCESS)
917 break;
919 result = support;
921 if (result == MEDIA_NOT_ENOUGH_DATA) {
922 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 ());
924 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
925 EnqueueWork (closure, false);
926 closure->unref ();
928 return false;
931 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo->GetName ());
932 demuxerInfo = (DemuxerInfo *) demuxerInfo->next;
935 if (demuxerInfo == NULL) {
936 // No demuxer found, report an error
937 const char *source_name = file ? file : uri;
939 if (!source_name) {
940 switch (source->GetType ()) {
941 case MediaSourceTypeProgressive:
942 case MediaSourceTypeFile:
943 source_name = ((FileSource *) source)->GetFileName ();
944 break;
945 case MediaSourceTypeMms:
946 case MediaSourceTypeMmsEntry:
947 source_name = "live source";
948 break;
949 default:
950 source_name = "unknown source";
951 break;
954 char *msg = g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name);
955 ReportErrorOccurred (new ErrorEventArgs (MediaError,
956 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT"),
957 MEDIA_UNKNOWN_CODEC, msg));
958 g_free (msg);
959 return false;
962 // Found a demuxer
963 demuxer = demuxerInfo->Create (this, source);
964 } else {
965 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer->GetTypeName ());
968 if (demuxer->IsOpened ())
969 return true;
971 if (demuxer->IsOpening ())
972 return false;
974 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
976 demuxer->OpenDemuxerAsync ();
978 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
980 return demuxer != NULL && demuxer->IsOpened ();
983 bool
984 Media::SelectDecodersAsync ()
986 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
988 g_return_val_if_fail (error_reported == false, false);
989 g_return_val_if_fail (initialized == true, false);
991 if (demuxer == NULL) {
992 ReportErrorOccurred ("No demuxer to select decoders from.");
993 return false;
996 // If the demuxer has no streams (ASXDemuxer for instance)
997 // then just return success.
998 if (demuxer->GetStreamCount () == 0)
999 return true;
1001 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
1003 // Select codecs for each stream
1004 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1005 IMediaStream *stream = demuxer->GetStream (i);
1008 if (stream == NULL) {
1009 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
1010 return false;
1013 if (stream->GetDecoder () != NULL)
1014 continue;
1016 const char *codec = stream->GetCodec ();
1017 IMediaDecoder *decoder = NULL;
1019 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec);
1021 DecoderInfo *current_decoder = registered_decoders;
1022 while (current_decoder != NULL && !current_decoder->Supports (codec)) {
1023 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder->GetName (), codec);
1024 current_decoder = (DecoderInfo*) current_decoder->next;
1027 if (current_decoder == NULL) {
1028 Media::Warning (MEDIA_UNKNOWN_CODEC, "Unknown codec: '%s'.", codec);
1029 continue;
1032 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder->GetName (), codec);
1033 decoder = current_decoder->Create (this, stream);
1035 stream->SetDecoder (decoder);
1036 decoder->unref ();
1039 if (error_reported)
1040 return false;
1042 // Open the codecs
1043 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1045 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1046 IMediaStream *stream = demuxer->GetStream (i);
1047 IMediaDecoder *decoder;
1049 if (stream == NULL)
1050 continue;
1052 decoder = stream->GetDecoder ();
1054 if (decoder == NULL) {
1055 ReportErrorOccurred (new ErrorEventArgs (MediaError,
1056 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT")));
1057 return false;
1060 if (decoder->IsOpening () || decoder->IsOpened ())
1061 continue;
1063 decoder->OpenDecoderAsync ();
1066 if (error_reported)
1067 return false;
1069 // Wait until all the codecs have opened
1070 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1072 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1073 IMediaStream *stream = demuxer->GetStream (i);
1074 IMediaDecoder *decoder;
1076 if (stream == NULL)
1077 continue;
1079 decoder = stream->GetDecoder ();
1081 if (decoder == NULL) {
1082 ReportErrorOccurred (MEDIA_FAIL);
1083 return false;
1086 if (decoder->IsOpening ()) {
1087 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
1088 EnqueueWork (closure, false);
1089 closure->unref ();
1090 return false;
1093 if (!decoder->IsOpened ()) {
1094 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1095 ReportErrorOccurred (MEDIA_FAIL);
1096 return false;
1101 // All the codecs have been opened now.
1102 // Find converters for each of them (whenever required).
1104 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1106 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1107 IMediaStream *stream = demuxer->GetStream (i);
1108 IMediaDecoder *decoder;
1110 if (stream == NULL)
1111 continue;
1113 decoder = stream->GetDecoder ();
1115 if (decoder == NULL) {
1116 ReportErrorOccurred (MEDIA_FAIL);
1117 return false;
1120 if (stream->GetType () != MediaTypeVideo)
1121 continue; // Only video streams need converters
1123 if (decoder->GetPixelFormat () == MoonPixelFormatRGB32 || decoder->GetPixelFormat () == MoonPixelFormatRGBA32)
1124 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1126 // Select converter for this stream
1127 VideoStream *vs = (VideoStream *) stream;
1129 ConverterInfo* current_conv = registered_converters;
1130 while (current_conv != NULL && !current_conv->Supports (decoder->GetPixelFormat (), MoonPixelFormatRGB32)) {
1131 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1132 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1133 current_conv = (ConverterInfo*) current_conv->next;
1137 if (current_conv == NULL) {
1138 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER);
1139 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1140 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1141 return false;
1144 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1145 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1147 vs->converter = current_conv->Create (this, vs);
1148 vs->converter->input_format = decoder->GetPixelFormat ();
1149 vs->converter->output_format = MoonPixelFormatRGB32;
1150 if (!MEDIA_SUCCEEDED (vs->converter->Open ())) {
1151 vs->converter->unref ();
1152 vs->converter = NULL;
1153 ReportErrorOccurred (MEDIA_FAIL);
1154 return false;
1158 // Loop through all the streams, return true if at least one has a codec.
1160 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1161 IMediaStream *stream = demuxer->GetStream (i);
1163 if (stream == NULL)
1164 continue;
1166 if (stream->GetDecoder () != NULL)
1167 return true;
1170 // No codecs found for no stream, report an error.
1171 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1172 return false;
1175 bool
1176 Media::EnqueueWork (MediaClosure *closure, bool wakeup)
1178 bool result = false;
1179 bool disposed;
1181 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure);
1183 g_return_val_if_fail (closure != NULL, false);
1185 if (IsDisposed ())
1186 return false;
1188 mutex.Lock ();
1189 disposed = this->is_disposed;
1190 if (disposed) {
1191 result = false;
1192 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed);
1193 } else {
1194 MediaThreadPool::AddWork (closure, wakeup);
1195 result = true;
1197 mutex.Unlock ();
1199 return result;
1202 MediaResult
1203 Media::DisposeObjectInternal (MediaClosure *closure)
1205 closure->GetContext ()->Dispose ();
1206 return MEDIA_SUCCESS;
1209 void
1210 Media::DisposeObject (EventObject *obj)
1212 MediaDisposeObjectClosure *closure = new MediaDisposeObjectClosure (this, DisposeObjectInternal, obj);
1213 if (!EnqueueWork (closure, true)) {
1214 LOG_PIPELINE ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj);
1215 obj->Dispose ();
1217 closure->unref ();
1220 void
1221 Media::WakeUp ()
1223 MediaThreadPool::WakeUp ();
1226 void
1227 Media::ClearQueue ()
1229 LOG_PIPELINE ("Media::ClearQueue ().\n");
1230 MediaThreadPool::RemoveWork (this);
1234 * ASXDemuxer
1237 ASXDemuxer::ASXDemuxer (Media *media, IMediaSource *source)
1238 : IMediaDemuxer (Type::ASXDEMUXER, media, source)
1240 playlist = NULL;
1243 ASXDemuxer::~ASXDemuxer ()
1247 void
1248 ASXDemuxer::Dispose ()
1250 if (playlist) {
1251 playlist->unref ();
1252 playlist = NULL;
1254 IMediaDemuxer::Dispose ();
1257 void
1258 ASXDemuxer::OpenDemuxerAsyncInternal ()
1260 MediaResult result;
1261 PlaylistRoot *root;
1262 ErrorEventArgs *args = NULL;
1263 Media *media = GetMediaReffed ();
1265 g_return_if_fail (media != NULL);
1267 root = media->GetPlaylistRoot ();
1269 g_return_if_fail (root != NULL);
1271 PlaylistParser *parser = new PlaylistParser (root, source);
1273 if (MEDIA_SUCCEEDED (parser->Parse ())) {
1274 result = MEDIA_SUCCESS;
1275 playlist = parser->GetPlaylist ();
1276 playlist->ref ();
1277 } else {
1278 result = MEDIA_FAIL;
1279 args = parser->GetErrorEventArgs ();
1280 if (args != NULL)
1281 args->ref ();
1284 delete parser;
1286 if (MEDIA_SUCCEEDED (result)) {
1287 ReportOpenDemuxerCompleted ();
1288 } else if (args != NULL) {
1289 args->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1290 ReportErrorOccurred (args);
1291 } else {
1292 ReportErrorOccurred (result);
1294 if (args)
1295 args->unref ();
1297 media->unref ();
1301 * ASXDemuxerInfo
1304 MediaResult
1305 ASXDemuxerInfo::Supports (IMediaSource *source)
1307 if (PlaylistParser::IsASX2 (source) || PlaylistParser::IsASX3 (source)) {
1308 return MEDIA_SUCCESS;
1309 } else {
1310 return MEDIA_FAIL;
1314 IMediaDemuxer *
1315 ASXDemuxerInfo::Create (Media *media, IMediaSource *source)
1317 return new ASXDemuxer (media, source);
1321 * ManagedStreamSource
1324 ManagedStreamSource::ManagedStreamSource (Media *media, ManagedStreamCallbacks *stream) : IMediaSource (Type::MANAGEDSTREAMSOURCE, media)
1326 memcpy (&this->stream, stream, sizeof (this->stream));
1329 ManagedStreamSource::~ManagedStreamSource ()
1331 stream.handle = NULL;
1334 gint32
1335 ManagedStreamSource::ReadInternal (void *buf, guint32 n)
1337 return stream.Read (stream.handle, buf, 0, n);
1340 gint32
1341 ManagedStreamSource::PeekInternal (void *buf, guint32 n)
1343 int read;
1345 read = stream.Read (stream.handle, buf, 0, n);
1346 stream.Seek (stream.handle, -read, 1 /* SeekOrigin.Current */);
1347 return read;
1350 bool
1351 ManagedStreamSource::SeekInternal (gint64 offset, int mode)
1353 stream.Seek (stream.handle, offset, mode /* FIXME: check if mode values matches SeekOrigin values */);
1354 return true;
1357 gint64
1358 ManagedStreamSource::GetPositionInternal ()
1360 return stream.Position (stream.handle);
1363 gint64
1364 ManagedStreamSource::GetSizeInternal ()
1366 return stream.Length (stream.handle);
1370 * FileSource
1373 FileSource::FileSource (Media *media, const char *filename) : IMediaSource (Type::FILESOURCE, media)
1375 this->filename = g_strdup (filename);
1376 fd = NULL;
1377 size = 0;
1378 temp_file = false;
1381 FileSource::FileSource (Media *media, bool temp_file) : IMediaSource (Type::FILESOURCE, media)
1383 filename = NULL;
1384 fd = NULL;
1385 size = 0;
1386 this->temp_file = temp_file;
1389 FileSource::~FileSource ()
1393 void
1394 FileSource::Dispose ()
1396 g_free (filename);
1397 filename = NULL;
1398 if (fd != NULL) {
1399 fclose (fd);
1400 fd = NULL;
1402 IMediaSource::Dispose ();
1405 MediaResult
1406 FileSource::Initialize ()
1408 int tmp_fd;
1410 LOG_PIPELINE ("FileSource::Initialize ()\n");
1412 if (fd != NULL)
1413 return MEDIA_SUCCESS;
1415 if (temp_file) {
1416 if (filename != NULL)
1417 return MEDIA_FILE_ERROR;
1419 filename = g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL);
1421 if ((tmp_fd = g_mkstemp (filename)) == -1) {
1422 g_free (filename);
1423 filename = NULL;
1425 return MEDIA_FAIL;
1428 fd = fdopen (tmp_fd, "r");
1430 setvbuf (fd, buffer, _IOFBF, sizeof (buffer));
1431 } else {
1432 if (filename == NULL)
1433 return MEDIA_FILE_ERROR;
1435 fd = g_fopen (filename, "r");
1438 if (fd == NULL)
1439 return MEDIA_FILE_ERROR;
1441 UpdateSize ();
1443 return MEDIA_SUCCESS;
1446 MediaResult
1447 FileSource::Open (const char *filename)
1449 g_return_val_if_fail (filename != NULL, MEDIA_FAIL);
1451 g_free (this->filename);
1452 this->filename = g_strdup (filename);
1454 if (fd != NULL) {
1455 fclose (fd);
1456 fd = NULL;
1459 fd = fopen (filename, "r");
1461 if (fd == NULL)
1462 return MEDIA_FAIL;
1464 UpdateSize ();
1466 return MEDIA_SUCCESS;
1469 void
1470 FileSource::UpdateSize ()
1472 struct stat st;
1474 g_return_if_fail (fd != NULL);
1476 if (fstat (fileno (fd), &st) != -1) {
1477 size = st.st_size;
1478 } else {
1479 size = 0;
1483 gint64
1484 FileSource::GetSizeInternal ()
1486 return size;
1489 gint64
1490 FileSource::GetPositionInternal ()
1492 gint64 result;
1494 if (fd == NULL)
1495 return -1;
1497 result = ftell (fd);
1499 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %" G_GINT64_FORMAT "\n", result);
1501 return result;
1504 bool
1505 FileSource::SeekInternal (gint64 offset, int mode)
1507 gint64 n;
1509 if (fd == NULL)
1510 return false;
1512 LOG_PIPELINE ("FileSource::SeekInternal (%" G_GINT64_FORMAT ", %i)\n", offset, mode);
1514 clearerr (fd);
1515 n = fseek (fd, offset, mode);
1517 return n != -1;
1520 gint32
1521 FileSource::ReadInternal (void *buf, guint32 n)
1523 ssize_t nread = 0;
1525 if (fd == NULL) {
1526 errno = EINVAL;
1527 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf, n);
1528 return -1;
1531 clearerr (fd);
1532 nread = fread (buf, 1, n, fd);
1534 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n, (int) nread);
1536 return nread;
1539 gint32
1540 FileSource::PeekInternal (void *buf, guint32 n)
1542 gint32 result;
1544 result = ReadSome (buf, n);
1546 Seek (-result, SEEK_CUR);
1548 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %" G_GINT64_FORMAT " [Done]\n", GET_OBJ_ID (this), buf, n, GetPosition ());
1550 return result;
1553 bool
1554 FileSource::Eof ()
1556 if (fd == NULL)
1557 return false;
1559 return feof (fd);
1563 * ProgressiveSource
1566 ProgressiveSource::ProgressiveSource (Media *media, const char *uri) : FileSource (media, true)
1568 write_pos = 0;
1569 size = -1;
1570 write_fd = NULL;
1571 cancellable = NULL;
1572 this->uri = g_strdup (uri);
1575 ProgressiveSource::~ProgressiveSource ()
1577 CloseWriteFile ();
1580 void
1581 ProgressiveSource::Dispose ()
1583 g_free (uri);
1584 uri = NULL;
1586 if (cancellable) {
1587 if (Surface::InMainThread ()) {
1588 delete_cancellable (this);
1589 } else {
1590 // we have to cancel/delete he cancellable on the main thread
1591 // it may end up doing a lot of stuff, including calling into
1592 // mozilla.
1594 // The tick call will ref us until the callback has been called.
1595 // Note that it may cause a warning to be printed
1596 // in ref () (reffing an object with a refcount of 0).
1597 // TODO: find a way to avoid the warning in this case, imho this is
1598 // a valid case of reffing an object with a refcount of 0.
1599 AddTickCallSafe (delete_cancellable);
1603 FileSource::Dispose ();
1606 void
1607 ProgressiveSource::delete_cancellable (EventObject *data)
1609 ProgressiveSource *src = (ProgressiveSource *) data;
1610 if (src->cancellable) {
1611 src->cancellable->Cancel ();
1612 delete src->cancellable;
1613 src->cancellable = NULL;
1617 MediaResult
1618 ProgressiveSource::Initialize ()
1620 MediaResult result = MEDIA_SUCCESS;
1621 Application *application;
1623 application = GetDeployment ()->GetCurrentApplication ();
1625 g_return_val_if_fail (application != NULL, MEDIA_FAIL);
1626 g_return_val_if_fail (filename == NULL, MEDIA_FAIL);
1627 g_return_val_if_fail (cancellable == NULL, MEDIA_FAIL);
1629 result = FileSource::Initialize ();
1631 if (!MEDIA_SUCCEEDED (result))
1632 return result;
1634 write_fd = g_fopen (filename, "w");
1635 if (write_fd == NULL) {
1636 char *msg = g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename);
1637 ReportErrorOccurred (msg);
1638 g_free (msg);
1639 return MEDIA_FAIL;
1642 // unlink the file right away so that it'll be deleted even if we crash.
1643 if (moonlight_flags & RUNTIME_INIT_KEEP_MEDIA) {
1644 printf ("Moonlight: The media file %s will not deleted.\n", filename);
1645 } else {
1646 g_unlink (filename);
1649 cancellable = new Cancellable ();
1650 Uri *u = new Uri ();
1651 if (u->Parse (uri)) {
1652 application->GetResource (NULL, u, notify_func, data_write, MediaPolicy, cancellable, (gpointer) this);
1653 } else {
1654 result = MEDIA_FAIL;
1655 char *msg = g_strdup_printf ("Could not parse the uri '%s'", uri);
1656 ReportErrorOccurred (msg);
1657 g_free (msg);
1659 delete u;
1661 return result;
1664 void
1665 ProgressiveSource::notify_func (NotifyType type, gint64 args, void *closure)
1667 g_return_if_fail (closure != NULL);
1668 ((ProgressiveSource *) closure)->Notify (type, args);
1671 void
1672 ProgressiveSource::Notify (NotifyType type, gint64 args)
1674 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT ")\n",
1675 type,
1676 type == ::NotifySize ? "NotifySize" :
1677 (type == NotifyCompleted ? "NotifyCompleted" :
1678 (type == NotifyFailed ? "NotifyFailed" :
1679 (type == NotifyStarted ? "NotifyStarted" :
1680 (type == NotifyProgressChanged ? "NotifyProgressChanged" : "unknown")))),
1681 args);
1683 switch (type) {
1684 case ::NotifySize:
1685 NotifySize (args);
1686 break;
1687 case NotifyCompleted:
1688 DownloadComplete ();
1689 break;
1690 case NotifyFailed:
1691 DownloadFailed ();
1692 break;
1693 case NotifyStarted:
1694 case NotifyProgressChanged:
1695 default:
1696 break;
1700 void
1701 ProgressiveSource::data_write (void *data, gint32 offset, gint32 n, void *closure)
1703 g_return_if_fail (closure != NULL);
1704 ((ProgressiveSource *) closure)->DataWrite (data, offset, n);
1707 void
1708 ProgressiveSource::DataWrite (void *buf, gint32 offset, gint32 n)
1710 size_t nwritten;
1711 Media *media = NULL;
1713 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf, offset, n, media, filename);
1715 if (IsDisposed ())
1716 return;
1718 g_return_if_fail (write_fd != NULL);
1720 media = GetMediaReffed ();
1722 if (n == 0) {
1723 // We've got the entire file, update the size
1724 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.
1726 // Close our write handle, we won't write more now
1727 CloseWriteFile ();
1729 goto cleanup;
1732 nwritten = fwrite (buf, 1, n, write_fd);
1733 fflush (write_fd);
1735 Lock ();
1736 write_pos += nwritten;
1737 Unlock ();
1739 cleanup:
1740 if (media) {
1741 media->WakeUp ();
1742 media->ReportDownloadProgress ((double) (offset + n) / (double) size);
1743 media->unref ();
1747 void
1748 ProgressiveSource::NotifySize (gint64 size)
1750 LOG_PIPELINE ("ProgressiveSource::NotifySize (%" G_GINT64_FORMAT ")\n", size);
1752 Lock ();
1753 this->size = size;
1754 Unlock ();
1757 void
1758 ProgressiveSource::DownloadComplete ()
1760 MediaResult result = MEDIA_SUCCESS;
1761 Media *media = GetMediaReffed ();
1763 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1765 Lock ();
1766 if (write_pos != size && size != -1) { // what happend here?
1767 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT ") != the reported size (%" G_GINT64_FORMAT ")\n", write_pos, size);
1770 this->size = write_pos;
1772 // Close our write handle, we won't write more now
1773 CloseWriteFile ();
1775 Unlock ();
1777 if (!MEDIA_SUCCEEDED (result))
1778 ReportErrorOccurred (result);
1780 if (media) {
1781 media->ReportDownloadProgress (1.0);
1782 media->WakeUp ();
1783 media->unref ();
1787 void
1788 ProgressiveSource::DownloadFailed ()
1790 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1792 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
1795 void
1796 ProgressiveSource::CloseWriteFile ()
1798 if (write_fd == NULL)
1799 return;
1801 fclose (write_fd);
1802 write_fd = NULL;
1806 * MemorySource
1809 MemorySource::MemorySource (Media *media, void *memory, gint32 size, gint64 start, bool owner)
1810 : IMediaSource (Type::MEMORYSOURCE, media)
1812 this->memory = memory;
1813 this->size = size;
1814 this->start = start;
1815 this->pos = 0;
1816 this->owner = owner;
1819 MemorySource::~MemorySource ()
1821 if (owner)
1822 g_free (memory);
1825 bool
1826 MemorySource::SeekInternal (gint64 offset, int mode)
1828 gint64 real_offset;
1830 switch (mode) {
1831 case SEEK_SET:
1832 real_offset = offset - start;
1833 if (real_offset < 0 || real_offset >= size)
1834 return false;
1835 pos = real_offset;
1836 return true;
1837 case SEEK_CUR:
1838 if (pos + offset > size || pos + offset < 0)
1839 return false;
1840 pos += offset;
1841 return true;
1842 case SEEK_END:
1843 if (size - offset > size || size - offset < 0)
1844 return false;
1845 pos = size - offset;
1846 return true;
1847 default:
1848 return false;
1850 return true;
1853 gint32
1854 MemorySource::ReadInternal (void *buffer, guint32 n)
1856 guint32 k = MIN (n, size - pos);
1857 memcpy (buffer, ((char*) memory) + pos, k);
1858 pos += k;
1859 return k;
1862 gint32
1863 MemorySource::PeekInternal (void *buffer, guint32 n)
1865 gint64 start = this->start + pos;
1867 if (this->start > start)
1868 return 0;
1870 if ((this->start + size) < (start + n))
1871 return 0;
1873 memcpy (buffer, ((char*) memory) + this->start - start, n);
1874 return n;
1878 * MediaThreadPool
1881 pthread_mutex_t MediaThreadPool::mutex = PTHREAD_MUTEX_INITIALIZER;
1882 pthread_cond_t MediaThreadPool::condition = PTHREAD_COND_INITIALIZER;
1883 pthread_cond_t MediaThreadPool::completed_condition = PTHREAD_COND_INITIALIZER;
1884 int MediaThreadPool::count = 0;
1885 pthread_t MediaThreadPool::threads [max_threads];
1886 Media *MediaThreadPool::medias [max_threads];
1887 Deployment *MediaThreadPool::deployments [max_threads];
1888 bool MediaThreadPool::shutting_down = false;
1889 List *MediaThreadPool::queue = NULL;
1890 bool MediaThreadPool::valid [max_threads];
1892 void
1893 MediaThreadPool::AddWork (MediaClosure *closure, bool wakeup)
1895 pthread_attr_t attribs;
1896 int result = 0;
1898 pthread_mutex_lock (&mutex);
1900 if (shutting_down) {
1901 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1902 } else {
1903 if (queue == NULL)
1904 queue = new List ();
1905 queue->Append (new MediaWork (closure));
1907 // check if all threads are busy with other Media objects
1908 bool spawn = true;
1909 if (count == 0) {
1910 spawn = true;
1911 } else if (count < max_threads) {
1912 Media *media = closure->GetMedia ();
1913 for (int i = 0; i < count; i++) {
1914 if (medias [i] == NULL || medias [i] == media) {
1915 spawn = false; // there is a thread working on this media or not working at all.
1916 break;
1919 } else {
1920 spawn = false;
1923 if (spawn) {
1924 int prev_count = count;
1926 count++; // start up another thread.
1928 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count);
1930 for (int i = prev_count; i < count && result == 0; i++) {
1931 valid [i] = false;
1932 medias [i] = NULL;
1933 deployments [i] = NULL;
1935 pthread_attr_init (&attribs);
1936 pthread_attr_setdetachstate (&attribs, PTHREAD_CREATE_JOINABLE);
1937 result = pthread_create (&threads [i], &attribs, WorkerLoop, NULL);
1938 pthread_attr_destroy (&attribs);
1940 if (result != 0) {
1941 fprintf (stderr, "Moonlight: could not create media thread: %s (%i)\n", strerror (result), result);
1942 } else {
1943 valid [i] = true;
1948 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1949 closure->GetDescription (), closure, closure->GetMedia (), GET_OBJ_ID (closure->GetMedia ()), closure->GetDeployment (), queue ? queue->Length () : -1);
1951 if (wakeup)
1952 pthread_cond_signal (&condition);
1954 pthread_mutex_unlock (&mutex);
1957 void
1958 MediaThreadPool::WaitForCompletion (Deployment *deployment)
1960 bool waiting = false;
1961 MediaWork *current = NULL;
1963 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment);
1965 VERIFY_MAIN_THREAD;
1967 pthread_mutex_lock (&mutex);
1968 do {
1969 waiting = false;
1971 /* check if the deployment is being worked on */
1972 for (int i = 0; i < count; i++) {
1973 if (deployments [i] == deployment) {
1974 waiting = true;
1975 break;
1978 /* check if the deployment is in the queue */
1979 if (!waiting && queue != NULL) {
1980 current = (MediaWork *) queue->First ();
1981 while (current != NULL) {
1982 if (current->closure->GetDeployment () == deployment) {
1983 waiting = true;
1984 break;
1986 current = (MediaWork *) current->next;
1989 if (waiting) {
1990 timespec ts;
1991 ts.tv_sec = 0;
1992 ts.tv_nsec = 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
1993 pthread_cond_timedwait (&completed_condition, &mutex, &ts);
1995 } while (waiting);
1996 pthread_mutex_unlock (&mutex);
1999 void
2000 MediaThreadPool::RemoveWork (Media *media)
2002 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media, GET_OBJ_ID (media));
2004 List::Node *next;
2005 List::Node *first = NULL;
2006 List::Node *last = NULL;
2007 List::Node *current = NULL;
2008 int counter = 0;
2010 pthread_mutex_lock (&mutex);
2012 // create a list of nodes to delete
2013 current = queue != NULL ? queue->First () : NULL;
2014 while (current != NULL) {
2015 next = current->next; // retrieve next before Unlinking
2016 MediaWork *mw = (MediaWork *) current;
2017 if (mw->closure->GetMedia () == media) {
2018 queue->Unlink (current);
2019 if (first == NULL) {
2020 first = current;
2021 } else {
2022 last->next = current;
2024 last = current;
2025 counter++;
2026 break;
2028 current = next;
2031 pthread_mutex_unlock (&mutex);
2033 // We have to delete the list nodes with the
2034 // queue mutex unlocked, due to refcounting
2035 // (our node's (MediaWork) dtor will cause unrefs,
2036 // which may cause other dtors to be called,
2037 // eventually ending up wanting to lock the mutex
2038 // again).
2040 current = first;
2041 while (current != NULL) {
2042 next = current->next;
2043 delete current;
2044 current = next;
2048 void
2049 MediaThreadPool::WakeUp ()
2051 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2053 pthread_mutex_lock (&mutex);
2054 pthread_cond_signal (&condition);
2055 pthread_mutex_unlock (&mutex);
2058 bool
2059 MediaThreadPool::IsThreadPoolThread ()
2061 bool result = false;
2062 pthread_mutex_lock (&mutex);
2063 for (int i = 0; i < count; i++) {
2064 if (pthread_equal (pthread_self (), threads [i])) {
2065 result = true;
2066 break;
2069 pthread_mutex_unlock (&mutex);
2070 return result;
2073 void
2074 MediaThreadPool::Initialize ()
2076 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2077 VERIFY_MAIN_THREAD;
2079 shutting_down = false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2082 void
2083 MediaThreadPool::Shutdown ()
2085 List::Node *current = NULL;
2086 List::Node *next = NULL;
2088 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count);
2090 VERIFY_MAIN_THREAD;
2092 g_return_if_fail (!shutting_down);
2094 pthread_mutex_lock (&mutex);
2096 shutting_down = true;
2097 pthread_cond_broadcast (&condition);
2099 for (int i = 0; i < count; i++) {
2100 if (!valid [i])
2101 continue;
2103 pthread_mutex_unlock (&mutex);
2104 pthread_join (threads [i], NULL);
2105 pthread_mutex_lock (&mutex);
2108 if (queue != NULL) {
2109 current = queue->First ();
2110 queue->Clear (false);
2111 delete queue;
2112 queue = NULL;
2114 count = 0;
2116 pthread_mutex_unlock (&mutex);
2118 // deleting a node can have side-effects, so we first copy the list of nodes,
2119 // clear the original and loop over the copy while deleting the nodes.
2120 // this prevents any reentering issues while deleting nodes.
2121 while (current != NULL) {
2122 next = current->next;
2123 delete current;
2124 current = next;
2127 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2130 void *
2131 MediaThreadPool::WorkerLoop (void *data)
2133 MediaWork *node = NULL;
2134 Media *media = NULL;
2135 int self_index = -1;
2138 * Unblock any signals. We inherit the blocked signals from the thread that
2139 * created us, and if that thread happens to be a thread that has signals
2140 * blocked, we might end up deadlocking in the gc (since the gc delivers
2141 * a suspend signal, this thread never gets it because the signal is blocked,
2142 * and the gc waits for us to handle the suspend signal).
2143 * The pulseaudio thread is one example of a thread that has all signals
2144 * blocked, causing this issue if we create a new thread from the
2145 * pulseaudio thread.
2148 sigset_t signal_set;
2149 int err = 0;
2150 if ((err = sigemptyset (&signal_set)) != 0) {
2151 fprintf (stderr, "Moonlight: Media thread pool was unable to create an empty set of signals: %s (%i).\n", strerror (err), err);
2152 } else if ((err = pthread_sigmask (SIG_SETMASK, &signal_set, NULL)) != 0) {
2153 fprintf (stderr, "Moonlight: Media thread pool was unable to unblock all signals: %s (%i).\n", strerror (err), err);
2155 if (err != 0) {
2156 /* Something failed. Check if all signals are unblocked, if not, exit
2157 * the thread. Exiting the thread might cause media playback to fail,
2158 * while continuing with blocked signals will probably end up
2159 * deadlocking the gc.*/
2160 bool any_blocked_signals = false;
2162 if (pthread_sigmask (SIG_BLOCK, NULL, &signal_set) != 0) {
2163 any_blocked_signals = true; /* Assume the worst */
2164 } else if (!sigisemptyset (&signal_set)) {
2165 any_blocked_signals = true;
2168 if (any_blocked_signals) {
2169 fprintf (stderr, "Moonlight: A media thread was started with blocked signals and could not unblock them. The media thread will exit (this may cause media playback to fail).\n");
2170 return NULL;
2174 pthread_mutex_lock (&mutex);
2175 for (int i = 0; i < count; i++) {
2176 if (pthread_equal (threads [i], pthread_self ())) {
2177 self_index = i;
2178 break;
2181 pthread_mutex_unlock (&mutex);
2183 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index);
2185 g_return_val_if_fail (self_index >= 0, NULL);
2187 while (!shutting_down) {
2188 pthread_mutex_lock (&mutex);
2190 medias [self_index] = NULL;
2191 deployments [self_index] = NULL;
2192 /* if anybody was waiting for us to finish working, notify them */
2193 if (media != NULL)
2194 pthread_cond_signal (&completed_condition);
2196 media = NULL;
2197 node = (MediaWork *) (queue != NULL ? queue->First () : NULL);
2199 while (node != NULL) {
2200 media = node->closure->GetMedia ();
2202 for (int i = 0; i < count; i++) {
2203 if (medias [i] == media) {
2204 // another thread is working for the same media object.
2205 // we need to find something else to do.
2206 media = NULL;
2207 break;
2211 if (media != NULL)
2212 break;
2214 node = (MediaWork *) node->next;
2217 if (node == NULL) {
2218 pthread_cond_wait (&condition, &mutex);
2219 } else {
2220 queue->Unlink (node);
2223 if (node != NULL) {
2224 medias [self_index] = media;
2225 /* At this point the current deployment might be wrong, so avoid
2226 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2227 * here, since it might end up doing a lot of work with the mutex
2228 * locked. */
2229 deployments [self_index] = media->GetUnsafeDeployment ();
2232 pthread_mutex_unlock (&mutex);
2234 if (node == NULL)
2235 continue;
2237 media->SetCurrentDeployment (true, true);
2239 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);
2241 node->closure->Call ();
2243 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node);
2245 delete node;
2248 pthread_mutex_lock (&mutex);
2249 deployments [self_index] = NULL;
2250 medias [self_index] = NULL;
2251 /* if anybody was waiting for us to finish working, notify them */
2252 if (media != NULL)
2253 pthread_cond_signal (&completed_condition);
2254 pthread_mutex_unlock (&mutex);
2256 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index);
2258 return NULL;
2263 * MediaClosure
2266 MediaClosure::MediaClosure (Media *media, MediaCallback *callback, EventObject *context, const char *description)
2267 : EventObject (Type::MEDIACLOSURE, true)
2269 Init (media, callback, context);
2270 this->description = description;
2273 MediaClosure::MediaClosure (Type::Kind object_kind, Media *media, MediaCallback *callback, EventObject *context)
2274 : EventObject (object_kind, true)
2276 Init (media, callback, context);
2279 void
2280 MediaClosure::Init (Media *media, MediaCallback *callback, EventObject *context)
2282 result = MEDIA_INVALID;
2283 description = NULL;
2284 this->callback = callback;
2285 this->context = context;
2286 if (this->context)
2287 this->context->ref ();
2288 this->media = media;
2289 if (this->media)
2290 this->media->ref ();
2292 // put checks at the end so that fields are still initialized, since we can't abort construction.
2293 g_return_if_fail (callback != NULL);
2294 g_return_if_fail (media != NULL);
2297 void
2298 MediaClosure::Dispose ()
2300 if (context) {
2301 context->unref ();
2302 context = NULL;
2305 if (media) {
2306 media->unref ();
2307 media = NULL;
2310 callback = NULL;
2312 EventObject::Dispose ();
2315 void
2316 MediaClosure::Call ()
2318 if (callback) {
2319 result = callback (this);
2320 } else {
2321 result = MEDIA_NO_CALLBACK;
2326 * MediaDisposeObjectClosure
2328 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media *media, MediaCallback *callback, EventObject *context)
2329 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE, media, callback, context)
2333 void
2334 MediaDisposeObjectClosure::Dispose ()
2336 if (!CallExecuted ()) {
2337 // we haven't been executed. do it now.
2338 #if SANITY && DEBUG
2339 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2340 #endif
2341 Call ();
2344 MediaClosure::Dispose ();
2348 * MediaSeekClosure
2350 MediaSeekClosure::MediaSeekClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2351 : MediaClosure (Type::MEDIASEEKCLOSURE, media, callback, context)
2353 this->pts = pts;
2357 * MediaReportSeekCompletedClosure
2360 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2361 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE, media, callback, context)
2363 g_return_if_fail (context != NULL);
2365 this->pts = pts;
2368 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2372 void
2373 MediaReportSeekCompletedClosure::Dispose ()
2375 MediaClosure::Dispose ();
2379 * MediaGetFrameClosure
2382 MediaGetFrameClosure::MediaGetFrameClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, IMediaStream *stream)
2383 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2385 this->stream = NULL;
2387 g_return_if_fail (context != NULL);
2388 g_return_if_fail (stream != NULL);
2390 this->stream = stream;
2391 // this->stream->ref ();
2393 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2396 MediaGetFrameClosure::~MediaGetFrameClosure ()
2398 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2401 void
2402 MediaGetFrameClosure::Dispose ()
2404 if (stream) {
2405 // stream->unref ();
2406 stream = NULL;
2409 MediaClosure::Dispose ();
2410 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2414 * MediaReportFrameCompletedClosure
2417 MediaReportFrameCompletedClosure::MediaReportFrameCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, MediaFrame *frame)
2418 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2420 this->frame = NULL;
2422 g_return_if_fail (context != NULL);
2423 g_return_if_fail (frame != NULL);
2425 this->frame = frame;
2426 this->frame->ref ();
2429 void
2430 MediaReportFrameCompletedClosure::Dispose ()
2432 if (frame) {
2433 frame->unref ();
2434 frame = NULL;
2437 MediaClosure::Dispose ();
2441 * IMediaStream
2444 IMediaStream::IMediaStream (Type::Kind kind, Media *media) : IMediaObject (kind, media)
2446 context = NULL;
2448 extra_data_size = 0;
2449 extra_data = NULL;
2451 duration = 0;
2453 decoder = NULL;
2454 codec_id = 0;
2455 codec = NULL;
2457 min_padding = 0;
2458 index = -1;
2459 selected = false;
2460 input_ended = false;
2461 output_ended = false;
2463 first_pts = G_MAXUINT64; // The first pts in the stream, initialized to G_MAXUINT64
2464 last_popped_pts = G_MAXUINT64; // The pts of the last frame returned, initialized to G_MAXUINT64
2465 last_enqueued_pts = G_MAXUINT64; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2466 last_available_pts = 0; // The pts of the last available frame, initialized to 0
2469 void
2470 IMediaStream::Dispose ()
2472 if (decoder) {
2473 IMediaDecoder *d = decoder;
2474 decoder = NULL;
2475 d->Dispose ();
2476 d->unref ();
2478 g_free (extra_data);
2479 extra_data = NULL;
2480 g_free (codec);
2481 codec = NULL;
2483 ClearQueue ();
2484 IMediaObject::Dispose ();
2487 char *
2488 IMediaStream::CreateCodec (int codec_id)
2490 switch (codec_id) {
2491 case CODEC_WMV1: return g_strdup ("wmv1");
2492 case CODEC_WMV2: return g_strdup ("wmv2");
2493 case CODEC_WMV3: return g_strdup ("wmv3");
2494 case CODEC_WMVA: return g_strdup ("wmva");
2495 case CODEC_WVC1: return g_strdup ("vc1");
2496 case CODEC_RGBA: return g_strdup ("rgba");
2497 case CODEC_YV12: return g_strdup ("yv12");
2498 case CODEC_MP3: return g_strdup ("mp3");
2499 case CODEC_WMAV1: return g_strdup ("wmav1");
2500 case CODEC_WMAV2: return g_strdup ("wmav2");
2501 case CODEC_WMAV3: return g_strdup ("wmav3");
2502 case CODEC_PCM: return g_strdup ("pcm");
2503 default:
2504 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id);
2506 /* This algorithm needs testing.
2507 char *result;
2508 int size, current;
2509 int a = (codec_id & 0x000000FF);
2510 int b = (codec_id & 0x0000FF00) >> 8;
2511 int c = (codec_id & 0x00FF0000) >> 16;
2512 int d = (codec_id & 0xFF000000) >> 24;
2514 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2516 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2518 result = (char *) g_malloc (size + 1);
2519 current = 0;
2520 if (a)
2521 result [current++] = (char) a;
2522 if (b)
2523 result [current++] = (char) b;
2524 if (c)
2525 result [current++] = (char) c;
2526 if (d)
2527 result [current++] = (char) d;
2528 result [current] = 0;
2530 return g_strdup ("<unknown>");
2535 bool
2536 IMediaStream::IsQueueEmpty ()
2538 return queue.IsEmpty ();
2541 const char *
2542 IMediaStream::GetStreamTypeName ()
2544 switch (GetType ()) {
2545 case MediaTypeVideo: return "Video";
2546 case MediaTypeAudio: return "Audio";
2547 case MediaTypeMarker: return "Marker";
2548 default: return "Unknown";
2552 void
2553 IMediaStream::ReportSeekCompleted ()
2555 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2556 input_ended = false;
2557 output_ended = false;
2558 ClearQueue ();
2559 if (decoder != NULL)
2560 decoder->ReportSeekCompleted ();
2563 IMediaDemuxer *
2564 IMediaStream::GetDemuxer ()
2566 Media *media;
2567 IMediaDemuxer *result;
2569 if (IsDisposed ())
2570 return NULL;
2572 media = GetMediaReffed ();
2574 g_return_val_if_fail (media != NULL, NULL);
2576 result = media->GetDemuxer ();
2578 media->unref ();
2580 return result;
2583 IMediaDecoder *
2584 IMediaStream::GetDecoder ()
2586 return decoder;
2589 void
2590 IMediaStream::SetDecoder (IMediaDecoder *value)
2592 if (decoder)
2593 decoder->unref ();
2594 decoder = value;
2595 if (decoder)
2596 decoder->ref ();
2599 bool
2600 IMediaStream::GetOutputEnded ()
2602 return output_ended;
2605 void
2606 IMediaStream::SetOutputEnded (bool value)
2608 output_ended = value;
2611 bool
2612 IMediaStream::GetInputEnded ()
2614 return input_ended;
2617 void
2618 IMediaStream::SetInputEnded (bool value)
2620 input_ended = value;
2621 if (GetDecoder () != NULL)
2622 GetDecoder ()->ReportInputEnded ();
2625 guint64
2626 IMediaStream::GetBufferedSize ()
2628 guint64 result;
2630 queue.Lock ();
2631 if (first_pts == G_MAXUINT64 || last_enqueued_pts == G_MAXUINT64)
2632 result = 0;
2633 else if (last_popped_pts == G_MAXUINT64)
2634 result = last_enqueued_pts - first_pts;
2635 else
2636 result = last_enqueued_pts - last_popped_pts;
2637 queue.Unlock ();
2639 LOG_BUFFERING ("IMediaStream::GetBufferedSize (): id: %i, codec: %s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, result: %" G_GUINT64_FORMAT " ms\n",
2640 GET_OBJ_ID (this), codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts), MilliSeconds_FromPts (result));
2642 return result;
2646 #if DEBUG
2647 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2649 void
2650 IMediaStream::PrintBufferInformation ()
2652 guint64 buffer_size = GetBufferedSize ();
2654 printf (" <%s: ", codec);
2656 if (GetSelected ()) {
2657 printf ("size: %.4" G_GINT64_FORMAT ", first: %.4" G_GINT64_FORMAT ", last popped: %.4" G_GINT64_FORMAT ", last enq: %.4" G_GINT64_FORMAT ", frames enq: %i>",
2658 TO_MS (buffer_size), TO_MS (first_pts), TO_MS (last_popped_pts),
2659 TO_MS (last_enqueued_pts), queue.Length ());
2660 } else {
2661 printf ("(not selected) >");
2664 #endif
2666 void
2667 IMediaStream::EnqueueFrame (MediaFrame *frame)
2669 bool first = false;
2670 Media *media;
2672 g_return_if_fail (Media::InMediaThread ());
2674 media = GetMediaReffed ();
2675 g_return_if_fail (media != NULL);
2677 if (media->IsStopped ()) {
2678 /* We need to enqueue one frame so that we can render the first frame for a stopped media element */
2679 if (first_pts != G_MAXUINT64) {
2680 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame (we already have at least one frame).\n", frame);
2681 goto cleanup;
2682 } else {
2683 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, but enqueing since we're empty.\n", frame);
2687 if (frame->buffer == NULL) {
2688 /* for some reason there is no output from the decoder, possibly because it needs more data from the demuxer before outputting anything */
2689 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No data in frame, not storing it.\n", frame);
2690 goto cleanup;
2693 queue.Lock ();
2694 if (first_pts == G_MAXUINT64)
2695 first_pts = frame->pts;
2697 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
2699 #if 0
2700 if (last_enqueued_pts > frame->pts && last_enqueued_pts != G_MAXUINT64 && frame->event != FrameEventEOF && frame->buflen > 0) {
2701 g_warning ("IMediaStream::EnqueueFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, "
2702 "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",
2703 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2704 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), frame, frame->buflen, MilliSeconds_FromPts (frame->pts));
2706 #endif
2708 last_enqueued_pts = frame->pts;
2709 first = queue.LinkedList ()->Length () == 0;
2710 queue.LinkedList ()->Append (new StreamNode (frame));
2711 queue.Unlock ();
2713 SetLastAvailablePts (frame->pts);
2715 if (first)
2716 EmitSafe (FirstFrameEnqueuedEvent);
2718 FrameEnqueued ();
2720 cleanup:
2721 media->unref ();
2723 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",
2724 codec, first, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2725 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts - first_pts), frame, frame->buflen);
2728 MediaFrame *
2729 IMediaStream::PopFrame ()
2731 MediaFrame *result = NULL;
2732 StreamNode *node = NULL;
2734 // We use the queue lock to synchronize access to
2735 // last_popped_pts/last_enqueued_pts/first_pts
2737 queue.Lock ();
2738 node = (StreamNode *) queue.LinkedList ()->First ();
2739 if (node != NULL) {
2740 result = node->GetFrame ();
2741 result->ref ();
2742 queue.LinkedList ()->Remove (node);
2743 last_popped_pts = result->pts;
2745 queue.Unlock ();
2747 LOG_BUFFERING ("IMediaStream::PopFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT " ms, last_popped_pts: %" G_GUINT64_FORMAT " ms, last_enqueued_pts: %" G_GUINT64_FORMAT " ms, buffer: %" G_GUINT64_FORMAT " ms, frame: %p, frame->buflen: %i\n",
2748 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2749 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), result, result ? result->buflen : 0);
2751 if (!input_ended && !output_ended && result != NULL) {
2752 IMediaDemuxer *demuxer = GetDemuxer ();
2753 if (demuxer != NULL)
2754 demuxer->FillBuffers ();
2757 return result;
2760 void
2761 IMediaStream::ClearQueue ()
2763 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2764 queue.Lock ();
2765 queue.LinkedList ()->Clear (true);
2766 first_pts = G_MAXUINT64;
2767 last_popped_pts = G_MAXUINT64;
2768 last_enqueued_pts = G_MAXUINT64;
2769 queue.Unlock ();
2772 void
2773 IMediaStream::SetSelected (bool value)
2775 Media *media = GetMediaReffed ();
2777 selected = value;
2778 if (media) {
2779 if (media->GetDemuxer ())
2780 media->GetDemuxer ()->UpdateSelected (this);
2781 media->unref ();
2786 * IMediaStream.StreamNode
2789 IMediaStream::StreamNode::StreamNode (MediaFrame *f)
2791 frame = f;
2792 frame->ref ();
2795 IMediaStream::StreamNode::~StreamNode ()
2797 frame->unref ();
2798 frame = NULL;
2801 * IMediaDemuxer
2804 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media, IMediaSource *source) : IMediaObject (kind, media)
2806 this->source = source;
2807 this->source->ref ();
2808 stream_count = 0;
2809 streams = NULL;
2810 opened = false;
2811 opening = false;
2812 seeking = false;
2813 pending_stream = NULL;
2814 pending_fill_buffers = false;
2817 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media)
2818 : IMediaObject (kind, media)
2820 source = NULL;
2821 stream_count = 0;
2822 streams = NULL;
2823 opened = false;
2824 opening = false;
2825 seeking = false;
2826 pending_stream = NULL;
2827 pending_fill_buffers = false;
2830 void
2831 IMediaDemuxer::Dispose ()
2833 if (streams != NULL) {
2834 IMediaStream **tmp = streams;
2835 int stream_count = this->stream_count;
2836 streams = NULL;
2837 for (int i = 0; i < stream_count; i++) {
2838 tmp [i]->Dispose ();
2839 tmp [i]->unref ();
2841 g_free (tmp);
2843 if (source) {
2844 source->unref ();
2845 source = NULL;
2847 if (pending_stream != NULL) {
2848 pending_stream->unref ();
2849 pending_stream = NULL;
2851 opened = false;
2852 IMediaObject::Dispose ();
2855 MediaResult
2856 IMediaDemuxer::OpenCallback (MediaClosure *closure)
2858 IMediaDemuxer *demuxer;
2860 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure);
2862 demuxer = (IMediaDemuxer *) closure->GetContext ();
2863 demuxer->OpenDemuxerAsync ();
2865 return MEDIA_SUCCESS;
2868 void
2869 IMediaDemuxer::EnqueueOpen ()
2871 MediaClosure *closure;
2872 Media *media = GetMediaReffed ();
2874 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2876 if (media == NULL)
2877 return;
2879 closure = new MediaClosure (media, OpenCallback, this, "IMediaDemuxer::OpenCallback");
2880 media->EnqueueWork (closure, false);
2881 closure->unref ();
2882 media->unref ();
2885 void
2886 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2888 Media *media = GetMediaReffed ();
2890 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media);
2892 opened = true;
2893 opening = false;
2895 // Media might be null if we got disposed for some reason.
2896 if (!media)
2897 return;
2899 media->ReportOpenDemuxerCompleted ();
2900 media->unref ();
2903 void
2904 IMediaDemuxer::ReportGetFrameProgress (double progress)
2906 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress);
2909 void
2910 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream *stream)
2912 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream);
2915 void
2916 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind, gint64 value)
2918 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %" G_GINT64_FORMAT ")\n", kind, value);
2921 void
2922 IMediaDemuxer::EnqueueReportGetFrameCompleted (MediaFrame *frame)
2924 Media *media = GetMediaReffed ();
2926 if (media == NULL)
2927 return;
2929 MediaClosure *closure = new MediaReportFrameCompletedClosure (media, ReportGetFrameCompletedCallback, this, frame);
2930 media->EnqueueWork (closure);
2931 closure->unref ();
2932 media->unref ();
2935 MediaResult
2936 IMediaDemuxer::ReportGetFrameCompletedCallback (MediaClosure *closure)
2938 MediaReportFrameCompletedClosure *c = (MediaReportFrameCompletedClosure *) closure;
2940 g_return_val_if_fail (c != NULL, MEDIA_FAIL);
2941 g_return_val_if_fail (c->GetDemuxer () != NULL, MEDIA_FAIL);
2943 c->GetDemuxer ()->ReportGetFrameCompleted (c->GetFrame ());
2945 return MEDIA_SUCCESS;
2948 void
2949 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame *frame)
2951 Media *media;
2953 g_return_if_fail (frame == NULL || (frame != NULL && frame->stream != NULL));
2954 g_return_if_fail (pending_stream != NULL);
2956 media = GetMediaReffed ();
2958 g_return_if_fail (media != NULL);
2960 /* ensure we're on a media thread */
2961 if (!Media::InMediaThread ()) {
2962 EnqueueReportGetFrameCompleted (frame);
2963 goto cleanup;
2966 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p) %i %s %" G_GUINT64_FORMAT " ms\n", frame, GET_OBJ_ID (this), frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : (guint64) -1);
2968 if (frame == NULL) {
2969 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame, pending_stream->GetStreamTypeName ());
2970 // No more data for this stream
2971 pending_stream->SetInputEnded (true);
2972 } else if (!frame->stream->IsDisposed ()) {
2973 IMediaDecoder *decoder = frame->stream->GetDecoder ();
2974 if (decoder != NULL)
2975 decoder->DecodeFrameAsync (frame, true /* always enqueue */);
2978 pending_stream->unref ();
2979 pending_stream = NULL; // not waiting for anything more
2981 // enqueue some more
2982 FillBuffers ();
2984 cleanup:
2985 if (media)
2986 media->unref ();
2989 MediaResult
2990 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure *c)
2992 MediaReportSeekCompletedClosure *closure = (MediaReportSeekCompletedClosure *) c;
2993 IMediaDemuxer *demuxer;
2995 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
2996 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
2998 demuxer = (IMediaDemuxer *) closure->GetContext ();
2999 demuxer->ReportSeekCompleted (closure->GetPts ());
3001 return MEDIA_SUCCESS;
3004 void
3005 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts)
3007 Media *media = GetMediaReffed ();
3009 if (media == NULL)
3010 return;
3012 MediaClosure *closure = new MediaReportSeekCompletedClosure (media, ReportSeekCompletedCallback, this, pts);
3013 media->EnqueueWork (closure);
3014 closure->unref ();
3015 media->unref ();
3018 void
3019 IMediaDemuxer::ReportSeekCompleted (guint64 pts)
3021 Media *media;
3023 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT ")\n", pts);
3025 g_return_if_fail (seeking);
3027 if (!Media::InMediaThread ()) {
3028 EnqueueReportSeekCompleted (pts);
3029 return;
3032 #if SANITY
3033 if (pending_stream != NULL)
3034 printf ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT "): we can't be waiting for a frame now.\n", pts);
3035 #endif
3037 media = GetMediaReffed ();
3039 g_return_if_fail (media != NULL);
3041 /* We need to call ReportSeekCompleted once for every time SeekAsync(pts) was called */
3042 for (int i = 0; i < GetStreamCount (); i++) {
3043 IMediaStream *stream = GetStream (i);
3045 if (stream == NULL)
3046 continue;
3048 stream->ReportSeekCompleted ();
3051 media->ReportSeekCompleted (pts);
3052 media->unref ();
3054 mutex.Lock ();
3055 seeks.RemoveAt (0);
3056 seeking = !seeks.IsEmpty ();
3057 mutex.Unlock ();
3059 if (!seeking) {
3060 pending_fill_buffers = false;
3061 FillBuffers ();
3062 } else {
3063 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT "): still pending seeks, enqueuing another seek.\n", pts);
3064 EnqueueSeek ();
3067 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT ") [Done]\n", pts);
3070 void
3071 IMediaDemuxer::OpenDemuxerAsync ()
3073 g_return_if_fail (opened == false);
3075 opening = true;
3076 opened = false;
3077 OpenDemuxerAsyncInternal ();
3080 MediaResult
3081 IMediaDemuxer::GetFrameCallback (MediaClosure *c)
3083 MediaGetFrameClosure *closure = (MediaGetFrameClosure *) c;
3084 IMediaDemuxer *demuxer;
3086 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
3087 g_return_val_if_fail (closure->GetStream () != NULL, MEDIA_FAIL);
3088 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
3090 demuxer = (IMediaDemuxer *) closure->GetContext ();
3091 demuxer->GetFrameAsync (closure->GetStream ());
3093 return MEDIA_SUCCESS;
3096 void
3097 IMediaDemuxer::EnqueueGetFrame (IMediaStream *stream)
3099 g_return_if_fail (pending_stream == NULL); // we can't be waiting for another frame.
3101 Media *media = GetMediaReffed ();
3103 if (media == NULL)
3104 return;
3106 MediaClosure *closure = new MediaGetFrameClosure (media, GetFrameCallback, this, stream);
3107 media->EnqueueWork (closure);
3108 closure->unref ();
3109 media->unref ();
3112 void
3113 IMediaDemuxer::GetFrameAsync (IMediaStream *stream)
3115 Media *media = NULL;
3117 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream, stream->GetStreamTypeName (), Media::InMediaThread ());
3119 if (!Media::InMediaThread ()) {
3120 EnqueueGetFrame (stream);
3121 return;
3124 if (seeking) {
3125 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (): delayed since we're waiting for a seek.\n");
3126 goto cleanup;
3129 if (pending_stream != NULL) {
3130 /* we're already waiting for a frame */
3131 goto cleanup;
3134 media = GetMediaReffed ();
3136 g_return_if_fail (media != NULL);
3138 if (stream != NULL) {
3139 pending_stream = stream;
3140 pending_stream->ref ();
3141 GetFrameAsyncInternal (stream);
3144 cleanup:
3145 if (media)
3146 media->unref ();
3149 MediaResult
3150 IMediaDemuxer::SeekCallback (MediaClosure *closure)
3152 MediaSeekClosure *seek = (MediaSeekClosure *) closure;
3153 seek->GetDemuxer ()->SeekAsync ();
3154 return MEDIA_SUCCESS;
3157 void
3158 IMediaDemuxer::EnqueueSeek ()
3160 Media *media = GetMediaReffed ();
3161 MediaSeekClosure *closure;
3163 g_return_if_fail (media != NULL);
3165 closure = new MediaSeekClosure (media, SeekCallback, this, 0);
3166 media->EnqueueWork (closure, true);
3167 closure->unref ();
3168 media->unref ();
3171 void
3172 IMediaDemuxer::SeekAsync ()
3174 guint64 pts = G_MAXUINT64;
3176 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (), seeking: %i\n", seeking);
3178 g_return_if_fail (Media::InMediaThread ());
3180 seeking = true; /* this ensures that we stop demuxing frames asap */
3182 if (pending_stream != NULL) {
3183 /* we're waiting for the decoder to decode a frame, wait a bit with the seek */
3184 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i waiting for a frame, postponing seek\n", GET_OBJ_ID (this));
3185 EnqueueSeek ();
3186 return;
3189 mutex.Lock ();
3190 if (!seeks.IsEmpty ())
3191 pts = ((PtsNode *) seeks.First ())->pts;
3192 mutex.Unlock ();
3194 if (pts == G_MAXUINT64) {
3195 LOG_PIPELINE ("IMediaDemuxer.:SeekAsync (): %i no pending seek?\n", GET_OBJ_ID (this));
3196 seeking = false;
3197 return;
3200 /* Ask the demuxer to seek */
3201 /* at this point the pipeline shouldn't be doing anything else (for this media) */
3202 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i seeking to %" G_GUINT64_FORMAT "\n", GET_OBJ_ID (this), pts);
3203 SeekAsyncInternal (pts);
3206 void
3207 IMediaDemuxer::SeekAsync (guint64 pts)
3209 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%" G_GUINT64_FORMAT ")\n", pts);
3210 VERIFY_MAIN_THREAD;
3212 if (IsDisposed ())
3213 return;
3215 mutex.Lock ();
3216 seeks.Append (new PtsNode (pts));
3217 mutex.Unlock ();
3219 EnqueueSeek ();
3222 void
3223 IMediaDemuxer::ClearBuffers ()
3225 pending_fill_buffers = false;
3227 /* Clear all the buffer queues */
3228 for (int i = 0; i < GetStreamCount (); i++) {
3229 IMediaStream *stream = GetStream (i);
3231 if (stream == NULL)
3232 continue;
3234 stream->ClearQueue ();
3238 MediaResult
3239 IMediaDemuxer::FillBuffersCallback (MediaClosure *closure)
3241 IMediaDemuxer *demuxer = (IMediaDemuxer *) closure->GetContext ();
3242 demuxer->FillBuffersInternal ();
3243 return MEDIA_SUCCESS;
3246 void
3247 IMediaDemuxer::FillBuffers ()
3249 Media *media = NULL;
3250 MediaClosure *closure;
3251 bool enqueue = true;
3253 mutex.Lock ();
3254 if (pending_fill_buffers) {
3255 // there's already a FillBuffers request enqueued
3256 enqueue = false;
3257 } else {
3258 media = GetMediaReffed ();
3259 if (media == NULL) {
3260 enqueue = false;
3261 } else {
3262 enqueue = true;
3263 pending_fill_buffers = true;
3266 mutex.Unlock ();
3268 if (enqueue) {
3269 closure = new MediaClosure (media, FillBuffersCallback, this, "IMediaDemuxer::FillBuffersCallback");
3270 media->EnqueueWork (closure);
3271 closure->unref ();
3274 if (media != NULL)
3275 media->unref ();
3278 void
3279 IMediaDemuxer::FillBuffersInternal ()
3281 IMediaStream *stream;
3282 IMediaStream *request_stream = NULL;
3283 guint64 min_buffered_size = G_MAXUINT64;
3284 Media *media = GetMediaReffed ();
3285 guint64 buffering_time = 0;
3286 guint64 buffered_size = 0;
3287 int ended = 0;
3288 int media_streams = 0;
3290 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (), %i %s buffering time: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, pending_stream: %i %s\n", GET_OBJ_ID (this), GetTypeName (), buffering_time, media != NULL ? MilliSeconds_FromPts (media->GetBufferingTime ()) : -1, GET_OBJ_ID (pending_stream), pending_stream ? pending_stream->GetStreamTypeName () : "NULL");
3292 mutex.Lock ();
3293 pending_fill_buffers = false;
3294 mutex.Unlock ();
3296 if (IsDisposed ())
3297 goto cleanup;
3299 // If we're waiting for something, there's nothing to do here.
3300 if (pending_stream != NULL)
3301 goto cleanup;
3303 // Find the stream with the smallest buffered size, and request a frame from that stream.
3304 g_return_if_fail (media != NULL);
3306 buffering_time = media->GetBufferingTime ();
3308 if (buffering_time == 0) {
3309 // Play as soon as possible.
3310 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3311 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3312 // all streams have 1 frame queued.
3313 buffering_time = 1;
3316 for (int i = 0; i < GetStreamCount (); i++) {
3317 IMediaDecoder *decoder = NULL;
3319 stream = GetStream (i);
3320 if (!stream->GetSelected ())
3321 continue;
3323 if (stream->GetType () != MediaTypeVideo &&
3324 stream->GetType () != MediaTypeAudio)
3325 continue;
3327 media_streams++;
3328 if (stream->GetOutputEnded ()) {
3329 ended++;
3330 continue; // this stream has ended.
3333 decoder = stream->GetDecoder ();
3334 if (decoder == NULL) {
3335 fprintf (stderr, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream->GetStreamTypeName (), GET_OBJ_ID (stream), stream->GetRefCount ());
3336 continue; // no decoder??
3339 buffered_size = stream->GetBufferedSize ();
3340 min_buffered_size = MIN (min_buffered_size, buffered_size);
3342 if (buffered_size >= buffering_time)
3343 continue; // this stream has enough data buffered.
3345 if (!decoder->IsDecoderQueueEmpty ())
3346 continue; // this stream is waiting for data to be decoded.
3348 if (buffered_size <= min_buffered_size)
3349 request_stream = stream;
3351 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): codec: %s, stream id: %i, buffered size: %" G_GUINT64_FORMAT " ms, buffering time: %" G_GUINT64_FORMAT " ms, last popped time: %" G_GUINT64_FORMAT " ms\n",
3352 stream->codec, GET_OBJ_ID (stream), MilliSeconds_FromPts (buffered_size), MilliSeconds_FromPts (buffering_time), MilliSeconds_FromPts (stream->GetLastPoppedPts ()));
3355 if (request_stream != NULL) {
3356 if (media->IsStopped ()) {
3357 if (!request_stream->IsQueueEmpty ()) {
3358 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, and we have frames in the buffer.\n");
3359 goto cleanup;
3360 } else {
3361 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, but the buffer is empty, continuing\n");
3365 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));
3366 GetFrameAsync (request_stream);
3369 if (media_streams > 0) {
3370 if (ended == media_streams) {
3371 media->ReportBufferingProgress (1.0);
3372 } else {
3373 if (min_buffered_size > 0 && buffering_time > 0) {
3374 double progress = ((double) min_buffered_size / (double) buffering_time);
3375 media->ReportBufferingProgress (progress);
3380 cleanup:
3381 if (media)
3382 media->unref ();
3384 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT " ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3387 guint64
3388 IMediaDemuxer::GetBufferedSize ()
3390 guint64 result = G_MAXUINT64;
3391 IMediaStream *stream;
3393 for (int i = 0; i < GetStreamCount (); i++) {
3394 stream = GetStream (i);
3395 if (!stream->GetSelected ())
3396 continue;
3398 if (stream->GetType () != MediaTypeVideo && stream->GetType () != MediaTypeAudio)
3399 continue;
3401 result = MIN (result, stream->GetBufferedSize ());
3404 return result;
3407 guint64
3408 IMediaDemuxer::GetLastAvailablePts ()
3410 guint64 result = G_MAXUINT64;
3411 IMediaStream *stream;
3413 for (int i = 0; i < GetStreamCount (); i++) {
3414 stream = GetStream (i);
3416 if (stream == NULL || !stream->GetSelected ())
3417 continue;
3419 result = MIN (result, stream->GetLastAvailablePts ());
3422 if (result == G_MAXUINT64)
3423 result = 0;
3425 return result;
3428 #if DEBUG
3429 void
3430 IMediaDemuxer::PrintBufferInformation ()
3432 printf ("Buffer: %" G_GINT64_FORMAT "", MilliSeconds_FromPts (GetBufferedSize ()));
3433 for (int i = 0; i < GetStreamCount (); i++) {
3434 GetStream (i)->PrintBufferInformation ();
3436 printf ("\n");
3438 #endif
3440 guint64
3441 IMediaDemuxer::GetDuration ()
3443 guint64 result = 0;
3444 for (int i = 0; i < GetStreamCount (); i++)
3445 result = MAX (result, GetStream (i)->duration);
3446 return result;
3449 IMediaStream*
3450 IMediaDemuxer::GetStream (int index)
3452 return (index < 0 || index >= stream_count) ? NULL : streams [index];
3456 * MediaFrame
3459 MediaFrame::MediaFrame (IMediaStream *stream)
3460 : EventObject (Type::MEDIAFRAME, true)
3462 Initialize ();
3464 g_return_if_fail (stream != NULL);
3466 this->stream = stream;
3467 this->stream->ref ();
3470 MediaFrame::MediaFrame (IMediaStream *stream, guint8 *buffer, guint32 buflen, guint64 pts, bool keyframe)
3471 : EventObject (Type::MEDIAFRAME, true)
3473 Initialize ();
3475 g_return_if_fail (stream != NULL);
3477 this->stream = stream;
3478 this->stream->ref ();
3479 this->buffer = buffer;
3480 this->buflen = buflen;
3481 this->pts = pts;
3483 #if 0
3484 if (buflen > 4 && false) {
3485 printf ("MediaFrame::MediaFrame () %s buffer: ", stream->GetStreamTypeName ());
3486 for (int i = 0; i < 4; i++)
3487 printf (" 0x%x", buffer [i]);
3488 printf ("\n");
3490 #endif
3492 if (keyframe)
3493 AddState (MediaFrameKeyFrame);
3496 void
3497 MediaFrame::Initialize ()
3499 decoder_specific_data = NULL;
3500 stream = NULL;
3501 marker = NULL;
3503 duration = 0;
3504 pts = 0;
3506 buffer = NULL;
3507 buflen = 0;
3508 state = 0;
3509 event = 0;
3511 for (int i = 0; i < 4; i++) {
3512 data_stride[i] = 0;
3513 srcStride[i] = 0;
3516 srcSlideY = 0;
3517 srcSlideH = 0;
3518 width = 0;
3519 height = 0;
3522 MediaFrame::~MediaFrame ()
3526 void
3527 MediaFrame::Dispose ()
3529 IMediaDecoder *decoder;
3531 #if SANITY
3532 // We can be called either on the main thread just before destruction
3533 // (in which case there are no races since the code which unreffed us
3534 // is the only code which knows about us), or at any time from the
3535 // media thread.
3537 if (GetRefCount () != 0 && stream != NULL) {
3538 if (!Media::InMediaThread ()) {
3539 // if refcount != 0 we're not being called just before destruction, in which case we should
3540 // only be on the media thread.
3541 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3544 #endif
3546 if (decoder_specific_data != NULL && stream != NULL) {
3547 decoder = stream->GetDecoder ();
3548 if (decoder != NULL)
3549 decoder->Cleanup (this);
3551 g_free (buffer);
3552 buffer = NULL;
3553 if (marker) {
3554 marker->unref ();
3555 marker = NULL;
3557 if (stream) {
3558 stream->unref ();
3559 stream = NULL;
3562 EventObject::Dispose ();
3565 void
3566 MediaFrame::SetSrcSlideY (int value)
3568 srcSlideY = value;
3571 void
3572 MediaFrame::SetSrcSlideH (int value)
3574 srcSlideH = value;
3577 void
3578 MediaFrame::SetSrcStride (int a, int b, int c, int d)
3580 srcStride [0] = a;
3581 srcStride [1] = b;
3582 srcStride [2] = c;
3583 srcStride [3] = d;
3586 void
3587 MediaFrame::SetDataStride (guint8* a, guint8* b, guint8* c, guint8* d)
3589 data_stride [0] = a;
3590 data_stride [1] = b;
3591 data_stride [2] = c;
3592 data_stride [3] = d;
3596 * IMediaObject.EventData
3599 IMediaObject::EventData::EventData (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3601 this->event_id = event_id;
3602 this->handler = handler;
3603 this->context = context;
3604 this->context->ref ();
3605 this->invoke_on_main_thread = invoke_on_main_thread;
3608 IMediaObject::EventData::~EventData ()
3610 context->unref ();
3611 context = NULL;
3615 * IMediaObject.EmitData
3618 IMediaObject::EmitData::EmitData (int event_id, EventHandler handler, EventObject *context, EventArgs *args)
3620 this->event_id = event_id;
3621 this->handler = handler;
3622 this->context = context;
3623 this->context->ref ();
3624 this->args = args;
3625 if (this->args)
3626 this->args->ref ();
3629 IMediaObject::EmitData::~EmitData ()
3631 context->unref ();
3632 context = NULL;
3633 if (args) {
3634 args->unref ();
3635 args = NULL;
3640 * IMediaObject
3643 IMediaObject::IMediaObject (Type::Kind kind, Media *media)
3644 : EventObject (kind, true)
3646 this->media = media;
3647 if (this->media)
3648 this->media->ref ();
3649 g_return_if_fail (media != NULL);
3650 events = NULL;
3651 emit_on_main_thread = NULL;
3654 void
3655 IMediaObject::Dispose ()
3658 #if SANITY
3659 // We can be called either on the main thread just before destruction
3660 // (in which case there are no races since the code which unreffed us
3661 // is the only code which knows about us), or at any time from the
3662 // media thread.
3663 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3664 // if refcount != 0 we're not being called just before destruction, in which case we should
3665 // only be on the media thread.
3666 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3668 #endif
3670 media_mutex.Lock ();
3671 if (media) {
3672 media->unref ();
3673 media = NULL;
3675 media_mutex.Unlock ();
3677 event_mutex.Lock ();
3678 delete events;
3679 events = NULL;
3680 if (emit_on_main_thread != NULL) {
3681 delete emit_on_main_thread;
3682 emit_on_main_thread = NULL;
3684 event_mutex.Unlock ();
3686 EventObject::Dispose ();
3689 void
3690 IMediaObject::AddSafeHandler (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3692 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id, handler, context, invoke_on_main_thread);
3693 EventData *ed;
3695 if (!IsDisposed ()) {
3696 ed = new EventData (event_id, handler, context, invoke_on_main_thread);
3697 event_mutex.Lock ();
3698 if (events == NULL)
3699 events = new List ();
3700 events->Append (ed);
3701 event_mutex.Unlock ();
3705 void
3706 IMediaObject::RemoveSafeHandlers (EventObject *context)
3708 EventData *ed;
3709 EventData *next;
3711 event_mutex.Lock ();
3712 if (events != NULL) {
3713 ed = (EventData *) events->First ();
3714 while (ed != NULL) {
3715 next = (EventData *) ed->next;
3716 if (ed->context == context)
3717 events->Remove (ed);
3718 ed = next;
3721 event_mutex.Unlock ();
3724 void
3725 IMediaObject::EmitSafe (int event_id, EventArgs *args)
3727 List *emits = NULL; // The events to emit on this thread.
3728 EventData *ed;
3729 EmitData *emit;
3731 if (events == NULL)
3732 goto cleanup;
3734 // Create a list of all the events to emit
3735 // don't keep the lock while emitting.
3736 event_mutex.Lock ();
3737 if (events != NULL) {
3738 ed = (EventData *) events->First ();
3739 while (ed != NULL) {
3740 if (ed->event_id == event_id) {
3741 emit = new EmitData (event_id, ed->handler, ed->context, args);
3742 if (ed->invoke_on_main_thread) {
3743 if (emit_on_main_thread == NULL)
3744 emit_on_main_thread = new List ();
3745 emit_on_main_thread->Append (emit);
3746 } else {
3747 if (emits == NULL)
3748 emits = new List ();
3749 emits->Append (emit);
3752 ed = (EventData *) ed->next;
3755 event_mutex.Unlock ();
3757 // emit the events to be emitted on this thread
3758 EmitList (emits);
3760 if (Surface::InMainThread ()) {
3761 // if we're already on the main thread,
3762 // we can the events to be emitted
3763 // on the main thread
3764 List *tmp;
3765 event_mutex.Lock ();
3766 tmp = emit_on_main_thread;
3767 emit_on_main_thread = NULL;
3768 event_mutex.Unlock ();
3769 EmitList (tmp);
3770 } else {
3771 AddTickCallSafe (EmitListCallback);
3774 cleanup:
3775 if (args)
3776 args->unref ();
3779 void
3780 IMediaObject::EmitListMain ()
3782 VERIFY_MAIN_THREAD;
3784 List *list;
3785 event_mutex.Lock ();
3786 list = emit_on_main_thread;
3787 emit_on_main_thread = NULL;
3788 event_mutex.Unlock ();
3789 EmitList (list);
3792 void
3793 IMediaObject::EmitListCallback (EventObject *obj)
3795 IMediaObject *media_obj = (IMediaObject *) obj;
3796 media_obj->EmitListMain ();
3799 void
3800 IMediaObject::EmitList (List *list)
3802 EmitData *emit;
3804 if (list == NULL)
3805 return;
3807 emit = (EmitData *) list->First ();
3808 while (emit != NULL) {
3809 emit->handler (this, emit->args, emit->context);
3810 emit = (EmitData *) emit->next;
3813 delete list;
3816 Media *
3817 IMediaObject::GetMediaReffed ()
3819 Media *result;
3820 media_mutex.Lock ();
3821 result = media;
3822 if (result)
3823 result->ref ();
3824 media_mutex.Unlock ();
3825 return result;
3828 void
3829 IMediaObject::ReportErrorOccurred (char const *message)
3831 g_return_if_fail (media != NULL);
3833 media->ReportErrorOccurred (message);
3836 void
3837 IMediaObject::ReportErrorOccurred (MediaResult result)
3839 g_return_if_fail (media != NULL);
3841 media->ReportErrorOccurred (result);
3844 void
3845 IMediaObject::ReportErrorOccurred (ErrorEventArgs *args)
3847 g_return_if_fail (media != NULL);
3849 media->ReportErrorOccurred (args);
3852 void
3853 IMediaObject::SetMedia (Media *value)
3855 media_mutex.Lock ();
3856 if (media)
3857 media->unref ();
3858 media = value;
3859 if (media)
3860 media->ref ();
3861 media_mutex.Unlock ();
3865 * IMediaSource
3868 IMediaSource::IMediaSource (Type::Kind kind, Media *media)
3869 : IMediaObject (kind, media)
3871 pthread_mutexattr_t attribs;
3872 pthread_mutexattr_init (&attribs);
3873 pthread_mutexattr_settype (&attribs, PTHREAD_MUTEX_RECURSIVE);
3874 pthread_mutex_init (&mutex, &attribs);
3875 pthread_mutexattr_destroy (&attribs);
3877 pthread_cond_init (&condition, NULL);
3880 IMediaSource::~IMediaSource ()
3882 pthread_mutex_destroy (&mutex);
3883 pthread_cond_destroy (&condition);
3886 void
3887 IMediaSource::Dispose ()
3889 IMediaObject::Dispose ();
3892 void
3893 IMediaSource::Lock ()
3895 pthread_mutex_lock (&mutex);
3898 void
3899 IMediaSource::Unlock ()
3901 pthread_mutex_unlock (&mutex);
3904 gint32
3905 IMediaSource::ReadSome (void *buf, guint32 n)
3907 gint32 result;
3909 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf, n);
3911 Lock ();
3913 result = ReadInternal (buf, n);
3915 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %" G_GINT64_FORMAT "\n", GET_OBJ_ID (this), buf, n, result, GetPosition ());
3917 Unlock ();
3919 return result;
3922 bool
3923 IMediaSource::ReadAll (void *buf, guint32 n)
3925 gint32 read;
3926 gint64 prev = GetPosition ();
3927 gint64 avail = GetLastAvailablePosition ();
3929 //printf ("IMediaSource::ReadAll (%p, %u), position: %" G_GINT64_FORMAT "\n", buf, n, prev);
3931 read = ReadSome (buf, n);
3933 if ((gint64) read != (gint64) n) {
3934 FileSource *fs = NULL;
3936 if (GetType () == MediaSourceTypeFile)
3937 fs = (FileSource *) this;
3938 g_warning ("IMediaSource::ReadInternal (%i): Read failed, read %i bytes. available size: %" G_GINT64_FORMAT ", size: %" G_GINT64_FORMAT ", pos: %" G_GINT64_FORMAT ", prev pos: %" G_GINT64_FORMAT ", position not available: %" G_GINT64_FORMAT ", feof: %i, ferror: %i, strerror: %s\n",
3939 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>");
3940 print_stack_trace ();
3943 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf, n, read);
3945 return (gint64) read == (gint64) n;
3948 bool
3949 IMediaSource::Peek (void *buf, guint32 n)
3951 bool result;
3952 gint64 read;
3954 Lock ();
3956 read = PeekInternal (buf, n);
3957 result = read == (gint64) n;
3959 Unlock ();
3961 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %" G_GINT64_FORMAT " bytes.\n", buf, n, result, read);
3963 return result;
3966 bool
3967 IMediaSource::Seek (gint64 offset, int mode)
3969 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%" G_GINT64_FORMAT ", %d = %s)\n",
3970 GET_OBJ_ID (this), ToString (), offset, mode, mode == SEEK_SET ? "SEEK_SET"
3971 : (mode == SEEK_CUR ? "SEEK_CUR" : (mode == SEEK_END ? "SEEK_END" : "<invalid value>")));
3973 bool result;
3974 Lock ();
3975 result = SeekInternal (offset, mode);
3976 Unlock ();
3977 return result;
3980 bool
3981 IMediaSource::IsPositionAvailable (gint64 position, bool *eof)
3983 gint64 available = GetLastAvailablePosition ();
3984 gint64 size = GetSize ();
3986 *eof = false;
3988 if (size != -1 && size < position) {
3989 // Size is known and smaller than the requested position
3990 *eof = true;
3991 return false;
3994 if (available != -1 && available < position) {
3995 // Not everything is available and the available position is smaller than the requested position
3996 *eof = false;
3997 return false;
4000 if (size == -1 && available == -1) {
4001 // Size is not known, but everything is available??
4002 // This is probably due to a bug in the derived *Source class
4003 *eof = false;
4004 fprintf (stderr, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
4005 return false;
4008 return true;
4011 gint64
4012 IMediaSource::GetLastAvailablePosition ()
4014 gint64 result;
4015 Lock ();
4016 result = GetLastAvailablePositionInternal ();
4017 Unlock ();
4018 return result;
4021 gint64
4022 IMediaSource::GetPositionInternal ()
4024 // This method should be overridden (or never called for the classes which doesn't override it).
4025 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4026 print_stack_trace ();
4028 return -1;
4030 bool
4031 IMediaSource::SeekInternal (gint64 offset, int mode)
4033 g_warning ("IMediaSource (%s)::SeekInternal (%" G_GINT64_FORMAT ", %i): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), offset, mode);
4034 print_stack_trace ();
4036 return false;
4039 gint32
4040 IMediaSource::ReadInternal (void *buffer, guint32 n)
4042 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);
4043 print_stack_trace ();
4045 return 0;
4048 gint32
4049 IMediaSource::PeekInternal (void *buffer, guint32 n)
4051 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);
4052 print_stack_trace ();
4054 return 0;
4057 gint64
4058 IMediaSource::GetSizeInternal ()
4060 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4061 print_stack_trace ();
4063 return 0;
4066 gint64
4067 IMediaSource::GetPosition ()
4069 gint64 result;
4070 Lock ();
4071 result = GetPositionInternal ();
4072 Unlock ();
4073 return result;
4076 gint64
4077 IMediaSource::GetSize ()
4079 gint64 result;
4080 Lock ();
4081 result = GetSizeInternal ();
4082 Unlock ();
4083 return result;
4087 * IMediaDemuxer
4090 void
4091 IMediaDemuxer::SetStreams (IMediaStream** streams, int count)
4093 this->streams = streams;
4094 this->stream_count = count;
4096 for (int i = 0; i < count; i++)
4097 this->streams [i]->ref ();
4100 gint32
4101 IMediaDemuxer::AddStream (IMediaStream *stream)
4103 g_return_val_if_fail (stream != NULL, -1);
4105 stream_count++;
4106 streams = (IMediaStream **) g_realloc (streams, stream_count * sizeof (IMediaStream *));
4107 streams [stream_count - 1] = stream;
4108 stream->ref ();
4110 return stream_count - 1;
4114 * IMediaDecoder
4117 IMediaDecoder::IMediaDecoder (Type::Kind kind, Media *media, IMediaStream *stream) : IMediaObject (kind, media)
4119 this->stream = NULL;
4121 g_return_if_fail (stream != NULL);
4123 this->stream = stream;
4124 this->stream->ref ();
4126 opening = false;
4127 opened = false;
4128 input_ended = false;
4131 void
4132 IMediaDecoder::Dispose ()
4134 if (stream != NULL) {
4135 IMediaStream *s = stream;
4136 stream = NULL;
4137 s->Dispose ();
4138 s->unref ();
4139 s = NULL;
4142 queue.Clear (true);
4144 IMediaObject::Dispose ();
4147 void
4148 IMediaDecoder::ReportSeekCompleted ()
4150 queue.Clear (true);
4151 input_ended = false;
4152 CleanState ();
4155 void
4156 IMediaDecoder::ReportInputEnded ()
4158 input_ended = true;
4159 if (IsDecoderQueueEmpty ()) {
4160 InputEnded ();
4164 void
4165 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame *frame)
4167 IMediaDemuxer *demuxer;
4168 IMediaStream *stream;
4169 Media *media = NULL;
4171 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
4173 g_return_if_fail (frame != NULL);
4175 media = GetMediaReffed ();
4176 g_return_if_fail (media != NULL);
4178 stream = frame->stream;
4179 if (stream == NULL)
4180 goto cleanup;
4182 frame->stream->EnqueueFrame (frame);
4184 demuxer = stream->GetDemuxer ();
4185 if (demuxer != NULL)
4186 demuxer->FillBuffers ();
4188 if (input_ended && IsDecoderQueueEmpty ())
4189 InputEnded ();
4191 cleanup:
4192 if (media)
4193 media->unref ();
4196 MediaResult
4197 IMediaDecoder::DecodeFrameCallback (MediaClosure *closure)
4200 IMediaDecoder *decoder = (IMediaDecoder *) closure->GetContext ();
4201 IMediaDecoder::FrameNode *node = (IMediaDecoder::FrameNode *) decoder->queue.Pop ();
4203 if (node != NULL) {
4204 decoder->DecodeFrameAsync (node->frame, false);
4205 delete node;
4208 return MEDIA_SUCCESS;
4211 void
4212 IMediaDecoder::DecodeFrameAsync (MediaFrame *frame, bool enqueue_always)
4214 Media *media;
4216 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame, (frame && frame->stream) ? frame->stream->GetStreamTypeName () : NULL);
4218 if (IsDisposed ())
4219 return;
4221 g_return_if_fail (frame != NULL);
4223 media = GetMediaReffed ();
4225 g_return_if_fail (media != NULL);
4227 if (enqueue_always || !Media::InMediaThread ()) {
4228 MediaClosure *closure = new MediaClosure (media, DecodeFrameCallback, this, "IMediaDecoder::DecodeFrameCallback");
4229 queue.Push (new FrameNode (frame));
4230 media->EnqueueWork (closure);
4231 closure->unref ();
4232 goto cleanup;
4235 DecodeFrameAsyncInternal (frame);
4237 cleanup:
4238 media->unref ();
4241 void
4242 IMediaDecoder::OpenDecoderAsync ()
4244 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4246 g_return_if_fail (opening == false);
4247 g_return_if_fail (opened == false);
4249 opening = true;
4250 OpenDecoderAsyncInternal ();
4253 void
4254 IMediaDecoder::ReportOpenDecoderCompleted ()
4256 Media *media = GetMediaReffed ();
4258 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4260 opening = false;
4261 opened = true;
4263 g_return_if_fail (media != NULL);
4265 media->ReportOpenDecoderCompleted (this);
4266 media->unref ();
4270 * IImageConverter
4273 IImageConverter::IImageConverter (Type::Kind kind, Media *media, VideoStream *stream) : IMediaObject (kind, media)
4275 output_format = MoonPixelFormatNone;
4276 input_format = MoonPixelFormatNone;
4277 this->stream = stream;
4281 * VideoStream
4284 VideoStream::VideoStream (Media *media) : IMediaStream (Type::VIDEOSTREAM, media)
4286 converter = NULL;
4287 bits_per_sample = 0;
4288 pts_per_frame = 0;
4289 initial_pts = 0;
4290 height = 0;
4291 width = 0;
4294 VideoStream::VideoStream (Media *media, int codec_id, guint32 width, guint32 height, guint64 duration, gpointer extra_data, guint32 extra_data_size)
4295 : IMediaStream (Type::VIDEOSTREAM, media)
4297 converter = NULL;
4298 bits_per_sample = 0;
4299 pts_per_frame = 0;
4300 initial_pts = 0;
4301 this->height = height;
4302 this->width = width;
4303 this->duration = duration;
4304 this->codec_id = codec_id;
4305 this->codec = CreateCodec (codec_id);
4306 this->extra_data = extra_data;
4307 this->extra_data_size = extra_data_size;
4310 VideoStream::~VideoStream ()
4314 void
4315 VideoStream::Dispose ()
4317 if (converter) {
4318 converter->Dispose ();
4319 converter->unref ();
4320 converter = NULL;
4322 IMediaStream::Dispose ();
4326 * MediaMarkerFoundClosure
4329 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media *media, MediaCallback *callback, MediaElement *context)
4330 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE, media, callback, context)
4332 marker = NULL;
4335 void
4336 MediaMarkerFoundClosure::Dispose ()
4338 if (marker) {
4339 marker->unref ();
4340 marker = NULL;
4342 MediaClosure::Dispose ();
4345 void
4346 MediaMarkerFoundClosure::SetMarker (MediaMarker *marker)
4348 if (this->marker)
4349 this->marker->unref ();
4350 this->marker = marker;
4351 if (this->marker)
4352 this->marker->ref ();
4356 * MediaMarker
4359 MediaMarker::MediaMarker (const char *type, const char *text, guint64 pts)
4360 : EventObject (Type::MEDIAMARKER)
4362 this->type = g_strdup (type);
4363 this->text = g_strdup (text);
4364 this->pts = pts;
4367 MediaMarker::~MediaMarker ()
4369 g_free (type);
4370 g_free (text);
4374 * MarkerStream
4377 MarkerStream::MarkerStream (Media *media) : IMediaStream (Type::MARKERSTREAM, media)
4379 closure = NULL;
4382 void
4383 MarkerStream::Dispose ()
4385 if (closure) {
4386 closure->unref ();
4387 closure = NULL;
4390 IMediaStream::Dispose ();
4393 void
4394 MarkerStream::MarkerFound (MediaFrame *frame)
4396 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4398 if (GetDecoder () == NULL) {
4399 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4400 return;
4403 GetDecoder ()->DecodeFrameAsync (frame, false);
4406 void
4407 MarkerStream::FrameEnqueued ()
4409 MediaFrame *frame;
4411 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4413 frame = PopFrame ();
4415 if (frame == NULL) {
4416 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4417 return;
4420 if (closure != NULL) {
4421 closure->SetMarker (frame->marker);
4422 closure->Call ();
4423 closure->SetMarker (NULL);
4424 } else {
4425 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4426 mutex.Lock ();
4427 list.Append (new MediaMarker::Node (frame->marker));
4428 mutex.Unlock ();
4431 frame->unref ();
4434 MediaMarker *
4435 MarkerStream::Pop ()
4437 MediaMarker *result = NULL;
4438 MediaMarker::Node *node;
4440 mutex.Lock ();
4441 node = (MediaMarker::Node *) list.First ();
4442 if (node != NULL) {
4443 result = node->marker;
4444 result->ref ();
4445 list.Remove (node);
4447 mutex.Unlock ();
4449 return result;
4452 void
4453 MarkerStream::SetCallback (MediaMarkerFoundClosure *closure)
4455 if (this->closure)
4456 this->closure->unref ();
4457 this->closure = closure;
4458 if (this->closure)
4459 this->closure->ref ();
4463 * MediaWork
4465 MediaWork::MediaWork (MediaClosure *c)
4467 g_return_if_fail (c != NULL);
4469 closure = c;
4470 closure->ref ();
4473 MediaWork::~MediaWork ()
4475 g_return_if_fail (closure != NULL);
4477 closure->unref ();
4478 closure = NULL;
4482 * PassThroughDecoderInfo
4485 bool
4486 PassThroughDecoderInfo::Supports (const char *codec)
4488 const char *video_fourccs [] = { "yv12", "rgba", NULL };
4489 const char *audio_fourccs [] = { "pcm", NULL };
4491 for (int i = 0; video_fourccs [i] != NULL; i++)
4492 if (!strcmp (codec, video_fourccs [i]))
4493 return true;
4495 for (int i = 0; audio_fourccs [i] != NULL; i++)
4496 if (!strcmp (codec, audio_fourccs [i]))
4497 return true;
4499 return false;
4503 * PassThroughDecoder
4506 PassThroughDecoder::PassThroughDecoder (Media *media, IMediaStream *stream)
4507 : IMediaDecoder (Type::PASSTHROUGHDECODER, media, stream)
4511 void
4512 PassThroughDecoder::Dispose ()
4514 IMediaDecoder::Dispose ();
4517 void
4518 PassThroughDecoder::OpenDecoderAsyncInternal ()
4520 const char *fourcc = GetStream ()->GetCodec ();
4522 if (!strcmp (fourcc, "yv12")) {
4523 SetPixelFormat (MoonPixelFormatYUV420P);
4524 } else if (!strcmp (fourcc, "rgba")) {
4525 SetPixelFormat (MoonPixelFormatRGBA32);
4526 } else if (!strcmp (fourcc, "pcm")) {
4527 // nothing to do here
4528 } else {
4529 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc));
4530 return;
4533 ReportOpenDecoderCompleted ();
4536 void
4537 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4539 frame->AddState (MediaFrameDecoded);
4540 if (GetPixelFormat () == MoonPixelFormatYUV420P) {
4541 VideoStream *vs = (VideoStream *) GetStream ();
4543 frame->width = vs->width;
4544 frame->height = vs->height;
4546 frame->data_stride[0] = frame->buffer;
4547 frame->data_stride[1] = frame->buffer + (frame->width*frame->height);
4548 frame->data_stride[2] = frame->buffer + (frame->width*frame->height)+(frame->width/2*frame->height/2);
4549 frame->buffer = NULL;
4550 frame->srcStride[0] = frame->width;
4551 frame->srcSlideY = frame->width;
4552 frame->srcSlideH = frame->height;
4554 frame->AddState (MediaFramePlanar);
4556 ReportDecodeFrameCompleted (frame);
4560 * NullDecoderInfo
4563 bool
4564 NullDecoderInfo::Supports (const char *codec)
4566 const char *video_fourccs [] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL };
4567 const char *audio_fourccs [] = { "wmav1","wmav2", "wmav3", "mp3", NULL};
4569 for (int i = 0; video_fourccs [i] != NULL; i++)
4570 if (!strcmp (codec, video_fourccs [i]))
4571 return true;
4573 for (int i = 0; audio_fourccs [i] != NULL; i++)
4574 if (!strcmp (codec, audio_fourccs [i]))
4575 return true;
4578 return false;
4582 * NullDecoder
4585 NullDecoder::NullDecoder (Media *media, IMediaStream *stream) : IMediaDecoder (Type::NULLDECODER, media, stream)
4587 logo = NULL;
4588 logo_size = 0;
4589 prev_pts = G_MAXUINT64;
4592 void
4593 NullDecoder::Dispose ()
4595 g_free (logo);
4596 logo = NULL;
4598 IMediaDecoder::Dispose ();
4601 MediaResult
4602 NullDecoder::DecodeVideoFrame (MediaFrame *frame)
4604 // free encoded buffer and alloc a new one for our image
4605 g_free (frame->buffer);
4606 frame->buflen = logo_size;
4607 frame->buffer = (guint8*) g_malloc (frame->buflen);
4608 memcpy (frame->buffer, logo, frame->buflen);
4609 frame->AddState (MediaFrameDecoded);
4611 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4613 return MEDIA_SUCCESS;
4616 MediaResult
4617 NullDecoder::DecodeAudioFrame (MediaFrame *frame)
4619 AudioStream *as = (AudioStream *) GetStream ();
4620 guint32 samples;
4621 guint32 data_size;
4622 guint64 diff_pts;
4624 // discard encoded data
4625 g_free (frame->buffer);
4627 // We have no idea here how long the encoded audio data is
4628 // for the first frame we use 0.1 seconds, for the rest
4629 // we calculate the time since the last frame
4631 if (prev_pts == G_MAXUINT64 || frame->pts <= prev_pts) {
4632 samples = as->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4633 } else {
4634 diff_pts = frame->pts - prev_pts;
4635 samples = (float) as->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT / (float) diff_pts);
4637 prev_pts = frame->pts;
4639 data_size = samples * as->GetChannels () * 2 /* 16 bit audio */;
4641 frame->buflen = data_size;
4642 frame->buffer = (guint8 *) g_malloc0 (frame->buflen);
4644 frame->AddState (MediaFrameDecoded);
4646 return MEDIA_SUCCESS;
4649 void
4650 NullDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4652 MediaResult result = MEDIA_FAIL;
4653 IMediaStream *stream = GetStream ();
4655 if (stream->GetType () == MediaTypeAudio) {
4656 result = DecodeAudioFrame (frame);
4657 } else if (stream->GetType () == MediaTypeVideo) {
4658 result = DecodeVideoFrame (frame);
4661 if (MEDIA_SUCCEEDED (result)) {
4662 ReportDecodeFrameCompleted (frame);
4663 } else {
4664 ReportErrorOccurred (result);
4668 void
4669 NullDecoder::OpenDecoderAsyncInternal ()
4671 MediaResult result;
4672 IMediaStream *stream = GetStream ();
4674 if (stream->GetType () == MediaTypeAudio)
4675 result = OpenAudio ();
4676 else if (stream->GetType () == MediaTypeVideo)
4677 result = OpenVideo ();
4678 else
4679 result = MEDIA_FAIL;
4681 if (MEDIA_SUCCEEDED (result)) {
4682 ReportOpenDecoderCompleted ();
4683 } else {
4684 ReportErrorOccurred (result);
4688 MediaResult
4689 NullDecoder::OpenAudio ()
4691 return MEDIA_SUCCESS;
4694 MediaResult
4695 NullDecoder::OpenVideo ()
4697 VideoStream *vs = (VideoStream *) GetStream ();
4698 guint32 dest_height = vs->height;
4699 guint32 dest_width = vs->width;
4700 guint32 dest_i = 0;
4702 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4703 extern const char moonlight_logo [];
4704 const char *image = moonlight_logo;
4706 guint32 img_offset = *((guint32*)(image + 10));
4707 guint32 img_width = *((guint32*)(image + 18));
4708 guint32 img_height = *((guint32*)(image + 22));
4709 guint32 img_stride = (img_width * 3 + 3) & ~3; // in bytes
4710 guint32 img_i, img_h, img_w;
4711 guint32 start_w = (dest_width-img_width)/2;
4712 guint32 end_w = start_w + img_width;
4713 guint32 start_h = (dest_height-img_height)/2;
4714 guint32 end_h = start_h + img_height;
4716 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);
4718 // create the buffer for our image
4719 logo_size = dest_height * dest_width * 4;
4720 logo = (guint8*) g_malloc (logo_size);
4721 memset (logo, 0x00, logo_size);
4723 // write our image centered into the destination rectangle, flipped horizontally
4724 dest_i = 4;
4725 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4726 for (guint32 dest_w = 0; dest_w < dest_width; dest_w++) {
4727 if (dest_w >= start_w && dest_w < end_w && dest_h >= start_h && dest_h < end_h) {
4728 img_h = (dest_h - start_h) % img_height;
4729 img_w = (dest_w - start_w) % img_width;
4730 img_i = img_h * img_stride + img_w * 3;
4732 logo [logo_size - dest_i + 0] = image [img_offset + img_i + 0];
4733 logo [logo_size - dest_i + 1] = image [img_offset + img_i + 1];
4734 logo [logo_size - dest_i + 2] = image [img_offset + img_i + 2];
4736 logo [logo_size - dest_i + 3] = 0xff;
4738 dest_i += 4;
4742 // Flip the image vertically
4743 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4744 for (guint32 dest_w = 0; dest_w < dest_width / 2; dest_w++) {
4745 guint32 tmp;
4746 guint32 a = (dest_h * dest_width + dest_w) * 4;
4747 guint32 b = (dest_h * dest_width + dest_width - dest_w) * 4 - 4;
4748 for (guint32 c = 0; c < 3; c++) {
4749 tmp = logo [a + c];
4750 logo [a + c] = logo [b + c];
4751 logo [b + c] = tmp;
4756 SetPixelFormat (MoonPixelFormatRGB32);
4758 return MEDIA_SUCCESS;
4762 * ExternalDemuxer
4765 ExternalDemuxer::ExternalDemuxer (Media *media, void *instance, CloseDemuxerCallback close_demuxer,
4766 GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample, OpenDemuxerAsyncCallback open_demuxer,
4767 SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
4768 : IMediaDemuxer (Type::EXTERNALDEMUXER, media)
4770 this->close_demuxer_callback = close_demuxer;
4771 this->get_diagnostic_async_callback = get_diagnostic;
4772 this->get_sample_async_callback = get_sample;
4773 this->open_demuxer_async_callback = open_demuxer;
4774 this->seek_async_callback = seek;
4775 this->switch_media_stream_async_callback = switch_media_stream;
4776 this->instance = instance;
4778 can_seek = true;
4779 pthread_rwlock_init (&rwlock, NULL);
4781 g_return_if_fail (instance != NULL);
4782 g_return_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL);
4785 ExternalDemuxer::~ExternalDemuxer ()
4787 pthread_rwlock_destroy (&rwlock);
4790 void
4791 ExternalDemuxer::Dispose ()
4793 ClearCallbacks ();
4794 IMediaDemuxer::Dispose ();
4797 void
4798 ExternalDemuxer::ClearCallbacks ()
4800 pthread_rwlock_wrlock (&rwlock);
4801 close_demuxer_callback = NULL;
4802 get_diagnostic_async_callback = NULL;
4803 get_sample_async_callback = NULL;
4804 open_demuxer_async_callback = NULL;
4805 seek_async_callback = NULL;
4806 switch_media_stream_async_callback = NULL;
4807 instance = NULL;
4808 pthread_rwlock_unlock (&rwlock);
4811 void
4812 ExternalDemuxer::SetCanSeek (bool value)
4814 can_seek = value;
4817 gint32
4818 ExternalDemuxer::AddStream (IMediaStream *stream)
4820 return IMediaDemuxer::AddStream (stream);
4823 void
4824 ExternalDemuxer::CloseDemuxerInternal ()
4826 pthread_rwlock_rdlock (&rwlock);
4827 if (close_demuxer_callback != NULL) {
4828 close_demuxer_callback (instance);
4829 } else {
4830 #if SANITY
4831 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4832 #endif
4834 pthread_rwlock_unlock (&rwlock);
4837 void
4838 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind)
4840 pthread_rwlock_rdlock (&rwlock);
4841 if (get_diagnostic_async_callback != NULL) {
4842 get_diagnostic_async_callback (instance, diagnosticsKind);
4843 } else {
4844 #if SANITY
4845 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4846 #endif
4848 pthread_rwlock_unlock (&rwlock);
4851 void
4852 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
4854 g_return_if_fail (stream != NULL);
4856 pthread_rwlock_rdlock (&rwlock);
4857 if (get_sample_async_callback != NULL) {
4858 get_sample_async_callback (instance, stream->GetStreamType ());
4859 } else {
4860 #if SANITY
4861 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4862 #endif
4864 pthread_rwlock_unlock (&rwlock);
4867 void
4868 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4870 pthread_rwlock_rdlock (&rwlock);
4871 if (open_demuxer_async_callback != NULL) {
4872 open_demuxer_async_callback (instance, this);
4873 } else {
4874 #if SANITY
4875 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4876 #endif
4878 pthread_rwlock_unlock (&rwlock);
4881 void
4882 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime)
4884 pthread_rwlock_rdlock (&rwlock);
4885 if (seek_async_callback != NULL) {
4886 seek_async_callback (instance, seekToTime);
4887 } else {
4888 #if SANITY
4889 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4890 #endif
4892 pthread_rwlock_unlock (&rwlock);
4895 void
4896 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *mediaStreamDescription)
4898 g_return_if_fail (mediaStreamDescription != NULL);
4900 pthread_rwlock_rdlock (&rwlock);
4901 if (switch_media_stream_async_callback != NULL) {
4902 switch_media_stream_async_callback (instance, mediaStreamDescription);
4903 } else {
4904 #if SANITY
4905 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4906 #endif
4908 pthread_rwlock_unlock (&rwlock);
4913 * AudioStream
4916 AudioStream::AudioStream (Media *media)
4917 : IMediaStream (Type::AUDIOSTREAM, media)
4921 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)
4922 : IMediaStream (Type::AUDIOSTREAM, media)
4924 this->codec_id = codec_id;
4925 this->codec = CreateCodec (codec_id);
4926 this->extra_data = extra_data;
4927 this->extra_data_size = extra_data_size;
4928 input_bits_per_sample = bits_per_sample;
4929 output_bits_per_sample = bits_per_sample;
4930 input_block_align = block_align;
4931 output_block_align = block_align;
4932 input_sample_rate = sample_rate;
4933 output_sample_rate = sample_rate;
4934 input_channels = channels;
4935 output_channels = channels;
4936 input_bit_rate = bit_rate;
4937 output_bit_rate = bit_rate;
4941 * ExternalDecoder
4944 ExternalDecoder::ExternalDecoder (Media *media, IMediaStream *stream, void *instance, const char *name,
4945 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async,
4946 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async,
4947 ExternalDecoder_CleanupCallback cleanup,
4948 ExternalDecoder_CleanStateCallback clean_state,
4949 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame,
4950 ExternalDecoder_DisposeCallback dispose,
4951 ExternalDecoder_DtorCallback dtor)
4952 : IMediaDecoder (Type::EXTERNALDECODER, media, stream)
4954 this->instance = instance;
4955 this->name = g_strdup (name);
4956 this->decode_frame_async = decode_frame_async;
4957 this->open_decoder_async = open_decoder_async;
4958 this->cleanup = cleanup;
4959 this->clean_state = clean_state;
4960 this->has_delayed_frame = has_delayed_frame;
4961 this->dispose = dispose;
4962 this->dtor = dtor;
4965 ExternalDecoder::~ExternalDecoder ()
4967 dtor (instance);
4968 g_free (name);
4971 void
4972 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4974 decode_frame_async (instance, frame);
4977 void
4978 ExternalDecoder::OpenDecoderAsyncInternal ()
4980 open_decoder_async (instance);
4983 void
4984 ExternalDecoder::Dispose ()
4986 dispose (instance);
4988 IMediaDecoder::Dispose ();
4991 void
4992 ExternalDecoder::Cleanup (MediaFrame *frame)
4994 cleanup (instance, frame);
4997 void
4998 ExternalDecoder::CleanState ()
5000 clean_state (instance);
5003 bool
5004 ExternalDecoder::HasDelayedFrame ()
5006 return has_delayed_frame (instance);
5009 void
5010 ExternalDecoder::InputEnded ()
5012 GetStream ()->SetOutputEnded (true);
5016 * ExternalDecoderInfo
5019 ExternalDecoderInfo::ExternalDecoderInfo (void *instance, const char *name, ExternalDecoderInfo_SupportsCallback supports, ExternalDecoderInfo_Create create, ExternalDecoderInfo_dtor dtor)
5021 this->instance = instance;
5022 this->supports = supports;
5023 this->create = create;
5024 this->dtor = dtor;
5025 this->name = g_strdup (name);
5028 bool
5029 ExternalDecoderInfo::Supports (const char *codec)
5031 return supports (instance, codec);
5034 IMediaDecoder *
5035 ExternalDecoderInfo::Create (Media *media, IMediaStream *stream)
5037 return create (instance, media, stream);
5040 ExternalDecoderInfo::~ExternalDecoderInfo ()
5042 if (dtor != NULL)
5043 dtor (instance);
5044 g_free (name);