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"
29 #define DEBUG_ADVANCEFRAME 0
35 MediaPlayer::MediaPlayer (MediaElement
*el
)
36 : EventObject (Type::MEDIAPLAYER
)
38 LOG_MEDIAPLAYER ("MediaPlayer::MediaPlayer (%p, id=%i), id=%i\n", el
, GET_OBJ_ID (el
), GET_OBJ_ID (this));
49 format
= MoonPixelFormatRGB32
;
50 advance_frame_timeout_id
= 0;
54 audio_unlocked
= NULL
;
59 MediaPlayer::~MediaPlayer ()
61 LOG_MEDIAPLAYER ("MediaPlayer::~MediaPlayer (), id=%i\n", GET_OBJ_ID (this));
66 MediaPlayer::Dispose ()
68 LOG_MEDIAPLAYER ("MediaPlayer::Dispose (), id=%i\n", GET_OBJ_ID (this));
76 EventObject::Dispose ();
80 MediaPlayer::GetAudio ()
82 AudioSource
*result
= NULL
;
87 if (audio_unlocked
!= NULL
) {
88 result
= audio_unlocked
;
97 MediaPlayer::SetSurface (Surface
*s
)
99 if (!SetSurfaceLock ())
102 EventObject::SetSurface (s
);
108 MediaPlayer::AudioFinishedCallback (EventObject
*user_data
)
110 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinishedCallback ()\n");
113 MediaPlayer
*mplayer
= (MediaPlayer
*) user_data
;
114 mplayer
->AudioFinished ();
118 MediaPlayer::AudioFinished ()
120 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinished () VideoEnded: %i, AudioEnded: %i AudioSource id: %i\n", GetBit (VideoEnded
), GetBit (AudioEnded
), GET_OBJ_ID (audio_unlocked
));
122 // This method must be thread-safe
124 if (!Surface::InMainThread ()) {
125 AddTickCallSafe (AudioFinishedCallback
);
130 if (!GetBit (AudioEnded
)) {
137 MediaPlayer::VideoFinished ()
139 LOG_MEDIAPLAYER ("MediaPlayer::VideoFinished () VideoEnded: %i, AudioEnded: %i\n", GetBit (VideoEnded
), GetBit (AudioEnded
));
142 if (!GetBit (VideoEnded
)) {
149 MediaPlayer::CheckFinished ()
151 LOG_MEDIAPLAYER ("MediaPlayer::CheckFinished (), HasVideo: %i, VideoEnded: %i, HasAudio: %i, AudioEnded: %i\n",
152 HasVideo (), GetBit (VideoEnded
), HasAudio (), GetBit (AudioEnded
));
155 if (HasVideo () && !GetBit (VideoEnded
))
158 if (HasAudio () && !GetBit (AudioEnded
))
161 Emit (MediaEndedEvent
);
165 MediaPlayer::AudioFailed (AudioSource
*source
)
167 // This method must be thread-safe
170 if (this->audio_unlocked
== source
) {
171 AudioPlayer::Remove (this->audio_unlocked
);
172 this->audio_unlocked
->unref ();
173 this->audio_unlocked
= NULL
;
179 MediaPlayer::Open (Media
*media
, PlaylistEntry
*entry
)
181 IMediaDecoder
*encoding
;
182 IMediaStream
*stream
;
183 guint64 asx_duration
;
184 gint32
*audio_stream_index
= NULL
;
187 LOG_MEDIAPLAYER ("MediaPlayer::Open (%p), current media: %p\n", media
, this->media
);
193 printf ("MediaPlayer::Open (): media is NULL.\n");
197 if (!media
->IsOpened ()) {
198 printf ("MediaPlayer::Open (): media isn't opened.\n");
207 // Find audio/video streams
208 IMediaDemuxer
*demuxer
= media
->GetDemuxer ();
209 VideoStream
*vstream
= NULL
;
210 AudioStream
*astream
= NULL
, *astream2
= NULL
;
212 if (demuxer
== NULL
) {
213 fprintf (stderr
, "MediaPlayer::Open (): media doesn't have a demuxer.\n");
217 audio_stream_index
= element
->GetAudioStreamIndex ();
219 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
220 stream
= demuxer
->GetStream (i
);
221 encoding
= stream
->GetDecoder (); //stream->codec;
223 if (encoding
== NULL
)
224 continue; // No encoding was found for the stream.
226 switch (stream
->GetType ()) {
228 audio_stream_count
++;
229 if (audio_stream_index
!= NULL
){
230 if (*audio_stream_index
== audio_stream_count
- 1) {
231 astream
= (AudioStream
*) stream
;
234 astream2
= (AudioStream
*) stream
;
236 if (astream
== NULL
|| astream
->GetBitRate () < astream2
->GetBitRate ())
242 vstream
= (VideoStream
*) stream
;
244 if (video_stream
!= NULL
&& vstream
->GetBitRate () < video_stream
->GetBitRate ())
247 video_stream
= vstream
;
248 video_stream
->SetSelected (true);
249 video_stream
->ref ();
251 height
= video_stream
->height
;
252 width
= video_stream
->width
;
254 SetVideoBufferSize (width
, height
);
256 // printf ("video size: %i, %i\n", video_stream->width, video_stream->height);
258 case MediaTypeMarker
:
259 LOG_MEDIAPLAYER ("MediaPlayer::Open (): Found a marker stream, selecting it.\n");
260 stream
->SetSelected (true);
266 if (astream
!= NULL
) {
267 audio
= AudioPlayer::Add (this, astream
);
269 // Only select the audio stream if we can actually play it
270 astream
->SetSelected (true);
272 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected audio stream (%d) properties:\n"
273 "\tchannels: Input: %d Output: %d\n"
274 "\tsample_rate: Input: %d Output: %d\n"
275 "\tbit_rate: Input: %d Output: %d\n"
276 "\tblock_align: Input: %d Output: %d\n"
277 "\tbits_per_sample: Input: %d Output: %d\n"
279 "\tduration: %" G_GUINT64_FORMAT
"\n"
280 "\textra data size: %d\n",
281 astream
->index
, astream
->GetChannels (), astream
->GetOutputChannels (),
282 astream
->GetSampleRate (), astream
->GetOutputSampleRate (),
283 astream
->GetBitRate (), astream
->GetOutputBitRate (),
284 astream
->GetBlockAlign (), astream
->GetOutputBlockAlign (),
285 astream
->GetBitsPerSample (), astream
->GetOutputBitsPerSample (),
286 astream
->GetCodecId (), astream
->GetDuration (), astream
->GetExtraDataSize ());
287 if (astream
->extra_data_size
> 0) {
289 LOG_MEDIAPLAYER ("\textra data: ");
290 for (n
= 0; n
< astream
->extra_data_size
; n
++)
291 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8
*)astream
->extra_data
)[n
]);
292 LOG_MEDIAPLAYER ("\n");
295 this->audio_unlocked
= audio
;
299 if (video_stream
!= NULL
) {
300 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected Video stream (%d) properties:\n"
303 "\tbits_per_sample: %d\n"
306 "\tpts_per_frame: %" G_GUINT64_FORMAT
"\n"
307 "\tduration: %" G_GUINT64_FORMAT
"\n"
308 "\textra data size: %d\n",
309 video_stream
->index
, video_stream
->width
, video_stream
->height
, video_stream
->bits_per_sample
,
310 video_stream
->bit_rate
, video_stream
->codec_id
, video_stream
->pts_per_frame
,
311 video_stream
->duration
, video_stream
->extra_data_size
);
312 if (video_stream
->extra_data_size
> 0) {
314 LOG_MEDIAPLAYER ("\textra data: ");
315 for (n
= 0; n
< video_stream
->extra_data_size
; n
++)
316 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8
*)video_stream
->extra_data
)[n
]);
317 LOG_MEDIAPLAYER ("\n");
326 start_pts
= TimeSpan_ToPts (entry
->GetStartTime ());
327 LOG_MEDIAPLAYER ("MediaPlayer::Open (), setting start_pts to: %" G_GUINT64_FORMAT
" (%" G_GUINT64_FORMAT
" ms).\n", start_pts
, MilliSeconds_FromPts (start_pts
));
328 // note that we might be re-opening a media which is not at position 0,
329 // so it is not possible to optimize away the case where start_pts = 0.
330 element
->Seek (start_pts
, true);
332 if (entry
->GetIsLive ())
336 duration
= media
->GetDemuxer ()->GetDuration ();
338 if (entry
!= NULL
&& entry
->HasInheritedDuration () && entry
->GetInheritedDuration ()->HasTimeSpan ()) {
339 asx_duration
= TimeSpan_ToPts (entry
->GetInheritedDuration ()->GetTimeSpan ());
340 if (asx_duration
< duration
|| GetBit (IsLive
)) {
341 duration
= asx_duration
;
342 SetBit (FixedDuration
);
346 SetBit (LoadFramePending
);
348 media
->AddSafeHandler (Media::SeekCompletedEvent
, SeekCompletedCallback
, this);
349 media
->SetBufferingTime (element
->GetBufferingTime ());
352 video_stream
->AddSafeHandler (IMediaStream::FirstFrameEnqueuedEvent
, FirstFrameEnqueuedCallback
, this);
353 // We may attach the handler above after the first frame has been queued,
354 // so just execute LoadVideoFrame once right away
362 MediaPlayer::SetVideoBufferSize (gint32 width
, gint32 height
)
366 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (%i, %i). buffer_width: %i, buffer_height: %i\n", width
, height
, buffer_width
, buffer_height
);
370 cairo_surface_destroy (surface
);
374 /* NOTE: We only accept RGB32 or RGBA32 data here */
375 stride
= cairo_format_stride_for_width (format
== MoonPixelFormatRGB32
? CAIRO_FORMAT_RGB24
: CAIRO_FORMAT_ARGB32
, MAX (width
, buffer_width
));
378 int remain
= stride
% 64;
379 stride
+= 64 - remain
;
382 if (width
> buffer_width
|| height
> buffer_height
) {
383 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (): creating new buffer.\n");
385 // for conversion to rgb32 format needed for rendering with 16 byte alignment
386 if (posix_memalign ((void **)(&rgb_buffer
), 16, height
* stride
)) {
388 g_warning ("Could not allocate memory for video RGB buffer");
391 memset (rgb_buffer
, 0, height
* stride
);
393 buffer_width
= width
;
394 buffer_height
= height
;
398 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (): creating new surface, width: %i, height: %i, stride: %i\n", width
, height
, stride
);
399 /* NOTE: We only accept RGB32 or RGBA32 data here */
400 surface
= cairo_image_surface_create_for_data (rgb_buffer
, format
== MoonPixelFormatRGB32
? CAIRO_FORMAT_RGB24
: CAIRO_FORMAT_ARGB32
, width
, height
, stride
);
404 MediaPlayer::Initialize ()
406 LOG_MEDIAPLAYER ("MediaPlayer::Initialize ()\n");
409 // Clear out any state, bits, etc
410 state_unlocked
= (PlayerState
) 0;
411 // Set initial states and bits
413 SetBit (SeekSynched
);
422 first_live_pts
= G_MAXUINT64
;
424 audio_stream_count
= 0;
428 frames_update_timestamp
= 0;
431 rendered_frames_per_second
= 0.0;
432 dropped_frames_per_second
= 0.0;
436 MediaPlayer::Close ()
438 LOG_MEDIAPLAYER ("MediaPlayer::Close ()\n");
442 if (audio_unlocked
) {
443 AudioPlayer::Remove (audio_unlocked
);
444 audio_unlocked
->Dispose ();
445 audio_unlocked
->unref ();
446 audio_unlocked
= NULL
;
452 // Reset state back to what it was at instantiation
454 if (rgb_buffer
!= NULL
) {
461 if (surface
!= NULL
) {
462 cairo_surface_destroy (surface
);
467 video_stream
->RemoveSafeHandlers (this);
468 video_stream
->unref ();
481 // Puts the data into our rgb buffer.
482 // If necessary converts the data from its source format to rgb first.
486 MediaPlayer::RenderFrame (MediaFrame
*frame
)
488 VideoStream
*stream
= (VideoStream
*) frame
->stream
;
490 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 ());
493 if (!frame
->IsDecoded ()) {
494 fprintf (stderr
, "MediaPlayer::RenderFrame (): Trying to render a frame which hasn't been decoded yet.\n");
498 if ((frame
->width
> 0 && frame
->width
!= width
) || (frame
->height
> 0 && frame
->height
!= height
) || (format
!= stream
->GetDecoder ()->GetPixelFormat ())) {
499 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",
500 frame
->width
, frame
->height
, video_stream
->width
, video_stream
->height
, width
, height
);
502 if (frame
->width
> 0)
503 width
= frame
->width
;
504 if (frame
->height
> 0)
505 height
= frame
->height
;
507 format
= stream
->GetDecoder ()->GetPixelFormat ();
509 SetVideoBufferSize (width
, height
);
512 if (!frame
->IsPlanar ()) {
513 // Just copy the data
514 guint32 stride
= cairo_image_surface_get_stride (surface
);
515 for (int i
= 0; i
< height
; i
++)
516 memcpy (rgb_buffer
+ stride
* i
, frame
->buffer
+ i
* width
* 4, width
* 4);
517 SetBit (RenderedFrame
);
518 element
->MediaInvalidate ();
522 if (frame
->data_stride
== NULL
||
523 frame
->data_stride
[1] == NULL
||
524 frame
->data_stride
[2] == NULL
) {
528 guint8
*rgb_dest
[3] = { rgb_buffer
, NULL
, NULL
};
529 int rgb_stride
[3] = { cairo_image_surface_get_stride (surface
), 0, 0 };
531 stream
->converter
->Convert (frame
->data_stride
, frame
->srcStride
, frame
->srcSlideY
,
532 frame
->srcSlideH
, rgb_dest
, rgb_stride
);
534 SetBit (RenderedFrame
);
535 element
->MediaInvalidate ();
539 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, \
540 MilliSeconds_FromPts (frame->pts), \
541 MilliSeconds_FromPts (target_pts), \
542 (gint64) MilliSeconds_FromPts (frame->pts) - (gint64) MilliSeconds_FromPts (target_pts), \
543 rendered_frames_per_second, \
544 dropped_frames_per_second, \
545 dropped_frames_per_second + rendered_frames_per_second);
548 MediaPlayer::AdvanceFrame ()
550 MediaFrame
*frame
= NULL
;
551 IMediaStream
*stream
;
552 guint64 target_pts
= 0;
553 guint64 target_pts_start
= 0;
554 guint64 target_pts_end
= 0;
555 guint64 target_pts_delta
= MilliSeconds_ToPts (100);
557 double dropped_frames_per_second
= -1;
558 double rendered_frames_per_second
= -1;
563 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",
564 state_unlocked
, current_pts
, IsPaused (), IsSeeking (), GetBit (VideoEnded
), GetBit (AudioEnded
), HasVideo (), HasAudio ());
567 RemoveBit (LoadFramePending
);
575 if (GetBit (VideoEnded
))
581 // If the audio isn't playing, there might be slight length-difference between
582 // audio and video streams (the audio is shorted and finished earlier for instance)
583 // Treat this case as if there's no audio at all.
585 if (audio
!= NULL
&& audio
->GetState () == AudioPlaying
) {
586 // use target_pts as set by audio thread
587 target_pts
= GetTargetPts ();
588 if (target_pts
== G_MAXUINT64
) {
589 // This might happen if we've called Play on the audio source, but it hasn't actually played anything yet.
590 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): invalid target pts from the audio stream.\n");
595 // no audio to sync to
596 guint64 now
= TimeSpan_ToPts (element
->GetTimeManager()->GetCurrentTime ());
597 guint64 elapsed_pts
= now
- start_time
;
599 target_pts
= elapsed_pts
;
602 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",
603 target_pts, MilliSeconds_FromPts (target_pts), elapsed_pts, MilliSeconds_FromPts (elapsed_pts), start_time, MilliSeconds_FromPts (start_time));
611 this->target_pts
= target_pts
;
613 target_pts_start
= target_pts_delta
> target_pts
? 0 : target_pts
- target_pts_delta
;
614 target_pts_end
= target_pts
+ target_pts_delta
;
616 if (current_pts
>= target_pts_end
&& GetBit (SeekSynched
) && !(HasAudio () && GetBit (AudioEnded
))) {
617 #if DEBUG_ADVANCEFRAME
618 printf ("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",
619 MilliSeconds_FromPts (current_pts
), MilliSeconds_FromPts (target_pts
), MilliSeconds_FromPts (target_pts_delta
), current_pts
- target_pts
, MilliSeconds_FromPts (current_pts
- target_pts
));
624 #if DEBUG_ADVANCEFRAME
625 printf ("MediaPlayer::AdvanceFrame (): target pts: %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms\n", target_pts
, MilliSeconds_FromPts (target_pts
));
629 frame
= video_stream
->PopFrame ();
631 if (video_stream
->GetOutputEnded ()) {
633 // Set the target pts to the last pts we showed, since target_pts is what's reported as our current position.
634 this->target_pts
= current_pts
;
640 SetBufferUnderflow ();
641 // If we have audio, we keep playing (and loosing frames) until the audio playing stops due to buffer underflow
642 // TODO: determine if we don't have video due to not having enough data (in which case we should start buffering),
643 // or if it is because the decoder can't keep up (in which case we should drop frames).
647 stream
= frame
->stream
;
648 current_pts
= frame
->pts
;
651 //printf ("MediaPlayer::AdvanceFrame (): current_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, duration: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
652 // current_pts, MilliSeconds_FromPts (current_pts),
653 // duration, MilliSeconds_FromPts (duration));
655 if (GetBit (IsLive
)) {
656 first_live_pts
= MIN (current_pts
, first_live_pts
);
659 if (GetBit (FixedDuration
)) {
661 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",
662 GetBit (IsLive), MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (duration), MilliSeconds_FromPts (first_live_pts), MilliSeconds_FromPts (current_pts - first_live_pts));
664 if (GetBit (IsLive
)) {
665 if (current_pts
- first_live_pts
> duration
) {
666 // TODO: Move this out of AdvanceFrame, here it only works for media which has video, not for audio-only media.
672 if (current_pts
> duration
) {
678 if (GetBit (VideoEnded
)) {
679 //printf ("MediaPlayer::AdvanceFrame (): Reached end of duration.\n");
685 if (!frame
->IsDecoded ()) {
686 printf ("MediaPlayer::AdvanceFrame (): Got a non-decoded frame.\n");
690 if (update
&& current_pts
>= target_pts_start
) {
691 if (!GetBit (SeekSynched
)) {
692 SetBit (SeekSynched
);
693 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
));
695 // we are in sync (or ahead) of audio playback
699 if (video_stream
->IsQueueEmpty ()) {
700 // no more packets in queue, this frame is the most recent we have available
704 // we are lagging behind, drop this frame
707 //LOG_RS ("[SKIPPED]");
708 media
->DisposeObject (frame
);
713 if (update
&& frame
&& GetBit (SeekSynched
)) {
715 //LOG_RS ("[RENDER]");
721 media
->DisposeObject (frame
);
727 if (frames_update_timestamp
== 0) {
728 frames_update_timestamp
= now
;
729 } else if ((now
- frames_update_timestamp
) > TIMESPANTICKS_IN_SECOND
) {
730 double time_elapsed
= (double) (now
- frames_update_timestamp
) / (double) TIMESPANTICKS_IN_SECOND
;
731 dropped_frames_per_second
= (double) dropped_frames
/ time_elapsed
;
732 rendered_frames_per_second
= (double) rendered_frames
/ time_elapsed
;
733 dropped_frames
= rendered_frames
= 0;
734 frames_update_timestamp
= now
;
736 this->dropped_frames_per_second
= dropped_frames_per_second
;
737 this->rendered_frames_per_second
= rendered_frames_per_second
;
744 MediaPlayer::FirstFrameEnqueuedHandler (EventObject
*sender
, EventArgs
*args
)
750 MediaPlayer::LoadVideoFrameCallback (EventObject
*object
)
752 ((MediaPlayer
*) object
)->LoadVideoFrame ();
756 MediaPlayer::LoadVideoFrame ()
761 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), HasVideo: %i, LoadFramePending: %i\n", HasVideo (), state_unlocked
& LoadFramePending
);
767 if (!IsLoadFramePending ())
770 frame
= video_stream
->PopFrame ();
775 target_pts
= GetTargetPts ();
777 if (target_pts
== G_MAXUINT64
)
780 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
);
782 if (frame
->pts
+ video_stream
->pts_per_frame
>= target_pts
) {
783 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (): rendering.\n");
784 RemoveBit (LoadFramePending
);
786 element
->MediaInvalidate ();
788 AddTickCallSafe (LoadVideoFrameCallback
);
791 media
->DisposeObject (frame
);
798 MediaPlayer::AdvanceFrameCallback (void *user_data
)
800 MediaPlayer
*mplayer
= (MediaPlayer
*) user_data
;
801 mplayer
->SetCurrentDeployment ();
802 mplayer
->AdvanceFrame ();
804 Deployment::SetCurrent (NULL
);
810 MediaPlayer::SetTimeout (gint32 timeout
/* set to 0 to clear */)
812 TimeManager
*time_manager
= element
? element
->GetTimeManager () : NULL
;
813 bool clear
= timeout
== 0 || advance_frame_timeout_id
!= 0;
815 LOG_MEDIAPLAYER ("MediaPlayer::SetTimeout (%i) time_manager: %p id: %i\n", timeout
, time_manager
, GET_OBJ_ID (time_manager
));
817 if (clear
&& advance_frame_timeout_id
!= 0) {
818 if (time_manager
!= NULL
) {
819 time_manager
->RemoveTimeout (advance_frame_timeout_id
);
821 g_warning ("MediaPlayer::SetTimeout (): Could not clear timeout. Leaking ourselves to not crash.\n");
822 ref (); // This will prevent us from getting destroyed.
824 advance_frame_timeout_id
= 0;
828 if (time_manager
== NULL
) {
829 g_warning ("MediaPlayer::SetTimeout (): Could not set timeout (no time manager).\n");
831 advance_frame_timeout_id
= time_manager
->AddTimeout (MOON_PRIORITY_DEFAULT
, timeout
, AdvanceFrameCallback
, this);
841 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i, IsPlaying: %i, IsSeeking: %i\n", state_unlocked
, IsPlaying (), IsSeeking ());
848 RemoveBit (BufferUnderflow
);
849 start_time
= TimeSpan_ToPts (element
->GetTimeManager()->GetCurrentTime ());
850 start_time
-= target_pts
;
858 SetTimeout (GetTimeoutInterval ());
860 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i [Done]\n", state_unlocked
);
864 MediaPlayer::GetTimeoutInterval ()
866 gint32 result
; // ms between timeouts
867 guint64 pts_per_frame
= 0;
872 pts_per_frame
= video_stream
->pts_per_frame
;
873 // there are 10000 pts in a millisecond, anything less than that will result in 0 (and an endless loop)
874 if (pts_per_frame
< PTS_PER_MILLISECOND
|| pts_per_frame
>= (guint64
) G_MAXINT32
) {
875 // If the stream doesn't know its frame rate, use a default of 60 fps
876 result
= (gint32
) (1000.0 / 60.0);
878 result
= (gint32
) MilliSeconds_FromPts (pts_per_frame
);
884 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
);
890 MediaPlayer::SetAudioStreamIndex (gint32 index
)
892 IMediaDemuxer
*demuxer
= NULL
;
893 IMediaStream
*stream
= NULL
;
894 AudioStream
*next_stream
= NULL
;
895 AudioStream
*prev_stream
= NULL
;
896 gint32 audio_streams_found
= 0;
899 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i).\n", index
);
902 if (index
< 0 || index
>= audio_stream_count
) {
903 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Invalid audio stream index.\n", index
);
908 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No media.\n", index
);
914 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No audio source.\n", index
);
918 demuxer
= media
->GetDemuxer ();
920 if (demuxer
== NULL
) {
921 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Media doesn't have a demuxer.\n", index
);
925 prev_stream
= audio
->GetAudioStream ();
927 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
928 stream
= demuxer
->GetStream (i
);
930 if (stream
->GetType () != MediaTypeAudio
)
933 if (audio_streams_found
== index
) {
934 next_stream
= (AudioStream
*) stream
;
938 audio_streams_found
++;
941 if (next_stream
!= NULL
) {
942 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i). Switched stream from #%i to #%i\n", index
, audio_streams_found
++, index
);
943 prev_stream
->SetSelected (false);
944 next_stream
->SetSelected (true);
945 audio
->SetAudioStream (next_stream
);
952 MediaPlayer::GetCanPause ()
954 // FIXME: should return false if it is streaming media
956 return GetBit (CanPause
);
960 MediaPlayer::SetCanPause (bool value
)
963 SetBitTo (CanPause
, value
);
967 MediaPlayer::Pause ()
971 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i\n", state_unlocked
);
987 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i [Done]\n", state_unlocked
);
991 MediaPlayer::GetTargetPts ()
1000 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);
1002 if (audio
!= NULL
&& audio
->GetState () == AudioPlaying
)
1003 result
= audio
->GetCurrentPts ();
1005 result
= target_pts
;
1014 MediaPlayer::SeekCompletedHandler (Media
*media
, EventArgs
*args
)
1016 LOG_MEDIAPLAYER ("MediaPlayer::SeekCompletedHandler () seeks: %i\n", seeks
);
1024 SetBit (LoadFramePending
);
1030 MediaPlayer::NotifySeek (guint64 pts
)
1032 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
);
1036 guint64 duration
= GetDuration ();
1038 g_return_if_fail (GetCanSeek ());
1040 if (pts
> start_pts
+ duration
)
1041 pts
= start_pts
+ duration
;
1043 if (pts
< start_pts
)
1049 SetBit (LoadFramePending
);
1050 RemoveBit (SeekSynched
);
1051 RemoveBit (AudioEnded
);
1052 RemoveBit (VideoEnded
);
1058 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
);
1062 MediaPlayer::GetCanSeek ()
1065 return GetBit (CanSeek
);
1069 MediaPlayer::SetCanSeek (bool value
)
1072 SetBitTo (CanSeek
, value
);
1076 MediaPlayer::Stop ()
1078 LOG_MEDIAPLAYER ("MediaPlayer::Stop (), state: %i\n", state_unlocked
);
1089 RemoveBit (AudioEnded
);
1090 RemoveBit (VideoEnded
);
1094 MediaPlayer::StopAudio ()
1098 LOG_MEDIAPLAYER ("MediaPlayer::StopAudio (), state: %i\n", state_unlocked
);
1101 audio
= GetAudio (); // This returns a reffed AudioSource
1109 MediaPlayer::GetBalance ()
1116 audio
= GetAudio ();
1118 result
= audio
->GetBalance ();
1121 fprintf (stderr
, "MediaPlayer::GetBalance (): There's no audio source to get the balance from\n");
1129 MediaPlayer::SetBalance (double balance
)
1133 LOG_MEDIAPLAYER ("MediaPlayer::SetBalance (%f)\n", balance
);
1138 else if (balance
> 1.0)
1141 audio
= GetAudio ();
1143 audio
->SetBalance (balance
);
1146 //fprintf (stderr, "MediaPlayer::SetBalance (%f): There's no audio source to set the balance\n", balance);
1151 MediaPlayer::GetVolume ()
1158 audio
= GetAudio ();
1160 result
= audio
->GetVolume ();
1163 fprintf (stderr
, "MediaPlayer::GetVolume (): There's no audio source to get the volume from\n");
1171 MediaPlayer::SetVolume (double volume
)
1175 LOG_MEDIAPLAYER ("MediaPlayer::SetVolume (%f)\n", volume
);
1180 else if (volume
> 1.0)
1183 audio
= GetAudio ();
1185 audio
->SetVolume (volume
);
1188 //fprintf (stderr, "MediaPlayer::SetVolume (%f): There's no audio source to set the volume\n", volume);
1193 MediaPlayer::GetMuted ()
1200 audio
= GetAudio ();
1202 result
= audio
->GetMuted ();
1205 fprintf (stderr
, "MediaPlayer::GetMuted (): There's no audio.\n");
1213 MediaPlayer::SetMuted (bool muted
)
1217 LOG_MEDIAPLAYER ("MediaPlayer::SetMuted (%i)\n", muted
);
1220 audio
= GetAudio ();
1222 audio
->SetMuted (true);
1225 //fprintf (stderr, "MediaPlayer::SetMuted (%i): There's no audio to mute.\n", muted);
1230 MediaPlayer::SetBit (PlayerState s
)
1233 state_unlocked
= (PlayerState
) (s
| state_unlocked
);
1238 MediaPlayer::RemoveBit (PlayerState s
)
1241 state_unlocked
= (PlayerState
) (~s
& state_unlocked
);
1246 MediaPlayer::SetBitTo (PlayerState s
, bool value
)
1256 MediaPlayer::GetBit (PlayerState s
)
1260 result
= (state_unlocked
& s
) == s
;
1266 MediaPlayer::SetState (PlayerState s
)
1269 state_unlocked
= (PlayerState
) ((state_unlocked
& ~StateMask
) | s
);
1273 MediaPlayer::PlayerState
1274 MediaPlayer::GetState ()
1278 result
= state_unlocked
;
1284 MediaPlayer::IsBufferUnderflow ()
1286 return GetBit (BufferUnderflow
);
1290 MediaPlayer::IsLoadFramePending ()
1292 return GetBit (LoadFramePending
);
1296 MediaPlayer::IsSeeking ()
1302 MediaPlayer::HasRenderedFrame ()
1304 return GetBit (RenderedFrame
);
1308 MediaPlayer::IsPlaying ()
1310 return (GetState () & StateMask
) == Playing
;
1314 MediaPlayer::IsPaused ()
1316 return (GetState () & StateMask
) == Paused
;
1320 MediaPlayer::IsStopped ()
1322 return (GetState () & StateMask
) == Stopped
;
1326 MediaPlayer::SetBufferUnderflow ()
1328 SetBitTo (BufferUnderflow
, true);
1329 EmitBufferUnderflow ();
1333 MediaPlayer::EmitBufferUnderflow ()
1335 if (Surface::InMainThread ()) {
1336 Emit (BufferUnderflowEvent
);
1338 AddTickCallSafe (EmitBufferUnderflowAsync
);
1343 MediaPlayer::EmitBufferUnderflowAsync (EventObject
*obj
)
1345 ((MediaPlayer
*) obj
)->EmitBufferUnderflow ();