1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * pipeline.cpp: Pipeline for the media
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.
17 #include <glib/gstdio.h>
26 #include "codec-version.h"
27 #include "pipeline-ffmpeg.h"
31 #include "mediaelement.h"
33 #include "asf/asf-structures.h"
34 #include "yuv-converter.h"
36 #include "mms-downloader.h"
37 #include "pipeline-ui.h"
38 #include "pipeline-asf.h"
40 #include "deployment.h"
41 #include "timesource.h"
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));
72 error_reported
= false;
73 buffering_enabled
= false;
74 in_open_internal
= false;
76 download_progress
= 0.0;
77 buffering_progress
= 0.0;
79 if (!GetDeployment ()->RegisterMedia (this))
85 LOG_PIPELINE ("Media::~Media (), id: %i\n", GET_OBJ_ID (this));
93 bool was_disposed
= false;
95 LOG_PIPELINE ("Media::Dispose (), id: %i\n", GET_OBJ_ID (this));
98 was_disposed
= is_disposed
;
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.
108 IMediaObject::Dispose ();
113 if (!MediaThreadPool::IsThreadPoolThread ()) {
114 g_warning ("Media::Dispose (): Not in thread-pool thread, and we haven't been disposed already.\n");
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.
138 this->demuxer
= NULL
;
147 IMediaObject::Dispose ();
149 GetDeployment ()->UnregisterMedia (this);
153 Media::IsMSCodecsInstalled ()
155 return registered_ms_codecs
;
159 Media::RegisterMSCodecs (void)
163 char *libmscodecs_path
= NULL
;
164 const char *functions
[] = {"register_codec_pack", NULL
};
165 const gchar
*home
= g_get_home_dir ();
166 registering_ms_codecs
= true;
168 if (!(moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
)) {
169 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
174 libmscodecs_path
= g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME
, NULL
);
176 if (!(g_file_test (libmscodecs_path
, G_FILE_TEST_EXISTS
) && g_file_test (libmscodecs_path
, G_FILE_TEST_IS_REGULAR
))) {
177 if (libmscodecs_path
)
178 g_free (libmscodecs_path
);
179 libmscodecs_path
= g_strdup (CODEC_LIBRARY_NAME
);
182 dl
= dlopen (libmscodecs_path
, RTLD_LAZY
);
184 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path
);
186 int pre_decoders
= 0;
187 int post_decoders
= 0;
189 /* Count the number of current decoders */
191 current
= registered_decoders
;
192 while (current
!= NULL
) {
194 current
= current
->next
;
197 for (int i
= 0; functions
[i
] != NULL
; i
++) {
198 reg
= (register_codec
) dlsym (dl
, functions
[i
]);
200 (*reg
) (MOONLIGHT_CODEC_ABI_VERSION
);
202 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions
[i
], libmscodecs_path
);
206 /* Count the number of decoders after registering the ms codecs */
207 current
= registered_decoders
;
208 while (current
!= NULL
) {
210 current
= current
->next
;
213 /* We could only load the codecs if the codec pack actually registered any decoders
214 * This ensures that if the user has invalid codecs for whatever reason, we request
216 registered_ms_codecs
= post_decoders
> pre_decoders
;
218 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path
, dlerror ());
220 g_free (libmscodecs_path
);
222 registering_ms_codecs
= false;
226 Media::SetBufferingEnabled (bool value
)
228 buffering_enabled
= value
;
233 Media::SetBufferingTime (guint64 buffering_time
)
236 this->buffering_time
= buffering_time
;
241 Media::GetBufferingTime ()
245 result
= buffering_time
;
251 Media::GetPlaylistRoot ()
260 markers
= new List ();
266 Media::RegisterDemuxer (DemuxerInfo
*info
)
268 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
270 if (registered_demuxers
== NULL
) {
271 registered_demuxers
= info
;
273 MediaInfo
* current
= registered_demuxers
;
274 while (current
->next
!= NULL
)
275 current
= current
->next
;
276 current
->next
= info
;
281 Media::RegisterConverter (ConverterInfo
*info
)
283 //printf ("Media::RegisterConverter (%p)\n", info);
285 if (registered_converters
== NULL
) {
286 registered_converters
= info
;
288 MediaInfo
*current
= registered_converters
;
289 while (current
->next
!= NULL
)
290 current
= current
->next
;
291 current
->next
= info
;
296 Media::RegisterDecoder (DecoderInfo
*info
)
300 //printf ("Media::RegisterDecoder (%p)\n", info);
302 if (registered_decoders
== NULL
) {
303 registered_decoders
= info
;
305 if (registering_ms_codecs
) {
306 // MS codecs might get registered after all other codecs (right after installing them),
307 // which means after the null codecs so if they don't get special treatment, they won't
308 // get used until the next browser restart (when they're registered normally).
309 // So instead of appending them, we prepend them.
310 info
->next
= registered_decoders
;
311 registered_decoders
= info
;
313 current
= registered_decoders
;
314 while (current
->next
!= NULL
)
315 current
= current
->next
;
316 current
->next
= info
;
319 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info
->GetName ());
325 LOG_PIPELINE ("Media::Initialize ()\n");
328 Media::RegisterDemuxer (new ASFDemuxerInfo ());
329 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
330 Media::RegisterDemuxer (new ASXDemuxerInfo ());
333 if (!(moonlight_flags
& RUNTIME_INIT_FFMPEG_YUV_CONVERTER
))
334 Media::RegisterConverter (new YUVConverterInfo ());
337 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
338 if (moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
) {
341 #ifdef INCLUDE_FFMPEG
342 if (!(moonlight_flags
& RUNTIME_INIT_DISABLE_FFMPEG_CODECS
)) {
347 Media::RegisterDecoder (new PassThroughDecoderInfo ());
348 Media::RegisterDecoder (new NullDecoderInfo ());
350 MediaThreadPool::Initialize ();
356 LOG_PIPELINE ("Media::Shutdown ()\n");
361 // Make sure all threads are stopped
362 AudioPlayer::Shutdown ();
363 MediaThreadPool::Shutdown ();
365 current
= registered_decoders
;
366 while (current
!= NULL
) {
367 next
= current
->next
;
371 registered_decoders
= NULL
;
373 current
= registered_demuxers
;
374 while (current
!= NULL
) {
375 next
= current
->next
;
379 registered_demuxers
= NULL
;
381 current
= registered_converters
;
382 while (current
!= NULL
) {
383 next
= current
->next
;
387 registered_converters
= NULL
;
389 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
393 Media::Warning (MediaResult result
, const char *format
, ...)
397 if (MEDIA_SUCCEEDED (result
))
400 fprintf (stderr
, "Moonlight: MediaResult = %d; ", result
);
402 va_start (args
, format
);
403 vfprintf (stderr
, format
, args
);
406 fputc ('\n', stderr
);
410 Media::InMediaThread ()
412 return MediaThreadPool::IsThreadPoolThread ();
416 Media::ReportBufferingProgress (double progress
)
418 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress
, buffering_progress
);
420 progress
= MAX (MIN (progress
, 1.0), 0.0);
422 if (progress
== buffering_progress
)
425 if (progress
< buffering_progress
|| progress
> (buffering_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
426 buffering_progress
= progress
;
427 EmitSafe (BufferingProgressChangedEvent
, new ProgressEventArgs (progress
));
432 Media::ReportDownloadProgress (double progress
)
434 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress
, download_progress
);
436 progress
= MAX (MIN (progress
, 1.0), 0.0);
438 if (progress
<= download_progress
) {
440 * Download progress percentage can actually go down - if the file size
441 * goes up. Yes, the file size can go up.
446 if (progress
> (download_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
447 download_progress
= progress
;
448 EmitSafe (DownloadProgressChangedEvent
, new ProgressEventArgs (progress
));
453 Media::SeekAsync (guint64 pts
)
455 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
457 if (demuxer
== NULL
) {
458 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
462 demuxer
->SeekAsync (pts
);
466 Media::ReportSeekCompleted (guint64 pts
)
468 LOG_PIPELINE ("Media::ReportSeekCompleted (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
470 buffering_progress
= 0;
472 EmitSafe (SeekCompletedEvent
);
476 Media::ReportOpenCompleted ()
478 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
480 EmitSafe (OpenCompletedEvent
);
484 Media::ReportOpenDemuxerCompleted ()
486 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
492 Media::ReportOpenDecoderCompleted (IMediaDecoder
*decoder
)
494 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder
, GET_OBJ_ID (this));
496 g_return_if_fail (decoder
!= NULL
);
502 Media::ReportErrorOccurred (ErrorEventArgs
*args
)
504 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args
, args
== NULL
? NULL
: args
->GetErrorMessage());
507 fprintf (stderr
, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args
->GetErrorType()), args
->GetErrorCode(), args
->GetErrorMessage(), args
->GetExtendedMessage());
509 fprintf (stderr
, "Moonlight: Unspecified media error.\n");
512 if (!error_reported
) {
513 error_reported
= true;
514 EmitSafe (MediaErrorEvent
, args
);
519 Media::ReportErrorOccurred (const char *message
)
521 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message
);
523 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 3001, message
)));
527 Media::ReportErrorOccurred (MediaResult result
)
529 char *msg
= g_strdup_printf ("Media error: %i.", result
);
530 ReportErrorOccurred (msg
);
537 LOG_PIPELINE ("Media::PlayAsync ()\n");
539 MediaClosure
*closure
= new MediaClosure (this, PlayCallback
, this, "Media::PlayAsync");
540 EnqueueWork (closure
);
547 LOG_PIPELINE ("Media::PauseAsync ()\n");
553 LOG_PIPELINE ("Media::StopAsync ()\n");
555 MediaClosure
*closure
= new MediaClosure (this, StopCallback
, this, "Media::StopAsync");
556 EnqueueWork (closure
);
561 Media::StopCallback (MediaClosure
*closure
)
563 closure
->GetMedia ()->Stop ();
564 return MEDIA_SUCCESS
;
568 Media::PlayCallback (MediaClosure
*closure
)
570 closure
->GetMedia ()->Play ();
571 return MEDIA_SUCCESS
;
577 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
579 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
583 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
587 demuxer
->ClearBuffers ();
593 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
595 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
599 demuxer
->FillBuffers ();
603 Media::Initialize (Downloader
*downloader
, const char *PartName
)
605 IMediaSource
*source
;
607 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader
, PartName
, GET_OBJ_ID (this));
609 g_return_if_fail (downloader
!= NULL
);
610 g_return_if_fail (file
== NULL
);
611 g_return_if_fail (uri
!= NULL
|| PartName
!= NULL
);
612 g_return_if_fail (initialized
== false);
613 g_return_if_fail (error_reported
== false);
614 g_return_if_fail (this->source
== NULL
);
616 if (downloader
->Completed ()) {
617 file
= downloader
->GetDownloadedFilename (PartName
);
620 ReportErrorOccurred ("Couldn't get downloaded filename.");
625 if (file
== NULL
&& PartName
!= NULL
&& PartName
[0] != 0) {
626 ReportErrorOccurred ("We don't support using media in zip files which haven't been downloaded yet (i.e. calling MediaElement.SetSource (dl, 'foo') with a dl which hasn't downloaded the file yet)");
631 InternalDownloader
*idl
= downloader
->GetInternalDownloader ();
632 MmsDownloader
*mms_dl
= (idl
&& idl
->GetObjectType () == Type::MMSDOWNLOADER
) ? (MmsDownloader
*) idl
: NULL
;
634 if (mms_dl
== NULL
) {
635 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
639 source
= new MmsSource (this, downloader
);
641 source
= new FileSource (this, file
);
649 Media::Initialize (IMediaSource
*source
)
653 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source
, GET_OBJ_ID (this));
655 g_return_if_fail (source
!= NULL
);
656 g_return_if_fail (this->source
== NULL
);
657 g_return_if_fail (initialized
== false);
659 result
= source
->Initialize ();
660 if (!MEDIA_SUCCEEDED (result
)) {
661 ReportErrorOccurred (result
);
666 this->source
= source
;
667 this->source
->ref ();
671 Media::Initialize (const char *uri
)
674 IMediaSource
*source
= NULL
;
676 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri
, GET_OBJ_ID (this));
678 g_return_if_fail (uri
!= NULL
);
679 g_return_if_fail (file
== NULL
);
680 g_return_if_fail (uri
!= NULL
);
681 g_return_if_fail (initialized
== false);
682 g_return_if_fail (error_reported
== false);
683 g_return_if_fail (source
== NULL
);
684 g_return_if_fail (this->source
== NULL
);
686 this->uri
= g_strdup (uri
);
689 if (g_str_has_prefix (uri
, "mms://") || g_str_has_prefix (uri
, "rtsp://") || g_str_has_prefix (uri
, "rtsps://")) {
690 dl
= Surface::CreateDownloader (this);
692 ReportErrorOccurred ("Couldn't create downloader.");
696 dl
->Open ("GET", uri
, StreamingPolicy
);
698 if (dl
->GetFailedMessage () == NULL
) {
699 Initialize (dl
, NULL
);
701 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
702 MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
710 source
= new ProgressiveSource (this, uri
);
716 Media::Initialize (IMediaDemuxer
*demuxer
)
718 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer
, GET_OBJ_ID (this));
720 g_return_if_fail (demuxer
!= NULL
);
721 g_return_if_fail (this->demuxer
== NULL
);
722 g_return_if_fail (initialized
== false);
724 this->demuxer
= demuxer
;
725 this->demuxer
->ref ();
732 Media::RetryHttp (ErrorEventArgs
*args
)
734 char *http_uri
= NULL
;
736 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri
);
738 g_return_if_fail (uri
!= NULL
);
739 g_return_if_fail (source
!= NULL
);
742 ReportErrorOccurred (args
);
746 // CHECK: If the current protocolo is rtsps, should we retry http or https?
748 if (g_str_has_prefix (uri
, "mms://")) {
749 http_uri
= g_strdup_printf ("http://%s", uri
+ 6);
750 } else if (g_str_has_prefix (uri
, "rtsp://")) {
751 http_uri
= g_strdup_printf ("http://%s", uri
+ 7);
752 } else if (g_str_has_prefix (uri
, "rtsps://")) {
753 http_uri
= g_strdup_printf ("http://%s", uri
+ 8);
755 ReportErrorOccurred (args
);
761 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri
);
765 /* this method is called on the main thread, ensure Dispose is called on the source on the media thread */
766 DisposeObject (source
);
770 error_reported
= false;
772 Initialize (http_uri
);
783 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
785 g_return_if_fail (initialized
== true);
787 EmitSafe (OpeningEvent
);
789 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenAsync");
790 EnqueueWork (closure
);
795 Media::OpenInternal ()
797 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
799 g_return_if_fail (initialized
== true);
802 // This may happen due to the recursion detection below
803 // Example: we try open a demuxer, the demuxer opens successfully
804 // right away and calls ReportDemuxerOpenComplete which will call
805 // us. Due to the recursion detection we'll enqueue a call to
806 // OpenInternal, while the first OpenInternal may succeed and
807 // set opened to true.
808 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
812 // detect recursive calls.
814 if (in_open_internal
) {
815 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
816 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
817 EnqueueWork (closure
);
822 in_open_internal
= true;
827 if (!SelectDemuxerAsync ()) {
828 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
835 if (!SelectDecodersAsync ()) {
836 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
843 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
845 EmitSafe (OpenCompletedEvent
);
848 in_open_internal
= false;
852 Media::OpenInternal (MediaClosure
*closure
)
854 Media
*media
= (Media
*) closure
->GetContext ();
856 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
858 media
->OpenInternal ();
860 return MEDIA_SUCCESS
;
864 Media::SelectDemuxerAsync ()
866 DemuxerInfo
*demuxerInfo
;
871 LOG_PIPELINE ("Media::SelectDemuxer () id: %i, demuxer: %p, IsOpened: %i, IsOpening: %i\n", GET_OBJ_ID (this), demuxer
, demuxer
? demuxer
->IsOpened () : -1, demuxer
? demuxer
->IsOpening () : -1);
873 g_return_val_if_fail (error_reported
== false, false);
874 g_return_val_if_fail (initialized
== true, false);
876 // Check if demuxer already is open
877 if (demuxer
!= NULL
) {
878 if (demuxer
->IsOpened ())
880 if (!demuxer
->IsOpening ())
881 demuxer
->OpenDemuxerAsync ();
882 return demuxer
->IsOpened ();
885 g_return_val_if_fail (source
!= NULL
, false);
887 // Check if the source knows how to create the demuxer
888 demuxer
= source
->CreateDemuxer (this);
890 if (demuxer
== NULL
) { // No demuxer created, we need to find it ourselves.
891 if (source
->CanSeek () && source
->GetPosition () > 0) {
892 if (!source
->Seek (0, SEEK_SET
)) {
893 LOG_PIPELINE ("Media::SelectDemuxer (): could not seek to position 0 of the input stream. Will try to continue anyway.\n");
896 // Check if we have at least 1024 bytes or eof
897 if (!source
->IsPositionAvailable (16, &eof
)) {
899 // We need to try again later.
900 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
902 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
903 EnqueueWork (closure
, false);
911 demuxerInfo
= registered_demuxers
;
912 while (demuxer
== NULL
&& demuxerInfo
!= NULL
) {
913 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo
->GetName ());
914 support
= demuxerInfo
->Supports (source
);
916 if (support
== MEDIA_SUCCESS
)
921 if (result
== MEDIA_NOT_ENOUGH_DATA
) {
922 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't determine whether it can handle the media or not due to not enough data being available yet.\n", demuxerInfo
->GetName ());
924 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
925 EnqueueWork (closure
, false);
931 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo
->GetName ());
932 demuxerInfo
= (DemuxerInfo
*) demuxerInfo
->next
;
935 if (demuxerInfo
== NULL
) {
936 // No demuxer found, report an error
937 const char *source_name
= file
? file
: uri
;
940 switch (source
->GetType ()) {
941 case MediaSourceTypeProgressive
:
942 case MediaSourceTypeFile
:
943 source_name
= ((FileSource
*) source
)->GetFileName ();
945 case MediaSourceTypeMms
:
946 case MediaSourceTypeMmsEntry
:
947 source_name
= "live source";
950 source_name
= "unknown source";
954 char *msg
= g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name
);
955 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
956 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT"),
957 MEDIA_UNKNOWN_CODEC
, msg
));
963 demuxer
= demuxerInfo
->Create (this, source
);
965 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer
->GetTypeName ());
968 if (demuxer
->IsOpened ())
971 if (demuxer
->IsOpening ())
974 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
976 demuxer
->OpenDemuxerAsync ();
978 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
980 return demuxer
!= NULL
&& demuxer
->IsOpened ();
984 Media::SelectDecodersAsync ()
986 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
988 g_return_val_if_fail (error_reported
== false, false);
989 g_return_val_if_fail (initialized
== true, false);
991 if (demuxer
== NULL
) {
992 ReportErrorOccurred ("No demuxer to select decoders from.");
996 // If the demuxer has no streams (ASXDemuxer for instance)
997 // then just return success.
998 if (demuxer
->GetStreamCount () == 0)
1001 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
1003 // Select codecs for each stream
1004 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1005 IMediaStream
*stream
= demuxer
->GetStream (i
);
1008 if (stream
== NULL
) {
1009 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
1013 if (stream
->GetDecoder () != NULL
)
1016 const char *codec
= stream
->GetCodec ();
1017 IMediaDecoder
*decoder
= NULL
;
1019 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec
);
1021 DecoderInfo
*current_decoder
= registered_decoders
;
1022 while (current_decoder
!= NULL
&& !current_decoder
->Supports (codec
)) {
1023 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder
->GetName (), codec
);
1024 current_decoder
= (DecoderInfo
*) current_decoder
->next
;
1027 if (current_decoder
== NULL
) {
1028 Media::Warning (MEDIA_UNKNOWN_CODEC
, "Unknown codec: '%s'.", codec
);
1032 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder
->GetName (), codec
);
1033 decoder
= current_decoder
->Create (this, stream
);
1035 stream
->SetDecoder (decoder
);
1043 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1045 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1046 IMediaStream
*stream
= demuxer
->GetStream (i
);
1047 IMediaDecoder
*decoder
;
1052 decoder
= stream
->GetDecoder ();
1054 if (decoder
== NULL
) {
1055 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
1056 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT")));
1060 if (decoder
->IsOpening () || decoder
->IsOpened ())
1063 decoder
->OpenDecoderAsync ();
1069 // Wait until all the codecs have opened
1070 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1072 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1073 IMediaStream
*stream
= demuxer
->GetStream (i
);
1074 IMediaDecoder
*decoder
;
1079 decoder
= stream
->GetDecoder ();
1081 if (decoder
== NULL
) {
1082 ReportErrorOccurred (MEDIA_FAIL
);
1086 if (decoder
->IsOpening ()) {
1087 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
1088 EnqueueWork (closure
, false);
1093 if (!decoder
->IsOpened ()) {
1094 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1095 ReportErrorOccurred (MEDIA_FAIL
);
1101 // All the codecs have been opened now.
1102 // Find converters for each of them (whenever required).
1104 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1106 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1107 IMediaStream
*stream
= demuxer
->GetStream (i
);
1108 IMediaDecoder
*decoder
;
1113 decoder
= stream
->GetDecoder ();
1115 if (decoder
== NULL
) {
1116 ReportErrorOccurred (MEDIA_FAIL
);
1120 if (stream
->GetType () != MediaTypeVideo
)
1121 continue; // Only video streams need converters
1123 if (decoder
->GetPixelFormat () == MoonPixelFormatRGB32
|| decoder
->GetPixelFormat () == MoonPixelFormatRGBA32
)
1124 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1126 // Select converter for this stream
1127 VideoStream
*vs
= (VideoStream
*) stream
;
1129 ConverterInfo
* current_conv
= registered_converters
;
1130 while (current_conv
!= NULL
&& !current_conv
->Supports (decoder
->GetPixelFormat (), MoonPixelFormatRGB32
)) {
1131 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1132 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1133 current_conv
= (ConverterInfo
*) current_conv
->next
;
1137 if (current_conv
== NULL
) {
1138 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER
);
1139 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1140 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1144 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1145 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1147 vs
->converter
= current_conv
->Create (this, vs
);
1148 vs
->converter
->input_format
= decoder
->GetPixelFormat ();
1149 vs
->converter
->output_format
= MoonPixelFormatRGB32
;
1150 if (!MEDIA_SUCCEEDED (vs
->converter
->Open ())) {
1151 vs
->converter
->unref ();
1152 vs
->converter
= NULL
;
1153 ReportErrorOccurred (MEDIA_FAIL
);
1158 // Loop through all the streams, return true if at least one has a codec.
1160 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1161 IMediaStream
*stream
= demuxer
->GetStream (i
);
1166 if (stream
->GetDecoder () != NULL
)
1170 // No codecs found for no stream, report an error.
1171 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1176 Media::EnqueueWork (MediaClosure
*closure
, bool wakeup
)
1178 bool result
= false;
1181 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure
);
1183 g_return_val_if_fail (closure
!= NULL
, false);
1189 disposed
= this->is_disposed
;
1192 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed
);
1194 MediaThreadPool::AddWork (closure
, wakeup
);
1203 Media::DisposeObjectInternal (MediaClosure
*closure
)
1205 closure
->GetContext ()->Dispose ();
1206 return MEDIA_SUCCESS
;
1210 Media::DisposeObject (EventObject
*obj
)
1212 MediaDisposeObjectClosure
*closure
= new MediaDisposeObjectClosure (this, DisposeObjectInternal
, obj
);
1213 if (!EnqueueWork (closure
, true)) {
1214 LOG_PIPELINE ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj
);
1223 MediaThreadPool::WakeUp ();
1227 Media::ClearQueue ()
1229 LOG_PIPELINE ("Media::ClearQueue ().\n");
1230 MediaThreadPool::RemoveWork (this);
1237 ASXDemuxer::ASXDemuxer (Media
*media
, IMediaSource
*source
)
1238 : IMediaDemuxer (Type::ASXDEMUXER
, media
, source
)
1243 ASXDemuxer::~ASXDemuxer ()
1248 ASXDemuxer::Dispose ()
1254 IMediaDemuxer::Dispose ();
1258 ASXDemuxer::OpenDemuxerAsyncInternal ()
1262 ErrorEventArgs
*args
= NULL
;
1263 Media
*media
= GetMediaReffed ();
1265 g_return_if_fail (media
!= NULL
);
1267 root
= media
->GetPlaylistRoot ();
1269 g_return_if_fail (root
!= NULL
);
1271 PlaylistParser
*parser
= new PlaylistParser (root
, source
);
1273 if (MEDIA_SUCCEEDED (parser
->Parse ())) {
1274 result
= MEDIA_SUCCESS
;
1275 playlist
= parser
->GetPlaylist ();
1278 result
= MEDIA_FAIL
;
1279 args
= parser
->GetErrorEventArgs ();
1286 if (MEDIA_SUCCEEDED (result
)) {
1287 ReportOpenDemuxerCompleted ();
1288 } else if (args
!= NULL
) {
1289 args
->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1290 ReportErrorOccurred (args
);
1292 ReportErrorOccurred (result
);
1305 ASXDemuxerInfo::Supports (IMediaSource
*source
)
1307 if (PlaylistParser::IsASX2 (source
) || PlaylistParser::IsASX3 (source
)) {
1308 return MEDIA_SUCCESS
;
1315 ASXDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
1317 return new ASXDemuxer (media
, source
);
1321 * ManagedStreamSource
1324 ManagedStreamSource::ManagedStreamSource (Media
*media
, ManagedStreamCallbacks
*stream
) : IMediaSource (Type::MANAGEDSTREAMSOURCE
, media
)
1326 memcpy (&this->stream
, stream
, sizeof (this->stream
));
1329 ManagedStreamSource::~ManagedStreamSource ()
1331 stream
.handle
= NULL
;
1335 ManagedStreamSource::ReadInternal (void *buf
, guint32 n
)
1337 return stream
.Read (stream
.handle
, buf
, 0, n
);
1341 ManagedStreamSource::PeekInternal (void *buf
, guint32 n
)
1345 read
= stream
.Read (stream
.handle
, buf
, 0, n
);
1346 stream
.Seek (stream
.handle
, -read
, 1 /* SeekOrigin.Current */);
1351 ManagedStreamSource::SeekInternal (gint64 offset
, int mode
)
1353 stream
.Seek (stream
.handle
, offset
, mode
/* FIXME: check if mode values matches SeekOrigin values */);
1358 ManagedStreamSource::GetPositionInternal ()
1360 return stream
.Position (stream
.handle
);
1364 ManagedStreamSource::GetSizeInternal ()
1366 return stream
.Length (stream
.handle
);
1373 FileSource::FileSource (Media
*media
, const char *filename
) : IMediaSource (Type::FILESOURCE
, media
)
1375 this->filename
= g_strdup (filename
);
1381 FileSource::FileSource (Media
*media
, bool temp_file
) : IMediaSource (Type::FILESOURCE
, media
)
1386 this->temp_file
= temp_file
;
1389 FileSource::~FileSource ()
1394 FileSource::Dispose ()
1402 IMediaSource::Dispose ();
1406 FileSource::Initialize ()
1410 LOG_PIPELINE ("FileSource::Initialize ()\n");
1413 return MEDIA_SUCCESS
;
1416 if (filename
!= NULL
)
1417 return MEDIA_FILE_ERROR
;
1419 filename
= g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL
);
1421 if ((tmp_fd
= g_mkstemp (filename
)) == -1) {
1428 fd
= fdopen (tmp_fd
, "r");
1430 setvbuf (fd
, buffer
, _IOFBF
, sizeof (buffer
));
1432 if (filename
== NULL
)
1433 return MEDIA_FILE_ERROR
;
1435 fd
= g_fopen (filename
, "r");
1439 return MEDIA_FILE_ERROR
;
1443 return MEDIA_SUCCESS
;
1447 FileSource::Open (const char *filename
)
1449 g_return_val_if_fail (filename
!= NULL
, MEDIA_FAIL
);
1451 g_free (this->filename
);
1452 this->filename
= g_strdup (filename
);
1459 fd
= fopen (filename
, "r");
1466 return MEDIA_SUCCESS
;
1470 FileSource::UpdateSize ()
1474 g_return_if_fail (fd
!= NULL
);
1476 if (fstat (fileno (fd
), &st
) != -1) {
1484 FileSource::GetSizeInternal ()
1490 FileSource::GetPositionInternal ()
1497 result
= ftell (fd
);
1499 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %" G_GINT64_FORMAT
"\n", result
);
1505 FileSource::SeekInternal (gint64 offset
, int mode
)
1512 LOG_PIPELINE ("FileSource::SeekInternal (%" G_GINT64_FORMAT
", %i)\n", offset
, mode
);
1515 n
= fseek (fd
, offset
, mode
);
1521 FileSource::ReadInternal (void *buf
, guint32 n
)
1527 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf
, n
);
1532 nread
= fread (buf
, 1, n
, fd
);
1534 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n
, (int) nread
);
1540 FileSource::PeekInternal (void *buf
, guint32 n
)
1544 result
= ReadSome (buf
, n
);
1546 Seek (-result
, SEEK_CUR
);
1548 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %" G_GINT64_FORMAT
" [Done]\n", GET_OBJ_ID (this), buf
, n
, GetPosition ());
1566 ProgressiveSource::ProgressiveSource (Media
*media
, const char *uri
) : FileSource (media
, true)
1572 this->uri
= g_strdup (uri
);
1575 ProgressiveSource::~ProgressiveSource ()
1581 ProgressiveSource::Dispose ()
1587 if (Surface::InMainThread ()) {
1588 delete_cancellable (this);
1590 // we have to cancel/delete he cancellable on the main thread
1591 // it may end up doing a lot of stuff, including calling into
1594 // The tick call will ref us until the callback has been called.
1595 // Note that it may cause a warning to be printed
1596 // in ref () (reffing an object with a refcount of 0).
1597 // TODO: find a way to avoid the warning in this case, imho this is
1598 // a valid case of reffing an object with a refcount of 0.
1599 AddTickCallSafe (delete_cancellable
);
1603 FileSource::Dispose ();
1607 ProgressiveSource::delete_cancellable (EventObject
*data
)
1609 ProgressiveSource
*src
= (ProgressiveSource
*) data
;
1610 if (src
->cancellable
) {
1611 src
->cancellable
->Cancel ();
1612 delete src
->cancellable
;
1613 src
->cancellable
= NULL
;
1618 ProgressiveSource::Initialize ()
1620 MediaResult result
= MEDIA_SUCCESS
;
1621 Application
*application
;
1623 application
= GetDeployment ()->GetCurrentApplication ();
1625 g_return_val_if_fail (application
!= NULL
, MEDIA_FAIL
);
1626 g_return_val_if_fail (filename
== NULL
, MEDIA_FAIL
);
1627 g_return_val_if_fail (cancellable
== NULL
, MEDIA_FAIL
);
1629 result
= FileSource::Initialize ();
1631 if (!MEDIA_SUCCEEDED (result
))
1634 write_fd
= g_fopen (filename
, "w");
1635 if (write_fd
== NULL
) {
1636 char *msg
= g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename
);
1637 ReportErrorOccurred (msg
);
1642 // unlink the file right away so that it'll be deleted even if we crash.
1643 if (moonlight_flags
& RUNTIME_INIT_KEEP_MEDIA
) {
1644 printf ("Moonlight: The media file %s will not deleted.\n", filename
);
1646 g_unlink (filename
);
1649 cancellable
= new Cancellable ();
1650 Uri
*u
= new Uri ();
1651 if (u
->Parse (uri
)) {
1652 application
->GetResource (NULL
, u
, notify_func
, data_write
, MediaPolicy
, cancellable
, (gpointer
) this);
1654 result
= MEDIA_FAIL
;
1655 char *msg
= g_strdup_printf ("Could not parse the uri '%s'", uri
);
1656 ReportErrorOccurred (msg
);
1665 ProgressiveSource::notify_func (NotifyType type
, gint64 args
, void *closure
)
1667 g_return_if_fail (closure
!= NULL
);
1668 ((ProgressiveSource
*) closure
)->Notify (type
, args
);
1672 ProgressiveSource::Notify (NotifyType type
, gint64 args
)
1674 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT
")\n",
1676 type
== ::NotifySize
? "NotifySize" :
1677 (type
== NotifyCompleted
? "NotifyCompleted" :
1678 (type
== NotifyFailed
? "NotifyFailed" :
1679 (type
== NotifyStarted
? "NotifyStarted" :
1680 (type
== NotifyProgressChanged
? "NotifyProgressChanged" : "unknown")))),
1687 case NotifyCompleted
:
1688 DownloadComplete ();
1694 case NotifyProgressChanged
:
1701 ProgressiveSource::data_write (void *data
, gint32 offset
, gint32 n
, void *closure
)
1703 g_return_if_fail (closure
!= NULL
);
1704 ((ProgressiveSource
*) closure
)->DataWrite (data
, offset
, n
);
1708 ProgressiveSource::DataWrite (void *buf
, gint32 offset
, gint32 n
)
1711 Media
*media
= NULL
;
1713 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf
, offset
, n
, media
, filename
);
1718 g_return_if_fail (write_fd
!= NULL
);
1720 media
= GetMediaReffed ();
1723 // We've got the entire file, update the size
1724 size
= write_pos
; // Since this method is the only method that writes to write_pos, and we're not reentrant, there is no need to lock here.
1726 // Close our write handle, we won't write more now
1732 nwritten
= fwrite (buf
, 1, n
, write_fd
);
1736 write_pos
+= nwritten
;
1742 media
->ReportDownloadProgress ((double) (offset
+ n
) / (double) size
);
1748 ProgressiveSource::NotifySize (gint64 size
)
1750 LOG_PIPELINE ("ProgressiveSource::NotifySize (%" G_GINT64_FORMAT
")\n", size
);
1758 ProgressiveSource::DownloadComplete ()
1760 MediaResult result
= MEDIA_SUCCESS
;
1761 Media
*media
= GetMediaReffed ();
1763 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1766 if (write_pos
!= size
&& size
!= -1) { // what happend here?
1767 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT
") != the reported size (%" G_GINT64_FORMAT
")\n", write_pos
, size
);
1770 this->size
= write_pos
;
1772 // Close our write handle, we won't write more now
1777 if (!MEDIA_SUCCEEDED (result
))
1778 ReportErrorOccurred (result
);
1781 media
->ReportDownloadProgress (1.0);
1788 ProgressiveSource::DownloadFailed ()
1790 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1792 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
1796 ProgressiveSource::CloseWriteFile ()
1798 if (write_fd
== NULL
)
1809 MemorySource::MemorySource (Media
*media
, void *memory
, gint32 size
, gint64 start
, bool owner
)
1810 : IMediaSource (Type::MEMORYSOURCE
, media
)
1812 this->memory
= memory
;
1814 this->start
= start
;
1816 this->owner
= owner
;
1819 MemorySource::~MemorySource ()
1826 MemorySource::SeekInternal (gint64 offset
, int mode
)
1832 real_offset
= offset
- start
;
1833 if (real_offset
< 0 || real_offset
>= size
)
1838 if (pos
+ offset
> size
|| pos
+ offset
< 0)
1843 if (size
- offset
> size
|| size
- offset
< 0)
1845 pos
= size
- offset
;
1854 MemorySource::ReadInternal (void *buffer
, guint32 n
)
1856 guint32 k
= MIN (n
, size
- pos
);
1857 memcpy (buffer
, ((char*) memory
) + pos
, k
);
1863 MemorySource::PeekInternal (void *buffer
, guint32 n
)
1865 gint64 start
= this->start
+ pos
;
1867 if (this->start
> start
)
1870 if ((this->start
+ size
) < (start
+ n
))
1873 memcpy (buffer
, ((char*) memory
) + this->start
- start
, n
);
1881 pthread_mutex_t
MediaThreadPool::mutex
= PTHREAD_MUTEX_INITIALIZER
;
1882 pthread_cond_t
MediaThreadPool::condition
= PTHREAD_COND_INITIALIZER
;
1883 pthread_cond_t
MediaThreadPool::completed_condition
= PTHREAD_COND_INITIALIZER
;
1884 int MediaThreadPool::count
= 0;
1885 pthread_t
MediaThreadPool::threads
[max_threads
];
1886 Media
*MediaThreadPool::medias
[max_threads
];
1887 Deployment
*MediaThreadPool::deployments
[max_threads
];
1888 bool MediaThreadPool::shutting_down
= false;
1889 List
*MediaThreadPool::queue
= NULL
;
1890 bool MediaThreadPool::valid
[max_threads
];
1893 MediaThreadPool::AddWork (MediaClosure
*closure
, bool wakeup
)
1895 pthread_attr_t attribs
;
1898 pthread_mutex_lock (&mutex
);
1900 if (shutting_down
) {
1901 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1904 queue
= new List ();
1905 queue
->Append (new MediaWork (closure
));
1907 // check if all threads are busy with other Media objects
1911 } else if (count
< max_threads
) {
1912 Media
*media
= closure
->GetMedia ();
1913 for (int i
= 0; i
< count
; i
++) {
1914 if (medias
[i
] == NULL
|| medias
[i
] == media
) {
1915 spawn
= false; // there is a thread working on this media or not working at all.
1924 int prev_count
= count
;
1926 count
++; // start up another thread.
1928 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count
);
1930 for (int i
= prev_count
; i
< count
&& result
== 0; i
++) {
1933 deployments
[i
] = NULL
;
1935 pthread_attr_init (&attribs
);
1936 pthread_attr_setdetachstate (&attribs
, PTHREAD_CREATE_JOINABLE
);
1937 result
= pthread_create (&threads
[i
], &attribs
, WorkerLoop
, NULL
);
1938 pthread_attr_destroy (&attribs
);
1941 fprintf (stderr
, "Moonlight: could not create media thread: %s (%i)\n", strerror (result
), result
);
1948 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1949 closure
->GetDescription (), closure
, closure
->GetMedia (), GET_OBJ_ID (closure
->GetMedia ()), closure
->GetDeployment (), queue
? queue
->Length () : -1);
1952 pthread_cond_signal (&condition
);
1954 pthread_mutex_unlock (&mutex
);
1958 MediaThreadPool::WaitForCompletion (Deployment
*deployment
)
1960 bool waiting
= false;
1961 MediaWork
*current
= NULL
;
1963 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment
);
1967 pthread_mutex_lock (&mutex
);
1971 /* check if the deployment is being worked on */
1972 for (int i
= 0; i
< count
; i
++) {
1973 if (deployments
[i
] == deployment
) {
1978 /* check if the deployment is in the queue */
1979 if (!waiting
&& queue
!= NULL
) {
1980 current
= (MediaWork
*) queue
->First ();
1981 while (current
!= NULL
) {
1982 if (current
->closure
->GetDeployment () == deployment
) {
1986 current
= (MediaWork
*) current
->next
;
1992 ts
.tv_nsec
= 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
1993 pthread_cond_timedwait (&completed_condition
, &mutex
, &ts
);
1996 pthread_mutex_unlock (&mutex
);
2000 MediaThreadPool::RemoveWork (Media
*media
)
2002 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media
, GET_OBJ_ID (media
));
2005 List::Node
*first
= NULL
;
2006 List::Node
*last
= NULL
;
2007 List::Node
*current
= NULL
;
2010 pthread_mutex_lock (&mutex
);
2012 // create a list of nodes to delete
2013 current
= queue
!= NULL
? queue
->First () : NULL
;
2014 while (current
!= NULL
) {
2015 next
= current
->next
; // retrieve next before Unlinking
2016 MediaWork
*mw
= (MediaWork
*) current
;
2017 if (mw
->closure
->GetMedia () == media
) {
2018 queue
->Unlink (current
);
2019 if (first
== NULL
) {
2022 last
->next
= current
;
2031 pthread_mutex_unlock (&mutex
);
2033 // We have to delete the list nodes with the
2034 // queue mutex unlocked, due to refcounting
2035 // (our node's (MediaWork) dtor will cause unrefs,
2036 // which may cause other dtors to be called,
2037 // eventually ending up wanting to lock the mutex
2041 while (current
!= NULL
) {
2042 next
= current
->next
;
2049 MediaThreadPool::WakeUp ()
2051 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2053 pthread_mutex_lock (&mutex
);
2054 pthread_cond_signal (&condition
);
2055 pthread_mutex_unlock (&mutex
);
2059 MediaThreadPool::IsThreadPoolThread ()
2061 bool result
= false;
2062 pthread_mutex_lock (&mutex
);
2063 for (int i
= 0; i
< count
; i
++) {
2064 if (pthread_equal (pthread_self (), threads
[i
])) {
2069 pthread_mutex_unlock (&mutex
);
2074 MediaThreadPool::Initialize ()
2076 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2079 shutting_down
= false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2083 MediaThreadPool::Shutdown ()
2085 List::Node
*current
= NULL
;
2086 List::Node
*next
= NULL
;
2088 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count
);
2092 g_return_if_fail (!shutting_down
);
2094 pthread_mutex_lock (&mutex
);
2096 shutting_down
= true;
2097 pthread_cond_broadcast (&condition
);
2099 for (int i
= 0; i
< count
; i
++) {
2103 pthread_mutex_unlock (&mutex
);
2104 pthread_join (threads
[i
], NULL
);
2105 pthread_mutex_lock (&mutex
);
2108 if (queue
!= NULL
) {
2109 current
= queue
->First ();
2110 queue
->Clear (false);
2116 pthread_mutex_unlock (&mutex
);
2118 // deleting a node can have side-effects, so we first copy the list of nodes,
2119 // clear the original and loop over the copy while deleting the nodes.
2120 // this prevents any reentering issues while deleting nodes.
2121 while (current
!= NULL
) {
2122 next
= current
->next
;
2127 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2131 MediaThreadPool::WorkerLoop (void *data
)
2133 MediaWork
*node
= NULL
;
2134 Media
*media
= NULL
;
2135 int self_index
= -1;
2138 * Unblock any signals. We inherit the blocked signals from the thread that
2139 * created us, and if that thread happens to be a thread that has signals
2140 * blocked, we might end up deadlocking in the gc (since the gc delivers
2141 * a suspend signal, this thread never gets it because the signal is blocked,
2142 * and the gc waits for us to handle the suspend signal).
2143 * The pulseaudio thread is one example of a thread that has all signals
2144 * blocked, causing this issue if we create a new thread from the
2145 * pulseaudio thread.
2148 sigset_t signal_set
;
2150 if ((err
= sigemptyset (&signal_set
)) != 0) {
2151 fprintf (stderr
, "Moonlight: Media thread pool was unable to create an empty set of signals: %s (%i).\n", strerror (err
), err
);
2152 } else if ((err
= pthread_sigmask (SIG_SETMASK
, &signal_set
, NULL
)) != 0) {
2153 fprintf (stderr
, "Moonlight: Media thread pool was unable to unblock all signals: %s (%i).\n", strerror (err
), err
);
2156 /* Something failed. Check if all signals are unblocked, if not, exit
2157 * the thread. Exiting the thread might cause media playback to fail,
2158 * while continuing with blocked signals will probably end up
2159 * deadlocking the gc.*/
2160 bool any_blocked_signals
= false;
2162 if (pthread_sigmask (SIG_BLOCK
, NULL
, &signal_set
) != 0) {
2163 any_blocked_signals
= true; /* Assume the worst */
2164 } else if (!sigisemptyset (&signal_set
)) {
2165 any_blocked_signals
= true;
2168 if (any_blocked_signals
) {
2169 fprintf (stderr
, "Moonlight: A media thread was started with blocked signals and could not unblock them. The media thread will exit (this may cause media playback to fail).\n");
2174 pthread_mutex_lock (&mutex
);
2175 for (int i
= 0; i
< count
; i
++) {
2176 if (pthread_equal (threads
[i
], pthread_self ())) {
2181 pthread_mutex_unlock (&mutex
);
2183 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index
);
2185 g_return_val_if_fail (self_index
>= 0, NULL
);
2187 while (!shutting_down
) {
2188 pthread_mutex_lock (&mutex
);
2190 medias
[self_index
] = NULL
;
2191 deployments
[self_index
] = NULL
;
2192 /* if anybody was waiting for us to finish working, notify them */
2194 pthread_cond_signal (&completed_condition
);
2197 node
= (MediaWork
*) (queue
!= NULL
? queue
->First () : NULL
);
2199 while (node
!= NULL
) {
2200 media
= node
->closure
->GetMedia ();
2202 for (int i
= 0; i
< count
; i
++) {
2203 if (medias
[i
] == media
) {
2204 // another thread is working for the same media object.
2205 // we need to find something else to do.
2214 node
= (MediaWork
*) node
->next
;
2218 pthread_cond_wait (&condition
, &mutex
);
2220 queue
->Unlink (node
);
2224 medias
[self_index
] = media
;
2225 /* At this point the current deployment might be wrong, so avoid
2226 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2227 * here, since it might end up doing a lot of work with the mutex
2229 deployments
[self_index
] = media
->GetUnsafeDeployment ();
2232 pthread_mutex_unlock (&mutex
);
2237 media
->SetCurrentDeployment (true, true);
2239 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: got %s %p for media %p on deployment %p, there are %d nodes left.\n", (int) pthread_self (), node
->closure
->GetDescription (), node
, media
, media
->GetDeployment (), queue
? queue
->Length () : -1);
2241 node
->closure
->Call ();
2243 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node
);
2248 pthread_mutex_lock (&mutex
);
2249 deployments
[self_index
] = NULL
;
2250 medias
[self_index
] = NULL
;
2251 /* if anybody was waiting for us to finish working, notify them */
2253 pthread_cond_signal (&completed_condition
);
2254 pthread_mutex_unlock (&mutex
);
2256 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index
);
2266 MediaClosure::MediaClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
, const char *description
)
2267 : EventObject (Type::MEDIACLOSURE
, true)
2269 Init (media
, callback
, context
);
2270 this->description
= description
;
2273 MediaClosure::MediaClosure (Type::Kind object_kind
, Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2274 : EventObject (object_kind
, true)
2276 Init (media
, callback
, context
);
2280 MediaClosure::Init (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2282 result
= MEDIA_INVALID
;
2284 this->callback
= callback
;
2285 this->context
= context
;
2287 this->context
->ref ();
2288 this->media
= media
;
2290 this->media
->ref ();
2292 // put checks at the end so that fields are still initialized, since we can't abort construction.
2293 g_return_if_fail (callback
!= NULL
);
2294 g_return_if_fail (media
!= NULL
);
2298 MediaClosure::Dispose ()
2312 EventObject::Dispose ();
2316 MediaClosure::Call ()
2319 result
= callback (this);
2321 result
= MEDIA_NO_CALLBACK
;
2326 * MediaDisposeObjectClosure
2328 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2329 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE
, media
, callback
, context
)
2334 MediaDisposeObjectClosure::Dispose ()
2336 if (!CallExecuted ()) {
2337 // we haven't been executed. do it now.
2339 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2344 MediaClosure::Dispose ();
2350 MediaSeekClosure::MediaSeekClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2351 : MediaClosure (Type::MEDIASEEKCLOSURE
, media
, callback
, context
)
2357 * MediaReportSeekCompletedClosure
2360 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2361 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE
, media
, callback
, context
)
2363 g_return_if_fail (context
!= NULL
);
2368 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2373 MediaReportSeekCompletedClosure::Dispose ()
2375 MediaClosure::Dispose ();
2379 * MediaGetFrameClosure
2382 MediaGetFrameClosure::MediaGetFrameClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, IMediaStream
*stream
)
2383 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2385 this->stream
= NULL
;
2387 g_return_if_fail (context
!= NULL
);
2388 g_return_if_fail (stream
!= NULL
);
2390 this->stream
= stream
;
2391 // this->stream->ref ();
2393 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2396 MediaGetFrameClosure::~MediaGetFrameClosure ()
2398 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2402 MediaGetFrameClosure::Dispose ()
2405 // stream->unref ();
2409 MediaClosure::Dispose ();
2410 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2414 * MediaReportFrameCompletedClosure
2417 MediaReportFrameCompletedClosure::MediaReportFrameCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, MediaFrame
*frame
)
2418 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2422 g_return_if_fail (context
!= NULL
);
2423 g_return_if_fail (frame
!= NULL
);
2425 this->frame
= frame
;
2426 this->frame
->ref ();
2430 MediaReportFrameCompletedClosure::Dispose ()
2437 MediaClosure::Dispose ();
2444 IMediaStream::IMediaStream (Type::Kind kind
, Media
*media
) : IMediaObject (kind
, media
)
2448 extra_data_size
= 0;
2460 input_ended
= false;
2461 output_ended
= false;
2463 first_pts
= G_MAXUINT64
; // The first pts in the stream, initialized to G_MAXUINT64
2464 last_popped_pts
= G_MAXUINT64
; // The pts of the last frame returned, initialized to G_MAXUINT64
2465 last_enqueued_pts
= G_MAXUINT64
; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2466 last_available_pts
= 0; // The pts of the last available frame, initialized to 0
2470 IMediaStream::Dispose ()
2473 IMediaDecoder
*d
= decoder
;
2478 g_free (extra_data
);
2484 IMediaObject::Dispose ();
2488 IMediaStream::CreateCodec (int codec_id
)
2491 case CODEC_WMV1
: return g_strdup ("wmv1");
2492 case CODEC_WMV2
: return g_strdup ("wmv2");
2493 case CODEC_WMV3
: return g_strdup ("wmv3");
2494 case CODEC_WMVA
: return g_strdup ("wmva");
2495 case CODEC_WVC1
: return g_strdup ("vc1");
2496 case CODEC_RGBA
: return g_strdup ("rgba");
2497 case CODEC_YV12
: return g_strdup ("yv12");
2498 case CODEC_MP3
: return g_strdup ("mp3");
2499 case CODEC_WMAV1
: return g_strdup ("wmav1");
2500 case CODEC_WMAV2
: return g_strdup ("wmav2");
2501 case CODEC_WMAV3
: return g_strdup ("wmav3");
2502 case CODEC_PCM
: return g_strdup ("pcm");
2504 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id
);
2506 /* This algorithm needs testing.
2509 int a = (codec_id & 0x000000FF);
2510 int b = (codec_id & 0x0000FF00) >> 8;
2511 int c = (codec_id & 0x00FF0000) >> 16;
2512 int d = (codec_id & 0xFF000000) >> 24;
2514 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2516 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2518 result = (char *) g_malloc (size + 1);
2521 result [current++] = (char) a;
2523 result [current++] = (char) b;
2525 result [current++] = (char) c;
2527 result [current++] = (char) d;
2528 result [current] = 0;
2530 return g_strdup ("<unknown>");
2536 IMediaStream::IsQueueEmpty ()
2538 return queue
.IsEmpty ();
2542 IMediaStream::GetStreamTypeName ()
2544 switch (GetType ()) {
2545 case MediaTypeVideo
: return "Video";
2546 case MediaTypeAudio
: return "Audio";
2547 case MediaTypeMarker
: return "Marker";
2548 default: return "Unknown";
2553 IMediaStream::ReportSeekCompleted ()
2555 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2556 input_ended
= false;
2557 output_ended
= false;
2559 if (decoder
!= NULL
)
2560 decoder
->ReportSeekCompleted ();
2564 IMediaStream::GetDemuxer ()
2567 IMediaDemuxer
*result
;
2572 media
= GetMediaReffed ();
2574 g_return_val_if_fail (media
!= NULL
, NULL
);
2576 result
= media
->GetDemuxer ();
2584 IMediaStream::GetDecoder ()
2590 IMediaStream::SetDecoder (IMediaDecoder
*value
)
2600 IMediaStream::GetOutputEnded ()
2602 return output_ended
;
2606 IMediaStream::SetOutputEnded (bool value
)
2608 output_ended
= value
;
2612 IMediaStream::GetInputEnded ()
2618 IMediaStream::SetInputEnded (bool value
)
2620 input_ended
= value
;
2621 if (GetDecoder () != NULL
)
2622 GetDecoder ()->ReportInputEnded ();
2626 IMediaStream::GetBufferedSize ()
2631 if (first_pts
== G_MAXUINT64
|| last_enqueued_pts
== G_MAXUINT64
)
2633 else if (last_popped_pts
== G_MAXUINT64
)
2634 result
= last_enqueued_pts
- first_pts
;
2636 result
= last_enqueued_pts
- last_popped_pts
;
2639 LOG_BUFFERING ("IMediaStream::GetBufferedSize (): id: %i, codec: %s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, result: %" G_GUINT64_FORMAT
" ms\n",
2640 GET_OBJ_ID (this), codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
), MilliSeconds_FromPts (result
));
2647 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2650 IMediaStream::PrintBufferInformation ()
2652 guint64 buffer_size
= GetBufferedSize ();
2654 printf (" <%s: ", codec
);
2656 if (GetSelected ()) {
2657 printf ("size: %.4" G_GINT64_FORMAT
", first: %.4" G_GINT64_FORMAT
", last popped: %.4" G_GINT64_FORMAT
", last enq: %.4" G_GINT64_FORMAT
", frames enq: %i>",
2658 TO_MS (buffer_size
), TO_MS (first_pts
), TO_MS (last_popped_pts
),
2659 TO_MS (last_enqueued_pts
), queue
.Length ());
2661 printf ("(not selected) >");
2667 IMediaStream::EnqueueFrame (MediaFrame
*frame
)
2672 g_return_if_fail (Media::InMediaThread ());
2674 media
= GetMediaReffed ();
2675 g_return_if_fail (media
!= NULL
);
2677 if (media
->IsStopped ()) {
2678 /* We need to enqueue one frame so that we can render the first frame for a stopped media element */
2679 if (first_pts
!= G_MAXUINT64
) {
2680 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame (we already have at least one frame).\n", frame
);
2683 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, but enqueing since we're empty.\n", frame
);
2687 if (frame
->buffer
== NULL
) {
2688 /* for some reason there is no output from the decoder, possibly because it needs more data from the demuxer before outputting anything */
2689 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No data in frame, not storing it.\n", frame
);
2694 if (first_pts
== G_MAXUINT64
)
2695 first_pts
= frame
->pts
;
2697 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
2700 if (last_enqueued_pts
> frame
->pts
&& last_enqueued_pts
!= G_MAXUINT64
&& frame
->event
!= FrameEventEOF
&& frame
->buflen
> 0) {
2701 g_warning ("IMediaStream::EnqueueFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, "
2702 "buffer: %" G_GUINT64_FORMAT
" ms, frame: %p, frame->buflen: %i, frame->pts: %" G_GUINT64_FORMAT
" ms (the last enqueued frame's pts is below the current frame's pts)\n",
2703 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2704 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), frame
, frame
->buflen
, MilliSeconds_FromPts (frame
->pts
));
2708 last_enqueued_pts
= frame
->pts
;
2709 first
= queue
.LinkedList ()->Length () == 0;
2710 queue
.LinkedList ()->Append (new StreamNode (frame
));
2713 SetLastAvailablePts (frame
->pts
);
2716 EmitSafe (FirstFrameEnqueuedEvent
);
2723 LOG_BUFFERING ("IMediaStream::EnqueueFrame (): codec: %.5s, first: %i, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, buffer: %" G_GUINT64_FORMAT
" ms, frame: %p, frame->buflen: %i\n",
2724 codec
, first
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2725 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
- first_pts
), frame
, frame
->buflen
);
2729 IMediaStream::PopFrame ()
2731 MediaFrame
*result
= NULL
;
2732 StreamNode
*node
= NULL
;
2734 // We use the queue lock to synchronize access to
2735 // last_popped_pts/last_enqueued_pts/first_pts
2738 node
= (StreamNode
*) queue
.LinkedList ()->First ();
2740 result
= node
->GetFrame ();
2742 queue
.LinkedList ()->Remove (node
);
2743 last_popped_pts
= result
->pts
;
2747 LOG_BUFFERING ("IMediaStream::PopFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, buffer: %" G_GUINT64_FORMAT
" ms, frame: %p, frame->buflen: %i\n",
2748 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2749 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), result
, result
? result
->buflen
: 0);
2751 if (!input_ended
&& !output_ended
&& result
!= NULL
) {
2752 IMediaDemuxer
*demuxer
= GetDemuxer ();
2753 if (demuxer
!= NULL
)
2754 demuxer
->FillBuffers ();
2761 IMediaStream::ClearQueue ()
2763 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2765 queue
.LinkedList ()->Clear (true);
2766 first_pts
= G_MAXUINT64
;
2767 last_popped_pts
= G_MAXUINT64
;
2768 last_enqueued_pts
= G_MAXUINT64
;
2773 IMediaStream::SetSelected (bool value
)
2775 Media
*media
= GetMediaReffed ();
2779 if (media
->GetDemuxer ())
2780 media
->GetDemuxer ()->UpdateSelected (this);
2786 * IMediaStream.StreamNode
2789 IMediaStream::StreamNode::StreamNode (MediaFrame
*f
)
2795 IMediaStream::StreamNode::~StreamNode ()
2804 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
, IMediaSource
*source
) : IMediaObject (kind
, media
)
2806 this->source
= source
;
2807 this->source
->ref ();
2813 pending_stream
= NULL
;
2814 pending_fill_buffers
= false;
2817 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
)
2818 : IMediaObject (kind
, media
)
2826 pending_stream
= NULL
;
2827 pending_fill_buffers
= false;
2831 IMediaDemuxer::Dispose ()
2833 if (streams
!= NULL
) {
2834 IMediaStream
**tmp
= streams
;
2835 int stream_count
= this->stream_count
;
2837 for (int i
= 0; i
< stream_count
; i
++) {
2838 tmp
[i
]->Dispose ();
2847 if (pending_stream
!= NULL
) {
2848 pending_stream
->unref ();
2849 pending_stream
= NULL
;
2852 IMediaObject::Dispose ();
2856 IMediaDemuxer::OpenCallback (MediaClosure
*closure
)
2858 IMediaDemuxer
*demuxer
;
2860 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure
);
2862 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2863 demuxer
->OpenDemuxerAsync ();
2865 return MEDIA_SUCCESS
;
2869 IMediaDemuxer::EnqueueOpen ()
2871 MediaClosure
*closure
;
2872 Media
*media
= GetMediaReffed ();
2874 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2879 closure
= new MediaClosure (media
, OpenCallback
, this, "IMediaDemuxer::OpenCallback");
2880 media
->EnqueueWork (closure
, false);
2886 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2888 Media
*media
= GetMediaReffed ();
2890 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media
);
2895 // Media might be null if we got disposed for some reason.
2899 media
->ReportOpenDemuxerCompleted ();
2904 IMediaDemuxer::ReportGetFrameProgress (double progress
)
2906 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress
);
2910 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream
*stream
)
2912 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream
);
2916 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind
, gint64 value
)
2918 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %" G_GINT64_FORMAT
")\n", kind
, value
);
2922 IMediaDemuxer::EnqueueReportGetFrameCompleted (MediaFrame
*frame
)
2924 Media
*media
= GetMediaReffed ();
2929 MediaClosure
*closure
= new MediaReportFrameCompletedClosure (media
, ReportGetFrameCompletedCallback
, this, frame
);
2930 media
->EnqueueWork (closure
);
2936 IMediaDemuxer::ReportGetFrameCompletedCallback (MediaClosure
*closure
)
2938 MediaReportFrameCompletedClosure
*c
= (MediaReportFrameCompletedClosure
*) closure
;
2940 g_return_val_if_fail (c
!= NULL
, MEDIA_FAIL
);
2941 g_return_val_if_fail (c
->GetDemuxer () != NULL
, MEDIA_FAIL
);
2943 c
->GetDemuxer ()->ReportGetFrameCompleted (c
->GetFrame ());
2945 return MEDIA_SUCCESS
;
2949 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame
*frame
)
2953 g_return_if_fail (frame
== NULL
|| (frame
!= NULL
&& frame
->stream
!= NULL
));
2954 g_return_if_fail (pending_stream
!= NULL
);
2956 media
= GetMediaReffed ();
2958 g_return_if_fail (media
!= NULL
);
2960 /* ensure we're on a media thread */
2961 if (!Media::InMediaThread ()) {
2962 EnqueueReportGetFrameCompleted (frame
);
2966 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p) %i %s %" G_GUINT64_FORMAT
" ms\n", frame
, GET_OBJ_ID (this), frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : (guint64
) -1);
2968 if (frame
== NULL
) {
2969 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame
, pending_stream
->GetStreamTypeName ());
2970 // No more data for this stream
2971 pending_stream
->SetInputEnded (true);
2972 } else if (!frame
->stream
->IsDisposed ()) {
2973 IMediaDecoder
*decoder
= frame
->stream
->GetDecoder ();
2974 if (decoder
!= NULL
)
2975 decoder
->DecodeFrameAsync (frame
, true /* always enqueue */);
2978 pending_stream
->unref ();
2979 pending_stream
= NULL
; // not waiting for anything more
2981 // enqueue some more
2990 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure
*c
)
2992 MediaReportSeekCompletedClosure
*closure
= (MediaReportSeekCompletedClosure
*) c
;
2993 IMediaDemuxer
*demuxer
;
2995 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
2996 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
2998 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2999 demuxer
->ReportSeekCompleted (closure
->GetPts ());
3001 return MEDIA_SUCCESS
;
3005 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts
)
3007 Media
*media
= GetMediaReffed ();
3012 MediaClosure
*closure
= new MediaReportSeekCompletedClosure (media
, ReportSeekCompletedCallback
, this, pts
);
3013 media
->EnqueueWork (closure
);
3019 IMediaDemuxer::ReportSeekCompleted (guint64 pts
)
3023 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
")\n", pts
);
3025 g_return_if_fail (seeking
);
3027 if (!Media::InMediaThread ()) {
3028 EnqueueReportSeekCompleted (pts
);
3033 if (pending_stream
!= NULL
)
3034 printf ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
"): we can't be waiting for a frame now.\n", pts
);
3037 media
= GetMediaReffed ();
3039 g_return_if_fail (media
!= NULL
);
3041 /* We need to call ReportSeekCompleted once for every time SeekAsync(pts) was called */
3042 for (int i
= 0; i
< GetStreamCount (); i
++) {
3043 IMediaStream
*stream
= GetStream (i
);
3048 stream
->ReportSeekCompleted ();
3051 media
->ReportSeekCompleted (pts
);
3056 seeking
= !seeks
.IsEmpty ();
3060 pending_fill_buffers
= false;
3063 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
"): still pending seeks, enqueuing another seek.\n", pts
);
3067 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
") [Done]\n", pts
);
3071 IMediaDemuxer::OpenDemuxerAsync ()
3073 g_return_if_fail (opened
== false);
3077 OpenDemuxerAsyncInternal ();
3081 IMediaDemuxer::GetFrameCallback (MediaClosure
*c
)
3083 MediaGetFrameClosure
*closure
= (MediaGetFrameClosure
*) c
;
3084 IMediaDemuxer
*demuxer
;
3086 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
3087 g_return_val_if_fail (closure
->GetStream () != NULL
, MEDIA_FAIL
);
3088 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
3090 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3091 demuxer
->GetFrameAsync (closure
->GetStream ());
3093 return MEDIA_SUCCESS
;
3097 IMediaDemuxer::EnqueueGetFrame (IMediaStream
*stream
)
3099 g_return_if_fail (pending_stream
== NULL
); // we can't be waiting for another frame.
3101 Media
*media
= GetMediaReffed ();
3106 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
3107 media
->EnqueueWork (closure
);
3113 IMediaDemuxer::GetFrameAsync (IMediaStream
*stream
)
3115 Media
*media
= NULL
;
3117 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream
, stream
->GetStreamTypeName (), Media::InMediaThread ());
3119 if (!Media::InMediaThread ()) {
3120 EnqueueGetFrame (stream
);
3125 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (): delayed since we're waiting for a seek.\n");
3129 if (pending_stream
!= NULL
) {
3130 /* we're already waiting for a frame */
3134 media
= GetMediaReffed ();
3136 g_return_if_fail (media
!= NULL
);
3138 if (stream
!= NULL
) {
3139 pending_stream
= stream
;
3140 pending_stream
->ref ();
3141 GetFrameAsyncInternal (stream
);
3150 IMediaDemuxer::SeekCallback (MediaClosure
*closure
)
3152 MediaSeekClosure
*seek
= (MediaSeekClosure
*) closure
;
3153 seek
->GetDemuxer ()->SeekAsync ();
3154 return MEDIA_SUCCESS
;
3158 IMediaDemuxer::EnqueueSeek ()
3160 Media
*media
= GetMediaReffed ();
3161 MediaSeekClosure
*closure
;
3163 g_return_if_fail (media
!= NULL
);
3165 closure
= new MediaSeekClosure (media
, SeekCallback
, this, 0);
3166 media
->EnqueueWork (closure
, true);
3172 IMediaDemuxer::SeekAsync ()
3174 guint64 pts
= G_MAXUINT64
;
3176 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (), seeking: %i\n", seeking
);
3178 g_return_if_fail (Media::InMediaThread ());
3180 seeking
= true; /* this ensures that we stop demuxing frames asap */
3182 if (pending_stream
!= NULL
) {
3183 /* we're waiting for the decoder to decode a frame, wait a bit with the seek */
3184 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i waiting for a frame, postponing seek\n", GET_OBJ_ID (this));
3190 if (!seeks
.IsEmpty ())
3191 pts
= ((PtsNode
*) seeks
.First ())->pts
;
3194 if (pts
== G_MAXUINT64
) {
3195 LOG_PIPELINE ("IMediaDemuxer.:SeekAsync (): %i no pending seek?\n", GET_OBJ_ID (this));
3200 /* Ask the demuxer to seek */
3201 /* at this point the pipeline shouldn't be doing anything else (for this media) */
3202 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i seeking to %" G_GUINT64_FORMAT
"\n", GET_OBJ_ID (this), pts
);
3203 SeekAsyncInternal (pts
);
3207 IMediaDemuxer::SeekAsync (guint64 pts
)
3209 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%" G_GUINT64_FORMAT
")\n", pts
);
3216 seeks
.Append (new PtsNode (pts
));
3223 IMediaDemuxer::ClearBuffers ()
3225 pending_fill_buffers
= false;
3227 /* Clear all the buffer queues */
3228 for (int i
= 0; i
< GetStreamCount (); i
++) {
3229 IMediaStream
*stream
= GetStream (i
);
3234 stream
->ClearQueue ();
3239 IMediaDemuxer::FillBuffersCallback (MediaClosure
*closure
)
3241 IMediaDemuxer
*demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3242 demuxer
->FillBuffersInternal ();
3243 return MEDIA_SUCCESS
;
3247 IMediaDemuxer::FillBuffers ()
3249 Media
*media
= NULL
;
3250 MediaClosure
*closure
;
3251 bool enqueue
= true;
3254 if (pending_fill_buffers
) {
3255 // there's already a FillBuffers request enqueued
3258 media
= GetMediaReffed ();
3259 if (media
== NULL
) {
3263 pending_fill_buffers
= true;
3269 closure
= new MediaClosure (media
, FillBuffersCallback
, this, "IMediaDemuxer::FillBuffersCallback");
3270 media
->EnqueueWork (closure
);
3279 IMediaDemuxer::FillBuffersInternal ()
3281 IMediaStream
*stream
;
3282 IMediaStream
*request_stream
= NULL
;
3283 guint64 min_buffered_size
= G_MAXUINT64
;
3284 Media
*media
= GetMediaReffed ();
3285 guint64 buffering_time
= 0;
3286 guint64 buffered_size
= 0;
3288 int media_streams
= 0;
3290 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (), %i %s buffering time: %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms, pending_stream: %i %s\n", GET_OBJ_ID (this), GetTypeName (), buffering_time
, media
!= NULL
? MilliSeconds_FromPts (media
->GetBufferingTime ()) : -1, GET_OBJ_ID (pending_stream
), pending_stream
? pending_stream
->GetStreamTypeName () : "NULL");
3293 pending_fill_buffers
= false;
3299 // If we're waiting for something, there's nothing to do here.
3300 if (pending_stream
!= NULL
)
3303 // Find the stream with the smallest buffered size, and request a frame from that stream.
3304 g_return_if_fail (media
!= NULL
);
3306 buffering_time
= media
->GetBufferingTime ();
3308 if (buffering_time
== 0) {
3309 // Play as soon as possible.
3310 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3311 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3312 // all streams have 1 frame queued.
3316 for (int i
= 0; i
< GetStreamCount (); i
++) {
3317 IMediaDecoder
*decoder
= NULL
;
3319 stream
= GetStream (i
);
3320 if (!stream
->GetSelected ())
3323 if (stream
->GetType () != MediaTypeVideo
&&
3324 stream
->GetType () != MediaTypeAudio
)
3328 if (stream
->GetOutputEnded ()) {
3330 continue; // this stream has ended.
3333 decoder
= stream
->GetDecoder ();
3334 if (decoder
== NULL
) {
3335 fprintf (stderr
, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream
->GetStreamTypeName (), GET_OBJ_ID (stream
), stream
->GetRefCount ());
3336 continue; // no decoder??
3339 buffered_size
= stream
->GetBufferedSize ();
3340 min_buffered_size
= MIN (min_buffered_size
, buffered_size
);
3342 if (buffered_size
>= buffering_time
)
3343 continue; // this stream has enough data buffered.
3345 if (!decoder
->IsDecoderQueueEmpty ())
3346 continue; // this stream is waiting for data to be decoded.
3348 if (buffered_size
<= min_buffered_size
)
3349 request_stream
= stream
;
3351 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): codec: %s, stream id: %i, buffered size: %" G_GUINT64_FORMAT
" ms, buffering time: %" G_GUINT64_FORMAT
" ms, last popped time: %" G_GUINT64_FORMAT
" ms\n",
3352 stream
->codec
, GET_OBJ_ID (stream
), MilliSeconds_FromPts (buffered_size
), MilliSeconds_FromPts (buffering_time
), MilliSeconds_FromPts (stream
->GetLastPoppedPts ()));
3355 if (request_stream
!= NULL
) {
3356 if (media
->IsStopped ()) {
3357 if (!request_stream
->IsQueueEmpty ()) {
3358 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, and we have frames in the buffer.\n");
3361 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, but the buffer is empty, continuing\n");
3365 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): requesting frame from %s stream (%i), min_buffered_size: %" G_GUINT64_FORMAT
" ms\n", request_stream
->GetStreamTypeName (), GET_OBJ_ID (stream
), MilliSeconds_FromPts (min_buffered_size
));
3366 GetFrameAsync (request_stream
);
3369 if (media_streams
> 0) {
3370 if (ended
== media_streams
) {
3371 media
->ReportBufferingProgress (1.0);
3373 if (min_buffered_size
> 0 && buffering_time
> 0) {
3374 double progress
= ((double) min_buffered_size
/ (double) buffering_time
);
3375 media
->ReportBufferingProgress (progress
);
3384 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT
" ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3388 IMediaDemuxer::GetBufferedSize ()
3390 guint64 result
= G_MAXUINT64
;
3391 IMediaStream
*stream
;
3393 for (int i
= 0; i
< GetStreamCount (); i
++) {
3394 stream
= GetStream (i
);
3395 if (!stream
->GetSelected ())
3398 if (stream
->GetType () != MediaTypeVideo
&& stream
->GetType () != MediaTypeAudio
)
3401 result
= MIN (result
, stream
->GetBufferedSize ());
3408 IMediaDemuxer::GetLastAvailablePts ()
3410 guint64 result
= G_MAXUINT64
;
3411 IMediaStream
*stream
;
3413 for (int i
= 0; i
< GetStreamCount (); i
++) {
3414 stream
= GetStream (i
);
3416 if (stream
== NULL
|| !stream
->GetSelected ())
3419 result
= MIN (result
, stream
->GetLastAvailablePts ());
3422 if (result
== G_MAXUINT64
)
3430 IMediaDemuxer::PrintBufferInformation ()
3432 printf ("Buffer: %" G_GINT64_FORMAT
"", MilliSeconds_FromPts (GetBufferedSize ()));
3433 for (int i
= 0; i
< GetStreamCount (); i
++) {
3434 GetStream (i
)->PrintBufferInformation ();
3441 IMediaDemuxer::GetDuration ()
3444 for (int i
= 0; i
< GetStreamCount (); i
++)
3445 result
= MAX (result
, GetStream (i
)->duration
);
3450 IMediaDemuxer::GetStream (int index
)
3452 return (index
< 0 || index
>= stream_count
) ? NULL
: streams
[index
];
3459 MediaFrame::MediaFrame (IMediaStream
*stream
)
3460 : EventObject (Type::MEDIAFRAME
, true)
3464 g_return_if_fail (stream
!= NULL
);
3466 this->stream
= stream
;
3467 this->stream
->ref ();
3470 MediaFrame::MediaFrame (IMediaStream
*stream
, guint8
*buffer
, guint32 buflen
, guint64 pts
, bool keyframe
)
3471 : EventObject (Type::MEDIAFRAME
, true)
3475 g_return_if_fail (stream
!= NULL
);
3477 this->stream
= stream
;
3478 this->stream
->ref ();
3479 this->buffer
= buffer
;
3480 this->buflen
= buflen
;
3484 if (buflen
> 4 && false) {
3485 printf ("MediaFrame::MediaFrame () %s buffer: ", stream
->GetStreamTypeName ());
3486 for (int i
= 0; i
< 4; i
++)
3487 printf (" 0x%x", buffer
[i
]);
3493 AddState (MediaFrameKeyFrame
);
3497 MediaFrame::Initialize ()
3499 decoder_specific_data
= NULL
;
3511 for (int i
= 0; i
< 4; i
++) {
3522 MediaFrame::~MediaFrame ()
3527 MediaFrame::Dispose ()
3529 IMediaDecoder
*decoder
;
3532 // We can be called either on the main thread just before destruction
3533 // (in which case there are no races since the code which unreffed us
3534 // is the only code which knows about us), or at any time from the
3537 if (GetRefCount () != 0 && stream
!= NULL
) {
3538 if (!Media::InMediaThread ()) {
3539 // if refcount != 0 we're not being called just before destruction, in which case we should
3540 // only be on the media thread.
3541 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3546 if (decoder_specific_data
!= NULL
&& stream
!= NULL
) {
3547 decoder
= stream
->GetDecoder ();
3548 if (decoder
!= NULL
)
3549 decoder
->Cleanup (this);
3562 EventObject::Dispose ();
3566 MediaFrame::SetSrcSlideY (int value
)
3572 MediaFrame::SetSrcSlideH (int value
)
3578 MediaFrame::SetSrcStride (int a
, int b
, int c
, int d
)
3587 MediaFrame::SetDataStride (guint8
* a
, guint8
* b
, guint8
* c
, guint8
* d
)
3589 data_stride
[0] = a
;
3590 data_stride
[1] = b
;
3591 data_stride
[2] = c
;
3592 data_stride
[3] = d
;
3596 * IMediaObject.EventData
3599 IMediaObject::EventData::EventData (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3601 this->event_id
= event_id
;
3602 this->handler
= handler
;
3603 this->context
= context
;
3604 this->context
->ref ();
3605 this->invoke_on_main_thread
= invoke_on_main_thread
;
3608 IMediaObject::EventData::~EventData ()
3615 * IMediaObject.EmitData
3618 IMediaObject::EmitData::EmitData (int event_id
, EventHandler handler
, EventObject
*context
, EventArgs
*args
)
3620 this->event_id
= event_id
;
3621 this->handler
= handler
;
3622 this->context
= context
;
3623 this->context
->ref ();
3629 IMediaObject::EmitData::~EmitData ()
3643 IMediaObject::IMediaObject (Type::Kind kind
, Media
*media
)
3644 : EventObject (kind
, true)
3646 this->media
= media
;
3648 this->media
->ref ();
3649 g_return_if_fail (media
!= NULL
);
3651 emit_on_main_thread
= NULL
;
3655 IMediaObject::Dispose ()
3659 // We can be called either on the main thread just before destruction
3660 // (in which case there are no races since the code which unreffed us
3661 // is the only code which knows about us), or at any time from the
3663 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3664 // if refcount != 0 we're not being called just before destruction, in which case we should
3665 // only be on the media thread.
3666 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3670 media_mutex
.Lock ();
3675 media_mutex
.Unlock ();
3677 event_mutex
.Lock ();
3680 if (emit_on_main_thread
!= NULL
) {
3681 delete emit_on_main_thread
;
3682 emit_on_main_thread
= NULL
;
3684 event_mutex
.Unlock ();
3686 EventObject::Dispose ();
3690 IMediaObject::AddSafeHandler (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3692 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id
, handler
, context
, invoke_on_main_thread
);
3695 if (!IsDisposed ()) {
3696 ed
= new EventData (event_id
, handler
, context
, invoke_on_main_thread
);
3697 event_mutex
.Lock ();
3699 events
= new List ();
3700 events
->Append (ed
);
3701 event_mutex
.Unlock ();
3706 IMediaObject::RemoveSafeHandlers (EventObject
*context
)
3711 event_mutex
.Lock ();
3712 if (events
!= NULL
) {
3713 ed
= (EventData
*) events
->First ();
3714 while (ed
!= NULL
) {
3715 next
= (EventData
*) ed
->next
;
3716 if (ed
->context
== context
)
3717 events
->Remove (ed
);
3721 event_mutex
.Unlock ();
3725 IMediaObject::EmitSafe (int event_id
, EventArgs
*args
)
3727 List
*emits
= NULL
; // The events to emit on this thread.
3734 // Create a list of all the events to emit
3735 // don't keep the lock while emitting.
3736 event_mutex
.Lock ();
3737 if (events
!= NULL
) {
3738 ed
= (EventData
*) events
->First ();
3739 while (ed
!= NULL
) {
3740 if (ed
->event_id
== event_id
) {
3741 emit
= new EmitData (event_id
, ed
->handler
, ed
->context
, args
);
3742 if (ed
->invoke_on_main_thread
) {
3743 if (emit_on_main_thread
== NULL
)
3744 emit_on_main_thread
= new List ();
3745 emit_on_main_thread
->Append (emit
);
3748 emits
= new List ();
3749 emits
->Append (emit
);
3752 ed
= (EventData
*) ed
->next
;
3755 event_mutex
.Unlock ();
3757 // emit the events to be emitted on this thread
3760 if (Surface::InMainThread ()) {
3761 // if we're already on the main thread,
3762 // we can the events to be emitted
3763 // on the main thread
3765 event_mutex
.Lock ();
3766 tmp
= emit_on_main_thread
;
3767 emit_on_main_thread
= NULL
;
3768 event_mutex
.Unlock ();
3771 AddTickCallSafe (EmitListCallback
);
3780 IMediaObject::EmitListMain ()
3785 event_mutex
.Lock ();
3786 list
= emit_on_main_thread
;
3787 emit_on_main_thread
= NULL
;
3788 event_mutex
.Unlock ();
3793 IMediaObject::EmitListCallback (EventObject
*obj
)
3795 IMediaObject
*media_obj
= (IMediaObject
*) obj
;
3796 media_obj
->EmitListMain ();
3800 IMediaObject::EmitList (List
*list
)
3807 emit
= (EmitData
*) list
->First ();
3808 while (emit
!= NULL
) {
3809 emit
->handler (this, emit
->args
, emit
->context
);
3810 emit
= (EmitData
*) emit
->next
;
3817 IMediaObject::GetMediaReffed ()
3820 media_mutex
.Lock ();
3824 media_mutex
.Unlock ();
3829 IMediaObject::ReportErrorOccurred (char const *message
)
3831 g_return_if_fail (media
!= NULL
);
3833 media
->ReportErrorOccurred (message
);
3837 IMediaObject::ReportErrorOccurred (MediaResult result
)
3839 g_return_if_fail (media
!= NULL
);
3841 media
->ReportErrorOccurred (result
);
3845 IMediaObject::ReportErrorOccurred (ErrorEventArgs
*args
)
3847 g_return_if_fail (media
!= NULL
);
3849 media
->ReportErrorOccurred (args
);
3853 IMediaObject::SetMedia (Media
*value
)
3855 media_mutex
.Lock ();
3861 media_mutex
.Unlock ();
3868 IMediaSource::IMediaSource (Type::Kind kind
, Media
*media
)
3869 : IMediaObject (kind
, media
)
3871 pthread_mutexattr_t attribs
;
3872 pthread_mutexattr_init (&attribs
);
3873 pthread_mutexattr_settype (&attribs
, PTHREAD_MUTEX_RECURSIVE
);
3874 pthread_mutex_init (&mutex
, &attribs
);
3875 pthread_mutexattr_destroy (&attribs
);
3877 pthread_cond_init (&condition
, NULL
);
3880 IMediaSource::~IMediaSource ()
3882 pthread_mutex_destroy (&mutex
);
3883 pthread_cond_destroy (&condition
);
3887 IMediaSource::Dispose ()
3889 IMediaObject::Dispose ();
3893 IMediaSource::Lock ()
3895 pthread_mutex_lock (&mutex
);
3899 IMediaSource::Unlock ()
3901 pthread_mutex_unlock (&mutex
);
3905 IMediaSource::ReadSome (void *buf
, guint32 n
)
3909 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf
, n
);
3913 result
= ReadInternal (buf
, n
);
3915 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %" G_GINT64_FORMAT
"\n", GET_OBJ_ID (this), buf
, n
, result
, GetPosition ());
3923 IMediaSource::ReadAll (void *buf
, guint32 n
)
3926 gint64 prev
= GetPosition ();
3927 gint64 avail
= GetLastAvailablePosition ();
3929 //printf ("IMediaSource::ReadAll (%p, %u), position: %" G_GINT64_FORMAT "\n", buf, n, prev);
3931 read
= ReadSome (buf
, n
);
3933 if ((gint64
) read
!= (gint64
) n
) {
3934 FileSource
*fs
= NULL
;
3936 if (GetType () == MediaSourceTypeFile
)
3937 fs
= (FileSource
*) this;
3938 g_warning ("IMediaSource::ReadInternal (%i): Read failed, read %i bytes. available size: %" G_GINT64_FORMAT
", size: %" G_GINT64_FORMAT
", pos: %" G_GINT64_FORMAT
", prev pos: %" G_GINT64_FORMAT
", position not available: %" G_GINT64_FORMAT
", feof: %i, ferror: %i, strerror: %s\n",
3939 n
, read
, avail
, GetSize (), GetPosition (), prev
, prev
+ n
, fs
? feof (fs
->fd
) : -1, fs
? ferror (fs
->fd
) : -1, fs
? strerror (ferror (fs
->fd
)) : "<N/A>");
3940 print_stack_trace ();
3943 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf
, n
, read
);
3945 return (gint64
) read
== (gint64
) n
;
3949 IMediaSource::Peek (void *buf
, guint32 n
)
3956 read
= PeekInternal (buf
, n
);
3957 result
= read
== (gint64
) n
;
3961 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %" G_GINT64_FORMAT
" bytes.\n", buf
, n
, result
, read
);
3967 IMediaSource::Seek (gint64 offset
, int mode
)
3969 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%" G_GINT64_FORMAT
", %d = %s)\n",
3970 GET_OBJ_ID (this), ToString (), offset
, mode
, mode
== SEEK_SET
? "SEEK_SET"
3971 : (mode
== SEEK_CUR
? "SEEK_CUR" : (mode
== SEEK_END
? "SEEK_END" : "<invalid value>")));
3975 result
= SeekInternal (offset
, mode
);
3981 IMediaSource::IsPositionAvailable (gint64 position
, bool *eof
)
3983 gint64 available
= GetLastAvailablePosition ();
3984 gint64 size
= GetSize ();
3988 if (size
!= -1 && size
< position
) {
3989 // Size is known and smaller than the requested position
3994 if (available
!= -1 && available
< position
) {
3995 // Not everything is available and the available position is smaller than the requested position
4000 if (size
== -1 && available
== -1) {
4001 // Size is not known, but everything is available??
4002 // This is probably due to a bug in the derived *Source class
4004 fprintf (stderr
, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
4012 IMediaSource::GetLastAvailablePosition ()
4016 result
= GetLastAvailablePositionInternal ();
4022 IMediaSource::GetPositionInternal ()
4024 // This method should be overridden (or never called for the classes which doesn't override it).
4025 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4026 print_stack_trace ();
4031 IMediaSource::SeekInternal (gint64 offset
, int mode
)
4033 g_warning ("IMediaSource (%s)::SeekInternal (%" G_GINT64_FORMAT
", %i): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), offset
, mode
);
4034 print_stack_trace ();
4040 IMediaSource::ReadInternal (void *buffer
, guint32 n
)
4042 g_warning ("IMediaSource (%s)::ReadInternal (%p, %u): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), buffer
, n
);
4043 print_stack_trace ();
4049 IMediaSource::PeekInternal (void *buffer
, guint32 n
)
4051 g_warning ("IMediaSource (%s)::PeekInternal (%p, %u): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), buffer
, n
);
4052 print_stack_trace ();
4058 IMediaSource::GetSizeInternal ()
4060 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4061 print_stack_trace ();
4067 IMediaSource::GetPosition ()
4071 result
= GetPositionInternal ();
4077 IMediaSource::GetSize ()
4081 result
= GetSizeInternal ();
4091 IMediaDemuxer::SetStreams (IMediaStream
** streams
, int count
)
4093 this->streams
= streams
;
4094 this->stream_count
= count
;
4096 for (int i
= 0; i
< count
; i
++)
4097 this->streams
[i
]->ref ();
4101 IMediaDemuxer::AddStream (IMediaStream
*stream
)
4103 g_return_val_if_fail (stream
!= NULL
, -1);
4106 streams
= (IMediaStream
**) g_realloc (streams
, stream_count
* sizeof (IMediaStream
*));
4107 streams
[stream_count
- 1] = stream
;
4110 return stream_count
- 1;
4117 IMediaDecoder::IMediaDecoder (Type::Kind kind
, Media
*media
, IMediaStream
*stream
) : IMediaObject (kind
, media
)
4119 this->stream
= NULL
;
4121 g_return_if_fail (stream
!= NULL
);
4123 this->stream
= stream
;
4124 this->stream
->ref ();
4128 input_ended
= false;
4132 IMediaDecoder::Dispose ()
4134 if (stream
!= NULL
) {
4135 IMediaStream
*s
= stream
;
4144 IMediaObject::Dispose ();
4148 IMediaDecoder::ReportSeekCompleted ()
4151 input_ended
= false;
4156 IMediaDecoder::ReportInputEnded ()
4159 if (IsDecoderQueueEmpty ()) {
4165 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame
*frame
)
4167 IMediaDemuxer
*demuxer
;
4168 IMediaStream
*stream
;
4169 Media
*media
= NULL
;
4171 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
4173 g_return_if_fail (frame
!= NULL
);
4175 media
= GetMediaReffed ();
4176 g_return_if_fail (media
!= NULL
);
4178 stream
= frame
->stream
;
4182 frame
->stream
->EnqueueFrame (frame
);
4184 demuxer
= stream
->GetDemuxer ();
4185 if (demuxer
!= NULL
)
4186 demuxer
->FillBuffers ();
4188 if (input_ended
&& IsDecoderQueueEmpty ())
4197 IMediaDecoder::DecodeFrameCallback (MediaClosure
*closure
)
4200 IMediaDecoder
*decoder
= (IMediaDecoder
*) closure
->GetContext ();
4201 IMediaDecoder::FrameNode
*node
= (IMediaDecoder::FrameNode
*) decoder
->queue
.Pop ();
4204 decoder
->DecodeFrameAsync (node
->frame
, false);
4208 return MEDIA_SUCCESS
;
4212 IMediaDecoder::DecodeFrameAsync (MediaFrame
*frame
, bool enqueue_always
)
4216 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame
, (frame
&& frame
->stream
) ? frame
->stream
->GetStreamTypeName () : NULL
);
4221 g_return_if_fail (frame
!= NULL
);
4223 media
= GetMediaReffed ();
4225 g_return_if_fail (media
!= NULL
);
4227 if (enqueue_always
|| !Media::InMediaThread ()) {
4228 MediaClosure
*closure
= new MediaClosure (media
, DecodeFrameCallback
, this, "IMediaDecoder::DecodeFrameCallback");
4229 queue
.Push (new FrameNode (frame
));
4230 media
->EnqueueWork (closure
);
4235 DecodeFrameAsyncInternal (frame
);
4242 IMediaDecoder::OpenDecoderAsync ()
4244 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4246 g_return_if_fail (opening
== false);
4247 g_return_if_fail (opened
== false);
4250 OpenDecoderAsyncInternal ();
4254 IMediaDecoder::ReportOpenDecoderCompleted ()
4256 Media
*media
= GetMediaReffed ();
4258 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4263 g_return_if_fail (media
!= NULL
);
4265 media
->ReportOpenDecoderCompleted (this);
4273 IImageConverter::IImageConverter (Type::Kind kind
, Media
*media
, VideoStream
*stream
) : IMediaObject (kind
, media
)
4275 output_format
= MoonPixelFormatNone
;
4276 input_format
= MoonPixelFormatNone
;
4277 this->stream
= stream
;
4284 VideoStream::VideoStream (Media
*media
) : IMediaStream (Type::VIDEOSTREAM
, media
)
4287 bits_per_sample
= 0;
4294 VideoStream::VideoStream (Media
*media
, int codec_id
, guint32 width
, guint32 height
, guint64 duration
, gpointer extra_data
, guint32 extra_data_size
)
4295 : IMediaStream (Type::VIDEOSTREAM
, media
)
4298 bits_per_sample
= 0;
4301 this->height
= height
;
4302 this->width
= width
;
4303 this->duration
= duration
;
4304 this->codec_id
= codec_id
;
4305 this->codec
= CreateCodec (codec_id
);
4306 this->extra_data
= extra_data
;
4307 this->extra_data_size
= extra_data_size
;
4310 VideoStream::~VideoStream ()
4315 VideoStream::Dispose ()
4318 converter
->Dispose ();
4319 converter
->unref ();
4322 IMediaStream::Dispose ();
4326 * MediaMarkerFoundClosure
4329 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media
*media
, MediaCallback
*callback
, MediaElement
*context
)
4330 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE
, media
, callback
, context
)
4336 MediaMarkerFoundClosure::Dispose ()
4342 MediaClosure::Dispose ();
4346 MediaMarkerFoundClosure::SetMarker (MediaMarker
*marker
)
4349 this->marker
->unref ();
4350 this->marker
= marker
;
4352 this->marker
->ref ();
4359 MediaMarker::MediaMarker (const char *type
, const char *text
, guint64 pts
)
4360 : EventObject (Type::MEDIAMARKER
)
4362 this->type
= g_strdup (type
);
4363 this->text
= g_strdup (text
);
4367 MediaMarker::~MediaMarker ()
4377 MarkerStream::MarkerStream (Media
*media
) : IMediaStream (Type::MARKERSTREAM
, media
)
4383 MarkerStream::Dispose ()
4390 IMediaStream::Dispose ();
4394 MarkerStream::MarkerFound (MediaFrame
*frame
)
4396 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4398 if (GetDecoder () == NULL
) {
4399 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4403 GetDecoder ()->DecodeFrameAsync (frame
, false);
4407 MarkerStream::FrameEnqueued ()
4411 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4413 frame
= PopFrame ();
4415 if (frame
== NULL
) {
4416 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4420 if (closure
!= NULL
) {
4421 closure
->SetMarker (frame
->marker
);
4423 closure
->SetMarker (NULL
);
4425 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4427 list
.Append (new MediaMarker::Node (frame
->marker
));
4435 MarkerStream::Pop ()
4437 MediaMarker
*result
= NULL
;
4438 MediaMarker::Node
*node
;
4441 node
= (MediaMarker::Node
*) list
.First ();
4443 result
= node
->marker
;
4453 MarkerStream::SetCallback (MediaMarkerFoundClosure
*closure
)
4456 this->closure
->unref ();
4457 this->closure
= closure
;
4459 this->closure
->ref ();
4465 MediaWork::MediaWork (MediaClosure
*c
)
4467 g_return_if_fail (c
!= NULL
);
4473 MediaWork::~MediaWork ()
4475 g_return_if_fail (closure
!= NULL
);
4482 * PassThroughDecoderInfo
4486 PassThroughDecoderInfo::Supports (const char *codec
)
4488 const char *video_fourccs
[] = { "yv12", "rgba", NULL
};
4489 const char *audio_fourccs
[] = { "pcm", NULL
};
4491 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4492 if (!strcmp (codec
, video_fourccs
[i
]))
4495 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4496 if (!strcmp (codec
, audio_fourccs
[i
]))
4503 * PassThroughDecoder
4506 PassThroughDecoder::PassThroughDecoder (Media
*media
, IMediaStream
*stream
)
4507 : IMediaDecoder (Type::PASSTHROUGHDECODER
, media
, stream
)
4512 PassThroughDecoder::Dispose ()
4514 IMediaDecoder::Dispose ();
4518 PassThroughDecoder::OpenDecoderAsyncInternal ()
4520 const char *fourcc
= GetStream ()->GetCodec ();
4522 if (!strcmp (fourcc
, "yv12")) {
4523 SetPixelFormat (MoonPixelFormatYUV420P
);
4524 } else if (!strcmp (fourcc
, "rgba")) {
4525 SetPixelFormat (MoonPixelFormatRGBA32
);
4526 } else if (!strcmp (fourcc
, "pcm")) {
4527 // nothing to do here
4529 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc
));
4533 ReportOpenDecoderCompleted ();
4537 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4539 frame
->AddState (MediaFrameDecoded
);
4540 if (GetPixelFormat () == MoonPixelFormatYUV420P
) {
4541 VideoStream
*vs
= (VideoStream
*) GetStream ();
4543 frame
->width
= vs
->width
;
4544 frame
->height
= vs
->height
;
4546 frame
->data_stride
[0] = frame
->buffer
;
4547 frame
->data_stride
[1] = frame
->buffer
+ (frame
->width
*frame
->height
);
4548 frame
->data_stride
[2] = frame
->buffer
+ (frame
->width
*frame
->height
)+(frame
->width
/2*frame
->height
/2);
4549 frame
->buffer
= NULL
;
4550 frame
->srcStride
[0] = frame
->width
;
4551 frame
->srcSlideY
= frame
->width
;
4552 frame
->srcSlideH
= frame
->height
;
4554 frame
->AddState (MediaFramePlanar
);
4556 ReportDecodeFrameCompleted (frame
);
4564 NullDecoderInfo::Supports (const char *codec
)
4566 const char *video_fourccs
[] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL
};
4567 const char *audio_fourccs
[] = { "wmav1","wmav2", "wmav3", "mp3", NULL
};
4569 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4570 if (!strcmp (codec
, video_fourccs
[i
]))
4573 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4574 if (!strcmp (codec
, audio_fourccs
[i
]))
4585 NullDecoder::NullDecoder (Media
*media
, IMediaStream
*stream
) : IMediaDecoder (Type::NULLDECODER
, media
, stream
)
4589 prev_pts
= G_MAXUINT64
;
4593 NullDecoder::Dispose ()
4598 IMediaDecoder::Dispose ();
4602 NullDecoder::DecodeVideoFrame (MediaFrame
*frame
)
4604 // free encoded buffer and alloc a new one for our image
4605 g_free (frame
->buffer
);
4606 frame
->buflen
= logo_size
;
4607 frame
->buffer
= (guint8
*) g_malloc (frame
->buflen
);
4608 memcpy (frame
->buffer
, logo
, frame
->buflen
);
4609 frame
->AddState (MediaFrameDecoded
);
4611 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4613 return MEDIA_SUCCESS
;
4617 NullDecoder::DecodeAudioFrame (MediaFrame
*frame
)
4619 AudioStream
*as
= (AudioStream
*) GetStream ();
4624 // discard encoded data
4625 g_free (frame
->buffer
);
4627 // We have no idea here how long the encoded audio data is
4628 // for the first frame we use 0.1 seconds, for the rest
4629 // we calculate the time since the last frame
4631 if (prev_pts
== G_MAXUINT64
|| frame
->pts
<= prev_pts
) {
4632 samples
= as
->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4634 diff_pts
= frame
->pts
- prev_pts
;
4635 samples
= (float) as
->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT
/ (float) diff_pts
);
4637 prev_pts
= frame
->pts
;
4639 data_size
= samples
* as
->GetChannels () * 2 /* 16 bit audio */;
4641 frame
->buflen
= data_size
;
4642 frame
->buffer
= (guint8
*) g_malloc0 (frame
->buflen
);
4644 frame
->AddState (MediaFrameDecoded
);
4646 return MEDIA_SUCCESS
;
4650 NullDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4652 MediaResult result
= MEDIA_FAIL
;
4653 IMediaStream
*stream
= GetStream ();
4655 if (stream
->GetType () == MediaTypeAudio
) {
4656 result
= DecodeAudioFrame (frame
);
4657 } else if (stream
->GetType () == MediaTypeVideo
) {
4658 result
= DecodeVideoFrame (frame
);
4661 if (MEDIA_SUCCEEDED (result
)) {
4662 ReportDecodeFrameCompleted (frame
);
4664 ReportErrorOccurred (result
);
4669 NullDecoder::OpenDecoderAsyncInternal ()
4672 IMediaStream
*stream
= GetStream ();
4674 if (stream
->GetType () == MediaTypeAudio
)
4675 result
= OpenAudio ();
4676 else if (stream
->GetType () == MediaTypeVideo
)
4677 result
= OpenVideo ();
4679 result
= MEDIA_FAIL
;
4681 if (MEDIA_SUCCEEDED (result
)) {
4682 ReportOpenDecoderCompleted ();
4684 ReportErrorOccurred (result
);
4689 NullDecoder::OpenAudio ()
4691 return MEDIA_SUCCESS
;
4695 NullDecoder::OpenVideo ()
4697 VideoStream
*vs
= (VideoStream
*) GetStream ();
4698 guint32 dest_height
= vs
->height
;
4699 guint32 dest_width
= vs
->width
;
4702 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4703 extern const char moonlight_logo
[];
4704 const char *image
= moonlight_logo
;
4706 guint32 img_offset
= *((guint32
*)(image
+ 10));
4707 guint32 img_width
= *((guint32
*)(image
+ 18));
4708 guint32 img_height
= *((guint32
*)(image
+ 22));
4709 guint32 img_stride
= (img_width
* 3 + 3) & ~3; // in bytes
4710 guint32 img_i
, img_h
, img_w
;
4711 guint32 start_w
= (dest_width
-img_width
)/2;
4712 guint32 end_w
= start_w
+ img_width
;
4713 guint32 start_h
= (dest_height
-img_height
)/2;
4714 guint32 end_h
= start_h
+ img_height
;
4716 LOG_PIPELINE ("offset: %i, width: 0x%x = %i, height: 0x%x = %i, stride: %i\n", img_offset
, img_width
, img_width
, img_height
, img_height
, img_stride
);
4718 // create the buffer for our image
4719 logo_size
= dest_height
* dest_width
* 4;
4720 logo
= (guint8
*) g_malloc (logo_size
);
4721 memset (logo
, 0x00, logo_size
);
4723 // write our image centered into the destination rectangle, flipped horizontally
4725 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4726 for (guint32 dest_w
= 0; dest_w
< dest_width
; dest_w
++) {
4727 if (dest_w
>= start_w
&& dest_w
< end_w
&& dest_h
>= start_h
&& dest_h
< end_h
) {
4728 img_h
= (dest_h
- start_h
) % img_height
;
4729 img_w
= (dest_w
- start_w
) % img_width
;
4730 img_i
= img_h
* img_stride
+ img_w
* 3;
4732 logo
[logo_size
- dest_i
+ 0] = image
[img_offset
+ img_i
+ 0];
4733 logo
[logo_size
- dest_i
+ 1] = image
[img_offset
+ img_i
+ 1];
4734 logo
[logo_size
- dest_i
+ 2] = image
[img_offset
+ img_i
+ 2];
4736 logo
[logo_size
- dest_i
+ 3] = 0xff;
4742 // Flip the image vertically
4743 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4744 for (guint32 dest_w
= 0; dest_w
< dest_width
/ 2; dest_w
++) {
4746 guint32 a
= (dest_h
* dest_width
+ dest_w
) * 4;
4747 guint32 b
= (dest_h
* dest_width
+ dest_width
- dest_w
) * 4 - 4;
4748 for (guint32 c
= 0; c
< 3; c
++) {
4750 logo
[a
+ c
] = logo
[b
+ c
];
4756 SetPixelFormat (MoonPixelFormatRGB32
);
4758 return MEDIA_SUCCESS
;
4765 ExternalDemuxer::ExternalDemuxer (Media
*media
, void *instance
, CloseDemuxerCallback close_demuxer
,
4766 GetDiagnosticAsyncCallback get_diagnostic
, GetFrameAsyncCallback get_sample
, OpenDemuxerAsyncCallback open_demuxer
,
4767 SeekAsyncCallback seek
, SwitchMediaStreamAsyncCallback switch_media_stream
)
4768 : IMediaDemuxer (Type::EXTERNALDEMUXER
, media
)
4770 this->close_demuxer_callback
= close_demuxer
;
4771 this->get_diagnostic_async_callback
= get_diagnostic
;
4772 this->get_sample_async_callback
= get_sample
;
4773 this->open_demuxer_async_callback
= open_demuxer
;
4774 this->seek_async_callback
= seek
;
4775 this->switch_media_stream_async_callback
= switch_media_stream
;
4776 this->instance
= instance
;
4779 pthread_rwlock_init (&rwlock
, NULL
);
4781 g_return_if_fail (instance
!= NULL
);
4782 g_return_if_fail (close_demuxer
!= NULL
&& get_diagnostic
!= NULL
&& get_sample
!= NULL
&& open_demuxer
!= NULL
&& seek
!= NULL
&& switch_media_stream
!= NULL
);
4785 ExternalDemuxer::~ExternalDemuxer ()
4787 pthread_rwlock_destroy (&rwlock
);
4791 ExternalDemuxer::Dispose ()
4794 IMediaDemuxer::Dispose ();
4798 ExternalDemuxer::ClearCallbacks ()
4800 pthread_rwlock_wrlock (&rwlock
);
4801 close_demuxer_callback
= NULL
;
4802 get_diagnostic_async_callback
= NULL
;
4803 get_sample_async_callback
= NULL
;
4804 open_demuxer_async_callback
= NULL
;
4805 seek_async_callback
= NULL
;
4806 switch_media_stream_async_callback
= NULL
;
4808 pthread_rwlock_unlock (&rwlock
);
4812 ExternalDemuxer::SetCanSeek (bool value
)
4818 ExternalDemuxer::AddStream (IMediaStream
*stream
)
4820 return IMediaDemuxer::AddStream (stream
);
4824 ExternalDemuxer::CloseDemuxerInternal ()
4826 pthread_rwlock_rdlock (&rwlock
);
4827 if (close_demuxer_callback
!= NULL
) {
4828 close_demuxer_callback (instance
);
4831 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4834 pthread_rwlock_unlock (&rwlock
);
4838 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind
)
4840 pthread_rwlock_rdlock (&rwlock
);
4841 if (get_diagnostic_async_callback
!= NULL
) {
4842 get_diagnostic_async_callback (instance
, diagnosticsKind
);
4845 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4848 pthread_rwlock_unlock (&rwlock
);
4852 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
4854 g_return_if_fail (stream
!= NULL
);
4856 pthread_rwlock_rdlock (&rwlock
);
4857 if (get_sample_async_callback
!= NULL
) {
4858 get_sample_async_callback (instance
, stream
->GetStreamType ());
4861 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4864 pthread_rwlock_unlock (&rwlock
);
4868 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4870 pthread_rwlock_rdlock (&rwlock
);
4871 if (open_demuxer_async_callback
!= NULL
) {
4872 open_demuxer_async_callback (instance
, this);
4875 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4878 pthread_rwlock_unlock (&rwlock
);
4882 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime
)
4884 pthread_rwlock_rdlock (&rwlock
);
4885 if (seek_async_callback
!= NULL
) {
4886 seek_async_callback (instance
, seekToTime
);
4889 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4892 pthread_rwlock_unlock (&rwlock
);
4896 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*mediaStreamDescription
)
4898 g_return_if_fail (mediaStreamDescription
!= NULL
);
4900 pthread_rwlock_rdlock (&rwlock
);
4901 if (switch_media_stream_async_callback
!= NULL
) {
4902 switch_media_stream_async_callback (instance
, mediaStreamDescription
);
4905 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4908 pthread_rwlock_unlock (&rwlock
);
4916 AudioStream::AudioStream (Media
*media
)
4917 : IMediaStream (Type::AUDIOSTREAM
, media
)
4921 AudioStream::AudioStream (Media
*media
, int codec_id
, int bits_per_sample
, int block_align
, int sample_rate
, int channels
, int bit_rate
, gpointer extra_data
, guint32 extra_data_size
)
4922 : IMediaStream (Type::AUDIOSTREAM
, media
)
4924 this->codec_id
= codec_id
;
4925 this->codec
= CreateCodec (codec_id
);
4926 this->extra_data
= extra_data
;
4927 this->extra_data_size
= extra_data_size
;
4928 input_bits_per_sample
= bits_per_sample
;
4929 output_bits_per_sample
= bits_per_sample
;
4930 input_block_align
= block_align
;
4931 output_block_align
= block_align
;
4932 input_sample_rate
= sample_rate
;
4933 output_sample_rate
= sample_rate
;
4934 input_channels
= channels
;
4935 output_channels
= channels
;
4936 input_bit_rate
= bit_rate
;
4937 output_bit_rate
= bit_rate
;
4944 ExternalDecoder::ExternalDecoder (Media
*media
, IMediaStream
*stream
, void *instance
, const char *name
,
4945 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async
,
4946 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async
,
4947 ExternalDecoder_CleanupCallback cleanup
,
4948 ExternalDecoder_CleanStateCallback clean_state
,
4949 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame
,
4950 ExternalDecoder_DisposeCallback dispose
,
4951 ExternalDecoder_DtorCallback dtor
)
4952 : IMediaDecoder (Type::EXTERNALDECODER
, media
, stream
)
4954 this->instance
= instance
;
4955 this->name
= g_strdup (name
);
4956 this->decode_frame_async
= decode_frame_async
;
4957 this->open_decoder_async
= open_decoder_async
;
4958 this->cleanup
= cleanup
;
4959 this->clean_state
= clean_state
;
4960 this->has_delayed_frame
= has_delayed_frame
;
4961 this->dispose
= dispose
;
4965 ExternalDecoder::~ExternalDecoder ()
4972 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4974 decode_frame_async (instance
, frame
);
4978 ExternalDecoder::OpenDecoderAsyncInternal ()
4980 open_decoder_async (instance
);
4984 ExternalDecoder::Dispose ()
4988 IMediaDecoder::Dispose ();
4992 ExternalDecoder::Cleanup (MediaFrame
*frame
)
4994 cleanup (instance
, frame
);
4998 ExternalDecoder::CleanState ()
5000 clean_state (instance
);
5004 ExternalDecoder::HasDelayedFrame ()
5006 return has_delayed_frame (instance
);
5010 ExternalDecoder::InputEnded ()
5012 GetStream ()->SetOutputEnded (true);
5016 * ExternalDecoderInfo
5019 ExternalDecoderInfo::ExternalDecoderInfo (void *instance
, const char *name
, ExternalDecoderInfo_SupportsCallback supports
, ExternalDecoderInfo_Create create
, ExternalDecoderInfo_dtor dtor
)
5021 this->instance
= instance
;
5022 this->supports
= supports
;
5023 this->create
= create
;
5025 this->name
= g_strdup (name
);
5029 ExternalDecoderInfo::Supports (const char *codec
)
5031 return supports (instance
, codec
);
5035 ExternalDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
5037 return create (instance
, media
, stream
);
5040 ExternalDecoderInfo::~ExternalDecoderInfo ()