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.
139 this->demuxer
= NULL
;
149 IMediaObject::Dispose ();
151 GetDeployment ()->UnregisterMedia (this);
155 Media::IsMSCodecsInstalled ()
157 return registered_ms_codecs
;
161 Media::RegisterMSCodecs (void)
165 char *libmscodecs_path
= NULL
;
166 const char *functions
[] = {"register_codec_pack", NULL
};
167 const gchar
*home
= g_get_home_dir ();
168 registering_ms_codecs
= true;
170 if (!(moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
)) {
171 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
176 libmscodecs_path
= g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME
, NULL
);
178 if (!(g_file_test (libmscodecs_path
, G_FILE_TEST_EXISTS
) && g_file_test (libmscodecs_path
, G_FILE_TEST_IS_REGULAR
))) {
179 if (libmscodecs_path
)
180 g_free (libmscodecs_path
);
181 libmscodecs_path
= g_strdup (CODEC_LIBRARY_NAME
);
184 dl
= dlopen (libmscodecs_path
, RTLD_LAZY
);
186 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path
);
188 int pre_decoders
= 0;
189 int post_decoders
= 0;
191 /* Count the number of current decoders */
193 current
= registered_decoders
;
194 while (current
!= NULL
) {
196 current
= current
->next
;
199 for (int i
= 0; functions
[i
] != NULL
; i
++) {
200 reg
= (register_codec
) dlsym (dl
, functions
[i
]);
202 (*reg
) (MOONLIGHT_CODEC_ABI_VERSION
);
204 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions
[i
], libmscodecs_path
);
208 /* Count the number of decoders after registering the ms codecs */
209 current
= registered_decoders
;
210 while (current
!= NULL
) {
212 current
= current
->next
;
215 /* We could only load the codecs if the codec pack actually registered any decoders
216 * This ensures that if the user has invalid codecs for whatever reason, we request
218 registered_ms_codecs
= post_decoders
> pre_decoders
;
220 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path
, dlerror ());
222 g_free (libmscodecs_path
);
224 registering_ms_codecs
= false;
228 Media::SetBufferingEnabled (bool value
)
230 buffering_enabled
= value
;
235 Media::SetBufferingTime (guint64 buffering_time
)
238 this->buffering_time
= buffering_time
;
243 Media::GetBufferingTime ()
247 result
= buffering_time
;
253 Media::GetPlaylistRoot ()
259 Media::GetDemuxerReffed ()
261 IMediaDemuxer
*result
;
263 result
= this->demuxer
;
274 markers
= new List ();
280 Media::RegisterDemuxer (DemuxerInfo
*info
)
282 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
284 if (registered_demuxers
== NULL
) {
285 registered_demuxers
= info
;
287 MediaInfo
* current
= registered_demuxers
;
288 while (current
->next
!= NULL
)
289 current
= current
->next
;
290 current
->next
= info
;
295 Media::RegisterConverter (ConverterInfo
*info
)
297 //printf ("Media::RegisterConverter (%p)\n", info);
299 if (registered_converters
== NULL
) {
300 registered_converters
= info
;
302 MediaInfo
*current
= registered_converters
;
303 while (current
->next
!= NULL
)
304 current
= current
->next
;
305 current
->next
= info
;
310 Media::RegisterDecoder (DecoderInfo
*info
)
314 //printf ("Media::RegisterDecoder (%p)\n", info);
316 if (registered_decoders
== NULL
) {
317 registered_decoders
= info
;
319 if (registering_ms_codecs
) {
320 // MS codecs might get registered after all other codecs (right after installing them),
321 // which means after the null codecs so if they don't get special treatment, they won't
322 // get used until the next browser restart (when they're registered normally).
323 // So instead of appending them, we prepend them.
324 info
->next
= registered_decoders
;
325 registered_decoders
= info
;
327 current
= registered_decoders
;
328 while (current
->next
!= NULL
)
329 current
= current
->next
;
330 current
->next
= info
;
333 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info
->GetName ());
339 LOG_PIPELINE ("Media::Initialize ()\n");
342 Media::RegisterDemuxer (new ASFDemuxerInfo ());
343 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
344 Media::RegisterDemuxer (new ASXDemuxerInfo ());
347 if (!(moonlight_flags
& RUNTIME_INIT_FFMPEG_YUV_CONVERTER
))
348 Media::RegisterConverter (new YUVConverterInfo ());
351 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
352 if (moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
) {
355 #ifdef INCLUDE_FFMPEG
356 if (!(moonlight_flags
& RUNTIME_INIT_DISABLE_FFMPEG_CODECS
)) {
361 Media::RegisterDecoder (new PassThroughDecoderInfo ());
362 Media::RegisterDecoder (new NullDecoderInfo ());
364 MediaThreadPool::Initialize ();
370 LOG_PIPELINE ("Media::Shutdown ()\n");
375 // Make sure all threads are stopped
376 AudioPlayer::Shutdown ();
377 MediaThreadPool::Shutdown ();
379 current
= registered_decoders
;
380 while (current
!= NULL
) {
381 next
= current
->next
;
385 registered_decoders
= NULL
;
387 current
= registered_demuxers
;
388 while (current
!= NULL
) {
389 next
= current
->next
;
393 registered_demuxers
= NULL
;
395 current
= registered_converters
;
396 while (current
!= NULL
) {
397 next
= current
->next
;
401 registered_converters
= NULL
;
403 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
407 Media::Warning (MediaResult result
, const char *format
, ...)
411 if (MEDIA_SUCCEEDED (result
))
414 fprintf (stderr
, "Moonlight: MediaResult = %d; ", result
);
416 va_start (args
, format
);
417 vfprintf (stderr
, format
, args
);
420 fputc ('\n', stderr
);
424 Media::InMediaThread ()
426 return MediaThreadPool::IsThreadPoolThread ();
430 Media::ReportBufferingProgress (double progress
)
432 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress
, buffering_progress
);
434 progress
= MAX (MIN (progress
, 1.0), 0.0);
436 if (progress
== buffering_progress
)
439 if (progress
< buffering_progress
|| progress
> (buffering_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
440 buffering_progress
= progress
;
441 EmitSafe (BufferingProgressChangedEvent
, new ProgressEventArgs (progress
));
446 Media::ReportDownloadProgress (double progress
)
448 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress
, download_progress
);
450 progress
= MAX (MIN (progress
, 1.0), 0.0);
452 if (progress
<= download_progress
) {
454 * Download progress percentage can actually go down - if the file size
455 * goes up. Yes, the file size can go up.
460 if (progress
> (download_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
461 download_progress
= progress
;
462 EmitSafe (DownloadProgressChangedEvent
, new ProgressEventArgs (progress
));
467 Media::SeekAsync (guint64 pts
)
469 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
471 if (demuxer
== NULL
) {
472 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
476 demuxer
->SeekAsync (pts
);
480 Media::ReportSeekCompleted (guint64 pts
)
482 LOG_PIPELINE ("Media::ReportSeekCompleted (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
484 buffering_progress
= 0;
486 EmitSafe (SeekCompletedEvent
);
490 Media::ReportOpenCompleted ()
492 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
494 EmitSafe (OpenCompletedEvent
);
498 Media::ReportOpenDemuxerCompleted ()
500 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
506 Media::ReportOpenDecoderCompleted (IMediaDecoder
*decoder
)
508 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder
, GET_OBJ_ID (this));
510 g_return_if_fail (decoder
!= NULL
);
516 Media::ReportErrorOccurred (ErrorEventArgs
*args
)
518 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args
, args
== NULL
? NULL
: args
->GetErrorMessage());
521 fprintf (stderr
, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args
->GetErrorType()), args
->GetErrorCode(), args
->GetErrorMessage(), args
->GetExtendedMessage());
523 fprintf (stderr
, "Moonlight: Unspecified media error.\n");
526 if (!error_reported
) {
527 error_reported
= true;
528 EmitSafe (MediaErrorEvent
, args
);
533 Media::ReportErrorOccurred (const char *message
)
535 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message
);
537 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 3001, message
)));
541 Media::ReportErrorOccurred (MediaResult result
)
543 char *msg
= g_strdup_printf ("Media error: %i.", result
);
544 ReportErrorOccurred (msg
);
551 LOG_PIPELINE ("Media::PlayAsync ()\n");
553 MediaClosure
*closure
= new MediaClosure (this, PlayCallback
, this, "Media::PlayAsync");
554 EnqueueWork (closure
);
561 LOG_PIPELINE ("Media::PauseAsync ()\n");
567 LOG_PIPELINE ("Media::StopAsync ()\n");
569 MediaClosure
*closure
= new MediaClosure (this, StopCallback
, this, "Media::StopAsync");
570 EnqueueWork (closure
);
575 Media::StopCallback (MediaClosure
*closure
)
577 closure
->GetMedia ()->Stop ();
578 return MEDIA_SUCCESS
;
582 Media::PlayCallback (MediaClosure
*closure
)
584 closure
->GetMedia ()->Play ();
585 return MEDIA_SUCCESS
;
591 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
593 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
597 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
601 demuxer
->ClearBuffers ();
607 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
609 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
613 demuxer
->FillBuffers ();
617 Media::Initialize (Downloader
*downloader
, const char *PartName
)
619 IMediaSource
*source
;
621 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader
, PartName
, GET_OBJ_ID (this));
623 g_return_if_fail (downloader
!= NULL
);
624 g_return_if_fail (file
== NULL
);
625 g_return_if_fail (uri
!= NULL
|| PartName
!= NULL
);
626 g_return_if_fail (initialized
== false);
627 g_return_if_fail (error_reported
== false);
628 g_return_if_fail (this->source
== NULL
);
630 if (downloader
->Completed ()) {
631 file
= downloader
->GetDownloadedFilename (PartName
);
634 ReportErrorOccurred ("Couldn't get downloaded filename.");
639 if (file
== NULL
&& PartName
!= NULL
&& PartName
[0] != 0) {
640 ReportErrorOccurred ("We don't support using media in zip files which haven't been downloaded yet (i.e. calling MediaElement.SetSource (dl, 'foo') with a dl which hasn't downloaded the file yet)");
645 InternalDownloader
*idl
= downloader
->GetInternalDownloader ();
646 MmsDownloader
*mms_dl
= (idl
&& idl
->GetObjectType () == Type::MMSDOWNLOADER
) ? (MmsDownloader
*) idl
: NULL
;
648 if (mms_dl
== NULL
) {
649 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
653 source
= new MmsSource (this, downloader
);
655 source
= new FileSource (this, file
);
663 Media::Initialize (IMediaSource
*source
)
667 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source
, GET_OBJ_ID (this));
669 g_return_if_fail (source
!= NULL
);
670 g_return_if_fail (this->source
== NULL
);
671 g_return_if_fail (initialized
== false);
673 result
= source
->Initialize ();
674 if (!MEDIA_SUCCEEDED (result
)) {
675 ReportErrorOccurred (result
);
680 this->source
= source
;
681 this->source
->ref ();
685 Media::Initialize (const char *uri
)
688 IMediaSource
*source
= NULL
;
690 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri
, GET_OBJ_ID (this));
692 g_return_if_fail (uri
!= NULL
);
693 g_return_if_fail (file
== NULL
);
694 g_return_if_fail (uri
!= NULL
);
695 g_return_if_fail (initialized
== false);
696 g_return_if_fail (error_reported
== false);
697 g_return_if_fail (source
== NULL
);
698 g_return_if_fail (this->source
== NULL
);
700 this->uri
= g_strdup (uri
);
703 if (g_str_has_prefix (uri
, "mms://") || g_str_has_prefix (uri
, "rtsp://") || g_str_has_prefix (uri
, "rtsps://")) {
704 dl
= Surface::CreateDownloader (this);
706 ReportErrorOccurred ("Couldn't create downloader.");
710 dl
->Open ("GET", uri
, StreamingPolicy
);
712 if (dl
->GetFailedMessage () == NULL
) {
713 Initialize (dl
, NULL
);
715 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
716 MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
724 source
= new ProgressiveSource (this, uri
);
730 Media::Initialize (IMediaDemuxer
*demuxer
)
732 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer
, GET_OBJ_ID (this));
734 g_return_if_fail (demuxer
!= NULL
);
735 g_return_if_fail (this->demuxer
== NULL
);
736 g_return_if_fail (initialized
== false);
738 this->demuxer
= demuxer
;
739 this->demuxer
->ref ();
746 Media::RetryHttp (ErrorEventArgs
*args
)
748 char *http_uri
= NULL
;
750 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri
);
752 g_return_if_fail (uri
!= NULL
);
753 g_return_if_fail (source
!= NULL
);
756 ReportErrorOccurred (args
);
760 // CHECK: If the current protocolo is rtsps, should we retry http or https?
762 if (g_str_has_prefix (uri
, "mms://")) {
763 http_uri
= g_strdup_printf ("http://%s", uri
+ 6);
764 } else if (g_str_has_prefix (uri
, "rtsp://")) {
765 http_uri
= g_strdup_printf ("http://%s", uri
+ 7);
766 } else if (g_str_has_prefix (uri
, "rtsps://")) {
767 http_uri
= g_strdup_printf ("http://%s", uri
+ 8);
769 ReportErrorOccurred (args
);
775 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri
);
779 /* this method is called on the main thread, ensure Dispose is called on the source on the media thread */
780 DisposeObject (source
);
784 error_reported
= false;
786 Initialize (http_uri
);
797 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
799 g_return_if_fail (initialized
== true);
801 EmitSafe (OpeningEvent
);
803 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenAsync");
804 EnqueueWork (closure
);
809 Media::OpenInternal ()
811 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
813 g_return_if_fail (initialized
== true);
816 // This may happen due to the recursion detection below
817 // Example: we try open a demuxer, the demuxer opens successfully
818 // right away and calls ReportDemuxerOpenComplete which will call
819 // us. Due to the recursion detection we'll enqueue a call to
820 // OpenInternal, while the first OpenInternal may succeed and
821 // set opened to true.
822 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
826 // detect recursive calls.
828 if (in_open_internal
) {
829 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
830 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
831 EnqueueWork (closure
);
836 in_open_internal
= true;
841 if (!SelectDemuxerAsync ()) {
842 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
849 if (!SelectDecodersAsync ()) {
850 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
857 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
859 EmitSafe (OpenCompletedEvent
);
862 in_open_internal
= false;
866 Media::OpenInternal (MediaClosure
*closure
)
868 Media
*media
= (Media
*) closure
->GetContext ();
870 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
872 media
->OpenInternal ();
874 return MEDIA_SUCCESS
;
878 Media::SelectDemuxerAsync ()
880 DemuxerInfo
*demuxerInfo
;
885 LOG_PIPELINE ("Media::SelectDemuxer () id: %i, demuxer: %p, IsOpened: %i, IsOpening: %i\n", GET_OBJ_ID (this), demuxer
, demuxer
? demuxer
->IsOpened () : -1, demuxer
? demuxer
->IsOpening () : -1);
887 g_return_val_if_fail (error_reported
== false, false);
888 g_return_val_if_fail (initialized
== true, false);
890 // Check if demuxer already is open
891 if (demuxer
!= NULL
) {
892 if (demuxer
->IsOpened ())
894 if (!demuxer
->IsOpening ())
895 demuxer
->OpenDemuxerAsync ();
896 return demuxer
->IsOpened ();
899 g_return_val_if_fail (source
!= NULL
, false);
901 // Check if the source knows how to create the demuxer
902 demuxer
= source
->CreateDemuxer (this);
904 if (demuxer
== NULL
) { // No demuxer created, we need to find it ourselves.
905 if (source
->CanSeek () && source
->GetPosition () > 0) {
906 if (!source
->Seek (0, SEEK_SET
)) {
907 LOG_PIPELINE ("Media::SelectDemuxer (): could not seek to position 0 of the input stream. Will try to continue anyway.\n");
910 // Check if we have at least 1024 bytes or eof
911 if (!source
->IsPositionAvailable (16, &eof
)) {
913 // We need to try again later.
914 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
916 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
917 EnqueueWork (closure
, false);
925 demuxerInfo
= registered_demuxers
;
926 while (demuxer
== NULL
&& demuxerInfo
!= NULL
) {
927 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo
->GetName ());
928 support
= demuxerInfo
->Supports (source
);
930 if (support
== MEDIA_SUCCESS
)
935 if (result
== MEDIA_NOT_ENOUGH_DATA
) {
936 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't determine whether it can handle the media or not due to not enough data being available yet.\n", demuxerInfo
->GetName ());
938 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
939 EnqueueWork (closure
, false);
945 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo
->GetName ());
946 demuxerInfo
= (DemuxerInfo
*) demuxerInfo
->next
;
949 if (demuxerInfo
== NULL
) {
950 // No demuxer found, report an error
951 const char *source_name
= file
? file
: uri
;
954 switch (source
->GetType ()) {
955 case MediaSourceTypeProgressive
:
956 case MediaSourceTypeFile
:
957 source_name
= ((FileSource
*) source
)->GetFileName ();
959 case MediaSourceTypeMms
:
960 case MediaSourceTypeMmsEntry
:
961 source_name
= "live source";
964 source_name
= "unknown source";
968 char *msg
= g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name
);
969 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
970 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT"),
971 MEDIA_UNKNOWN_CODEC
, msg
));
977 demuxer
= demuxerInfo
->Create (this, source
);
979 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer
->GetTypeName ());
982 if (demuxer
->IsOpened ())
985 if (demuxer
->IsOpening ())
988 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
990 demuxer
->OpenDemuxerAsync ();
992 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
994 return demuxer
!= NULL
&& demuxer
->IsOpened ();
998 Media::SelectDecodersAsync ()
1000 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
1002 g_return_val_if_fail (error_reported
== false, false);
1003 g_return_val_if_fail (initialized
== true, false);
1005 if (demuxer
== NULL
) {
1006 ReportErrorOccurred ("No demuxer to select decoders from.");
1010 // If the demuxer has no streams (ASXDemuxer for instance)
1011 // then just return success.
1012 if (demuxer
->GetStreamCount () == 0)
1015 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
1017 // Select codecs for each stream
1018 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1019 IMediaStream
*stream
= demuxer
->GetStream (i
);
1022 if (stream
== NULL
) {
1023 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
1027 if (stream
->GetDecoder () != NULL
)
1030 const char *codec
= stream
->GetCodec ();
1031 IMediaDecoder
*decoder
= NULL
;
1033 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec
);
1035 DecoderInfo
*current_decoder
= registered_decoders
;
1036 while (current_decoder
!= NULL
&& !current_decoder
->Supports (codec
)) {
1037 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder
->GetName (), codec
);
1038 current_decoder
= (DecoderInfo
*) current_decoder
->next
;
1041 if (current_decoder
== NULL
) {
1042 Media::Warning (MEDIA_UNKNOWN_CODEC
, "Unknown codec: '%s'.", codec
);
1046 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder
->GetName (), codec
);
1047 decoder
= current_decoder
->Create (this, stream
);
1049 stream
->SetDecoder (decoder
);
1057 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1059 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1060 IMediaStream
*stream
= demuxer
->GetStream (i
);
1061 IMediaDecoder
*decoder
;
1066 decoder
= stream
->GetDecoder ();
1068 if (decoder
== NULL
) {
1069 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
1070 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT")));
1074 if (decoder
->IsOpening () || decoder
->IsOpened ())
1077 decoder
->OpenDecoderAsync ();
1083 // Wait until all the codecs have opened
1084 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1086 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1087 IMediaStream
*stream
= demuxer
->GetStream (i
);
1088 IMediaDecoder
*decoder
;
1093 decoder
= stream
->GetDecoder ();
1095 if (decoder
== NULL
) {
1096 ReportErrorOccurred (MEDIA_FAIL
);
1100 if (decoder
->IsOpening ()) {
1101 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
1102 EnqueueWork (closure
, false);
1107 if (!decoder
->IsOpened ()) {
1108 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1109 ReportErrorOccurred (MEDIA_FAIL
);
1115 // All the codecs have been opened now.
1116 // Find converters for each of them (whenever required).
1118 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1120 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1121 IMediaStream
*stream
= demuxer
->GetStream (i
);
1122 IMediaDecoder
*decoder
;
1127 decoder
= stream
->GetDecoder ();
1129 if (decoder
== NULL
) {
1130 ReportErrorOccurred (MEDIA_FAIL
);
1134 if (stream
->GetType () != MediaTypeVideo
)
1135 continue; // Only video streams need converters
1137 if (decoder
->GetPixelFormat () == MoonPixelFormatRGB32
|| decoder
->GetPixelFormat () == MoonPixelFormatRGBA32
)
1138 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1140 // Select converter for this stream
1141 VideoStream
*vs
= (VideoStream
*) stream
;
1143 ConverterInfo
* current_conv
= registered_converters
;
1144 while (current_conv
!= NULL
&& !current_conv
->Supports (decoder
->GetPixelFormat (), MoonPixelFormatRGB32
)) {
1145 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1146 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1147 current_conv
= (ConverterInfo
*) current_conv
->next
;
1151 if (current_conv
== NULL
) {
1152 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER
);
1153 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1154 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1158 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1159 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1161 vs
->converter
= current_conv
->Create (this, vs
);
1162 vs
->converter
->input_format
= decoder
->GetPixelFormat ();
1163 vs
->converter
->output_format
= MoonPixelFormatRGB32
;
1164 if (!MEDIA_SUCCEEDED (vs
->converter
->Open ())) {
1165 vs
->converter
->unref ();
1166 vs
->converter
= NULL
;
1167 ReportErrorOccurred (MEDIA_FAIL
);
1172 // Loop through all the streams, return true if at least one has a codec.
1174 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1175 IMediaStream
*stream
= demuxer
->GetStream (i
);
1180 if (stream
->GetDecoder () != NULL
)
1184 // No codecs found for no stream, report an error.
1185 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1190 Media::EnqueueWork (MediaClosure
*closure
, bool wakeup
)
1192 bool result
= false;
1195 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure
);
1197 g_return_val_if_fail (closure
!= NULL
, false);
1203 disposed
= this->is_disposed
;
1206 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed
);
1208 MediaThreadPool::AddWork (closure
, wakeup
);
1217 Media::DisposeObjectInternal (MediaClosure
*closure
)
1219 closure
->GetContext ()->Dispose ();
1220 return MEDIA_SUCCESS
;
1224 Media::DisposeObject (EventObject
*obj
)
1226 MediaDisposeObjectClosure
*closure
= new MediaDisposeObjectClosure (this, DisposeObjectInternal
, obj
);
1227 if (!EnqueueWork (closure
, true)) {
1228 LOG_PIPELINE ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj
);
1237 MediaThreadPool::WakeUp ();
1241 Media::ClearQueue ()
1243 LOG_PIPELINE ("Media::ClearQueue ().\n");
1244 MediaThreadPool::RemoveWork (this);
1251 ASXDemuxer::ASXDemuxer (Media
*media
, IMediaSource
*source
)
1252 : IMediaDemuxer (Type::ASXDEMUXER
, media
, source
)
1257 ASXDemuxer::~ASXDemuxer ()
1262 ASXDemuxer::Dispose ()
1268 IMediaDemuxer::Dispose ();
1272 ASXDemuxer::OpenDemuxerAsyncInternal ()
1276 ErrorEventArgs
*args
= NULL
;
1277 Media
*media
= GetMediaReffed ();
1279 g_return_if_fail (media
!= NULL
);
1281 root
= media
->GetPlaylistRoot ();
1283 g_return_if_fail (root
!= NULL
);
1285 PlaylistParser
*parser
= new PlaylistParser (root
, source
);
1287 if (MEDIA_SUCCEEDED (parser
->Parse ())) {
1288 result
= MEDIA_SUCCESS
;
1289 playlist
= parser
->GetPlaylist ();
1292 result
= MEDIA_FAIL
;
1293 args
= parser
->GetErrorEventArgs ();
1300 if (MEDIA_SUCCEEDED (result
)) {
1301 ReportOpenDemuxerCompleted ();
1302 } else if (result
== MEDIA_NOT_ENOUGH_DATA
) {
1304 } else if (args
!= NULL
) {
1305 args
->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1306 ReportErrorOccurred (args
);
1308 ReportErrorOccurred (result
);
1321 ASXDemuxerInfo::Supports (IMediaSource
*source
)
1323 if (PlaylistParser::IsASX2 (source
) || PlaylistParser::IsASX3 (source
)) {
1324 return MEDIA_SUCCESS
;
1331 ASXDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
1333 return new ASXDemuxer (media
, source
);
1337 * ManagedStreamSource
1340 ManagedStreamSource::ManagedStreamSource (Media
*media
, ManagedStreamCallbacks
*stream
) : IMediaSource (Type::MANAGEDSTREAMSOURCE
, media
)
1342 memcpy (&this->stream
, stream
, sizeof (this->stream
));
1345 ManagedStreamSource::~ManagedStreamSource ()
1347 stream
.handle
= NULL
;
1351 ManagedStreamSource::ReadInternal (void *buf
, guint32 n
)
1353 return stream
.Read (stream
.handle
, buf
, 0, n
);
1357 ManagedStreamSource::PeekInternal (void *buf
, guint32 n
)
1361 read
= stream
.Read (stream
.handle
, buf
, 0, n
);
1362 stream
.Seek (stream
.handle
, -read
, 1 /* SeekOrigin.Current */);
1367 ManagedStreamSource::SeekInternal (gint64 offset
, int mode
)
1369 stream
.Seek (stream
.handle
, offset
, mode
/* FIXME: check if mode values matches SeekOrigin values */);
1374 ManagedStreamSource::GetPositionInternal ()
1376 return stream
.Position (stream
.handle
);
1380 ManagedStreamSource::GetSizeInternal ()
1382 return stream
.Length (stream
.handle
);
1389 FileSource::FileSource (Media
*media
, const char *filename
) : IMediaSource (Type::FILESOURCE
, media
)
1391 this->filename
= g_strdup (filename
);
1397 FileSource::FileSource (Type::Kind object_type
, Media
*media
, bool temp_file
) : IMediaSource (object_type
, media
)
1402 this->temp_file
= temp_file
;
1405 FileSource::~FileSource ()
1410 FileSource::Dispose ()
1418 IMediaSource::Dispose ();
1422 FileSource::Initialize ()
1426 LOG_PIPELINE ("FileSource::Initialize ()\n");
1429 return MEDIA_SUCCESS
;
1432 if (filename
!= NULL
)
1433 return MEDIA_FILE_ERROR
;
1435 filename
= g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL
);
1437 if ((tmp_fd
= g_mkstemp (filename
)) == -1) {
1444 fd
= fdopen (tmp_fd
, "r");
1446 setvbuf (fd
, buffer
, _IOFBF
, sizeof (buffer
));
1448 if (filename
== NULL
)
1449 return MEDIA_FILE_ERROR
;
1451 fd
= g_fopen (filename
, "r");
1455 return MEDIA_FILE_ERROR
;
1459 return MEDIA_SUCCESS
;
1463 FileSource::Open (const char *filename
)
1465 g_return_val_if_fail (filename
!= NULL
, MEDIA_FAIL
);
1467 g_free (this->filename
);
1468 this->filename
= g_strdup (filename
);
1475 fd
= fopen (filename
, "r");
1482 return MEDIA_SUCCESS
;
1486 FileSource::UpdateSize ()
1490 g_return_if_fail (fd
!= NULL
);
1492 if (fstat (fileno (fd
), &st
) != -1) {
1500 FileSource::GetSizeInternal ()
1506 FileSource::GetPositionInternal ()
1513 result
= ftell (fd
);
1515 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %" G_GINT64_FORMAT
"\n", result
);
1521 FileSource::SeekInternal (gint64 offset
, int mode
)
1528 LOG_PIPELINE ("FileSource::SeekInternal (%" G_GINT64_FORMAT
", %i)\n", offset
, mode
);
1531 n
= fseek (fd
, offset
, mode
);
1537 FileSource::ReadInternal (void *buf
, guint32 n
)
1543 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf
, n
);
1548 nread
= fread (buf
, 1, n
, fd
);
1550 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n
, (int) nread
);
1556 FileSource::PeekInternal (void *buf
, guint32 n
)
1560 result
= ReadSome (buf
, n
);
1562 Seek (-result
, SEEK_CUR
);
1564 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %" G_GINT64_FORMAT
" [Done]\n", GET_OBJ_ID (this), buf
, n
, GetPosition ());
1582 ProgressiveSource::ProgressiveSource (Media
*media
, const char *uri
) : FileSource (Type::PROGRESSIVESOURCE
, media
, true)
1588 this->uri
= g_strdup (uri
);
1591 ProgressiveSource::~ProgressiveSource ()
1597 ProgressiveSource::Dispose ()
1603 if (Surface::InMainThread ()) {
1604 delete_cancellable (this);
1606 // we have to cancel/delete he cancellable on the main thread
1607 // it may end up doing a lot of stuff, including calling into
1610 // The tick call will ref us until the callback has been called.
1611 // Note that it may cause a warning to be printed
1612 // in ref () (reffing an object with a refcount of 0).
1613 // TODO: find a way to avoid the warning in this case, imho this is
1614 // a valid case of reffing an object with a refcount of 0.
1615 AddTickCall (delete_cancellable
);
1619 FileSource::Dispose ();
1623 ProgressiveSource::delete_cancellable (EventObject
*data
)
1625 ProgressiveSource
*src
= (ProgressiveSource
*) data
;
1626 if (src
->cancellable
) {
1627 src
->cancellable
->Cancel ();
1628 delete src
->cancellable
;
1629 src
->cancellable
= NULL
;
1634 ProgressiveSource::Initialize ()
1636 MediaResult result
= MEDIA_SUCCESS
;
1637 Application
*application
;
1639 application
= GetDeployment ()->GetCurrentApplication ();
1641 g_return_val_if_fail (application
!= NULL
, MEDIA_FAIL
);
1642 g_return_val_if_fail (filename
== NULL
, MEDIA_FAIL
);
1643 g_return_val_if_fail (cancellable
== NULL
, MEDIA_FAIL
);
1645 result
= FileSource::Initialize ();
1647 if (!MEDIA_SUCCEEDED (result
))
1650 write_fd
= g_fopen (filename
, "w");
1651 if (write_fd
== NULL
) {
1652 char *msg
= g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename
);
1653 ReportErrorOccurred (msg
);
1658 // unlink the file right away so that it'll be deleted even if we crash.
1659 if (moonlight_flags
& RUNTIME_INIT_KEEP_MEDIA
) {
1660 printf ("Moonlight: The media file %s will not deleted.\n", filename
);
1662 g_unlink (filename
);
1665 cancellable
= new Cancellable ();
1666 Uri
*u
= new Uri ();
1667 if (u
->Parse (uri
)) {
1668 if (!application
->GetResource (NULL
, u
, notify_func
, data_write
, MediaPolicy
, cancellable
, (gpointer
) this)) {
1669 result
= MEDIA_FAIL
;
1670 char *msg
= g_strdup_printf ("invalid path found in uri '%s'", uri
);
1671 ReportErrorOccurred (msg
);
1675 result
= MEDIA_FAIL
;
1676 char *msg
= g_strdup_printf ("Could not parse the uri '%s'", uri
);
1677 ReportErrorOccurred (msg
);
1686 ProgressiveSource::notify_func (NotifyType type
, gint64 args
, void *closure
)
1688 g_return_if_fail (closure
!= NULL
);
1689 ((ProgressiveSource
*) closure
)->Notify (type
, args
);
1693 ProgressiveSource::Notify (NotifyType type
, gint64 args
)
1695 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT
")\n",
1697 type
== ::NotifySize
? "NotifySize" :
1698 (type
== NotifyCompleted
? "NotifyCompleted" :
1699 (type
== NotifyFailed
? "NotifyFailed" :
1700 (type
== NotifyStarted
? "NotifyStarted" :
1701 (type
== NotifyProgressChanged
? "NotifyProgressChanged" : "unknown")))),
1708 case NotifyCompleted
:
1709 DownloadComplete ();
1715 case NotifyProgressChanged
:
1722 ProgressiveSource::data_write (void *data
, gint32 offset
, gint32 n
, void *closure
)
1724 g_return_if_fail (closure
!= NULL
);
1725 ((ProgressiveSource
*) closure
)->DataWrite (data
, offset
, n
);
1729 ProgressiveSource::DataWrite (void *buf
, gint32 offset
, gint32 n
)
1732 Media
*media
= NULL
;
1734 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf
, offset
, n
, media
, filename
);
1739 g_return_if_fail (write_fd
!= NULL
);
1741 media
= GetMediaReffed ();
1744 // We've got the entire file, update the size
1745 size
= write_pos
; // Since this method is the only method that writes to write_pos, and we're not reentrant, there is no need to lock here.
1747 // Close our write handle, we won't write more now
1753 nwritten
= fwrite (buf
, 1, n
, write_fd
);
1757 write_pos
+= nwritten
;
1763 media
->ReportDownloadProgress ((double) (offset
+ n
) / (double) size
);
1769 ProgressiveSource::NotifySize (gint64 size
)
1771 LOG_PIPELINE ("ProgressiveSource::NotifySize (%" G_GINT64_FORMAT
")\n", size
);
1779 ProgressiveSource::DownloadComplete ()
1781 MediaResult result
= MEDIA_SUCCESS
;
1782 Media
*media
= GetMediaReffed ();
1784 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1787 if (write_pos
!= size
&& size
!= -1) { // what happend here?
1788 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT
") != the reported size (%" G_GINT64_FORMAT
")\n", write_pos
, size
);
1791 this->size
= write_pos
;
1793 // Close our write handle, we won't write more now
1798 if (!MEDIA_SUCCEEDED (result
))
1799 ReportErrorOccurred (result
);
1802 media
->ReportDownloadProgress (1.0);
1809 ProgressiveSource::DownloadFailed ()
1811 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1813 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
1817 ProgressiveSource::CloseWriteFile ()
1819 if (write_fd
== NULL
)
1830 MemorySource::MemorySource (Media
*media
, void *memory
, gint32 size
, gint64 start
, bool owner
)
1831 : IMediaSource (Type::MEMORYSOURCE
, media
)
1833 this->memory
= memory
;
1835 this->start
= start
;
1837 this->owner
= owner
;
1840 MemorySource::~MemorySource ()
1847 MemorySource::SeekInternal (gint64 offset
, int mode
)
1853 real_offset
= offset
- start
;
1854 if (real_offset
< 0 || real_offset
>= size
)
1859 if (pos
+ offset
> size
|| pos
+ offset
< 0)
1864 if (size
- offset
> size
|| size
- offset
< 0)
1866 pos
= size
- offset
;
1875 MemorySource::ReadInternal (void *buffer
, guint32 n
)
1877 guint32 k
= MIN (n
, size
- pos
);
1878 memcpy (buffer
, ((char*) memory
) + pos
, k
);
1884 MemorySource::PeekInternal (void *buffer
, guint32 n
)
1886 gint64 start
= this->start
+ pos
;
1888 if (this->start
> start
)
1891 if ((this->start
+ size
) < (start
+ n
))
1894 memcpy (buffer
, ((char*) memory
) + this->start
- start
, n
);
1902 pthread_mutex_t
MediaThreadPool::mutex
= PTHREAD_MUTEX_INITIALIZER
;
1903 pthread_cond_t
MediaThreadPool::condition
= PTHREAD_COND_INITIALIZER
;
1904 pthread_cond_t
MediaThreadPool::completed_condition
= PTHREAD_COND_INITIALIZER
;
1905 int MediaThreadPool::count
= 0;
1906 pthread_t
MediaThreadPool::threads
[max_threads
];
1907 Media
*MediaThreadPool::medias
[max_threads
];
1908 Deployment
*MediaThreadPool::deployments
[max_threads
];
1909 bool MediaThreadPool::shutting_down
= false;
1910 List
*MediaThreadPool::queue
= NULL
;
1911 bool MediaThreadPool::valid
[max_threads
];
1914 MediaThreadPool::AddWork (MediaClosure
*closure
, bool wakeup
)
1916 pthread_attr_t attribs
;
1919 pthread_mutex_lock (&mutex
);
1921 if (shutting_down
) {
1922 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1925 queue
= new List ();
1926 queue
->Append (new MediaWork (closure
));
1928 // check if all threads are busy with other Media objects
1932 } else if (count
< max_threads
) {
1933 Media
*media
= closure
->GetMedia ();
1934 for (int i
= 0; i
< count
; i
++) {
1935 if (medias
[i
] == NULL
|| medias
[i
] == media
) {
1936 spawn
= false; // there is a thread working on this media or not working at all.
1945 int prev_count
= count
;
1947 count
++; // start up another thread.
1949 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count
);
1951 for (int i
= prev_count
; i
< count
&& result
== 0; i
++) {
1954 deployments
[i
] = NULL
;
1956 pthread_attr_init (&attribs
);
1957 pthread_attr_setdetachstate (&attribs
, PTHREAD_CREATE_JOINABLE
);
1958 result
= pthread_create (&threads
[i
], &attribs
, WorkerLoop
, NULL
);
1959 pthread_attr_destroy (&attribs
);
1962 fprintf (stderr
, "Moonlight: could not create media thread: %s (%i)\n", strerror (result
), result
);
1969 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1970 closure
->GetDescription (), closure
, closure
->GetMedia (), GET_OBJ_ID (closure
->GetMedia ()), closure
->GetDeployment (), queue
? queue
->Length () : -1);
1973 pthread_cond_signal (&condition
);
1975 pthread_mutex_unlock (&mutex
);
1979 MediaThreadPool::WaitForCompletion (Deployment
*deployment
)
1981 bool waiting
= false;
1982 MediaWork
*current
= NULL
;
1984 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment
);
1988 pthread_mutex_lock (&mutex
);
1992 /* check if the deployment is being worked on */
1993 for (int i
= 0; i
< count
; i
++) {
1994 if (deployments
[i
] == deployment
) {
1999 /* check if the deployment is in the queue */
2000 if (!waiting
&& queue
!= NULL
) {
2001 current
= (MediaWork
*) queue
->First ();
2002 while (current
!= NULL
) {
2003 if (current
->closure
->GetDeployment () == deployment
) {
2007 current
= (MediaWork
*) current
->next
;
2013 ts
.tv_nsec
= 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
2014 pthread_cond_timedwait (&completed_condition
, &mutex
, &ts
);
2017 pthread_mutex_unlock (&mutex
);
2021 MediaThreadPool::RemoveWork (Media
*media
)
2023 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media
, GET_OBJ_ID (media
));
2026 List::Node
*first
= NULL
;
2027 List::Node
*last
= NULL
;
2028 List::Node
*current
= NULL
;
2031 pthread_mutex_lock (&mutex
);
2033 // create a list of nodes to delete
2034 current
= queue
!= NULL
? queue
->First () : NULL
;
2035 while (current
!= NULL
) {
2036 next
= current
->next
; // retrieve next before Unlinking
2037 MediaWork
*mw
= (MediaWork
*) current
;
2038 if (mw
->closure
->GetMedia () == media
) {
2039 queue
->Unlink (current
);
2040 if (first
== NULL
) {
2043 last
->next
= current
;
2052 pthread_mutex_unlock (&mutex
);
2054 // We have to delete the list nodes with the
2055 // queue mutex unlocked, due to refcounting
2056 // (our node's (MediaWork) dtor will cause unrefs,
2057 // which may cause other dtors to be called,
2058 // eventually ending up wanting to lock the mutex
2062 while (current
!= NULL
) {
2063 next
= current
->next
;
2070 MediaThreadPool::WakeUp ()
2072 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2074 pthread_mutex_lock (&mutex
);
2075 pthread_cond_signal (&condition
);
2076 pthread_mutex_unlock (&mutex
);
2080 MediaThreadPool::IsThreadPoolThread ()
2082 bool result
= false;
2083 pthread_mutex_lock (&mutex
);
2084 for (int i
= 0; i
< count
; i
++) {
2085 if (pthread_equal (pthread_self (), threads
[i
])) {
2090 pthread_mutex_unlock (&mutex
);
2095 MediaThreadPool::Initialize ()
2097 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2100 shutting_down
= false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2104 MediaThreadPool::Shutdown ()
2106 List::Node
*current
= NULL
;
2107 List::Node
*next
= NULL
;
2109 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count
);
2113 g_return_if_fail (!shutting_down
);
2115 pthread_mutex_lock (&mutex
);
2117 shutting_down
= true;
2118 pthread_cond_broadcast (&condition
);
2120 for (int i
= 0; i
< count
; i
++) {
2124 pthread_mutex_unlock (&mutex
);
2125 pthread_join (threads
[i
], NULL
);
2126 pthread_mutex_lock (&mutex
);
2129 if (queue
!= NULL
) {
2130 current
= queue
->First ();
2131 queue
->Clear (false);
2137 pthread_mutex_unlock (&mutex
);
2139 // deleting a node can have side-effects, so we first copy the list of nodes,
2140 // clear the original and loop over the copy while deleting the nodes.
2141 // this prevents any reentering issues while deleting nodes.
2142 while (current
!= NULL
) {
2143 next
= current
->next
;
2148 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2152 MediaThreadPool::WorkerLoop (void *data
)
2154 MediaWork
*node
= NULL
;
2155 Media
*media
= NULL
;
2156 int self_index
= -1;
2159 * Unblock any signals. We inherit the blocked signals from the thread that
2160 * created us, and if that thread happens to be a thread that has signals
2161 * blocked, we might end up deadlocking in the gc (since the gc delivers
2162 * a suspend signal, this thread never gets it because the signal is blocked,
2163 * and the gc waits for us to handle the suspend signal).
2164 * The pulseaudio thread is one example of a thread that has all signals
2165 * blocked, causing this issue if we create a new thread from the
2166 * pulseaudio thread.
2169 sigset_t signal_set
;
2171 if ((err
= sigemptyset (&signal_set
)) != 0) {
2172 fprintf (stderr
, "Moonlight: Media thread pool was unable to create an empty set of signals: %s (%i).\n", strerror (err
), err
);
2173 } else if ((err
= pthread_sigmask (SIG_SETMASK
, &signal_set
, NULL
)) != 0) {
2174 fprintf (stderr
, "Moonlight: Media thread pool was unable to unblock all signals: %s (%i).\n", strerror (err
), err
);
2177 /* Something failed. Check if all signals are unblocked, if not, exit
2178 * the thread. Exiting the thread might cause media playback to fail,
2179 * while continuing with blocked signals will probably end up
2180 * deadlocking the gc.*/
2181 bool any_blocked_signals
= false;
2183 if (pthread_sigmask (SIG_BLOCK
, NULL
, &signal_set
) != 0) {
2184 any_blocked_signals
= true; /* Assume the worst */
2185 } else if (!sigisemptyset (&signal_set
)) {
2186 any_blocked_signals
= true;
2189 if (any_blocked_signals
) {
2190 fprintf (stderr
, "Moonlight: A media thread was started with blocked signals and could not unblock them. The media thread will exit (this may cause media playback to fail).\n");
2195 pthread_mutex_lock (&mutex
);
2196 for (int i
= 0; i
< count
; i
++) {
2197 if (pthread_equal (threads
[i
], pthread_self ())) {
2202 pthread_mutex_unlock (&mutex
);
2204 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index
);
2206 g_return_val_if_fail (self_index
>= 0, NULL
);
2208 while (!shutting_down
) {
2209 pthread_mutex_lock (&mutex
);
2211 medias
[self_index
] = NULL
;
2212 deployments
[self_index
] = NULL
;
2213 /* if anybody was waiting for us to finish working, notify them */
2215 pthread_cond_signal (&completed_condition
);
2218 node
= (MediaWork
*) (queue
!= NULL
? queue
->First () : NULL
);
2220 while (node
!= NULL
) {
2221 media
= node
->closure
->GetMedia ();
2223 for (int i
= 0; i
< count
; i
++) {
2224 if (medias
[i
] == media
) {
2225 // another thread is working for the same media object.
2226 // we need to find something else to do.
2235 node
= (MediaWork
*) node
->next
;
2239 pthread_cond_wait (&condition
, &mutex
);
2241 queue
->Unlink (node
);
2245 medias
[self_index
] = media
;
2246 /* At this point the current deployment might be wrong, so avoid
2247 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2248 * here, since it might end up doing a lot of work with the mutex
2250 deployments
[self_index
] = media
->GetUnsafeDeployment ();
2253 pthread_mutex_unlock (&mutex
);
2258 media
->SetCurrentDeployment (true, true);
2260 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: got %s %p for media %p on deployment %p, there are %d nodes left.\n", (int) pthread_self (), node
->closure
->GetDescription (), node
, media
, media
->GetDeployment (), queue
? queue
->Length () : -1);
2262 node
->closure
->Call ();
2264 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node
);
2269 pthread_mutex_lock (&mutex
);
2270 deployments
[self_index
] = NULL
;
2271 medias
[self_index
] = NULL
;
2272 /* if anybody was waiting for us to finish working, notify them */
2274 pthread_cond_signal (&completed_condition
);
2275 pthread_mutex_unlock (&mutex
);
2277 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index
);
2287 MediaClosure::MediaClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
, const char *description
)
2288 : EventObject (Type::MEDIACLOSURE
, true)
2290 Init (media
, callback
, context
);
2291 this->description
= description
;
2294 MediaClosure::MediaClosure (Type::Kind object_kind
, Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2295 : EventObject (object_kind
, true)
2297 Init (media
, callback
, context
);
2301 MediaClosure::Init (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2303 result
= MEDIA_INVALID
;
2305 this->callback
= callback
;
2306 this->context
= context
;
2308 this->context
->ref ();
2309 this->media
= media
;
2311 this->media
->ref ();
2313 // put checks at the end so that fields are still initialized, since we can't abort construction.
2314 g_return_if_fail (callback
!= NULL
);
2315 g_return_if_fail (media
!= NULL
);
2319 MediaClosure::Dispose ()
2333 EventObject::Dispose ();
2337 MediaClosure::Call ()
2340 result
= callback (this);
2342 result
= MEDIA_NO_CALLBACK
;
2347 * MediaDisposeObjectClosure
2349 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2350 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE
, media
, callback
, context
)
2355 MediaDisposeObjectClosure::Dispose ()
2357 if (!CallExecuted ()) {
2358 // we haven't been executed. do it now.
2360 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2365 MediaClosure::Dispose ();
2371 MediaSeekClosure::MediaSeekClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2372 : MediaClosure (Type::MEDIASEEKCLOSURE
, media
, callback
, context
)
2378 * MediaReportSeekCompletedClosure
2381 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2382 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE
, media
, callback
, context
)
2384 g_return_if_fail (context
!= NULL
);
2389 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2394 MediaReportSeekCompletedClosure::Dispose ()
2396 MediaClosure::Dispose ();
2400 * MediaGetFrameClosure
2403 MediaGetFrameClosure::MediaGetFrameClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, IMediaStream
*stream
)
2404 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2406 this->stream
= NULL
;
2408 g_return_if_fail (context
!= NULL
);
2409 g_return_if_fail (stream
!= NULL
);
2411 this->stream
= stream
;
2412 // this->stream->ref ();
2414 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2417 MediaGetFrameClosure::~MediaGetFrameClosure ()
2419 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2423 MediaGetFrameClosure::Dispose ()
2426 // stream->unref ();
2430 MediaClosure::Dispose ();
2431 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2435 * MediaReportFrameCompletedClosure
2438 MediaReportFrameCompletedClosure::MediaReportFrameCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, MediaFrame
*frame
)
2439 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2443 g_return_if_fail (context
!= NULL
);
2445 this->frame
= frame
;
2447 this->frame
->ref ();
2451 MediaReportFrameCompletedClosure::Dispose ()
2458 MediaClosure::Dispose ();
2465 IMediaStream::IMediaStream (Type::Kind kind
, Media
*media
) : IMediaObject (kind
, media
)
2469 extra_data_size
= 0;
2481 input_ended
= false;
2482 output_ended
= false;
2484 first_pts
= G_MAXUINT64
; // The first pts in the stream, initialized to G_MAXUINT64
2485 last_popped_pts
= G_MAXUINT64
; // The pts of the last frame returned, initialized to G_MAXUINT64
2486 last_enqueued_pts
= G_MAXUINT64
; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2487 last_available_pts
= 0; // The pts of the last available frame, initialized to 0
2491 IMediaStream::Dispose ()
2494 IMediaDecoder
*d
= decoder
;
2499 g_free (extra_data
);
2505 IMediaObject::Dispose ();
2509 IMediaStream::CreateCodec (int codec_id
)
2512 case CODEC_WMV1
: return g_strdup ("wmv1");
2513 case CODEC_WMV2
: return g_strdup ("wmv2");
2514 case CODEC_WMV3
: return g_strdup ("wmv3");
2515 case CODEC_WMVA
: return g_strdup ("wmva");
2516 case CODEC_WVC1
: return g_strdup ("vc1");
2517 case CODEC_RGBA
: return g_strdup ("rgba");
2518 case CODEC_YV12
: return g_strdup ("yv12");
2519 case CODEC_MP3
: return g_strdup ("mp3");
2520 case CODEC_WMAV1
: return g_strdup ("wmav1");
2521 case CODEC_WMAV2
: return g_strdup ("wmav2");
2522 case CODEC_WMAV3
: return g_strdup ("wmav3");
2523 case CODEC_PCM
: return g_strdup ("pcm");
2525 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id
);
2527 /* This algorithm needs testing.
2530 int a = (codec_id & 0x000000FF);
2531 int b = (codec_id & 0x0000FF00) >> 8;
2532 int c = (codec_id & 0x00FF0000) >> 16;
2533 int d = (codec_id & 0xFF000000) >> 24;
2535 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2537 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2539 result = (char *) g_malloc (size + 1);
2542 result [current++] = (char) a;
2544 result [current++] = (char) b;
2546 result [current++] = (char) c;
2548 result [current++] = (char) d;
2549 result [current] = 0;
2551 return g_strdup ("<unknown>");
2557 IMediaStream::IsQueueEmpty ()
2559 return queue
.IsEmpty ();
2563 IMediaStream::GetStreamTypeName ()
2565 switch (GetType ()) {
2566 case MediaTypeVideo
: return "Video";
2567 case MediaTypeAudio
: return "Audio";
2568 case MediaTypeMarker
: return "Marker";
2569 default: return "Unknown";
2574 IMediaStream::ReportSeekCompleted ()
2576 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2577 input_ended
= false;
2578 output_ended
= false;
2580 if (decoder
!= NULL
)
2581 decoder
->ReportSeekCompleted ();
2585 IMediaStream::GetDemuxerReffed ()
2588 IMediaDemuxer
*result
;
2593 media
= GetMediaReffed ();
2595 g_return_val_if_fail (media
!= NULL
, NULL
);
2597 result
= media
->GetDemuxerReffed ();
2605 IMediaStream::GetDecoder ()
2611 IMediaStream::SetDecoder (IMediaDecoder
*value
)
2621 IMediaStream::GetOutputEnded ()
2623 return output_ended
;
2627 IMediaStream::SetOutputEnded (bool value
)
2629 output_ended
= value
;
2633 IMediaStream::GetInputEnded ()
2639 IMediaStream::SetInputEnded (bool value
)
2641 input_ended
= value
;
2642 if (GetDecoder () != NULL
)
2643 GetDecoder ()->ReportInputEnded ();
2647 IMediaStream::GetBufferedSize ()
2652 if (first_pts
== G_MAXUINT64
|| last_enqueued_pts
== G_MAXUINT64
)
2654 else if (last_popped_pts
== G_MAXUINT64
)
2655 result
= last_enqueued_pts
- first_pts
;
2657 result
= last_enqueued_pts
- last_popped_pts
;
2660 LOG_BUFFERING ("IMediaStream::GetBufferedSize (): id: %i, codec: %s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, result: %" G_GUINT64_FORMAT
" ms\n",
2661 GET_OBJ_ID (this), codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
), MilliSeconds_FromPts (result
));
2668 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2671 IMediaStream::PrintBufferInformation ()
2673 guint64 buffer_size
= GetBufferedSize ();
2675 printf (" <%s: ", codec
);
2677 if (GetSelected ()) {
2678 printf ("size: %.4" G_GINT64_FORMAT
", first: %.4" G_GINT64_FORMAT
", last popped: %.4" G_GINT64_FORMAT
", last enq: %.4" G_GINT64_FORMAT
", frames enq: %i>",
2679 TO_MS (buffer_size
), TO_MS (first_pts
), TO_MS (last_popped_pts
),
2680 TO_MS (last_enqueued_pts
), queue
.Length ());
2682 printf ("(not selected) >");
2688 IMediaStream::EnqueueFrame (MediaFrame
*frame
)
2691 guint64 seeked_to_pts
= 0;
2693 IMediaDemuxer
*demuxer
= NULL
;
2694 /* Add nodes to be deleted here, they'll automaticall be deleted when the method exits. */
2695 /* The reason for doing this at method exit is to not do deletion (with unrefs, etc) with a mutex held */
2698 g_return_if_fail (Media::InMediaThread ());
2700 media
= GetMediaReffed ();
2701 g_return_if_fail (media
!= NULL
);
2703 if (media
->IsStopped ()) {
2704 /* We need to enqueue one frame so that we can render the first frame for a stopped media element */
2705 if (!IsQueueEmpty ()) {
2706 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame (we already have at least one frame).\n", frame
);
2709 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, but enqueing since we're empty.\n", frame
);
2713 if (frame
->buffer
== NULL
) {
2714 /* for some reason there is no output from the decoder, possibly because it needs more data from the demuxer before outputting anything */
2715 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No data in frame, not storing it.\n", frame
);
2719 demuxer
= GetDemuxerReffed ();
2720 if (demuxer
== NULL
) {
2721 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No demuxer.\n", frame
);
2724 seeked_to_pts
= demuxer
->GetSeekedToPts ();
2728 if (first_pts
== G_MAXUINT64
)
2729 first_pts
= frame
->pts
;
2731 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
2734 if (last_enqueued_pts
> frame
->pts
&& last_enqueued_pts
!= G_MAXUINT64
&& frame
->event
!= FrameEventEOF
&& frame
->buflen
> 0) {
2735 g_warning ("IMediaStream::EnqueueFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, "
2736 "buffer: %" G_GUINT64_FORMAT
" ms, frame: %p, frame->buflen: %i, frame->pts: %" G_GUINT64_FORMAT
" ms (the last enqueued frame's pts is below the current frame's pts)\n",
2737 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2738 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), frame
, frame
->buflen
, MilliSeconds_FromPts (frame
->pts
));
2742 last_enqueued_pts
= frame
->pts
;
2743 first
= queue
.LinkedList ()->Length () == 0;
2744 queue
.LinkedList ()->Append (new StreamNode (frame
));
2746 if (seeked_to_pts
!= G_MAXUINT64
&& first_pts
< seeked_to_pts
2747 && (GetObjectType () == Type::AUDIOSTREAM
|| GetObjectType () == Type::VIDEOSTREAM
)) {
2748 StreamNode
*last_key_frame
= NULL
;
2752 /* we need to remove any frames before the last key frame before the seeked-to pts */
2754 /* find the last key frame below the seeked-to pts */
2755 node
= (StreamNode
*) queue
.LinkedList ()->First ();
2756 while (node
!= NULL
&& node
->GetFrame ()->pts
< seeked_to_pts
) {
2757 if (GetObjectType () == Type::AUDIOSTREAM
|| node
->GetFrame ()->IsKeyFrame ())
2758 last_key_frame
= node
;
2760 node
= (StreamNode
*) node
->next
;
2763 if (last_key_frame
!= NULL
) {
2764 /* remove any frames before that last key frame */
2765 node
= (StreamNode
*) last_key_frame
->prev
;
2766 while (node
!= NULL
) {
2767 n
= (StreamNode
*) node
->prev
;
2768 queue
.LinkedList ()->Unlink (node
);
2769 trash
.Append (node
);
2773 /* update the first pts to point to the real first pts */
2774 node
= (StreamNode
*) queue
.LinkedList ()->First ();
2775 guint64 next_first_pts
= node
== NULL
? G_MAXUINT64
: node
->GetFrame ()->pts
;
2776 LOG_PIPELINE ("%s::EnqueueFrame (): setting first_pts to: %" G_GUINT64_FORMAT
", from %" G_GUINT64_FORMAT
" (demuxer first pts: %" G_GUINT64_FORMAT
")\n",
2777 GetTypeName (), first_pts
, next_first_pts
, seeked_to_pts
);
2778 first_pts
= next_first_pts
;
2784 SetLastAvailablePts (frame
->pts
);
2787 EmitSafe (FirstFrameEnqueuedEvent
);
2794 LOG_BUFFERING ("IMediaStream::EnqueueFrame (): codec: %.5s, first: %i, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, buffer: %" G_GUINT64_FORMAT
" ms, frame: %p, frame->buflen: %i\n",
2795 codec
, first
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2796 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
- first_pts
), frame
, frame
->buflen
);
2800 IMediaStream::PopFrame ()
2802 MediaFrame
*result
= NULL
;
2803 StreamNode
*node
= NULL
;
2805 // We use the queue lock to synchronize access to
2806 // last_popped_pts/last_enqueued_pts/first_pts
2809 node
= (StreamNode
*) queue
.LinkedList ()->First ();
2811 result
= node
->GetFrame ();
2813 queue
.LinkedList ()->Remove (node
);
2814 last_popped_pts
= result
->pts
;
2818 LOG_BUFFERING ("IMediaStream::PopFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %" G_GUINT64_FORMAT
" ms, buffer: %" G_GUINT64_FORMAT
" ms, frame: %p, frame->buflen: %i\n",
2819 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2820 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), result
, result
? result
->buflen
: 0);
2822 if (!input_ended
&& !output_ended
&& result
!= NULL
) {
2823 IMediaDemuxer
*demuxer
= GetDemuxerReffed ();
2824 if (demuxer
!= NULL
) {
2825 demuxer
->FillBuffers ();
2834 IMediaStream::ClearQueue ()
2836 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2838 queue
.LinkedList ()->Clear (true);
2839 first_pts
= G_MAXUINT64
;
2840 last_popped_pts
= G_MAXUINT64
;
2841 last_enqueued_pts
= G_MAXUINT64
;
2846 IMediaStream::SetSelected (bool value
)
2848 IMediaDemuxer
*demuxer
;
2852 demuxer
= GetDemuxerReffed ();
2854 if (demuxer
!= NULL
) {
2855 demuxer
->UpdateSelected (this);
2861 * IMediaStream.StreamNode
2864 IMediaStream::StreamNode::StreamNode (MediaFrame
*f
)
2870 IMediaStream::StreamNode::~StreamNode ()
2879 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
, IMediaSource
*source
) : IMediaObject (kind
, media
)
2881 this->source
= source
;
2882 this->source
->ref ();
2888 pending_stream
= NULL
;
2889 pending_fill_buffers
= false;
2890 seeked_to_pts
= G_MAXUINT64
;
2893 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
)
2894 : IMediaObject (kind
, media
)
2902 pending_stream
= NULL
;
2903 pending_fill_buffers
= false;
2904 seeked_to_pts
= G_MAXUINT64
;
2908 IMediaDemuxer::Dispose ()
2910 if (streams
!= NULL
) {
2911 IMediaStream
**tmp
= streams
;
2912 int stream_count
= this->stream_count
;
2914 for (int i
= 0; i
< stream_count
; i
++) {
2915 tmp
[i
]->Dispose ();
2924 if (pending_stream
!= NULL
) {
2925 pending_stream
->unref ();
2926 pending_stream
= NULL
;
2929 IMediaObject::Dispose ();
2933 IMediaDemuxer::OpenCallback (MediaClosure
*closure
)
2935 IMediaDemuxer
*demuxer
;
2937 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure
);
2939 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2940 demuxer
->OpenDemuxerAsync ();
2942 return MEDIA_SUCCESS
;
2946 IMediaDemuxer::EnqueueOpen ()
2948 MediaClosure
*closure
;
2949 Media
*media
= GetMediaReffed ();
2951 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2956 closure
= new MediaClosure (media
, OpenCallback
, this, "IMediaDemuxer::OpenCallback");
2957 media
->EnqueueWork (closure
, false);
2963 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2965 Media
*media
= GetMediaReffed ();
2967 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media
);
2972 // Media might be null if we got disposed for some reason.
2976 media
->ReportOpenDemuxerCompleted ();
2981 IMediaDemuxer::ReportGetFrameProgress (double progress
)
2983 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress
);
2987 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream
*stream
)
2989 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream
);
2993 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind
, gint64 value
)
2995 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %" G_GINT64_FORMAT
")\n", kind
, value
);
2999 IMediaDemuxer::EnqueueReportGetFrameCompleted (MediaFrame
*frame
)
3001 Media
*media
= GetMediaReffed ();
3006 MediaClosure
*closure
= new MediaReportFrameCompletedClosure (media
, ReportGetFrameCompletedCallback
, this, frame
);
3007 media
->EnqueueWork (closure
);
3013 IMediaDemuxer::ReportGetFrameCompletedCallback (MediaClosure
*closure
)
3015 MediaReportFrameCompletedClosure
*c
= (MediaReportFrameCompletedClosure
*) closure
;
3017 g_return_val_if_fail (c
!= NULL
, MEDIA_FAIL
);
3018 g_return_val_if_fail (c
->GetDemuxer () != NULL
, MEDIA_FAIL
);
3020 c
->GetDemuxer ()->ReportGetFrameCompleted (c
->GetFrame ());
3022 return MEDIA_SUCCESS
;
3026 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame
*frame
)
3030 g_return_if_fail (frame
== NULL
|| (frame
!= NULL
&& frame
->stream
!= NULL
));
3031 g_return_if_fail (pending_stream
!= NULL
);
3033 media
= GetMediaReffed ();
3035 g_return_if_fail (media
!= NULL
);
3037 /* ensure we're on a media thread */
3038 if (!Media::InMediaThread ()) {
3039 EnqueueReportGetFrameCompleted (frame
);
3043 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p) %i %s %" G_GUINT64_FORMAT
" ms\n", frame
, GET_OBJ_ID (this), frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : (guint64
) -1);
3045 if (frame
== NULL
) {
3046 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame
, pending_stream
->GetStreamTypeName ());
3047 // No more data for this stream
3048 pending_stream
->SetInputEnded (true);
3049 } else if (!frame
->stream
->IsDisposed ()) {
3050 IMediaDecoder
*decoder
= frame
->stream
->GetDecoder ();
3051 if (decoder
!= NULL
)
3052 decoder
->DecodeFrameAsync (frame
, true /* always enqueue */);
3055 pending_stream
->unref ();
3056 pending_stream
= NULL
; // not waiting for anything more
3058 // enqueue some more
3067 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure
*c
)
3069 MediaReportSeekCompletedClosure
*closure
= (MediaReportSeekCompletedClosure
*) c
;
3070 IMediaDemuxer
*demuxer
;
3072 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
3073 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
3075 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3076 demuxer
->ReportSeekCompleted (closure
->GetPts ());
3078 return MEDIA_SUCCESS
;
3082 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts
)
3084 Media
*media
= GetMediaReffed ();
3089 MediaClosure
*closure
= new MediaReportSeekCompletedClosure (media
, ReportSeekCompletedCallback
, this, pts
);
3090 media
->EnqueueWork (closure
);
3096 IMediaDemuxer::ReportSeekCompleted (guint64 pts
)
3100 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
")\n", pts
);
3102 g_return_if_fail (seeking
);
3104 if (!Media::InMediaThread ()) {
3105 EnqueueReportSeekCompleted (pts
);
3110 if (pending_stream
!= NULL
)
3111 printf ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
"): we can't be waiting for a frame now.\n", pts
);
3114 media
= GetMediaReffed ();
3116 g_return_if_fail (media
!= NULL
);
3118 /* We need to call ReportSeekCompleted once for every time SeekAsync(pts) was called */
3119 for (int i
= 0; i
< GetStreamCount (); i
++) {
3120 IMediaStream
*stream
= GetStream (i
);
3125 stream
->ReportSeekCompleted ();
3130 seeking
= !seeks
.IsEmpty ();
3133 media
->ReportSeekCompleted (pts
);
3137 seeked_to_pts
= pts
;
3138 pending_fill_buffers
= false;
3141 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
"): still pending seeks, enqueuing another seek.\n", pts
);
3145 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
") [Done]\n", pts
);
3149 IMediaDemuxer::OpenDemuxerAsync ()
3151 g_return_if_fail (opened
== false);
3155 OpenDemuxerAsyncInternal ();
3159 IMediaDemuxer::GetFrameCallback (MediaClosure
*c
)
3161 MediaGetFrameClosure
*closure
= (MediaGetFrameClosure
*) c
;
3162 IMediaDemuxer
*demuxer
;
3164 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
3165 g_return_val_if_fail (closure
->GetStream () != NULL
, MEDIA_FAIL
);
3166 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
3168 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3169 demuxer
->GetFrameAsync (closure
->GetStream ());
3171 return MEDIA_SUCCESS
;
3175 IMediaDemuxer::EnqueueGetFrame (IMediaStream
*stream
)
3177 g_return_if_fail (pending_stream
== NULL
); // we can't be waiting for another frame.
3179 Media
*media
= GetMediaReffed ();
3184 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
3185 media
->EnqueueWork (closure
);
3191 IMediaDemuxer::GetFrameAsync (IMediaStream
*stream
)
3193 Media
*media
= NULL
;
3195 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream
, stream
->GetStreamTypeName (), Media::InMediaThread ());
3197 if (!Media::InMediaThread ()) {
3198 EnqueueGetFrame (stream
);
3203 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (): delayed since we're waiting for a seek.\n");
3207 if (pending_stream
!= NULL
) {
3208 /* we're already waiting for a frame */
3212 media
= GetMediaReffed ();
3214 g_return_if_fail (media
!= NULL
);
3216 if (stream
!= NULL
) {
3217 pending_stream
= stream
;
3218 pending_stream
->ref ();
3219 GetFrameAsyncInternal (stream
);
3228 IMediaDemuxer::SeekCallback (MediaClosure
*closure
)
3230 MediaSeekClosure
*seek
= (MediaSeekClosure
*) closure
;
3231 seek
->GetDemuxer ()->SeekAsync ();
3232 return MEDIA_SUCCESS
;
3236 IMediaDemuxer::EnqueueSeek ()
3238 Media
*media
= GetMediaReffed ();
3239 MediaSeekClosure
*closure
;
3241 g_return_if_fail (media
!= NULL
);
3243 closure
= new MediaSeekClosure (media
, SeekCallback
, this, 0);
3244 media
->EnqueueWork (closure
, true);
3250 IMediaDemuxer::SeekAsync ()
3252 guint64 pts
= G_MAXUINT64
;
3254 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (), seeking: %i\n", seeking
);
3256 g_return_if_fail (Media::InMediaThread ());
3258 seeking
= true; /* this ensures that we stop demuxing frames asap */
3260 if (pending_stream
!= NULL
) {
3261 /* we're waiting for the decoder to decode a frame, wait a bit with the seek */
3262 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i waiting for a frame, postponing seek\n", GET_OBJ_ID (this));
3268 if (!seeks
.IsEmpty ())
3269 pts
= ((PtsNode
*) seeks
.First ())->pts
;
3272 if (pts
== G_MAXUINT64
) {
3273 LOG_PIPELINE ("IMediaDemuxer.:SeekAsync (): %i no pending seek?\n", GET_OBJ_ID (this));
3278 /* Ask the demuxer to seek */
3279 /* at this point the pipeline shouldn't be doing anything else (for this media) */
3280 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i seeking to %" G_GUINT64_FORMAT
"\n", GET_OBJ_ID (this), pts
);
3281 Media
*media
= GetMediaReffed ();
3283 media
->EmitSafe (Media::SeekingEvent
);
3286 SeekAsyncInternal (pts
);
3290 IMediaDemuxer::SeekAsync (guint64 pts
)
3292 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%" G_GUINT64_FORMAT
")\n", pts
);
3299 seeks
.Append (new PtsNode (pts
));
3306 IMediaDemuxer::ClearBuffers ()
3308 pending_fill_buffers
= false;
3310 /* Clear all the buffer queues */
3311 for (int i
= 0; i
< GetStreamCount (); i
++) {
3312 IMediaStream
*stream
= GetStream (i
);
3317 stream
->ClearQueue ();
3322 IMediaDemuxer::FillBuffersCallback (MediaClosure
*closure
)
3324 IMediaDemuxer
*demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3325 demuxer
->FillBuffersInternal ();
3326 return MEDIA_SUCCESS
;
3330 IMediaDemuxer::FillBuffers ()
3332 Media
*media
= NULL
;
3333 MediaClosure
*closure
;
3334 bool enqueue
= true;
3337 if (pending_fill_buffers
) {
3338 // there's already a FillBuffers request enqueued
3341 media
= GetMediaReffed ();
3342 if (media
== NULL
) {
3346 pending_fill_buffers
= true;
3352 closure
= new MediaClosure (media
, FillBuffersCallback
, this, "IMediaDemuxer::FillBuffersCallback");
3353 media
->EnqueueWork (closure
);
3362 IMediaDemuxer::FillBuffersInternal ()
3364 IMediaStream
*stream
;
3365 IMediaStream
*request_stream
= NULL
;
3366 guint64 min_buffered_size
= G_MAXUINT64
;
3367 Media
*media
= GetMediaReffed ();
3368 guint64 buffering_time
= 0;
3369 guint64 buffered_size
= 0;
3371 int media_streams
= 0;
3373 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (), %i %s buffering time: %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms, pending_stream: %i %s\n", GET_OBJ_ID (this), GetTypeName (), buffering_time
, media
!= NULL
? MilliSeconds_FromPts (media
->GetBufferingTime ()) : -1, GET_OBJ_ID (pending_stream
), pending_stream
? pending_stream
->GetStreamTypeName () : "NULL");
3376 pending_fill_buffers
= false;
3382 // If we're waiting for something, there's nothing to do here.
3383 if (pending_stream
!= NULL
)
3386 // Find the stream with the smallest buffered size, and request a frame from that stream.
3387 g_return_if_fail (media
!= NULL
);
3389 buffering_time
= media
->GetBufferingTime ();
3391 if (buffering_time
== 0) {
3392 // Play as soon as possible.
3393 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3394 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3395 // all streams have 1 frame queued.
3399 for (int i
= 0; i
< GetStreamCount (); i
++) {
3400 IMediaDecoder
*decoder
= NULL
;
3402 stream
= GetStream (i
);
3403 if (!stream
->GetSelected ())
3406 if (stream
->GetType () != MediaTypeVideo
&&
3407 stream
->GetType () != MediaTypeAudio
)
3411 if (stream
->GetOutputEnded ()) {
3413 continue; // this stream has ended.
3416 decoder
= stream
->GetDecoder ();
3417 if (decoder
== NULL
) {
3418 fprintf (stderr
, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream
->GetStreamTypeName (), GET_OBJ_ID (stream
), stream
->GetRefCount ());
3419 continue; // no decoder??
3422 buffered_size
= stream
->GetBufferedSize ();
3423 min_buffered_size
= MIN (min_buffered_size
, buffered_size
);
3425 if (buffered_size
>= buffering_time
)
3426 continue; // this stream has enough data buffered.
3428 if (!decoder
->IsDecoderQueueEmpty ())
3429 continue; // this stream is waiting for data to be decoded.
3431 if (buffered_size
<= min_buffered_size
)
3432 request_stream
= stream
;
3434 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): codec: %s, stream id: %i, buffered size: %" G_GUINT64_FORMAT
" ms, buffering time: %" G_GUINT64_FORMAT
" ms, last popped time: %" G_GUINT64_FORMAT
" ms\n",
3435 stream
->codec
, GET_OBJ_ID (stream
), MilliSeconds_FromPts (buffered_size
), MilliSeconds_FromPts (buffering_time
), MilliSeconds_FromPts (stream
->GetLastPoppedPts ()));
3438 if (request_stream
!= NULL
) {
3439 if (media
->IsStopped ()) {
3440 if (!request_stream
->IsQueueEmpty ()) {
3441 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, and we have frames in the buffer.\n");
3444 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped, but the buffer is empty, continuing\n");
3448 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): requesting frame from %s stream (%i), min_buffered_size: %" G_GUINT64_FORMAT
" ms\n", request_stream
->GetStreamTypeName (), GET_OBJ_ID (stream
), MilliSeconds_FromPts (min_buffered_size
));
3449 GetFrameAsync (request_stream
);
3452 if (media_streams
> 0) {
3453 if (ended
== media_streams
) {
3454 media
->ReportBufferingProgress (1.0);
3456 if (min_buffered_size
> 0 && buffering_time
> 0) {
3457 double progress
= ((double) min_buffered_size
/ (double) buffering_time
);
3458 media
->ReportBufferingProgress (progress
);
3467 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT
" ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3471 IMediaDemuxer::GetBufferedSize ()
3473 guint64 result
= G_MAXUINT64
;
3474 IMediaStream
*stream
;
3476 for (int i
= 0; i
< GetStreamCount (); i
++) {
3477 stream
= GetStream (i
);
3478 if (!stream
->GetSelected ())
3481 if (stream
->GetType () != MediaTypeVideo
&& stream
->GetType () != MediaTypeAudio
)
3484 result
= MIN (result
, stream
->GetBufferedSize ());
3491 IMediaDemuxer::GetLastAvailablePts ()
3493 guint64 result
= G_MAXUINT64
;
3494 IMediaStream
*stream
;
3496 for (int i
= 0; i
< GetStreamCount (); i
++) {
3497 stream
= GetStream (i
);
3499 if (stream
== NULL
|| !stream
->GetSelected ())
3502 result
= MIN (result
, stream
->GetLastAvailablePts ());
3505 if (result
== G_MAXUINT64
)
3513 IMediaDemuxer::PrintBufferInformation ()
3515 printf ("Buffer: %" G_GINT64_FORMAT
"", MilliSeconds_FromPts (GetBufferedSize ()));
3516 for (int i
= 0; i
< GetStreamCount (); i
++) {
3517 GetStream (i
)->PrintBufferInformation ();
3524 IMediaDemuxer::GetDuration ()
3527 for (int i
= 0; i
< GetStreamCount (); i
++)
3528 result
= MAX (result
, GetStream (i
)->duration
);
3533 IMediaDemuxer::GetStream (int index
)
3535 return (index
< 0 || index
>= stream_count
) ? NULL
: streams
[index
];
3542 MediaFrame::MediaFrame (IMediaStream
*stream
)
3543 : EventObject (Type::MEDIAFRAME
, true)
3547 g_return_if_fail (stream
!= NULL
);
3549 this->stream
= stream
;
3550 this->stream
->ref ();
3553 MediaFrame::MediaFrame (IMediaStream
*stream
, guint8
*buffer
, guint32 buflen
, guint64 pts
, bool keyframe
)
3554 : EventObject (Type::MEDIAFRAME
, true)
3558 g_return_if_fail (stream
!= NULL
);
3560 this->stream
= stream
;
3561 this->stream
->ref ();
3562 this->buffer
= buffer
;
3563 this->buflen
= buflen
;
3567 if (buflen
> 4 && false) {
3568 printf ("MediaFrame::MediaFrame () %s buffer: ", stream
->GetStreamTypeName ());
3569 for (int i
= 0; i
< 4; i
++)
3570 printf (" 0x%x", buffer
[i
]);
3576 AddState (MediaFrameKeyFrame
);
3580 MediaFrame::Initialize ()
3582 decoder_specific_data
= NULL
;
3594 for (int i
= 0; i
< 4; i
++) {
3605 MediaFrame::~MediaFrame ()
3610 MediaFrame::Dispose ()
3612 IMediaDecoder
*decoder
;
3615 // We can be called either on the main thread just before destruction
3616 // (in which case there are no races since the code which unreffed us
3617 // is the only code which knows about us), or at any time from the
3620 if (GetRefCount () != 0 && stream
!= NULL
) {
3621 if (!Media::InMediaThread ()) {
3622 // if refcount != 0 we're not being called just before destruction, in which case we should
3623 // only be on the media thread.
3624 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3629 if (decoder_specific_data
!= NULL
&& stream
!= NULL
) {
3630 decoder
= stream
->GetDecoder ();
3631 if (decoder
!= NULL
)
3632 decoder
->Cleanup (this);
3645 EventObject::Dispose ();
3649 MediaFrame::SetSrcSlideY (int value
)
3655 MediaFrame::SetSrcSlideH (int value
)
3661 MediaFrame::SetSrcStride (int a
, int b
, int c
, int d
)
3670 MediaFrame::SetDataStride (guint8
* a
, guint8
* b
, guint8
* c
, guint8
* d
)
3672 data_stride
[0] = a
;
3673 data_stride
[1] = b
;
3674 data_stride
[2] = c
;
3675 data_stride
[3] = d
;
3679 * IMediaObject.EventData
3682 IMediaObject::EventData::EventData (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3684 this->event_id
= event_id
;
3685 this->handler
= handler
;
3686 this->context
= context
;
3687 this->context
->ref ();
3688 this->invoke_on_main_thread
= invoke_on_main_thread
;
3691 IMediaObject::EventData::~EventData ()
3698 * IMediaObject.EmitData
3701 IMediaObject::EmitData::EmitData (int event_id
, EventHandler handler
, EventObject
*context
, EventArgs
*args
)
3703 this->event_id
= event_id
;
3704 this->handler
= handler
;
3705 this->context
= context
;
3706 this->context
->ref ();
3712 IMediaObject::EmitData::~EmitData ()
3726 IMediaObject::IMediaObject (Type::Kind kind
, Media
*media
)
3727 : EventObject (kind
, true)
3729 this->media
= media
;
3731 this->media
->ref ();
3732 g_return_if_fail (media
!= NULL
);
3734 emit_on_main_thread
= NULL
;
3738 IMediaObject::Dispose ()
3742 // We can be called either on the main thread just before destruction
3743 // (in which case there are no races since the code which unreffed us
3744 // is the only code which knows about us), or at any time from the
3746 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3747 // if refcount != 0 we're not being called just before destruction, in which case we should
3748 // only be on the media thread.
3749 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3753 media_mutex
.Lock ();
3758 media_mutex
.Unlock ();
3760 event_mutex
.Lock ();
3763 if (emit_on_main_thread
!= NULL
) {
3764 delete emit_on_main_thread
;
3765 emit_on_main_thread
= NULL
;
3767 event_mutex
.Unlock ();
3769 EventObject::Dispose ();
3773 IMediaObject::AddSafeHandler (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3775 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id
, handler
, context
, invoke_on_main_thread
);
3778 if (!IsDisposed ()) {
3779 ed
= new EventData (event_id
, handler
, context
, invoke_on_main_thread
);
3780 event_mutex
.Lock ();
3782 events
= new List ();
3783 events
->Append (ed
);
3784 event_mutex
.Unlock ();
3789 IMediaObject::RemoveSafeHandlers (EventObject
*context
)
3794 event_mutex
.Lock ();
3795 if (events
!= NULL
) {
3796 ed
= (EventData
*) events
->First ();
3797 while (ed
!= NULL
) {
3798 next
= (EventData
*) ed
->next
;
3799 if (ed
->context
== context
)
3800 events
->Remove (ed
);
3804 event_mutex
.Unlock ();
3808 IMediaObject::EmitSafe (int event_id
, EventArgs
*args
)
3810 List
*emits
= NULL
; // The events to emit on this thread.
3817 // Create a list of all the events to emit
3818 // don't keep the lock while emitting.
3819 event_mutex
.Lock ();
3820 if (events
!= NULL
) {
3821 ed
= (EventData
*) events
->First ();
3822 while (ed
!= NULL
) {
3823 if (ed
->event_id
== event_id
) {
3824 emit
= new EmitData (event_id
, ed
->handler
, ed
->context
, args
);
3825 if (ed
->invoke_on_main_thread
) {
3826 if (emit_on_main_thread
== NULL
)
3827 emit_on_main_thread
= new List ();
3828 emit_on_main_thread
->Append (emit
);
3831 emits
= new List ();
3832 emits
->Append (emit
);
3835 ed
= (EventData
*) ed
->next
;
3838 event_mutex
.Unlock ();
3840 // emit the events to be emitted on this thread
3843 if (Surface::InMainThread ()) {
3844 // if we're already on the main thread,
3845 // we can the events to be emitted
3846 // on the main thread
3848 event_mutex
.Lock ();
3849 tmp
= emit_on_main_thread
;
3850 emit_on_main_thread
= NULL
;
3851 event_mutex
.Unlock ();
3854 AddTickCall (EmitListCallback
);
3863 IMediaObject::EmitListMain ()
3868 event_mutex
.Lock ();
3869 list
= emit_on_main_thread
;
3870 emit_on_main_thread
= NULL
;
3871 event_mutex
.Unlock ();
3876 IMediaObject::EmitListCallback (EventObject
*obj
)
3878 IMediaObject
*media_obj
= (IMediaObject
*) obj
;
3879 media_obj
->EmitListMain ();
3883 IMediaObject::EmitList (List
*list
)
3890 emit
= (EmitData
*) list
->First ();
3891 while (emit
!= NULL
) {
3892 emit
->handler (this, emit
->args
, emit
->context
);
3893 emit
= (EmitData
*) emit
->next
;
3900 IMediaObject::GetMediaReffed ()
3903 media_mutex
.Lock ();
3907 media_mutex
.Unlock ();
3912 IMediaObject::ReportErrorOccurred (char const *message
)
3914 g_return_if_fail (media
!= NULL
);
3916 media
->ReportErrorOccurred (message
);
3920 IMediaObject::ReportErrorOccurred (MediaResult result
)
3922 g_return_if_fail (media
!= NULL
);
3924 media
->ReportErrorOccurred (result
);
3928 IMediaObject::ReportErrorOccurred (ErrorEventArgs
*args
)
3930 g_return_if_fail (media
!= NULL
);
3932 media
->ReportErrorOccurred (args
);
3936 IMediaObject::SetMedia (Media
*value
)
3938 media_mutex
.Lock ();
3944 media_mutex
.Unlock ();
3951 IMediaSource::IMediaSource (Type::Kind kind
, Media
*media
)
3952 : IMediaObject (kind
, media
)
3954 pthread_mutexattr_t attribs
;
3955 pthread_mutexattr_init (&attribs
);
3956 pthread_mutexattr_settype (&attribs
, PTHREAD_MUTEX_RECURSIVE
);
3957 pthread_mutex_init (&mutex
, &attribs
);
3958 pthread_mutexattr_destroy (&attribs
);
3960 pthread_cond_init (&condition
, NULL
);
3963 IMediaSource::~IMediaSource ()
3965 pthread_mutex_destroy (&mutex
);
3966 pthread_cond_destroy (&condition
);
3970 IMediaSource::Dispose ()
3972 IMediaObject::Dispose ();
3976 IMediaSource::Lock ()
3978 pthread_mutex_lock (&mutex
);
3982 IMediaSource::Unlock ()
3984 pthread_mutex_unlock (&mutex
);
3988 IMediaSource::ReadSome (void *buf
, guint32 n
)
3992 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf
, n
);
3996 result
= ReadInternal (buf
, n
);
3998 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %" G_GINT64_FORMAT
"\n", GET_OBJ_ID (this), buf
, n
, result
, GetPosition ());
4006 IMediaSource::ReadAll (void *buf
, guint32 n
)
4009 gint64 prev
= GetPosition ();
4010 gint64 avail
= GetLastAvailablePosition ();
4012 //printf ("IMediaSource::ReadAll (%p, %u), position: %" G_GINT64_FORMAT "\n", buf, n, prev);
4014 read
= ReadSome (buf
, n
);
4016 if ((gint64
) read
!= (gint64
) n
) {
4017 FileSource
*fs
= NULL
;
4019 if (GetType () == MediaSourceTypeFile
)
4020 fs
= (FileSource
*) this;
4021 g_warning ("IMediaSource::ReadInternal (%i): Read failed, read %i bytes. available size: %" G_GINT64_FORMAT
", size: %" G_GINT64_FORMAT
", pos: %" G_GINT64_FORMAT
", prev pos: %" G_GINT64_FORMAT
", position not available: %" G_GINT64_FORMAT
", feof: %i, ferror: %i, strerror: %s\n",
4022 n
, read
, avail
, GetSize (), GetPosition (), prev
, prev
+ n
, fs
? feof (fs
->fd
) : -1, fs
? ferror (fs
->fd
) : -1, fs
? strerror (ferror (fs
->fd
)) : "<N/A>");
4023 print_stack_trace ();
4026 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf
, n
, read
);
4028 return (gint64
) read
== (gint64
) n
;
4032 IMediaSource::Peek (void *buf
, guint32 n
)
4039 read
= PeekInternal (buf
, n
);
4040 result
= read
== (gint64
) n
;
4044 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %" G_GINT64_FORMAT
" bytes.\n", buf
, n
, result
, read
);
4050 IMediaSource::Seek (gint64 offset
, int mode
)
4052 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%" G_GINT64_FORMAT
", %d = %s)\n",
4053 GET_OBJ_ID (this), ToString (), offset
, mode
, mode
== SEEK_SET
? "SEEK_SET"
4054 : (mode
== SEEK_CUR
? "SEEK_CUR" : (mode
== SEEK_END
? "SEEK_END" : "<invalid value>")));
4058 result
= SeekInternal (offset
, mode
);
4064 IMediaSource::IsPositionAvailable (gint64 position
, bool *eof
)
4066 gint64 available
= GetLastAvailablePosition ();
4067 gint64 size
= GetSize ();
4071 if (size
!= -1 && size
< position
) {
4072 // Size is known and smaller than the requested position
4077 if (available
!= -1 && available
< position
) {
4078 // Not everything is available and the available position is smaller than the requested position
4083 if (size
== -1 && available
== -1) {
4084 // Size is not known, but everything is available??
4085 // This is probably due to a bug in the derived *Source class
4087 fprintf (stderr
, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
4095 IMediaSource::GetLastAvailablePosition ()
4099 result
= GetLastAvailablePositionInternal ();
4105 IMediaSource::GetPositionInternal ()
4107 // This method should be overridden (or never called for the classes which doesn't override it).
4108 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4109 print_stack_trace ();
4114 IMediaSource::SeekInternal (gint64 offset
, int mode
)
4116 g_warning ("IMediaSource (%s)::SeekInternal (%" G_GINT64_FORMAT
", %i): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), offset
, mode
);
4117 print_stack_trace ();
4123 IMediaSource::ReadInternal (void *buffer
, guint32 n
)
4125 g_warning ("IMediaSource (%s)::ReadInternal (%p, %u): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), buffer
, n
);
4126 print_stack_trace ();
4132 IMediaSource::PeekInternal (void *buffer
, guint32 n
)
4134 g_warning ("IMediaSource (%s)::PeekInternal (%p, %u): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), buffer
, n
);
4135 print_stack_trace ();
4141 IMediaSource::GetSizeInternal ()
4143 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
4144 print_stack_trace ();
4150 IMediaSource::GetPosition ()
4154 result
= GetPositionInternal ();
4160 IMediaSource::GetSize ()
4164 result
= GetSizeInternal ();
4174 IMediaDemuxer::SetStreams (IMediaStream
** streams
, int count
)
4176 this->streams
= streams
;
4177 this->stream_count
= count
;
4179 for (int i
= 0; i
< count
; i
++)
4180 this->streams
[i
]->ref ();
4184 IMediaDemuxer::AddStream (IMediaStream
*stream
)
4186 g_return_val_if_fail (stream
!= NULL
, -1);
4189 streams
= (IMediaStream
**) g_realloc (streams
, stream_count
* sizeof (IMediaStream
*));
4190 streams
[stream_count
- 1] = stream
;
4193 return stream_count
- 1;
4200 IMediaDecoder::IMediaDecoder (Type::Kind kind
, Media
*media
, IMediaStream
*stream
) : IMediaObject (kind
, media
)
4202 this->stream
= NULL
;
4204 g_return_if_fail (stream
!= NULL
);
4206 this->stream
= stream
;
4207 this->stream
->ref ();
4211 input_ended
= false;
4215 IMediaDecoder::Dispose ()
4217 if (stream
!= NULL
) {
4218 IMediaStream
*s
= stream
;
4227 IMediaObject::Dispose ();
4231 IMediaDecoder::ReportSeekCompleted ()
4234 input_ended
= false;
4239 IMediaDecoder::ReportInputEnded ()
4242 if (IsDecoderQueueEmpty ()) {
4248 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame
*frame
)
4250 IMediaDemuxer
*demuxer
;
4251 IMediaStream
*stream
;
4252 Media
*media
= NULL
;
4254 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
4256 g_return_if_fail (frame
!= NULL
);
4258 media
= GetMediaReffed ();
4259 g_return_if_fail (media
!= NULL
);
4261 stream
= frame
->stream
;
4265 frame
->stream
->EnqueueFrame (frame
);
4267 demuxer
= stream
->GetDemuxerReffed ();
4268 if (demuxer
!= NULL
) {
4269 demuxer
->FillBuffers ();
4273 if (input_ended
&& IsDecoderQueueEmpty ())
4282 IMediaDecoder::DecodeFrameCallback (MediaClosure
*closure
)
4285 IMediaDecoder
*decoder
= (IMediaDecoder
*) closure
->GetContext ();
4286 IMediaDecoder::FrameNode
*node
= (IMediaDecoder::FrameNode
*) decoder
->queue
.Pop ();
4289 decoder
->DecodeFrameAsync (node
->frame
, false);
4293 return MEDIA_SUCCESS
;
4297 IMediaDecoder::DecodeFrameAsync (MediaFrame
*frame
, bool enqueue_always
)
4301 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame
, (frame
&& frame
->stream
) ? frame
->stream
->GetStreamTypeName () : NULL
);
4306 g_return_if_fail (frame
!= NULL
);
4308 media
= GetMediaReffed ();
4310 g_return_if_fail (media
!= NULL
);
4312 if (enqueue_always
|| !Media::InMediaThread ()) {
4313 MediaClosure
*closure
= new MediaClosure (media
, DecodeFrameCallback
, this, "IMediaDecoder::DecodeFrameCallback");
4314 queue
.Push (new FrameNode (frame
));
4315 media
->EnqueueWork (closure
);
4320 DecodeFrameAsyncInternal (frame
);
4327 IMediaDecoder::OpenDecoderAsync ()
4329 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4331 g_return_if_fail (opening
== false);
4332 g_return_if_fail (opened
== false);
4335 OpenDecoderAsyncInternal ();
4339 IMediaDecoder::ReportOpenDecoderCompleted ()
4341 Media
*media
= GetMediaReffed ();
4343 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4348 g_return_if_fail (media
!= NULL
);
4350 media
->ReportOpenDecoderCompleted (this);
4358 IImageConverter::IImageConverter (Type::Kind kind
, Media
*media
, VideoStream
*stream
) : IMediaObject (kind
, media
)
4360 output_format
= MoonPixelFormatNone
;
4361 input_format
= MoonPixelFormatNone
;
4362 this->stream
= stream
;
4369 VideoStream::VideoStream (Media
*media
) : IMediaStream (Type::VIDEOSTREAM
, media
)
4372 bits_per_sample
= 0;
4379 VideoStream::VideoStream (Media
*media
, int codec_id
, guint32 width
, guint32 height
, guint64 duration
, gpointer extra_data
, guint32 extra_data_size
)
4380 : IMediaStream (Type::VIDEOSTREAM
, media
)
4383 bits_per_sample
= 0;
4386 this->height
= height
;
4387 this->width
= width
;
4388 this->duration
= duration
;
4389 this->codec_id
= codec_id
;
4390 this->codec
= CreateCodec (codec_id
);
4391 this->extra_data
= extra_data
;
4392 this->extra_data_size
= extra_data_size
;
4395 VideoStream::~VideoStream ()
4400 VideoStream::Dispose ()
4403 converter
->Dispose ();
4404 converter
->unref ();
4407 IMediaStream::Dispose ();
4411 * MediaMarkerFoundClosure
4414 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media
*media
, MediaCallback
*callback
, MediaElement
*context
)
4415 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE
, media
, callback
, context
)
4421 MediaMarkerFoundClosure::Dispose ()
4427 MediaClosure::Dispose ();
4431 MediaMarkerFoundClosure::SetMarker (MediaMarker
*marker
)
4434 this->marker
->unref ();
4435 this->marker
= marker
;
4437 this->marker
->ref ();
4444 MediaMarker::MediaMarker (const char *type
, const char *text
, guint64 pts
)
4445 : EventObject (Type::MEDIAMARKER
)
4447 this->type
= g_strdup (type
);
4448 this->text
= g_strdup (text
);
4452 MediaMarker::~MediaMarker ()
4462 MarkerStream::MarkerStream (Media
*media
) : IMediaStream (Type::MARKERSTREAM
, media
)
4468 MarkerStream::Dispose ()
4475 IMediaStream::Dispose ();
4479 MarkerStream::MarkerFound (MediaFrame
*frame
)
4481 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4483 if (GetDecoder () == NULL
) {
4484 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4488 GetDecoder ()->DecodeFrameAsync (frame
, false);
4492 MarkerStream::FrameEnqueued ()
4496 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4498 frame
= PopFrame ();
4500 if (frame
== NULL
) {
4501 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4505 if (closure
!= NULL
) {
4506 closure
->SetMarker (frame
->marker
);
4508 closure
->SetMarker (NULL
);
4510 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4512 list
.Append (new MediaMarker::Node (frame
->marker
));
4520 MarkerStream::Pop ()
4522 MediaMarker
*result
= NULL
;
4523 MediaMarker::Node
*node
;
4526 node
= (MediaMarker::Node
*) list
.First ();
4528 result
= node
->marker
;
4538 MarkerStream::SetCallback (MediaMarkerFoundClosure
*closure
)
4541 this->closure
->unref ();
4542 this->closure
= closure
;
4544 this->closure
->ref ();
4550 MediaWork::MediaWork (MediaClosure
*c
)
4552 g_return_if_fail (c
!= NULL
);
4558 MediaWork::~MediaWork ()
4560 g_return_if_fail (closure
!= NULL
);
4567 * PassThroughDecoderInfo
4571 PassThroughDecoderInfo::Supports (const char *codec
)
4573 const char *video_fourccs
[] = { "yv12", "rgba", NULL
};
4574 const char *audio_fourccs
[] = { "pcm", NULL
};
4576 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4577 if (!strcmp (codec
, video_fourccs
[i
]))
4580 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4581 if (!strcmp (codec
, audio_fourccs
[i
]))
4588 * PassThroughDecoder
4591 PassThroughDecoder::PassThroughDecoder (Media
*media
, IMediaStream
*stream
)
4592 : IMediaDecoder (Type::PASSTHROUGHDECODER
, media
, stream
)
4597 PassThroughDecoder::Dispose ()
4599 IMediaDecoder::Dispose ();
4603 PassThroughDecoder::OpenDecoderAsyncInternal ()
4605 const char *fourcc
= GetStream ()->GetCodec ();
4607 if (!strcmp (fourcc
, "yv12")) {
4608 SetPixelFormat (MoonPixelFormatYUV420P
);
4609 } else if (!strcmp (fourcc
, "rgba")) {
4610 SetPixelFormat (MoonPixelFormatRGBA32
);
4611 } else if (!strcmp (fourcc
, "pcm")) {
4612 // nothing to do here
4614 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc
));
4618 ReportOpenDecoderCompleted ();
4622 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4624 frame
->AddState (MediaFrameDecoded
);
4625 if (GetPixelFormat () == MoonPixelFormatYUV420P
) {
4626 VideoStream
*vs
= (VideoStream
*) GetStream ();
4628 frame
->width
= vs
->width
;
4629 frame
->height
= vs
->height
;
4631 frame
->data_stride
[0] = frame
->buffer
;
4632 frame
->data_stride
[1] = frame
->buffer
+ (frame
->width
*frame
->height
);
4633 frame
->data_stride
[2] = frame
->buffer
+ (frame
->width
*frame
->height
)+(frame
->width
/2*frame
->height
/2);
4634 frame
->buffer
= NULL
;
4635 frame
->srcStride
[0] = frame
->width
;
4636 frame
->srcSlideY
= frame
->width
;
4637 frame
->srcSlideH
= frame
->height
;
4639 frame
->AddState (MediaFramePlanar
);
4641 ReportDecodeFrameCompleted (frame
);
4649 NullDecoderInfo::Supports (const char *codec
)
4651 const char *video_fourccs
[] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL
};
4652 const char *audio_fourccs
[] = { "wmav1","wmav2", "wmav3", "mp3", NULL
};
4654 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4655 if (!strcmp (codec
, video_fourccs
[i
]))
4658 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4659 if (!strcmp (codec
, audio_fourccs
[i
]))
4670 NullDecoder::NullDecoder (Media
*media
, IMediaStream
*stream
) : IMediaDecoder (Type::NULLDECODER
, media
, stream
)
4674 prev_pts
= G_MAXUINT64
;
4678 NullDecoder::Dispose ()
4683 IMediaDecoder::Dispose ();
4687 NullDecoder::DecodeVideoFrame (MediaFrame
*frame
)
4689 // free encoded buffer and alloc a new one for our image
4690 g_free (frame
->buffer
);
4691 frame
->buflen
= logo_size
;
4692 frame
->buffer
= (guint8
*) g_malloc (frame
->buflen
);
4693 memcpy (frame
->buffer
, logo
, frame
->buflen
);
4694 frame
->AddState (MediaFrameDecoded
);
4696 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4698 return MEDIA_SUCCESS
;
4702 NullDecoder::DecodeAudioFrame (MediaFrame
*frame
)
4704 AudioStream
*as
= (AudioStream
*) GetStream ();
4709 // discard encoded data
4710 g_free (frame
->buffer
);
4712 // We have no idea here how long the encoded audio data is
4713 // for the first frame we use 0.1 seconds, for the rest
4714 // we calculate the time since the last frame
4716 if (prev_pts
== G_MAXUINT64
|| frame
->pts
<= prev_pts
) {
4717 samples
= as
->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4719 diff_pts
= frame
->pts
- prev_pts
;
4720 samples
= (float) as
->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT
/ (float) diff_pts
);
4722 prev_pts
= frame
->pts
;
4724 data_size
= samples
* as
->GetChannels () * 2 /* 16 bit audio */;
4726 frame
->buflen
= data_size
;
4727 frame
->buffer
= (guint8
*) g_malloc0 (frame
->buflen
);
4729 frame
->AddState (MediaFrameDecoded
);
4731 return MEDIA_SUCCESS
;
4735 NullDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4737 MediaResult result
= MEDIA_FAIL
;
4738 IMediaStream
*stream
= GetStream ();
4740 if (stream
->GetType () == MediaTypeAudio
) {
4741 result
= DecodeAudioFrame (frame
);
4742 } else if (stream
->GetType () == MediaTypeVideo
) {
4743 result
= DecodeVideoFrame (frame
);
4746 if (MEDIA_SUCCEEDED (result
)) {
4747 ReportDecodeFrameCompleted (frame
);
4749 ReportErrorOccurred (result
);
4754 NullDecoder::OpenDecoderAsyncInternal ()
4757 IMediaStream
*stream
= GetStream ();
4759 if (stream
->GetType () == MediaTypeAudio
)
4760 result
= OpenAudio ();
4761 else if (stream
->GetType () == MediaTypeVideo
)
4762 result
= OpenVideo ();
4764 result
= MEDIA_FAIL
;
4766 if (MEDIA_SUCCEEDED (result
)) {
4767 ReportOpenDecoderCompleted ();
4769 ReportErrorOccurred (result
);
4774 NullDecoder::OpenAudio ()
4776 return MEDIA_SUCCESS
;
4780 NullDecoder::OpenVideo ()
4782 VideoStream
*vs
= (VideoStream
*) GetStream ();
4783 guint32 dest_height
= vs
->height
;
4784 guint32 dest_width
= vs
->width
;
4787 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4788 extern const char moonlight_logo
[];
4789 const char *image
= moonlight_logo
;
4791 guint32 img_offset
= *((guint32
*)(image
+ 10));
4792 guint32 img_width
= *((guint32
*)(image
+ 18));
4793 guint32 img_height
= *((guint32
*)(image
+ 22));
4794 guint32 img_stride
= (img_width
* 3 + 3) & ~3; // in bytes
4795 guint32 img_i
, img_h
, img_w
;
4796 guint32 start_w
= (dest_width
-img_width
)/2;
4797 guint32 end_w
= start_w
+ img_width
;
4798 guint32 start_h
= (dest_height
-img_height
)/2;
4799 guint32 end_h
= start_h
+ img_height
;
4801 LOG_PIPELINE ("offset: %i, width: 0x%x = %i, height: 0x%x = %i, stride: %i\n", img_offset
, img_width
, img_width
, img_height
, img_height
, img_stride
);
4803 // create the buffer for our image
4804 logo_size
= dest_height
* dest_width
* 4;
4805 logo
= (guint8
*) g_malloc (logo_size
);
4806 memset (logo
, 0x00, logo_size
);
4808 // write our image centered into the destination rectangle, flipped horizontally
4810 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4811 for (guint32 dest_w
= 0; dest_w
< dest_width
; dest_w
++) {
4812 if (dest_w
>= start_w
&& dest_w
< end_w
&& dest_h
>= start_h
&& dest_h
< end_h
) {
4813 img_h
= (dest_h
- start_h
) % img_height
;
4814 img_w
= (dest_w
- start_w
) % img_width
;
4815 img_i
= img_h
* img_stride
+ img_w
* 3;
4817 logo
[logo_size
- dest_i
+ 0] = image
[img_offset
+ img_i
+ 0];
4818 logo
[logo_size
- dest_i
+ 1] = image
[img_offset
+ img_i
+ 1];
4819 logo
[logo_size
- dest_i
+ 2] = image
[img_offset
+ img_i
+ 2];
4821 logo
[logo_size
- dest_i
+ 3] = 0xff;
4827 // Flip the image vertically
4828 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4829 for (guint32 dest_w
= 0; dest_w
< dest_width
/ 2; dest_w
++) {
4831 guint32 a
= (dest_h
* dest_width
+ dest_w
) * 4;
4832 guint32 b
= (dest_h
* dest_width
+ dest_width
- dest_w
) * 4 - 4;
4833 for (guint32 c
= 0; c
< 3; c
++) {
4835 logo
[a
+ c
] = logo
[b
+ c
];
4841 SetPixelFormat (MoonPixelFormatRGB32
);
4843 return MEDIA_SUCCESS
;
4850 ExternalDemuxer::ExternalDemuxer (Media
*media
, void *instance
, CloseDemuxerCallback close_demuxer
,
4851 GetDiagnosticAsyncCallback get_diagnostic
, GetFrameAsyncCallback get_sample
, OpenDemuxerAsyncCallback open_demuxer
,
4852 SeekAsyncCallback seek
, SwitchMediaStreamAsyncCallback switch_media_stream
)
4853 : IMediaDemuxer (Type::EXTERNALDEMUXER
, media
)
4855 this->close_demuxer_callback
= close_demuxer
;
4856 this->get_diagnostic_async_callback
= get_diagnostic
;
4857 this->get_sample_async_callback
= get_sample
;
4858 this->open_demuxer_async_callback
= open_demuxer
;
4859 this->seek_async_callback
= seek
;
4860 this->switch_media_stream_async_callback
= switch_media_stream
;
4861 this->instance
= instance
;
4864 pthread_rwlock_init (&rwlock
, NULL
);
4866 g_return_if_fail (instance
!= NULL
);
4867 g_return_if_fail (close_demuxer
!= NULL
&& get_diagnostic
!= NULL
&& get_sample
!= NULL
&& open_demuxer
!= NULL
&& seek
!= NULL
&& switch_media_stream
!= NULL
);
4870 ExternalDemuxer::~ExternalDemuxer ()
4872 pthread_rwlock_destroy (&rwlock
);
4876 ExternalDemuxer::Dispose ()
4879 IMediaDemuxer::Dispose ();
4883 ExternalDemuxer::ClearCallbacks ()
4885 pthread_rwlock_wrlock (&rwlock
);
4886 close_demuxer_callback
= NULL
;
4887 get_diagnostic_async_callback
= NULL
;
4888 get_sample_async_callback
= NULL
;
4889 open_demuxer_async_callback
= NULL
;
4890 seek_async_callback
= NULL
;
4891 switch_media_stream_async_callback
= NULL
;
4893 pthread_rwlock_unlock (&rwlock
);
4897 ExternalDemuxer::SetCanSeek (bool value
)
4903 ExternalDemuxer::AddStream (IMediaStream
*stream
)
4905 return IMediaDemuxer::AddStream (stream
);
4909 ExternalDemuxer::CloseDemuxerInternal ()
4911 pthread_rwlock_rdlock (&rwlock
);
4912 if (close_demuxer_callback
!= NULL
) {
4913 close_demuxer_callback (instance
);
4916 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4919 pthread_rwlock_unlock (&rwlock
);
4923 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind
)
4925 pthread_rwlock_rdlock (&rwlock
);
4926 if (get_diagnostic_async_callback
!= NULL
) {
4927 get_diagnostic_async_callback (instance
, diagnosticsKind
);
4930 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4933 pthread_rwlock_unlock (&rwlock
);
4937 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
4939 g_return_if_fail (stream
!= NULL
);
4941 pthread_rwlock_rdlock (&rwlock
);
4942 if (get_sample_async_callback
!= NULL
) {
4943 get_sample_async_callback (instance
, stream
->GetStreamType ());
4946 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4949 pthread_rwlock_unlock (&rwlock
);
4953 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4955 pthread_rwlock_rdlock (&rwlock
);
4956 if (open_demuxer_async_callback
!= NULL
) {
4957 open_demuxer_async_callback (instance
, this);
4960 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4963 pthread_rwlock_unlock (&rwlock
);
4967 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime
)
4969 pthread_rwlock_rdlock (&rwlock
);
4970 if (seek_async_callback
!= NULL
) {
4971 seek_async_callback (instance
, seekToTime
);
4974 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4977 pthread_rwlock_unlock (&rwlock
);
4981 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*mediaStreamDescription
)
4983 g_return_if_fail (mediaStreamDescription
!= NULL
);
4985 pthread_rwlock_rdlock (&rwlock
);
4986 if (switch_media_stream_async_callback
!= NULL
) {
4987 switch_media_stream_async_callback (instance
, mediaStreamDescription
);
4990 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4993 pthread_rwlock_unlock (&rwlock
);
5001 AudioStream::AudioStream (Media
*media
)
5002 : IMediaStream (Type::AUDIOSTREAM
, media
)
5006 AudioStream::AudioStream (Media
*media
, int codec_id
, int bits_per_sample
, int block_align
, int sample_rate
, int channels
, int bit_rate
, gpointer extra_data
, guint32 extra_data_size
)
5007 : IMediaStream (Type::AUDIOSTREAM
, media
)
5009 this->codec_id
= codec_id
;
5010 this->codec
= CreateCodec (codec_id
);
5011 this->extra_data
= extra_data
;
5012 this->extra_data_size
= extra_data_size
;
5013 input_bits_per_sample
= bits_per_sample
;
5014 output_bits_per_sample
= bits_per_sample
;
5015 input_block_align
= block_align
;
5016 output_block_align
= block_align
;
5017 input_sample_rate
= sample_rate
;
5018 output_sample_rate
= sample_rate
;
5019 input_channels
= channels
;
5020 output_channels
= channels
;
5021 input_bit_rate
= bit_rate
;
5022 output_bit_rate
= bit_rate
;
5029 ExternalDecoder::ExternalDecoder (Media
*media
, IMediaStream
*stream
, void *instance
, const char *name
,
5030 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async
,
5031 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async
,
5032 ExternalDecoder_CleanupCallback cleanup
,
5033 ExternalDecoder_CleanStateCallback clean_state
,
5034 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame
,
5035 ExternalDecoder_DisposeCallback dispose
,
5036 ExternalDecoder_DtorCallback dtor
)
5037 : IMediaDecoder (Type::EXTERNALDECODER
, media
, stream
)
5039 this->instance
= instance
;
5040 this->name
= g_strdup (name
);
5041 this->decode_frame_async
= decode_frame_async
;
5042 this->open_decoder_async
= open_decoder_async
;
5043 this->cleanup
= cleanup
;
5044 this->clean_state
= clean_state
;
5045 this->has_delayed_frame
= has_delayed_frame
;
5046 this->dispose
= dispose
;
5050 ExternalDecoder::~ExternalDecoder ()
5057 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
5059 decode_frame_async (instance
, frame
);
5063 ExternalDecoder::OpenDecoderAsyncInternal ()
5065 open_decoder_async (instance
);
5069 ExternalDecoder::Dispose ()
5073 IMediaDecoder::Dispose ();
5077 ExternalDecoder::Cleanup (MediaFrame
*frame
)
5079 cleanup (instance
, frame
);
5083 ExternalDecoder::CleanState ()
5085 clean_state (instance
);
5089 ExternalDecoder::HasDelayedFrame ()
5091 return has_delayed_frame (instance
);
5095 ExternalDecoder::InputEnded ()
5097 GetStream ()->SetOutputEnded (true);
5101 * ExternalDecoderInfo
5104 ExternalDecoderInfo::ExternalDecoderInfo (void *instance
, const char *name
, ExternalDecoderInfo_SupportsCallback supports
, ExternalDecoderInfo_Create create
, ExternalDecoderInfo_dtor dtor
)
5106 this->instance
= instance
;
5107 this->supports
= supports
;
5108 this->create
= create
;
5110 this->name
= g_strdup (name
);
5114 ExternalDecoderInfo::Supports (const char *codec
)
5116 return supports (instance
, codec
);
5120 ExternalDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
5122 return create (instance
, media
, stream
);
5125 ExternalDecoderInfo::~ExternalDecoderInfo ()