2009-12-07 Rolf Bjarne Kvinge <RKvinge@novell.com>
[moon.git] / src / pipeline.cpp
blob5ce63b83d76bfbe3b1d16a36009e735cb2e56f49
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 mutex.Lock ();
138 dmx = this->demuxer;
139 this->demuxer = NULL;
140 mutex.Unlock ();
141 if (dmx) {
142 dmx->Dispose ();
143 dmx->unref ();
146 delete markers;
147 markers = NULL;
149 IMediaObject::Dispose ();
151 GetDeployment ()->UnregisterMedia (this);
154 bool
155 Media::IsMSCodecsInstalled ()
157 return registered_ms_codecs;
160 void
161 Media::RegisterMSCodecs (void)
163 register_codec reg;
164 void *dl;
165 char *libmscodecs_path = NULL;
166 const char *functions [] = {"register_codec_pack", NULL};
167 const gchar *home = g_get_home_dir ();
168 registering_ms_codecs = true;
170 if (!(moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS)) {
171 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
172 return;
175 if (home != NULL)
176 libmscodecs_path = g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME, NULL);
178 if (!(g_file_test (libmscodecs_path, G_FILE_TEST_EXISTS) && g_file_test (libmscodecs_path, G_FILE_TEST_IS_REGULAR))) {
179 if (libmscodecs_path)
180 g_free (libmscodecs_path);
181 libmscodecs_path = g_strdup (CODEC_LIBRARY_NAME);
184 dl = dlopen (libmscodecs_path, RTLD_LAZY);
185 if (dl != NULL) {
186 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path);
188 int pre_decoders = 0;
189 int post_decoders = 0;
191 /* Count the number of current decoders */
192 MediaInfo *current;
193 current = registered_decoders;
194 while (current != NULL) {
195 pre_decoders++;
196 current = current->next;
199 for (int i = 0; functions [i] != NULL; i++) {
200 reg = (register_codec) dlsym (dl, functions [i]);
201 if (reg != NULL) {
202 (*reg) (MOONLIGHT_CODEC_ABI_VERSION);
203 } else {
204 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions [i], libmscodecs_path);
208 /* Count the number of decoders after registering the ms codecs */
209 current = registered_decoders;
210 while (current != NULL) {
211 post_decoders++;
212 current = current->next;
215 /* We could only load the codecs if the codec pack actually registered any decoders
216 * This ensures that if the user has invalid codecs for whatever reason, we request
217 * a new download. */
218 registered_ms_codecs = post_decoders > pre_decoders;
219 } else {
220 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path, dlerror ());
222 g_free (libmscodecs_path);
224 registering_ms_codecs = false;
227 void
228 Media::SetBufferingEnabled (bool value)
230 buffering_enabled = value;
231 WakeUp ();
234 void
235 Media::SetBufferingTime (guint64 buffering_time)
237 mutex.Lock ();
238 this->buffering_time = buffering_time;
239 mutex.Unlock ();
242 guint64
243 Media::GetBufferingTime ()
245 guint64 result;
246 mutex.Lock ();
247 result = buffering_time;
248 mutex.Unlock ();
249 return result;
252 PlaylistRoot *
253 Media::GetPlaylistRoot ()
255 return playlist;
258 IMediaDemuxer *
259 Media::GetDemuxerReffed ()
261 IMediaDemuxer *result;
262 mutex.Lock ();
263 result = this->demuxer;
264 if (result)
265 result->ref ();
266 mutex.Unlock ();
267 return result;
270 List *
271 Media::GetMarkers ()
273 if (markers == NULL)
274 markers = new List ();
276 return markers;
279 void
280 Media::RegisterDemuxer (DemuxerInfo *info)
282 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
283 info->next = NULL;
284 if (registered_demuxers == NULL) {
285 registered_demuxers = info;
286 } else {
287 MediaInfo* current = registered_demuxers;
288 while (current->next != NULL)
289 current = current->next;
290 current->next = info;
294 void
295 Media::RegisterConverter (ConverterInfo *info)
297 //printf ("Media::RegisterConverter (%p)\n", info);
298 info->next = NULL;
299 if (registered_converters == NULL) {
300 registered_converters = info;
301 } else {
302 MediaInfo *current = registered_converters;
303 while (current->next != NULL)
304 current = current->next;
305 current->next = info;
309 void
310 Media::RegisterDecoder (DecoderInfo *info)
312 MediaInfo *current;
314 //printf ("Media::RegisterDecoder (%p)\n", info);
315 info->next = NULL;
316 if (registered_decoders == NULL) {
317 registered_decoders = info;
318 } else {
319 if (registering_ms_codecs) {
320 // MS codecs might get registered after all other codecs (right after installing them),
321 // which means after the null codecs so if they don't get special treatment, they won't
322 // get used until the next browser restart (when they're registered normally).
323 // So instead of appending them, we prepend them.
324 info->next = registered_decoders;
325 registered_decoders = info;
326 } else {
327 current = registered_decoders;
328 while (current->next != NULL)
329 current = current->next;
330 current->next = info;
333 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info->GetName ());
336 void
337 Media::Initialize ()
339 LOG_PIPELINE ("Media::Initialize ()\n");
341 // demuxers
342 Media::RegisterDemuxer (new ASFDemuxerInfo ());
343 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
344 Media::RegisterDemuxer (new ASXDemuxerInfo ());
346 // converters
347 if (!(moonlight_flags & RUNTIME_INIT_FFMPEG_YUV_CONVERTER))
348 Media::RegisterConverter (new YUVConverterInfo ());
350 // decoders
351 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
352 if (moonlight_flags & RUNTIME_INIT_ENABLE_MS_CODECS) {
353 RegisterMSCodecs ();
355 #ifdef INCLUDE_FFMPEG
356 if (!(moonlight_flags & RUNTIME_INIT_DISABLE_FFMPEG_CODECS)) {
357 register_ffmpeg ();
359 #endif
361 Media::RegisterDecoder (new PassThroughDecoderInfo ());
362 Media::RegisterDecoder (new NullDecoderInfo ());
364 MediaThreadPool::Initialize ();
367 void
368 Media::Shutdown ()
370 LOG_PIPELINE ("Media::Shutdown ()\n");
372 MediaInfo *current;
373 MediaInfo *next;
375 // Make sure all threads are stopped
376 AudioPlayer::Shutdown ();
377 MediaThreadPool::Shutdown ();
379 current = registered_decoders;
380 while (current != NULL) {
381 next = current->next;
382 delete current;
383 current = next;
385 registered_decoders = NULL;
387 current = registered_demuxers;
388 while (current != NULL) {
389 next = current->next;
390 delete current;
391 current = next;
393 registered_demuxers = NULL;
395 current = registered_converters;
396 while (current != NULL) {
397 next = current->next;
398 delete current;
399 current = next;
401 registered_converters = NULL;
403 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
406 void
407 Media::Warning (MediaResult result, const char *format, ...)
409 va_list args;
411 if (MEDIA_SUCCEEDED (result))
412 return;
414 fprintf (stderr, "Moonlight: MediaResult = %d; ", result);
416 va_start (args, format);
417 vfprintf (stderr, format, args);
418 va_end (args);
420 fputc ('\n', stderr);
423 bool
424 Media::InMediaThread ()
426 return MediaThreadPool::IsThreadPoolThread ();
429 void
430 Media::ReportBufferingProgress (double progress)
432 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress, buffering_progress);
434 progress = MAX (MIN (progress, 1.0), 0.0);
436 if (progress == buffering_progress)
437 return;
439 if (progress < buffering_progress || progress > (buffering_progress + 0.005) || progress == 1.0 || progress == 0.0) {
440 buffering_progress = progress;
441 EmitSafe (BufferingProgressChangedEvent, new ProgressEventArgs (progress));
445 void
446 Media::ReportDownloadProgress (double progress)
448 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress, download_progress);
450 progress = MAX (MIN (progress, 1.0), 0.0);
452 if (progress <= download_progress) {
454 * Download progress percentage can actually go down - if the file size
455 * goes up. Yes, the file size can go up.
457 return;
460 if (progress > (download_progress + 0.005) || progress == 1.0 || progress == 0.0) {
461 download_progress = progress;
462 EmitSafe (DownloadProgressChangedEvent, new ProgressEventArgs (progress));
466 void
467 Media::SeekAsync (guint64 pts)
469 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
471 if (demuxer == NULL) {
472 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
473 return;
476 demuxer->SeekAsync (pts);
479 void
480 Media::ReportSeekCompleted (guint64 pts)
482 LOG_PIPELINE ("Media::ReportSeekCompleted (%" G_GUINT64_FORMAT "), id: %i\n", pts, GET_OBJ_ID (this));
484 buffering_progress = 0;
485 ClearQueue ();
486 EmitSafe (SeekCompletedEvent);
489 void
490 Media::ReportOpenCompleted ()
492 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
494 EmitSafe (OpenCompletedEvent);
497 void
498 Media::ReportOpenDemuxerCompleted ()
500 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
502 OpenInternal ();
505 void
506 Media::ReportOpenDecoderCompleted (IMediaDecoder *decoder)
508 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder, GET_OBJ_ID (this));
510 g_return_if_fail (decoder != NULL);
512 OpenInternal ();
515 void
516 Media::ReportErrorOccurred (ErrorEventArgs *args)
518 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args, args == NULL ? NULL : args->GetErrorMessage());
520 if (args) {
521 fprintf (stderr, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args->GetErrorType()), args->GetErrorCode(), args->GetErrorMessage(), args->GetExtendedMessage());
522 } else {
523 fprintf (stderr, "Moonlight: Unspecified media error.\n");
526 if (!error_reported) {
527 error_reported = true;
528 EmitSafe (MediaErrorEvent, args);
532 void
533 Media::ReportErrorOccurred (const char *message)
535 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message);
537 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, message)));
540 void
541 Media::ReportErrorOccurred (MediaResult result)
543 char *msg = g_strdup_printf ("Media error: %i.", result);
544 ReportErrorOccurred (msg);
545 g_free (msg);
548 void
549 Media::PlayAsync ()
551 LOG_PIPELINE ("Media::PlayAsync ()\n");
553 MediaClosure *closure = new MediaClosure (this, PlayCallback, this, "Media::PlayAsync");
554 EnqueueWork (closure);
555 closure->unref ();
558 void
559 Media::PauseAsync ()
561 LOG_PIPELINE ("Media::PauseAsync ()\n");
564 void
565 Media::StopAsync ()
567 LOG_PIPELINE ("Media::StopAsync ()\n");
569 MediaClosure *closure = new MediaClosure (this, StopCallback, this, "Media::StopAsync");
570 EnqueueWork (closure);
571 closure->unref ();
574 MediaResult
575 Media::StopCallback (MediaClosure *closure)
577 closure->GetMedia ()->Stop ();
578 return MEDIA_SUCCESS;
581 MediaResult
582 Media::PlayCallback (MediaClosure *closure)
584 closure->GetMedia ()->Play ();
585 return MEDIA_SUCCESS;
588 void
589 Media::Stop ()
591 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
593 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
595 stopped = true;
597 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
598 /* ClearQueue (); */
600 if (demuxer != NULL)
601 demuxer->ClearBuffers ();
604 void
605 Media::Play ()
607 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
609 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
611 stopped = false;
612 if (demuxer != NULL)
613 demuxer->FillBuffers ();
616 void
617 Media::Initialize (Downloader *downloader, const char *PartName)
619 IMediaSource *source;
621 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader, PartName, GET_OBJ_ID (this));
623 g_return_if_fail (downloader != NULL);
624 g_return_if_fail (file == NULL);
625 g_return_if_fail (uri != NULL || PartName != NULL);
626 g_return_if_fail (initialized == false);
627 g_return_if_fail (error_reported == false);
628 g_return_if_fail (this->source == NULL);
630 if (downloader->Completed ()) {
631 file = downloader->GetDownloadedFilename (PartName);
633 if (file == NULL) {
634 ReportErrorOccurred ("Couldn't get downloaded filename.");
635 return;
639 if (file == NULL && PartName != NULL && PartName [0] != 0) {
640 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)");
641 return;
644 if (file == NULL) {
645 InternalDownloader *idl = downloader->GetInternalDownloader ();
646 MmsDownloader *mms_dl = (idl && idl->GetObjectType () == Type::MMSDOWNLOADER) ? (MmsDownloader *) idl : NULL;
648 if (mms_dl == NULL) {
649 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
650 return;
653 source = new MmsSource (this, downloader);
654 } else {
655 source = new FileSource (this, file);
658 Initialize (source);
659 source->unref ();
662 void
663 Media::Initialize (IMediaSource *source)
665 MediaResult result;
667 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source, GET_OBJ_ID (this));
669 g_return_if_fail (source != NULL);
670 g_return_if_fail (this->source == NULL);
671 g_return_if_fail (initialized == false);
673 result = source->Initialize ();
674 if (!MEDIA_SUCCEEDED (result)) {
675 ReportErrorOccurred (result);
676 return;
679 initialized = true;
680 this->source = source;
681 this->source->ref ();
684 void
685 Media::Initialize (const char *uri)
687 Downloader *dl;
688 IMediaSource *source = NULL;
690 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri, GET_OBJ_ID (this));
692 g_return_if_fail (uri != NULL);
693 g_return_if_fail (file == NULL);
694 g_return_if_fail (uri != NULL);
695 g_return_if_fail (initialized == false);
696 g_return_if_fail (error_reported == false);
697 g_return_if_fail (source == NULL);
698 g_return_if_fail (this->source == NULL);
700 this->uri = g_strdup (uri);
703 if (g_str_has_prefix (uri, "mms://") || g_str_has_prefix (uri, "rtsp://") || g_str_has_prefix (uri, "rtsps://")) {
704 dl = Surface::CreateDownloader (this);
705 if (dl == NULL) {
706 ReportErrorOccurred ("Couldn't create downloader.");
707 return;
710 dl->Open ("GET", uri, StreamingPolicy);
712 if (dl->GetFailedMessage () == NULL) {
713 Initialize (dl, NULL);
714 } else {
715 ReportErrorOccurred (new ErrorEventArgs (MediaError,
716 MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
719 dl->unref ();
721 return;
724 source = new ProgressiveSource (this, uri);
725 Initialize (source);
726 source->unref ();
729 void
730 Media::Initialize (IMediaDemuxer *demuxer)
732 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer, GET_OBJ_ID (this));
734 g_return_if_fail (demuxer != NULL);
735 g_return_if_fail (this->demuxer == NULL);
736 g_return_if_fail (initialized == false);
738 this->demuxer = demuxer;
739 this->demuxer->ref ();
741 initialized = true;
745 void
746 Media::RetryHttp (ErrorEventArgs *args)
748 char *http_uri = NULL;
750 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri);
752 g_return_if_fail (uri != NULL);
753 g_return_if_fail (source != NULL);
755 if (http_retried) {
756 ReportErrorOccurred (args);
757 return;
760 // CHECK: If the current protocolo is rtsps, should we retry http or https?
762 if (g_str_has_prefix (uri, "mms://")) {
763 http_uri = g_strdup_printf ("http://%s", uri + 6);
764 } else if (g_str_has_prefix (uri, "rtsp://")) {
765 http_uri = g_strdup_printf ("http://%s", uri + 7);
766 } else if (g_str_has_prefix (uri, "rtsps://")) {
767 http_uri = g_strdup_printf ("http://%s", uri + 8);
768 } else {
769 ReportErrorOccurred (args);
770 return;
773 http_retried = true;
775 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri);
777 g_free (uri);
778 uri = NULL;
779 /* this method is called on the main thread, ensure Dispose is called on the source on the media thread */
780 DisposeObject (source);
781 source->unref ();
782 source = NULL;
783 initialized = false;
784 error_reported = false;
786 Initialize (http_uri);
788 g_free (http_uri);
790 if (!error_reported)
791 OpenAsync ();
794 void
795 Media::OpenAsync ()
797 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
799 g_return_if_fail (initialized == true);
801 EmitSafe (OpeningEvent);
803 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenAsync");
804 EnqueueWork (closure);
805 closure->unref ();
808 void
809 Media::OpenInternal ()
811 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
813 g_return_if_fail (initialized == true);
815 if (opened) {
816 // This may happen due to the recursion detection below
817 // Example: we try open a demuxer, the demuxer opens successfully
818 // right away and calls ReportDemuxerOpenComplete which will call
819 // us. Due to the recursion detection we'll enqueue a call to
820 // OpenInternal, while the first OpenInternal may succeed and
821 // set opened to true.
822 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
823 return;
826 // detect recursive calls.
828 if (in_open_internal) {
829 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
830 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
831 EnqueueWork (closure);
832 closure->unref ();
833 return;
836 in_open_internal = true;
838 if (error_reported)
839 goto cleanup;
841 if (!SelectDemuxerAsync ()) {
842 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
843 goto cleanup;
846 if (error_reported)
847 goto cleanup;
849 if (!SelectDecodersAsync ()) {
850 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
851 goto cleanup;
854 opened = true;
855 opening = false;
857 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
859 EmitSafe (OpenCompletedEvent);
861 cleanup:
862 in_open_internal = false;
865 MediaResult
866 Media::OpenInternal (MediaClosure *closure)
868 Media *media = (Media *) closure->GetContext ();
870 g_return_val_if_fail (media != NULL, MEDIA_FAIL);
872 media->OpenInternal ();
874 return MEDIA_SUCCESS;
877 bool
878 Media::SelectDemuxerAsync ()
880 DemuxerInfo *demuxerInfo;
881 MediaResult support;
882 MediaResult result;
883 bool eof;
885 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);
887 g_return_val_if_fail (error_reported == false, false);
888 g_return_val_if_fail (initialized == true, false);
890 // Check if demuxer already is open
891 if (demuxer != NULL) {
892 if (demuxer->IsOpened ())
893 return true;
894 if (!demuxer->IsOpening ())
895 demuxer->OpenDemuxerAsync ();
896 return demuxer->IsOpened ();
899 g_return_val_if_fail (source != NULL, false);
901 // Check if the source knows how to create the demuxer
902 demuxer = source->CreateDemuxer (this);
904 if (demuxer == NULL) { // No demuxer created, we need to find it ourselves.
905 if (source->CanSeek () && source->GetPosition () > 0) {
906 if (!source->Seek (0, SEEK_SET)) {
907 LOG_PIPELINE ("Media::SelectDemuxer (): could not seek to position 0 of the input stream. Will try to continue anyway.\n");
910 // Check if we have at least 1024 bytes or eof
911 if (!source->IsPositionAvailable (16, &eof)) {
912 if (!eof) {
913 // We need to try again later.
914 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
916 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
917 EnqueueWork (closure, false);
918 closure->unref ();
920 return false;
924 // Select a demuxer
925 demuxerInfo = registered_demuxers;
926 while (demuxer == NULL && demuxerInfo != NULL) {
927 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo->GetName ());
928 support = demuxerInfo->Supports (source);
930 if (support == MEDIA_SUCCESS)
931 break;
933 result = support;
935 if (result == MEDIA_NOT_ENOUGH_DATA) {
936 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 ());
938 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
939 EnqueueWork (closure, false);
940 closure->unref ();
942 return false;
945 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo->GetName ());
946 demuxerInfo = (DemuxerInfo *) demuxerInfo->next;
949 if (demuxerInfo == NULL) {
950 // No demuxer found, report an error
951 const char *source_name = file ? file : uri;
953 if (!source_name) {
954 switch (source->GetType ()) {
955 case MediaSourceTypeProgressive:
956 case MediaSourceTypeFile:
957 source_name = ((FileSource *) source)->GetFileName ();
958 break;
959 case MediaSourceTypeMms:
960 case MediaSourceTypeMmsEntry:
961 source_name = "live source";
962 break;
963 default:
964 source_name = "unknown source";
965 break;
968 char *msg = g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name);
969 ReportErrorOccurred (new ErrorEventArgs (MediaError,
970 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT"),
971 MEDIA_UNKNOWN_CODEC, msg));
972 g_free (msg);
973 return false;
976 // Found a demuxer
977 demuxer = demuxerInfo->Create (this, source);
978 } else {
979 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer->GetTypeName ());
982 if (demuxer->IsOpened ())
983 return true;
985 if (demuxer->IsOpening ())
986 return false;
988 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
990 demuxer->OpenDemuxerAsync ();
992 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer), demuxer->GetTypeName ());
994 return demuxer != NULL && demuxer->IsOpened ();
997 bool
998 Media::SelectDecodersAsync ()
1000 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
1002 g_return_val_if_fail (error_reported == false, false);
1003 g_return_val_if_fail (initialized == true, false);
1005 if (demuxer == NULL) {
1006 ReportErrorOccurred ("No demuxer to select decoders from.");
1007 return false;
1010 // If the demuxer has no streams (ASXDemuxer for instance)
1011 // then just return success.
1012 if (demuxer->GetStreamCount () == 0)
1013 return true;
1015 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
1017 // Select codecs for each stream
1018 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1019 IMediaStream *stream = demuxer->GetStream (i);
1022 if (stream == NULL) {
1023 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
1024 return false;
1027 if (stream->GetDecoder () != NULL)
1028 continue;
1030 const char *codec = stream->GetCodec ();
1031 IMediaDecoder *decoder = NULL;
1033 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec);
1035 DecoderInfo *current_decoder = registered_decoders;
1036 while (current_decoder != NULL && !current_decoder->Supports (codec)) {
1037 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder->GetName (), codec);
1038 current_decoder = (DecoderInfo*) current_decoder->next;
1041 if (current_decoder == NULL) {
1042 Media::Warning (MEDIA_UNKNOWN_CODEC, "Unknown codec: '%s'.", codec);
1043 continue;
1046 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder->GetName (), codec);
1047 decoder = current_decoder->Create (this, stream);
1049 stream->SetDecoder (decoder);
1050 decoder->unref ();
1053 if (error_reported)
1054 return false;
1056 // Open the codecs
1057 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1059 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1060 IMediaStream *stream = demuxer->GetStream (i);
1061 IMediaDecoder *decoder;
1063 if (stream == NULL)
1064 continue;
1066 decoder = stream->GetDecoder ();
1068 if (decoder == NULL) {
1069 ReportErrorOccurred (new ErrorEventArgs (MediaError,
1070 MoonError (MoonError::EXCEPTION, 3001, "AG_E_INVALID_FILE_FORMAT")));
1071 return false;
1074 if (decoder->IsOpening () || decoder->IsOpened ())
1075 continue;
1077 decoder->OpenDecoderAsync ();
1080 if (error_reported)
1081 return false;
1083 // Wait until all the codecs have opened
1084 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1086 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1087 IMediaStream *stream = demuxer->GetStream (i);
1088 IMediaDecoder *decoder;
1090 if (stream == NULL)
1091 continue;
1093 decoder = stream->GetDecoder ();
1095 if (decoder == NULL) {
1096 ReportErrorOccurred (MEDIA_FAIL);
1097 return false;
1100 if (decoder->IsOpening ()) {
1101 MediaClosure *closure = new MediaClosure (this, OpenInternal, this, "Media::OpenInternal");
1102 EnqueueWork (closure, false);
1103 closure->unref ();
1104 return false;
1107 if (!decoder->IsOpened ()) {
1108 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1109 ReportErrorOccurred (MEDIA_FAIL);
1110 return false;
1115 // All the codecs have been opened now.
1116 // Find converters for each of them (whenever required).
1118 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1120 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1121 IMediaStream *stream = demuxer->GetStream (i);
1122 IMediaDecoder *decoder;
1124 if (stream == NULL)
1125 continue;
1127 decoder = stream->GetDecoder ();
1129 if (decoder == NULL) {
1130 ReportErrorOccurred (MEDIA_FAIL);
1131 return false;
1134 if (stream->GetType () != MediaTypeVideo)
1135 continue; // Only video streams need converters
1137 if (decoder->GetPixelFormat () == MoonPixelFormatRGB32 || decoder->GetPixelFormat () == MoonPixelFormatRGBA32)
1138 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1140 // Select converter for this stream
1141 VideoStream *vs = (VideoStream *) stream;
1143 ConverterInfo* current_conv = registered_converters;
1144 while (current_conv != NULL && !current_conv->Supports (decoder->GetPixelFormat (), MoonPixelFormatRGB32)) {
1145 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1146 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1147 current_conv = (ConverterInfo*) current_conv->next;
1151 if (current_conv == NULL) {
1152 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER);
1153 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1154 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1155 return false;
1158 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1159 current_conv->GetName (), decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1161 vs->converter = current_conv->Create (this, vs);
1162 vs->converter->input_format = decoder->GetPixelFormat ();
1163 vs->converter->output_format = MoonPixelFormatRGB32;
1164 if (!MEDIA_SUCCEEDED (vs->converter->Open ())) {
1165 vs->converter->unref ();
1166 vs->converter = NULL;
1167 ReportErrorOccurred (MEDIA_FAIL);
1168 return false;
1172 // Loop through all the streams, return true if at least one has a codec.
1174 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
1175 IMediaStream *stream = demuxer->GetStream (i);
1177 if (stream == NULL)
1178 continue;
1180 if (stream->GetDecoder () != NULL)
1181 return true;
1184 // No codecs found for no stream, report an error.
1185 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1186 return false;
1189 bool
1190 Media::EnqueueWork (MediaClosure *closure, bool wakeup)
1192 bool result = false;
1193 bool disposed;
1195 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure);
1197 g_return_val_if_fail (closure != NULL, false);
1199 if (IsDisposed ())
1200 return false;
1202 mutex.Lock ();
1203 disposed = this->is_disposed;
1204 if (disposed) {
1205 result = false;
1206 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed);
1207 } else {
1208 MediaThreadPool::AddWork (closure, wakeup);
1209 result = true;
1211 mutex.Unlock ();
1213 return result;
1216 MediaResult
1217 Media::DisposeObjectInternal (MediaClosure *closure)
1219 closure->GetContext ()->Dispose ();
1220 return MEDIA_SUCCESS;
1223 void
1224 Media::DisposeObject (EventObject *obj)
1226 MediaDisposeObjectClosure *closure = new MediaDisposeObjectClosure (this, DisposeObjectInternal, obj);
1227 if (!EnqueueWork (closure, true)) {
1228 LOG_PIPELINE ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj);
1229 obj->Dispose ();
1231 closure->unref ();
1234 void
1235 Media::WakeUp ()
1237 MediaThreadPool::WakeUp ();
1240 void
1241 Media::ClearQueue ()
1243 LOG_PIPELINE ("Media::ClearQueue ().\n");
1244 MediaThreadPool::RemoveWork (this);
1248 * ASXDemuxer
1251 ASXDemuxer::ASXDemuxer (Media *media, IMediaSource *source)
1252 : IMediaDemuxer (Type::ASXDEMUXER, media, source)
1254 playlist = NULL;
1257 ASXDemuxer::~ASXDemuxer ()
1261 void
1262 ASXDemuxer::Dispose ()
1264 if (playlist) {
1265 playlist->unref ();
1266 playlist = NULL;
1268 IMediaDemuxer::Dispose ();
1271 void
1272 ASXDemuxer::OpenDemuxerAsyncInternal ()
1274 MediaResult result;
1275 PlaylistRoot *root;
1276 ErrorEventArgs *args = NULL;
1277 Media *media = GetMediaReffed ();
1279 g_return_if_fail (media != NULL);
1281 root = media->GetPlaylistRoot ();
1283 g_return_if_fail (root != NULL);
1285 PlaylistParser *parser = new PlaylistParser (root, source);
1287 if (MEDIA_SUCCEEDED (parser->Parse ())) {
1288 result = MEDIA_SUCCESS;
1289 playlist = parser->GetPlaylist ();
1290 playlist->ref ();
1291 } else {
1292 result = MEDIA_FAIL;
1293 args = parser->GetErrorEventArgs ();
1294 if (args != NULL)
1295 args->ref ();
1298 delete parser;
1300 if (MEDIA_SUCCEEDED (result)) {
1301 ReportOpenDemuxerCompleted ();
1302 } else if (result == MEDIA_NOT_ENOUGH_DATA) {
1303 EnqueueOpen ();
1304 } else if (args != NULL) {
1305 args->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1306 ReportErrorOccurred (args);
1307 } else {
1308 ReportErrorOccurred (result);
1310 if (args)
1311 args->unref ();
1313 media->unref ();
1317 * ASXDemuxerInfo
1320 MediaResult
1321 ASXDemuxerInfo::Supports (IMediaSource *source)
1323 if (PlaylistParser::IsASX2 (source) || PlaylistParser::IsASX3 (source)) {
1324 return MEDIA_SUCCESS;
1325 } else {
1326 return MEDIA_FAIL;
1330 IMediaDemuxer *
1331 ASXDemuxerInfo::Create (Media *media, IMediaSource *source)
1333 return new ASXDemuxer (media, source);
1337 * ManagedStreamSource
1340 ManagedStreamSource::ManagedStreamSource (Media *media, ManagedStreamCallbacks *stream) : IMediaSource (Type::MANAGEDSTREAMSOURCE, media)
1342 memcpy (&this->stream, stream, sizeof (this->stream));
1345 ManagedStreamSource::~ManagedStreamSource ()
1347 stream.handle = NULL;
1350 gint32
1351 ManagedStreamSource::ReadInternal (void *buf, guint32 n)
1353 return stream.Read (stream.handle, buf, 0, n);
1356 gint32
1357 ManagedStreamSource::PeekInternal (void *buf, guint32 n)
1359 int read;
1361 read = stream.Read (stream.handle, buf, 0, n);
1362 stream.Seek (stream.handle, -read, 1 /* SeekOrigin.Current */);
1363 return read;
1366 bool
1367 ManagedStreamSource::SeekInternal (gint64 offset, int mode)
1369 stream.Seek (stream.handle, offset, mode /* FIXME: check if mode values matches SeekOrigin values */);
1370 return true;
1373 gint64
1374 ManagedStreamSource::GetPositionInternal ()
1376 return stream.Position (stream.handle);
1379 gint64
1380 ManagedStreamSource::GetSizeInternal ()
1382 return stream.Length (stream.handle);
1386 * FileSource
1389 FileSource::FileSource (Media *media, const char *filename) : IMediaSource (Type::FILESOURCE, media)
1391 this->filename = g_strdup (filename);
1392 fd = NULL;
1393 size = 0;
1394 temp_file = false;
1397 FileSource::FileSource (Type::Kind object_type, Media *media, bool temp_file) : IMediaSource (object_type, media)
1399 filename = NULL;
1400 fd = NULL;
1401 size = 0;
1402 this->temp_file = temp_file;
1405 FileSource::~FileSource ()
1409 void
1410 FileSource::Dispose ()
1412 g_free (filename);
1413 filename = NULL;
1414 if (fd != NULL) {
1415 fclose (fd);
1416 fd = NULL;
1418 IMediaSource::Dispose ();
1421 MediaResult
1422 FileSource::Initialize ()
1424 int tmp_fd;
1426 LOG_PIPELINE ("FileSource::Initialize ()\n");
1428 if (fd != NULL)
1429 return MEDIA_SUCCESS;
1431 if (temp_file) {
1432 if (filename != NULL)
1433 return MEDIA_FILE_ERROR;
1435 filename = g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL);
1437 if ((tmp_fd = g_mkstemp (filename)) == -1) {
1438 g_free (filename);
1439 filename = NULL;
1441 return MEDIA_FAIL;
1444 fd = fdopen (tmp_fd, "r");
1446 setvbuf (fd, buffer, _IOFBF, sizeof (buffer));
1447 } else {
1448 if (filename == NULL)
1449 return MEDIA_FILE_ERROR;
1451 fd = g_fopen (filename, "r");
1454 if (fd == NULL)
1455 return MEDIA_FILE_ERROR;
1457 UpdateSize ();
1459 return MEDIA_SUCCESS;
1462 MediaResult
1463 FileSource::Open (const char *filename)
1465 g_return_val_if_fail (filename != NULL, MEDIA_FAIL);
1467 g_free (this->filename);
1468 this->filename = g_strdup (filename);
1470 if (fd != NULL) {
1471 fclose (fd);
1472 fd = NULL;
1475 fd = fopen (filename, "r");
1477 if (fd == NULL)
1478 return MEDIA_FAIL;
1480 UpdateSize ();
1482 return MEDIA_SUCCESS;
1485 void
1486 FileSource::UpdateSize ()
1488 struct stat st;
1490 g_return_if_fail (fd != NULL);
1492 if (fstat (fileno (fd), &st) != -1) {
1493 size = st.st_size;
1494 } else {
1495 size = 0;
1499 gint64
1500 FileSource::GetSizeInternal ()
1502 return size;
1505 gint64
1506 FileSource::GetPositionInternal ()
1508 gint64 result;
1510 if (fd == NULL)
1511 return -1;
1513 result = ftell (fd);
1515 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %" G_GINT64_FORMAT "\n", result);
1517 return result;
1520 bool
1521 FileSource::SeekInternal (gint64 offset, int mode)
1523 gint64 n;
1525 if (fd == NULL)
1526 return false;
1528 LOG_PIPELINE ("FileSource::SeekInternal (%" G_GINT64_FORMAT ", %i)\n", offset, mode);
1530 clearerr (fd);
1531 n = fseek (fd, offset, mode);
1533 return n != -1;
1536 gint32
1537 FileSource::ReadInternal (void *buf, guint32 n)
1539 ssize_t nread = 0;
1541 if (fd == NULL) {
1542 errno = EINVAL;
1543 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf, n);
1544 return -1;
1547 clearerr (fd);
1548 nread = fread (buf, 1, n, fd);
1550 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n, (int) nread);
1552 return nread;
1555 gint32
1556 FileSource::PeekInternal (void *buf, guint32 n)
1558 gint32 result;
1560 result = ReadSome (buf, n);
1562 Seek (-result, SEEK_CUR);
1564 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %" G_GINT64_FORMAT " [Done]\n", GET_OBJ_ID (this), buf, n, GetPosition ());
1566 return result;
1569 bool
1570 FileSource::Eof ()
1572 if (fd == NULL)
1573 return false;
1575 return feof (fd);
1579 * ProgressiveSource
1582 ProgressiveSource::ProgressiveSource (Media *media, const char *uri) : FileSource (Type::PROGRESSIVESOURCE, media, true)
1584 write_pos = 0;
1585 size = -1;
1586 write_fd = NULL;
1587 cancellable = NULL;
1588 this->uri = g_strdup (uri);
1591 ProgressiveSource::~ProgressiveSource ()
1593 CloseWriteFile ();
1596 void
1597 ProgressiveSource::Dispose ()
1599 g_free (uri);
1600 uri = NULL;
1602 if (cancellable) {
1603 if (Surface::InMainThread ()) {
1604 delete_cancellable (this);
1605 } else {
1606 // we have to cancel/delete he cancellable on the main thread
1607 // it may end up doing a lot of stuff, including calling into
1608 // mozilla.
1610 // The tick call will ref us until the callback has been called.
1611 // Note that it may cause a warning to be printed
1612 // in ref () (reffing an object with a refcount of 0).
1613 // TODO: find a way to avoid the warning in this case, imho this is
1614 // a valid case of reffing an object with a refcount of 0.
1615 AddTickCall (delete_cancellable);
1619 FileSource::Dispose ();
1622 void
1623 ProgressiveSource::delete_cancellable (EventObject *data)
1625 ProgressiveSource *src = (ProgressiveSource *) data;
1626 if (src->cancellable) {
1627 src->cancellable->Cancel ();
1628 delete src->cancellable;
1629 src->cancellable = NULL;
1633 MediaResult
1634 ProgressiveSource::Initialize ()
1636 MediaResult result = MEDIA_SUCCESS;
1637 Application *application;
1639 application = GetDeployment ()->GetCurrentApplication ();
1641 g_return_val_if_fail (application != NULL, MEDIA_FAIL);
1642 g_return_val_if_fail (filename == NULL, MEDIA_FAIL);
1643 g_return_val_if_fail (cancellable == NULL, MEDIA_FAIL);
1645 result = FileSource::Initialize ();
1647 if (!MEDIA_SUCCEEDED (result))
1648 return result;
1650 write_fd = g_fopen (filename, "w");
1651 if (write_fd == NULL) {
1652 char *msg = g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename);
1653 ReportErrorOccurred (msg);
1654 g_free (msg);
1655 return MEDIA_FAIL;
1658 // unlink the file right away so that it'll be deleted even if we crash.
1659 if (moonlight_flags & RUNTIME_INIT_KEEP_MEDIA) {
1660 printf ("Moonlight: The media file %s will not deleted.\n", filename);
1661 } else {
1662 g_unlink (filename);
1665 cancellable = new Cancellable ();
1666 Uri *u = new Uri ();
1667 if (u->Parse (uri)) {
1668 if (!application->GetResource (NULL, u, notify_func, data_write, MediaPolicy, cancellable, (gpointer) this)) {
1669 result = MEDIA_FAIL;
1670 char *msg = g_strdup_printf ("invalid path found in uri '%s'", uri);
1671 ReportErrorOccurred (msg);
1672 g_free (msg);
1674 } else {
1675 result = MEDIA_FAIL;
1676 char *msg = g_strdup_printf ("Could not parse the uri '%s'", uri);
1677 ReportErrorOccurred (msg);
1678 g_free (msg);
1680 delete u;
1682 return result;
1685 void
1686 ProgressiveSource::notify_func (NotifyType type, gint64 args, void *closure)
1688 g_return_if_fail (closure != NULL);
1689 ((ProgressiveSource *) closure)->Notify (type, args);
1692 void
1693 ProgressiveSource::Notify (NotifyType type, gint64 args)
1695 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT ")\n",
1696 type,
1697 type == ::NotifySize ? "NotifySize" :
1698 (type == NotifyCompleted ? "NotifyCompleted" :
1699 (type == NotifyFailed ? "NotifyFailed" :
1700 (type == NotifyStarted ? "NotifyStarted" :
1701 (type == NotifyProgressChanged ? "NotifyProgressChanged" : "unknown")))),
1702 args);
1704 switch (type) {
1705 case ::NotifySize:
1706 NotifySize (args);
1707 break;
1708 case NotifyCompleted:
1709 DownloadComplete ();
1710 break;
1711 case NotifyFailed:
1712 DownloadFailed ();
1713 break;
1714 case NotifyStarted:
1715 case NotifyProgressChanged:
1716 default:
1717 break;
1721 void
1722 ProgressiveSource::data_write (void *data, gint32 offset, gint32 n, void *closure)
1724 g_return_if_fail (closure != NULL);
1725 ((ProgressiveSource *) closure)->DataWrite (data, offset, n);
1728 void
1729 ProgressiveSource::DataWrite (void *buf, gint32 offset, gint32 n)
1731 size_t nwritten;
1732 Media *media = NULL;
1734 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf, offset, n, media, filename);
1736 if (IsDisposed ())
1737 return;
1739 g_return_if_fail (write_fd != NULL);
1741 media = GetMediaReffed ();
1743 if (n == 0) {
1744 // We've got the entire file, update the size
1745 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.
1747 // Close our write handle, we won't write more now
1748 CloseWriteFile ();
1750 goto cleanup;
1753 nwritten = fwrite (buf, 1, n, write_fd);
1754 fflush (write_fd);
1756 Lock ();
1757 write_pos += nwritten;
1758 Unlock ();
1760 cleanup:
1761 if (media) {
1762 media->WakeUp ();
1763 media->ReportDownloadProgress ((double) (offset + n) / (double) size);
1764 media->unref ();
1768 void
1769 ProgressiveSource::NotifySize (gint64 size)
1771 LOG_PIPELINE ("ProgressiveSource::NotifySize (%" G_GINT64_FORMAT ")\n", size);
1773 Lock ();
1774 this->size = size;
1775 Unlock ();
1778 void
1779 ProgressiveSource::DownloadComplete ()
1781 MediaResult result = MEDIA_SUCCESS;
1782 Media *media = GetMediaReffed ();
1784 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1786 Lock ();
1787 if (write_pos != size && size != -1) { // what happend here?
1788 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT ") != the reported size (%" G_GINT64_FORMAT ")\n", write_pos, size);
1791 this->size = write_pos;
1793 // Close our write handle, we won't write more now
1794 CloseWriteFile ();
1796 Unlock ();
1798 if (!MEDIA_SUCCEEDED (result))
1799 ReportErrorOccurred (result);
1801 if (media) {
1802 media->ReportDownloadProgress (1.0);
1803 media->WakeUp ();
1804 media->unref ();
1808 void
1809 ProgressiveSource::DownloadFailed ()
1811 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1813 ReportErrorOccurred (new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 4001, "AG_E_NETWORK_ERROR")));
1816 void
1817 ProgressiveSource::CloseWriteFile ()
1819 if (write_fd == NULL)
1820 return;
1822 fclose (write_fd);
1823 write_fd = NULL;
1827 * MemorySource
1830 MemorySource::MemorySource (Media *media, void *memory, gint32 size, gint64 start, bool owner)
1831 : IMediaSource (Type::MEMORYSOURCE, media)
1833 this->memory = memory;
1834 this->size = size;
1835 this->start = start;
1836 this->pos = 0;
1837 this->owner = owner;
1840 MemorySource::~MemorySource ()
1842 if (owner)
1843 g_free (memory);
1846 bool
1847 MemorySource::SeekInternal (gint64 offset, int mode)
1849 gint64 real_offset;
1851 switch (mode) {
1852 case SEEK_SET:
1853 real_offset = offset - start;
1854 if (real_offset < 0 || real_offset >= size)
1855 return false;
1856 pos = real_offset;
1857 return true;
1858 case SEEK_CUR:
1859 if (pos + offset > size || pos + offset < 0)
1860 return false;
1861 pos += offset;
1862 return true;
1863 case SEEK_END:
1864 if (size - offset > size || size - offset < 0)
1865 return false;
1866 pos = size - offset;
1867 return true;
1868 default:
1869 return false;
1871 return true;
1874 gint32
1875 MemorySource::ReadInternal (void *buffer, guint32 n)
1877 guint32 k = MIN (n, size - pos);
1878 memcpy (buffer, ((char*) memory) + pos, k);
1879 pos += k;
1880 return k;
1883 gint32
1884 MemorySource::PeekInternal (void *buffer, guint32 n)
1886 gint64 start = this->start + pos;
1888 if (this->start > start)
1889 return 0;
1891 if ((this->start + size) < (start + n))
1892 return 0;
1894 memcpy (buffer, ((char*) memory) + this->start - start, n);
1895 return n;
1899 * MediaThreadPool
1902 pthread_mutex_t MediaThreadPool::mutex = PTHREAD_MUTEX_INITIALIZER;
1903 pthread_cond_t MediaThreadPool::condition = PTHREAD_COND_INITIALIZER;
1904 pthread_cond_t MediaThreadPool::completed_condition = PTHREAD_COND_INITIALIZER;
1905 int MediaThreadPool::count = 0;
1906 pthread_t MediaThreadPool::threads [max_threads];
1907 Media *MediaThreadPool::medias [max_threads];
1908 Deployment *MediaThreadPool::deployments [max_threads];
1909 bool MediaThreadPool::shutting_down = false;
1910 List *MediaThreadPool::queue = NULL;
1911 bool MediaThreadPool::valid [max_threads];
1913 void
1914 MediaThreadPool::AddWork (MediaClosure *closure, bool wakeup)
1916 pthread_attr_t attribs;
1917 int result = 0;
1919 pthread_mutex_lock (&mutex);
1921 if (shutting_down) {
1922 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1923 } else {
1924 if (queue == NULL)
1925 queue = new List ();
1926 queue->Append (new MediaWork (closure));
1928 // check if all threads are busy with other Media objects
1929 bool spawn = true;
1930 if (count == 0) {
1931 spawn = true;
1932 } else if (count < max_threads) {
1933 Media *media = closure->GetMedia ();
1934 for (int i = 0; i < count; i++) {
1935 if (medias [i] == NULL || medias [i] == media) {
1936 spawn = false; // there is a thread working on this media or not working at all.
1937 break;
1940 } else {
1941 spawn = false;
1944 if (spawn) {
1945 int prev_count = count;
1947 count++; // start up another thread.
1949 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count);
1951 for (int i = prev_count; i < count && result == 0; i++) {
1952 valid [i] = false;
1953 medias [i] = NULL;
1954 deployments [i] = NULL;
1956 pthread_attr_init (&attribs);
1957 pthread_attr_setdetachstate (&attribs, PTHREAD_CREATE_JOINABLE);
1958 result = pthread_create (&threads [i], &attribs, WorkerLoop, NULL);
1959 pthread_attr_destroy (&attribs);
1961 if (result != 0) {
1962 fprintf (stderr, "Moonlight: could not create media thread: %s (%i)\n", strerror (result), result);
1963 } else {
1964 valid [i] = true;
1969 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1970 closure->GetDescription (), closure, closure->GetMedia (), GET_OBJ_ID (closure->GetMedia ()), closure->GetDeployment (), queue ? queue->Length () : -1);
1972 if (wakeup)
1973 pthread_cond_signal (&condition);
1975 pthread_mutex_unlock (&mutex);
1978 void
1979 MediaThreadPool::WaitForCompletion (Deployment *deployment)
1981 bool waiting = false;
1982 MediaWork *current = NULL;
1984 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment);
1986 VERIFY_MAIN_THREAD;
1988 pthread_mutex_lock (&mutex);
1989 do {
1990 waiting = false;
1992 /* check if the deployment is being worked on */
1993 for (int i = 0; i < count; i++) {
1994 if (deployments [i] == deployment) {
1995 waiting = true;
1996 break;
1999 /* check if the deployment is in the queue */
2000 if (!waiting && queue != NULL) {
2001 current = (MediaWork *) queue->First ();
2002 while (current != NULL) {
2003 if (current->closure->GetDeployment () == deployment) {
2004 waiting = true;
2005 break;
2007 current = (MediaWork *) current->next;
2010 if (waiting) {
2011 timespec ts;
2012 ts.tv_sec = 0;
2013 ts.tv_nsec = 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
2014 pthread_cond_timedwait (&completed_condition, &mutex, &ts);
2016 } while (waiting);
2017 pthread_mutex_unlock (&mutex);
2020 void
2021 MediaThreadPool::RemoveWork (Media *media)
2023 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media, GET_OBJ_ID (media));
2025 List::Node *next;
2026 List::Node *first = NULL;
2027 List::Node *last = NULL;
2028 List::Node *current = NULL;
2029 int counter = 0;
2031 pthread_mutex_lock (&mutex);
2033 // create a list of nodes to delete
2034 current = queue != NULL ? queue->First () : NULL;
2035 while (current != NULL) {
2036 next = current->next; // retrieve next before Unlinking
2037 MediaWork *mw = (MediaWork *) current;
2038 if (mw->closure->GetMedia () == media) {
2039 queue->Unlink (current);
2040 if (first == NULL) {
2041 first = current;
2042 } else {
2043 last->next = current;
2045 last = current;
2046 counter++;
2047 break;
2049 current = next;
2052 pthread_mutex_unlock (&mutex);
2054 // We have to delete the list nodes with the
2055 // queue mutex unlocked, due to refcounting
2056 // (our node's (MediaWork) dtor will cause unrefs,
2057 // which may cause other dtors to be called,
2058 // eventually ending up wanting to lock the mutex
2059 // again).
2061 current = first;
2062 while (current != NULL) {
2063 next = current->next;
2064 delete current;
2065 current = next;
2069 void
2070 MediaThreadPool::WakeUp ()
2072 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2074 pthread_mutex_lock (&mutex);
2075 pthread_cond_signal (&condition);
2076 pthread_mutex_unlock (&mutex);
2079 bool
2080 MediaThreadPool::IsThreadPoolThread ()
2082 bool result = false;
2083 pthread_mutex_lock (&mutex);
2084 for (int i = 0; i < count; i++) {
2085 if (pthread_equal (pthread_self (), threads [i])) {
2086 result = true;
2087 break;
2090 pthread_mutex_unlock (&mutex);
2091 return result;
2094 void
2095 MediaThreadPool::Initialize ()
2097 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2098 VERIFY_MAIN_THREAD;
2100 shutting_down = false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2103 void
2104 MediaThreadPool::Shutdown ()
2106 List::Node *current = NULL;
2107 List::Node *next = NULL;
2109 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count);
2111 VERIFY_MAIN_THREAD;
2113 g_return_if_fail (!shutting_down);
2115 pthread_mutex_lock (&mutex);
2117 shutting_down = true;
2118 pthread_cond_broadcast (&condition);
2120 for (int i = 0; i < count; i++) {
2121 if (!valid [i])
2122 continue;
2124 pthread_mutex_unlock (&mutex);
2125 pthread_join (threads [i], NULL);
2126 pthread_mutex_lock (&mutex);
2129 if (queue != NULL) {
2130 current = queue->First ();
2131 queue->Clear (false);
2132 delete queue;
2133 queue = NULL;
2135 count = 0;
2137 pthread_mutex_unlock (&mutex);
2139 // deleting a node can have side-effects, so we first copy the list of nodes,
2140 // clear the original and loop over the copy while deleting the nodes.
2141 // this prevents any reentering issues while deleting nodes.
2142 while (current != NULL) {
2143 next = current->next;
2144 delete current;
2145 current = next;
2148 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2151 void *
2152 MediaThreadPool::WorkerLoop (void *data)
2154 MediaWork *node = NULL;
2155 Media *media = NULL;
2156 int self_index = -1;
2159 * Unblock any signals. We inherit the blocked signals from the thread that
2160 * created us, and if that thread happens to be a thread that has signals
2161 * blocked, we might end up deadlocking in the gc (since the gc delivers
2162 * a suspend signal, this thread never gets it because the signal is blocked,
2163 * and the gc waits for us to handle the suspend signal).
2164 * The pulseaudio thread is one example of a thread that has all signals
2165 * blocked, causing this issue if we create a new thread from the
2166 * pulseaudio thread.
2169 sigset_t signal_set;
2170 int err = 0;
2171 if ((err = sigemptyset (&signal_set)) != 0) {
2172 fprintf (stderr, "Moonlight: Media thread pool was unable to create an empty set of signals: %s (%i).\n", strerror (err), err);
2173 } else if ((err = pthread_sigmask (SIG_SETMASK, &signal_set, NULL)) != 0) {
2174 fprintf (stderr, "Moonlight: Media thread pool was unable to unblock all signals: %s (%i).\n", strerror (err), err);
2176 if (err != 0) {
2177 /* Something failed. Check if all signals are unblocked, if not, exit
2178 * the thread. Exiting the thread might cause media playback to fail,
2179 * while continuing with blocked signals will probably end up
2180 * deadlocking the gc.*/
2181 bool any_blocked_signals = false;
2183 if (pthread_sigmask (SIG_BLOCK, NULL, &signal_set) != 0) {
2184 any_blocked_signals = true; /* Assume the worst */
2185 } else if (!sigisemptyset (&signal_set)) {
2186 any_blocked_signals = true;
2189 if (any_blocked_signals) {
2190 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");
2191 return NULL;
2195 pthread_mutex_lock (&mutex);
2196 for (int i = 0; i < count; i++) {
2197 if (pthread_equal (threads [i], pthread_self ())) {
2198 self_index = i;
2199 break;
2202 pthread_mutex_unlock (&mutex);
2204 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index);
2206 g_return_val_if_fail (self_index >= 0, NULL);
2208 while (!shutting_down) {
2209 pthread_mutex_lock (&mutex);
2211 medias [self_index] = NULL;
2212 deployments [self_index] = NULL;
2213 /* if anybody was waiting for us to finish working, notify them */
2214 if (media != NULL)
2215 pthread_cond_signal (&completed_condition);
2217 media = NULL;
2218 node = (MediaWork *) (queue != NULL ? queue->First () : NULL);
2220 while (node != NULL) {
2221 media = node->closure->GetMedia ();
2223 for (int i = 0; i < count; i++) {
2224 if (medias [i] == media) {
2225 // another thread is working for the same media object.
2226 // we need to find something else to do.
2227 media = NULL;
2228 break;
2232 if (media != NULL)
2233 break;
2235 node = (MediaWork *) node->next;
2238 if (node == NULL) {
2239 pthread_cond_wait (&condition, &mutex);
2240 } else {
2241 queue->Unlink (node);
2244 if (node != NULL) {
2245 medias [self_index] = media;
2246 /* At this point the current deployment might be wrong, so avoid
2247 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2248 * here, since it might end up doing a lot of work with the mutex
2249 * locked. */
2250 deployments [self_index] = media->GetUnsafeDeployment ();
2253 pthread_mutex_unlock (&mutex);
2255 if (node == NULL)
2256 continue;
2258 media->SetCurrentDeployment (true, true);
2260 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);
2262 node->closure->Call ();
2264 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node);
2266 delete node;
2269 pthread_mutex_lock (&mutex);
2270 deployments [self_index] = NULL;
2271 medias [self_index] = NULL;
2272 /* if anybody was waiting for us to finish working, notify them */
2273 if (media != NULL)
2274 pthread_cond_signal (&completed_condition);
2275 pthread_mutex_unlock (&mutex);
2277 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index);
2279 return NULL;
2284 * MediaClosure
2287 MediaClosure::MediaClosure (Media *media, MediaCallback *callback, EventObject *context, const char *description)
2288 : EventObject (Type::MEDIACLOSURE, true)
2290 Init (media, callback, context);
2291 this->description = description;
2294 MediaClosure::MediaClosure (Type::Kind object_kind, Media *media, MediaCallback *callback, EventObject *context)
2295 : EventObject (object_kind, true)
2297 Init (media, callback, context);
2300 void
2301 MediaClosure::Init (Media *media, MediaCallback *callback, EventObject *context)
2303 result = MEDIA_INVALID;
2304 description = NULL;
2305 this->callback = callback;
2306 this->context = context;
2307 if (this->context)
2308 this->context->ref ();
2309 this->media = media;
2310 if (this->media)
2311 this->media->ref ();
2313 // put checks at the end so that fields are still initialized, since we can't abort construction.
2314 g_return_if_fail (callback != NULL);
2315 g_return_if_fail (media != NULL);
2318 void
2319 MediaClosure::Dispose ()
2321 if (context) {
2322 context->unref ();
2323 context = NULL;
2326 if (media) {
2327 media->unref ();
2328 media = NULL;
2331 callback = NULL;
2333 EventObject::Dispose ();
2336 void
2337 MediaClosure::Call ()
2339 if (callback) {
2340 result = callback (this);
2341 } else {
2342 result = MEDIA_NO_CALLBACK;
2347 * MediaDisposeObjectClosure
2349 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media *media, MediaCallback *callback, EventObject *context)
2350 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE, media, callback, context)
2354 void
2355 MediaDisposeObjectClosure::Dispose ()
2357 if (!CallExecuted ()) {
2358 // we haven't been executed. do it now.
2359 #if SANITY && DEBUG
2360 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2361 #endif
2362 Call ();
2365 MediaClosure::Dispose ();
2369 * MediaSeekClosure
2371 MediaSeekClosure::MediaSeekClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2372 : MediaClosure (Type::MEDIASEEKCLOSURE, media, callback, context)
2374 this->pts = pts;
2378 * MediaReportSeekCompletedClosure
2381 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, guint64 pts)
2382 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE, media, callback, context)
2384 g_return_if_fail (context != NULL);
2386 this->pts = pts;
2389 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2393 void
2394 MediaReportSeekCompletedClosure::Dispose ()
2396 MediaClosure::Dispose ();
2400 * MediaGetFrameClosure
2403 MediaGetFrameClosure::MediaGetFrameClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, IMediaStream *stream)
2404 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2406 this->stream = NULL;
2408 g_return_if_fail (context != NULL);
2409 g_return_if_fail (stream != NULL);
2411 this->stream = stream;
2412 // this->stream->ref ();
2414 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2417 MediaGetFrameClosure::~MediaGetFrameClosure ()
2419 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2422 void
2423 MediaGetFrameClosure::Dispose ()
2425 if (stream) {
2426 // stream->unref ();
2427 stream = NULL;
2430 MediaClosure::Dispose ();
2431 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2435 * MediaReportFrameCompletedClosure
2438 MediaReportFrameCompletedClosure::MediaReportFrameCompletedClosure (Media *media, MediaCallback *callback, IMediaDemuxer *context, MediaFrame *frame)
2439 : MediaClosure (Type::MEDIAGETFRAMECLOSURE, media, callback, context)
2441 this->frame = NULL;
2443 g_return_if_fail (context != NULL);
2445 this->frame = frame;
2446 if (this->frame)
2447 this->frame->ref ();
2450 void
2451 MediaReportFrameCompletedClosure::Dispose ()
2453 if (frame) {
2454 frame->unref ();
2455 frame = NULL;
2458 MediaClosure::Dispose ();
2462 * IMediaStream
2465 IMediaStream::IMediaStream (Type::Kind kind, Media *media) : IMediaObject (kind, media)
2467 context = NULL;
2469 extra_data_size = 0;
2470 extra_data = NULL;
2472 duration = 0;
2474 decoder = NULL;
2475 codec_id = 0;
2476 codec = NULL;
2478 min_padding = 0;
2479 index = -1;
2480 selected = false;
2481 input_ended = false;
2482 output_ended = false;
2484 first_pts = G_MAXUINT64; // The first pts in the stream, initialized to G_MAXUINT64
2485 last_popped_pts = G_MAXUINT64; // The pts of the last frame returned, initialized to G_MAXUINT64
2486 last_enqueued_pts = G_MAXUINT64; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2487 last_available_pts = 0; // The pts of the last available frame, initialized to 0
2490 void
2491 IMediaStream::Dispose ()
2493 if (decoder) {
2494 IMediaDecoder *d = decoder;
2495 decoder = NULL;
2496 d->Dispose ();
2497 d->unref ();
2499 g_free (extra_data);
2500 extra_data = NULL;
2501 g_free (codec);
2502 codec = NULL;
2504 ClearQueue ();
2505 IMediaObject::Dispose ();
2508 char *
2509 IMediaStream::CreateCodec (int codec_id)
2511 switch (codec_id) {
2512 case CODEC_WMV1: return g_strdup ("wmv1");
2513 case CODEC_WMV2: return g_strdup ("wmv2");
2514 case CODEC_WMV3: return g_strdup ("wmv3");
2515 case CODEC_WMVA: return g_strdup ("wmva");
2516 case CODEC_WVC1: return g_strdup ("vc1");
2517 case CODEC_RGBA: return g_strdup ("rgba");
2518 case CODEC_YV12: return g_strdup ("yv12");
2519 case CODEC_MP3: return g_strdup ("mp3");
2520 case CODEC_WMAV1: return g_strdup ("wmav1");
2521 case CODEC_WMAV2: return g_strdup ("wmav2");
2522 case CODEC_WMAV3: return g_strdup ("wmav3");
2523 case CODEC_PCM: return g_strdup ("pcm");
2524 default:
2525 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id);
2527 /* This algorithm needs testing.
2528 char *result;
2529 int size, current;
2530 int a = (codec_id & 0x000000FF);
2531 int b = (codec_id & 0x0000FF00) >> 8;
2532 int c = (codec_id & 0x00FF0000) >> 16;
2533 int d = (codec_id & 0xFF000000) >> 24;
2535 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2537 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2539 result = (char *) g_malloc (size + 1);
2540 current = 0;
2541 if (a)
2542 result [current++] = (char) a;
2543 if (b)
2544 result [current++] = (char) b;
2545 if (c)
2546 result [current++] = (char) c;
2547 if (d)
2548 result [current++] = (char) d;
2549 result [current] = 0;
2551 return g_strdup ("<unknown>");
2556 bool
2557 IMediaStream::IsQueueEmpty ()
2559 return queue.IsEmpty ();
2562 const char *
2563 IMediaStream::GetStreamTypeName ()
2565 switch (GetType ()) {
2566 case MediaTypeVideo: return "Video";
2567 case MediaTypeAudio: return "Audio";
2568 case MediaTypeMarker: return "Marker";
2569 default: return "Unknown";
2573 void
2574 IMediaStream::ReportSeekCompleted ()
2576 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2577 input_ended = false;
2578 output_ended = false;
2579 ClearQueue ();
2580 if (decoder != NULL)
2581 decoder->ReportSeekCompleted ();
2584 IMediaDemuxer *
2585 IMediaStream::GetDemuxerReffed ()
2587 Media *media;
2588 IMediaDemuxer *result;
2590 if (IsDisposed ())
2591 return NULL;
2593 media = GetMediaReffed ();
2595 g_return_val_if_fail (media != NULL, NULL);
2597 result = media->GetDemuxerReffed ();
2599 media->unref ();
2601 return result;
2604 IMediaDecoder *
2605 IMediaStream::GetDecoder ()
2607 return decoder;
2610 void
2611 IMediaStream::SetDecoder (IMediaDecoder *value)
2613 if (decoder)
2614 decoder->unref ();
2615 decoder = value;
2616 if (decoder)
2617 decoder->ref ();
2620 bool
2621 IMediaStream::GetOutputEnded ()
2623 return output_ended;
2626 void
2627 IMediaStream::SetOutputEnded (bool value)
2629 output_ended = value;
2632 bool
2633 IMediaStream::GetInputEnded ()
2635 return input_ended;
2638 void
2639 IMediaStream::SetInputEnded (bool value)
2641 input_ended = value;
2642 if (GetDecoder () != NULL)
2643 GetDecoder ()->ReportInputEnded ();
2646 guint64
2647 IMediaStream::GetBufferedSize ()
2649 guint64 result;
2651 queue.Lock ();
2652 if (first_pts == G_MAXUINT64 || last_enqueued_pts == G_MAXUINT64)
2653 result = 0;
2654 else if (last_popped_pts == G_MAXUINT64)
2655 result = last_enqueued_pts - first_pts;
2656 else
2657 result = last_enqueued_pts - last_popped_pts;
2658 queue.Unlock ();
2660 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",
2661 GET_OBJ_ID (this), codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts), MilliSeconds_FromPts (result));
2663 return result;
2667 #if DEBUG
2668 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2670 void
2671 IMediaStream::PrintBufferInformation ()
2673 guint64 buffer_size = GetBufferedSize ();
2675 printf (" <%s: ", codec);
2677 if (GetSelected ()) {
2678 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>",
2679 TO_MS (buffer_size), TO_MS (first_pts), TO_MS (last_popped_pts),
2680 TO_MS (last_enqueued_pts), queue.Length ());
2681 } else {
2682 printf ("(not selected) >");
2685 #endif
2687 void
2688 IMediaStream::EnqueueFrame (MediaFrame *frame)
2690 bool first = false;
2691 guint64 seeked_to_pts = 0;
2692 Media *media;
2693 IMediaDemuxer *demuxer = NULL;
2694 /* Add nodes to be deleted here, they'll automaticall be deleted when the method exits. */
2695 /* The reason for doing this at method exit is to not do deletion (with unrefs, etc) with a mutex held */
2696 List trash;
2698 g_return_if_fail (Media::InMediaThread ());
2700 media = GetMediaReffed ();
2701 g_return_if_fail (media != NULL);
2703 if (media->IsStopped ()) {
2704 /* We need to enqueue one frame so that we can render the first frame for a stopped media element */
2705 if (!IsQueueEmpty ()) {
2706 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame (we already have at least one frame).\n", frame);
2707 goto cleanup;
2708 } else {
2709 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, but enqueing since we're empty.\n", frame);
2713 if (frame->buffer == NULL) {
2714 /* for some reason there is no output from the decoder, possibly because it needs more data from the demuxer before outputting anything */
2715 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No data in frame, not storing it.\n", frame);
2716 goto cleanup;
2719 demuxer = GetDemuxerReffed ();
2720 if (demuxer == NULL) {
2721 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No demuxer.\n", frame);
2722 goto cleanup;
2724 seeked_to_pts = demuxer->GetSeekedToPts ();
2725 demuxer->unref ();
2727 queue.Lock ();
2728 if (first_pts == G_MAXUINT64)
2729 first_pts = frame->pts;
2731 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
2733 #if 0
2734 if (last_enqueued_pts > frame->pts && last_enqueued_pts != G_MAXUINT64 && frame->event != FrameEventEOF && frame->buflen > 0) {
2735 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, "
2736 "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",
2737 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2738 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), frame, frame->buflen, MilliSeconds_FromPts (frame->pts));
2740 #endif
2742 last_enqueued_pts = frame->pts;
2743 first = queue.LinkedList ()->Length () == 0;
2744 queue.LinkedList ()->Append (new StreamNode (frame));
2746 if (seeked_to_pts != G_MAXUINT64 && first_pts < seeked_to_pts
2747 && (GetObjectType () == Type::AUDIOSTREAM || GetObjectType () == Type::VIDEOSTREAM)) {
2748 StreamNode *last_key_frame = NULL;
2749 StreamNode *node;
2750 StreamNode *n;
2752 /* we need to remove any frames before the last key frame before the seeked-to pts */
2754 /* find the last key frame below the seeked-to pts */
2755 node = (StreamNode *) queue.LinkedList ()->First ();
2756 while (node != NULL && node->GetFrame ()->pts < seeked_to_pts) {
2757 if (GetObjectType () == Type::AUDIOSTREAM || node->GetFrame ()->IsKeyFrame ())
2758 last_key_frame = node;
2760 node = (StreamNode *) node->next;
2763 if (last_key_frame != NULL) {
2764 /* remove any frames before that last key frame */
2765 node = (StreamNode *) last_key_frame->prev;
2766 while (node != NULL) {
2767 n = (StreamNode *) node->prev;
2768 queue.LinkedList ()->Unlink (node);
2769 trash.Append (node);
2770 node = n;
2773 /* update the first pts to point to the real first pts */
2774 node = (StreamNode *) queue.LinkedList ()->First ();
2775 guint64 next_first_pts = node == NULL ? G_MAXUINT64 : node->GetFrame ()->pts;
2776 LOG_PIPELINE ("%s::EnqueueFrame (): setting first_pts to: %" G_GUINT64_FORMAT ", from %" G_GUINT64_FORMAT " (demuxer first pts: %" G_GUINT64_FORMAT ")\n",
2777 GetTypeName (), first_pts, next_first_pts, seeked_to_pts);
2778 first_pts = next_first_pts;
2782 queue.Unlock ();
2784 SetLastAvailablePts (frame->pts);
2786 if (first)
2787 EmitSafe (FirstFrameEnqueuedEvent);
2789 FrameEnqueued ();
2791 cleanup:
2792 media->unref ();
2794 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",
2795 codec, first, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2796 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts - first_pts), frame, frame->buflen);
2799 MediaFrame *
2800 IMediaStream::PopFrame ()
2802 MediaFrame *result = NULL;
2803 StreamNode *node = NULL;
2805 // We use the queue lock to synchronize access to
2806 // last_popped_pts/last_enqueued_pts/first_pts
2808 queue.Lock ();
2809 node = (StreamNode *) queue.LinkedList ()->First ();
2810 if (node != NULL) {
2811 result = node->GetFrame ();
2812 result->ref ();
2813 queue.LinkedList ()->Remove (node);
2814 last_popped_pts = result->pts;
2816 queue.Unlock ();
2818 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",
2819 codec, MilliSeconds_FromPts (first_pts), MilliSeconds_FromPts (last_popped_pts), MilliSeconds_FromPts (last_enqueued_pts),
2820 MilliSeconds_FromPts (last_popped_pts != G_MAXUINT64 ? last_enqueued_pts - last_popped_pts : last_enqueued_pts), result, result ? result->buflen : 0);
2822 if (!input_ended && !output_ended && result != NULL) {
2823 IMediaDemuxer *demuxer = GetDemuxerReffed ();
2824 if (demuxer != NULL) {
2825 demuxer->FillBuffers ();
2826 demuxer->unref ();
2830 return result;
2833 void
2834 IMediaStream::ClearQueue ()
2836 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2837 queue.Lock ();
2838 queue.LinkedList ()->Clear (true);
2839 first_pts = G_MAXUINT64;
2840 last_popped_pts = G_MAXUINT64;
2841 last_enqueued_pts = G_MAXUINT64;
2842 queue.Unlock ();
2845 void
2846 IMediaStream::SetSelected (bool value)
2848 IMediaDemuxer *demuxer;
2850 selected = value;
2852 demuxer = GetDemuxerReffed ();
2854 if (demuxer != NULL) {
2855 demuxer->UpdateSelected (this);
2856 demuxer->unref ();
2861 * IMediaStream.StreamNode
2864 IMediaStream::StreamNode::StreamNode (MediaFrame *f)
2866 frame = f;
2867 frame->ref ();
2870 IMediaStream::StreamNode::~StreamNode ()
2872 frame->unref ();
2873 frame = NULL;
2876 * IMediaDemuxer
2879 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media, IMediaSource *source) : IMediaObject (kind, media)
2881 this->source = source;
2882 this->source->ref ();
2883 stream_count = 0;
2884 streams = NULL;
2885 opened = false;
2886 opening = false;
2887 seeking = false;
2888 pending_stream = NULL;
2889 pending_fill_buffers = false;
2890 seeked_to_pts = G_MAXUINT64;
2893 IMediaDemuxer::IMediaDemuxer (Type::Kind kind, Media *media)
2894 : IMediaObject (kind, media)
2896 source = NULL;
2897 stream_count = 0;
2898 streams = NULL;
2899 opened = false;
2900 opening = false;
2901 seeking = false;
2902 pending_stream = NULL;
2903 pending_fill_buffers = false;
2904 seeked_to_pts = G_MAXUINT64;
2907 void
2908 IMediaDemuxer::Dispose ()
2910 if (streams != NULL) {
2911 IMediaStream **tmp = streams;
2912 int stream_count = this->stream_count;
2913 streams = NULL;
2914 for (int i = 0; i < stream_count; i++) {
2915 tmp [i]->Dispose ();
2916 tmp [i]->unref ();
2918 g_free (tmp);
2920 if (source) {
2921 source->unref ();
2922 source = NULL;
2924 if (pending_stream != NULL) {
2925 pending_stream->unref ();
2926 pending_stream = NULL;
2928 opened = false;
2929 IMediaObject::Dispose ();
2932 MediaResult
2933 IMediaDemuxer::OpenCallback (MediaClosure *closure)
2935 IMediaDemuxer *demuxer;
2937 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure);
2939 demuxer = (IMediaDemuxer *) closure->GetContext ();
2940 demuxer->OpenDemuxerAsync ();
2942 return MEDIA_SUCCESS;
2945 void
2946 IMediaDemuxer::EnqueueOpen ()
2948 MediaClosure *closure;
2949 Media *media = GetMediaReffed ();
2951 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2953 if (media == NULL)
2954 return;
2956 closure = new MediaClosure (media, OpenCallback, this, "IMediaDemuxer::OpenCallback");
2957 media->EnqueueWork (closure, false);
2958 closure->unref ();
2959 media->unref ();
2962 void
2963 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2965 Media *media = GetMediaReffed ();
2967 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media);
2969 opened = true;
2970 opening = false;
2972 // Media might be null if we got disposed for some reason.
2973 if (!media)
2974 return;
2976 media->ReportOpenDemuxerCompleted ();
2977 media->unref ();
2980 void
2981 IMediaDemuxer::ReportGetFrameProgress (double progress)
2983 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress);
2986 void
2987 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream *stream)
2989 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream);
2992 void
2993 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind, gint64 value)
2995 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %" G_GINT64_FORMAT ")\n", kind, value);
2998 void
2999 IMediaDemuxer::EnqueueReportGetFrameCompleted (MediaFrame *frame)
3001 Media *media = GetMediaReffed ();
3003 if (media == NULL)
3004 return;
3006 MediaClosure *closure = new MediaReportFrameCompletedClosure (media, ReportGetFrameCompletedCallback, this, frame);
3007 media->EnqueueWork (closure);
3008 closure->unref ();
3009 media->unref ();
3012 MediaResult
3013 IMediaDemuxer::ReportGetFrameCompletedCallback (MediaClosure *closure)
3015 MediaReportFrameCompletedClosure *c = (MediaReportFrameCompletedClosure *) closure;
3017 g_return_val_if_fail (c != NULL, MEDIA_FAIL);
3018 g_return_val_if_fail (c->GetDemuxer () != NULL, MEDIA_FAIL);
3020 c->GetDemuxer ()->ReportGetFrameCompleted (c->GetFrame ());
3022 return MEDIA_SUCCESS;
3025 void
3026 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame *frame)
3028 Media *media;
3030 g_return_if_fail (frame == NULL || (frame != NULL && frame->stream != NULL));
3031 g_return_if_fail (pending_stream != NULL);
3033 media = GetMediaReffed ();
3035 g_return_if_fail (media != NULL);
3037 /* ensure we're on a media thread */
3038 if (!Media::InMediaThread ()) {
3039 EnqueueReportGetFrameCompleted (frame);
3040 goto cleanup;
3043 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);
3045 if (frame == NULL) {
3046 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame, pending_stream->GetStreamTypeName ());
3047 // No more data for this stream
3048 pending_stream->SetInputEnded (true);
3049 } else if (!frame->stream->IsDisposed ()) {
3050 IMediaDecoder *decoder = frame->stream->GetDecoder ();
3051 if (decoder != NULL)
3052 decoder->DecodeFrameAsync (frame, true /* always enqueue */);
3055 pending_stream->unref ();
3056 pending_stream = NULL; // not waiting for anything more
3058 // enqueue some more
3059 FillBuffers ();
3061 cleanup:
3062 if (media)
3063 media->unref ();
3066 MediaResult
3067 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure *c)
3069 MediaReportSeekCompletedClosure *closure = (MediaReportSeekCompletedClosure *) c;
3070 IMediaDemuxer *demuxer;
3072 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
3073 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
3075 demuxer = (IMediaDemuxer *) closure->GetContext ();
3076 demuxer->ReportSeekCompleted (closure->GetPts ());
3078 return MEDIA_SUCCESS;
3081 void
3082 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts)
3084 Media *media = GetMediaReffed ();
3086 if (media == NULL)
3087 return;
3089 MediaClosure *closure = new MediaReportSeekCompletedClosure (media, ReportSeekCompletedCallback, this, pts);
3090 media->EnqueueWork (closure);
3091 closure->unref ();
3092 media->unref ();
3095 void
3096 IMediaDemuxer::ReportSeekCompleted (guint64 pts)
3098 Media *media;
3100 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT ")\n", pts);
3102 g_return_if_fail (seeking);
3104 if (!Media::InMediaThread ()) {
3105 EnqueueReportSeekCompleted (pts);
3106 return;
3109 #if SANITY
3110 if (pending_stream != NULL)
3111 printf ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT "): we can't be waiting for a frame now.\n", pts);
3112 #endif
3114 media = GetMediaReffed ();
3116 g_return_if_fail (media != NULL);
3118 /* We need to call ReportSeekCompleted once for every time SeekAsync(pts) was called */
3119 for (int i = 0; i < GetStreamCount (); i++) {
3120 IMediaStream *stream = GetStream (i);
3122 if (stream == NULL)
3123 continue;
3125 stream->ReportSeekCompleted ();
3128 mutex.Lock ();
3129 seeks.RemoveAt (0);
3130 seeking = !seeks.IsEmpty ();
3131 mutex.Unlock ();
3133 media->ReportSeekCompleted (pts);
3134 media->unref ();
3136 if (!seeking) {
3137 seeked_to_pts = pts;
3138 pending_fill_buffers = false;
3139 FillBuffers ();
3140 } else {
3141 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT "): still pending seeks, enqueuing another seek.\n", pts);
3142 EnqueueSeek ();
3145 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT ") [Done]\n", pts);
3148 void
3149 IMediaDemuxer::OpenDemuxerAsync ()
3151 g_return_if_fail (opened == false);
3153 opening = true;
3154 opened = false;
3155 OpenDemuxerAsyncInternal ();
3158 MediaResult
3159 IMediaDemuxer::GetFrameCallback (MediaClosure *c)
3161 MediaGetFrameClosure *closure = (MediaGetFrameClosure *) c;
3162 IMediaDemuxer *demuxer;
3164 g_return_val_if_fail (closure != NULL, MEDIA_FAIL);
3165 g_return_val_if_fail (closure->GetStream () != NULL, MEDIA_FAIL);
3166 g_return_val_if_fail (closure->GetContext () != NULL, MEDIA_FAIL);
3168 demuxer = (IMediaDemuxer *) closure->GetContext ();
3169 demuxer->GetFrameAsync (closure->GetStream ());
3171 return MEDIA_SUCCESS;
3174 void
3175 IMediaDemuxer::EnqueueGetFrame (IMediaStream *stream)
3177 g_return_if_fail (pending_stream == NULL); // we can't be waiting for another frame.
3179 Media *media = GetMediaReffed ();
3181 if (media == NULL)
3182 return;
3184 MediaClosure *closure = new MediaGetFrameClosure (media, GetFrameCallback, this, stream);
3185 media->EnqueueWork (closure);
3186 closure->unref ();
3187 media->unref ();
3190 void
3191 IMediaDemuxer::GetFrameAsync (IMediaStream *stream)
3193 Media *media = NULL;
3195 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream, stream->GetStreamTypeName (), Media::InMediaThread ());
3197 if (!Media::InMediaThread ()) {
3198 EnqueueGetFrame (stream);
3199 return;
3202 if (seeking) {
3203 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (): delayed since we're waiting for a seek.\n");
3204 goto cleanup;
3207 if (pending_stream != NULL) {
3208 /* we're already waiting for a frame */
3209 goto cleanup;
3212 media = GetMediaReffed ();
3214 g_return_if_fail (media != NULL);
3216 if (stream != NULL) {
3217 pending_stream = stream;
3218 pending_stream->ref ();
3219 GetFrameAsyncInternal (stream);
3222 cleanup:
3223 if (media)
3224 media->unref ();
3227 MediaResult
3228 IMediaDemuxer::SeekCallback (MediaClosure *closure)
3230 MediaSeekClosure *seek = (MediaSeekClosure *) closure;
3231 seek->GetDemuxer ()->SeekAsync ();
3232 return MEDIA_SUCCESS;
3235 void
3236 IMediaDemuxer::EnqueueSeek ()
3238 Media *media = GetMediaReffed ();
3239 MediaSeekClosure *closure;
3241 g_return_if_fail (media != NULL);
3243 closure = new MediaSeekClosure (media, SeekCallback, this, 0);
3244 media->EnqueueWork (closure, true);
3245 closure->unref ();
3246 media->unref ();
3249 void
3250 IMediaDemuxer::SeekAsync ()
3252 guint64 pts = G_MAXUINT64;
3254 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (), seeking: %i\n", seeking);
3256 g_return_if_fail (Media::InMediaThread ());
3258 seeking = true; /* this ensures that we stop demuxing frames asap */
3260 if (pending_stream != NULL) {
3261 /* we're waiting for the decoder to decode a frame, wait a bit with the seek */
3262 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i waiting for a frame, postponing seek\n", GET_OBJ_ID (this));
3263 EnqueueSeek ();
3264 return;
3267 mutex.Lock ();
3268 if (!seeks.IsEmpty ())
3269 pts = ((PtsNode *) seeks.First ())->pts;
3270 mutex.Unlock ();
3272 if (pts == G_MAXUINT64) {
3273 LOG_PIPELINE ("IMediaDemuxer.:SeekAsync (): %i no pending seek?\n", GET_OBJ_ID (this));
3274 seeking = false;
3275 return;
3278 /* Ask the demuxer to seek */
3279 /* at this point the pipeline shouldn't be doing anything else (for this media) */
3280 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i seeking to %" G_GUINT64_FORMAT "\n", GET_OBJ_ID (this), pts);
3281 Media *media = GetMediaReffed ();
3282 if (media) {
3283 media->EmitSafe (Media::SeekingEvent);
3284 media->unref ();
3286 SeekAsyncInternal (pts);
3289 void
3290 IMediaDemuxer::SeekAsync (guint64 pts)
3292 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%" G_GUINT64_FORMAT ")\n", pts);
3293 VERIFY_MAIN_THREAD;
3295 if (IsDisposed ())
3296 return;
3298 mutex.Lock ();
3299 seeks.Append (new PtsNode (pts));
3300 mutex.Unlock ();
3302 EnqueueSeek ();
3305 void
3306 IMediaDemuxer::ClearBuffers ()
3308 pending_fill_buffers = false;
3310 /* Clear all the buffer queues */
3311 for (int i = 0; i < GetStreamCount (); i++) {
3312 IMediaStream *stream = GetStream (i);
3314 if (stream == NULL)
3315 continue;
3317 stream->ClearQueue ();
3321 MediaResult
3322 IMediaDemuxer::FillBuffersCallback (MediaClosure *closure)
3324 IMediaDemuxer *demuxer = (IMediaDemuxer *) closure->GetContext ();
3325 demuxer->FillBuffersInternal ();
3326 return MEDIA_SUCCESS;
3329 void
3330 IMediaDemuxer::FillBuffers ()
3332 Media *media = NULL;
3333 MediaClosure *closure;
3334 bool enqueue = true;
3336 mutex.Lock ();
3337 if (pending_fill_buffers) {
3338 // there's already a FillBuffers request enqueued
3339 enqueue = false;
3340 } else {
3341 media = GetMediaReffed ();
3342 if (media == NULL) {
3343 enqueue = false;
3344 } else {
3345 enqueue = true;
3346 pending_fill_buffers = true;
3349 mutex.Unlock ();
3351 if (enqueue) {
3352 closure = new MediaClosure (media, FillBuffersCallback, this, "IMediaDemuxer::FillBuffersCallback");
3353 media->EnqueueWork (closure);
3354 closure->unref ();
3357 if (media != NULL)
3358 media->unref ();
3361 void
3362 IMediaDemuxer::FillBuffersInternal ()
3364 IMediaStream *stream;
3365 IMediaStream *request_stream = NULL;
3366 guint64 min_buffered_size = G_MAXUINT64;
3367 Media *media = GetMediaReffed ();
3368 guint64 buffering_time = 0;
3369 guint64 buffered_size = 0;
3370 int ended = 0;
3371 int media_streams = 0;
3373 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");
3375 mutex.Lock ();
3376 pending_fill_buffers = false;
3377 mutex.Unlock ();
3379 if (IsDisposed ())
3380 goto cleanup;
3382 // If we're waiting for something, there's nothing to do here.
3383 if (pending_stream != NULL)
3384 goto cleanup;
3386 // Find the stream with the smallest buffered size, and request a frame from that stream.
3387 g_return_if_fail (media != NULL);
3389 buffering_time = media->GetBufferingTime ();
3391 if (buffering_time == 0) {
3392 // Play as soon as possible.
3393 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3394 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3395 // all streams have 1 frame queued.
3396 buffering_time = 1;
3399 for (int i = 0; i < GetStreamCount (); i++) {
3400 IMediaDecoder *decoder = NULL;
3402 stream = GetStream (i);
3403 if (!stream->GetSelected ())
3404 continue;
3406 if (stream->GetType () != MediaTypeVideo &&
3407 stream->GetType () != MediaTypeAudio)
3408 continue;
3410 media_streams++;
3411 if (stream->GetOutputEnded ()) {
3412 ended++;
3413 continue; // this stream has ended.
3416 decoder = stream->GetDecoder ();
3417 if (decoder == NULL) {
3418 fprintf (stderr, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream->GetStreamTypeName (), GET_OBJ_ID (stream), stream->GetRefCount ());
3419 continue; // no decoder??
3422 buffered_size = stream->GetBufferedSize ();
3423 min_buffered_size = MIN (min_buffered_size, buffered_size);
3425 if (buffered_size >= buffering_time)
3426 continue; // this stream has enough data buffered.
3428 if (!decoder->IsDecoderQueueEmpty ())
3429 continue; // this stream is waiting for data to be decoded.
3431 if (buffered_size <= min_buffered_size)
3432 request_stream = stream;
3434 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",
3435 stream->codec, GET_OBJ_ID (stream), MilliSeconds_FromPts (buffered_size), MilliSeconds_FromPts (buffering_time), MilliSeconds_FromPts (stream->GetLastPoppedPts ()));
3438 if (request_stream != NULL) {
3439 if (media->IsStopped ()) {
3440 if (!request_stream->IsQueueEmpty ()) {
3441 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, and we have frames in the buffer.\n");
3442 goto cleanup;
3443 } else {
3444 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, but the buffer is empty, continuing\n");
3448 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));
3449 GetFrameAsync (request_stream);
3452 if (media_streams > 0) {
3453 if (ended == media_streams) {
3454 media->ReportBufferingProgress (1.0);
3455 } else {
3456 if (min_buffered_size > 0 && buffering_time > 0) {
3457 double progress = ((double) min_buffered_size / (double) buffering_time);
3458 media->ReportBufferingProgress (progress);
3463 cleanup:
3464 if (media)
3465 media->unref ();
3467 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT " ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3470 guint64
3471 IMediaDemuxer::GetBufferedSize ()
3473 guint64 result = G_MAXUINT64;
3474 IMediaStream *stream;
3476 for (int i = 0; i < GetStreamCount (); i++) {
3477 stream = GetStream (i);
3478 if (!stream->GetSelected ())
3479 continue;
3481 if (stream->GetType () != MediaTypeVideo && stream->GetType () != MediaTypeAudio)
3482 continue;
3484 result = MIN (result, stream->GetBufferedSize ());
3487 return result;
3490 guint64
3491 IMediaDemuxer::GetLastAvailablePts ()
3493 guint64 result = G_MAXUINT64;
3494 IMediaStream *stream;
3496 for (int i = 0; i < GetStreamCount (); i++) {
3497 stream = GetStream (i);
3499 if (stream == NULL || !stream->GetSelected ())
3500 continue;
3502 result = MIN (result, stream->GetLastAvailablePts ());
3505 if (result == G_MAXUINT64)
3506 result = 0;
3508 return result;
3511 #if DEBUG
3512 void
3513 IMediaDemuxer::PrintBufferInformation ()
3515 printf ("Buffer: %" G_GINT64_FORMAT "", MilliSeconds_FromPts (GetBufferedSize ()));
3516 for (int i = 0; i < GetStreamCount (); i++) {
3517 GetStream (i)->PrintBufferInformation ();
3519 printf ("\n");
3521 #endif
3523 guint64
3524 IMediaDemuxer::GetDuration ()
3526 guint64 result = 0;
3527 for (int i = 0; i < GetStreamCount (); i++)
3528 result = MAX (result, GetStream (i)->duration);
3529 return result;
3532 IMediaStream*
3533 IMediaDemuxer::GetStream (int index)
3535 return (index < 0 || index >= stream_count) ? NULL : streams [index];
3539 * MediaFrame
3542 MediaFrame::MediaFrame (IMediaStream *stream)
3543 : EventObject (Type::MEDIAFRAME, true)
3545 Initialize ();
3547 g_return_if_fail (stream != NULL);
3549 this->stream = stream;
3550 this->stream->ref ();
3553 MediaFrame::MediaFrame (IMediaStream *stream, guint8 *buffer, guint32 buflen, guint64 pts, bool keyframe)
3554 : EventObject (Type::MEDIAFRAME, true)
3556 Initialize ();
3558 g_return_if_fail (stream != NULL);
3560 this->stream = stream;
3561 this->stream->ref ();
3562 this->buffer = buffer;
3563 this->buflen = buflen;
3564 this->pts = pts;
3566 #if 0
3567 if (buflen > 4 && false) {
3568 printf ("MediaFrame::MediaFrame () %s buffer: ", stream->GetStreamTypeName ());
3569 for (int i = 0; i < 4; i++)
3570 printf (" 0x%x", buffer [i]);
3571 printf ("\n");
3573 #endif
3575 if (keyframe)
3576 AddState (MediaFrameKeyFrame);
3579 void
3580 MediaFrame::Initialize ()
3582 decoder_specific_data = NULL;
3583 stream = NULL;
3584 marker = NULL;
3586 duration = 0;
3587 pts = 0;
3589 buffer = NULL;
3590 buflen = 0;
3591 state = 0;
3592 event = 0;
3594 for (int i = 0; i < 4; i++) {
3595 data_stride[i] = 0;
3596 srcStride[i] = 0;
3599 srcSlideY = 0;
3600 srcSlideH = 0;
3601 width = 0;
3602 height = 0;
3605 MediaFrame::~MediaFrame ()
3609 void
3610 MediaFrame::Dispose ()
3612 IMediaDecoder *decoder;
3614 #if SANITY
3615 // We can be called either on the main thread just before destruction
3616 // (in which case there are no races since the code which unreffed us
3617 // is the only code which knows about us), or at any time from the
3618 // media thread.
3620 if (GetRefCount () != 0 && stream != NULL) {
3621 if (!Media::InMediaThread ()) {
3622 // if refcount != 0 we're not being called just before destruction, in which case we should
3623 // only be on the media thread.
3624 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3627 #endif
3629 if (decoder_specific_data != NULL && stream != NULL) {
3630 decoder = stream->GetDecoder ();
3631 if (decoder != NULL)
3632 decoder->Cleanup (this);
3634 g_free (buffer);
3635 buffer = NULL;
3636 if (marker) {
3637 marker->unref ();
3638 marker = NULL;
3640 if (stream) {
3641 stream->unref ();
3642 stream = NULL;
3645 EventObject::Dispose ();
3648 void
3649 MediaFrame::SetSrcSlideY (int value)
3651 srcSlideY = value;
3654 void
3655 MediaFrame::SetSrcSlideH (int value)
3657 srcSlideH = value;
3660 void
3661 MediaFrame::SetSrcStride (int a, int b, int c, int d)
3663 srcStride [0] = a;
3664 srcStride [1] = b;
3665 srcStride [2] = c;
3666 srcStride [3] = d;
3669 void
3670 MediaFrame::SetDataStride (guint8* a, guint8* b, guint8* c, guint8* d)
3672 data_stride [0] = a;
3673 data_stride [1] = b;
3674 data_stride [2] = c;
3675 data_stride [3] = d;
3679 * IMediaObject.EventData
3682 IMediaObject::EventData::EventData (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3684 this->event_id = event_id;
3685 this->handler = handler;
3686 this->context = context;
3687 this->context->ref ();
3688 this->invoke_on_main_thread = invoke_on_main_thread;
3691 IMediaObject::EventData::~EventData ()
3693 context->unref ();
3694 context = NULL;
3698 * IMediaObject.EmitData
3701 IMediaObject::EmitData::EmitData (int event_id, EventHandler handler, EventObject *context, EventArgs *args)
3703 this->event_id = event_id;
3704 this->handler = handler;
3705 this->context = context;
3706 this->context->ref ();
3707 this->args = args;
3708 if (this->args)
3709 this->args->ref ();
3712 IMediaObject::EmitData::~EmitData ()
3714 context->unref ();
3715 context = NULL;
3716 if (args) {
3717 args->unref ();
3718 args = NULL;
3723 * IMediaObject
3726 IMediaObject::IMediaObject (Type::Kind kind, Media *media)
3727 : EventObject (kind, true)
3729 this->media = media;
3730 if (this->media)
3731 this->media->ref ();
3732 g_return_if_fail (media != NULL);
3733 events = NULL;
3734 emit_on_main_thread = NULL;
3737 void
3738 IMediaObject::Dispose ()
3741 #if SANITY
3742 // We can be called either on the main thread just before destruction
3743 // (in which case there are no races since the code which unreffed us
3744 // is the only code which knows about us), or at any time from the
3745 // media thread.
3746 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3747 // if refcount != 0 we're not being called just before destruction, in which case we should
3748 // only be on the media thread.
3749 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3751 #endif
3753 media_mutex.Lock ();
3754 if (media) {
3755 media->unref ();
3756 media = NULL;
3758 media_mutex.Unlock ();
3760 event_mutex.Lock ();
3761 delete events;
3762 events = NULL;
3763 if (emit_on_main_thread != NULL) {
3764 delete emit_on_main_thread;
3765 emit_on_main_thread = NULL;
3767 event_mutex.Unlock ();
3769 EventObject::Dispose ();
3772 void
3773 IMediaObject::AddSafeHandler (int event_id, EventHandler handler, EventObject *context, bool invoke_on_main_thread)
3775 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id, handler, context, invoke_on_main_thread);
3776 EventData *ed;
3778 if (!IsDisposed ()) {
3779 ed = new EventData (event_id, handler, context, invoke_on_main_thread);
3780 event_mutex.Lock ();
3781 if (events == NULL)
3782 events = new List ();
3783 events->Append (ed);
3784 event_mutex.Unlock ();
3788 void
3789 IMediaObject::RemoveSafeHandlers (EventObject *context)
3791 EventData *ed;
3792 EventData *next;
3794 event_mutex.Lock ();
3795 if (events != NULL) {
3796 ed = (EventData *) events->First ();
3797 while (ed != NULL) {
3798 next = (EventData *) ed->next;
3799 if (ed->context == context)
3800 events->Remove (ed);
3801 ed = next;
3804 event_mutex.Unlock ();
3807 void
3808 IMediaObject::EmitSafe (int event_id, EventArgs *args)
3810 List *emits = NULL; // The events to emit on this thread.
3811 EventData *ed;
3812 EmitData *emit;
3814 if (events == NULL)
3815 goto cleanup;
3817 // Create a list of all the events to emit
3818 // don't keep the lock while emitting.
3819 event_mutex.Lock ();
3820 if (events != NULL) {
3821 ed = (EventData *) events->First ();
3822 while (ed != NULL) {
3823 if (ed->event_id == event_id) {
3824 emit = new EmitData (event_id, ed->handler, ed->context, args);
3825 if (ed->invoke_on_main_thread) {
3826 if (emit_on_main_thread == NULL)
3827 emit_on_main_thread = new List ();
3828 emit_on_main_thread->Append (emit);
3829 } else {
3830 if (emits == NULL)
3831 emits = new List ();
3832 emits->Append (emit);
3835 ed = (EventData *) ed->next;
3838 event_mutex.Unlock ();
3840 // emit the events to be emitted on this thread
3841 EmitList (emits);
3843 if (Surface::InMainThread ()) {
3844 // if we're already on the main thread,
3845 // we can the events to be emitted
3846 // on the main thread
3847 List *tmp;
3848 event_mutex.Lock ();
3849 tmp = emit_on_main_thread;
3850 emit_on_main_thread = NULL;
3851 event_mutex.Unlock ();
3852 EmitList (tmp);
3853 } else {
3854 AddTickCall (EmitListCallback);
3857 cleanup:
3858 if (args)
3859 args->unref ();
3862 void
3863 IMediaObject::EmitListMain ()
3865 VERIFY_MAIN_THREAD;
3867 List *list;
3868 event_mutex.Lock ();
3869 list = emit_on_main_thread;
3870 emit_on_main_thread = NULL;
3871 event_mutex.Unlock ();
3872 EmitList (list);
3875 void
3876 IMediaObject::EmitListCallback (EventObject *obj)
3878 IMediaObject *media_obj = (IMediaObject *) obj;
3879 media_obj->EmitListMain ();
3882 void
3883 IMediaObject::EmitList (List *list)
3885 EmitData *emit;
3887 if (list == NULL)
3888 return;
3890 emit = (EmitData *) list->First ();
3891 while (emit != NULL) {
3892 emit->handler (this, emit->args, emit->context);
3893 emit = (EmitData *) emit->next;
3896 delete list;
3899 Media *
3900 IMediaObject::GetMediaReffed ()
3902 Media *result;
3903 media_mutex.Lock ();
3904 result = media;
3905 if (result)
3906 result->ref ();
3907 media_mutex.Unlock ();
3908 return result;
3911 void
3912 IMediaObject::ReportErrorOccurred (char const *message)
3914 g_return_if_fail (media != NULL);
3916 media->ReportErrorOccurred (message);
3919 void
3920 IMediaObject::ReportErrorOccurred (MediaResult result)
3922 g_return_if_fail (media != NULL);
3924 media->ReportErrorOccurred (result);
3927 void
3928 IMediaObject::ReportErrorOccurred (ErrorEventArgs *args)
3930 g_return_if_fail (media != NULL);
3932 media->ReportErrorOccurred (args);
3935 void
3936 IMediaObject::SetMedia (Media *value)
3938 media_mutex.Lock ();
3939 if (media)
3940 media->unref ();
3941 media = value;
3942 if (media)
3943 media->ref ();
3944 media_mutex.Unlock ();
3948 * IMediaSource
3951 IMediaSource::IMediaSource (Type::Kind kind, Media *media)
3952 : IMediaObject (kind, media)
3954 pthread_mutexattr_t attribs;
3955 pthread_mutexattr_init (&attribs);
3956 pthread_mutexattr_settype (&attribs, PTHREAD_MUTEX_RECURSIVE);
3957 pthread_mutex_init (&mutex, &attribs);
3958 pthread_mutexattr_destroy (&attribs);
3960 pthread_cond_init (&condition, NULL);
3963 IMediaSource::~IMediaSource ()
3965 pthread_mutex_destroy (&mutex);
3966 pthread_cond_destroy (&condition);
3969 void
3970 IMediaSource::Dispose ()
3972 IMediaObject::Dispose ();
3975 void
3976 IMediaSource::Lock ()
3978 pthread_mutex_lock (&mutex);
3981 void
3982 IMediaSource::Unlock ()
3984 pthread_mutex_unlock (&mutex);
3987 gint32
3988 IMediaSource::ReadSome (void *buf, guint32 n)
3990 gint32 result;
3992 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf, n);
3994 Lock ();
3996 result = ReadInternal (buf, n);
3998 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %" G_GINT64_FORMAT "\n", GET_OBJ_ID (this), buf, n, result, GetPosition ());
4000 Unlock ();
4002 return result;
4005 bool
4006 IMediaSource::ReadAll (void *buf, guint32 n)
4008 gint32 read;
4009 gint64 prev = GetPosition ();
4010 gint64 avail = GetLastAvailablePosition ();
4012 //printf ("IMediaSource::ReadAll (%p, %u), position: %" G_GINT64_FORMAT "\n", buf, n, prev);
4014 read = ReadSome (buf, n);
4016 if ((gint64) read != (gint64) n) {
4017 FileSource *fs = NULL;
4019 if (GetType () == MediaSourceTypeFile)
4020 fs = (FileSource *) this;
4021 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",
4022 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>");
4023 print_stack_trace ();
4026 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf, n, read);
4028 return (gint64) read == (gint64) n;
4031 bool
4032 IMediaSource::Peek (void *buf, guint32 n)
4034 bool result;
4035 gint64 read;
4037 Lock ();
4039 read = PeekInternal (buf, n);
4040 result = read == (gint64) n;
4042 Unlock ();
4044 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %" G_GINT64_FORMAT " bytes.\n", buf, n, result, read);
4046 return result;
4049 bool
4050 IMediaSource::Seek (gint64 offset, int mode)
4052 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%" G_GINT64_FORMAT ", %d = %s)\n",
4053 GET_OBJ_ID (this), ToString (), offset, mode, mode == SEEK_SET ? "SEEK_SET"
4054 : (mode == SEEK_CUR ? "SEEK_CUR" : (mode == SEEK_END ? "SEEK_END" : "<invalid value>")));
4056 bool result;
4057 Lock ();
4058 result = SeekInternal (offset, mode);
4059 Unlock ();
4060 return result;
4063 bool
4064 IMediaSource::IsPositionAvailable (gint64 position, bool *eof)
4066 gint64 available = GetLastAvailablePosition ();
4067 gint64 size = GetSize ();
4069 *eof = false;
4071 if (size != -1 && size < position) {
4072 // Size is known and smaller than the requested position
4073 *eof = true;
4074 return false;
4077 if (available != -1 && available < position) {
4078 // Not everything is available and the available position is smaller than the requested position
4079 *eof = false;
4080 return false;
4083 if (size == -1 && available == -1) {
4084 // Size is not known, but everything is available??
4085 // This is probably due to a bug in the derived *Source class
4086 *eof = false;
4087 fprintf (stderr, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
4088 return false;
4091 return true;
4094 gint64
4095 IMediaSource::GetLastAvailablePosition ()
4097 gint64 result;
4098 Lock ();
4099 result = GetLastAvailablePositionInternal ();
4100 Unlock ();
4101 return result;
4104 gint64
4105 IMediaSource::GetPositionInternal ()
4107 // This method should be overridden (or never called for the classes which doesn't override it).
4108 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4109 print_stack_trace ();
4111 return -1;
4113 bool
4114 IMediaSource::SeekInternal (gint64 offset, int mode)
4116 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);
4117 print_stack_trace ();
4119 return false;
4122 gint32
4123 IMediaSource::ReadInternal (void *buffer, guint32 n)
4125 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);
4126 print_stack_trace ();
4128 return 0;
4131 gint32
4132 IMediaSource::PeekInternal (void *buffer, guint32 n)
4134 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);
4135 print_stack_trace ();
4137 return 0;
4140 gint64
4141 IMediaSource::GetSizeInternal ()
4143 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4144 print_stack_trace ();
4146 return 0;
4149 gint64
4150 IMediaSource::GetPosition ()
4152 gint64 result;
4153 Lock ();
4154 result = GetPositionInternal ();
4155 Unlock ();
4156 return result;
4159 gint64
4160 IMediaSource::GetSize ()
4162 gint64 result;
4163 Lock ();
4164 result = GetSizeInternal ();
4165 Unlock ();
4166 return result;
4170 * IMediaDemuxer
4173 void
4174 IMediaDemuxer::SetStreams (IMediaStream** streams, int count)
4176 this->streams = streams;
4177 this->stream_count = count;
4179 for (int i = 0; i < count; i++)
4180 this->streams [i]->ref ();
4183 gint32
4184 IMediaDemuxer::AddStream (IMediaStream *stream)
4186 g_return_val_if_fail (stream != NULL, -1);
4188 stream_count++;
4189 streams = (IMediaStream **) g_realloc (streams, stream_count * sizeof (IMediaStream *));
4190 streams [stream_count - 1] = stream;
4191 stream->ref ();
4193 return stream_count - 1;
4197 * IMediaDecoder
4200 IMediaDecoder::IMediaDecoder (Type::Kind kind, Media *media, IMediaStream *stream) : IMediaObject (kind, media)
4202 this->stream = NULL;
4204 g_return_if_fail (stream != NULL);
4206 this->stream = stream;
4207 this->stream->ref ();
4209 opening = false;
4210 opened = false;
4211 input_ended = false;
4214 void
4215 IMediaDecoder::Dispose ()
4217 if (stream != NULL) {
4218 IMediaStream *s = stream;
4219 stream = NULL;
4220 s->Dispose ();
4221 s->unref ();
4222 s = NULL;
4225 queue.Clear (true);
4227 IMediaObject::Dispose ();
4230 void
4231 IMediaDecoder::ReportSeekCompleted ()
4233 queue.Clear (true);
4234 input_ended = false;
4235 CleanState ();
4238 void
4239 IMediaDecoder::ReportInputEnded ()
4241 input_ended = true;
4242 if (IsDecoderQueueEmpty ()) {
4243 InputEnded ();
4247 void
4248 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame *frame)
4250 IMediaDemuxer *demuxer;
4251 IMediaStream *stream;
4252 Media *media = NULL;
4254 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %" G_GUINT64_FORMAT " ms\n", frame, frame ? frame->stream->GetStreamTypeName () : "", frame ? MilliSeconds_FromPts (frame->pts) : 0);
4256 g_return_if_fail (frame != NULL);
4258 media = GetMediaReffed ();
4259 g_return_if_fail (media != NULL);
4261 stream = frame->stream;
4262 if (stream == NULL)
4263 goto cleanup;
4265 frame->stream->EnqueueFrame (frame);
4267 demuxer = stream->GetDemuxerReffed ();
4268 if (demuxer != NULL) {
4269 demuxer->FillBuffers ();
4270 demuxer->unref ();
4273 if (input_ended && IsDecoderQueueEmpty ())
4274 InputEnded ();
4276 cleanup:
4277 if (media)
4278 media->unref ();
4281 MediaResult
4282 IMediaDecoder::DecodeFrameCallback (MediaClosure *closure)
4285 IMediaDecoder *decoder = (IMediaDecoder *) closure->GetContext ();
4286 IMediaDecoder::FrameNode *node = (IMediaDecoder::FrameNode *) decoder->queue.Pop ();
4288 if (node != NULL) {
4289 decoder->DecodeFrameAsync (node->frame, false);
4290 delete node;
4293 return MEDIA_SUCCESS;
4296 void
4297 IMediaDecoder::DecodeFrameAsync (MediaFrame *frame, bool enqueue_always)
4299 Media *media;
4301 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame, (frame && frame->stream) ? frame->stream->GetStreamTypeName () : NULL);
4303 if (IsDisposed ())
4304 return;
4306 g_return_if_fail (frame != NULL);
4308 media = GetMediaReffed ();
4310 g_return_if_fail (media != NULL);
4312 if (enqueue_always || !Media::InMediaThread ()) {
4313 MediaClosure *closure = new MediaClosure (media, DecodeFrameCallback, this, "IMediaDecoder::DecodeFrameCallback");
4314 queue.Push (new FrameNode (frame));
4315 media->EnqueueWork (closure);
4316 closure->unref ();
4317 goto cleanup;
4320 DecodeFrameAsyncInternal (frame);
4322 cleanup:
4323 media->unref ();
4326 void
4327 IMediaDecoder::OpenDecoderAsync ()
4329 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4331 g_return_if_fail (opening == false);
4332 g_return_if_fail (opened == false);
4334 opening = true;
4335 OpenDecoderAsyncInternal ();
4338 void
4339 IMediaDecoder::ReportOpenDecoderCompleted ()
4341 Media *media = GetMediaReffed ();
4343 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4345 opening = false;
4346 opened = true;
4348 g_return_if_fail (media != NULL);
4350 media->ReportOpenDecoderCompleted (this);
4351 media->unref ();
4355 * IImageConverter
4358 IImageConverter::IImageConverter (Type::Kind kind, Media *media, VideoStream *stream) : IMediaObject (kind, media)
4360 output_format = MoonPixelFormatNone;
4361 input_format = MoonPixelFormatNone;
4362 this->stream = stream;
4366 * VideoStream
4369 VideoStream::VideoStream (Media *media) : IMediaStream (Type::VIDEOSTREAM, media)
4371 converter = NULL;
4372 bits_per_sample = 0;
4373 pts_per_frame = 0;
4374 initial_pts = 0;
4375 height = 0;
4376 width = 0;
4379 VideoStream::VideoStream (Media *media, int codec_id, guint32 width, guint32 height, guint64 duration, gpointer extra_data, guint32 extra_data_size)
4380 : IMediaStream (Type::VIDEOSTREAM, media)
4382 converter = NULL;
4383 bits_per_sample = 0;
4384 pts_per_frame = 0;
4385 initial_pts = 0;
4386 this->height = height;
4387 this->width = width;
4388 this->duration = duration;
4389 this->codec_id = codec_id;
4390 this->codec = CreateCodec (codec_id);
4391 this->extra_data = extra_data;
4392 this->extra_data_size = extra_data_size;
4395 VideoStream::~VideoStream ()
4399 void
4400 VideoStream::Dispose ()
4402 if (converter) {
4403 converter->Dispose ();
4404 converter->unref ();
4405 converter = NULL;
4407 IMediaStream::Dispose ();
4411 * MediaMarkerFoundClosure
4414 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media *media, MediaCallback *callback, MediaElement *context)
4415 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE, media, callback, context)
4417 marker = NULL;
4420 void
4421 MediaMarkerFoundClosure::Dispose ()
4423 if (marker) {
4424 marker->unref ();
4425 marker = NULL;
4427 MediaClosure::Dispose ();
4430 void
4431 MediaMarkerFoundClosure::SetMarker (MediaMarker *marker)
4433 if (this->marker)
4434 this->marker->unref ();
4435 this->marker = marker;
4436 if (this->marker)
4437 this->marker->ref ();
4441 * MediaMarker
4444 MediaMarker::MediaMarker (const char *type, const char *text, guint64 pts)
4445 : EventObject (Type::MEDIAMARKER)
4447 this->type = g_strdup (type);
4448 this->text = g_strdup (text);
4449 this->pts = pts;
4452 MediaMarker::~MediaMarker ()
4454 g_free (type);
4455 g_free (text);
4459 * MarkerStream
4462 MarkerStream::MarkerStream (Media *media) : IMediaStream (Type::MARKERSTREAM, media)
4464 closure = NULL;
4467 void
4468 MarkerStream::Dispose ()
4470 if (closure) {
4471 closure->unref ();
4472 closure = NULL;
4475 IMediaStream::Dispose ();
4478 void
4479 MarkerStream::MarkerFound (MediaFrame *frame)
4481 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4483 if (GetDecoder () == NULL) {
4484 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4485 return;
4488 GetDecoder ()->DecodeFrameAsync (frame, false);
4491 void
4492 MarkerStream::FrameEnqueued ()
4494 MediaFrame *frame;
4496 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4498 frame = PopFrame ();
4500 if (frame == NULL) {
4501 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4502 return;
4505 if (closure != NULL) {
4506 closure->SetMarker (frame->marker);
4507 closure->Call ();
4508 closure->SetMarker (NULL);
4509 } else {
4510 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4511 mutex.Lock ();
4512 list.Append (new MediaMarker::Node (frame->marker));
4513 mutex.Unlock ();
4516 frame->unref ();
4519 MediaMarker *
4520 MarkerStream::Pop ()
4522 MediaMarker *result = NULL;
4523 MediaMarker::Node *node;
4525 mutex.Lock ();
4526 node = (MediaMarker::Node *) list.First ();
4527 if (node != NULL) {
4528 result = node->marker;
4529 result->ref ();
4530 list.Remove (node);
4532 mutex.Unlock ();
4534 return result;
4537 void
4538 MarkerStream::SetCallback (MediaMarkerFoundClosure *closure)
4540 if (this->closure)
4541 this->closure->unref ();
4542 this->closure = closure;
4543 if (this->closure)
4544 this->closure->ref ();
4548 * MediaWork
4550 MediaWork::MediaWork (MediaClosure *c)
4552 g_return_if_fail (c != NULL);
4554 closure = c;
4555 closure->ref ();
4558 MediaWork::~MediaWork ()
4560 g_return_if_fail (closure != NULL);
4562 closure->unref ();
4563 closure = NULL;
4567 * PassThroughDecoderInfo
4570 bool
4571 PassThroughDecoderInfo::Supports (const char *codec)
4573 const char *video_fourccs [] = { "yv12", "rgba", NULL };
4574 const char *audio_fourccs [] = { "pcm", NULL };
4576 for (int i = 0; video_fourccs [i] != NULL; i++)
4577 if (!strcmp (codec, video_fourccs [i]))
4578 return true;
4580 for (int i = 0; audio_fourccs [i] != NULL; i++)
4581 if (!strcmp (codec, audio_fourccs [i]))
4582 return true;
4584 return false;
4588 * PassThroughDecoder
4591 PassThroughDecoder::PassThroughDecoder (Media *media, IMediaStream *stream)
4592 : IMediaDecoder (Type::PASSTHROUGHDECODER, media, stream)
4596 void
4597 PassThroughDecoder::Dispose ()
4599 IMediaDecoder::Dispose ();
4602 void
4603 PassThroughDecoder::OpenDecoderAsyncInternal ()
4605 const char *fourcc = GetStream ()->GetCodec ();
4607 if (!strcmp (fourcc, "yv12")) {
4608 SetPixelFormat (MoonPixelFormatYUV420P);
4609 } else if (!strcmp (fourcc, "rgba")) {
4610 SetPixelFormat (MoonPixelFormatRGBA32);
4611 } else if (!strcmp (fourcc, "pcm")) {
4612 // nothing to do here
4613 } else {
4614 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc));
4615 return;
4618 ReportOpenDecoderCompleted ();
4621 void
4622 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4624 frame->AddState (MediaFrameDecoded);
4625 if (GetPixelFormat () == MoonPixelFormatYUV420P) {
4626 VideoStream *vs = (VideoStream *) GetStream ();
4628 frame->width = vs->width;
4629 frame->height = vs->height;
4631 frame->data_stride[0] = frame->buffer;
4632 frame->data_stride[1] = frame->buffer + (frame->width*frame->height);
4633 frame->data_stride[2] = frame->buffer + (frame->width*frame->height)+(frame->width/2*frame->height/2);
4634 frame->buffer = NULL;
4635 frame->srcStride[0] = frame->width;
4636 frame->srcSlideY = frame->width;
4637 frame->srcSlideH = frame->height;
4639 frame->AddState (MediaFramePlanar);
4641 ReportDecodeFrameCompleted (frame);
4645 * NullDecoderInfo
4648 bool
4649 NullDecoderInfo::Supports (const char *codec)
4651 const char *video_fourccs [] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL };
4652 const char *audio_fourccs [] = { "wmav1","wmav2", "wmav3", "mp3", NULL};
4654 for (int i = 0; video_fourccs [i] != NULL; i++)
4655 if (!strcmp (codec, video_fourccs [i]))
4656 return true;
4658 for (int i = 0; audio_fourccs [i] != NULL; i++)
4659 if (!strcmp (codec, audio_fourccs [i]))
4660 return true;
4663 return false;
4667 * NullDecoder
4670 NullDecoder::NullDecoder (Media *media, IMediaStream *stream) : IMediaDecoder (Type::NULLDECODER, media, stream)
4672 logo = NULL;
4673 logo_size = 0;
4674 prev_pts = G_MAXUINT64;
4677 void
4678 NullDecoder::Dispose ()
4680 g_free (logo);
4681 logo = NULL;
4683 IMediaDecoder::Dispose ();
4686 MediaResult
4687 NullDecoder::DecodeVideoFrame (MediaFrame *frame)
4689 // free encoded buffer and alloc a new one for our image
4690 g_free (frame->buffer);
4691 frame->buflen = logo_size;
4692 frame->buffer = (guint8*) g_malloc (frame->buflen);
4693 memcpy (frame->buffer, logo, frame->buflen);
4694 frame->AddState (MediaFrameDecoded);
4696 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4698 return MEDIA_SUCCESS;
4701 MediaResult
4702 NullDecoder::DecodeAudioFrame (MediaFrame *frame)
4704 AudioStream *as = (AudioStream *) GetStream ();
4705 guint32 samples;
4706 guint32 data_size;
4707 guint64 diff_pts;
4709 // discard encoded data
4710 g_free (frame->buffer);
4712 // We have no idea here how long the encoded audio data is
4713 // for the first frame we use 0.1 seconds, for the rest
4714 // we calculate the time since the last frame
4716 if (prev_pts == G_MAXUINT64 || frame->pts <= prev_pts) {
4717 samples = as->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4718 } else {
4719 diff_pts = frame->pts - prev_pts;
4720 samples = (float) as->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT / (float) diff_pts);
4722 prev_pts = frame->pts;
4724 data_size = samples * as->GetChannels () * 2 /* 16 bit audio */;
4726 frame->buflen = data_size;
4727 frame->buffer = (guint8 *) g_malloc0 (frame->buflen);
4729 frame->AddState (MediaFrameDecoded);
4731 return MEDIA_SUCCESS;
4734 void
4735 NullDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
4737 MediaResult result = MEDIA_FAIL;
4738 IMediaStream *stream = GetStream ();
4740 if (stream->GetType () == MediaTypeAudio) {
4741 result = DecodeAudioFrame (frame);
4742 } else if (stream->GetType () == MediaTypeVideo) {
4743 result = DecodeVideoFrame (frame);
4746 if (MEDIA_SUCCEEDED (result)) {
4747 ReportDecodeFrameCompleted (frame);
4748 } else {
4749 ReportErrorOccurred (result);
4753 void
4754 NullDecoder::OpenDecoderAsyncInternal ()
4756 MediaResult result;
4757 IMediaStream *stream = GetStream ();
4759 if (stream->GetType () == MediaTypeAudio)
4760 result = OpenAudio ();
4761 else if (stream->GetType () == MediaTypeVideo)
4762 result = OpenVideo ();
4763 else
4764 result = MEDIA_FAIL;
4766 if (MEDIA_SUCCEEDED (result)) {
4767 ReportOpenDecoderCompleted ();
4768 } else {
4769 ReportErrorOccurred (result);
4773 MediaResult
4774 NullDecoder::OpenAudio ()
4776 return MEDIA_SUCCESS;
4779 MediaResult
4780 NullDecoder::OpenVideo ()
4782 VideoStream *vs = (VideoStream *) GetStream ();
4783 guint32 dest_height = vs->height;
4784 guint32 dest_width = vs->width;
4785 guint32 dest_i = 0;
4787 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4788 extern const char moonlight_logo [];
4789 const char *image = moonlight_logo;
4791 guint32 img_offset = *((guint32*)(image + 10));
4792 guint32 img_width = *((guint32*)(image + 18));
4793 guint32 img_height = *((guint32*)(image + 22));
4794 guint32 img_stride = (img_width * 3 + 3) & ~3; // in bytes
4795 guint32 img_i, img_h, img_w;
4796 guint32 start_w = (dest_width-img_width)/2;
4797 guint32 end_w = start_w + img_width;
4798 guint32 start_h = (dest_height-img_height)/2;
4799 guint32 end_h = start_h + img_height;
4801 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);
4803 // create the buffer for our image
4804 logo_size = dest_height * dest_width * 4;
4805 logo = (guint8*) g_malloc (logo_size);
4806 memset (logo, 0x00, logo_size);
4808 // write our image centered into the destination rectangle, flipped horizontally
4809 dest_i = 4;
4810 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4811 for (guint32 dest_w = 0; dest_w < dest_width; dest_w++) {
4812 if (dest_w >= start_w && dest_w < end_w && dest_h >= start_h && dest_h < end_h) {
4813 img_h = (dest_h - start_h) % img_height;
4814 img_w = (dest_w - start_w) % img_width;
4815 img_i = img_h * img_stride + img_w * 3;
4817 logo [logo_size - dest_i + 0] = image [img_offset + img_i + 0];
4818 logo [logo_size - dest_i + 1] = image [img_offset + img_i + 1];
4819 logo [logo_size - dest_i + 2] = image [img_offset + img_i + 2];
4821 logo [logo_size - dest_i + 3] = 0xff;
4823 dest_i += 4;
4827 // Flip the image vertically
4828 for (guint32 dest_h = 0; dest_h < dest_height; dest_h++) {
4829 for (guint32 dest_w = 0; dest_w < dest_width / 2; dest_w++) {
4830 guint32 tmp;
4831 guint32 a = (dest_h * dest_width + dest_w) * 4;
4832 guint32 b = (dest_h * dest_width + dest_width - dest_w) * 4 - 4;
4833 for (guint32 c = 0; c < 3; c++) {
4834 tmp = logo [a + c];
4835 logo [a + c] = logo [b + c];
4836 logo [b + c] = tmp;
4841 SetPixelFormat (MoonPixelFormatRGB32);
4843 return MEDIA_SUCCESS;
4847 * ExternalDemuxer
4850 ExternalDemuxer::ExternalDemuxer (Media *media, void *instance, CloseDemuxerCallback close_demuxer,
4851 GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample, OpenDemuxerAsyncCallback open_demuxer,
4852 SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
4853 : IMediaDemuxer (Type::EXTERNALDEMUXER, media)
4855 this->close_demuxer_callback = close_demuxer;
4856 this->get_diagnostic_async_callback = get_diagnostic;
4857 this->get_sample_async_callback = get_sample;
4858 this->open_demuxer_async_callback = open_demuxer;
4859 this->seek_async_callback = seek;
4860 this->switch_media_stream_async_callback = switch_media_stream;
4861 this->instance = instance;
4863 can_seek = true;
4864 pthread_rwlock_init (&rwlock, NULL);
4866 g_return_if_fail (instance != NULL);
4867 g_return_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL);
4870 ExternalDemuxer::~ExternalDemuxer ()
4872 pthread_rwlock_destroy (&rwlock);
4875 void
4876 ExternalDemuxer::Dispose ()
4878 ClearCallbacks ();
4879 IMediaDemuxer::Dispose ();
4882 void
4883 ExternalDemuxer::ClearCallbacks ()
4885 pthread_rwlock_wrlock (&rwlock);
4886 close_demuxer_callback = NULL;
4887 get_diagnostic_async_callback = NULL;
4888 get_sample_async_callback = NULL;
4889 open_demuxer_async_callback = NULL;
4890 seek_async_callback = NULL;
4891 switch_media_stream_async_callback = NULL;
4892 instance = NULL;
4893 pthread_rwlock_unlock (&rwlock);
4896 void
4897 ExternalDemuxer::SetCanSeek (bool value)
4899 can_seek = value;
4902 gint32
4903 ExternalDemuxer::AddStream (IMediaStream *stream)
4905 return IMediaDemuxer::AddStream (stream);
4908 void
4909 ExternalDemuxer::CloseDemuxerInternal ()
4911 pthread_rwlock_rdlock (&rwlock);
4912 if (close_demuxer_callback != NULL) {
4913 close_demuxer_callback (instance);
4914 } else {
4915 #if SANITY
4916 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4917 #endif
4919 pthread_rwlock_unlock (&rwlock);
4922 void
4923 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind)
4925 pthread_rwlock_rdlock (&rwlock);
4926 if (get_diagnostic_async_callback != NULL) {
4927 get_diagnostic_async_callback (instance, diagnosticsKind);
4928 } else {
4929 #if SANITY
4930 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4931 #endif
4933 pthread_rwlock_unlock (&rwlock);
4936 void
4937 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream *stream)
4939 g_return_if_fail (stream != NULL);
4941 pthread_rwlock_rdlock (&rwlock);
4942 if (get_sample_async_callback != NULL) {
4943 get_sample_async_callback (instance, stream->GetStreamType ());
4944 } else {
4945 #if SANITY
4946 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4947 #endif
4949 pthread_rwlock_unlock (&rwlock);
4952 void
4953 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4955 pthread_rwlock_rdlock (&rwlock);
4956 if (open_demuxer_async_callback != NULL) {
4957 open_demuxer_async_callback (instance, this);
4958 } else {
4959 #if SANITY
4960 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4961 #endif
4963 pthread_rwlock_unlock (&rwlock);
4966 void
4967 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime)
4969 pthread_rwlock_rdlock (&rwlock);
4970 if (seek_async_callback != NULL) {
4971 seek_async_callback (instance, seekToTime);
4972 } else {
4973 #if SANITY
4974 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4975 #endif
4977 pthread_rwlock_unlock (&rwlock);
4980 void
4981 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream *mediaStreamDescription)
4983 g_return_if_fail (mediaStreamDescription != NULL);
4985 pthread_rwlock_rdlock (&rwlock);
4986 if (switch_media_stream_async_callback != NULL) {
4987 switch_media_stream_async_callback (instance, mediaStreamDescription);
4988 } else {
4989 #if SANITY
4990 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4991 #endif
4993 pthread_rwlock_unlock (&rwlock);
4998 * AudioStream
5001 AudioStream::AudioStream (Media *media)
5002 : IMediaStream (Type::AUDIOSTREAM, media)
5006 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)
5007 : IMediaStream (Type::AUDIOSTREAM, media)
5009 this->codec_id = codec_id;
5010 this->codec = CreateCodec (codec_id);
5011 this->extra_data = extra_data;
5012 this->extra_data_size = extra_data_size;
5013 input_bits_per_sample = bits_per_sample;
5014 output_bits_per_sample = bits_per_sample;
5015 input_block_align = block_align;
5016 output_block_align = block_align;
5017 input_sample_rate = sample_rate;
5018 output_sample_rate = sample_rate;
5019 input_channels = channels;
5020 output_channels = channels;
5021 input_bit_rate = bit_rate;
5022 output_bit_rate = bit_rate;
5026 * ExternalDecoder
5029 ExternalDecoder::ExternalDecoder (Media *media, IMediaStream *stream, void *instance, const char *name,
5030 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async,
5031 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async,
5032 ExternalDecoder_CleanupCallback cleanup,
5033 ExternalDecoder_CleanStateCallback clean_state,
5034 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame,
5035 ExternalDecoder_DisposeCallback dispose,
5036 ExternalDecoder_DtorCallback dtor)
5037 : IMediaDecoder (Type::EXTERNALDECODER, media, stream)
5039 this->instance = instance;
5040 this->name = g_strdup (name);
5041 this->decode_frame_async = decode_frame_async;
5042 this->open_decoder_async = open_decoder_async;
5043 this->cleanup = cleanup;
5044 this->clean_state = clean_state;
5045 this->has_delayed_frame = has_delayed_frame;
5046 this->dispose = dispose;
5047 this->dtor = dtor;
5050 ExternalDecoder::~ExternalDecoder ()
5052 dtor (instance);
5053 g_free (name);
5056 void
5057 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame *frame)
5059 decode_frame_async (instance, frame);
5062 void
5063 ExternalDecoder::OpenDecoderAsyncInternal ()
5065 open_decoder_async (instance);
5068 void
5069 ExternalDecoder::Dispose ()
5071 dispose (instance);
5073 IMediaDecoder::Dispose ();
5076 void
5077 ExternalDecoder::Cleanup (MediaFrame *frame)
5079 cleanup (instance, frame);
5082 void
5083 ExternalDecoder::CleanState ()
5085 clean_state (instance);
5088 bool
5089 ExternalDecoder::HasDelayedFrame ()
5091 return has_delayed_frame (instance);
5094 void
5095 ExternalDecoder::InputEnded ()
5097 GetStream ()->SetOutputEnded (true);
5101 * ExternalDecoderInfo
5104 ExternalDecoderInfo::ExternalDecoderInfo (void *instance, const char *name, ExternalDecoderInfo_SupportsCallback supports, ExternalDecoderInfo_Create create, ExternalDecoderInfo_dtor dtor)
5106 this->instance = instance;
5107 this->supports = supports;
5108 this->create = create;
5109 this->dtor = dtor;
5110 this->name = g_strdup (name);
5113 bool
5114 ExternalDecoderInfo::Supports (const char *codec)
5116 return supports (instance, codec);
5119 IMediaDecoder *
5120 ExternalDecoderInfo::Create (Media *media, IMediaStream *stream)
5122 return create (instance, media, stream);
5125 ExternalDecoderInfo::~ExternalDecoderInfo ()
5127 if (dtor != NULL)
5128 dtor (instance);
5129 g_free (name);