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>
25 #include "codec-version.h"
26 #include "pipeline-ffmpeg.h"
30 #include "mediaelement.h"
32 #include "asf/asf-structures.h"
33 #include "yuv-converter.h"
35 #include "mms-downloader.h"
36 #include "pipeline-ui.h"
37 #include "pipeline-asf.h"
39 #include "deployment.h"
40 #include "timesource.h"
46 bool Media::registering_ms_codecs
= false;
47 bool Media::registered_ms_codecs
= false;
49 DemuxerInfo
*Media::registered_demuxers
= NULL
;
50 DecoderInfo
*Media::registered_decoders
= NULL
;
51 ConverterInfo
*Media::registered_converters
= NULL
;
53 Media::Media (PlaylistRoot
*root
)
54 : IMediaObject (Type::MEDIA
, this)
56 LOG_PIPELINE ("Media::Media (), id: %i\n", GET_OBJ_ID (this));
71 error_reported
= false;
72 buffering_enabled
= false;
73 in_open_internal
= false;
75 download_progress
= 0.0;
76 buffering_progress
= 0.0;
78 if (!GetDeployment ()->RegisterMedia (this))
84 LOG_PIPELINE ("Media::~Media (), id: %i\n", GET_OBJ_ID (this));
92 bool was_disposed
= false;
94 LOG_PIPELINE ("Media::Dispose (), id: %i\n", GET_OBJ_ID (this));
97 was_disposed
= is_disposed
;
102 * Don't run our dispose code more than once, we may end up deleted on the main thread
103 * which will cause Dispose to be called on the main thread too. Since Dispose must already
104 * have been called on the media thread, we're safe.
107 IMediaObject::Dispose ();
112 if (!MediaThreadPool::IsThreadPoolThread ()) {
113 g_warning ("Media::Dispose (): Not in thread-pool thread, and we haven't been disposed already.\n");
120 * We're on a media thread, and there is no other work in the queue: we can ensure that nothing
121 * more will ever execute on the media thread related to this Media instance.
137 this->demuxer
= NULL
;
146 IMediaObject::Dispose ();
148 GetDeployment ()->UnregisterMedia (this);
152 Media::IsMSCodecsInstalled ()
154 return registered_ms_codecs
;
158 Media::RegisterMSCodecs (void)
162 char *libmscodecs_path
= NULL
;
163 const char *functions
[] = {"register_codec_pack", NULL
};
164 const gchar
*home
= g_get_home_dir ();
165 registering_ms_codecs
= true;
167 if (!(moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
)) {
168 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
173 libmscodecs_path
= g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME
, NULL
);
175 if (!(g_file_test (libmscodecs_path
, G_FILE_TEST_EXISTS
) && g_file_test (libmscodecs_path
, G_FILE_TEST_IS_REGULAR
))) {
176 if (libmscodecs_path
)
177 g_free (libmscodecs_path
);
178 libmscodecs_path
= g_strdup (CODEC_LIBRARY_NAME
);
181 dl
= dlopen (libmscodecs_path
, RTLD_LAZY
);
183 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path
);
185 for (int i
= 0; functions
[i
] != NULL
; i
++) {
186 reg
= (register_codec
) dlsym (dl
, functions
[i
]);
188 (*reg
) (MOONLIGHT_CODEC_ABI_VERSION
);
190 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions
[i
], libmscodecs_path
);
193 registered_ms_codecs
= true;
195 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path
, dlerror ());
197 g_free (libmscodecs_path
);
199 registering_ms_codecs
= false;
203 Media::SetBufferingEnabled (bool value
)
205 buffering_enabled
= value
;
210 Media::SetBufferingTime (guint64 buffering_time
)
213 this->buffering_time
= buffering_time
;
217 demuxer
->FillBuffers ();
221 Media::GetBufferingTime ()
225 result
= buffering_time
;
231 Media::GetPlaylistRoot ()
240 markers
= new List ();
246 Media::RegisterDemuxer (DemuxerInfo
*info
)
248 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
250 if (registered_demuxers
== NULL
) {
251 registered_demuxers
= info
;
253 MediaInfo
* current
= registered_demuxers
;
254 while (current
->next
!= NULL
)
255 current
= current
->next
;
256 current
->next
= info
;
261 Media::RegisterConverter (ConverterInfo
*info
)
263 //printf ("Media::RegisterConverter (%p)\n", info);
265 if (registered_converters
== NULL
) {
266 registered_converters
= info
;
268 MediaInfo
*current
= registered_converters
;
269 while (current
->next
!= NULL
)
270 current
= current
->next
;
271 current
->next
= info
;
276 Media::RegisterDecoder (DecoderInfo
*info
)
280 //printf ("Media::RegisterDecoder (%p)\n", info);
282 if (registered_decoders
== NULL
) {
283 registered_decoders
= info
;
285 if (registering_ms_codecs
) {
286 // MS codecs might get registered after all other codecs (right after installing them),
287 // which means after the null codecs so if they don't get special treatment, they won't
288 // get used until the next browser restart (when they're registered normally).
289 // So instead of appending them, we prepend them.
290 info
->next
= registered_decoders
;
291 registered_decoders
= info
;
293 current
= registered_decoders
;
294 while (current
->next
!= NULL
)
295 current
= current
->next
;
296 current
->next
= info
;
299 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info
->GetName ());
305 LOG_PIPELINE ("Media::Initialize ()\n");
308 Media::RegisterDemuxer (new ASFDemuxerInfo ());
309 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
310 Media::RegisterDemuxer (new ASXDemuxerInfo ());
313 if (!(moonlight_flags
& RUNTIME_INIT_FFMPEG_YUV_CONVERTER
))
314 Media::RegisterConverter (new YUVConverterInfo ());
317 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
318 if (moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
) {
321 #ifdef INCLUDE_FFMPEG
322 if (!(moonlight_flags
& RUNTIME_INIT_DISABLE_FFMPEG_CODECS
)) {
327 Media::RegisterDecoder (new PassThroughDecoderInfo ());
328 Media::RegisterDecoder (new NullDecoderInfo ());
330 MediaThreadPool::Initialize ();
336 LOG_PIPELINE ("Media::Shutdown ()\n");
341 // Make sure all threads are stopped
342 AudioPlayer::Shutdown ();
343 MediaThreadPool::Shutdown ();
345 current
= registered_decoders
;
346 while (current
!= NULL
) {
347 next
= current
->next
;
351 registered_decoders
= NULL
;
353 current
= registered_demuxers
;
354 while (current
!= NULL
) {
355 next
= current
->next
;
359 registered_demuxers
= NULL
;
361 current
= registered_converters
;
362 while (current
!= NULL
) {
363 next
= current
->next
;
367 registered_converters
= NULL
;
369 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
373 Media::Warning (MediaResult result
, const char *format
, ...)
377 if (MEDIA_SUCCEEDED (result
))
380 fprintf (stderr
, "Moonlight: MediaResult = %d; ", result
);
382 va_start (args
, format
);
383 vfprintf (stderr
, format
, args
);
386 fputc ('\n', stderr
);
390 Media::InMediaThread ()
392 return MediaThreadPool::IsThreadPoolThread ();
396 Media::ReportBufferingProgress (double progress
)
398 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress
, buffering_progress
);
400 progress
= MAX (MIN (progress
, 1.0), 0.0);
402 if (progress
== buffering_progress
)
405 if (progress
< buffering_progress
|| progress
> (buffering_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
406 buffering_progress
= progress
;
407 EmitSafe (BufferingProgressChangedEvent
, new ProgressEventArgs (progress
));
412 Media::ReportDownloadProgress (double progress
)
414 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress
, download_progress
);
416 progress
= MAX (MIN (progress
, 1.0), 0.0);
418 if (progress
<= download_progress
) {
420 * Download progress percentage can actually go down - if the file size
421 * goes up. Yes, the file size can go up.
426 if (progress
> (download_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
427 download_progress
= progress
;
428 EmitSafe (DownloadProgressChangedEvent
, new ProgressEventArgs (progress
));
433 Media::SeekAsync (guint64 pts
)
435 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
437 if (demuxer
== NULL
) {
438 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
442 demuxer
->SeekAsync (pts
);
446 Media::ReportSeekCompleted (guint64 pts
)
448 LOG_PIPELINE ("Media::ReportSeekCompleted (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
450 buffering_progress
= 0;
452 EmitSafe (SeekCompletedEvent
);
456 Media::ReportOpenCompleted ()
458 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
460 EmitSafe (OpenCompletedEvent
);
464 Media::ReportOpenDemuxerCompleted ()
466 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
472 Media::ReportOpenDecoderCompleted (IMediaDecoder
*decoder
)
474 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder
, GET_OBJ_ID (this));
476 g_return_if_fail (decoder
!= NULL
);
482 Media::ReportErrorOccurred (ErrorEventArgs
*args
)
484 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args
, args
== NULL
? NULL
: args
->GetErrorMessage());
487 fprintf (stderr
, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args
->GetErrorType()), args
->GetErrorCode(), args
->GetErrorMessage(), args
->GetExtendedMessage());
489 fprintf (stderr
, "Moonlight: Unspecified media error.\n");
492 if (!error_reported
) {
493 error_reported
= true;
494 EmitSafe (MediaErrorEvent
, args
);
499 Media::ReportErrorOccurred (const char *message
)
501 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message
);
503 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 3001, message
)));
507 Media::ReportErrorOccurred (MediaResult result
)
509 char *msg
= g_strdup_printf ("Media error: %i.", result
);
510 ReportErrorOccurred (msg
);
517 LOG_PIPELINE ("Media::PlayAsync ()\n");
519 MediaClosure
*closure
= new MediaClosure (this, PlayCallback
, this, "Media::PlayAsync");
520 EnqueueWork (closure
);
527 LOG_PIPELINE ("Media::PauseAsync ()\n");
533 LOG_PIPELINE ("Media::StopAsync ()\n");
535 MediaClosure
*closure
= new MediaClosure (this, StopCallback
, this, "Media::StopAsync");
536 EnqueueWork (closure
);
541 Media::StopCallback (MediaClosure
*closure
)
543 closure
->GetMedia ()->Stop ();
544 return MEDIA_SUCCESS
;
548 Media::PlayCallback (MediaClosure
*closure
)
550 closure
->GetMedia ()->Play ();
551 return MEDIA_SUCCESS
;
557 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
559 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
563 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
567 demuxer
->ClearBuffers ();
573 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
575 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
579 demuxer
->FillBuffers ();
583 Media::Initialize (Downloader
*downloader
, const char *PartName
)
585 IMediaSource
*source
;
587 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader
, PartName
, GET_OBJ_ID (this));
589 g_return_if_fail (downloader
!= NULL
);
590 g_return_if_fail (file
== NULL
);
591 g_return_if_fail (uri
!= NULL
|| PartName
!= NULL
);
592 g_return_if_fail (initialized
== false);
593 g_return_if_fail (error_reported
== false);
594 g_return_if_fail (this->source
== NULL
);
596 if (downloader
->Completed ()) {
597 file
= downloader
->GetDownloadedFilename (PartName
);
600 ReportErrorOccurred ("Couldn't get downloaded filename.");
605 if (file
== NULL
&& PartName
!= NULL
&& PartName
[0] != 0) {
606 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)");
611 InternalDownloader
*idl
= downloader
->GetInternalDownloader ();
612 MmsDownloader
*mms_dl
= (idl
&& idl
->GetObjectType () == Type::MMSDOWNLOADER
) ? (MmsDownloader
*) idl
: NULL
;
614 if (mms_dl
== NULL
) {
615 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
619 source
= new MmsSource (this, downloader
);
621 source
= new FileSource (this, file
);
629 Media::Initialize (IMediaSource
*source
)
633 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source
, GET_OBJ_ID (this));
635 g_return_if_fail (source
!= NULL
);
636 g_return_if_fail (this->source
== NULL
);
637 g_return_if_fail (initialized
== false);
639 result
= source
->Initialize ();
640 if (!MEDIA_SUCCEEDED (result
)) {
641 ReportErrorOccurred (result
);
646 this->source
= source
;
647 this->source
->ref ();
651 Media::Initialize (const char *uri
)
654 IMediaSource
*source
= NULL
;
656 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri
, GET_OBJ_ID (this));
658 g_return_if_fail (uri
!= NULL
);
659 g_return_if_fail (file
== NULL
);
660 g_return_if_fail (uri
!= NULL
);
661 g_return_if_fail (initialized
== false);
662 g_return_if_fail (error_reported
== false);
663 g_return_if_fail (source
== NULL
);
664 g_return_if_fail (this->source
== NULL
);
666 this->uri
= g_strdup (uri
);
669 if (g_str_has_prefix (uri
, "mms://") || g_str_has_prefix (uri
, "rtsp://") || g_str_has_prefix (uri
, "rtsps://")) {
670 dl
= Surface::CreateDownloader (this);
672 ReportErrorOccurred ("Couldn't create downloader.");
676 dl
->Open ("GET", uri
, StreamingPolicy
);
678 if (dl
->GetFailedMessage () == NULL
) {
679 Initialize (dl
, NULL
);
681 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
682 MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
690 source
= new ProgressiveSource (this, uri
);
696 Media::Initialize (IMediaDemuxer
*demuxer
)
698 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer
, GET_OBJ_ID (this));
700 g_return_if_fail (demuxer
!= NULL
);
701 g_return_if_fail (this->demuxer
== NULL
);
702 g_return_if_fail (initialized
== false);
704 this->demuxer
= demuxer
;
705 this->demuxer
->ref ();
712 Media::RetryHttp (ErrorEventArgs
*args
)
714 char *http_uri
= NULL
;
716 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri
);
718 g_return_if_fail (uri
!= NULL
);
719 g_return_if_fail (source
!= NULL
);
722 ReportErrorOccurred (args
);
726 // CHECK: If the current protocolo is rtsps, should we retry http or https?
728 if (g_str_has_prefix (uri
, "mms://")) {
729 http_uri
= g_strdup_printf ("http://%s", uri
+ 6);
730 } else if (g_str_has_prefix (uri
, "rtsp://")) {
731 http_uri
= g_strdup_printf ("http://%s", uri
+ 7);
732 } else if (g_str_has_prefix (uri
, "rtsps://")) {
733 http_uri
= g_strdup_printf ("http://%s", uri
+ 8);
735 ReportErrorOccurred (args
);
741 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri
);
745 /* this method is called on the main thread, ensure Dispose is called on the source on the media thread */
746 DisposeObject (source
);
750 error_reported
= false;
752 Initialize (http_uri
);
763 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
765 g_return_if_fail (initialized
== true);
767 EmitSafe (OpeningEvent
);
769 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenAsync");
770 EnqueueWork (closure
);
775 Media::OpenInternal ()
777 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
779 g_return_if_fail (initialized
== true);
782 // This may happen due to the recursion detection below
783 // Example: we try open a demuxer, the demuxer opens successfully
784 // right away and calls ReportDemuxerOpenComplete which will call
785 // us. Due to the recursion detection we'll enqueue a call to
786 // OpenInternal, while the first OpenInternal may succeed and
787 // set opened to true.
788 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
792 // detect recursive calls.
794 if (in_open_internal
) {
795 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
796 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
797 EnqueueWork (closure
);
802 in_open_internal
= true;
807 if (!SelectDemuxerAsync ()) {
808 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
815 if (!SelectDecodersAsync ()) {
816 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
820 demuxer
->FillBuffers ();
825 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
827 EmitSafe (OpenCompletedEvent
);
830 in_open_internal
= false;
834 Media::OpenInternal (MediaClosure
*closure
)
836 Media
*media
= (Media
*) closure
->GetContext ();
838 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
840 media
->OpenInternal ();
842 return MEDIA_SUCCESS
;
846 Media::SelectDemuxerAsync ()
848 DemuxerInfo
*demuxerInfo
;
853 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);
855 g_return_val_if_fail (error_reported
== false, false);
856 g_return_val_if_fail (initialized
== true, false);
858 // Check if demuxer already is open
859 if (demuxer
!= NULL
) {
860 if (demuxer
->IsOpened ())
862 if (!demuxer
->IsOpening ())
863 demuxer
->OpenDemuxerAsync ();
864 return demuxer
->IsOpened ();
867 g_return_val_if_fail (source
!= NULL
, false);
869 // Check if the source knows how to create the demuxer
870 demuxer
= source
->CreateDemuxer (this);
872 if (demuxer
== NULL
) { // No demuxer created, we need to find it ourselves.
873 // Check if we have at least 1024 bytes or eof
874 if (!source
->IsPositionAvailable (16, &eof
)) {
876 // We need to try again later.
877 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
879 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
880 EnqueueWork (closure
, false);
888 demuxerInfo
= registered_demuxers
;
889 while (demuxer
== NULL
&& demuxerInfo
!= NULL
) {
890 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo
->GetName ());
891 support
= demuxerInfo
->Supports (source
);
893 if (support
== MEDIA_SUCCESS
)
898 if (result
== MEDIA_NOT_ENOUGH_DATA
) {
899 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 ());
901 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
902 EnqueueWork (closure
, false);
908 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo
->GetName ());
909 demuxerInfo
= (DemuxerInfo
*) demuxerInfo
->next
;
912 if (demuxerInfo
== NULL
) {
913 // No demuxer found, report an error
914 const char *source_name
= file
? file
: uri
;
917 switch (source
->GetType ()) {
918 case MediaSourceTypeProgressive
:
919 case MediaSourceTypeFile
:
920 source_name
= ((FileSource
*) source
)->GetFileName ();
922 case MediaSourceTypeMms
:
923 case MediaSourceTypeMmsEntry
:
924 source_name
= "live source";
927 source_name
= "unknown source";
931 char *msg
= g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name
);
932 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
933 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT"),
934 MEDIA_UNKNOWN_CODEC
, msg
));
940 demuxer
= demuxerInfo
->Create (this, source
);
942 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer
->GetTypeName ());
945 if (demuxer
->IsOpened ())
948 if (demuxer
->IsOpening ())
951 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
953 demuxer
->OpenDemuxerAsync ();
955 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
957 return demuxer
!= NULL
&& demuxer
->IsOpened ();
961 Media::SelectDecodersAsync ()
963 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
965 g_return_val_if_fail (error_reported
== false, false);
966 g_return_val_if_fail (initialized
== true, false);
968 if (demuxer
== NULL
) {
969 ReportErrorOccurred ("No demuxer to select decoders from.");
973 // If the demuxer has no streams (ASXDemuxer for instance)
974 // then just return success.
975 if (demuxer
->GetStreamCount () == 0)
978 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
980 // Select codecs for each stream
981 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
982 IMediaStream
*stream
= demuxer
->GetStream (i
);
985 if (stream
== NULL
) {
986 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
990 if (stream
->GetDecoder () != NULL
)
993 const char *codec
= stream
->GetCodec ();
994 IMediaDecoder
*decoder
= NULL
;
996 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec
);
998 DecoderInfo
*current_decoder
= registered_decoders
;
999 while (current_decoder
!= NULL
&& !current_decoder
->Supports (codec
)) {
1000 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder
->GetName (), codec
);
1001 current_decoder
= (DecoderInfo
*) current_decoder
->next
;
1004 if (current_decoder
== NULL
) {
1005 Media::Warning (MEDIA_UNKNOWN_CODEC
, "Unknown codec: '%s'.", codec
);
1009 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder
->GetName (), codec
);
1010 decoder
= current_decoder
->Create (this, stream
);
1012 stream
->SetDecoder (decoder
);
1020 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1022 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1023 IMediaStream
*stream
= demuxer
->GetStream (i
);
1024 IMediaDecoder
*decoder
;
1029 decoder
= stream
->GetDecoder ();
1031 if (decoder
== NULL
) {
1032 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
1033 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT")));
1037 if (decoder
->IsOpening () || decoder
->IsOpened ())
1040 decoder
->OpenDecoderAsync ();
1046 // Wait until all the codecs have opened
1047 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1049 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1050 IMediaStream
*stream
= demuxer
->GetStream (i
);
1051 IMediaDecoder
*decoder
;
1056 decoder
= stream
->GetDecoder ();
1058 if (decoder
== NULL
) {
1059 ReportErrorOccurred (MEDIA_FAIL
);
1063 if (decoder
->IsOpening ()) {
1064 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
1065 EnqueueWork (closure
, false);
1070 if (!decoder
->IsOpened ()) {
1071 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1072 ReportErrorOccurred (MEDIA_FAIL
);
1078 // All the codecs have been opened now.
1079 // Find converters for each of them (whenever required).
1081 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1083 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1084 IMediaStream
*stream
= demuxer
->GetStream (i
);
1085 IMediaDecoder
*decoder
;
1090 decoder
= stream
->GetDecoder ();
1092 if (decoder
== NULL
) {
1093 ReportErrorOccurred (MEDIA_FAIL
);
1097 if (stream
->GetType () != MediaTypeVideo
)
1098 continue; // Only video streams need converters
1100 if (decoder
->GetPixelFormat () == MoonPixelFormatRGB32
|| decoder
->GetPixelFormat () == MoonPixelFormatRGBA32
)
1101 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1103 // Select converter for this stream
1104 VideoStream
*vs
= (VideoStream
*) stream
;
1106 ConverterInfo
* current_conv
= registered_converters
;
1107 while (current_conv
!= NULL
&& !current_conv
->Supports (decoder
->GetPixelFormat (), MoonPixelFormatRGB32
)) {
1108 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1109 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1110 current_conv
= (ConverterInfo
*) current_conv
->next
;
1114 if (current_conv
== NULL
) {
1115 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER
);
1116 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1117 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1121 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1122 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1124 vs
->converter
= current_conv
->Create (this, vs
);
1125 vs
->converter
->input_format
= decoder
->GetPixelFormat ();
1126 vs
->converter
->output_format
= MoonPixelFormatRGB32
;
1127 if (!MEDIA_SUCCEEDED (vs
->converter
->Open ())) {
1128 vs
->converter
->unref ();
1129 vs
->converter
= NULL
;
1130 ReportErrorOccurred (MEDIA_FAIL
);
1135 // Loop through all the streams, return true if at least one has a codec.
1137 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1138 IMediaStream
*stream
= demuxer
->GetStream (i
);
1143 if (stream
->GetDecoder () != NULL
)
1147 // No codecs found for no stream, report an error.
1148 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1153 Media::EnqueueWork (MediaClosure
*closure
, bool wakeup
)
1155 bool result
= false;
1158 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure
);
1160 g_return_val_if_fail (closure
!= NULL
, false);
1166 disposed
= this->is_disposed
;
1169 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed
);
1171 MediaThreadPool::AddWork (closure
, wakeup
);
1180 Media::DisposeObjectInternal (MediaClosure
*closure
)
1182 closure
->GetContext ()->Dispose ();
1183 return MEDIA_SUCCESS
;
1187 Media::DisposeObject (EventObject
*obj
)
1189 MediaDisposeObjectClosure
*closure
= new MediaDisposeObjectClosure (this, DisposeObjectInternal
, obj
);
1190 if (!EnqueueWork (closure
, true)) {
1191 LOG_PIPELINE ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj
);
1200 MediaThreadPool::WakeUp ();
1204 Media::ClearQueue ()
1206 LOG_PIPELINE ("Media::ClearQueue ().\n");
1207 MediaThreadPool::RemoveWork (this);
1214 ASXDemuxer::ASXDemuxer (Media
*media
, IMediaSource
*source
)
1215 : IMediaDemuxer (Type::ASXDEMUXER
, media
, source
)
1220 ASXDemuxer::~ASXDemuxer ()
1225 ASXDemuxer::Dispose ()
1231 IMediaDemuxer::Dispose ();
1235 ASXDemuxer::OpenDemuxerAsyncInternal ()
1239 ErrorEventArgs
*args
= NULL
;
1240 Media
*media
= GetMediaReffed ();
1242 g_return_if_fail (media
!= NULL
);
1244 root
= media
->GetPlaylistRoot ();
1246 g_return_if_fail (root
!= NULL
);
1248 PlaylistParser
*parser
= new PlaylistParser (root
, source
);
1250 if (MEDIA_SUCCEEDED (parser
->Parse ())) {
1251 result
= MEDIA_SUCCESS
;
1252 playlist
= parser
->GetPlaylist ();
1255 result
= MEDIA_FAIL
;
1256 args
= parser
->GetErrorEventArgs ();
1263 if (MEDIA_SUCCEEDED (result
)) {
1264 ReportOpenDemuxerCompleted ();
1265 } else if (args
!= NULL
) {
1266 args
->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1267 ReportErrorOccurred (args
);
1269 ReportErrorOccurred (result
);
1282 ASXDemuxerInfo::Supports (IMediaSource
*source
)
1284 if (PlaylistParser::IsASX2 (source
) || PlaylistParser::IsASX3 (source
)) {
1285 return MEDIA_SUCCESS
;
1292 ASXDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
1294 return new ASXDemuxer (media
, source
);
1298 * ManagedStreamSource
1301 ManagedStreamSource::ManagedStreamSource (Media
*media
, ManagedStreamCallbacks
*stream
) : IMediaSource (Type::MANAGEDSTREAMSOURCE
, media
)
1303 memcpy (&this->stream
, stream
, sizeof (this->stream
));
1306 ManagedStreamSource::~ManagedStreamSource ()
1308 stream
.handle
= NULL
;
1312 ManagedStreamSource::ReadInternal (void *buf
, guint32 n
)
1314 return stream
.Read (stream
.handle
, buf
, 0, n
);
1318 ManagedStreamSource::PeekInternal (void *buf
, guint32 n
)
1322 read
= stream
.Read (stream
.handle
, buf
, 0, n
);
1323 stream
.Seek (stream
.handle
, -read
, 1 /* SeekOrigin.Current */);
1328 ManagedStreamSource::SeekInternal (gint64 offset
, int mode
)
1330 stream
.Seek (stream
.handle
, offset
, mode
/* FIXME: check if mode values matches SeekOrigin values */);
1335 ManagedStreamSource::GetPositionInternal ()
1337 return stream
.Position (stream
.handle
);
1341 ManagedStreamSource::GetSizeInternal ()
1343 return stream
.Length (stream
.handle
);
1350 FileSource::FileSource (Media
*media
, const char *filename
) : IMediaSource (Type::FILESOURCE
, media
)
1352 this->filename
= g_strdup (filename
);
1358 FileSource::FileSource (Media
*media
, bool temp_file
) : IMediaSource (Type::FILESOURCE
, media
)
1363 this->temp_file
= temp_file
;
1366 FileSource::~FileSource ()
1371 FileSource::Dispose ()
1379 IMediaSource::Dispose ();
1383 FileSource::Initialize ()
1387 LOG_PIPELINE ("FileSource::Initialize ()\n");
1390 return MEDIA_SUCCESS
;
1393 if (filename
!= NULL
)
1394 return MEDIA_FILE_ERROR
;
1396 filename
= g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL
);
1398 if ((tmp_fd
= g_mkstemp (filename
)) == -1) {
1405 fd
= fdopen (tmp_fd
, "r");
1407 setvbuf (fd
, buffer
, _IOFBF
, sizeof (buffer
));
1409 if (filename
== NULL
)
1410 return MEDIA_FILE_ERROR
;
1412 fd
= g_fopen (filename
, "r");
1416 return MEDIA_FILE_ERROR
;
1420 return MEDIA_SUCCESS
;
1424 FileSource::Open (const char *filename
)
1426 g_return_val_if_fail (filename
!= NULL
, MEDIA_FAIL
);
1428 g_free (this->filename
);
1429 this->filename
= g_strdup (filename
);
1436 fd
= fopen (filename
, "r");
1443 return MEDIA_SUCCESS
;
1447 FileSource::UpdateSize ()
1451 g_return_if_fail (fd
!= NULL
);
1453 if (fstat (fileno (fd
), &st
) != -1) {
1461 FileSource::GetSizeInternal ()
1467 FileSource::GetPositionInternal ()
1474 result
= ftell (fd
);
1476 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %" G_GINT64_FORMAT
"\n", result
);
1482 FileSource::SeekInternal (gint64 offset
, int mode
)
1489 LOG_PIPELINE ("FileSource::SeekInternal (%" G_GINT64_FORMAT
", %i)\n", offset
, mode
);
1492 n
= fseek (fd
, offset
, mode
);
1498 FileSource::ReadInternal (void *buf
, guint32 n
)
1504 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf
, n
);
1509 nread
= fread (buf
, 1, n
, fd
);
1511 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n
, (int) nread
);
1517 FileSource::PeekInternal (void *buf
, guint32 n
)
1521 result
= ReadSome (buf
, n
);
1523 Seek (-result
, SEEK_CUR
);
1525 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %" G_GINT64_FORMAT
" [Done]\n", GET_OBJ_ID (this), buf
, n
, GetPosition ());
1543 ProgressiveSource::ProgressiveSource (Media
*media
, const char *uri
) : FileSource (media
, true)
1549 this->uri
= g_strdup (uri
);
1553 ProgressiveSource::Dispose ()
1559 if (Surface::InMainThread ()) {
1560 delete_cancellable (this);
1562 // we have to cancel/delete he cancellable on the main thread
1563 // it may end up doing a lot of stuff, including calling into
1566 // The tick call will ref us until the callback has been called.
1567 // Note that it may cause a warning to be printed
1568 // in ref () (reffing an object with a refcount of 0).
1569 // TODO: find a way to avoid the warning in this case, imho this is
1570 // a valid case of reffing an object with a refcount of 0.
1571 AddTickCallSafe (delete_cancellable
);
1577 FileSource::Dispose ();
1581 ProgressiveSource::delete_cancellable (EventObject
*data
)
1583 ProgressiveSource
*src
= (ProgressiveSource
*) data
;
1584 if (src
->cancellable
) {
1585 src
->cancellable
->Cancel ();
1586 delete src
->cancellable
;
1587 src
->cancellable
= NULL
;
1592 ProgressiveSource::Initialize ()
1594 MediaResult result
= MEDIA_SUCCESS
;
1595 Application
*application
;
1597 application
= GetDeployment ()->GetCurrentApplication ();
1599 g_return_val_if_fail (application
!= NULL
, MEDIA_FAIL
);
1600 g_return_val_if_fail (filename
== NULL
, MEDIA_FAIL
);
1601 g_return_val_if_fail (cancellable
== NULL
, MEDIA_FAIL
);
1603 result
= FileSource::Initialize ();
1605 if (!MEDIA_SUCCEEDED (result
))
1608 write_fd
= g_fopen (filename
, "w");
1609 if (write_fd
== NULL
) {
1610 char *msg
= g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename
);
1611 ReportErrorOccurred (msg
);
1616 // unlink the file right away so that it'll be deleted even if we crash.
1617 if (moonlight_flags
& RUNTIME_INIT_KEEP_MEDIA
) {
1618 printf ("Moonlight: The media file %s will not deleted.\n", filename
);
1620 g_unlink (filename
);
1623 cancellable
= new Cancellable ();
1624 Uri
*u
= new Uri ();
1625 if (u
->Parse (uri
)) {
1626 application
->GetResource (NULL
, u
, notify_func
, data_write
, MediaPolicy
, cancellable
, (gpointer
) this);
1628 result
= MEDIA_FAIL
;
1629 char *msg
= g_strdup_printf ("Could not parse the uri '%s'", uri
);
1630 ReportErrorOccurred (msg
);
1639 ProgressiveSource::notify_func (NotifyType type
, gint64 args
, void *closure
)
1641 g_return_if_fail (closure
!= NULL
);
1642 ((ProgressiveSource
*) closure
)->Notify (type
, args
);
1646 ProgressiveSource::Notify (NotifyType type
, gint64 args
)
1648 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT
")\n",
1650 type
== ::NotifySize
? "NotifySize" :
1651 (type
== NotifyCompleted
? "NotifyCompleted" :
1652 (type
== NotifyFailed
? "NotifyFailed" :
1653 (type
== NotifyStarted
? "NotifyStarted" :
1654 (type
== NotifyProgressChanged
? "NotifyProgressChanged" : "unknown")))),
1661 case NotifyCompleted
:
1662 DownloadComplete ();
1668 case NotifyProgressChanged
:
1675 ProgressiveSource::data_write (void *data
, gint32 offset
, gint32 n
, void *closure
)
1677 g_return_if_fail (closure
!= NULL
);
1678 ((ProgressiveSource
*) closure
)->DataWrite (data
, offset
, n
);
1682 ProgressiveSource::DataWrite (void *buf
, gint32 offset
, gint32 n
)
1685 Media
*media
= NULL
;
1687 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf
, offset
, n
, media
, filename
);
1692 g_return_if_fail (write_fd
!= NULL
);
1694 media
= GetMediaReffed ();
1697 // We've got the entire file, update the size
1698 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.
1700 // Close our write handle, we won't write more now
1706 nwritten
= fwrite (buf
, 1, n
, write_fd
);
1710 write_pos
+= nwritten
;
1716 media
->ReportDownloadProgress ((double) (offset
+ n
) / (double) size
);
1722 ProgressiveSource::NotifySize (gint64 size
)
1724 LOG_PIPELINE ("ProgressiveSource::NotifySize (%" G_GINT64_FORMAT
")\n", size
);
1732 ProgressiveSource::DownloadComplete ()
1734 MediaResult result
= MEDIA_SUCCESS
;
1735 Media
*media
= GetMediaReffed ();
1737 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1740 if (write_pos
!= size
&& size
!= -1) { // what happend here?
1741 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT
") != the reported size (%" G_GINT64_FORMAT
")\n", write_pos
, size
);
1744 this->size
= write_pos
;
1746 // Close our write handle, we won't write more now
1751 if (!MEDIA_SUCCEEDED (result
))
1752 ReportErrorOccurred (result
);
1755 media
->ReportDownloadProgress (1.0);
1762 ProgressiveSource::DownloadFailed ()
1764 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1766 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
1770 ProgressiveSource::CloseWriteFile ()
1772 if (write_fd
== NULL
)
1783 MemorySource::MemorySource (Media
*media
, void *memory
, gint32 size
, gint64 start
, bool owner
)
1784 : IMediaSource (Type::MEMORYSOURCE
, media
)
1786 this->memory
= memory
;
1788 this->start
= start
;
1790 this->owner
= owner
;
1793 MemorySource::~MemorySource ()
1800 MemorySource::SeekInternal (gint64 offset
, int mode
)
1806 real_offset
= offset
- start
;
1807 if (real_offset
< 0 || real_offset
>= size
)
1812 if (pos
+ offset
> size
|| pos
+ offset
< 0)
1817 if (size
- offset
> size
|| size
- offset
< 0)
1819 pos
= size
- offset
;
1828 MemorySource::ReadInternal (void *buffer
, guint32 n
)
1830 guint32 k
= MIN (n
, size
- pos
);
1831 memcpy (buffer
, ((char*) memory
) + pos
, k
);
1837 MemorySource::PeekInternal (void *buffer
, guint32 n
)
1839 gint64 start
= this->start
+ pos
;
1841 if (this->start
> start
)
1844 if ((this->start
+ size
) < (start
+ n
))
1847 memcpy (buffer
, ((char*) memory
) + this->start
- start
, n
);
1855 pthread_mutex_t
MediaThreadPool::mutex
= PTHREAD_MUTEX_INITIALIZER
;
1856 pthread_cond_t
MediaThreadPool::condition
= PTHREAD_COND_INITIALIZER
;
1857 pthread_cond_t
MediaThreadPool::completed_condition
= PTHREAD_COND_INITIALIZER
;
1858 int MediaThreadPool::count
= 0;
1859 pthread_t
MediaThreadPool::threads
[max_threads
];
1860 Media
*MediaThreadPool::medias
[max_threads
];
1861 Deployment
*MediaThreadPool::deployments
[max_threads
];
1862 bool MediaThreadPool::shutting_down
= false;
1863 List
*MediaThreadPool::queue
= NULL
;
1864 bool MediaThreadPool::valid
[max_threads
];
1867 MediaThreadPool::AddWork (MediaClosure
*closure
, bool wakeup
)
1869 pthread_attr_t attribs
;
1872 pthread_mutex_lock (&mutex
);
1874 if (shutting_down
) {
1875 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1878 queue
= new List ();
1879 queue
->Append (new MediaWork (closure
));
1881 // check if all threads are busy with other Media objects
1885 } else if (count
< max_threads
) {
1886 Media
*media
= closure
->GetMedia ();
1887 for (int i
= 0; i
< count
; i
++) {
1888 if (medias
[i
] == NULL
|| medias
[i
] == media
) {
1889 spawn
= false; // there is a thread working on this media or not working at all.
1898 int prev_count
= count
;
1900 count
++; // start up another thread.
1902 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count
);
1904 for (int i
= prev_count
; i
< count
&& result
== 0; i
++) {
1907 deployments
[i
] = NULL
;
1909 pthread_attr_init (&attribs
);
1910 pthread_attr_setdetachstate (&attribs
, PTHREAD_CREATE_JOINABLE
);
1911 result
= pthread_create (&threads
[i
], &attribs
, WorkerLoop
, NULL
);
1912 pthread_attr_destroy (&attribs
);
1915 fprintf (stderr
, "Moonlight: could not create media thread: %s (%i)\n", strerror (result
), result
);
1922 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1923 closure
->GetDescription (), closure
, closure
->GetMedia (), GET_OBJ_ID (closure
->GetMedia ()), closure
->GetDeployment (), queue
? queue
->Length () : -1);
1926 pthread_cond_signal (&condition
);
1928 pthread_mutex_unlock (&mutex
);
1932 MediaThreadPool::WaitForCompletion (Deployment
*deployment
)
1934 bool waiting
= false;
1935 MediaWork
*current
= NULL
;
1937 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment
);
1941 pthread_mutex_lock (&mutex
);
1945 /* check if the deployment is being worked on */
1946 for (int i
= 0; i
< count
; i
++) {
1947 if (deployments
[i
] == deployment
) {
1952 /* check if the deployment is in the queue */
1953 if (!waiting
&& queue
!= NULL
) {
1954 current
= (MediaWork
*) queue
->First ();
1955 while (current
!= NULL
) {
1956 if (current
->closure
->GetDeployment () == deployment
) {
1960 current
= (MediaWork
*) current
->next
;
1966 ts
.tv_nsec
= 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
1967 pthread_cond_timedwait (&completed_condition
, &mutex
, &ts
);
1970 pthread_mutex_unlock (&mutex
);
1974 MediaThreadPool::RemoveWork (Media
*media
)
1976 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media
, GET_OBJ_ID (media
));
1979 List::Node
*first
= NULL
;
1980 List::Node
*last
= NULL
;
1981 List::Node
*current
= NULL
;
1984 pthread_mutex_lock (&mutex
);
1986 // create a list of nodes to delete
1987 current
= queue
!= NULL
? queue
->First () : NULL
;
1988 while (current
!= NULL
) {
1989 next
= current
->next
; // retrieve next before Unlinking
1990 MediaWork
*mw
= (MediaWork
*) current
;
1991 if (mw
->closure
->GetMedia () == media
) {
1992 queue
->Unlink (current
);
1993 if (first
== NULL
) {
1996 last
->next
= current
;
2005 pthread_mutex_unlock (&mutex
);
2007 // We have to delete the list nodes with the
2008 // queue mutex unlocked, due to refcounting
2009 // (our node's (MediaWork) dtor will cause unrefs,
2010 // which may cause other dtors to be called,
2011 // eventually ending up wanting to lock the mutex
2015 while (current
!= NULL
) {
2016 next
= current
->next
;
2023 MediaThreadPool::WakeUp ()
2025 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2027 pthread_mutex_lock (&mutex
);
2028 pthread_cond_signal (&condition
);
2029 pthread_mutex_unlock (&mutex
);
2033 MediaThreadPool::IsThreadPoolThread ()
2035 bool result
= false;
2036 pthread_mutex_lock (&mutex
);
2037 for (int i
= 0; i
< count
; i
++) {
2038 if (pthread_equal (pthread_self (), threads
[i
])) {
2043 pthread_mutex_unlock (&mutex
);
2048 MediaThreadPool::Initialize ()
2050 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2053 shutting_down
= false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2057 MediaThreadPool::Shutdown ()
2059 List::Node
*current
= NULL
;
2060 List::Node
*next
= NULL
;
2062 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count
);
2066 g_return_if_fail (!shutting_down
);
2068 pthread_mutex_lock (&mutex
);
2070 shutting_down
= true;
2071 pthread_cond_broadcast (&condition
);
2073 for (int i
= 0; i
< count
; i
++) {
2077 pthread_mutex_unlock (&mutex
);
2078 pthread_join (threads
[i
], NULL
);
2079 pthread_mutex_lock (&mutex
);
2082 if (queue
!= NULL
) {
2083 current
= queue
->First ();
2084 queue
->Clear (false);
2090 pthread_mutex_unlock (&mutex
);
2092 // deleting a node can have side-effects, so we first copy the list of nodes,
2093 // clear the original and loop over the copy while deleting the nodes.
2094 // this prevents any reentering issues while deleting nodes.
2095 while (current
!= NULL
) {
2096 next
= current
->next
;
2101 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2105 MediaThreadPool::WorkerLoop (void *data
)
2107 MediaWork
*node
= NULL
;
2108 Media
*media
= NULL
;
2109 int self_index
= -1;
2111 pthread_mutex_lock (&mutex
);
2112 for (int i
= 0; i
< count
; i
++) {
2113 if (pthread_equal (threads
[i
], pthread_self ())) {
2118 pthread_mutex_unlock (&mutex
);
2120 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index
);
2122 g_return_val_if_fail (self_index
>= 0, NULL
);
2124 while (!shutting_down
) {
2125 pthread_mutex_lock (&mutex
);
2127 medias
[self_index
] = NULL
;
2128 deployments
[self_index
] = NULL
;
2129 /* if anybody was waiting for us to finish working, notify them */
2131 pthread_cond_signal (&completed_condition
);
2134 node
= (MediaWork
*) (queue
!= NULL
? queue
->First () : NULL
);
2136 while (node
!= NULL
) {
2137 media
= node
->closure
->GetMedia ();
2139 for (int i
= 0; i
< count
; i
++) {
2140 if (medias
[i
] == media
) {
2141 // another thread is working for the same media object.
2142 // we need to find something else to do.
2151 node
= (MediaWork
*) node
->next
;
2155 pthread_cond_wait (&condition
, &mutex
);
2157 queue
->Unlink (node
);
2161 medias
[self_index
] = media
;
2162 /* At this point the current deployment might be wrong, so avoid
2163 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2164 * here, since it might end up doing a lot of work with the mutex
2166 deployments
[self_index
] = media
->GetUnsafeDeployment ();
2169 pthread_mutex_unlock (&mutex
);
2174 media
->SetCurrentDeployment (true, true);
2176 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);
2178 node
->closure
->Call ();
2180 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node
);
2185 pthread_mutex_lock (&mutex
);
2186 deployments
[self_index
] = NULL
;
2187 medias
[self_index
] = NULL
;
2188 /* if anybody was waiting for us to finish working, notify them */
2190 pthread_cond_signal (&completed_condition
);
2191 pthread_mutex_unlock (&mutex
);
2193 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index
);
2203 MediaClosure::MediaClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
, const char *description
)
2204 : EventObject (Type::MEDIACLOSURE
, true)
2206 Init (media
, callback
, context
);
2207 this->description
= description
;
2210 MediaClosure::MediaClosure (Type::Kind object_kind
, Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2211 : EventObject (object_kind
, true)
2213 Init (media
, callback
, context
);
2217 MediaClosure::Init (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2219 result
= MEDIA_INVALID
;
2221 this->callback
= callback
;
2222 this->context
= context
;
2224 this->context
->ref ();
2225 this->media
= media
;
2227 this->media
->ref ();
2229 // put checks at the end so that fields are still initialized, since we can't abort construction.
2230 g_return_if_fail (callback
!= NULL
);
2231 g_return_if_fail (media
!= NULL
);
2235 MediaClosure::Dispose ()
2249 EventObject::Dispose ();
2253 MediaClosure::Call ()
2256 result
= callback (this);
2258 result
= MEDIA_NO_CALLBACK
;
2263 * MediaDisposeObjectClosure
2265 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2266 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE
, media
, callback
, context
)
2271 MediaDisposeObjectClosure::Dispose ()
2273 if (!CallExecuted ()) {
2274 // we haven't been executed. do it now.
2276 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2281 MediaClosure::Dispose ();
2287 MediaSeekClosure::MediaSeekClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2288 : MediaClosure (Type::MEDIASEEKCLOSURE
, media
, callback
, context
)
2294 * MediaReportSeekCompletedClosure
2297 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2298 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE
, media
, callback
, context
)
2300 g_return_if_fail (context
!= NULL
);
2305 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2310 MediaReportSeekCompletedClosure::Dispose ()
2312 MediaClosure::Dispose ();
2316 * MediaGetFrameClosure
2319 MediaGetFrameClosure::MediaGetFrameClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, IMediaStream
*stream
)
2320 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2322 this->stream
= NULL
;
2324 g_return_if_fail (context
!= NULL
);
2325 g_return_if_fail (stream
!= NULL
);
2327 this->stream
= stream
;
2328 // this->stream->ref ();
2330 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2333 MediaGetFrameClosure::~MediaGetFrameClosure ()
2335 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2339 MediaGetFrameClosure::Dispose ()
2342 // stream->unref ();
2346 MediaClosure::Dispose ();
2347 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2351 * MediaReportFrameCompletedClosure
2354 MediaReportFrameCompletedClosure::MediaReportFrameCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, MediaFrame
*frame
)
2355 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2359 g_return_if_fail (context
!= NULL
);
2360 g_return_if_fail (frame
!= NULL
);
2362 this->frame
= frame
;
2363 this->frame
->ref ();
2367 MediaReportFrameCompletedClosure::Dispose ()
2374 MediaClosure::Dispose ();
2381 IMediaStream::IMediaStream (Type::Kind kind
, Media
*media
) : IMediaObject (kind
, media
)
2385 extra_data_size
= 0;
2397 input_ended
= false;
2398 output_ended
= false;
2400 first_pts
= G_MAXUINT64
; // The first pts in the stream, initialized to G_MAXUINT64
2401 last_popped_pts
= G_MAXUINT64
; // The pts of the last frame returned, initialized to G_MAXUINT64
2402 last_enqueued_pts
= G_MAXUINT64
; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2403 last_available_pts
= 0; // The pts of the last available frame, initialized to 0
2407 IMediaStream::Dispose ()
2410 IMediaDecoder
*d
= decoder
;
2415 g_free (extra_data
);
2421 IMediaObject::Dispose ();
2425 IMediaStream::CreateCodec (int codec_id
)
2428 case CODEC_WMV1
: return g_strdup ("wmv1");
2429 case CODEC_WMV2
: return g_strdup ("wmv2");
2430 case CODEC_WMV3
: return g_strdup ("wmv3");
2431 case CODEC_WMVA
: return g_strdup ("wmva");
2432 case CODEC_WVC1
: return g_strdup ("vc1");
2433 case CODEC_RGBA
: return g_strdup ("rgba");
2434 case CODEC_YV12
: return g_strdup ("yv12");
2435 case CODEC_MP3
: return g_strdup ("mp3");
2436 case CODEC_WMAV1
: return g_strdup ("wmav1");
2437 case CODEC_WMAV2
: return g_strdup ("wmav2");
2438 case CODEC_WMAV3
: return g_strdup ("wmav3");
2439 case CODEC_PCM
: return g_strdup ("pcm");
2441 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id
);
2443 /* This algorithm needs testing.
2446 int a = (codec_id & 0x000000FF);
2447 int b = (codec_id & 0x0000FF00) >> 8;
2448 int c = (codec_id & 0x00FF0000) >> 16;
2449 int d = (codec_id & 0xFF000000) >> 24;
2451 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2453 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2455 result = (char *) g_malloc (size + 1);
2458 result [current++] = (char) a;
2460 result [current++] = (char) b;
2462 result [current++] = (char) c;
2464 result [current++] = (char) d;
2465 result [current] = 0;
2467 return g_strdup ("<unknown>");
2473 IMediaStream::IsQueueEmpty ()
2475 return queue
.IsEmpty ();
2479 IMediaStream::GetStreamTypeName ()
2481 switch (GetType ()) {
2482 case MediaTypeVideo
: return "Video";
2483 case MediaTypeAudio
: return "Audio";
2484 case MediaTypeMarker
: return "Marker";
2485 default: return "Unknown";
2490 IMediaStream::ReportSeekCompleted ()
2492 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2493 input_ended
= false;
2494 output_ended
= false;
2496 if (decoder
!= NULL
)
2497 decoder
->ReportSeekCompleted ();
2501 IMediaStream::GetDemuxer ()
2504 IMediaDemuxer
*result
;
2509 media
= GetMediaReffed ();
2511 g_return_val_if_fail (media
!= NULL
, NULL
);
2513 result
= media
->GetDemuxer ();
2521 IMediaStream::GetDecoder ()
2527 IMediaStream::SetDecoder (IMediaDecoder
*value
)
2537 IMediaStream::GetOutputEnded ()
2539 return output_ended
;
2543 IMediaStream::SetOutputEnded (bool value
)
2545 output_ended
= value
;
2549 IMediaStream::GetInputEnded ()
2555 IMediaStream::SetInputEnded (bool value
)
2557 input_ended
= value
;
2558 if (GetDecoder () != NULL
)
2559 GetDecoder ()->ReportInputEnded ();
2563 IMediaStream::GetBufferedSize ()
2568 if (first_pts
== G_MAXUINT64
|| last_enqueued_pts
== G_MAXUINT64
)
2570 else if (last_popped_pts
== G_MAXUINT64
)
2571 result
= last_enqueued_pts
- first_pts
;
2573 result
= last_enqueued_pts
- last_popped_pts
;
2576 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",
2577 GET_OBJ_ID (this), codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
), MilliSeconds_FromPts (result
));
2584 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2587 IMediaStream::PrintBufferInformation ()
2589 guint64 buffer_size
= GetBufferedSize ();
2591 printf (" <%s: ", codec
);
2593 if (GetSelected ()) {
2594 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>",
2595 TO_MS (buffer_size
), TO_MS (first_pts
), TO_MS (last_popped_pts
),
2596 TO_MS (last_enqueued_pts
), queue
.Length ());
2598 printf ("(not selected) >");
2604 IMediaStream::EnqueueFrame (MediaFrame
*frame
)
2609 g_return_if_fail (Media::InMediaThread ());
2611 media
= GetMediaReffed ();
2612 g_return_if_fail (media
!= NULL
);
2614 if (media
->IsStopped ()) {
2615 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame.\n", frame
);
2619 if (frame
->buffer
== NULL
) {
2620 /* for some reason there is no output from the decoder, possibly because it needs more data from the demuxer before outputting anything */
2621 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): No data in frame, not storing it.\n", frame
);
2626 if (first_pts
== G_MAXUINT64
)
2627 first_pts
= frame
->pts
;
2629 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
2632 if (last_enqueued_pts
> frame
->pts
&& last_enqueued_pts
!= G_MAXUINT64
&& frame
->event
!= FrameEventEOF
&& frame
->buflen
> 0) {
2633 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, "
2634 "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",
2635 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2636 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), frame
, frame
->buflen
, MilliSeconds_FromPts (frame
->pts
));
2640 last_enqueued_pts
= frame
->pts
;
2641 first
= queue
.LinkedList ()->Length () == 0;
2642 queue
.LinkedList ()->Append (new StreamNode (frame
));
2645 SetLastAvailablePts (frame
->pts
);
2648 EmitSafe (FirstFrameEnqueuedEvent
);
2655 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",
2656 codec
, first
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2657 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
- first_pts
), frame
, frame
->buflen
);
2661 IMediaStream::PopFrame ()
2663 MediaFrame
*result
= NULL
;
2664 StreamNode
*node
= NULL
;
2666 // We use the queue lock to synchronize access to
2667 // last_popped_pts/last_enqueued_pts/first_pts
2670 node
= (StreamNode
*) queue
.LinkedList ()->First ();
2672 result
= node
->GetFrame ();
2674 queue
.LinkedList ()->Remove (node
);
2675 last_popped_pts
= result
->pts
;
2679 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",
2680 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2681 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), result
, result
? result
->buflen
: 0);
2683 if (!input_ended
&& !output_ended
&& result
!= NULL
) {
2684 IMediaDemuxer
*demuxer
= GetDemuxer ();
2685 if (demuxer
!= NULL
)
2686 demuxer
->FillBuffers ();
2693 IMediaStream::ClearQueue ()
2695 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2697 queue
.LinkedList ()->Clear (true);
2698 first_pts
= G_MAXUINT64
;
2699 last_popped_pts
= G_MAXUINT64
;
2700 last_enqueued_pts
= G_MAXUINT64
;
2705 IMediaStream::SetSelected (bool value
)
2707 Media
*media
= GetMediaReffed ();
2711 if (media
->GetDemuxer ())
2712 media
->GetDemuxer ()->UpdateSelected (this);
2718 * IMediaStream.StreamNode
2721 IMediaStream::StreamNode::StreamNode (MediaFrame
*f
)
2727 IMediaStream::StreamNode::~StreamNode ()
2736 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
, IMediaSource
*source
) : IMediaObject (kind
, media
)
2738 this->source
= source
;
2739 this->source
->ref ();
2745 seeking_pts
= G_MAXUINT64
;
2746 pending_stream
= NULL
;
2747 pending_fill_buffers
= false;
2750 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
)
2751 : IMediaObject (kind
, media
)
2759 seeking_pts
= G_MAXUINT64
;
2760 pending_stream
= NULL
;
2761 pending_fill_buffers
= false;
2765 IMediaDemuxer::Dispose ()
2767 if (streams
!= NULL
) {
2768 IMediaStream
**tmp
= streams
;
2769 int stream_count
= this->stream_count
;
2771 for (int i
= 0; i
< stream_count
; i
++) {
2772 tmp
[i
]->Dispose ();
2781 if (pending_stream
!= NULL
) {
2782 pending_stream
->unref ();
2783 pending_stream
= NULL
;
2786 IMediaObject::Dispose ();
2790 IMediaDemuxer::OpenCallback (MediaClosure
*closure
)
2792 IMediaDemuxer
*demuxer
;
2794 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure
);
2796 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2797 demuxer
->OpenDemuxerAsync ();
2799 return MEDIA_SUCCESS
;
2803 IMediaDemuxer::EnqueueOpen ()
2805 MediaClosure
*closure
;
2806 Media
*media
= GetMediaReffed ();
2808 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2810 closure
= new MediaClosure (media
, OpenCallback
, this, "IMediaDemuxer::OpenCallback");
2811 media
->EnqueueWork (closure
, false);
2817 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2819 Media
*media
= GetMediaReffed ();
2821 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media
);
2826 // Media might be null if we got disposed for some reason.
2830 media
->ReportOpenDemuxerCompleted ();
2835 IMediaDemuxer::ReportGetFrameProgress (double progress
)
2837 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress
);
2841 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream
*stream
)
2843 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream
);
2847 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind
, gint64 value
)
2849 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %" G_GINT64_FORMAT
")\n", kind
, value
);
2853 IMediaDemuxer::EnqueueReportGetFrameCompleted (MediaFrame
*frame
)
2855 Media
*media
= GetMediaReffed ();
2856 MediaClosure
*closure
= new MediaReportFrameCompletedClosure (media
, ReportGetFrameCompletedCallback
, this, frame
);
2857 media
->EnqueueWork (closure
);
2863 IMediaDemuxer::ReportGetFrameCompletedCallback (MediaClosure
*closure
)
2865 MediaReportFrameCompletedClosure
*c
= (MediaReportFrameCompletedClosure
*) closure
;
2867 g_return_val_if_fail (c
!= NULL
, MEDIA_FAIL
);
2868 g_return_val_if_fail (c
->GetDemuxer () != NULL
, MEDIA_FAIL
);
2870 c
->GetDemuxer ()->ReportGetFrameCompleted (c
->GetFrame ());
2872 return MEDIA_SUCCESS
;
2876 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame
*frame
)
2880 g_return_if_fail (frame
== NULL
|| (frame
!= NULL
&& frame
->stream
!= NULL
));
2881 g_return_if_fail (pending_stream
!= NULL
);
2883 media
= GetMediaReffed ();
2885 g_return_if_fail (media
!= NULL
);
2887 if (media
->IsStopped ()) {
2888 pending_stream
->unref ();
2889 pending_stream
= NULL
; // not waiting for anything more
2890 goto cleanup
; /* if we're stopped, just drop what we're doing. */
2893 /* ensure we're on a media thread */
2894 if (!Media::InMediaThread ()) {
2895 EnqueueReportGetFrameCompleted (frame
);
2899 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);
2901 if (frame
== NULL
) {
2902 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame
, pending_stream
->GetStreamTypeName ());
2903 // No more data for this stream
2904 pending_stream
->SetInputEnded (true);
2905 } else if (!frame
->stream
->IsDisposed ()) {
2906 IMediaDecoder
*decoder
= frame
->stream
->GetDecoder ();
2907 if (decoder
!= NULL
)
2908 decoder
->DecodeFrameAsync (frame
, true /* always enqueue */);
2911 pending_stream
->unref ();
2912 pending_stream
= NULL
; // not waiting for anything more
2914 // enqueue some more
2923 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure
*c
)
2925 MediaReportSeekCompletedClosure
*closure
= (MediaReportSeekCompletedClosure
*) c
;
2926 IMediaDemuxer
*demuxer
;
2928 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
2929 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
2931 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2932 demuxer
->ReportSeekCompleted (closure
->GetPts ());
2934 return MEDIA_SUCCESS
;
2938 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts
)
2940 Media
*media
= GetMediaReffed ();
2941 MediaClosure
*closure
= new MediaReportSeekCompletedClosure (media
, ReportSeekCompletedCallback
, this, pts
);
2942 media
->EnqueueWork (closure
);
2948 IMediaDemuxer::ReportSeekCompleted (guint64 pts
)
2952 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
")\n", pts
);
2954 g_return_if_fail (seeking
);
2956 if (!Media::InMediaThread ()) {
2957 EnqueueReportSeekCompleted (pts
);
2962 if (pending_stream
!= NULL
)
2963 printf ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
"): we can't be waiting for a frame now.\n", pts
);
2966 media
= GetMediaReffed ();
2968 g_return_if_fail (media
!= NULL
);
2971 for (int i
= 0; i
< GetStreamCount (); i
++) {
2972 IMediaStream
*stream
= GetStream (i
);
2977 stream
->ReportSeekCompleted ();
2980 media
->ReportSeekCompleted (pts
);
2984 pending_fill_buffers
= false;
2987 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%" G_GUINT64_FORMAT
") [Done]\n", pts
);
2991 IMediaDemuxer::OpenDemuxerAsync ()
2993 g_return_if_fail (opened
== false);
2997 OpenDemuxerAsyncInternal ();
3001 IMediaDemuxer::GetFrameCallback (MediaClosure
*c
)
3003 MediaGetFrameClosure
*closure
= (MediaGetFrameClosure
*) c
;
3004 IMediaDemuxer
*demuxer
;
3006 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
3007 g_return_val_if_fail (closure
->GetStream () != NULL
, MEDIA_FAIL
);
3008 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
3010 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3011 demuxer
->GetFrameAsync (closure
->GetStream ());
3013 return MEDIA_SUCCESS
;
3017 IMediaDemuxer::EnqueueGetFrame (IMediaStream
*stream
)
3019 g_return_if_fail (pending_stream
== NULL
); // we can't be waiting for another frame.
3021 Media
*media
= GetMediaReffed ();
3022 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
3023 media
->EnqueueWork (closure
);
3029 IMediaDemuxer::GetFrameAsync (IMediaStream
*stream
)
3031 Media
*media
= NULL
;
3033 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream
, stream
->GetStreamTypeName (), Media::InMediaThread ());
3035 if (!Media::InMediaThread ()) {
3036 EnqueueGetFrame (stream
);
3041 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (): delayed since we're waiting for a seek.\n");
3045 if (pending_stream
!= NULL
) {
3046 /* we're already waiting for a frame */
3050 media
= GetMediaReffed ();
3052 g_return_if_fail (media
!= NULL
);
3054 if (media
->IsStopped ())
3057 if (stream
!= NULL
) {
3058 pending_stream
= stream
;
3059 pending_stream
->ref ();
3060 GetFrameAsyncInternal (stream
);
3069 IMediaDemuxer::SeekCallback (MediaClosure
*closure
)
3071 MediaSeekClosure
*seek
= (MediaSeekClosure
*) closure
;
3072 seek
->GetDemuxer ()->SeekAsync ();
3073 return MEDIA_SUCCESS
;
3077 IMediaDemuxer::EnqueueSeek ()
3079 Media
*media
= GetMediaReffed ();
3080 MediaSeekClosure
*closure
;
3082 g_return_if_fail (media
!= NULL
);
3084 closure
= new MediaSeekClosure (media
, SeekCallback
, this, 0);
3085 media
->EnqueueWork (closure
, true);
3091 IMediaDemuxer::SeekAsync ()
3095 LOG_PIPELINE ("IMediaDemuxer::SeekAsync ()\n");
3097 g_return_if_fail (Media::InMediaThread ());
3099 seeking
= true; /* this ensures that we stop demuxing frames asap */
3105 if (pts
== G_MAXUINT64
) {
3107 * Two (or more) seeks was enqueued before the first one was processed
3108 * - this is the callback for the second (or subsequent) seek. Since we
3109 * keep track only of the last seeked-to position we've already seeked to
3112 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): Nothing to do, seek has been executed already.\n");
3116 if (pending_stream
!= NULL
) {
3117 /* we're waiting for the decoder to decode a frame, wait a bit with the seek */
3118 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (): %i waiting for a frame, postponing seek\n", GET_OBJ_ID (this));
3123 /* as the demuxer to seek */
3124 /* at this point the pipeline shouldn't be doing anything else (for this media) */
3125 SeekAsyncInternal (pts
);
3129 IMediaDemuxer::SeekAsync (guint64 pts
)
3131 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%" G_GUINT64_FORMAT
")\n", pts
);
3145 IMediaDemuxer::ClearBuffers ()
3147 pending_fill_buffers
= false;
3149 /* Clear all the buffer queues */
3150 for (int i
= 0; i
< GetStreamCount (); i
++) {
3151 IMediaStream
*stream
= GetStream (i
);
3156 stream
->ClearQueue ();
3161 IMediaDemuxer::FillBuffersCallback (MediaClosure
*closure
)
3163 IMediaDemuxer
*demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3164 demuxer
->FillBuffersInternal ();
3165 return MEDIA_SUCCESS
;
3169 IMediaDemuxer::FillBuffers ()
3171 Media
*media
= NULL
;
3172 MediaClosure
*closure
;
3173 bool enqueue
= true;
3176 if (pending_fill_buffers
) {
3177 // there's already a FillBuffers request enqueued
3180 media
= GetMediaReffed ();
3181 if (media
== NULL
) {
3185 pending_fill_buffers
= true;
3191 closure
= new MediaClosure (media
, FillBuffersCallback
, this, "IMediaDemuxer::FillBuffersCallback");
3192 media
->EnqueueWork (closure
);
3201 IMediaDemuxer::FillBuffersInternal ()
3203 IMediaStream
*stream
;
3204 IMediaStream
*request_stream
= NULL
;
3205 guint64 min_buffered_size
= G_MAXUINT64
;
3206 MediaResult result
= MEDIA_SUCCESS
;
3207 Media
*media
= GetMediaReffed ();
3208 guint64 buffering_time
= 0;
3209 guint64 buffered_size
= 0;
3211 int media_streams
= 0;
3213 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");
3216 pending_fill_buffers
= false;
3222 // If we're waiting for something, there's nothing to do here.
3223 if (pending_stream
!= NULL
)
3226 // Find the stream with the smallest buffered size, and request a frame from that stream.
3227 g_return_if_fail (media
!= NULL
);
3229 // If we're stopped there is nothing to do here.
3230 if (media
->IsStopped ()) {
3231 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped\n");
3235 buffering_time
= media
->GetBufferingTime ();
3237 if (buffering_time
== 0) {
3238 // Play as soon as possible.
3239 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3240 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3241 // all streams have 1 frame queued.
3245 for (int i
= 0; i
< GetStreamCount (); i
++) {
3246 IMediaDecoder
*decoder
= NULL
;
3248 stream
= GetStream (i
);
3249 if (!stream
->GetSelected ())
3252 if (stream
->GetType () != MediaTypeVideo
&&
3253 stream
->GetType () != MediaTypeAudio
)
3257 if (stream
->GetOutputEnded ()) {
3259 continue; // this stream has ended.
3262 decoder
= stream
->GetDecoder ();
3263 if (decoder
== NULL
) {
3264 fprintf (stderr
, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream
->GetStreamTypeName (), GET_OBJ_ID (stream
), stream
->GetRefCount ());
3265 continue; // no decoder??
3268 buffered_size
= stream
->GetBufferedSize ();
3269 min_buffered_size
= MIN (min_buffered_size
, buffered_size
);
3271 if (buffered_size
>= buffering_time
)
3272 continue; // this stream has enough data buffered.
3274 if (!decoder
->IsDecoderQueueEmpty ())
3275 continue; // this stream is waiting for data to be decoded.
3277 if (buffered_size
<= min_buffered_size
)
3278 request_stream
= stream
;
3280 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (): codec: %s, stream id: %i, result: %i, buffered size: %" G_GUINT64_FORMAT
" ms, buffering time: %" G_GUINT64_FORMAT
" ms, last popped time: %" G_GUINT64_FORMAT
" ms\n",
3281 stream
->codec
, GET_OBJ_ID (stream
), result
, MilliSeconds_FromPts (buffered_size
), MilliSeconds_FromPts (buffering_time
), MilliSeconds_FromPts (stream
->GetLastPoppedPts ()));
3284 if (request_stream
!= NULL
) {
3285 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
));
3286 GetFrameAsync (request_stream
);
3289 if (media_streams
> 0) {
3290 if (ended
== media_streams
) {
3291 media
->ReportBufferingProgress (1.0);
3293 if (min_buffered_size
> 0 && buffering_time
> 0) {
3294 double progress
= ((double) min_buffered_size
/ (double) buffering_time
);
3295 media
->ReportBufferingProgress (progress
);
3304 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT
" ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3308 IMediaDemuxer::GetBufferedSize ()
3310 guint64 result
= G_MAXUINT64
;
3311 IMediaStream
*stream
;
3313 for (int i
= 0; i
< GetStreamCount (); i
++) {
3314 stream
= GetStream (i
);
3315 if (!stream
->GetSelected ())
3318 if (stream
->GetType () != MediaTypeVideo
&& stream
->GetType () != MediaTypeAudio
)
3321 result
= MIN (result
, stream
->GetBufferedSize ());
3328 IMediaDemuxer::GetLastAvailablePts ()
3330 guint64 result
= G_MAXUINT64
;
3331 IMediaStream
*stream
;
3333 for (int i
= 0; i
< GetStreamCount (); i
++) {
3334 stream
= GetStream (i
);
3336 if (stream
== NULL
|| !stream
->GetSelected ())
3339 result
= MIN (result
, stream
->GetLastAvailablePts ());
3342 if (result
== G_MAXUINT64
)
3350 IMediaDemuxer::PrintBufferInformation ()
3352 printf ("Buffer: %" G_GINT64_FORMAT
"", MilliSeconds_FromPts (GetBufferedSize ()));
3353 for (int i
= 0; i
< GetStreamCount (); i
++) {
3354 GetStream (i
)->PrintBufferInformation ();
3361 IMediaDemuxer::GetDuration ()
3364 for (int i
= 0; i
< GetStreamCount (); i
++)
3365 result
= MAX (result
, GetStream (i
)->duration
);
3370 IMediaDemuxer::GetStream (int index
)
3372 return (index
< 0 || index
>= stream_count
) ? NULL
: streams
[index
];
3379 MediaFrame::MediaFrame (IMediaStream
*stream
)
3380 : EventObject (Type::MEDIAFRAME
, true)
3384 g_return_if_fail (stream
!= NULL
);
3386 this->stream
= stream
;
3387 this->stream
->ref ();
3390 MediaFrame::MediaFrame (IMediaStream
*stream
, guint8
*buffer
, guint32 buflen
, guint64 pts
, bool keyframe
)
3391 : EventObject (Type::MEDIAFRAME
, true)
3395 g_return_if_fail (stream
!= NULL
);
3397 this->stream
= stream
;
3398 this->stream
->ref ();
3399 this->buffer
= buffer
;
3400 this->buflen
= buflen
;
3404 if (buflen
> 4 && false) {
3405 printf ("MediaFrame::MediaFrame () %s buffer: ", stream
->GetStreamTypeName ());
3406 for (int i
= 0; i
< 4; i
++)
3407 printf (" 0x%x", buffer
[i
]);
3413 AddState (MediaFrameKeyFrame
);
3417 MediaFrame::Initialize ()
3419 decoder_specific_data
= NULL
;
3431 for (int i
= 0; i
< 4; i
++) {
3442 MediaFrame::~MediaFrame ()
3447 MediaFrame::Dispose ()
3449 IMediaDecoder
*decoder
;
3452 // We can be called either on the main thread just before destruction
3453 // (in which case there are no races since the code which unreffed us
3454 // is the only code which knows about us), or at any time from the
3457 if (GetRefCount () != 0 && stream
!= NULL
) {
3458 if (!Media::InMediaThread ()) {
3459 // if refcount != 0 we're not being called just before destruction, in which case we should
3460 // only be on the media thread.
3461 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3466 if (decoder_specific_data
!= NULL
&& stream
!= NULL
) {
3467 decoder
= stream
->GetDecoder ();
3468 if (decoder
!= NULL
)
3469 decoder
->Cleanup (this);
3482 EventObject::Dispose ();
3486 MediaFrame::SetSrcSlideY (int value
)
3492 MediaFrame::SetSrcSlideH (int value
)
3498 MediaFrame::SetSrcStride (int a
, int b
, int c
, int d
)
3507 MediaFrame::SetDataStride (guint8
* a
, guint8
* b
, guint8
* c
, guint8
* d
)
3509 data_stride
[0] = a
;
3510 data_stride
[1] = b
;
3511 data_stride
[2] = c
;
3512 data_stride
[3] = d
;
3516 * IMediaObject.EventData
3519 IMediaObject::EventData::EventData (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3521 this->event_id
= event_id
;
3522 this->handler
= handler
;
3523 this->context
= context
;
3524 this->context
->ref ();
3525 this->invoke_on_main_thread
= invoke_on_main_thread
;
3528 IMediaObject::EventData::~EventData ()
3535 * IMediaObject.EmitData
3538 IMediaObject::EmitData::EmitData (int event_id
, EventHandler handler
, EventObject
*context
, EventArgs
*args
)
3540 this->event_id
= event_id
;
3541 this->handler
= handler
;
3542 this->context
= context
;
3543 this->context
->ref ();
3549 IMediaObject::EmitData::~EmitData ()
3563 IMediaObject::IMediaObject (Type::Kind kind
, Media
*media
)
3564 : EventObject (kind
, true)
3566 this->media
= media
;
3568 this->media
->ref ();
3569 g_return_if_fail (media
!= NULL
);
3571 emit_on_main_thread
= NULL
;
3575 IMediaObject::Dispose ()
3579 // We can be called either on the main thread just before destruction
3580 // (in which case there are no races since the code which unreffed us
3581 // is the only code which knows about us), or at any time from the
3583 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3584 // if refcount != 0 we're not being called just before destruction, in which case we should
3585 // only be on the media thread.
3586 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3590 media_mutex
.Lock ();
3595 media_mutex
.Unlock ();
3597 event_mutex
.Lock ();
3600 if (emit_on_main_thread
!= NULL
) {
3601 delete emit_on_main_thread
;
3602 emit_on_main_thread
= NULL
;
3604 event_mutex
.Unlock ();
3606 EventObject::Dispose ();
3610 IMediaObject::AddSafeHandler (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3612 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id
, handler
, context
, invoke_on_main_thread
);
3615 if (!IsDisposed ()) {
3616 ed
= new EventData (event_id
, handler
, context
, invoke_on_main_thread
);
3617 event_mutex
.Lock ();
3619 events
= new List ();
3620 events
->Append (ed
);
3621 event_mutex
.Unlock ();
3626 IMediaObject::RemoveSafeHandlers (EventObject
*context
)
3631 event_mutex
.Lock ();
3632 if (events
!= NULL
) {
3633 ed
= (EventData
*) events
->First ();
3634 while (ed
!= NULL
) {
3635 next
= (EventData
*) ed
->next
;
3636 if (ed
->context
== context
)
3637 events
->Remove (ed
);
3641 event_mutex
.Unlock ();
3645 IMediaObject::EmitSafe (int event_id
, EventArgs
*args
)
3647 List
*emits
= NULL
; // The events to emit on this thread.
3654 // Create a list of all the events to emit
3655 // don't keep the lock while emitting.
3656 event_mutex
.Lock ();
3657 if (events
!= NULL
) {
3658 ed
= (EventData
*) events
->First ();
3659 while (ed
!= NULL
) {
3660 if (ed
->event_id
== event_id
) {
3661 emit
= new EmitData (event_id
, ed
->handler
, ed
->context
, args
);
3662 if (ed
->invoke_on_main_thread
) {
3663 if (emit_on_main_thread
== NULL
)
3664 emit_on_main_thread
= new List ();
3665 emit_on_main_thread
->Append (emit
);
3668 emits
= new List ();
3669 emits
->Append (emit
);
3672 ed
= (EventData
*) ed
->next
;
3675 event_mutex
.Unlock ();
3677 // emit the events to be emitted on this thread
3680 if (Surface::InMainThread ()) {
3681 // if we're already on the main thread,
3682 // we can the events to be emitted
3683 // on the main thread
3685 event_mutex
.Lock ();
3686 tmp
= emit_on_main_thread
;
3687 emit_on_main_thread
= NULL
;
3688 event_mutex
.Unlock ();
3691 AddTickCallSafe (EmitListCallback
);
3700 IMediaObject::EmitListMain ()
3705 event_mutex
.Lock ();
3706 list
= emit_on_main_thread
;
3707 emit_on_main_thread
= NULL
;
3708 event_mutex
.Unlock ();
3713 IMediaObject::EmitListCallback (EventObject
*obj
)
3715 IMediaObject
*media_obj
= (IMediaObject
*) obj
;
3716 media_obj
->EmitListMain ();
3720 IMediaObject::EmitList (List
*list
)
3727 emit
= (EmitData
*) list
->First ();
3728 while (emit
!= NULL
) {
3729 emit
->handler (this, emit
->args
, emit
->context
);
3730 emit
= (EmitData
*) emit
->next
;
3737 IMediaObject::GetMediaReffed ()
3740 media_mutex
.Lock ();
3744 media_mutex
.Unlock ();
3749 IMediaObject::ReportErrorOccurred (char const *message
)
3751 g_return_if_fail (media
!= NULL
);
3753 media
->ReportErrorOccurred (message
);
3757 IMediaObject::ReportErrorOccurred (MediaResult result
)
3759 g_return_if_fail (media
!= NULL
);
3761 media
->ReportErrorOccurred (result
);
3765 IMediaObject::ReportErrorOccurred (ErrorEventArgs
*args
)
3767 g_return_if_fail (media
!= NULL
);
3769 media
->ReportErrorOccurred (args
);
3773 IMediaObject::SetMedia (Media
*value
)
3775 media_mutex
.Lock ();
3781 media_mutex
.Unlock ();
3788 IMediaSource::IMediaSource (Type::Kind kind
, Media
*media
)
3789 : IMediaObject (kind
, media
)
3791 pthread_mutexattr_t attribs
;
3792 pthread_mutexattr_init (&attribs
);
3793 pthread_mutexattr_settype (&attribs
, PTHREAD_MUTEX_RECURSIVE
);
3794 pthread_mutex_init (&mutex
, &attribs
);
3795 pthread_mutexattr_destroy (&attribs
);
3797 pthread_cond_init (&condition
, NULL
);
3800 IMediaSource::~IMediaSource ()
3802 pthread_mutex_destroy (&mutex
);
3803 pthread_cond_destroy (&condition
);
3807 IMediaSource::Dispose ()
3809 IMediaObject::Dispose ();
3813 IMediaSource::Lock ()
3815 pthread_mutex_lock (&mutex
);
3819 IMediaSource::Unlock ()
3821 pthread_mutex_unlock (&mutex
);
3825 IMediaSource::ReadSome (void *buf
, guint32 n
)
3829 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf
, n
);
3833 result
= ReadInternal (buf
, n
);
3835 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %" G_GINT64_FORMAT
"\n", GET_OBJ_ID (this), buf
, n
, result
, GetPosition ());
3843 IMediaSource::ReadAll (void *buf
, guint32 n
)
3846 gint64 prev
= GetPosition ();
3847 gint64 avail
= GetLastAvailablePosition ();
3849 //printf ("IMediaSource::ReadAll (%p, %u), position: %" G_GINT64_FORMAT "\n", buf, n, prev);
3851 read
= ReadSome (buf
, n
);
3853 if ((gint64
) read
!= (gint64
) n
) {
3854 FileSource
*fs
= NULL
;
3856 if (GetType () == MediaSourceTypeFile
)
3857 fs
= (FileSource
*) this;
3858 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",
3859 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>");
3860 print_stack_trace ();
3863 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf
, n
, read
);
3865 return (gint64
) read
== (gint64
) n
;
3869 IMediaSource::Peek (void *buf
, guint32 n
)
3876 read
= PeekInternal (buf
, n
);
3877 result
= read
== (gint64
) n
;
3881 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %" G_GINT64_FORMAT
" bytes.\n", buf
, n
, result
, read
);
3887 IMediaSource::Seek (gint64 offset
, int mode
)
3889 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%" G_GINT64_FORMAT
", %d = %s)\n",
3890 GET_OBJ_ID (this), ToString (), offset
, mode
, mode
== SEEK_SET
? "SEEK_SET"
3891 : (mode
== SEEK_CUR
? "SEEK_CUR" : (mode
== SEEK_END
? "SEEK_END" : "<invalid value>")));
3895 result
= SeekInternal (offset
, mode
);
3901 IMediaSource::IsPositionAvailable (gint64 position
, bool *eof
)
3903 gint64 available
= GetLastAvailablePosition ();
3904 gint64 size
= GetSize ();
3908 if (size
!= -1 && size
< position
) {
3909 // Size is known and smaller than the requested position
3914 if (available
!= -1 && available
< position
) {
3915 // Not everything is available and the available position is smaller than the requested position
3920 if (size
== -1 && available
== -1) {
3921 // Size is not known, but everything is available??
3922 // This is probably due to a bug in the derived *Source class
3924 fprintf (stderr
, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
3932 IMediaSource::GetLastAvailablePosition ()
3936 result
= GetLastAvailablePositionInternal ();
3942 IMediaSource::GetPositionInternal ()
3944 // This method should be overridden (or never called for the classes which doesn't override it).
3945 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3946 print_stack_trace ();
3951 IMediaSource::SeekInternal (gint64 offset
, int mode
)
3953 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
);
3954 print_stack_trace ();
3960 IMediaSource::ReadInternal (void *buffer
, guint32 n
)
3962 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
);
3963 print_stack_trace ();
3969 IMediaSource::PeekInternal (void *buffer
, guint32 n
)
3971 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
);
3972 print_stack_trace ();
3978 IMediaSource::GetSizeInternal ()
3980 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3981 print_stack_trace ();
3987 IMediaSource::GetPosition ()
3991 result
= GetPositionInternal ();
3997 IMediaSource::GetSize ()
4001 result
= GetSizeInternal ();
4011 IMediaDemuxer::SetStreams (IMediaStream
** streams
, int count
)
4013 this->streams
= streams
;
4014 this->stream_count
= count
;
4016 for (int i
= 0; i
< count
; i
++)
4017 this->streams
[i
]->ref ();
4021 IMediaDemuxer::AddStream (IMediaStream
*stream
)
4023 g_return_val_if_fail (stream
!= NULL
, -1);
4026 streams
= (IMediaStream
**) g_realloc (streams
, stream_count
* sizeof (IMediaStream
*));
4027 streams
[stream_count
- 1] = stream
;
4030 return stream_count
- 1;
4037 IMediaDecoder::IMediaDecoder (Type::Kind kind
, Media
*media
, IMediaStream
*stream
) : IMediaObject (kind
, media
)
4039 this->stream
= NULL
;
4041 g_return_if_fail (stream
!= NULL
);
4043 this->stream
= stream
;
4044 this->stream
->ref ();
4048 input_ended
= false;
4052 IMediaDecoder::Dispose ()
4054 if (stream
!= NULL
) {
4055 IMediaStream
*s
= stream
;
4064 IMediaObject::Dispose ();
4068 IMediaDecoder::ReportSeekCompleted ()
4071 input_ended
= false;
4076 IMediaDecoder::ReportInputEnded ()
4079 if (IsDecoderQueueEmpty ()) {
4085 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame
*frame
)
4087 IMediaDemuxer
*demuxer
;
4088 IMediaStream
*stream
;
4089 Media
*media
= NULL
;
4091 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
4093 g_return_if_fail (frame
!= NULL
);
4095 media
= GetMediaReffed ();
4096 g_return_if_fail (media
!= NULL
);
4098 stream
= frame
->stream
;
4102 frame
->stream
->EnqueueFrame (frame
);
4104 demuxer
= stream
->GetDemuxer ();
4105 if (demuxer
!= NULL
)
4106 demuxer
->FillBuffers ();
4108 if (input_ended
&& IsDecoderQueueEmpty ())
4117 IMediaDecoder::DecodeFrameCallback (MediaClosure
*closure
)
4120 IMediaDecoder
*decoder
= (IMediaDecoder
*) closure
->GetContext ();
4121 IMediaDecoder::FrameNode
*node
= (IMediaDecoder::FrameNode
*) decoder
->queue
.Pop ();
4124 decoder
->DecodeFrameAsync (node
->frame
, false);
4128 return MEDIA_SUCCESS
;
4132 IMediaDecoder::DecodeFrameAsync (MediaFrame
*frame
, bool enqueue_always
)
4136 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame
, (frame
&& frame
->stream
) ? frame
->stream
->GetStreamTypeName () : NULL
);
4141 g_return_if_fail (frame
!= NULL
);
4143 media
= GetMediaReffed ();
4145 g_return_if_fail (media
!= NULL
);
4147 if (enqueue_always
|| !Media::InMediaThread ()) {
4148 MediaClosure
*closure
= new MediaClosure (media
, DecodeFrameCallback
, this, "IMediaDecoder::DecodeFrameCallback");
4149 queue
.Push (new FrameNode (frame
));
4150 media
->EnqueueWork (closure
);
4155 if (media
->IsStopped ())
4158 DecodeFrameAsyncInternal (frame
);
4165 IMediaDecoder::OpenDecoderAsync ()
4167 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4169 g_return_if_fail (opening
== false);
4170 g_return_if_fail (opened
== false);
4173 OpenDecoderAsyncInternal ();
4177 IMediaDecoder::ReportOpenDecoderCompleted ()
4179 Media
*media
= GetMediaReffed ();
4181 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4186 g_return_if_fail (media
!= NULL
);
4188 media
->ReportOpenDecoderCompleted (this);
4196 IImageConverter::IImageConverter (Type::Kind kind
, Media
*media
, VideoStream
*stream
) : IMediaObject (kind
, media
)
4198 output_format
= MoonPixelFormatNone
;
4199 input_format
= MoonPixelFormatNone
;
4200 this->stream
= stream
;
4207 VideoStream::VideoStream (Media
*media
) : IMediaStream (Type::VIDEOSTREAM
, media
)
4210 bits_per_sample
= 0;
4217 VideoStream::VideoStream (Media
*media
, int codec_id
, guint32 width
, guint32 height
, guint64 duration
, gpointer extra_data
, guint32 extra_data_size
)
4218 : IMediaStream (Type::VIDEOSTREAM
, media
)
4221 bits_per_sample
= 0;
4224 this->height
= height
;
4225 this->width
= width
;
4226 this->duration
= duration
;
4227 this->codec_id
= codec_id
;
4228 this->codec
= CreateCodec (codec_id
);
4229 this->extra_data
= extra_data
;
4230 this->extra_data_size
= extra_data_size
;
4233 VideoStream::~VideoStream ()
4238 VideoStream::Dispose ()
4241 converter
->Dispose ();
4242 converter
->unref ();
4245 IMediaStream::Dispose ();
4249 * MediaMarkerFoundClosure
4252 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media
*media
, MediaCallback
*callback
, MediaElement
*context
)
4253 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE
, media
, callback
, context
)
4259 MediaMarkerFoundClosure::Dispose ()
4265 MediaClosure::Dispose ();
4269 MediaMarkerFoundClosure::SetMarker (MediaMarker
*marker
)
4272 this->marker
->unref ();
4273 this->marker
= marker
;
4275 this->marker
->ref ();
4282 MediaMarker::MediaMarker (const char *type
, const char *text
, guint64 pts
)
4283 : EventObject (Type::MEDIAMARKER
)
4285 this->type
= g_strdup (type
);
4286 this->text
= g_strdup (text
);
4290 MediaMarker::~MediaMarker ()
4300 MarkerStream::MarkerStream (Media
*media
) : IMediaStream (Type::MARKERSTREAM
, media
)
4306 MarkerStream::Dispose ()
4313 IMediaStream::Dispose ();
4317 MarkerStream::MarkerFound (MediaFrame
*frame
)
4319 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4321 if (GetDecoder () == NULL
) {
4322 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4326 GetDecoder ()->DecodeFrameAsync (frame
, false);
4330 MarkerStream::FrameEnqueued ()
4334 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4336 frame
= PopFrame ();
4338 if (frame
== NULL
) {
4339 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4343 if (closure
!= NULL
) {
4344 closure
->SetMarker (frame
->marker
);
4346 closure
->SetMarker (NULL
);
4348 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4350 list
.Append (new MediaMarker::Node (frame
->marker
));
4358 MarkerStream::Pop ()
4360 MediaMarker
*result
= NULL
;
4361 MediaMarker::Node
*node
;
4364 node
= (MediaMarker::Node
*) list
.First ();
4366 result
= node
->marker
;
4376 MarkerStream::SetCallback (MediaMarkerFoundClosure
*closure
)
4379 this->closure
->unref ();
4380 this->closure
= closure
;
4382 this->closure
->ref ();
4388 MediaWork::MediaWork (MediaClosure
*c
)
4390 g_return_if_fail (c
!= NULL
);
4396 MediaWork::~MediaWork ()
4398 g_return_if_fail (closure
!= NULL
);
4405 * PassThroughDecoderInfo
4409 PassThroughDecoderInfo::Supports (const char *codec
)
4411 const char *video_fourccs
[] = { "yv12", "rgba", NULL
};
4412 const char *audio_fourccs
[] = { "pcm", NULL
};
4414 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4415 if (!strcmp (codec
, video_fourccs
[i
]))
4418 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4419 if (!strcmp (codec
, audio_fourccs
[i
]))
4426 * PassThroughDecoder
4429 PassThroughDecoder::PassThroughDecoder (Media
*media
, IMediaStream
*stream
)
4430 : IMediaDecoder (Type::PASSTHROUGHDECODER
, media
, stream
)
4435 PassThroughDecoder::Dispose ()
4437 IMediaDecoder::Dispose ();
4441 PassThroughDecoder::OpenDecoderAsyncInternal ()
4443 const char *fourcc
= GetStream ()->GetCodec ();
4445 if (!strcmp (fourcc
, "yv12")) {
4446 SetPixelFormat (MoonPixelFormatYUV420P
);
4447 } else if (!strcmp (fourcc
, "rgba")) {
4448 SetPixelFormat (MoonPixelFormatRGBA32
);
4449 } else if (!strcmp (fourcc
, "pcm")) {
4450 // nothing to do here
4452 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc
));
4456 ReportOpenDecoderCompleted ();
4460 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4462 frame
->AddState (MediaFrameDecoded
);
4463 if (GetPixelFormat () == MoonPixelFormatYUV420P
) {
4464 VideoStream
*vs
= (VideoStream
*) GetStream ();
4466 frame
->width
= vs
->width
;
4467 frame
->height
= vs
->height
;
4469 frame
->data_stride
[0] = frame
->buffer
;
4470 frame
->data_stride
[1] = frame
->buffer
+ (frame
->width
*frame
->height
);
4471 frame
->data_stride
[2] = frame
->buffer
+ (frame
->width
*frame
->height
)+(frame
->width
/2*frame
->height
/2);
4472 frame
->buffer
= NULL
;
4473 frame
->srcStride
[0] = frame
->width
;
4474 frame
->srcSlideY
= frame
->width
;
4475 frame
->srcSlideH
= frame
->height
;
4477 frame
->AddState (MediaFramePlanar
);
4479 ReportDecodeFrameCompleted (frame
);
4487 NullDecoderInfo::Supports (const char *codec
)
4489 const char *video_fourccs
[] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL
};
4490 const char *audio_fourccs
[] = { "wmav1","wmav2", "wmav3", "mp3", NULL
};
4492 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4493 if (!strcmp (codec
, video_fourccs
[i
]))
4496 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4497 if (!strcmp (codec
, audio_fourccs
[i
]))
4508 NullDecoder::NullDecoder (Media
*media
, IMediaStream
*stream
) : IMediaDecoder (Type::NULLDECODER
, media
, stream
)
4512 prev_pts
= G_MAXUINT64
;
4516 NullDecoder::Dispose ()
4521 IMediaDecoder::Dispose ();
4525 NullDecoder::DecodeVideoFrame (MediaFrame
*frame
)
4527 // free encoded buffer and alloc a new one for our image
4528 g_free (frame
->buffer
);
4529 frame
->buflen
= logo_size
;
4530 frame
->buffer
= (guint8
*) g_malloc (frame
->buflen
);
4531 memcpy (frame
->buffer
, logo
, frame
->buflen
);
4532 frame
->AddState (MediaFrameDecoded
);
4534 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4536 return MEDIA_SUCCESS
;
4540 NullDecoder::DecodeAudioFrame (MediaFrame
*frame
)
4542 AudioStream
*as
= (AudioStream
*) GetStream ();
4547 // discard encoded data
4548 g_free (frame
->buffer
);
4550 // We have no idea here how long the encoded audio data is
4551 // for the first frame we use 0.1 seconds, for the rest
4552 // we calculate the time since the last frame
4554 if (prev_pts
== G_MAXUINT64
|| frame
->pts
<= prev_pts
) {
4555 samples
= as
->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4557 diff_pts
= frame
->pts
- prev_pts
;
4558 samples
= (float) as
->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT
/ (float) diff_pts
);
4560 prev_pts
= frame
->pts
;
4562 data_size
= samples
* as
->GetChannels () * 2 /* 16 bit audio */;
4564 frame
->buflen
= data_size
;
4565 frame
->buffer
= (guint8
*) g_malloc0 (frame
->buflen
);
4567 frame
->AddState (MediaFrameDecoded
);
4569 return MEDIA_SUCCESS
;
4573 NullDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4575 MediaResult result
= MEDIA_FAIL
;
4576 IMediaStream
*stream
= GetStream ();
4578 if (stream
->GetType () == MediaTypeAudio
) {
4579 result
= DecodeAudioFrame (frame
);
4580 } else if (stream
->GetType () == MediaTypeVideo
) {
4581 result
= DecodeVideoFrame (frame
);
4584 if (MEDIA_SUCCEEDED (result
)) {
4585 ReportDecodeFrameCompleted (frame
);
4587 ReportErrorOccurred (result
);
4592 NullDecoder::OpenDecoderAsyncInternal ()
4595 IMediaStream
*stream
= GetStream ();
4597 if (stream
->GetType () == MediaTypeAudio
)
4598 result
= OpenAudio ();
4599 else if (stream
->GetType () == MediaTypeVideo
)
4600 result
= OpenVideo ();
4602 result
= MEDIA_FAIL
;
4604 if (MEDIA_SUCCEEDED (result
)) {
4605 ReportOpenDecoderCompleted ();
4607 ReportErrorOccurred (result
);
4612 NullDecoder::OpenAudio ()
4614 return MEDIA_SUCCESS
;
4618 NullDecoder::OpenVideo ()
4620 VideoStream
*vs
= (VideoStream
*) GetStream ();
4621 guint32 dest_height
= vs
->height
;
4622 guint32 dest_width
= vs
->width
;
4625 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4626 extern const char moonlight_logo
[];
4627 const char *image
= moonlight_logo
;
4629 guint32 img_offset
= *((guint32
*)(image
+ 10));
4630 guint32 img_width
= *((guint32
*)(image
+ 18));
4631 guint32 img_height
= *((guint32
*)(image
+ 22));
4632 guint32 img_stride
= (img_width
* 3 + 3) & ~3; // in bytes
4633 guint32 img_i
, img_h
, img_w
;
4634 guint32 start_w
= (dest_width
-img_width
)/2;
4635 guint32 end_w
= start_w
+ img_width
;
4636 guint32 start_h
= (dest_height
-img_height
)/2;
4637 guint32 end_h
= start_h
+ img_height
;
4639 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
);
4641 // create the buffer for our image
4642 logo_size
= dest_height
* dest_width
* 4;
4643 logo
= (guint8
*) g_malloc (logo_size
);
4644 memset (logo
, 0x00, logo_size
);
4646 // write our image centered into the destination rectangle, flipped horizontally
4648 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4649 for (guint32 dest_w
= 0; dest_w
< dest_width
; dest_w
++) {
4650 if (dest_w
>= start_w
&& dest_w
< end_w
&& dest_h
>= start_h
&& dest_h
< end_h
) {
4651 img_h
= (dest_h
- start_h
) % img_height
;
4652 img_w
= (dest_w
- start_w
) % img_width
;
4653 img_i
= img_h
* img_stride
+ img_w
* 3;
4655 logo
[logo_size
- dest_i
+ 0] = image
[img_offset
+ img_i
+ 0];
4656 logo
[logo_size
- dest_i
+ 1] = image
[img_offset
+ img_i
+ 1];
4657 logo
[logo_size
- dest_i
+ 2] = image
[img_offset
+ img_i
+ 2];
4659 logo
[logo_size
- dest_i
+ 3] = 0xff;
4665 // Flip the image vertically
4666 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4667 for (guint32 dest_w
= 0; dest_w
< dest_width
/ 2; dest_w
++) {
4669 guint32 a
= (dest_h
* dest_width
+ dest_w
) * 4;
4670 guint32 b
= (dest_h
* dest_width
+ dest_width
- dest_w
) * 4 - 4;
4671 for (guint32 c
= 0; c
< 3; c
++) {
4673 logo
[a
+ c
] = logo
[b
+ c
];
4679 SetPixelFormat (MoonPixelFormatRGB32
);
4681 return MEDIA_SUCCESS
;
4688 ExternalDemuxer::ExternalDemuxer (Media
*media
, void *instance
, CloseDemuxerCallback close_demuxer
,
4689 GetDiagnosticAsyncCallback get_diagnostic
, GetFrameAsyncCallback get_sample
, OpenDemuxerAsyncCallback open_demuxer
,
4690 SeekAsyncCallback seek
, SwitchMediaStreamAsyncCallback switch_media_stream
)
4691 : IMediaDemuxer (Type::EXTERNALDEMUXER
, media
)
4693 this->close_demuxer_callback
= close_demuxer
;
4694 this->get_diagnostic_async_callback
= get_diagnostic
;
4695 this->get_sample_async_callback
= get_sample
;
4696 this->open_demuxer_async_callback
= open_demuxer
;
4697 this->seek_async_callback
= seek
;
4698 this->switch_media_stream_async_callback
= switch_media_stream
;
4699 this->instance
= instance
;
4701 pthread_rwlock_init (&rwlock
, NULL
);
4703 g_return_if_fail (instance
!= NULL
);
4704 g_return_if_fail (close_demuxer
!= NULL
&& get_diagnostic
!= NULL
&& get_sample
!= NULL
&& open_demuxer
!= NULL
&& seek
!= NULL
&& switch_media_stream
!= NULL
);
4707 ExternalDemuxer::~ExternalDemuxer ()
4709 pthread_rwlock_destroy (&rwlock
);
4713 ExternalDemuxer::Dispose ()
4716 IMediaDemuxer::Dispose ();
4720 ExternalDemuxer::ClearCallbacks ()
4722 pthread_rwlock_wrlock (&rwlock
);
4723 close_demuxer_callback
= NULL
;
4724 get_diagnostic_async_callback
= NULL
;
4725 get_sample_async_callback
= NULL
;
4726 open_demuxer_async_callback
= NULL
;
4727 seek_async_callback
= NULL
;
4728 switch_media_stream_async_callback
= NULL
;
4730 pthread_rwlock_unlock (&rwlock
);
4734 ExternalDemuxer::SetCanSeek (bool value
)
4736 g_warning ("TODO: ExternalDemuxer::SetCanSeek ()");
4740 ExternalDemuxer::AddStream (IMediaStream
*stream
)
4742 return IMediaDemuxer::AddStream (stream
);
4746 ExternalDemuxer::CloseDemuxerInternal ()
4748 pthread_rwlock_rdlock (&rwlock
);
4749 if (close_demuxer_callback
!= NULL
) {
4750 close_demuxer_callback (instance
);
4753 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4756 pthread_rwlock_unlock (&rwlock
);
4760 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind
)
4762 pthread_rwlock_rdlock (&rwlock
);
4763 if (get_diagnostic_async_callback
!= NULL
) {
4764 get_diagnostic_async_callback (instance
, diagnosticsKind
);
4767 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4770 pthread_rwlock_unlock (&rwlock
);
4774 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
4776 g_return_if_fail (stream
!= NULL
);
4778 pthread_rwlock_rdlock (&rwlock
);
4779 if (get_sample_async_callback
!= NULL
) {
4780 get_sample_async_callback (instance
, stream
->GetStreamType ());
4783 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4786 pthread_rwlock_unlock (&rwlock
);
4790 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4792 pthread_rwlock_rdlock (&rwlock
);
4793 if (open_demuxer_async_callback
!= NULL
) {
4794 open_demuxer_async_callback (instance
, this);
4797 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4800 pthread_rwlock_unlock (&rwlock
);
4804 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime
)
4806 pthread_rwlock_rdlock (&rwlock
);
4807 if (seek_async_callback
!= NULL
) {
4808 seek_async_callback (instance
, seekToTime
);
4811 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4814 pthread_rwlock_unlock (&rwlock
);
4818 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*mediaStreamDescription
)
4820 g_return_if_fail (mediaStreamDescription
!= NULL
);
4822 pthread_rwlock_rdlock (&rwlock
);
4823 if (switch_media_stream_async_callback
!= NULL
) {
4824 switch_media_stream_async_callback (instance
, mediaStreamDescription
);
4827 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4830 pthread_rwlock_unlock (&rwlock
);
4838 AudioStream::AudioStream (Media
*media
)
4839 : IMediaStream (Type::AUDIOSTREAM
, media
)
4843 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
)
4844 : IMediaStream (Type::AUDIOSTREAM
, media
)
4846 this->codec_id
= codec_id
;
4847 this->codec
= CreateCodec (codec_id
);
4848 this->extra_data
= extra_data
;
4849 this->extra_data_size
= extra_data_size
;
4850 input_bits_per_sample
= bits_per_sample
;
4851 output_bits_per_sample
= bits_per_sample
;
4852 input_block_align
= block_align
;
4853 output_block_align
= block_align
;
4854 input_sample_rate
= sample_rate
;
4855 output_sample_rate
= sample_rate
;
4856 input_channels
= channels
;
4857 output_channels
= channels
;
4858 input_bit_rate
= bit_rate
;
4859 output_bit_rate
= bit_rate
;
4866 ExternalDecoder::ExternalDecoder (Media
*media
, IMediaStream
*stream
, void *instance
, const char *name
,
4867 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async
,
4868 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async
,
4869 ExternalDecoder_CleanupCallback cleanup
,
4870 ExternalDecoder_CleanStateCallback clean_state
,
4871 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame
,
4872 ExternalDecoder_DisposeCallback dispose
,
4873 ExternalDecoder_DtorCallback dtor
)
4874 : IMediaDecoder (Type::EXTERNALDECODER
, media
, stream
)
4876 this->instance
= instance
;
4877 this->name
= g_strdup (name
);
4878 this->decode_frame_async
= decode_frame_async
;
4879 this->open_decoder_async
= open_decoder_async
;
4880 this->cleanup
= cleanup
;
4881 this->clean_state
= clean_state
;
4882 this->has_delayed_frame
= has_delayed_frame
;
4883 this->dispose
= dispose
;
4887 ExternalDecoder::~ExternalDecoder ()
4894 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4896 decode_frame_async (instance
, frame
);
4900 ExternalDecoder::OpenDecoderAsyncInternal ()
4902 open_decoder_async (instance
);
4906 ExternalDecoder::Dispose ()
4910 IMediaDecoder::Dispose ();
4914 ExternalDecoder::Cleanup (MediaFrame
*frame
)
4916 cleanup (instance
, frame
);
4920 ExternalDecoder::CleanState ()
4922 clean_state (instance
);
4926 ExternalDecoder::HasDelayedFrame ()
4928 return has_delayed_frame (instance
);
4932 ExternalDecoder::InputEnded ()
4934 GetStream ()->SetOutputEnded (true);
4938 * ExternalDecoderInfo
4941 ExternalDecoderInfo::ExternalDecoderInfo (void *instance
, const char *name
, ExternalDecoderInfo_SupportsCallback supports
, ExternalDecoderInfo_Create create
, ExternalDecoderInfo_dtor dtor
)
4943 this->instance
= instance
;
4944 this->supports
= supports
;
4945 this->create
= create
;
4947 this->name
= g_strdup (name
);
4951 ExternalDecoderInfo::Supports (const char *codec
)
4953 return supports (instance
, codec
);
4957 ExternalDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
4959 return create (instance
, media
, stream
);
4962 ExternalDecoderInfo::~ExternalDecoderInfo ()