1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007, 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
18 #include "timesource.h"
19 #include "timemanager.h"
20 #include "mediaplayer.h"
25 #include "mediaelement.h"
33 MediaPlayer::MediaPlayer (MediaElement
*el
)
34 : EventObject (Type::MEDIAPLAYER
)
36 LOG_MEDIAPLAYER ("MediaPlayer::MediaPlayer (%p, id=%i), id=%i\n", el
, GET_OBJ_ID (el
), GET_OBJ_ID (this));
47 format
= MoonPixelFormatRGB32
;
48 advance_frame_timeout_id
= 0;
52 audio_unlocked
= NULL
;
57 MediaPlayer::~MediaPlayer ()
59 LOG_MEDIAPLAYER ("MediaPlayer::~MediaPlayer (), id=%i\n", GET_OBJ_ID (this));
64 MediaPlayer::Dispose ()
66 LOG_MEDIAPLAYER ("MediaPlayer::Dispose (), id=%i\n", GET_OBJ_ID (this));
74 EventObject::Dispose ();
78 MediaPlayer::GetAudio ()
80 AudioSource
*result
= NULL
;
85 if (audio_unlocked
!= NULL
) {
86 result
= audio_unlocked
;
95 MediaPlayer::AudioFinishedCallback (EventObject
*user_data
)
97 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinishedCallback ()\n");
100 MediaPlayer
*mplayer
= (MediaPlayer
*) user_data
;
101 mplayer
->AudioFinished ();
105 MediaPlayer::AudioFinished ()
107 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinished () VideoEnded: %i, AudioEnded: %i AudioSource id: %i\n", GetBit (VideoEnded
), GetBit (AudioEnded
), GET_OBJ_ID (audio_unlocked
));
109 // This method must be thread-safe
111 if (!Surface::InMainThread ()) {
112 AddTickCall (AudioFinishedCallback
);
117 if (!GetBit (AudioEnded
)) {
124 MediaPlayer::VideoFinished ()
126 LOG_MEDIAPLAYER ("MediaPlayer::VideoFinished () VideoEnded: %i, AudioEnded: %i\n", GetBit (VideoEnded
), GetBit (AudioEnded
));
129 if (!GetBit (VideoEnded
)) {
136 MediaPlayer::CheckFinished ()
138 LOG_MEDIAPLAYER ("MediaPlayer::CheckFinished (), HasVideo: %i, VideoEnded: %i, HasAudio: %i, AudioEnded: %i\n",
139 HasVideo (), GetBit (VideoEnded
), HasAudio (), GetBit (AudioEnded
));
142 if (HasVideo () && !GetBit (VideoEnded
))
145 if (HasAudio () && !GetBit (AudioEnded
))
148 Emit (MediaEndedEvent
);
152 MediaPlayer::AudioFailed (AudioSource
*source
)
154 // This method must be thread-safe
157 if (this->audio_unlocked
== source
) {
158 AudioPlayer::Remove (this->audio_unlocked
);
159 this->audio_unlocked
->unref ();
160 this->audio_unlocked
= NULL
;
166 MediaPlayer::Open (Media
*media
, PlaylistEntry
*entry
)
168 IMediaDecoder
*encoding
;
169 IMediaStream
*stream
;
170 guint64 asx_duration
;
171 gint32
*audio_stream_index
= NULL
;
174 LOG_MEDIAPLAYER ("MediaPlayer::Open (%p), current media: %p\n", media
, this->media
);
180 printf ("MediaPlayer::Open (): media is NULL.\n");
184 if (!media
->IsOpened ()) {
185 printf ("MediaPlayer::Open (): media isn't opened.\n");
194 // Find audio/video streams
195 IMediaDemuxer
*demuxer
= media
->GetDemuxerReffed ();
196 VideoStream
*vstream
= NULL
;
197 AudioStream
*astream
= NULL
, *astream2
= NULL
;
199 if (demuxer
== NULL
) {
200 fprintf (stderr
, "MediaPlayer::Open (): media doesn't have a demuxer.\n");
204 audio_stream_index
= element
->GetAudioStreamIndex ();
206 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
207 stream
= demuxer
->GetStream (i
);
208 encoding
= stream
->GetDecoder (); //stream->codec;
210 if (encoding
== NULL
)
211 continue; // No encoding was found for the stream.
213 switch (stream
->GetType ()) {
215 audio_stream_count
++;
216 if (audio_stream_index
!= NULL
){
217 if (*audio_stream_index
== audio_stream_count
- 1) {
218 astream
= (AudioStream
*) stream
;
221 astream2
= (AudioStream
*) stream
;
223 if (astream
== NULL
|| astream
->GetBitRate () < astream2
->GetBitRate ())
229 vstream
= (VideoStream
*) stream
;
231 if (video_stream
!= NULL
&& vstream
->GetBitRate () < video_stream
->GetBitRate ())
234 video_stream
= vstream
;
236 height
= video_stream
->height
;
237 width
= video_stream
->width
;
239 SetVideoBufferSize (width
, height
);
241 // printf ("video size: %i, %i\n", video_stream->width, video_stream->height);
243 case MediaTypeMarker
:
244 LOG_MEDIAPLAYER ("MediaPlayer::Open (): Found a marker stream, selecting it.\n");
245 stream
->SetSelected (true);
251 if (astream
!= NULL
) {
252 audio
= AudioPlayer::Add (this, astream
);
254 // Only select the audio stream if we can actually play it
255 astream
->SetSelected (true);
257 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected audio stream (%d) properties:\n"
258 "\tchannels: Input: %d Output: %d\n"
259 "\tsample_rate: Input: %d Output: %d\n"
260 "\tbit_rate: Input: %d Output: %d\n"
261 "\tblock_align: Input: %d Output: %d\n"
262 "\tbits_per_sample: Input: %d Output: %d\n"
264 "\tduration: %" G_GUINT64_FORMAT
"\n"
265 "\textra data size: %d\n",
266 astream
->index
, astream
->GetChannels (), astream
->GetOutputChannels (),
267 astream
->GetSampleRate (), astream
->GetOutputSampleRate (),
268 astream
->GetBitRate (), astream
->GetOutputBitRate (),
269 astream
->GetBlockAlign (), astream
->GetOutputBlockAlign (),
270 astream
->GetBitsPerSample (), astream
->GetOutputBitsPerSample (),
271 astream
->GetCodecId (), astream
->GetDuration (), astream
->GetExtraDataSize ());
272 if (astream
->extra_data_size
> 0) {
274 LOG_MEDIAPLAYER ("\textra data: ");
275 for (n
= 0; n
< astream
->extra_data_size
; n
++)
276 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8
*)astream
->extra_data
)[n
]);
277 LOG_MEDIAPLAYER ("\n");
280 this->audio_unlocked
= audio
;
284 if (video_stream
!= NULL
) {
285 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected Video stream (%d) properties:\n"
288 "\tbits_per_sample: %d\n"
291 "\tpts_per_frame: %" G_GUINT64_FORMAT
"\n"
292 "\tduration: %" G_GUINT64_FORMAT
"\n"
293 "\textra data size: %d\n",
294 video_stream
->index
, video_stream
->width
, video_stream
->height
, video_stream
->bits_per_sample
,
295 video_stream
->bit_rate
, video_stream
->codec_id
, video_stream
->pts_per_frame
,
296 video_stream
->duration
, video_stream
->extra_data_size
);
297 video_stream
->SetSelected (true);
298 video_stream
->ref ();
299 if (video_stream
->extra_data_size
> 0) {
301 LOG_MEDIAPLAYER ("\textra data: ");
302 for (n
= 0; n
< video_stream
->extra_data_size
; n
++)
303 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8
*)video_stream
->extra_data
)[n
]);
304 LOG_MEDIAPLAYER ("\n");
313 start_pts
= TimeSpan_ToPts (entry
->GetStartTime ());
314 LOG_MEDIAPLAYER ("MediaPlayer::Open (), setting start_pts to: %" G_GUINT64_FORMAT
" (%" G_GUINT64_FORMAT
" ms).\n", start_pts
, MilliSeconds_FromPts (start_pts
));
315 // note that we might be re-opening a media which is not at position 0,
316 // so it is not possible to optimize away the case where start_pts = 0.
317 element
->Seek (start_pts
, true);
319 if (entry
->GetIsLive ())
323 duration
= demuxer
->GetDuration ();
325 if (entry
!= NULL
&& entry
->HasInheritedDuration () && entry
->GetInheritedDuration ()->HasTimeSpan ()) {
326 asx_duration
= TimeSpan_ToPts (entry
->GetInheritedDuration ()->GetTimeSpan ());
327 if (asx_duration
< duration
|| GetBit (IsLive
)) {
328 duration
= asx_duration
;
329 SetBit (FixedDuration
);
333 SetBit (LoadFramePending
);
335 media
->AddSafeHandler (Media::SeekCompletedEvent
, SeekCompletedCallback
, this);
336 media
->SetBufferingTime (element
->GetBufferingTime ());
339 video_stream
->AddSafeHandler (IMediaStream::FirstFrameEnqueuedEvent
, FirstFrameEnqueuedCallback
, this);
340 // We may attach the handler above after the first frame has been queued,
341 // so just execute LoadVideoFrame once right away
351 MediaPlayer::SetVideoBufferSize (gint32 width
, gint32 height
)
355 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (%i, %i). buffer_width: %i, buffer_height: %i\n", width
, height
, buffer_width
, buffer_height
);
359 cairo_surface_destroy (surface
);
363 /* NOTE: We only accept RGB32 or RGBA32 data here */
364 stride
= cairo_format_stride_for_width (format
== MoonPixelFormatRGB32
? CAIRO_FORMAT_RGB24
: CAIRO_FORMAT_ARGB32
, MAX (width
, buffer_width
));
367 int remain
= stride
% 64;
368 stride
+= 64 - remain
;
371 if (width
> buffer_width
|| height
> buffer_height
) {
372 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (): creating new buffer.\n");
374 // for conversion to rgb32 format needed for rendering with 16 byte alignment
375 if (posix_memalign ((void **)(&rgb_buffer
), 16, height
* stride
)) {
377 g_warning ("Could not allocate memory for video RGB buffer");
380 memset (rgb_buffer
, 0, height
* stride
);
382 buffer_width
= width
;
383 buffer_height
= height
;
387 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (): creating new surface, width: %i, height: %i, stride: %i\n", width
, height
, stride
);
388 /* NOTE: We only accept RGB32 or RGBA32 data here */
389 surface
= cairo_image_surface_create_for_data (rgb_buffer
, format
== MoonPixelFormatRGB32
? CAIRO_FORMAT_RGB24
: CAIRO_FORMAT_ARGB32
, width
, height
, stride
);
393 MediaPlayer::Initialize ()
395 LOG_MEDIAPLAYER ("MediaPlayer::Initialize ()\n");
398 // Clear out any state, bits, etc
399 state_unlocked
= (PlayerState
) 0;
400 // Set initial states and bits
402 SetBit (SeekSynched
);
412 first_live_pts
= G_MAXUINT64
;
414 audio_stream_count
= 0;
418 frames_update_timestamp
= 0;
421 rendered_frames_per_second
= 0.0;
422 dropped_frames_per_second
= 0.0;
426 MediaPlayer::Close ()
428 LOG_MEDIAPLAYER ("MediaPlayer::Close ()\n");
432 if (audio_unlocked
) {
433 AudioPlayer::Remove (audio_unlocked
);
434 audio_unlocked
->Dispose ();
435 audio_unlocked
->unref ();
436 audio_unlocked
= NULL
;
442 // Reset state back to what it was at instantiation
444 if (rgb_buffer
!= NULL
) {
451 if (surface
!= NULL
) {
452 cairo_surface_destroy (surface
);
457 video_stream
->RemoveSafeHandlers (this);
458 video_stream
->unref ();
471 // Puts the data into our rgb buffer.
472 // If necessary converts the data from its source format to rgb first.
476 MediaPlayer::RenderFrame (MediaFrame
*frame
)
478 VideoStream
*stream
= (VideoStream
*) frame
->stream
;
480 LOG_MEDIAPLAYER_EX ("MediaPlayer::RenderFrame (%p), pts: %" G_GUINT64_FORMAT
" ms, buflen: %i, buffer: %p, IsPlanar: %i\n", frame
, MilliSeconds_FromPts (frame
->pts
), frame
->buflen
, frame
->buffer
, frame
->IsPlanar ());
483 if (!frame
->IsDecoded ()) {
484 fprintf (stderr
, "MediaPlayer::RenderFrame (): Trying to render a frame which hasn't been decoded yet.\n");
488 if ((frame
->width
> 0 && frame
->width
!= width
) || (frame
->height
> 0 && frame
->height
!= height
) || (format
!= stream
->GetDecoder ()->GetPixelFormat ())) {
489 LOG_MEDIAPLAYER ("MediaPlayer::RenderFrame () frame width: %i, frame height: %i, stream width: %i, stream height: %i, previous frame width: %i, previous frame height: %i\n",
490 frame
->width
, frame
->height
, video_stream
->width
, video_stream
->height
, width
, height
);
492 if (frame
->width
> 0)
493 width
= frame
->width
;
494 if (frame
->height
> 0)
495 height
= frame
->height
;
497 format
= stream
->GetDecoder ()->GetPixelFormat ();
499 SetVideoBufferSize (width
, height
);
502 if (!frame
->IsPlanar ()) {
503 // Just copy the data
504 guint32 stride
= cairo_image_surface_get_stride (surface
);
505 for (int i
= 0; i
< height
; i
++)
506 memcpy (rgb_buffer
+ stride
* i
, frame
->buffer
+ i
* width
* 4, width
* 4);
507 SetBit (RenderedFrame
);
508 element
->MediaInvalidate ();
512 if (frame
->data_stride
== NULL
||
513 frame
->data_stride
[1] == NULL
||
514 frame
->data_stride
[2] == NULL
) {
518 guint8
*rgb_dest
[3] = { rgb_buffer
, NULL
, NULL
};
519 int rgb_stride
[3] = { cairo_image_surface_get_stride (surface
), 0, 0 };
521 stream
->converter
->Convert (frame
->data_stride
, frame
->srcStride
, frame
->srcSlideY
,
522 frame
->srcSlideH
, rgb_dest
, rgb_stride
);
524 SetBit (RenderedFrame
);
525 element
->MediaInvalidate ();
529 printf ("MediaPlayer::AdvanceFrame (), %10s frame pts: %6llu ms, target pts: %6llu ms, diff: %+5lld, rendered fps: %5.2f, dropped fps: %5.2f, total: %5.2f\n", x, \
530 MilliSeconds_FromPts (frame->pts), \
531 MilliSeconds_FromPts (target_pts), \
532 (gint64) MilliSeconds_FromPts (frame->pts) - (gint64) MilliSeconds_FromPts (target_pts), \
533 rendered_frames_per_second, \
534 dropped_frames_per_second, \
535 dropped_frames_per_second + rendered_frames_per_second);
538 MediaPlayer::AdvanceFrame ()
540 MediaFrame
*frame
= NULL
;
541 IMediaStream
*stream
;
542 guint64 target_pts
= 0;
543 guint64 target_pts_start
= 0;
544 guint64 target_pts_end
= 0;
545 guint64 target_pts_delta
= MilliSeconds_ToPts (100);
547 double dropped_frames_per_second
= -1;
548 double rendered_frames_per_second
= -1;
553 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame () state: %i, current_pts = %" G_GUINT64_FORMAT
", IsPaused: %i, IsSeeking: %i, VideoEnded: %i, AudioEnded: %i, HasVideo: %i, HasAudio: %i\n",
554 state_unlocked
, current_pts
, IsPaused (), IsSeeking (), GetBit (VideoEnded
), GetBit (AudioEnded
), HasVideo (), HasAudio ());
557 RemoveBit (LoadFramePending
);
565 if (GetBit (VideoEnded
))
571 // If the audio isn't playing, there might be slight length-difference between
572 // audio and video streams (the audio is shorted and finished earlier for instance)
573 // Treat this case as if there's no audio at all.
575 if (audio
!= NULL
&& audio
->GetState () == AudioPlaying
) {
576 // use target_pts as set by audio thread
577 target_pts
= GetTargetPts ();
578 if (target_pts
== G_MAXUINT64
) {
579 // This might happen if we've called Play on the audio source, but it hasn't actually played anything yet.
580 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): invalid target pts from the audio stream.\n");
585 // no audio to sync to
586 guint64 now
= TimeSpan_ToPts (element
->GetTimeManager()->GetCurrentTime ());
587 guint64 elapsed_pts
= now
- start_time
;
589 target_pts
= elapsed_pts
;
592 printf ("MediaPlayer::AdvanceFrame (): determined target_pts to be: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, elapsed_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, start_time: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
593 target_pts, MilliSeconds_FromPts (target_pts), elapsed_pts, MilliSeconds_FromPts (elapsed_pts), start_time, MilliSeconds_FromPts (start_time));
601 this->target_pts
= target_pts
;
603 target_pts_start
= target_pts_delta
> target_pts
? 0 : target_pts
- target_pts_delta
;
604 target_pts_end
= target_pts
+ target_pts_delta
;
606 if (current_pts
>= target_pts_end
&& GetBit (SeekSynched
) && !(HasAudio () && GetBit (AudioEnded
))) {
607 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): video is running too fast, wait a bit (current_pts: %" G_GUINT64_FORMAT
" ms, target_pts: %" G_GUINT64_FORMAT
" ms, delta: %" G_GUINT64_FORMAT
" ms, diff: %" G_GINT64_FORMAT
" (%" G_GINT64_FORMAT
" ms)).\n",
608 MilliSeconds_FromPts (current_pts
), MilliSeconds_FromPts (target_pts
), MilliSeconds_FromPts (target_pts_delta
), current_pts
- target_pts
, MilliSeconds_FromPts (current_pts
- target_pts
));
612 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): target pts: %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms\n", target_pts
, MilliSeconds_FromPts (target_pts
));
615 frame
= video_stream
->PopFrame ();
617 if (video_stream
->GetOutputEnded ()) {
619 // Set the target pts to the last pts we showed, since target_pts is what's reported as our current position.
620 this->target_pts
= current_pts
;
626 SetBufferUnderflow ();
627 // If we have audio, we keep playing (and loosing frames) until the audio playing stops due to buffer underflow
628 // TODO: determine if we don't have video due to not having enough data (in which case we should start buffering),
629 // or if it is because the decoder can't keep up (in which case we should drop frames).
633 stream
= frame
->stream
;
634 current_pts
= frame
->pts
;
637 //printf ("MediaPlayer::AdvanceFrame (): current_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, duration: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
638 // current_pts, MilliSeconds_FromPts (current_pts),
639 // duration, MilliSeconds_FromPts (duration));
641 if (GetBit (IsLive
)) {
642 first_live_pts
= MIN (current_pts
, first_live_pts
);
645 if (GetBit (FixedDuration
)) {
647 printf ("MediaPlayer::AdvanceFrame (): (fixed duration, live: %i) current_pts: %" G_GUINT64_FORMAT " ms, duration: %" G_GUINT64_FORMAT " ms, first_live_pts: %" G_GUINT64_FORMAT " ms, diff: %" G_GUINT64_FORMAT "ms\n",
648 GetBit (IsLive), MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (duration), MilliSeconds_FromPts (first_live_pts), MilliSeconds_FromPts (current_pts - first_live_pts));
650 if (GetBit (IsLive
)) {
651 if (current_pts
- first_live_pts
> duration
) {
652 // TODO: Move this out of AdvanceFrame, here it only works for media which has video, not for audio-only media.
658 if (current_pts
> duration
) {
664 if (GetBit (VideoEnded
)) {
665 //printf ("MediaPlayer::AdvanceFrame (): Reached end of duration.\n");
671 if (!frame
->IsDecoded ()) {
672 printf ("MediaPlayer::AdvanceFrame (): Got a non-decoded frame.\n");
676 if (update
&& current_pts
>= target_pts_start
) {
677 if (!GetBit (SeekSynched
)) {
678 SetBit (SeekSynched
);
679 LOG_MEDIAPLAYER ("MediaPlayer::AdvanceFrame (): We have now successfully synched with the audio after the seek, current_pts: %" G_GUINT64_FORMAT
", target_pts_start: %" G_GUINT64_FORMAT
"\n", MilliSeconds_FromPts (current_pts
), MilliSeconds_FromPts (target_pts_start
));
681 // we are in sync (or ahead) of audio playback
685 if (video_stream
->IsQueueEmpty ()) {
686 // no more packets in queue, this frame is the most recent we have available
690 // we are lagging behind, drop this frame
693 //LOG_RS ("[SKIPPED]");
694 media
->DisposeObject (frame
);
699 if (update
&& frame
&& GetBit (SeekSynched
)) {
701 //LOG_RS ("[RENDER]");
707 media
->DisposeObject (frame
);
713 if (frames_update_timestamp
== 0) {
714 frames_update_timestamp
= now
;
715 } else if ((now
- frames_update_timestamp
) > TIMESPANTICKS_IN_SECOND
) {
716 double time_elapsed
= (double) (now
- frames_update_timestamp
) / (double) TIMESPANTICKS_IN_SECOND
;
717 dropped_frames_per_second
= (double) dropped_frames
/ time_elapsed
;
718 rendered_frames_per_second
= (double) rendered_frames
/ time_elapsed
;
719 dropped_frames
= rendered_frames
= 0;
720 frames_update_timestamp
= now
;
722 this->dropped_frames_per_second
= dropped_frames_per_second
;
723 this->rendered_frames_per_second
= rendered_frames_per_second
;
730 MediaPlayer::FirstFrameEnqueuedHandler (EventObject
*sender
, EventArgs
*args
)
736 MediaPlayer::LoadVideoFrameCallback (EventObject
*object
)
738 ((MediaPlayer
*) object
)->LoadVideoFrame ();
742 MediaPlayer::LoadVideoFrame ()
747 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), HasVideo: %i, LoadFramePending: %i\n", HasVideo (), state_unlocked
& LoadFramePending
);
753 if (!IsLoadFramePending ())
756 frame
= video_stream
->PopFrame ();
761 target_pts
= GetTargetPts ();
763 if (target_pts
== G_MAXUINT64
)
766 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), packet pts: %" G_GUINT64_FORMAT
", target pts: %" G_GUINT64_FORMAT
", pts_per_frame: %" G_GUINT64_FORMAT
", buflen: %i\n", frame
->pts
, GetTargetPts (), video_stream
->pts_per_frame
, frame
->buflen
);
768 if (frame
->pts
+ video_stream
->pts_per_frame
>= target_pts
) {
769 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (): rendering.\n");
770 RemoveBit (LoadFramePending
);
772 element
->MediaInvalidate ();
774 AddTickCall (LoadVideoFrameCallback
);
777 media
->DisposeObject (frame
);
784 MediaPlayer::AdvanceFrameCallback (void *user_data
)
786 MediaPlayer
*mplayer
= (MediaPlayer
*) user_data
;
787 mplayer
->SetCurrentDeployment ();
788 mplayer
->AdvanceFrame ();
790 Deployment::SetCurrent (NULL
);
796 MediaPlayer::SetTimeout (gint32 timeout
/* set to 0 to clear */)
798 TimeManager
*time_manager
= element
? element
->GetTimeManager () : NULL
;
799 bool clear
= timeout
== 0 || advance_frame_timeout_id
!= 0;
801 LOG_MEDIAPLAYER ("MediaPlayer::SetTimeout (%i) time_manager: %p id: %i\n", timeout
, time_manager
, GET_OBJ_ID (time_manager
));
803 if (clear
&& advance_frame_timeout_id
!= 0) {
804 if (time_manager
!= NULL
) {
805 time_manager
->RemoveTimeout (advance_frame_timeout_id
);
807 g_warning ("MediaPlayer::SetTimeout (): Could not clear timeout. Leaking ourselves to not crash.\n");
808 ref (); // This will prevent us from getting destroyed.
810 advance_frame_timeout_id
= 0;
814 if (time_manager
== NULL
) {
815 g_warning ("MediaPlayer::SetTimeout (): Could not set timeout (no time manager).\n");
817 advance_frame_timeout_id
= time_manager
->AddTimeout (MOON_PRIORITY_DEFAULT
, timeout
, AdvanceFrameCallback
, this);
827 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i, IsPlaying: %i, IsSeeking: %i\n", state_unlocked
, IsPlaying (), IsSeeking ());
834 RemoveBit (BufferUnderflow
);
835 start_time
= TimeSpan_ToPts (element
->GetTimeManager()->GetCurrentTime ());
836 start_time
-= target_pts
;
844 SetTimeout (GetTimeoutInterval ());
846 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i [Done]\n", state_unlocked
);
850 MediaPlayer::GetTimeoutInterval ()
852 gint32 result
; // ms between timeouts
853 guint64 pts_per_frame
= 0;
858 pts_per_frame
= video_stream
->pts_per_frame
;
859 // there are 10000 pts in a millisecond, anything less than that will result in 0 (and an endless loop)
860 if (pts_per_frame
< PTS_PER_MILLISECOND
|| pts_per_frame
>= (guint64
) G_MAXINT32
) {
861 // If the stream doesn't know its frame rate, use a default of 60 fps
862 result
= (gint32
) (1000.0 / 60.0);
864 result
= (gint32
) MilliSeconds_FromPts (pts_per_frame
);
870 LOG_MEDIAPLAYER ("MediaPlayer::GetTimeoutInterval (): %i ms between frames gives fps: %.1f, pts_per_frame: %" G_GUINT64_FORMAT
", exact fps: %f\n", result
, 1000.0 / result
, pts_per_frame
, TIMESPANTICKS_IN_SECOND
/ (double) pts_per_frame
);
876 MediaPlayer::SetAudioStreamIndex (gint32 index
)
878 IMediaDemuxer
*demuxer
= NULL
;
879 IMediaStream
*stream
= NULL
;
880 AudioStream
*next_stream
= NULL
;
881 AudioStream
*prev_stream
= NULL
;
882 gint32 audio_streams_found
= 0;
885 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i).\n", index
);
888 if (index
< 0 || index
>= audio_stream_count
) {
889 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Invalid audio stream index.\n", index
);
894 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No media.\n", index
);
900 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No audio source.\n", index
);
904 demuxer
= media
->GetDemuxerReffed ();
906 if (demuxer
== NULL
) {
907 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Media doesn't have a demuxer.\n", index
);
911 prev_stream
= audio
->GetAudioStream ();
913 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
914 stream
= demuxer
->GetStream (i
);
916 if (stream
->GetType () != MediaTypeAudio
)
919 if (audio_streams_found
== index
) {
920 next_stream
= (AudioStream
*) stream
;
924 audio_streams_found
++;
927 if (next_stream
!= NULL
) {
928 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i). Switched stream from #%i to #%i\n", index
, audio_streams_found
++, index
);
929 prev_stream
->SetSelected (false);
930 next_stream
->SetSelected (true);
931 audio
->SetAudioStream (next_stream
);
939 MediaPlayer::GetCanPause ()
941 // FIXME: should return false if it is streaming media
943 return GetBit (CanPause
);
947 MediaPlayer::SetCanPause (bool value
)
950 SetBitTo (CanPause
, value
);
954 MediaPlayer::Pause ()
958 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i\n", state_unlocked
);
974 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i [Done]\n", state_unlocked
);
978 MediaPlayer::GetTargetPts ()
987 LOG_MEDIAPLAYER_EX ("MediaPlayer::GetTargetPts (): target_pts: %" G_GUINT64_FORMAT
", HasAudio (): %i, audio->GetCurrentPts (): %" G_GUINT64_FORMAT
"\n", target_pts
, audio
!= NULL
, audio
!= NULL
? audio
->GetCurrentPts () : 0);
989 if (audio
!= NULL
&& audio
->GetState () == AudioPlaying
)
990 result
= audio
->GetCurrentPts ();
1001 MediaPlayer::SeekCompletedHandler (Media
*media
, EventArgs
*args
)
1003 LOG_MEDIAPLAYER ("MediaPlayer::SeekCompletedHandler () seeks: %i\n", seeks
);
1011 SetBit (LoadFramePending
);
1017 MediaPlayer::NotifySeek (guint64 pts
)
1019 LOG_MEDIAPLAYER ("MediaPlayer::Seek (%" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms), media: %p, state: %i, current_pts: %" G_GUINT64_FORMAT
", IsPlaying (): %i, seeks: %i\n", pts
, MilliSeconds_FromPts (pts
), media
, state_unlocked
, current_pts
, IsPlaying (), seeks
);
1023 guint64 duration
= GetDuration ();
1025 g_return_if_fail (GetCanSeek ());
1027 if (pts
> start_pts
+ duration
)
1028 pts
= start_pts
+ duration
;
1030 if (pts
< start_pts
)
1036 SetBit (LoadFramePending
);
1037 RemoveBit (SeekSynched
);
1038 RemoveBit (AudioEnded
);
1039 RemoveBit (VideoEnded
);
1045 LOG_MEDIAPLAYER ("MediaPlayer::Seek (%" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms), media: %p, state: %i, current_pts: %" G_GUINT64_FORMAT
" [END]\n", pts
, MilliSeconds_FromPts (pts
), media
, state_unlocked
, current_pts
);
1049 MediaPlayer::GetCanSeek ()
1052 return GetBit (CanSeek
);
1056 MediaPlayer::SetCanSeek (bool value
)
1059 SetBitTo (CanSeek
, value
);
1063 MediaPlayer::Stop ()
1065 LOG_MEDIAPLAYER ("MediaPlayer::Stop (), state: %i\n", state_unlocked
);
1076 RemoveBit (AudioEnded
);
1077 RemoveBit (VideoEnded
);
1081 MediaPlayer::StopAudio ()
1085 LOG_MEDIAPLAYER ("MediaPlayer::StopAudio (), state: %i\n", state_unlocked
);
1088 audio
= GetAudio (); // This returns a reffed AudioSource
1096 MediaPlayer::GetBalance ()
1103 audio
= GetAudio ();
1105 result
= audio
->GetBalance ();
1108 fprintf (stderr
, "MediaPlayer::GetBalance (): There's no audio source to get the balance from\n");
1116 MediaPlayer::SetBalance (double balance
)
1120 LOG_MEDIAPLAYER ("MediaPlayer::SetBalance (%f)\n", balance
);
1125 else if (balance
> 1.0)
1128 audio
= GetAudio ();
1130 audio
->SetBalance (balance
);
1133 //fprintf (stderr, "MediaPlayer::SetBalance (%f): There's no audio source to set the balance\n", balance);
1138 MediaPlayer::GetVolume ()
1145 audio
= GetAudio ();
1147 result
= audio
->GetVolume ();
1150 fprintf (stderr
, "MediaPlayer::GetVolume (): There's no audio source to get the volume from\n");
1158 MediaPlayer::SetVolume (double volume
)
1162 LOG_MEDIAPLAYER ("MediaPlayer::SetVolume (%f)\n", volume
);
1167 else if (volume
> 1.0)
1170 audio
= GetAudio ();
1172 audio
->SetVolume (volume
);
1175 //fprintf (stderr, "MediaPlayer::SetVolume (%f): There's no audio source to set the volume\n", volume);
1180 MediaPlayer::GetMuted ()
1187 audio
= GetAudio ();
1189 result
= audio
->GetMuted ();
1192 fprintf (stderr
, "MediaPlayer::GetMuted (): There's no audio.\n");
1200 MediaPlayer::SetMuted (bool muted
)
1204 LOG_MEDIAPLAYER ("MediaPlayer::SetMuted (%i)\n", muted
);
1207 audio
= GetAudio ();
1209 audio
->SetMuted (true);
1212 //fprintf (stderr, "MediaPlayer::SetMuted (%i): There's no audio to mute.\n", muted);
1217 MediaPlayer::SetBit (PlayerState s
)
1220 state_unlocked
= (PlayerState
) (s
| state_unlocked
);
1225 MediaPlayer::RemoveBit (PlayerState s
)
1228 state_unlocked
= (PlayerState
) (~s
& state_unlocked
);
1233 MediaPlayer::SetBitTo (PlayerState s
, bool value
)
1243 MediaPlayer::GetBit (PlayerState s
)
1247 result
= (state_unlocked
& s
) == s
;
1253 MediaPlayer::SetState (PlayerState s
)
1256 state_unlocked
= (PlayerState
) ((state_unlocked
& ~StateMask
) | s
);
1260 MediaPlayer::PlayerState
1261 MediaPlayer::GetState ()
1265 result
= state_unlocked
;
1271 MediaPlayer::IsBufferUnderflow ()
1273 return GetBit (BufferUnderflow
);
1277 MediaPlayer::IsLoadFramePending ()
1279 return GetBit (LoadFramePending
);
1283 MediaPlayer::IsSeeking ()
1289 MediaPlayer::HasRenderedFrame ()
1291 return GetBit (RenderedFrame
);
1295 MediaPlayer::IsPlaying ()
1297 return (GetState () & StateMask
) == Playing
;
1301 MediaPlayer::IsPaused ()
1303 return (GetState () & StateMask
) == Paused
;
1307 MediaPlayer::IsStopped ()
1309 return (GetState () & StateMask
) == Stopped
;
1313 MediaPlayer::SetBufferUnderflow ()
1315 SetBitTo (BufferUnderflow
, true);
1316 EmitBufferUnderflow ();
1320 MediaPlayer::EmitBufferUnderflow ()
1322 if (Surface::InMainThread ()) {
1323 Emit (BufferUnderflowEvent
);
1325 AddTickCall (EmitBufferUnderflowAsync
);
1330 MediaPlayer::EmitBufferUnderflowAsync (EventObject
*obj
)
1332 ((MediaPlayer
*) obj
)->EmitBufferUnderflow ();