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));
93 LOG_PIPELINE ("Media::Dispose (), id: %i\n", GET_OBJ_ID (this));
96 if (!MediaThreadPool::IsThreadPoolThread ()) {
97 g_warning ("Media::Dispose (): Not in thread-pool thread.\n");
108 * We're on a media thread, and there is no other work in the queue: we can ensure that nothing
109 * more will ever execute on the media thread related to this Media instance.
125 this->demuxer
= NULL
;
134 IMediaObject::Dispose ();
136 GetDeployment ()->UnregisterMedia (this);
140 Media::IsMSCodecsInstalled ()
142 return registered_ms_codecs
;
146 Media::RegisterMSCodecs (void)
150 char *libmscodecs_path
= NULL
;
151 const char *functions
[] = {"register_codec_pack", NULL
};
152 const gchar
*home
= g_get_home_dir ();
153 registering_ms_codecs
= true;
155 if (!(moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
)) {
156 LOG_CODECS ("Moonlight: mscodecs haven't been enabled.\n");
161 libmscodecs_path
= g_build_filename (g_get_home_dir (), ".mozilla", "plugins", "moonlight", CODEC_LIBRARY_NAME
, NULL
);
163 if (!(g_file_test (libmscodecs_path
, G_FILE_TEST_EXISTS
) && g_file_test (libmscodecs_path
, G_FILE_TEST_IS_REGULAR
))) {
164 if (libmscodecs_path
)
165 g_free (libmscodecs_path
);
166 libmscodecs_path
= g_strdup (CODEC_LIBRARY_NAME
);
169 dl
= dlopen (libmscodecs_path
, RTLD_LAZY
);
171 LOG_CODECS ("Moonlight: Loaded mscodecs from: %s.\n", libmscodecs_path
);
173 for (int i
= 0; functions
[i
] != NULL
; i
++) {
174 reg
= (register_codec
) dlsym (dl
, functions
[i
]);
176 (*reg
) (MOONLIGHT_CODEC_ABI_VERSION
);
178 LOG_CODECS ("Moonlight: Cannot find %s in %s.\n", functions
[i
], libmscodecs_path
);
181 registered_ms_codecs
= true;
183 LOG_CODECS ("Moonlight: Cannot load %s: %s\n", libmscodecs_path
, dlerror ());
185 g_free (libmscodecs_path
);
187 registering_ms_codecs
= false;
191 Media::SetBufferingEnabled (bool value
)
193 buffering_enabled
= value
;
198 Media::SetBufferingTime (guint64 buffering_time
)
201 this->buffering_time
= buffering_time
;
205 demuxer
->FillBuffers ();
209 Media::GetBufferingTime ()
213 result
= buffering_time
;
219 Media::GetPlaylistRoot ()
228 markers
= new List ();
234 Media::RegisterDemuxer (DemuxerInfo
*info
)
236 //printf ("Media::RegisterDemuxer (%p - %s)\n", info, info->GetName ());
238 if (registered_demuxers
== NULL
) {
239 registered_demuxers
= info
;
241 MediaInfo
* current
= registered_demuxers
;
242 while (current
->next
!= NULL
)
243 current
= current
->next
;
244 current
->next
= info
;
249 Media::RegisterConverter (ConverterInfo
*info
)
251 //printf ("Media::RegisterConverter (%p)\n", info);
253 if (registered_converters
== NULL
) {
254 registered_converters
= info
;
256 MediaInfo
*current
= registered_converters
;
257 while (current
->next
!= NULL
)
258 current
= current
->next
;
259 current
->next
= info
;
264 Media::RegisterDecoder (DecoderInfo
*info
)
268 //printf ("Media::RegisterDecoder (%p)\n", info);
270 if (registered_decoders
== NULL
) {
271 registered_decoders
= info
;
273 if (registering_ms_codecs
) {
274 // MS codecs might get registered after all other codecs (right after installing them),
275 // which means after the null codecs so if they don't get special treatment, they won't
276 // get used until the next browser restart (when they're registered normally).
277 // So instead of appending them, we prepend them.
278 info
->next
= registered_decoders
;
279 registered_decoders
= info
;
281 current
= registered_decoders
;
282 while (current
->next
!= NULL
)
283 current
= current
->next
;
284 current
->next
= info
;
287 LOG_CODECS ("Moonlight: Codec has been registered: %s\n", info
->GetName ());
293 LOG_PIPELINE ("Media::Initialize ()\n");
296 Media::RegisterDemuxer (new ASFDemuxerInfo ());
297 Media::RegisterDemuxer (new Mp3DemuxerInfo ());
298 Media::RegisterDemuxer (new ASXDemuxerInfo ());
301 if (!(moonlight_flags
& RUNTIME_INIT_FFMPEG_YUV_CONVERTER
))
302 Media::RegisterConverter (new YUVConverterInfo ());
305 Media::RegisterDecoder (new ASFMarkerDecoderInfo ());
306 if (moonlight_flags
& RUNTIME_INIT_ENABLE_MS_CODECS
) {
309 #ifdef INCLUDE_FFMPEG
310 if (!(moonlight_flags
& RUNTIME_INIT_DISABLE_FFMPEG_CODECS
)) {
315 Media::RegisterDecoder (new PassThroughDecoderInfo ());
316 Media::RegisterDecoder (new NullDecoderInfo ());
318 MediaThreadPool::Initialize ();
324 LOG_PIPELINE ("Media::Shutdown ()\n");
329 // Make sure all threads are stopped
330 AudioPlayer::Shutdown ();
331 MediaThreadPool::Shutdown ();
333 current
= registered_decoders
;
334 while (current
!= NULL
) {
335 next
= current
->next
;
339 registered_decoders
= NULL
;
341 current
= registered_demuxers
;
342 while (current
!= NULL
) {
343 next
= current
->next
;
347 registered_demuxers
= NULL
;
349 current
= registered_converters
;
350 while (current
!= NULL
) {
351 next
= current
->next
;
355 registered_converters
= NULL
;
357 LOG_PIPELINE ("Media::Shutdown () [Done]\n");
361 Media::Warning (MediaResult result
, const char *format
, ...)
365 if (MEDIA_SUCCEEDED (result
))
368 fprintf (stderr
, "Moonlight: MediaResult = %d; ", result
);
370 va_start (args
, format
);
371 vfprintf (stderr
, format
, args
);
374 fputc ('\n', stderr
);
378 Media::InMediaThread ()
380 return MediaThreadPool::IsThreadPoolThread ();
384 Media::ReportBufferingProgress (double progress
)
386 LOG_BUFFERING ("Media::ReportBufferingProgress (%.3f), buffering_progress: %.3f\n", progress
, buffering_progress
);
388 progress
= MAX (MIN (progress
, 1.0), 0.0);
390 if (progress
== buffering_progress
)
393 if (progress
< buffering_progress
|| progress
> (buffering_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
394 buffering_progress
= progress
;
395 EmitSafe (BufferingProgressChangedEvent
, new ProgressEventArgs (progress
));
400 Media::ReportDownloadProgress (double progress
)
402 LOG_PIPELINE ("Media::ReportDownloadProgress (%.3f), download_progress: %.3f\n", progress
, download_progress
);
404 progress
= MAX (MIN (progress
, 1.0), 0.0);
406 // download progress can only go up
407 g_return_if_fail (progress
>= download_progress
);
409 if (progress
== download_progress
)
412 if (progress
> (download_progress
+ 0.005) || progress
== 1.0 || progress
== 0.0) {
413 download_progress
= progress
;
414 EmitSafe (DownloadProgressChangedEvent
, new ProgressEventArgs (progress
));
419 Media::SeekAsync (guint64 pts
)
421 LOG_PIPELINE ("Media::SeekAsync (%" G_GUINT64_FORMAT
"), id: %i\n", pts
, GET_OBJ_ID (this));
423 if (demuxer
== NULL
) {
424 ReportErrorOccurred ("Media::SeekAsync was called, but there is no demuxer to seek on.\n");
428 demuxer
->SeekAsync (pts
);
432 Media::ReportSeekCompleted (guint64 pts
)
434 LOG_PIPELINE ("Media::ReportSeekCompleted (%llu), id: %i\n", pts
, GET_OBJ_ID (this));
436 buffering_progress
= 0;
438 EmitSafe (SeekCompletedEvent
);
442 Media::ReportOpenCompleted ()
444 LOG_PIPELINE ("Media::ReportOpenCompleted (), id: %i\n", GET_OBJ_ID (this));
446 EmitSafe (OpenCompletedEvent
);
450 Media::ReportOpenDemuxerCompleted ()
452 LOG_PIPELINE ("Media::ReportOpenDemuxerCompleted (), id: %i\n", GET_OBJ_ID (this));
458 Media::ReportOpenDecoderCompleted (IMediaDecoder
*decoder
)
460 LOG_PIPELINE ("Media::ReportOpenDecoderCompleted (%p), id: %i\n", decoder
, GET_OBJ_ID (this));
462 g_return_if_fail (decoder
!= NULL
);
468 Media::ReportErrorOccurred (ErrorEventArgs
*args
)
470 LOG_PIPELINE ("Media::ReportErrorOccurred (%p %s)\n", args
, args
== NULL
? NULL
: args
->GetErrorMessage());
473 fprintf (stderr
, "Moonlight: %s %i %s %s\n", enums_int_to_str ("ErrorType", args
->GetErrorType()), args
->GetErrorCode(), args
->GetErrorMessage(), args
->GetExtendedMessage());
475 fprintf (stderr
, "Moonlight: Unspecified media error.\n");
478 if (!error_reported
) {
479 error_reported
= true;
480 EmitSafe (MediaErrorEvent
, args
);
485 Media::ReportErrorOccurred (const char *message
)
487 LOG_PIPELINE ("Media::ReportErrorOccurred (%s)\n", message
);
489 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 3001, message
)));
493 Media::ReportErrorOccurred (MediaResult result
)
495 char *msg
= g_strdup_printf ("Media error: %i.", result
);
496 ReportErrorOccurred (msg
);
503 LOG_PIPELINE ("Media::PlayAsync ()\n");
505 MediaClosure
*closure
= new MediaClosure (this, PlayCallback
, this, "Media::PlayAsync");
506 EnqueueWork (closure
);
513 LOG_PIPELINE ("Media::PauseAsync ()\n");
519 LOG_PIPELINE ("Media::StopAsync ()\n");
521 MediaClosure
*closure
= new MediaClosure (this, StopCallback
, this, "Media::StopAsync");
522 EnqueueWork (closure
);
527 Media::StopCallback (MediaClosure
*closure
)
529 closure
->GetMedia ()->Stop ();
530 return MEDIA_SUCCESS
;
534 Media::PlayCallback (MediaClosure
*closure
)
536 closure
->GetMedia ()->Play ();
537 return MEDIA_SUCCESS
;
543 LOG_PIPELINE ("Media::Stop () ID: %i\n", GET_OBJ_ID (this));
545 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
549 /* This can't be done, if PlayAsync was called right after StopAsync, we might actually remove the request to start playing again */
553 demuxer
->ClearBuffers ();
559 LOG_PIPELINE ("Media::Play () ID: %i\n", GET_OBJ_ID (this));
561 g_return_if_fail (MediaThreadPool::IsThreadPoolThread ());
565 demuxer
->FillBuffers ();
569 Media::Initialize (Downloader
*downloader
, const char *PartName
)
571 IMediaSource
*source
;
573 LOG_PIPELINE ("Media::Initialize (%p, '%s'), id: %i\n", downloader
, PartName
, GET_OBJ_ID (this));
575 g_return_if_fail (downloader
!= NULL
);
576 g_return_if_fail (file
== NULL
);
577 g_return_if_fail (uri
!= NULL
|| PartName
!= NULL
);
578 g_return_if_fail (initialized
== false);
579 g_return_if_fail (error_reported
== false);
580 g_return_if_fail (this->source
== NULL
);
582 if (downloader
->Completed ()) {
583 file
= downloader
->GetDownloadedFilename (PartName
);
586 ReportErrorOccurred ("Couldn't get downloaded filename.");
591 if (file
== NULL
&& PartName
!= NULL
&& PartName
[0] != 0) {
592 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)");
597 InternalDownloader
*idl
= downloader
->GetInternalDownloader ();
598 MmsDownloader
*mms_dl
= (idl
&& idl
->GetObjectType () == Type::MMSDOWNLOADER
) ? (MmsDownloader
*) idl
: NULL
;
600 if (mms_dl
== NULL
) {
601 ReportErrorOccurred ("We don't support using downloaders which haven't started yet.");
605 source
= new MmsSource (this, downloader
);
607 source
= new FileSource (this, file
);
615 Media::Initialize (IMediaSource
*source
)
619 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", source
, GET_OBJ_ID (this));
621 g_return_if_fail (source
!= NULL
);
622 g_return_if_fail (this->source
== NULL
);
623 g_return_if_fail (initialized
== false);
625 result
= source
->Initialize ();
626 if (!MEDIA_SUCCEEDED (result
)) {
627 ReportErrorOccurred (result
);
632 this->source
= source
;
633 this->source
->ref ();
637 Media::Initialize (const char *uri
)
640 IMediaSource
*source
= NULL
;
642 LOG_PIPELINE ("Media::Initialize ('%s'), id: %i\n", uri
, GET_OBJ_ID (this));
644 g_return_if_fail (uri
!= NULL
);
645 g_return_if_fail (file
== NULL
);
646 g_return_if_fail (uri
!= NULL
);
647 g_return_if_fail (initialized
== false);
648 g_return_if_fail (error_reported
== false);
649 g_return_if_fail (source
== NULL
);
650 g_return_if_fail (this->source
== NULL
);
652 this->uri
= g_strdup (uri
);
655 if (g_str_has_prefix (uri
, "mms://") || g_str_has_prefix (uri
, "rtsp://") || g_str_has_prefix (uri
, "rtsps://")) {
656 dl
= Surface::CreateDownloader (this);
658 ReportErrorOccurred ("Couldn't create downloader.");
662 dl
->Open ("GET", uri
, StreamingPolicy
);
664 if (dl
->GetFailedMessage () == NULL
) {
665 Initialize (dl
, NULL
);
667 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
668 MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
676 source
= new ProgressiveSource (this, uri
);
682 Media::Initialize (IMediaDemuxer
*demuxer
)
684 LOG_PIPELINE ("Media::Initialize (%p), id: %i\n", demuxer
, GET_OBJ_ID (this));
686 g_return_if_fail (demuxer
!= NULL
);
687 g_return_if_fail (this->demuxer
== NULL
);
688 g_return_if_fail (initialized
== false);
690 this->demuxer
= demuxer
;
691 this->demuxer
->ref ();
698 Media::RetryHttp (ErrorEventArgs
*args
)
700 char *http_uri
= NULL
;
702 LOG_PIPELINE ("Media::RetryHttp (), current uri: '%s'\n", uri
);
704 g_return_if_fail (uri
!= NULL
);
705 g_return_if_fail (source
!= NULL
);
708 ReportErrorOccurred (args
);
712 // CHECK: If the current protocolo is rtsps, should we retry http or https?
714 if (g_str_has_prefix (uri
, "mms://")) {
715 http_uri
= g_strdup_printf ("http://%s", uri
+ 6);
716 } else if (g_str_has_prefix (uri
, "rtsp://")) {
717 http_uri
= g_strdup_printf ("http://%s", uri
+ 7);
718 } else if (g_str_has_prefix (uri
, "rtsps://")) {
719 http_uri
= g_strdup_printf ("http://%s", uri
+ 8);
721 ReportErrorOccurred (args
);
727 LOG_PIPELINE ("Media::RetryHttp (), new uri: '%s'\n", http_uri
);
736 Initialize (http_uri
);
747 LOG_PIPELINE ("Media::OpenAsync (), id: %i\n", GET_OBJ_ID (this));
749 g_return_if_fail (initialized
== true);
751 EmitSafe (OpeningEvent
);
753 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenAsync");
754 EnqueueWork (closure
);
759 Media::OpenInternal ()
761 LOG_PIPELINE ("Media::OpenInternal (), id: %i\n", GET_OBJ_ID (this));
763 g_return_if_fail (initialized
== true);
766 // This may happen due to the recursion detection below
767 // Example: we try open a demuxer, the demuxer opens successfully
768 // right away and calls ReportDemuxerOpenComplete which will call
769 // us. Due to the recursion detection we'll enqueue a call to
770 // OpenInternal, while the first OpenInternal may succeed and
771 // set opened to true.
772 LOG_PIPELINE ("Media::OpenInteral (): already opened.\n");
776 // detect recursive calls.
778 if (in_open_internal
) {
779 LOG_PIPELINE ("Media::OpenInteral (): recursive.\n");
780 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
781 EnqueueWork (closure
);
786 in_open_internal
= true;
791 if (!SelectDemuxerAsync ()) {
792 LOG_PIPELINE ("Media::OpenInteral (): no demuxer yet.\n");
799 if (!SelectDecodersAsync ()) {
800 LOG_PIPELINE ("Media::OpenInteral (): no decoders yet.\n");
804 demuxer
->FillBuffers ();
809 LOG_PIPELINE ("Media::OpenInteral (): opened successfully.\n");
811 EmitSafe (OpenCompletedEvent
);
814 in_open_internal
= false;
818 Media::OpenInternal (MediaClosure
*closure
)
820 Media
*media
= (Media
*) closure
->GetContext ();
822 g_return_val_if_fail (media
!= NULL
, MEDIA_FAIL
);
824 media
->OpenInternal ();
826 return MEDIA_SUCCESS
;
830 Media::SelectDemuxerAsync ()
832 DemuxerInfo
*demuxerInfo
;
837 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);
839 g_return_val_if_fail (error_reported
== false, false);
840 g_return_val_if_fail (initialized
== true, false);
842 // Check if demuxer already is open
843 if (demuxer
!= NULL
) {
844 if (demuxer
->IsOpened ())
846 if (!demuxer
->IsOpening ())
847 demuxer
->OpenDemuxerAsync ();
848 return demuxer
->IsOpened ();
851 g_return_val_if_fail (source
!= NULL
, false);
853 // Check if the source knows how to create the demuxer
854 demuxer
= source
->CreateDemuxer (this);
856 if (demuxer
== NULL
) { // No demuxer created, we need to find it ourselves.
857 // Check if we have at least 1024 bytes or eof
858 if (!source
->IsPositionAvailable (16, &eof
)) {
860 // We need to try again later.
861 LOG_PIPELINE ("Media::SelectDemuxer (): We don't have enough data yet.\n");
863 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
864 EnqueueWork (closure
, false);
872 demuxerInfo
= registered_demuxers
;
873 while (demuxer
== NULL
&& demuxerInfo
!= NULL
) {
874 LOG_PIPELINE ("Media::SelectDemuxer ): Checking if '%s' can handle the media.\n", demuxerInfo
->GetName ());
875 support
= demuxerInfo
->Supports (source
);
877 if (support
== MEDIA_SUCCESS
)
882 if (result
== MEDIA_NOT_ENOUGH_DATA
) {
883 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 ());
885 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
886 EnqueueWork (closure
, false);
892 LOG_PIPELINE ("Media::SelectDemuxer (): '%s' can't handle this media.\n", demuxerInfo
->GetName ());
893 demuxerInfo
= (DemuxerInfo
*) demuxerInfo
->next
;
896 if (demuxerInfo
== NULL
) {
897 // No demuxer found, report an error
898 const char *source_name
= file
? file
: uri
;
901 switch (source
->GetType ()) {
902 case MediaSourceTypeProgressive
:
903 case MediaSourceTypeFile
:
904 source_name
= ((FileSource
*) source
)->GetFileName ();
906 case MediaSourceTypeMms
:
907 case MediaSourceTypeMmsEntry
:
908 source_name
= "live source";
911 source_name
= "unknown source";
915 char *msg
= g_strdup_printf ("No demuxers registered to handle the media source '%s'.", source_name
);
916 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
917 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT"),
918 MEDIA_UNKNOWN_CODEC
, msg
));
924 demuxer
= demuxerInfo
->Create (this, source
);
926 LOG_PIPELINE ("Media::SelectDemuxer (): The source created the demuxer (%s).\n", demuxer
->GetTypeName ());
929 if (demuxer
->IsOpened ())
932 if (demuxer
->IsOpening ())
935 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s)\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
937 demuxer
->OpenDemuxerAsync ();
939 LOG_PIPELINE ("Media::SelectDemuxer (), id: %i opening demuxer %i (%s) [Done]\n", GET_OBJ_ID (this), GET_OBJ_ID (demuxer
), demuxer
->GetTypeName ());
941 return demuxer
!= NULL
&& demuxer
->IsOpened ();
945 Media::SelectDecodersAsync ()
947 LOG_PIPELINE ("Media::SelectDecodersAsync () id: %i.\n", GET_OBJ_ID (this));
949 g_return_val_if_fail (error_reported
== false, false);
950 g_return_val_if_fail (initialized
== true, false);
952 if (demuxer
== NULL
) {
953 ReportErrorOccurred ("No demuxer to select decoders from.");
957 // If the demuxer has no streams (ASXDemuxer for instance)
958 // then just return success.
959 if (demuxer
->GetStreamCount () == 0)
962 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting decoders.\n");
964 // Select codecs for each stream
965 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
966 IMediaStream
*stream
= demuxer
->GetStream (i
);
969 if (stream
== NULL
) {
970 ReportErrorOccurred ("MEDIA_INVALID_STREAM");
974 if (stream
->GetDecoder () != NULL
)
977 const char *codec
= stream
->GetCodec ();
978 IMediaDecoder
*decoder
= NULL
;
980 LOG_CODECS ("Moonlight: Searching registered decoders for a decoder which supports '%s'\n", codec
);
982 DecoderInfo
*current_decoder
= registered_decoders
;
983 while (current_decoder
!= NULL
&& !current_decoder
->Supports (codec
)) {
984 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': no.\n", current_decoder
->GetName (), codec
);
985 current_decoder
= (DecoderInfo
*) current_decoder
->next
;
988 if (current_decoder
== NULL
) {
989 Media::Warning (MEDIA_UNKNOWN_CODEC
, "Unknown codec: '%s'.", codec
);
993 LOG_CODECS ("Moonlight: Checking if registered decoder '%s' supports codec '%s': yes.\n", current_decoder
->GetName (), codec
);
994 decoder
= current_decoder
->Create (this, stream
);
996 stream
->SetDecoder (decoder
);
1004 LOG_PIPELINE ("Media::SelectDecodersAsync (): Opening decoders.\n");
1006 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1007 IMediaStream
*stream
= demuxer
->GetStream (i
);
1008 IMediaDecoder
*decoder
;
1013 decoder
= stream
->GetDecoder ();
1015 if (decoder
== NULL
) {
1016 ReportErrorOccurred (new ErrorEventArgs (MediaError
,
1017 MoonError (MoonError::EXCEPTION
, 3001, "AG_E_INVALID_FILE_FORMAT")));
1021 if (decoder
->IsOpening () || decoder
->IsOpened ())
1024 decoder
->OpenDecoderAsync ();
1030 // Wait until all the codecs have opened
1031 LOG_PIPELINE ("Media::SelectDecodersAsync (): Waiting for decoders to open.\n");
1033 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1034 IMediaStream
*stream
= demuxer
->GetStream (i
);
1035 IMediaDecoder
*decoder
;
1040 decoder
= stream
->GetDecoder ();
1042 if (decoder
== NULL
) {
1043 ReportErrorOccurred (MEDIA_FAIL
);
1047 if (decoder
->IsOpening ()) {
1048 MediaClosure
*closure
= new MediaClosure (this, OpenInternal
, this, "Media::OpenInternal");
1049 EnqueueWork (closure
, false);
1054 if (!decoder
->IsOpened ()) {
1055 // After calling OpenDecoderAsync on a decoder, the decoder should either be opened, opening, or an error should have occurred.
1056 ReportErrorOccurred (MEDIA_FAIL
);
1062 // All the codecs have been opened now.
1063 // Find converters for each of them (whenever required).
1065 LOG_PIPELINE ("Media::SelectDecodersAsync (): Selecting converters.\n");
1067 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1068 IMediaStream
*stream
= demuxer
->GetStream (i
);
1069 IMediaDecoder
*decoder
;
1074 decoder
= stream
->GetDecoder ();
1076 if (decoder
== NULL
) {
1077 ReportErrorOccurred (MEDIA_FAIL
);
1081 if (stream
->GetType () != MediaTypeVideo
)
1082 continue; // Only video streams need converters
1084 if (decoder
->GetPixelFormat () == MoonPixelFormatRGB32
|| decoder
->GetPixelFormat () == MoonPixelFormatRGBA32
)
1085 continue; // We need RGB32, so any stream already producing RGB32 doesn't need a converter.
1087 // Select converter for this stream
1088 VideoStream
*vs
= (VideoStream
*) stream
;
1090 ConverterInfo
* current_conv
= registered_converters
;
1091 while (current_conv
!= NULL
&& !current_conv
->Supports (decoder
->GetPixelFormat (), MoonPixelFormatRGB32
)) {
1092 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': no.\n",
1093 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1094 current_conv
= (ConverterInfo
*) current_conv
->next
;
1098 if (current_conv
== NULL
) {
1099 ReportErrorOccurred (MEDIA_UNKNOWN_CONVERTER
);
1100 //Media::Warning (MEDIA_UNKNOWN_CONVERTER, "Can't convert from %d to %d: No converter found.",
1101 // decoder->GetPixelFormat (), MoonPixelFormatRGB32);
1105 LOG_PIPELINE ("Checking whether '%s' supports input '%d' and output '%d': yes.\n",
1106 current_conv
->GetName (), decoder
->GetPixelFormat (), MoonPixelFormatRGB32
);
1108 vs
->converter
= current_conv
->Create (this, vs
);
1109 vs
->converter
->input_format
= decoder
->GetPixelFormat ();
1110 vs
->converter
->output_format
= MoonPixelFormatRGB32
;
1111 if (!MEDIA_SUCCEEDED (vs
->converter
->Open ())) {
1112 vs
->converter
->unref ();
1113 vs
->converter
= NULL
;
1114 ReportErrorOccurred (MEDIA_FAIL
);
1119 // Loop through all the streams, return true if at least one has a codec.
1121 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
1122 IMediaStream
*stream
= demuxer
->GetStream (i
);
1127 if (stream
->GetDecoder () != NULL
)
1131 // No codecs found for no stream, report an error.
1132 ReportErrorOccurred ("Didn't find any codecs for any stream.");
1137 Media::EnqueueWork (MediaClosure
*closure
, bool wakeup
)
1139 bool result
= false;
1142 LOG_PIPELINE_EX ("Media::EnqueueWork (%p).\n", closure
);
1144 g_return_val_if_fail (closure
!= NULL
, false);
1145 g_return_val_if_fail (!IsDisposed (), false);
1148 disposed
= this->is_disposed
;
1151 LOG_PIPELINE ("Media::EnqueueWork (): disposed: %i, work not added\n", disposed
);
1153 MediaThreadPool::AddWork (closure
, wakeup
);
1162 Media::DisposeObjectInternal (MediaClosure
*closure
)
1164 closure
->GetContext ()->Dispose ();
1165 return MEDIA_SUCCESS
;
1169 Media::DisposeObject (EventObject
*obj
)
1171 MediaDisposeObjectClosure
*closure
= new MediaDisposeObjectClosure (this, DisposeObjectInternal
, obj
);
1172 if (!EnqueueWork (closure
, true)) {
1174 printf ("Media::DisposeObject (%p): Could not add callback to the media thread, calling Dispose directly.\n", obj
);
1184 MediaThreadPool::WakeUp ();
1188 Media::ClearQueue ()
1190 LOG_PIPELINE ("Media::ClearQueue ().\n");
1191 MediaThreadPool::RemoveWork (this);
1198 ASXDemuxer::ASXDemuxer (Media
*media
, IMediaSource
*source
)
1199 : IMediaDemuxer (Type::ASXDEMUXER
, media
, source
)
1204 ASXDemuxer::~ASXDemuxer ()
1209 ASXDemuxer::Dispose ()
1215 IMediaDemuxer::Dispose ();
1219 ASXDemuxer::OpenDemuxerAsyncInternal ()
1223 ErrorEventArgs
*args
= NULL
;
1224 Media
*media
= GetMediaReffed ();
1226 g_return_if_fail (media
!= NULL
);
1228 root
= media
->GetPlaylistRoot ();
1230 g_return_if_fail (root
!= NULL
);
1232 PlaylistParser
*parser
= new PlaylistParser (root
, source
);
1234 if (MEDIA_SUCCEEDED (parser
->Parse ())) {
1235 result
= MEDIA_SUCCESS
;
1236 playlist
= parser
->GetPlaylist ();
1239 result
= MEDIA_FAIL
;
1240 args
= parser
->GetErrorEventArgs ();
1247 if (MEDIA_SUCCEEDED (result
)) {
1248 ReportOpenDemuxerCompleted ();
1249 } else if (args
!= NULL
) {
1250 args
->ref (); // calling ReportErrorOccurred with an event args will end up unreffing it
1251 ReportErrorOccurred (args
);
1253 ReportErrorOccurred (result
);
1266 ASXDemuxerInfo::Supports (IMediaSource
*source
)
1268 if (PlaylistParser::IsASX2 (source
) || PlaylistParser::IsASX3 (source
)) {
1269 return MEDIA_SUCCESS
;
1276 ASXDemuxerInfo::Create (Media
*media
, IMediaSource
*source
)
1278 return new ASXDemuxer (media
, source
);
1282 * ManagedStreamSource
1285 ManagedStreamSource::ManagedStreamSource (Media
*media
, ManagedStreamCallbacks
*stream
) : IMediaSource (Type::MANAGEDSTREAMSOURCE
, media
)
1287 memcpy (&this->stream
, stream
, sizeof (this->stream
));
1290 ManagedStreamSource::~ManagedStreamSource ()
1292 stream
.handle
= NULL
;
1296 ManagedStreamSource::ReadInternal (void *buf
, guint32 n
)
1298 return stream
.Read (stream
.handle
, buf
, 0, n
);
1302 ManagedStreamSource::PeekInternal (void *buf
, guint32 n
)
1306 read
= stream
.Read (stream
.handle
, buf
, 0, n
);
1307 stream
.Seek (stream
.handle
, -read
, 1 /* SeekOrigin.Current */);
1312 ManagedStreamSource::SeekInternal (gint64 offset
, int mode
)
1314 stream
.Seek (stream
.handle
, offset
, mode
/* FIXME: check if mode values matches SeekOrigin values */);
1319 ManagedStreamSource::GetPositionInternal ()
1321 return stream
.Position (stream
.handle
);
1325 ManagedStreamSource::GetSizeInternal ()
1327 return stream
.Length (stream
.handle
);
1334 FileSource::FileSource (Media
*media
, const char *filename
) : IMediaSource (Type::FILESOURCE
, media
)
1336 this->filename
= g_strdup (filename
);
1342 FileSource::FileSource (Media
*media
, bool temp_file
) : IMediaSource (Type::FILESOURCE
, media
)
1347 this->temp_file
= temp_file
;
1350 FileSource::~FileSource ()
1355 FileSource::Dispose ()
1363 IMediaSource::Dispose ();
1367 FileSource::Initialize ()
1371 LOG_PIPELINE ("FileSource::Initialize ()\n");
1374 return MEDIA_SUCCESS
;
1377 if (filename
!= NULL
)
1378 return MEDIA_FILE_ERROR
;
1380 filename
= g_build_filename (g_get_tmp_dir (), "MoonlightProgressiveStream.XXXXXX", NULL
);
1382 if ((tmp_fd
= g_mkstemp (filename
)) == -1) {
1389 fd
= fdopen (tmp_fd
, "r");
1391 setvbuf (fd
, buffer
, _IOFBF
, sizeof (buffer
));
1393 if (filename
== NULL
)
1394 return MEDIA_FILE_ERROR
;
1396 fd
= g_fopen (filename
, "r");
1400 return MEDIA_FILE_ERROR
;
1404 return MEDIA_SUCCESS
;
1408 FileSource::Open (const char *filename
)
1410 g_return_val_if_fail (filename
!= NULL
, MEDIA_FAIL
);
1412 g_free (this->filename
);
1413 this->filename
= g_strdup (filename
);
1420 fd
= fopen (filename
, "r");
1427 return MEDIA_SUCCESS
;
1431 FileSource::UpdateSize ()
1435 g_return_if_fail (fd
!= NULL
);
1437 if (fstat (fileno (fd
), &st
) != -1) {
1445 FileSource::GetSizeInternal ()
1451 FileSource::GetPositionInternal ()
1458 result
= ftell (fd
);
1460 LOG_PIPELINE_EX ("FileSource::GetPositionInternal (): result: %lld\n", result
);
1466 FileSource::SeekInternal (gint64 offset
, int mode
)
1473 LOG_PIPELINE ("FileSource::SeekInternal (%lld, %i)\n", offset
, mode
);
1476 n
= fseek (fd
, offset
, mode
);
1482 FileSource::ReadInternal (void *buf
, guint32 n
)
1488 LOG_PIPELINE_ERROR ("FileSource::ReadInternal (%p, %u): File not open.\n", buf
, n
);
1493 nread
= fread (buf
, 1, n
, fd
);
1495 LOG_PIPELINE_EX ("FileSource::ReadInternal (0x????????, %i), nread: %i\n", (int) n
, (int) nread
);
1501 FileSource::PeekInternal (void *buf
, guint32 n
)
1505 result
= ReadSome (buf
, n
);
1507 Seek (-result
, SEEK_CUR
);
1509 LOG_PIPELINE_EX ("FileSource<%i>::PeekInternal (%p, %i), GetPosition (): %lld [Done]\n", GET_OBJ_ID (this), buf
, n
, GetPosition ());
1527 ProgressiveSource::ProgressiveSource (Media
*media
, const char *uri
) : FileSource (media
, true)
1533 this->uri
= g_strdup (uri
);
1537 ProgressiveSource::Dispose ()
1543 if (Surface::InMainThread ()) {
1544 delete_cancellable (this);
1546 // we have to cancel/delete he cancellable on the main thread
1547 // it may end up doing a lot of stuff, including calling into
1550 // The tick call will ref us until the callback has been called.
1551 // Note that it may cause a warning to be printed
1552 // in ref () (reffing an object with a refcount of 0).
1553 // TODO: find a way to avoid the warning in this case, imho this is
1554 // a valid case of reffing an object with a refcount of 0.
1555 AddTickCallSafe (delete_cancellable
);
1561 FileSource::Dispose ();
1565 ProgressiveSource::delete_cancellable (EventObject
*data
)
1567 ProgressiveSource
*src
= (ProgressiveSource
*) data
;
1568 if (src
->cancellable
) {
1569 src
->cancellable
->Cancel ();
1570 delete src
->cancellable
;
1571 src
->cancellable
= NULL
;
1576 ProgressiveSource::Initialize ()
1578 MediaResult result
= MEDIA_SUCCESS
;
1579 Application
*application
;
1581 application
= GetDeployment ()->GetCurrentApplication ();
1583 g_return_val_if_fail (application
!= NULL
, MEDIA_FAIL
);
1584 g_return_val_if_fail (filename
== NULL
, MEDIA_FAIL
);
1585 g_return_val_if_fail (cancellable
== NULL
, MEDIA_FAIL
);
1587 result
= FileSource::Initialize ();
1589 if (!MEDIA_SUCCEEDED (result
))
1592 write_fd
= g_fopen (filename
, "w");
1593 if (write_fd
== NULL
) {
1594 char *msg
= g_strdup_printf ("Could not open a write handle to the file '%s'\n", filename
);
1595 ReportErrorOccurred (msg
);
1600 // unlink the file right away so that it'll be deleted even if we crash.
1601 if (moonlight_flags
& RUNTIME_INIT_KEEP_MEDIA
) {
1602 printf ("Moonlight: The media file %s will not deleted.\n", filename
);
1604 g_unlink (filename
);
1607 cancellable
= new Cancellable ();
1608 Uri
*u
= new Uri ();
1609 if (u
->Parse (uri
)) {
1610 application
->GetResource (NULL
, u
, notify_func
, data_write
, MediaPolicy
, cancellable
, (gpointer
) this);
1612 result
= MEDIA_FAIL
;
1613 char *msg
= g_strdup_printf ("Could not parse the uri '%s'", uri
);
1614 ReportErrorOccurred (msg
);
1623 ProgressiveSource::notify_func (NotifyType type
, gint64 args
, void *closure
)
1625 g_return_if_fail (closure
!= NULL
);
1626 ((ProgressiveSource
*) closure
)->Notify (type
, args
);
1630 ProgressiveSource::Notify (NotifyType type
, gint64 args
)
1632 LOG_PIPELINE ("ProgressiveSource::Notify (%i = %s, %" G_GINT64_FORMAT
")\n",
1634 type
== ::NotifySize
? "NotifySize" :
1635 (type
== NotifyCompleted
? "NotifyCompleted" :
1636 (type
== NotifyFailed
? "NotifyFailed" :
1637 (type
== NotifyStarted
? "NotifyStarted" :
1638 (type
== NotifyProgressChanged
? "NotifyProgressChanged" : "unknown")))),
1645 case NotifyCompleted
:
1646 DownloadComplete ();
1652 case NotifyProgressChanged
:
1659 ProgressiveSource::data_write (void *data
, gint32 offset
, gint32 n
, void *closure
)
1661 g_return_if_fail (closure
!= NULL
);
1662 ((ProgressiveSource
*) closure
)->DataWrite (data
, offset
, n
);
1666 ProgressiveSource::DataWrite (void *buf
, gint32 offset
, gint32 n
)
1669 Media
*media
= GetMediaReffed ();
1671 LOG_PIPELINE ("ProgressiveSource::DataWrite (%p, %i, %i) media: %p, filename: %s\n", buf
, offset
, n
, media
, filename
);
1673 g_return_if_fail (write_fd
!= NULL
);
1676 // We've got the entire file, update the size
1677 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.
1679 // Close our write handle, we won't write more now
1685 nwritten
= fwrite (buf
, 1, n
, write_fd
);
1689 write_pos
+= nwritten
;
1695 media
->ReportDownloadProgress ((double) (offset
+ n
) / (double) size
);
1701 ProgressiveSource::NotifySize (gint64 size
)
1703 LOG_PIPELINE ("ProgressiveSource::NotifySize (%lld)\n", size
);
1711 ProgressiveSource::DownloadComplete ()
1713 MediaResult result
= MEDIA_SUCCESS
;
1714 Media
*media
= GetMediaReffed ();
1716 LOG_PIPELINE ("ProgressiveSource::DownloadComplete ()\n");
1719 if (write_pos
!= size
&& size
!= -1) { // what happend here?
1720 LOG_PIPELINE ("ProgressiveSource::DownloadComplete (): the downloaded size (%" G_GINT64_FORMAT
") != the reported size (%" G_GINT64_FORMAT
")\n", write_pos
, size
);
1723 this->size
= write_pos
;
1725 // Close our write handle, we won't write more now
1730 if (!MEDIA_SUCCEEDED (result
))
1731 ReportErrorOccurred (result
);
1734 media
->ReportDownloadProgress (1.0);
1741 ProgressiveSource::DownloadFailed ()
1743 LOG_PIPELINE ("ProgressiveSource::DownloadFailed ().\n");
1745 ReportErrorOccurred (new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 4001, "AG_E_NETWORK_ERROR")));
1749 ProgressiveSource::CloseWriteFile ()
1751 if (write_fd
== NULL
)
1762 MemorySource::MemorySource (Media
*media
, void *memory
, gint32 size
, gint64 start
, bool owner
)
1763 : IMediaSource (Type::MEMORYSOURCE
, media
)
1765 this->memory
= memory
;
1767 this->start
= start
;
1769 this->owner
= owner
;
1772 MemorySource::~MemorySource ()
1779 MemorySource::SeekInternal (gint64 offset
, int mode
)
1785 real_offset
= offset
- start
;
1786 if (real_offset
< 0 || real_offset
>= size
)
1791 if (pos
+ offset
> size
|| pos
+ offset
< 0)
1796 if (size
- offset
> size
|| size
- offset
< 0)
1798 pos
= size
- offset
;
1807 MemorySource::ReadInternal (void *buffer
, guint32 n
)
1809 guint32 k
= MIN (n
, size
- pos
);
1810 memcpy (buffer
, ((char*) memory
) + pos
, k
);
1816 MemorySource::PeekInternal (void *buffer
, guint32 n
)
1818 gint64 start
= this->start
+ pos
;
1820 if (this->start
> start
)
1823 if ((this->start
+ size
) < (start
+ n
))
1826 memcpy (buffer
, ((char*) memory
) + this->start
- start
, n
);
1834 pthread_mutex_t
MediaThreadPool::mutex
= PTHREAD_MUTEX_INITIALIZER
;
1835 pthread_cond_t
MediaThreadPool::condition
= PTHREAD_COND_INITIALIZER
;
1836 pthread_cond_t
MediaThreadPool::completed_condition
= PTHREAD_COND_INITIALIZER
;
1837 int MediaThreadPool::count
= 0;
1838 pthread_t
MediaThreadPool::threads
[max_threads
];
1839 Media
*MediaThreadPool::medias
[max_threads
];
1840 Deployment
*MediaThreadPool::deployments
[max_threads
];
1841 bool MediaThreadPool::shutting_down
= false;
1842 List
*MediaThreadPool::queue
= NULL
;
1843 bool MediaThreadPool::valid
[max_threads
];
1846 MediaThreadPool::AddWork (MediaClosure
*closure
, bool wakeup
)
1848 pthread_attr_t attribs
;
1851 pthread_mutex_lock (&mutex
);
1853 if (shutting_down
) {
1854 LOG_PIPELINE ("Moonlight: could not execute closure because we're shutting down.\n");
1857 queue
= new List ();
1858 queue
->Append (new MediaWork (closure
));
1860 // check if all threads are busy with other Media objects
1864 } else if (count
< max_threads
) {
1865 Media
*media
= closure
->GetMedia ();
1866 for (int i
= 0; i
< count
; i
++) {
1867 if (medias
[i
] == NULL
|| medias
[i
] == media
) {
1868 spawn
= false; // there is a thread working on this media or not working at all.
1877 int prev_count
= count
;
1879 count
++; // start up another thread.
1881 LOG_FRAMEREADERLOOP ("MediaThreadPool::AddWork (): spawning a new thread (we'll now have %i thread(s))\n", count
);
1883 for (int i
= prev_count
; i
< count
&& result
== 0; i
++) {
1886 deployments
[i
] = NULL
;
1888 pthread_attr_init (&attribs
);
1889 pthread_attr_setdetachstate (&attribs
, PTHREAD_CREATE_JOINABLE
);
1890 result
= pthread_create (&threads
[i
], &attribs
, WorkerLoop
, NULL
);
1891 pthread_attr_destroy (&attribs
);
1894 fprintf (stderr
, "Moonlight: could not create media thread: %s (%i)\n", strerror (result
), result
);
1901 LOG_FRAMEREADERLOOP ("MediaThreadLoop::AddWork () got %s %p for media %p (%i) on deployment %p, there are %d nodes left.\n",
1902 closure
->GetDescription (), closure
, closure
->GetMedia (), GET_OBJ_ID (closure
->GetMedia ()), closure
->GetDeployment (), queue
? queue
->Length () : -1);
1905 pthread_cond_signal (&condition
);
1907 pthread_mutex_unlock (&mutex
);
1911 MediaThreadPool::WaitForCompletion (Deployment
*deployment
)
1913 bool waiting
= false;
1914 MediaWork
*current
= NULL
;
1916 LOG_PIPELINE ("MediaThreadPool::WaitForCompletion (%p)\n", deployment
);
1920 pthread_mutex_lock (&mutex
);
1924 /* check if the deployment is being worked on */
1925 for (int i
= 0; i
< count
; i
++) {
1926 if (deployments
[i
] == deployment
) {
1931 /* check if the deployment is in the queue */
1932 if (!waiting
&& queue
!= NULL
) {
1933 current
= (MediaWork
*) queue
->First ();
1934 while (current
!= NULL
) {
1935 if (current
->closure
->GetDeployment () == deployment
) {
1939 current
= (MediaWork
*) current
->next
;
1945 ts
.tv_nsec
= 100000000; /* 0.1 seconds = 100 milliseconds = 100.000.000 nanoseconds */
1946 pthread_cond_timedwait (&completed_condition
, &mutex
, &ts
);
1949 pthread_mutex_unlock (&mutex
);
1953 MediaThreadPool::RemoveWork (Media
*media
)
1955 LOG_PIPELINE ("MediaThreadPool::RemoveWork (%p = %i)\n", media
, GET_OBJ_ID (media
));
1958 List::Node
*first
= NULL
;
1959 List::Node
*last
= NULL
;
1960 List::Node
*current
= NULL
;
1963 pthread_mutex_lock (&mutex
);
1965 // create a list of nodes to delete
1966 current
= queue
!= NULL
? queue
->First () : NULL
;
1967 while (current
!= NULL
) {
1968 next
= current
->next
; // retrieve next before Unlinking
1969 MediaWork
*mw
= (MediaWork
*) current
;
1970 if (mw
->closure
->GetMedia () == media
) {
1971 queue
->Unlink (current
);
1972 if (first
== NULL
) {
1975 last
->next
= current
;
1984 pthread_mutex_unlock (&mutex
);
1986 // We have to delete the list nodes with the
1987 // queue mutex unlocked, due to refcounting
1988 // (our node's (MediaWork) dtor will cause unrefs,
1989 // which may cause other dtors to be called,
1990 // eventually ending up wanting to lock the mutex
1994 while (current
!= NULL
) {
1995 next
= current
->next
;
2002 MediaThreadPool::WakeUp ()
2004 LOG_FRAMEREADERLOOP ("MediaThreadPool::WakeUp ()\n");
2006 pthread_mutex_lock (&mutex
);
2007 pthread_cond_signal (&condition
);
2008 pthread_mutex_unlock (&mutex
);
2012 MediaThreadPool::IsThreadPoolThread ()
2014 bool result
= false;
2015 pthread_mutex_lock (&mutex
);
2016 for (int i
= 0; i
< count
; i
++) {
2017 if (pthread_equal (pthread_self (), threads
[i
])) {
2022 pthread_mutex_unlock (&mutex
);
2027 MediaThreadPool::Initialize ()
2029 LOG_PIPELINE ("MediaThreadPool::Initialize ()\n");
2032 shutting_down
= false; // this may be true if the user closed a moonlight-tab (we'd shutdown), then opened another moonlight-tab.
2036 MediaThreadPool::Shutdown ()
2038 List::Node
*current
= NULL
;
2039 List::Node
*next
= NULL
;
2041 LOG_PIPELINE ("MediaThreadPool::Shutdown (), we have %i thread(s) to shut down\n", count
);
2045 g_return_if_fail (!shutting_down
);
2047 pthread_mutex_lock (&mutex
);
2049 shutting_down
= true;
2050 pthread_cond_broadcast (&condition
);
2052 for (int i
= 0; i
< count
; i
++) {
2056 pthread_mutex_unlock (&mutex
);
2057 pthread_join (threads
[i
], NULL
);
2058 pthread_mutex_lock (&mutex
);
2061 if (queue
!= NULL
) {
2062 current
= queue
->First ();
2063 queue
->Clear (false);
2069 pthread_mutex_unlock (&mutex
);
2071 // deleting a node can have side-effects, so we first copy the list of nodes,
2072 // clear the original and loop over the copy while deleting the nodes.
2073 // this prevents any reentering issues while deleting nodes.
2074 while (current
!= NULL
) {
2075 next
= current
->next
;
2080 LOG_PIPELINE ("MediaThreadPool::Shutdown () [Completed]\n");
2084 MediaThreadPool::WorkerLoop (void *data
)
2086 MediaWork
*node
= NULL
;
2087 Media
*media
= NULL
;
2088 int self_index
= -1;
2090 pthread_mutex_lock (&mutex
);
2091 for (int i
= 0; i
< count
; i
++) {
2092 if (pthread_equal (threads
[i
], pthread_self ())) {
2097 pthread_mutex_unlock (&mutex
);
2099 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Started thread with index %i.\n", (int) pthread_self (), self_index
);
2101 g_return_val_if_fail (self_index
>= 0, NULL
);
2103 while (!shutting_down
) {
2104 pthread_mutex_lock (&mutex
);
2106 medias
[self_index
] = NULL
;
2107 deployments
[self_index
] = NULL
;
2108 /* if anybody was waiting for us to finish working, notify them */
2110 pthread_cond_signal (&completed_condition
);
2113 node
= (MediaWork
*) (queue
!= NULL
? queue
->First () : NULL
);
2115 while (node
!= NULL
) {
2116 media
= node
->closure
->GetMedia ();
2118 for (int i
= 0; i
< count
; i
++) {
2119 if (medias
[i
] == media
) {
2120 // another thread is working for the same media object.
2121 // we need to find something else to do.
2130 node
= (MediaWork
*) node
->next
;
2134 pthread_cond_wait (&condition
, &mutex
);
2136 queue
->Unlink (node
);
2140 medias
[self_index
] = media
;
2141 /* At this point the current deployment might be wrong, so avoid
2142 * the warnings in GetDeployment. Do not move the call to SetCurrenDeployment
2143 * here, since it might end up doing a lot of work with the mutex
2145 deployments
[self_index
] = media
->GetUnsafeDeployment ();
2148 pthread_mutex_unlock (&mutex
);
2153 media
->SetCurrentDeployment (true, true);
2155 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);
2157 node
->closure
->Call ();
2159 LOG_FRAMEREADERLOOP ("MediaThreadLoop::WorkerLoop () %u: processed node %p\n", (int) pthread_self (), node
);
2164 pthread_mutex_lock (&mutex
);
2165 deployments
[self_index
] = NULL
;
2166 medias
[self_index
] = NULL
;
2167 /* if anybody was waiting for us to finish working, notify them */
2169 pthread_cond_signal (&completed_condition
);
2170 pthread_mutex_unlock (&mutex
);
2172 LOG_PIPELINE ("MediaThreadPool::WorkerLoop () %u: Exited (index: %i).\n", (int) pthread_self (), self_index
);
2182 MediaClosure::MediaClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
, const char *description
)
2183 : EventObject (Type::MEDIACLOSURE
, true)
2185 Init (media
, callback
, context
);
2186 this->description
= description
;
2189 MediaClosure::MediaClosure (Type::Kind object_kind
, Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2190 : EventObject (object_kind
, true)
2192 Init (media
, callback
, context
);
2196 MediaClosure::Init (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2198 result
= MEDIA_INVALID
;
2200 this->callback
= callback
;
2201 this->context
= context
;
2203 this->context
->ref ();
2204 this->media
= media
;
2206 this->media
->ref ();
2208 // put checks at the end so that fields are still initialized, since we can't abort construction.
2209 g_return_if_fail (callback
!= NULL
);
2210 g_return_if_fail (media
!= NULL
);
2214 MediaClosure::Dispose ()
2228 EventObject::Dispose ();
2232 MediaClosure::Call ()
2235 result
= callback (this);
2237 result
= MEDIA_NO_CALLBACK
;
2242 * MediaDisposeObjectClosure
2244 MediaDisposeObjectClosure::MediaDisposeObjectClosure (Media
*media
, MediaCallback
*callback
, EventObject
*context
)
2245 : MediaClosure (Type::MEDIADISPOSEOBJECTCLOSURE
, media
, callback
, context
)
2250 MediaDisposeObjectClosure::Dispose ()
2252 if (!CallExecuted ()) {
2253 // we haven't been executed. do it now.
2255 LOG_PIPELINE ("MediaDisposeObjectClosure::~MediaDisposeObjectClosure (): callback hasn't been executed, we'll do it now.\n");
2260 MediaClosure::Dispose ();
2266 MediaSeekClosure::MediaSeekClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2267 : MediaClosure (Type::MEDIASEEKCLOSURE
, media
, callback
, context
)
2273 * MediaReportSeekCompletedClosure
2276 MediaReportSeekCompletedClosure::MediaReportSeekCompletedClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, guint64 pts
)
2277 : MediaClosure (Type::MEDIAREPORTSEEKCOMPLETEDCLOSURE
, media
, callback
, context
)
2279 g_return_if_fail (context
!= NULL
);
2284 MediaReportSeekCompletedClosure::~MediaReportSeekCompletedClosure ()
2289 MediaReportSeekCompletedClosure::Dispose ()
2291 MediaClosure::Dispose ();
2295 * MediaGetFrameClosure
2298 MediaGetFrameClosure::MediaGetFrameClosure (Media
*media
, MediaCallback
*callback
, IMediaDemuxer
*context
, IMediaStream
*stream
)
2299 : MediaClosure (Type::MEDIAGETFRAMECLOSURE
, media
, callback
, context
)
2301 this->stream
= NULL
;
2303 g_return_if_fail (context
!= NULL
);
2304 g_return_if_fail (stream
!= NULL
);
2306 this->stream
= stream
;
2307 // this->stream->ref ();
2309 //fprintf (stderr, "MediaGetFrameClosure::MediaGetFrameClosure () id: %i\n", GetId ());
2312 MediaGetFrameClosure::~MediaGetFrameClosure ()
2314 //fprintf (stderr, "MediaGetFrameClosure::~MediaGetFrameClosure () id: %i\n", GetId ());
2318 MediaGetFrameClosure::Dispose ()
2321 // stream->unref ();
2325 MediaClosure::Dispose ();
2326 //fprintf (stderr, "MediaGetFrameClosure::Dispose () id: %i\n", GetId ());
2333 IMediaStream::IMediaStream (Type::Kind kind
, Media
*media
) : IMediaObject (kind
, media
)
2337 extra_data_size
= 0;
2349 input_ended
= false;
2350 output_ended
= false;
2352 first_pts
= G_MAXUINT64
; // The first pts in the stream, initialized to G_MAXUINT64
2353 last_popped_pts
= G_MAXUINT64
; // The pts of the last frame returned, initialized to G_MAXUINT64
2354 last_enqueued_pts
= G_MAXUINT64
; // The pts of the last frame enqueued, initialized to G_MAXUINT64
2355 last_available_pts
= 0; // The pts of the last available frame, initialized to 0
2359 IMediaStream::Dispose ()
2362 IMediaDecoder
*d
= decoder
;
2367 g_free (extra_data
);
2373 IMediaObject::Dispose ();
2377 IMediaStream::CreateCodec (int codec_id
)
2380 case CODEC_WMV1
: return g_strdup ("wmv1");
2381 case CODEC_WMV2
: return g_strdup ("wmv2");
2382 case CODEC_WMV3
: return g_strdup ("wmv3");
2383 case CODEC_WMVA
: return g_strdup ("wmva");
2384 case CODEC_WVC1
: return g_strdup ("vc1");
2385 case CODEC_RGBA
: return g_strdup ("rgba");
2386 case CODEC_YV12
: return g_strdup ("yv12");
2387 case CODEC_MP3
: return g_strdup ("mp3");
2388 case CODEC_WMAV1
: return g_strdup ("wmav1");
2389 case CODEC_WMAV2
: return g_strdup ("wmav2");
2390 case CODEC_WMAV3
: return g_strdup ("wmav3");
2391 case CODEC_PCM
: return g_strdup ("pcm");
2393 g_warning ("IMediaStream::CreateCodec (%i): Not implemented.\n", codec_id
);
2395 /* This algorithm needs testing.
2398 int a = (codec_id & 0x000000FF);
2399 int b = (codec_id & 0x0000FF00) >> 8;
2400 int c = (codec_id & 0x00FF0000) >> 16;
2401 int d = (codec_id & 0xFF000000) >> 24;
2403 size = (a != 0) + (b != 0) + (c != 0) + (d != 0);
2405 g_return_val_if_fail (size >= 0 && size <= 4, g_strdup (""));
2407 result = (char *) g_malloc (size + 1);
2410 result [current++] = (char) a;
2412 result [current++] = (char) b;
2414 result [current++] = (char) c;
2416 result [current++] = (char) d;
2417 result [current] = 0;
2419 return g_strdup ("<unknown>");
2425 IMediaStream::IsQueueEmpty ()
2427 return queue
.IsEmpty ();
2431 IMediaStream::GetStreamTypeName ()
2433 switch (GetType ()) {
2434 case MediaTypeVideo
: return "Video";
2435 case MediaTypeAudio
: return "Audio";
2436 case MediaTypeMarker
: return "Marker";
2437 default: return "Unknown";
2442 IMediaStream::ReportSeekCompleted ()
2444 LOG_PIPELINE ("IMediaStream::ReportSeekCompleted ()\n");
2445 input_ended
= false;
2446 output_ended
= false;
2448 if (decoder
!= NULL
)
2449 decoder
->ReportSeekCompleted ();
2453 IMediaStream::GetDemuxer ()
2456 IMediaDemuxer
*result
;
2461 media
= GetMediaReffed ();
2463 g_return_val_if_fail (media
!= NULL
, NULL
);
2465 result
= media
->GetDemuxer ();
2473 IMediaStream::GetDecoder ()
2479 IMediaStream::SetDecoder (IMediaDecoder
*value
)
2489 IMediaStream::GetOutputEnded ()
2491 return output_ended
;
2495 IMediaStream::SetOutputEnded (bool value
)
2497 output_ended
= value
;
2501 IMediaStream::GetInputEnded ()
2507 IMediaStream::SetInputEnded (bool value
)
2509 input_ended
= value
;
2510 if (GetDecoder () != NULL
)
2511 GetDecoder ()->ReportInputEnded ();
2515 IMediaStream::GetBufferedSize ()
2520 if (first_pts
== G_MAXUINT64
|| last_enqueued_pts
== G_MAXUINT64
)
2522 else if (last_popped_pts
== G_MAXUINT64
)
2523 result
= last_enqueued_pts
- first_pts
;
2525 result
= last_enqueued_pts
- last_popped_pts
;
2528 LOG_BUFFERING ("IMediaStream::GetBufferedSize (): id: %i, codec: %s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %llu ms, result: %llu ms\n",
2529 GET_OBJ_ID (this), codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
), MilliSeconds_FromPts (result
));
2536 #define TO_MS(x) (MilliSeconds_FromPts (x) == 1844674407370955ULL ? -1 : MilliSeconds_FromPts (x))
2539 IMediaStream::PrintBufferInformation ()
2541 guint64 buffer_size
= GetBufferedSize ();
2543 printf (" <%s: ", codec
);
2545 if (GetSelected ()) {
2546 printf ("size: %.4llu, first: %.4lli, last popped: %.4lli, last enq: %.4lli, frames enq: %i>",
2547 TO_MS (buffer_size
), TO_MS (first_pts
), TO_MS (last_popped_pts
),
2548 TO_MS (last_enqueued_pts
), queue
.Length ());
2550 printf ("(not selected) >");
2556 IMediaStream::EnqueueFrame (MediaFrame
*frame
)
2561 g_return_if_fail (Media::InMediaThread ());
2563 media
= GetMediaReffed ();
2564 g_return_if_fail (media
!= NULL
);
2566 if (media
->IsStopped ()) {
2567 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p): stopped, not enqueuing frame.\n", frame
);
2572 if (first_pts
== G_MAXUINT64
)
2573 first_pts
= frame
->pts
;
2575 LOG_PIPELINE ("IMediaStream::EnqueueFrame (%p) %s %" G_GUINT64_FORMAT
" ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
2578 if (last_enqueued_pts
> frame
->pts
&& last_enqueued_pts
!= G_MAXUINT64
&& frame
->event
!= FrameEventEOF
&& frame
->buflen
> 0) {
2579 g_warning ("IMediaStream::EnqueueFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %llu ms, "
2580 "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",
2581 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2582 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), frame
, frame
->buflen
, MilliSeconds_FromPts (frame
->pts
));
2586 last_enqueued_pts
= frame
->pts
;
2587 first
= queue
.LinkedList ()->Length () == 0;
2588 queue
.LinkedList ()->Append (new StreamNode (frame
));
2591 SetLastAvailablePts (frame
->pts
);
2594 EmitSafe (FirstFrameEnqueuedEvent
);
2601 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",
2602 codec
, first
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2603 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
- first_pts
), frame
, frame
->buflen
);
2607 IMediaStream::PopFrame ()
2609 MediaFrame
*result
= NULL
;
2610 StreamNode
*node
= NULL
;
2612 // We use the queue lock to synchronize access to
2613 // last_popped_pts/last_enqueued_pts/first_pts
2616 node
= (StreamNode
*) queue
.LinkedList ()->First ();
2618 result
= node
->GetFrame ();
2620 queue
.LinkedList ()->Remove (node
);
2621 last_popped_pts
= result
->pts
;
2625 LOG_BUFFERING ("IMediaStream::PopFrame (): codec: %.5s, first_pts: %" G_GUINT64_FORMAT
" ms, last_popped_pts: %" G_GUINT64_FORMAT
" ms, last_enqueued_pts: %llu ms, buffer: %llu ms, frame: %p, frame->buflen: %i\n",
2626 codec
, MilliSeconds_FromPts (first_pts
), MilliSeconds_FromPts (last_popped_pts
), MilliSeconds_FromPts (last_enqueued_pts
),
2627 MilliSeconds_FromPts (last_popped_pts
!= G_MAXUINT64
? last_enqueued_pts
- last_popped_pts
: last_enqueued_pts
), result
, result
? result
->buflen
: 0);
2629 if (!input_ended
&& !output_ended
&& result
!= NULL
) {
2630 IMediaDemuxer
*demuxer
= GetDemuxer ();
2631 if (demuxer
!= NULL
)
2632 demuxer
->FillBuffers ();
2639 IMediaStream::ClearQueue ()
2641 LOG_BUFFERING ("IMediaStream::ClearQueue ()\n");
2643 queue
.LinkedList ()->Clear (true);
2644 first_pts
= G_MAXUINT64
;
2645 last_popped_pts
= G_MAXUINT64
;
2646 last_enqueued_pts
= G_MAXUINT64
;
2651 IMediaStream::SetSelected (bool value
)
2653 Media
*media
= GetMediaReffed ();
2657 if (media
->GetDemuxer ())
2658 media
->GetDemuxer ()->UpdateSelected (this);
2664 * IMediaStream.StreamNode
2667 IMediaStream::StreamNode::StreamNode (MediaFrame
*f
)
2673 IMediaStream::StreamNode::~StreamNode ()
2682 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
, IMediaSource
*source
) : IMediaObject (kind
, media
)
2684 this->source
= source
;
2685 this->source
->ref ();
2690 pending_stream
= NULL
;
2691 pending_fill_buffers
= false;
2694 IMediaDemuxer::IMediaDemuxer (Type::Kind kind
, Media
*media
)
2695 : IMediaObject (kind
, media
)
2702 pending_stream
= NULL
;
2706 IMediaDemuxer::Dispose ()
2708 if (streams
!= NULL
) {
2709 IMediaStream
**tmp
= streams
;
2710 int stream_count
= this->stream_count
;
2712 for (int i
= 0; i
< stream_count
; i
++) {
2713 tmp
[i
]->Dispose ();
2722 if (pending_stream
!= NULL
) {
2723 pending_stream
->unref ();
2724 pending_stream
= NULL
;
2727 IMediaObject::Dispose ();
2731 IMediaDemuxer::OpenCallback (MediaClosure
*closure
)
2733 IMediaDemuxer
*demuxer
;
2735 LOG_PIPELINE ("IMediaDemuxer::OpenCallback (%p)\n", closure
);
2737 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2738 demuxer
->OpenDemuxerAsync ();
2740 return MEDIA_SUCCESS
;
2744 IMediaDemuxer::EnqueueOpen ()
2746 MediaClosure
*closure
;
2747 Media
*media
= GetMediaReffed ();
2749 LOG_PIPELINE ("IMediaDemuxer::EnqueueOpen ()\n");
2751 closure
= new MediaClosure (media
, OpenCallback
, this, "IMediaDemuxer::OpenCallback");
2752 media
->EnqueueWork (closure
, false);
2758 IMediaDemuxer::ReportOpenDemuxerCompleted ()
2760 Media
*media
= GetMediaReffed ();
2762 LOG_PIPELINE ("IMediaDemuxer::ReportDemuxerOpenCompleted () media: %p\n", media
);
2767 // Media might be null if we got disposed for some reason.
2771 media
->ReportOpenDemuxerCompleted ();
2776 IMediaDemuxer::ReportGetFrameProgress (double progress
)
2778 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameProgress (%f)\n", progress
);
2782 IMediaDemuxer::ReportSwitchMediaStreamCompleted (IMediaStream
*stream
)
2784 LOG_PIPELINE ("IMediaDemuxer::ReportSwitchMediaStreamCompleted (%p)\n", stream
);
2788 IMediaDemuxer::ReportGetDiagnosticCompleted (MediaStreamSourceDiagnosticKind kind
, gint64 value
)
2790 LOG_PIPELINE ("IMediaDemuxer::ReportGetDiagnosticCompleted (%i, %lld)\n", kind
, value
);
2794 IMediaDemuxer::ReportGetFrameCompleted (MediaFrame
*frame
)
2796 Media
*media
= GetMediaReffed ();
2798 g_return_if_fail (media
!= NULL
);
2800 if (media
->IsStopped ())
2801 goto cleanup
; /* if we're stopped, just drop what we're doing. */
2803 g_return_if_fail (frame
== NULL
|| (frame
!= NULL
&& frame
->stream
!= NULL
));
2804 g_return_if_fail (pending_stream
!= NULL
); // we must be waiting for a frame ...
2806 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p) %i %s %llu ms\n", frame
, GET_OBJ_ID (this), frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : (guint64
) -1);
2808 if (frame
== NULL
) {
2809 LOG_PIPELINE ("IMediaDemuxer::ReportGetFrameCompleted (%p): input end signaled for %s stream.\n", frame
, pending_stream
->GetStreamTypeName ());
2810 // No more data for this stream
2811 pending_stream
->SetInputEnded (true);
2812 } else if (!frame
->stream
->IsDisposed ()) {
2813 IMediaDecoder
*decoder
= frame
->stream
->GetDecoder ();
2814 if (decoder
!= NULL
)
2815 decoder
->DecodeFrameAsync (frame
, true /* always enqueue */);
2818 pending_stream
->unref ();
2819 pending_stream
= NULL
; // not waiting for anything more
2821 // enqueue some more
2830 IMediaDemuxer::ReportSeekCompletedCallback (MediaClosure
*c
)
2832 MediaReportSeekCompletedClosure
*closure
= (MediaReportSeekCompletedClosure
*) c
;
2833 IMediaDemuxer
*demuxer
;
2835 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
2836 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
2838 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2839 demuxer
->ReportSeekCompleted (closure
->GetPts ());
2841 return MEDIA_SUCCESS
;
2845 IMediaDemuxer::EnqueueReportSeekCompleted (guint64 pts
)
2847 Media
*media
= GetMediaReffed ();
2848 MediaClosure
*closure
= new MediaReportSeekCompletedClosure (media
, ReportSeekCompletedCallback
, this, pts
);
2849 media
->EnqueueWork (closure
);
2855 IMediaDemuxer::ReportSeekCompleted (guint64 pts
)
2859 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%llu)\n", pts
);
2861 if (!Media::InMediaThread ()) {
2862 EnqueueReportSeekCompleted (pts
);
2866 media
= GetMediaReffed ();
2868 g_return_if_fail (media
!= NULL
);
2871 for (int i
= 0; i
< GetStreamCount (); i
++) {
2872 IMediaStream
*stream
= GetStream (i
);
2877 stream
->ReportSeekCompleted ();
2880 media
->ReportSeekCompleted (pts
);
2883 if (pending_stream
) {
2884 pending_stream
->unref ();
2885 pending_stream
= NULL
;
2888 pending_fill_buffers
= false;
2891 LOG_PIPELINE ("IMediaDemuxer::ReportSeekCompleted (%llu) [Done]\n", pts
);
2895 IMediaDemuxer::OpenDemuxerAsync ()
2897 g_return_if_fail (opened
== false);
2901 OpenDemuxerAsyncInternal ();
2905 IMediaDemuxer::GetFrameCallback (MediaClosure
*c
)
2907 MediaGetFrameClosure
*closure
= (MediaGetFrameClosure
*) c
;
2908 IMediaDemuxer
*demuxer
;
2910 g_return_val_if_fail (closure
!= NULL
, MEDIA_FAIL
);
2911 g_return_val_if_fail (closure
->GetStream () != NULL
, MEDIA_FAIL
);
2912 g_return_val_if_fail (closure
->GetContext () != NULL
, MEDIA_FAIL
);
2914 demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
2915 demuxer
->GetFrameAsync (closure
->GetStream ());
2917 return MEDIA_SUCCESS
;
2921 IMediaDemuxer::EnqueueGetFrame (IMediaStream
*stream
)
2923 g_return_if_fail (pending_stream
== NULL
); // we can't be waiting for another frame.
2925 Media
*media
= GetMediaReffed ();
2926 MediaClosure
*closure
= new MediaGetFrameClosure (media
, GetFrameCallback
, this, stream
);
2927 media
->EnqueueWork (closure
);
2933 IMediaDemuxer::GetFrameAsync (IMediaStream
*stream
)
2937 LOG_PIPELINE ("IMediaDemuxer::GetFrameAsync (%p) %s InMediaThread: %i\n", stream
, stream
->GetStreamTypeName (), Media::InMediaThread ());
2939 if (!Media::InMediaThread ()) {
2940 EnqueueGetFrame (stream
);
2944 media
= GetMediaReffed ();
2946 g_return_if_fail (media
!= NULL
);
2948 if (media
->IsStopped ())
2951 if (stream
!= NULL
&& pending_stream
== NULL
) {
2952 pending_stream
= stream
;
2953 pending_stream
->ref ();
2954 GetFrameAsyncInternal (stream
);
2962 IMediaDemuxer::SeekCallback (MediaClosure
*closure
)
2964 MediaSeekClosure
*seek
= (MediaSeekClosure
*) closure
;
2965 seek
->GetDemuxer ()->SeekAsyncInternal (seek
->GetPts ());
2966 return MEDIA_SUCCESS
;
2970 IMediaDemuxer::EnqueueSeek (guint64 pts
)
2972 Media
*media
= GetMediaReffed ();
2973 MediaSeekClosure
*closure
;
2975 g_return_if_fail (media
!= NULL
);
2977 closure
= new MediaSeekClosure (media
, SeekCallback
, this, pts
);
2978 media
->EnqueueWork (closure
, true);
2984 IMediaDemuxer::SeekAsync (guint64 pts
)
2986 LOG_PIPELINE ("IMediaDemuxer::SeekAsync (%llu)\n", pts
);
2991 // we need to run this on the media thread, not the main thread.
2996 IMediaDemuxer::ClearBuffers ()
2998 pending_fill_buffers
= false;
3000 /* Clear all the buffer queues */
3001 for (int i
= 0; i
< GetStreamCount (); i
++) {
3002 IMediaStream
*stream
= GetStream (i
);
3007 stream
->ClearQueue ();
3012 IMediaDemuxer::FillBuffersCallback (MediaClosure
*closure
)
3014 IMediaDemuxer
*demuxer
= (IMediaDemuxer
*) closure
->GetContext ();
3015 demuxer
->FillBuffersInternal ();
3016 return MEDIA_SUCCESS
;
3020 IMediaDemuxer::FillBuffers ()
3022 Media
*media
= NULL
;
3023 MediaClosure
*closure
;
3024 bool enqueue
= true;
3027 if (pending_fill_buffers
) {
3028 // there's already a FillBuffers request enqueued
3031 media
= GetMediaReffed ();
3032 if (media
== NULL
) {
3036 pending_fill_buffers
= true;
3042 closure
= new MediaClosure (media
, FillBuffersCallback
, this, "IMediaDemuxer::FillBuffersCallback");
3043 media
->EnqueueWork (closure
);
3052 IMediaDemuxer::FillBuffersInternal ()
3054 IMediaStream
*stream
;
3055 IMediaStream
*request_stream
= NULL
;
3056 guint64 min_buffered_size
= G_MAXUINT64
;
3057 MediaResult result
= MEDIA_SUCCESS
;
3058 Media
*media
= GetMediaReffed ();
3059 guint64 buffering_time
= 0;
3060 guint64 buffered_size
= 0;
3062 int media_streams
= 0;
3064 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal (), %i %s buffering time: %llu = %llu 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");
3067 pending_fill_buffers
= false;
3073 // If we're waiting for something, there's nothing to do here.
3074 if (pending_stream
!= NULL
)
3077 // Find the stream with the smallest buffered size, and request a frame from that stream.
3078 g_return_if_fail (media
!= NULL
);
3080 // If we're stopped there is nothing to do here.
3081 if (media
->IsStopped ()) {
3082 LOG_PIPELINE ("IMediaDemuxer::FillBuffersInternal (): stopped\n");
3086 buffering_time
= media
->GetBufferingTime ();
3088 if (buffering_time
== 0) {
3089 // Play as soon as possible.
3090 // However we still need something in the buffer, at least one frame, oherwise the buffering progress
3091 // will stay at 0%, so up the buffering time to 1 ms. This way we'll reach 100% buffering progress when
3092 // all streams have 1 frame queued.
3096 for (int i
= 0; i
< GetStreamCount (); i
++) {
3097 IMediaDecoder
*decoder
= NULL
;
3099 stream
= GetStream (i
);
3100 if (!stream
->GetSelected ())
3103 if (stream
->GetType () != MediaTypeVideo
&&
3104 stream
->GetType () != MediaTypeAudio
)
3108 if (stream
->GetOutputEnded ()) {
3110 continue; // this stream has ended.
3113 decoder
= stream
->GetDecoder ();
3114 if (decoder
== NULL
) {
3115 fprintf (stderr
, "IMediaDemuxer::FillBuffersInternal () %s stream has no decoder (id: %i refcount: %i)\n", stream
->GetStreamTypeName (), GET_OBJ_ID (stream
), stream
->GetRefCount ());
3116 continue; // no decoder??
3119 buffered_size
= stream
->GetBufferedSize ();
3120 min_buffered_size
= MIN (min_buffered_size
, buffered_size
);
3122 if (buffered_size
>= buffering_time
)
3123 continue; // this stream has enough data buffered.
3125 if (!decoder
->IsDecoderQueueEmpty ())
3126 continue; // this stream is waiting for data to be decoded.
3128 if (buffered_size
<= min_buffered_size
)
3129 request_stream
= stream
;
3131 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: %llu ms\n",
3132 stream
->codec
, GET_OBJ_ID (stream
), result
, MilliSeconds_FromPts (buffered_size
), MilliSeconds_FromPts (buffering_time
), MilliSeconds_FromPts (stream
->GetLastPoppedPts ()));
3135 if (request_stream
!= NULL
) {
3136 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
));
3137 GetFrameAsync (request_stream
);
3140 if (media_streams
> 0) {
3141 if (ended
== media_streams
) {
3142 media
->ReportBufferingProgress (1.0);
3144 if (min_buffered_size
> 0 && buffering_time
> 0) {
3145 double progress
= ((double) min_buffered_size
/ (double) buffering_time
);
3146 media
->ReportBufferingProgress (progress
);
3155 LOG_BUFFERING ("IMediaDemuxer::FillBuffersInternal () [Done]. BufferedSize: %" G_GUINT64_FORMAT
" ms\n", MilliSeconds_FromPts (GetBufferedSize ()));
3159 IMediaDemuxer::GetBufferedSize ()
3161 guint64 result
= G_MAXUINT64
;
3162 IMediaStream
*stream
;
3164 for (int i
= 0; i
< GetStreamCount (); i
++) {
3165 stream
= GetStream (i
);
3166 if (!stream
->GetSelected ())
3169 if (stream
->GetType () != MediaTypeVideo
&& stream
->GetType () != MediaTypeAudio
)
3172 result
= MIN (result
, stream
->GetBufferedSize ());
3179 IMediaDemuxer::GetLastAvailablePts ()
3181 guint64 result
= G_MAXUINT64
;
3182 IMediaStream
*stream
;
3184 for (int i
= 0; i
< GetStreamCount (); i
++) {
3185 stream
= GetStream (i
);
3187 if (stream
== NULL
|| !stream
->GetSelected ())
3190 result
= MIN (result
, stream
->GetLastAvailablePts ());
3193 if (result
== G_MAXUINT64
)
3201 IMediaDemuxer::PrintBufferInformation ()
3203 printf ("Buffer: %lld", MilliSeconds_FromPts (GetBufferedSize ()));
3204 for (int i
= 0; i
< GetStreamCount (); i
++) {
3205 GetStream (i
)->PrintBufferInformation ();
3212 IMediaDemuxer::GetDuration ()
3215 for (int i
= 0; i
< GetStreamCount (); i
++)
3216 result
= MAX (result
, GetStream (i
)->duration
);
3221 IMediaDemuxer::GetStream (int index
)
3223 return (index
< 0 || index
>= stream_count
) ? NULL
: streams
[index
];
3230 MediaFrame::MediaFrame (IMediaStream
*stream
)
3231 : EventObject (Type::MEDIAFRAME
, true)
3235 g_return_if_fail (stream
!= NULL
);
3237 this->stream
= stream
;
3238 this->stream
->ref ();
3241 MediaFrame::MediaFrame (IMediaStream
*stream
, guint8
*buffer
, guint32 buflen
, guint64 pts
, bool keyframe
)
3242 : EventObject (Type::MEDIAFRAME
, true)
3246 g_return_if_fail (stream
!= NULL
);
3248 this->stream
= stream
;
3249 this->stream
->ref ();
3250 this->buffer
= buffer
;
3251 this->buflen
= buflen
;
3255 if (buflen
> 4 && false) {
3256 printf ("MediaFrame::MediaFrame () %s buffer: ", stream
->GetStreamTypeName ());
3257 for (int i
= 0; i
< 4; i
++)
3258 printf (" 0x%x", buffer
[i
]);
3264 AddState (MediaFrameKeyFrame
);
3268 MediaFrame::Initialize ()
3270 decoder_specific_data
= NULL
;
3282 for (int i
= 0; i
< 4; i
++) {
3293 MediaFrame::~MediaFrame ()
3298 MediaFrame::Dispose ()
3300 IMediaDecoder
*decoder
;
3303 // We can be called either on the main thread just before destruction
3304 // (in which case there are no races since the code which unreffed us
3305 // is the only code which knows about us), or at any time from the
3308 if (GetRefCount () != 0 && stream
!= NULL
) {
3309 if (!Media::InMediaThread ()) {
3310 // if refcount != 0 we're not being called just before destruction, in which case we should
3311 // only be on the media thread.
3312 printf ("MediaFrame::Dispose (): this method should only be called from the media thread.\n");
3317 if (decoder_specific_data
!= NULL
&& stream
!= NULL
) {
3318 decoder
= stream
->GetDecoder ();
3319 if (decoder
!= NULL
)
3320 decoder
->Cleanup (this);
3333 EventObject::Dispose ();
3337 MediaFrame::SetSrcSlideY (int value
)
3343 MediaFrame::SetSrcSlideH (int value
)
3349 MediaFrame::SetSrcStride (int a
, int b
, int c
, int d
)
3358 MediaFrame::SetDataStride (guint8
* a
, guint8
* b
, guint8
* c
, guint8
* d
)
3360 data_stride
[0] = a
;
3361 data_stride
[1] = b
;
3362 data_stride
[2] = c
;
3363 data_stride
[3] = d
;
3367 * IMediaObject.EventData
3370 IMediaObject::EventData::EventData (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3372 this->event_id
= event_id
;
3373 this->handler
= handler
;
3374 this->context
= context
;
3375 this->context
->ref ();
3376 this->invoke_on_main_thread
= invoke_on_main_thread
;
3379 IMediaObject::EventData::~EventData ()
3386 * IMediaObject.EmitData
3389 IMediaObject::EmitData::EmitData (int event_id
, EventHandler handler
, EventObject
*context
, EventArgs
*args
)
3391 this->event_id
= event_id
;
3392 this->handler
= handler
;
3393 this->context
= context
;
3394 this->context
->ref ();
3400 IMediaObject::EmitData::~EmitData ()
3414 IMediaObject::IMediaObject (Type::Kind kind
, Media
*media
)
3415 : EventObject (kind
, true)
3417 this->media
= media
;
3419 this->media
->ref ();
3420 g_return_if_fail (media
!= NULL
);
3422 emit_on_main_thread
= NULL
;
3426 IMediaObject::Dispose ()
3430 // We can be called either on the main thread just before destruction
3431 // (in which case there are no races since the code which unreffed us
3432 // is the only code which knows about us), or at any time from the
3434 if (GetRefCount () != 0 && !Media::InMediaThread ()) {
3435 // if refcount != 0 we're not being called just before destruction, in which case we should
3436 // only be on the media thread.
3437 LOG_PIPELINE ("IMediaObject::Dispose (): this method should only be called from the media thread.\n");
3441 media_mutex
.Lock ();
3446 media_mutex
.Unlock ();
3448 event_mutex
.Lock ();
3451 if (emit_on_main_thread
!= NULL
) {
3452 delete emit_on_main_thread
;
3453 emit_on_main_thread
= NULL
;
3455 event_mutex
.Unlock ();
3457 EventObject::Dispose ();
3461 IMediaObject::AddSafeHandler (int event_id
, EventHandler handler
, EventObject
*context
, bool invoke_on_main_thread
)
3463 LOG_PIPELINE ("IMediaObject::AddSafeHandler (%i, %p, %p, %i)\n", event_id
, handler
, context
, invoke_on_main_thread
);
3466 if (!IsDisposed ()) {
3467 ed
= new EventData (event_id
, handler
, context
, invoke_on_main_thread
);
3468 event_mutex
.Lock ();
3470 events
= new List ();
3471 events
->Append (ed
);
3472 event_mutex
.Unlock ();
3477 IMediaObject::RemoveSafeHandlers (EventObject
*context
)
3482 event_mutex
.Lock ();
3483 if (events
!= NULL
) {
3484 ed
= (EventData
*) events
->First ();
3485 while (ed
!= NULL
) {
3486 next
= (EventData
*) ed
->next
;
3487 if (ed
->context
== context
)
3488 events
->Remove (ed
);
3492 event_mutex
.Unlock ();
3496 IMediaObject::EmitSafe (int event_id
, EventArgs
*args
)
3498 List
*emits
= NULL
; // The events to emit on this thread.
3505 // Create a list of all the events to emit
3506 // don't keep the lock while emitting.
3507 event_mutex
.Lock ();
3508 if (events
!= NULL
) {
3509 ed
= (EventData
*) events
->First ();
3510 while (ed
!= NULL
) {
3511 if (ed
->event_id
== event_id
) {
3512 emit
= new EmitData (event_id
, ed
->handler
, ed
->context
, args
);
3513 if (ed
->invoke_on_main_thread
) {
3514 if (emit_on_main_thread
== NULL
)
3515 emit_on_main_thread
= new List ();
3516 emit_on_main_thread
->Append (emit
);
3519 emits
= new List ();
3520 emits
->Append (emit
);
3523 ed
= (EventData
*) ed
->next
;
3526 event_mutex
.Unlock ();
3528 // emit the events to be emitted on this thread
3531 if (Surface::InMainThread ()) {
3532 // if we're already on the main thread,
3533 // we can the events to be emitted
3534 // on the main thread
3536 event_mutex
.Lock ();
3537 tmp
= emit_on_main_thread
;
3538 emit_on_main_thread
= NULL
;
3539 event_mutex
.Unlock ();
3542 AddTickCallSafe (EmitListCallback
);
3551 IMediaObject::EmitListMain ()
3556 event_mutex
.Lock ();
3557 list
= emit_on_main_thread
;
3558 emit_on_main_thread
= NULL
;
3559 event_mutex
.Unlock ();
3564 IMediaObject::EmitListCallback (EventObject
*obj
)
3566 IMediaObject
*media_obj
= (IMediaObject
*) obj
;
3567 media_obj
->EmitListMain ();
3571 IMediaObject::EmitList (List
*list
)
3578 emit
= (EmitData
*) list
->First ();
3579 while (emit
!= NULL
) {
3580 emit
->handler (this, emit
->args
, emit
->context
);
3581 emit
= (EmitData
*) emit
->next
;
3588 IMediaObject::GetMediaReffed ()
3591 media_mutex
.Lock ();
3595 media_mutex
.Unlock ();
3600 IMediaObject::ReportErrorOccurred (char const *message
)
3602 g_return_if_fail (media
!= NULL
);
3604 media
->ReportErrorOccurred (message
);
3608 IMediaObject::ReportErrorOccurred (MediaResult result
)
3610 g_return_if_fail (media
!= NULL
);
3612 media
->ReportErrorOccurred (result
);
3616 IMediaObject::ReportErrorOccurred (ErrorEventArgs
*args
)
3618 g_return_if_fail (media
!= NULL
);
3620 media
->ReportErrorOccurred (args
);
3624 IMediaObject::SetMedia (Media
*value
)
3626 media_mutex
.Lock ();
3632 media_mutex
.Unlock ();
3639 IMediaSource::IMediaSource (Type::Kind kind
, Media
*media
)
3640 : IMediaObject (kind
, media
)
3642 pthread_mutexattr_t attribs
;
3643 pthread_mutexattr_init (&attribs
);
3644 pthread_mutexattr_settype (&attribs
, PTHREAD_MUTEX_RECURSIVE
);
3645 pthread_mutex_init (&mutex
, &attribs
);
3646 pthread_mutexattr_destroy (&attribs
);
3648 pthread_cond_init (&condition
, NULL
);
3651 IMediaSource::~IMediaSource ()
3653 pthread_mutex_destroy (&mutex
);
3654 pthread_cond_destroy (&condition
);
3658 IMediaSource::Dispose ()
3660 IMediaObject::Dispose ();
3664 IMediaSource::Lock ()
3666 pthread_mutex_lock (&mutex
);
3670 IMediaSource::Unlock ()
3672 pthread_mutex_unlock (&mutex
);
3676 IMediaSource::ReadSome (void *buf
, guint32 n
)
3680 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u)\n", GET_OBJ_ID (this), buf
, n
);
3684 result
= ReadInternal (buf
, n
);
3686 LOG_PIPELINE_EX ("IMediaSource<%i>::ReadSome (%p, %u) read %i, position: %lld\n", GET_OBJ_ID (this), buf
, n
, result
, GetPosition ());
3694 IMediaSource::ReadAll (void *buf
, guint32 n
)
3697 gint64 prev
= GetPosition ();
3698 gint64 avail
= GetLastAvailablePosition ();
3700 //printf ("IMediaSource::ReadAll (%p, %u), position: %lld\n", buf, n, prev);
3702 read
= ReadSome (buf
, n
);
3704 if ((gint64
) read
!= (gint64
) n
) {
3705 FileSource
*fs
= NULL
;
3707 if (GetType () == MediaSourceTypeFile
)
3708 fs
= (FileSource
*) this;
3709 g_warning ("IMediaSource::ReadInternal (%i): Read failed, read %i bytes. available size: %lld, size: %lld, pos: %lld, prev pos: %lld, position not available: %lld, feof: %i, ferror: %i, strerror: %s\n",
3710 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>");
3711 print_stack_trace ();
3714 LOG_PIPELINE_EX ("IMediaSource<%d>::ReadAll (%p, %u), read: %d [Done].\n", GET_OBJ_ID (this), buf
, n
, read
);
3716 return (gint64
) read
== (gint64
) n
;
3720 IMediaSource::Peek (void *buf
, guint32 n
)
3727 read
= PeekInternal (buf
, n
);
3728 result
= read
== (gint64
) n
;
3732 LOG_PIPELINE ("IMediaSource::Peek (%p, %u): peek result: %i, read %lld bytes.\n", buf
, n
, result
, read
);
3738 IMediaSource::Seek (gint64 offset
, int mode
)
3740 LOG_PIPELINE ("IMediaSource<%d> (%s)::Seek (%lld, %d = %s)\n",
3741 GET_OBJ_ID (this), ToString (), offset
, mode
, mode
== SEEK_SET
? "SEEK_SET"
3742 : (mode
== SEEK_CUR
? "SEEK_CUR" : (mode
== SEEK_END
? "SEEK_END" : "<invalid value>")));
3746 result
= SeekInternal (offset
, mode
);
3752 IMediaSource::IsPositionAvailable (gint64 position
, bool *eof
)
3754 gint64 available
= GetLastAvailablePosition ();
3755 gint64 size
= GetSize ();
3759 if (size
!= -1 && size
< position
) {
3760 // Size is known and smaller than the requested position
3765 if (available
!= -1 && available
< position
) {
3766 // Not everything is available and the available position is smaller than the requested position
3771 if (size
== -1 && available
== -1) {
3772 // Size is not known, but everything is available??
3773 // This is probably due to a bug in the derived *Source class
3775 fprintf (stderr
, "Moonlight: media assert error (invalid source size), media playback errors will probably occur\n");
3783 IMediaSource::GetLastAvailablePosition ()
3787 result
= GetLastAvailablePositionInternal ();
3793 IMediaSource::GetPositionInternal ()
3795 // This method should be overridden (or never called for the classes which doesn't override it).
3796 g_warning ("IMediaSource (%s)::GetPositionInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3797 print_stack_trace ();
3802 IMediaSource::SeekInternal (gint64 offset
, int mode
)
3804 g_warning ("IMediaSource (%s)::SeekInternal (%lld, %i): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName (), offset
, mode
);
3805 print_stack_trace ();
3811 IMediaSource::ReadInternal (void *buffer
, guint32 n
)
3813 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
);
3814 print_stack_trace ();
3820 IMediaSource::PeekInternal (void *buffer
, guint32 n
)
3822 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
);
3823 print_stack_trace ();
3829 IMediaSource::GetSizeInternal ()
3831 g_warning ("IMediaSource (%s)::GetSizeInternal (): You hit a bug in moonlight, please attach gdb, get a stack trace and file bug.", GetTypeName ());
3832 print_stack_trace ();
3838 IMediaSource::GetPosition ()
3842 result
= GetPositionInternal ();
3848 IMediaSource::GetSize ()
3852 result
= GetSizeInternal ();
3862 IMediaDemuxer::SetStreams (IMediaStream
** streams
, int count
)
3864 this->streams
= streams
;
3865 this->stream_count
= count
;
3867 for (int i
= 0; i
< count
; i
++)
3868 this->streams
[i
]->ref ();
3872 IMediaDemuxer::AddStream (IMediaStream
*stream
)
3874 g_return_val_if_fail (stream
!= NULL
, -1);
3877 streams
= (IMediaStream
**) g_realloc (streams
, stream_count
* sizeof (IMediaStream
*));
3878 streams
[stream_count
- 1] = stream
;
3881 return stream_count
- 1;
3888 IMediaDecoder::IMediaDecoder (Type::Kind kind
, Media
*media
, IMediaStream
*stream
) : IMediaObject (kind
, media
)
3890 this->stream
= NULL
;
3892 g_return_if_fail (stream
!= NULL
);
3894 this->stream
= stream
;
3895 this->stream
->ref ();
3899 input_ended
= false;
3903 IMediaDecoder::Dispose ()
3905 if (stream
!= NULL
) {
3906 IMediaStream
*s
= stream
;
3915 IMediaObject::Dispose ();
3919 IMediaDecoder::ReportSeekCompleted ()
3922 input_ended
= false;
3927 IMediaDecoder::ReportInputEnded ()
3930 if (IsDecoderQueueEmpty ()) {
3936 IMediaDecoder::ReportDecodeFrameCompleted (MediaFrame
*frame
)
3938 IMediaDemuxer
*demuxer
;
3939 IMediaStream
*stream
;
3940 Media
*media
= NULL
;
3942 LOG_PIPELINE ("IMediaDecoder::ReportDecodeFrameCompleted (%p) %s %llu ms\n", frame
, frame
? frame
->stream
->GetStreamTypeName () : "", frame
? MilliSeconds_FromPts (frame
->pts
) : 0);
3944 g_return_if_fail (frame
!= NULL
);
3946 media
= GetMediaReffed ();
3947 g_return_if_fail (media
!= NULL
);
3949 stream
= frame
->stream
;
3953 frame
->stream
->EnqueueFrame (frame
);
3955 demuxer
= stream
->GetDemuxer ();
3956 if (demuxer
!= NULL
)
3957 demuxer
->FillBuffers ();
3959 if (input_ended
&& IsDecoderQueueEmpty ())
3968 IMediaDecoder::DecodeFrameCallback (MediaClosure
*closure
)
3971 IMediaDecoder
*decoder
= (IMediaDecoder
*) closure
->GetContext ();
3972 IMediaDecoder::FrameNode
*node
= (IMediaDecoder::FrameNode
*) decoder
->queue
.Pop ();
3975 decoder
->DecodeFrameAsync (node
->frame
, false);
3979 return MEDIA_SUCCESS
;
3983 IMediaDecoder::DecodeFrameAsync (MediaFrame
*frame
, bool enqueue_always
)
3987 LOG_PIPELINE ("IMediaDecoder::DecodeFrameAsync (%p) %s\n", frame
, (frame
&& frame
->stream
) ? frame
->stream
->GetStreamTypeName () : NULL
);
3992 g_return_if_fail (frame
!= NULL
);
3994 media
= GetMediaReffed ();
3996 g_return_if_fail (media
!= NULL
);
3998 if (enqueue_always
|| !Media::InMediaThread ()) {
3999 MediaClosure
*closure
= new MediaClosure (media
, DecodeFrameCallback
, this, "IMediaDecoder::DecodeFrameCallback");
4000 queue
.Push (new FrameNode (frame
));
4001 media
->EnqueueWork (closure
);
4006 if (media
->IsStopped ())
4009 DecodeFrameAsyncInternal (frame
);
4016 IMediaDecoder::OpenDecoderAsync ()
4018 LOG_PIPELINE ("IMediaDecoder::OpenDecoderAsync ()\n");
4020 g_return_if_fail (opening
== false);
4021 g_return_if_fail (opened
== false);
4024 OpenDecoderAsyncInternal ();
4028 IMediaDecoder::ReportOpenDecoderCompleted ()
4030 Media
*media
= GetMediaReffed ();
4032 LOG_PIPELINE ("IMediaDecoder::ReportOpenDecoderCompleted ()\n");
4037 g_return_if_fail (media
!= NULL
);
4039 media
->ReportOpenDecoderCompleted (this);
4047 IImageConverter::IImageConverter (Type::Kind kind
, Media
*media
, VideoStream
*stream
) : IMediaObject (kind
, media
)
4049 output_format
= MoonPixelFormatNone
;
4050 input_format
= MoonPixelFormatNone
;
4051 this->stream
= stream
;
4058 VideoStream::VideoStream (Media
*media
) : IMediaStream (Type::VIDEOSTREAM
, media
)
4061 bits_per_sample
= 0;
4068 VideoStream::VideoStream (Media
*media
, int codec_id
, guint32 width
, guint32 height
, guint64 duration
, gpointer extra_data
, guint32 extra_data_size
)
4069 : IMediaStream (Type::VIDEOSTREAM
, media
)
4072 bits_per_sample
= 0;
4075 this->height
= height
;
4076 this->width
= width
;
4077 this->duration
= duration
;
4078 this->codec_id
= codec_id
;
4079 this->codec
= CreateCodec (codec_id
);
4080 this->extra_data
= extra_data
;
4081 this->extra_data_size
= extra_data_size
;
4084 VideoStream::~VideoStream ()
4089 VideoStream::Dispose ()
4092 converter
->Dispose ();
4093 converter
->unref ();
4096 IMediaStream::Dispose ();
4100 * MediaMarkerFoundClosure
4103 MediaMarkerFoundClosure::MediaMarkerFoundClosure (Media
*media
, MediaCallback
*callback
, MediaElement
*context
)
4104 : MediaClosure (Type::MEDIAMARKERFOUNDCLOSURE
, media
, callback
, context
)
4110 MediaMarkerFoundClosure::Dispose ()
4116 MediaClosure::Dispose ();
4120 MediaMarkerFoundClosure::SetMarker (MediaMarker
*marker
)
4123 this->marker
->unref ();
4124 this->marker
= marker
;
4126 this->marker
->ref ();
4133 MediaMarker::MediaMarker (const char *type
, const char *text
, guint64 pts
)
4134 : EventObject (Type::MEDIAMARKER
)
4136 this->type
= g_strdup (type
);
4137 this->text
= g_strdup (text
);
4141 MediaMarker::~MediaMarker ()
4151 MarkerStream::MarkerStream (Media
*media
) : IMediaStream (Type::MARKERSTREAM
, media
)
4157 MarkerStream::Dispose ()
4164 IMediaStream::Dispose ();
4168 MarkerStream::MarkerFound (MediaFrame
*frame
)
4170 LOG_PIPELINE ("MarkerStream::MarkerFound ().\n");
4172 if (GetDecoder () == NULL
) {
4173 LOG_PIPELINE ("MarkerStream::MarkerFound (): Got marker, but there's no decoder for the marker.\n");
4177 GetDecoder ()->DecodeFrameAsync (frame
, false);
4181 MarkerStream::FrameEnqueued ()
4185 LOG_PIPELINE ("MarkerStream::FrameEnqueued ().\n");
4187 frame
= PopFrame ();
4189 if (frame
== NULL
) {
4190 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No frame.\n");
4194 if (closure
!= NULL
) {
4195 closure
->SetMarker (frame
->marker
);
4197 closure
->SetMarker (NULL
);
4199 LOG_PIPELINE ("MarkerStream::FrameEnqueued (): No callback.\n");
4201 list
.Append (new MediaMarker::Node (frame
->marker
));
4209 MarkerStream::Pop ()
4211 MediaMarker
*result
= NULL
;
4212 MediaMarker::Node
*node
;
4215 node
= (MediaMarker::Node
*) list
.First ();
4217 result
= node
->marker
;
4227 MarkerStream::SetCallback (MediaMarkerFoundClosure
*closure
)
4230 this->closure
->unref ();
4231 this->closure
= closure
;
4233 this->closure
->ref ();
4239 MediaWork::MediaWork (MediaClosure
*c
)
4241 g_return_if_fail (c
!= NULL
);
4247 MediaWork::~MediaWork ()
4249 g_return_if_fail (closure
!= NULL
);
4256 * PassThroughDecoderInfo
4260 PassThroughDecoderInfo::Supports (const char *codec
)
4262 const char *video_fourccs
[] = { "yv12", "rgba", NULL
};
4263 const char *audio_fourccs
[] = { "pcm", NULL
};
4265 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4266 if (!strcmp (codec
, video_fourccs
[i
]))
4269 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4270 if (!strcmp (codec
, audio_fourccs
[i
]))
4277 * PassThroughDecoder
4280 PassThroughDecoder::PassThroughDecoder (Media
*media
, IMediaStream
*stream
)
4281 : IMediaDecoder (Type::PASSTHROUGHDECODER
, media
, stream
)
4286 PassThroughDecoder::Dispose ()
4288 IMediaDecoder::Dispose ();
4292 PassThroughDecoder::OpenDecoderAsyncInternal ()
4294 const char *fourcc
= GetStream ()->GetCodec ();
4296 if (!strcmp (fourcc
, "yv12")) {
4297 SetPixelFormat (MoonPixelFormatYUV420P
);
4298 } else if (!strcmp (fourcc
, "rgba")) {
4299 SetPixelFormat (MoonPixelFormatRGBA32
);
4300 } else if (!strcmp (fourcc
, "pcm")) {
4301 // nothing to do here
4303 ReportErrorOccurred (g_strdup_printf ("Unknown fourcc: %s", fourcc
));
4307 ReportOpenDecoderCompleted ();
4311 PassThroughDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4313 frame
->AddState (MediaFrameDecoded
);
4314 if (GetPixelFormat () == MoonPixelFormatYUV420P
) {
4315 VideoStream
*vs
= (VideoStream
*) GetStream ();
4317 frame
->width
= vs
->width
;
4318 frame
->height
= vs
->height
;
4320 frame
->data_stride
[0] = frame
->buffer
;
4321 frame
->data_stride
[1] = frame
->buffer
+ (frame
->width
*frame
->height
);
4322 frame
->data_stride
[2] = frame
->buffer
+ (frame
->width
*frame
->height
)+(frame
->width
/2*frame
->height
/2);
4323 frame
->buffer
= NULL
;
4324 frame
->srcStride
[0] = frame
->width
;
4325 frame
->srcSlideY
= frame
->width
;
4326 frame
->srcSlideH
= frame
->height
;
4328 frame
->AddState (MediaFramePlanar
);
4330 ReportDecodeFrameCompleted (frame
);
4338 NullDecoderInfo::Supports (const char *codec
)
4340 const char *video_fourccs
[] = { "wmv1", "wmv2", "wmv3", "wmva", "vc1", NULL
};
4341 const char *audio_fourccs
[] = { "wmav1","wmav2", "wmav3", "mp3", NULL
};
4343 for (int i
= 0; video_fourccs
[i
] != NULL
; i
++)
4344 if (!strcmp (codec
, video_fourccs
[i
]))
4347 for (int i
= 0; audio_fourccs
[i
] != NULL
; i
++)
4348 if (!strcmp (codec
, audio_fourccs
[i
]))
4359 NullDecoder::NullDecoder (Media
*media
, IMediaStream
*stream
) : IMediaDecoder (Type::NULLDECODER
, media
, stream
)
4363 prev_pts
= G_MAXUINT64
;
4367 NullDecoder::Dispose ()
4372 IMediaDecoder::Dispose ();
4376 NullDecoder::DecodeVideoFrame (MediaFrame
*frame
)
4378 // free encoded buffer and alloc a new one for our image
4379 g_free (frame
->buffer
);
4380 frame
->buflen
= logo_size
;
4381 frame
->buffer
= (guint8
*) g_malloc (frame
->buflen
);
4382 memcpy (frame
->buffer
, logo
, frame
->buflen
);
4383 frame
->AddState (MediaFrameDecoded
);
4385 //printf ("NullVideoDecoder::DecodeFrame () pts: %" G_GUINT64_FORMAT ", w: %i, h: %i\n", frame->pts, w, h);
4387 return MEDIA_SUCCESS
;
4391 NullDecoder::DecodeAudioFrame (MediaFrame
*frame
)
4393 AudioStream
*as
= (AudioStream
*) GetStream ();
4398 // discard encoded data
4399 g_free (frame
->buffer
);
4401 // We have no idea here how long the encoded audio data is
4402 // for the first frame we use 0.1 seconds, for the rest
4403 // we calculate the time since the last frame
4405 if (prev_pts
== G_MAXUINT64
|| frame
->pts
<= prev_pts
) {
4406 samples
= as
->GetSampleRate () / 10; // start off sending 0.1 seconds of audio
4408 diff_pts
= frame
->pts
- prev_pts
;
4409 samples
= (float) as
->GetSampleRate () / (TIMESPANTICKS_IN_SECOND_FLOAT
/ (float) diff_pts
);
4411 prev_pts
= frame
->pts
;
4413 data_size
= samples
* as
->GetChannels () * 2 /* 16 bit audio */;
4415 frame
->buflen
= data_size
;
4416 frame
->buffer
= (guint8
*) g_malloc0 (frame
->buflen
);
4418 frame
->AddState (MediaFrameDecoded
);
4420 return MEDIA_SUCCESS
;
4424 NullDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4426 MediaResult result
= MEDIA_FAIL
;
4427 IMediaStream
*stream
= GetStream ();
4429 if (stream
->GetType () == MediaTypeAudio
) {
4430 result
= DecodeAudioFrame (frame
);
4431 } else if (stream
->GetType () == MediaTypeVideo
) {
4432 result
= DecodeVideoFrame (frame
);
4435 if (MEDIA_SUCCEEDED (result
)) {
4436 ReportDecodeFrameCompleted (frame
);
4438 ReportErrorOccurred (result
);
4443 NullDecoder::OpenDecoderAsyncInternal ()
4446 IMediaStream
*stream
= GetStream ();
4448 if (stream
->GetType () == MediaTypeAudio
)
4449 result
= OpenAudio ();
4450 else if (stream
->GetType () == MediaTypeVideo
)
4451 result
= OpenVideo ();
4453 result
= MEDIA_FAIL
;
4455 if (MEDIA_SUCCEEDED (result
)) {
4456 ReportOpenDecoderCompleted ();
4458 ReportErrorOccurred (result
);
4463 NullDecoder::OpenAudio ()
4465 return MEDIA_SUCCESS
;
4469 NullDecoder::OpenVideo ()
4471 VideoStream
*vs
= (VideoStream
*) GetStream ();
4472 guint32 dest_height
= vs
->height
;
4473 guint32 dest_width
= vs
->width
;
4476 // We assume that the input image is a 24 bit bitmap (bmp), stored bottum up and flipped vertically.
4477 extern const char moonlight_logo
[];
4478 const char *image
= moonlight_logo
;
4480 guint32 img_offset
= *((guint32
*)(image
+ 10));
4481 guint32 img_width
= *((guint32
*)(image
+ 18));
4482 guint32 img_height
= *((guint32
*)(image
+ 22));
4483 guint32 img_stride
= (img_width
* 3 + 3) & ~3; // in bytes
4484 guint32 img_i
, img_h
, img_w
;
4485 guint32 start_w
= (dest_width
-img_width
)/2;
4486 guint32 end_w
= start_w
+ img_width
;
4487 guint32 start_h
= (dest_height
-img_height
)/2;
4488 guint32 end_h
= start_h
+ img_height
;
4490 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
);
4492 // create the buffer for our image
4493 logo_size
= dest_height
* dest_width
* 4;
4494 logo
= (guint8
*) g_malloc (logo_size
);
4495 memset (logo
, 0x00, logo_size
);
4497 // write our image centered into the destination rectangle, flipped horizontally
4499 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4500 for (guint32 dest_w
= 0; dest_w
< dest_width
; dest_w
++) {
4501 if (dest_w
>= start_w
&& dest_w
< end_w
&& dest_h
>= start_h
&& dest_h
< end_h
) {
4502 img_h
= (dest_h
- start_h
) % img_height
;
4503 img_w
= (dest_w
- start_w
) % img_width
;
4504 img_i
= img_h
* img_stride
+ img_w
* 3;
4506 logo
[logo_size
- dest_i
+ 0] = image
[img_offset
+ img_i
+ 0];
4507 logo
[logo_size
- dest_i
+ 1] = image
[img_offset
+ img_i
+ 1];
4508 logo
[logo_size
- dest_i
+ 2] = image
[img_offset
+ img_i
+ 2];
4510 logo
[logo_size
- dest_i
+ 3] = 0xff;
4516 // Flip the image vertically
4517 for (guint32 dest_h
= 0; dest_h
< dest_height
; dest_h
++) {
4518 for (guint32 dest_w
= 0; dest_w
< dest_width
/ 2; dest_w
++) {
4520 guint32 a
= (dest_h
* dest_width
+ dest_w
) * 4;
4521 guint32 b
= (dest_h
* dest_width
+ dest_width
- dest_w
) * 4 - 4;
4522 for (guint32 c
= 0; c
< 3; c
++) {
4524 logo
[a
+ c
] = logo
[b
+ c
];
4530 SetPixelFormat (MoonPixelFormatRGB32
);
4532 return MEDIA_SUCCESS
;
4539 ExternalDemuxer::ExternalDemuxer (Media
*media
, void *instance
, CloseDemuxerCallback close_demuxer
,
4540 GetDiagnosticAsyncCallback get_diagnostic
, GetFrameAsyncCallback get_sample
, OpenDemuxerAsyncCallback open_demuxer
,
4541 SeekAsyncCallback seek
, SwitchMediaStreamAsyncCallback switch_media_stream
)
4542 : IMediaDemuxer (Type::EXTERNALDEMUXER
, media
)
4544 this->close_demuxer_callback
= close_demuxer
;
4545 this->get_diagnostic_async_callback
= get_diagnostic
;
4546 this->get_sample_async_callback
= get_sample
;
4547 this->open_demuxer_async_callback
= open_demuxer
;
4548 this->seek_async_callback
= seek
;
4549 this->switch_media_stream_async_callback
= switch_media_stream
;
4550 this->instance
= instance
;
4552 pthread_rwlock_init (&rwlock
, NULL
);
4554 g_return_if_fail (instance
!= NULL
);
4555 g_return_if_fail (close_demuxer
!= NULL
&& get_diagnostic
!= NULL
&& get_sample
!= NULL
&& open_demuxer
!= NULL
&& seek
!= NULL
&& switch_media_stream
!= NULL
);
4558 ExternalDemuxer::~ExternalDemuxer ()
4560 pthread_rwlock_destroy (&rwlock
);
4564 ExternalDemuxer::Dispose ()
4567 IMediaDemuxer::Dispose ();
4571 ExternalDemuxer::ClearCallbacks ()
4573 pthread_rwlock_wrlock (&rwlock
);
4574 close_demuxer_callback
= NULL
;
4575 get_diagnostic_async_callback
= NULL
;
4576 get_sample_async_callback
= NULL
;
4577 open_demuxer_async_callback
= NULL
;
4578 seek_async_callback
= NULL
;
4579 switch_media_stream_async_callback
= NULL
;
4581 pthread_rwlock_unlock (&rwlock
);
4585 ExternalDemuxer::SetCanSeek (bool value
)
4587 g_warning ("TODO: ExternalDemuxer::SetCanSeek ()");
4591 ExternalDemuxer::AddStream (IMediaStream
*stream
)
4593 return IMediaDemuxer::AddStream (stream
);
4597 ExternalDemuxer::CloseDemuxerInternal ()
4599 pthread_rwlock_rdlock (&rwlock
);
4600 if (close_demuxer_callback
!= NULL
) {
4601 close_demuxer_callback (instance
);
4604 printf ("ExternalDemuxer::CloseDemuxerInternal (): no function pointer.\n");
4607 pthread_rwlock_unlock (&rwlock
);
4611 ExternalDemuxer::GetDiagnosticAsyncInternal (MediaStreamSourceDiagnosticKind diagnosticsKind
)
4613 pthread_rwlock_rdlock (&rwlock
);
4614 if (get_diagnostic_async_callback
!= NULL
) {
4615 get_diagnostic_async_callback (instance
, diagnosticsKind
);
4618 printf ("ExternalDemuxer::GetDiagnosticsAsyncInternal (): no function pointer.\n");
4621 pthread_rwlock_unlock (&rwlock
);
4625 ExternalDemuxer::GetFrameAsyncInternal (IMediaStream
*stream
)
4627 g_return_if_fail (stream
!= NULL
);
4629 pthread_rwlock_rdlock (&rwlock
);
4630 if (get_sample_async_callback
!= NULL
) {
4631 get_sample_async_callback (instance
, stream
->GetStreamType ());
4634 printf ("ExternalDemuxer::GetFrameAsyncInternal (): no function pointer.\n");
4637 pthread_rwlock_unlock (&rwlock
);
4641 ExternalDemuxer::OpenDemuxerAsyncInternal ()
4643 pthread_rwlock_rdlock (&rwlock
);
4644 if (open_demuxer_async_callback
!= NULL
) {
4645 open_demuxer_async_callback (instance
, this);
4648 printf ("ExternalDemuxer::OpenDemuxerAsyncInternal (): no function pointer.\n");
4651 pthread_rwlock_unlock (&rwlock
);
4655 ExternalDemuxer::SeekAsyncInternal (guint64 seekToTime
)
4657 pthread_rwlock_rdlock (&rwlock
);
4658 if (seek_async_callback
!= NULL
) {
4659 seek_async_callback (instance
, seekToTime
);
4662 printf ("ExternalDemuxer::SeekAsyncInternal (): no function pointer.\n");
4665 pthread_rwlock_unlock (&rwlock
);
4669 ExternalDemuxer::SwitchMediaStreamAsyncInternal (IMediaStream
*mediaStreamDescription
)
4671 g_return_if_fail (mediaStreamDescription
!= NULL
);
4673 pthread_rwlock_rdlock (&rwlock
);
4674 if (switch_media_stream_async_callback
!= NULL
) {
4675 switch_media_stream_async_callback (instance
, mediaStreamDescription
);
4678 printf ("ExternalDemuxer::SwitchMediaStreamAsyncInternal (): no function pointer.\n");
4681 pthread_rwlock_unlock (&rwlock
);
4689 AudioStream::AudioStream (Media
*media
)
4690 : IMediaStream (Type::AUDIOSTREAM
, media
)
4694 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
)
4695 : IMediaStream (Type::AUDIOSTREAM
, media
)
4697 this->codec_id
= codec_id
;
4698 this->codec
= CreateCodec (codec_id
);
4699 this->extra_data
= extra_data
;
4700 this->extra_data_size
= extra_data_size
;
4701 input_bits_per_sample
= bits_per_sample
;
4702 output_bits_per_sample
= bits_per_sample
;
4703 input_block_align
= block_align
;
4704 output_block_align
= block_align
;
4705 input_sample_rate
= sample_rate
;
4706 output_sample_rate
= sample_rate
;
4707 input_channels
= channels
;
4708 output_channels
= channels
;
4709 input_bit_rate
= bit_rate
;
4710 output_bit_rate
= bit_rate
;
4717 ExternalDecoder::ExternalDecoder (Media
*media
, IMediaStream
*stream
, void *instance
, const char *name
,
4718 ExternalDecoder_DecodeFrameAsyncCallback decode_frame_async
,
4719 ExternalDecoder_OpenDecoderAsyncCallback open_decoder_async
,
4720 ExternalDecoder_CleanupCallback cleanup
,
4721 ExternalDecoder_CleanStateCallback clean_state
,
4722 ExternalDecoder_HasDelayedFrameCallback has_delayed_frame
,
4723 ExternalDecoder_DisposeCallback dispose
,
4724 ExternalDecoder_DtorCallback dtor
)
4725 : IMediaDecoder (Type::EXTERNALDECODER
, media
, stream
)
4727 this->instance
= instance
;
4728 this->name
= g_strdup (name
);
4729 this->decode_frame_async
= decode_frame_async
;
4730 this->open_decoder_async
= open_decoder_async
;
4731 this->cleanup
= cleanup
;
4732 this->clean_state
= clean_state
;
4733 this->has_delayed_frame
= has_delayed_frame
;
4734 this->dispose
= dispose
;
4738 ExternalDecoder::~ExternalDecoder ()
4745 ExternalDecoder::DecodeFrameAsyncInternal (MediaFrame
*frame
)
4747 decode_frame_async (instance
, frame
);
4751 ExternalDecoder::OpenDecoderAsyncInternal ()
4753 open_decoder_async (instance
);
4757 ExternalDecoder::Dispose ()
4761 IMediaDecoder::Dispose ();
4765 ExternalDecoder::Cleanup (MediaFrame
*frame
)
4767 cleanup (instance
, frame
);
4771 ExternalDecoder::CleanState ()
4773 clean_state (instance
);
4777 ExternalDecoder::HasDelayedFrame ()
4779 return has_delayed_frame (instance
);
4783 ExternalDecoder::InputEnded ()
4785 GetStream ()->SetOutputEnded (true);
4789 * ExternalDecoderInfo
4792 ExternalDecoderInfo::ExternalDecoderInfo (void *instance
, const char *name
, ExternalDecoderInfo_SupportsCallback supports
, ExternalDecoderInfo_Create create
, ExternalDecoderInfo_dtor dtor
)
4794 this->instance
= instance
;
4795 this->supports
= supports
;
4796 this->create
= create
;
4798 this->name
= g_strdup (name
);
4802 ExternalDecoderInfo::Supports (const char *codec
)
4804 return supports (instance
, codec
);
4808 ExternalDecoderInfo::Create (Media
*media
, IMediaStream
*stream
)
4810 return create (instance
, media
, stream
);
4813 ExternalDecoderInfo::~ExternalDecoderInfo ()