2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / mediaplayer.cpp
blob355512528c5331418b39bfbaf13bbe772418f148
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * mplayer.cpp:
5 * Contact:
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.
13 #include <config.h>
15 #include <stdlib.h>
16 #include <glib.h>
18 #include "timesource.h"
19 #include "timemanager.h"
20 #include "mediaplayer.h"
21 #include "pipeline.h"
22 #include "runtime.h"
23 #include "list.h"
24 #include "media.h"
25 #include "mediaelement.h"
26 #include "debug.h"
27 #include "playlist.h"
29 #define DEBUG_ADVANCEFRAME 0
32 * MediaPlayer
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));
40 VERIFY_MAIN_THREAD;
42 element = el;
44 video_stream = NULL;
45 surface = NULL;
46 rgb_buffer = NULL;
47 buffer_width = 0;
48 buffer_height = 0;
49 format = MoonPixelFormatRGB32;
50 advance_frame_timeout_id = 0;
52 media = NULL;
53 audio_unlocked = NULL;
55 Initialize ();
58 MediaPlayer::~MediaPlayer ()
60 LOG_MEDIAPLAYER ("MediaPlayer::~MediaPlayer (), id=%i\n", GET_OBJ_ID (this));
61 VERIFY_MAIN_THREAD;
64 void
65 MediaPlayer::Dispose ()
67 LOG_MEDIAPLAYER ("MediaPlayer::Dispose (), id=%i\n", GET_OBJ_ID (this));
69 VERIFY_MAIN_THREAD;
71 Close ();
73 element = NULL;
75 EventObject::Dispose ();
78 AudioSource *
79 MediaPlayer::GetAudio ()
81 AudioSource *result = NULL;
83 // Thread-safe
85 mutex.Lock ();
86 if (audio_unlocked != NULL) {
87 result = audio_unlocked;
88 result->ref ();
90 mutex.Unlock ();
92 return result;
95 void
96 MediaPlayer::SetSurface (Surface *s)
98 if (!SetSurfaceLock ())
99 return;
101 EventObject::SetSurface (s);
103 SetSurfaceUnlock ();
106 void
107 MediaPlayer::AudioFinishedCallback (EventObject *user_data)
109 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinishedCallback ()\n");
110 VERIFY_MAIN_THREAD;
112 MediaPlayer *mplayer = (MediaPlayer *) user_data;
113 mplayer->AudioFinished ();
116 void
117 MediaPlayer::AudioFinished ()
119 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinished () VideoEnded: %i, AudioEnded: %i AudioSource id: %i\n", GetBit (VideoEnded), GetBit (AudioEnded), GET_OBJ_ID (audio_unlocked));
121 // This method must be thread-safe
123 if (!Surface::InMainThread ()) {
124 AddTickCallSafe (AudioFinishedCallback);
125 return;
128 VERIFY_MAIN_THREAD;
129 if (!GetBit (AudioEnded)) {
130 SetBit (AudioEnded);
131 CheckFinished ();
135 void
136 MediaPlayer::VideoFinished ()
138 LOG_MEDIAPLAYER ("MediaPlayer::VideoFinished () VideoEnded: %i, AudioEnded: %i\n", GetBit (VideoEnded), GetBit (AudioEnded));
139 VERIFY_MAIN_THREAD;
141 if (!GetBit (VideoEnded)) {
142 SetBit (VideoEnded);
143 CheckFinished ();
147 void
148 MediaPlayer::CheckFinished ()
150 LOG_MEDIAPLAYER ("MediaPlayer::CheckFinished (), HasVideo: %i, VideoEnded: %i, HasAudio: %i, AudioEnded: %i\n",
151 HasVideo (), GetBit (VideoEnded), HasAudio (), GetBit (AudioEnded));
152 VERIFY_MAIN_THREAD;
154 if (HasVideo () && !GetBit (VideoEnded))
155 return;
157 if (HasAudio () && !GetBit (AudioEnded))
158 return;
160 Emit (MediaEndedEvent);
163 void
164 MediaPlayer::AudioFailed (AudioSource *source)
166 // This method must be thread-safe
168 mutex.Lock ();
169 if (this->audio_unlocked == source) {
170 AudioPlayer::Remove (this->audio_unlocked);
171 this->audio_unlocked->unref ();
172 this->audio_unlocked = NULL;
174 mutex.Unlock ();
177 bool
178 MediaPlayer::Open (Media *media, PlaylistEntry *entry)
180 IMediaDecoder *encoding;
181 IMediaStream *stream;
182 guint64 asx_duration;
183 gint32 *audio_stream_index = NULL;
184 AudioSource *audio;
186 LOG_MEDIAPLAYER ("MediaPlayer::Open (%p), current media: %p\n", media, this->media);
187 VERIFY_MAIN_THREAD;
189 Close ();
191 if (media == NULL) {
192 printf ("MediaPlayer::Open (): media is NULL.\n");
193 return false;
196 if (!media->IsOpened ()) {
197 printf ("MediaPlayer::Open (): media isn't opened.\n");
198 return false;
201 this->media = media;
202 this->media->ref ();
204 SetState (Opened);
206 // Find audio/video streams
207 IMediaDemuxer *demuxer = media->GetDemuxer ();
208 VideoStream *vstream = NULL;
209 AudioStream *astream = NULL, *astream2 = NULL;
211 if (demuxer == NULL) {
212 fprintf (stderr, "MediaPlayer::Open (): media doesn't have a demuxer.\n");
213 return false;
216 audio_stream_index = element->GetAudioStreamIndex ();
218 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
219 stream = demuxer->GetStream (i);
220 encoding = stream->GetDecoder (); //stream->codec;
222 if (encoding == NULL)
223 continue; // No encoding was found for the stream.
225 switch (stream->GetType ()) {
226 case MediaTypeAudio:
227 audio_stream_count++;
228 if (audio_stream_index != NULL){
229 if (*audio_stream_index == audio_stream_count - 1) {
230 astream = (AudioStream *) stream;
232 } else {
233 astream2 = (AudioStream *) stream;
235 if (astream == NULL || astream->GetBitRate () < astream2->GetBitRate ())
236 astream = astream2;
239 break;
240 case MediaTypeVideo:
241 vstream = (VideoStream *) stream;
243 if (video_stream != NULL && vstream->GetBitRate () < video_stream->GetBitRate ())
244 break;
246 video_stream = vstream;
247 video_stream->SetSelected (true);
248 video_stream->ref ();
250 height = video_stream->height;
251 width = video_stream->width;
253 SetVideoBufferSize (width, height);
255 // printf ("video size: %i, %i\n", video_stream->width, video_stream->height);
256 break;
257 case MediaTypeMarker:
258 LOG_MEDIAPLAYER ("MediaPlayer::Open (): Found a marker stream, selecting it.\n");
259 stream->SetSelected (true);
260 default:
261 break;
265 if (astream != NULL) {
266 audio = AudioPlayer::Add (this, astream);
267 if (audio != NULL) {
268 // Only select the audio stream if we can actually play it
269 astream->SetSelected (true);
270 audio->ref ();
271 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected audio stream (%d) properties:\n"
272 "\tchannels: Input: %d Output: %d\n"
273 "\tsample_rate: Input: %d Output: %d\n"
274 "\tbit_rate: Input: %d Output: %d\n"
275 "\tblock_align: Input: %d Output: %d\n"
276 "\tbits_per_sample: Input: %d Output: %d\n"
277 "\tcodec_id: 0x%x\n"
278 "\tduration: %" G_GUINT64_FORMAT "\n"
279 "\textra data size: %d\n",
280 astream->index, astream->GetChannels (), astream->GetOutputChannels (),
281 astream->GetSampleRate (), astream->GetOutputSampleRate (),
282 astream->GetBitRate (), astream->GetOutputBitRate (),
283 astream->GetBlockAlign (), astream->GetOutputBlockAlign (),
284 astream->GetBitsPerSample (), astream->GetOutputBitsPerSample (),
285 astream->GetCodecId (), astream->GetDuration (), astream->GetExtraDataSize ());
286 if (astream->extra_data_size > 0) {
287 int n;
288 LOG_MEDIAPLAYER ("\textra data: ");
289 for (n = 0; n < astream->extra_data_size; n++)
290 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8*)astream->extra_data)[n]);
291 LOG_MEDIAPLAYER ("\n");
293 mutex.Lock ();
294 this->audio_unlocked = audio;
295 mutex.Unlock ();
298 if (video_stream != NULL) {
299 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected Video stream (%d) properties:\n"
300 "\twidth: %d\n"
301 "\theight: %d\n"
302 "\tbits_per_sample: %d\n"
303 "\tbit_rate: %d\n"
304 "\tcodec_id: 0x%x\n"
305 "\tpts_per_frame: %" G_GUINT64_FORMAT "\n"
306 "\tduration: %" G_GUINT64_FORMAT "\n"
307 "\textra data size: %d\n",
308 video_stream->index, video_stream->width, video_stream->height, video_stream->bits_per_sample,
309 video_stream->bit_rate, video_stream->codec_id, video_stream->pts_per_frame,
310 video_stream->duration, video_stream->extra_data_size);
311 if (video_stream->extra_data_size > 0) {
312 int n;
313 LOG_MEDIAPLAYER ("\textra data: ");
314 for (n = 0; n < video_stream->extra_data_size; n++)
315 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8*)video_stream->extra_data)[n]);
316 LOG_MEDIAPLAYER ("\n");
320 current_pts = 0;
321 target_pts = 0;
322 start_pts = 0;
324 if (entry != NULL) {
325 start_pts = TimeSpan_ToPts (entry->GetStartTime ());
326 LOG_MEDIAPLAYER ("MediaPlayer::Open (), setting start_pts to: %" G_GUINT64_FORMAT " (%" G_GUINT64_FORMAT " ms).\n", start_pts, MilliSeconds_FromPts (start_pts));
327 // note that we might be re-opening a media which is not at position 0,
328 // so it is not possible to optimize away the case where start_pts = 0.
329 media->SeekAsync (start_pts);
331 if (entry->GetIsLive ())
332 SetBit (IsLive);
335 duration = media->GetDemuxer ()->GetDuration ();
337 if (entry != NULL && entry->HasInheritedDuration () && entry->GetInheritedDuration ()->HasTimeSpan ()) {
338 asx_duration = TimeSpan_ToPts (entry->GetInheritedDuration ()->GetTimeSpan ());
339 if (asx_duration < duration || GetBit (IsLive)) {
340 duration = asx_duration;
341 SetBit (FixedDuration);
345 SetBit (LoadFramePending);
347 media->AddSafeHandler (Media::SeekCompletedEvent, SeekCompletedCallback, this);
348 media->SetBufferingTime (element->GetBufferingTime ());
350 if (HasVideo ()) {
351 video_stream->AddSafeHandler (IMediaStream::FirstFrameEnqueuedEvent, FirstFrameEnqueuedCallback, this);
352 // We may attach the handler above after the first frame has been queued,
353 // so just execute LoadVideoFrame once right away
354 LoadVideoFrame ();
357 return true;
360 void
361 MediaPlayer::SetVideoBufferSize (gint32 width, gint32 height)
363 gint32 stride;
365 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (%i, %i). buffer_width: %i, buffer_height: %i\n", width, height, buffer_width, buffer_height);
366 VERIFY_MAIN_THREAD;
368 if (surface) {
369 cairo_surface_destroy (surface);
370 surface = NULL;
373 /* NOTE: We only accept RGB32 or RGBA32 data here */
374 stride = cairo_format_stride_for_width (format == MoonPixelFormatRGB32 ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32, MAX (width, buffer_width));
376 if (stride % 64) {
377 int remain = stride % 64;
378 stride += 64 - remain;
381 if (width > buffer_width || height > buffer_height) {
382 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (): creating new buffer.\n");
383 free (rgb_buffer);
384 // for conversion to rgb32 format needed for rendering with 16 byte alignment
385 if (posix_memalign ((void **)(&rgb_buffer), 16, height * stride)) {
386 rgb_buffer = NULL;
387 g_warning ("Could not allocate memory for video RGB buffer");
388 return;
390 memset (rgb_buffer, 0, height * stride);
392 buffer_width = width;
393 buffer_height = height;
396 // rendering surface
397 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (): creating new surface, width: %i, height: %i, stride: %i\n", width, height, stride);
398 /* NOTE: We only accept RGB32 or RGBA32 data here */
399 surface = cairo_image_surface_create_for_data (rgb_buffer, format == MoonPixelFormatRGB32 ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32, width, height, stride);
402 void
403 MediaPlayer::Initialize ()
405 LOG_MEDIAPLAYER ("MediaPlayer::Initialize ()\n");
406 VERIFY_MAIN_THREAD;
408 // Clear out any state, bits, etc
409 state_unlocked = (PlayerState) 0;
410 // Set initial states and bits
411 SetState (Stopped);
412 SetBit (SeekSynched);
413 SetBit (CanSeek);
414 SetBit (CanPause);
416 start_time = 0;
417 start_pts = 0;
418 current_pts = 0;
419 target_pts = 0;
420 first_live_pts = G_MAXUINT64;
422 audio_stream_count = 0;
423 height = 0;
424 width = 0;
426 frames_update_timestamp = 0;
427 rendered_frames = 0;
428 dropped_frames = 0;
429 rendered_frames_per_second = 0.0;
430 dropped_frames_per_second = 0.0;
433 void
434 MediaPlayer::Close ()
436 LOG_MEDIAPLAYER ("MediaPlayer::Close ()\n");
437 VERIFY_MAIN_THREAD;
439 mutex.Lock ();
440 if (audio_unlocked) {
441 AudioPlayer::Remove (audio_unlocked);
442 audio_unlocked->Dispose ();
443 audio_unlocked->unref ();
444 audio_unlocked = NULL;
446 mutex.Unlock ();
448 Stop ();
450 // Reset state back to what it was at instantiation
452 if (rgb_buffer != NULL) {
453 free (rgb_buffer);
454 rgb_buffer = NULL;
456 buffer_width = 0;
457 buffer_height = 0;
459 if (surface != NULL) {
460 cairo_surface_destroy (surface);
461 surface = NULL;
464 if (video_stream) {
465 video_stream->RemoveSafeHandlers (this);
466 video_stream->unref ();
467 video_stream = NULL;
470 if (media) {
471 media->unref ();
472 media = NULL;
475 Initialize ();
479 // Puts the data into our rgb buffer.
480 // If necessary converts the data from its source format to rgb first.
483 void
484 MediaPlayer::RenderFrame (MediaFrame *frame)
486 VideoStream *stream = (VideoStream *) frame->stream;
488 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 ());
489 VERIFY_MAIN_THREAD;
491 if (!frame->IsDecoded ()) {
492 fprintf (stderr, "MediaPlayer::RenderFrame (): Trying to render a frame which hasn't been decoded yet.\n");
493 return;
496 if ((frame->width > 0 && frame->width != width) || (frame->height > 0 && frame->height != height) || (format != stream->GetDecoder ()->GetPixelFormat ())) {
497 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",
498 frame->width, frame->height, video_stream->width, video_stream->height, width, height);
500 if (frame->width > 0)
501 width = frame->width;
502 if (frame->height > 0)
503 height = frame->height;
505 format = stream->GetDecoder ()->GetPixelFormat ();
507 SetVideoBufferSize (width, height);
510 if (!frame->IsPlanar ()) {
511 // Just copy the data
512 guint32 stride = cairo_image_surface_get_stride (surface);
513 for (int i = 0; i < height; i++)
514 memcpy (rgb_buffer + stride * i, frame->buffer + i * width * 4, width * 4);
515 SetBit (RenderedFrame);
516 element->MediaInvalidate ();
517 return;
520 if (frame->data_stride == NULL ||
521 frame->data_stride[1] == NULL ||
522 frame->data_stride[2] == NULL) {
523 return;
526 guint8 *rgb_dest [3] = { rgb_buffer, NULL, NULL };
527 int rgb_stride [3] = { cairo_image_surface_get_stride (surface), 0, 0 };
529 stream->converter->Convert (frame->data_stride, frame->srcStride, frame->srcSlideY,
530 frame->srcSlideH, rgb_dest, rgb_stride);
532 SetBit (RenderedFrame);
533 element->MediaInvalidate ();
536 #define LOG_RS(x) \
537 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, \
538 MilliSeconds_FromPts (frame->pts), \
539 MilliSeconds_FromPts (target_pts), \
540 (gint64) MilliSeconds_FromPts (frame->pts) - (gint64) MilliSeconds_FromPts (target_pts), \
541 rendered_frames_per_second, \
542 dropped_frames_per_second, \
543 dropped_frames_per_second + rendered_frames_per_second);
545 void
546 MediaPlayer::AdvanceFrame ()
548 MediaFrame *frame = NULL;
549 IMediaStream *stream;
550 guint64 target_pts = 0;
551 guint64 target_pts_start = 0;
552 guint64 target_pts_end = 0;
553 guint64 target_pts_delta = MilliSeconds_ToPts (100);
554 bool update = false;
555 double dropped_frames_per_second = -1;
556 double rendered_frames_per_second = -1;
557 AudioSource *audio;
559 guint64 now = 0;
561 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",
562 state_unlocked, current_pts, IsPaused (), IsSeeking (), GetBit (VideoEnded), GetBit (AudioEnded), HasVideo (), HasAudio ());
563 VERIFY_MAIN_THREAD;
565 RemoveBit (LoadFramePending);
567 if (IsPaused ())
568 return;
570 if (IsSeeking ())
571 return;
573 if (GetBit (VideoEnded))
574 return;
576 if (!HasVideo ())
577 return;
579 // If the audio isn't playing, there might be slight length-difference between
580 // audio and video streams (the audio is shorted and finished earlier for instance)
581 // Treat this case as if there's no audio at all.
582 audio = GetAudio ();
583 if (audio != NULL && audio->GetState () == AudioPlaying) {
584 // use target_pts as set by audio thread
585 target_pts = GetTargetPts ();
586 if (target_pts == G_MAXUINT64) {
587 // This might happen if we've called Play on the audio source, but it hasn't actually played anything yet.
588 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): invalid target pts from the audio stream.\n");
589 audio->unref ();
590 return;
592 } else {
593 // no audio to sync to
594 guint64 now = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
595 guint64 elapsed_pts = now - start_time;
597 target_pts = elapsed_pts;
600 printf ("MediaPlayer::AdvanceFrame (): determined target_pts to be: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, elapsed_pts: %llu = %llu ms, start_time: %llu = %llu ms\n",
601 target_pts, MilliSeconds_FromPts (target_pts), elapsed_pts, MilliSeconds_FromPts (elapsed_pts), start_time, MilliSeconds_FromPts (start_time));
604 if (audio != NULL) {
605 audio->unref ();
606 audio = NULL;
609 this->target_pts = target_pts;
611 target_pts_start = target_pts_delta > target_pts ? 0 : target_pts - target_pts_delta;
612 target_pts_end = target_pts + target_pts_delta;
614 if (current_pts >= target_pts_end && GetBit (SeekSynched) && !(HasAudio () && GetBit (AudioEnded))) {
615 #if DEBUG_ADVANCEFRAME
616 printf ("MediaPlayer::AdvanceFrame (): video is running too fast, wait a bit (current_pts: %" G_GUINT64_FORMAT " ms, target_pts: %" G_GUINT64_FORMAT " ms, delta: %llu ms, diff: %lld (%lld ms)).\n",
617 MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (target_pts), MilliSeconds_FromPts (target_pts_delta), current_pts - target_pts, MilliSeconds_FromPts (current_pts - target_pts));
618 #endif
619 return;
622 #if DEBUG_ADVANCEFRAME
623 printf ("MediaPlayer::AdvanceFrame (): target pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n", target_pts, MilliSeconds_FromPts (target_pts));
624 #endif
626 while (true) {
627 frame = video_stream->PopFrame ();
628 if (frame == NULL) {
629 if (video_stream->GetOutputEnded ()) {
630 if (!HasAudio ()) {
631 // Set the target pts to the last pts we showed, since target_pts is what's reported as our current position.
632 this->target_pts = current_pts;
634 VideoFinished ();
635 return;
637 if (!HasAudio ())
638 SetBufferUnderflow ();
639 // If we have audio, we keep playing (and loosing frames) until the audio playing stops due to buffer underflow
640 // TODO: determine if we don't have video due to not having enough data (in which case we should start buffering),
641 // or if it is because the decoder can't keep up (in which case we should drop frames).
642 break;
645 stream = frame->stream;
646 current_pts = frame->pts;
647 update = true;
649 //printf ("MediaPlayer::AdvanceFrame (): current_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, duration: %llu = %llu ms\n",
650 // current_pts, MilliSeconds_FromPts (current_pts),
651 // duration, MilliSeconds_FromPts (duration));
653 if (GetBit (IsLive)) {
654 first_live_pts = MIN (current_pts, first_live_pts);
657 if (GetBit (FixedDuration)) {
659 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",
660 GetBit (IsLive), MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (duration), MilliSeconds_FromPts (first_live_pts), MilliSeconds_FromPts (current_pts - first_live_pts));
662 if (GetBit (IsLive)) {
663 if (current_pts - first_live_pts > duration) {
664 // TODO: Move this out of AdvanceFrame, here it only works for media which has video, not for audio-only media.
665 StopAudio ();
666 AudioFinished ();
667 VideoFinished ();
669 } else {
670 if (current_pts > duration) {
671 StopAudio ();
672 AudioFinished ();
673 VideoFinished ();
676 if (GetBit (VideoEnded)) {
677 //printf ("MediaPlayer::AdvanceFrame (): Reached end of duration.\n");
678 update = false;
679 break;
683 if (!frame->IsDecoded ()) {
684 printf ("MediaPlayer::AdvanceFrame (): Got a non-decoded frame.\n");
685 update = false;
688 if (update && current_pts >= target_pts_start) {
689 if (!GetBit (SeekSynched)) {
690 SetBit (SeekSynched);
691 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));
693 // we are in sync (or ahead) of audio playback
694 break;
697 if (video_stream->IsQueueEmpty ()) {
698 // no more packets in queue, this frame is the most recent we have available
699 break;
702 // we are lagging behind, drop this frame
703 dropped_frames++;
705 //LOG_RS ("[SKIPPED]");
706 media->DisposeObject (frame);
707 frame->unref ();
708 frame = NULL;
711 if (update && frame && GetBit (SeekSynched)) {
712 rendered_frames++;
713 //LOG_RS ("[RENDER]");
715 RenderFrame (frame);
718 if (frame) {
719 media->DisposeObject (frame);
720 frame->unref ();
721 frame = NULL;
724 now = get_now ();
725 if (frames_update_timestamp == 0) {
726 frames_update_timestamp = now;
727 } else if ((now - frames_update_timestamp) > TIMESPANTICKS_IN_SECOND) {
728 double time_elapsed = (double) (now - frames_update_timestamp) / (double) TIMESPANTICKS_IN_SECOND;
729 dropped_frames_per_second = (double) dropped_frames / time_elapsed;
730 rendered_frames_per_second = (double) rendered_frames / time_elapsed;
731 dropped_frames = rendered_frames = 0;
732 frames_update_timestamp = now;
734 this->dropped_frames_per_second = dropped_frames_per_second;
735 this->rendered_frames_per_second = rendered_frames_per_second;
738 return;
741 void
742 MediaPlayer::FirstFrameEnqueuedHandler (EventObject *sender, EventArgs *args)
744 LoadVideoFrame ();
747 void
748 MediaPlayer::LoadVideoFrameCallback (EventObject *object)
750 ((MediaPlayer *) object)->LoadVideoFrame ();
753 void
754 MediaPlayer::LoadVideoFrame ()
756 guint64 target_pts;
757 MediaFrame *frame;
759 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), HasVideo: %i, LoadFramePending: %i\n", HasVideo (), state_unlocked & LoadFramePending);
760 VERIFY_MAIN_THREAD;
762 if (!HasVideo ())
763 return;
765 if (!IsLoadFramePending ())
766 return;
768 frame = video_stream->PopFrame ();
770 if (frame == NULL)
771 return;
773 target_pts = GetTargetPts ();
775 if (target_pts == G_MAXUINT64)
776 target_pts = 0;
778 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);
780 if (frame->pts + video_stream->pts_per_frame >= target_pts) {
781 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (): rendering.\n");
782 RemoveBit (LoadFramePending);
783 RenderFrame (frame);
784 element->MediaInvalidate ();
785 } else {
786 AddTickCallSafe (LoadVideoFrameCallback);
789 media->DisposeObject (frame);
790 frame->unref ();
792 return;
795 gboolean
796 MediaPlayer::AdvanceFrameCallback (void *user_data)
798 MediaPlayer *mplayer = (MediaPlayer *) user_data;
799 mplayer->SetCurrentDeployment ();
800 mplayer->AdvanceFrame ();
801 #if SANITY
802 Deployment::SetCurrent (NULL);
803 #endif
804 return true;
807 void
808 MediaPlayer::SetTimeout (gint32 timeout /* set to 0 to clear */)
810 TimeManager *time_manager = element ? element->GetTimeManager () : NULL;
811 bool clear = timeout == 0 || advance_frame_timeout_id != 0;
813 LOG_MEDIAPLAYER ("MediaPlayer::SetTimeout (%i) time_manager: %p id: %i\n", timeout, time_manager, GET_OBJ_ID (time_manager));
815 if (clear && advance_frame_timeout_id != 0) {
816 if (time_manager != NULL) {
817 time_manager->RemoveTimeout (advance_frame_timeout_id);
818 } else {
819 g_warning ("MediaPlayer::SetTimeout (): Could not clear timeout. Leaking ourselves to not crash.\n");
820 ref (); // This will prevent us from getting destroyed.
822 advance_frame_timeout_id = 0;
825 if (timeout != 0) {
826 if (time_manager == NULL) {
827 g_warning ("MediaPlayer::SetTimeout (): Could not set timeout (no time manager).\n");
828 } else {
829 advance_frame_timeout_id = time_manager->AddTimeout (MOON_PRIORITY_DEFAULT, timeout, AdvanceFrameCallback, this);
834 void
835 MediaPlayer::Play ()
837 AudioSource *audio;
839 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i, IsPlaying: %i, IsSeeking: %i\n", state_unlocked, IsPlaying (), IsSeeking ());
840 VERIFY_MAIN_THREAD;
842 if (IsSeeking ())
843 return;
845 SetState (Playing);
846 RemoveBit (BufferUnderflow);
847 start_time = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
848 start_time -= target_pts;
850 audio = GetAudio ();
851 if (audio) {
852 audio->Play ();
853 audio->unref ();
856 SetTimeout (GetTimeoutInterval ());
858 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i [Done]\n", state_unlocked);
861 gint32
862 MediaPlayer::GetTimeoutInterval ()
864 gint32 result; // ms between timeouts
865 guint64 pts_per_frame = 0;
867 VERIFY_MAIN_THREAD;
869 if (HasVideo ()) {
870 pts_per_frame = video_stream->pts_per_frame;
871 // there are 10000 pts in a millisecond, anything less than that will result in 0 (and an endless loop)
872 if (pts_per_frame < PTS_PER_MILLISECOND || pts_per_frame >= (guint64) G_MAXINT32) {
873 // If the stream doesn't know its frame rate, use a default of 60 fps
874 result = (gint32) (1000.0 / 60.0);
875 } else {
876 result = (gint32) MilliSeconds_FromPts (pts_per_frame);
878 } else {
879 result = 33;
882 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);
884 return result;
887 void
888 MediaPlayer::SetAudioStreamIndex (gint32 index)
890 IMediaDemuxer *demuxer = NULL;
891 IMediaStream *stream = NULL;
892 AudioStream *next_stream = NULL;
893 AudioStream *prev_stream = NULL;
894 gint32 audio_streams_found = 0;
895 AudioSource *audio;
897 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i).\n", index);
898 VERIFY_MAIN_THREAD;
900 if (index < 0 || index >= audio_stream_count) {
901 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Invalid audio stream index.\n", index);
902 return;
905 if (media == NULL) {
906 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No media.\n", index);
907 return;
910 audio = GetAudio ();
911 if (audio == NULL) {
912 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No audio source.\n", index);
913 return;
916 demuxer = media->GetDemuxer ();
918 if (demuxer == NULL) {
919 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Media doesn't have a demuxer.\n", index);
920 return;
923 prev_stream = audio->GetAudioStream ();
925 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
926 stream = demuxer->GetStream (i);
928 if (stream->GetType () != MediaTypeAudio)
929 continue;
931 if (audio_streams_found == index) {
932 next_stream = (AudioStream *) stream;
933 break;
936 audio_streams_found++;
939 if (next_stream != NULL) {
940 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i). Switched stream from #%i to #%i\n", index, audio_streams_found++, index);
941 prev_stream->SetSelected (false);
942 next_stream->SetSelected (true);
943 audio->SetAudioStream (next_stream);
946 audio->unref ();
949 bool
950 MediaPlayer::GetCanPause ()
952 // FIXME: should return false if it is streaming media
953 VERIFY_MAIN_THREAD;
954 return GetBit (CanPause);
957 void
958 MediaPlayer::SetCanPause (bool value)
960 VERIFY_MAIN_THREAD;
961 SetBitTo (CanPause, value);
964 void
965 MediaPlayer::Pause ()
967 AudioSource *audio;
969 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i\n", state_unlocked);
970 VERIFY_MAIN_THREAD;
972 if (IsPaused ())
973 return;
975 SetState (Paused);
977 audio = GetAudio ();
978 if (audio) {
979 audio->Pause ();
980 audio->unref ();
983 SetTimeout (0);
985 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i [Done]\n", state_unlocked);
988 guint64
989 MediaPlayer::GetTargetPts ()
991 AudioSource *audio;
992 guint64 result;
994 VERIFY_MAIN_THREAD;
996 audio = GetAudio ();
998 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);
1000 if (audio != NULL && audio->GetState () == AudioPlaying)
1001 result = audio->GetCurrentPts ();
1002 else
1003 result = target_pts;
1005 if (audio)
1006 audio->unref ();
1008 return result;
1011 void
1012 MediaPlayer::SeekCompletedHandler (Media *media, EventArgs *args)
1014 LOG_MEDIAPLAYER ("MediaPlayer::SeekCompletedHandler ()\n");
1015 VERIFY_MAIN_THREAD;
1017 RemoveBit (Seeking);
1018 if (HasVideo ()) {
1019 SetBit (LoadFramePending);
1020 LoadVideoFrame ();
1024 void
1025 MediaPlayer::NotifySeek (guint64 pts)
1027 LOG_MEDIAPLAYER ("MediaPlayer::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms), media: %p, state: %i, current_pts: %llu, IsPlaying (): %i\n", pts, MilliSeconds_FromPts (pts), media, state_unlocked, current_pts, IsPlaying ());
1028 VERIFY_MAIN_THREAD;
1030 guint64 duration = GetDuration ();
1032 g_return_if_fail (GetCanSeek ());
1034 if (pts > start_pts + duration)
1035 pts = start_pts + duration;
1037 if (pts < start_pts)
1038 pts = start_pts;
1040 StopAudio ();
1041 SetTimeout (0);
1043 SetBit (Seeking);
1044 SetBit (Seeking);
1045 SetBit (LoadFramePending);
1046 RemoveBit (SeekSynched);
1047 RemoveBit (AudioEnded);
1048 RemoveBit (VideoEnded);
1050 start_time = 0;
1051 current_pts = pts;
1052 target_pts = pts;
1054 LOG_MEDIAPLAYER ("MediaPlayer::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms), media: %p, state: %i, current_pts: %llu [END]\n", pts, MilliSeconds_FromPts (pts), media, state_unlocked, current_pts);
1057 bool
1058 MediaPlayer::GetCanSeek ()
1060 VERIFY_MAIN_THREAD;
1061 return GetBit (CanSeek);
1064 void
1065 MediaPlayer::SetCanSeek (bool value)
1067 VERIFY_MAIN_THREAD;
1068 SetBitTo (CanSeek, value);
1071 void
1072 MediaPlayer::Stop ()
1074 LOG_MEDIAPLAYER ("MediaPlayer::Stop (), state: %i\n", state_unlocked);
1075 VERIFY_MAIN_THREAD;
1077 StopAudio ();
1079 SetTimeout (0);
1081 start_time = 0;
1082 current_pts = 0;
1083 target_pts = 0;
1084 SetState (Stopped);
1085 RemoveBit (AudioEnded);
1086 RemoveBit (VideoEnded);
1089 void
1090 MediaPlayer::StopAudio ()
1092 AudioSource *audio;
1094 LOG_MEDIAPLAYER ("MediaPlayer::StopAudio (), state: %i\n", state_unlocked);
1095 VERIFY_MAIN_THREAD;
1097 audio = GetAudio (); // This returns a reffed AudioSource
1098 if (audio) {
1099 audio->Stop ();
1100 audio->unref ();
1104 double
1105 MediaPlayer::GetBalance ()
1107 double result;
1108 AudioSource *audio;
1110 VERIFY_MAIN_THREAD;
1112 audio = GetAudio ();
1113 if (audio) {
1114 result = audio->GetBalance ();
1115 audio->unref ();
1116 } else {
1117 fprintf (stderr, "MediaPlayer::GetBalance (): There's no audio source to get the balance from\n");
1118 result = 0.0;
1121 return result;
1124 void
1125 MediaPlayer::SetBalance (double balance)
1127 AudioSource *audio;
1129 LOG_MEDIAPLAYER ("MediaPlayer::SetBalance (%f)\n", balance);
1130 VERIFY_MAIN_THREAD;
1132 if (balance < -1.0)
1133 balance = -1.0;
1134 else if (balance > 1.0)
1135 balance = 1.0;
1137 audio = GetAudio ();
1138 if (audio) {
1139 audio->SetBalance (balance);
1140 audio->unref ();
1141 } else {
1142 //fprintf (stderr, "MediaPlayer::SetBalance (%f): There's no audio source to set the balance\n", balance);
1146 double
1147 MediaPlayer::GetVolume ()
1149 AudioSource *audio;
1150 double result;
1152 VERIFY_MAIN_THREAD;
1154 audio = GetAudio ();
1155 if (audio) {
1156 result = audio->GetVolume ();
1157 audio->unref ();
1158 } else {
1159 fprintf (stderr, "MediaPlayer::GetVolume (): There's no audio source to get the volume from\n");
1160 result = 0.0;
1163 return result;
1166 void
1167 MediaPlayer::SetVolume (double volume)
1169 AudioSource *audio;
1171 LOG_MEDIAPLAYER ("MediaPlayer::SetVolume (%f)\n", volume);
1172 VERIFY_MAIN_THREAD;
1174 if (volume < -1.0)
1175 volume = -1.0;
1176 else if (volume > 1.0)
1177 volume = 1.0;
1179 audio = GetAudio ();
1180 if (audio) {
1181 audio->SetVolume (volume);
1182 audio->unref ();
1183 } else {
1184 //fprintf (stderr, "MediaPlayer::SetVolume (%f): There's no audio source to set the volume\n", volume);
1188 bool
1189 MediaPlayer::GetMuted ()
1191 bool result;
1192 AudioSource *audio;
1194 VERIFY_MAIN_THREAD;
1196 audio = GetAudio ();
1197 if (audio) {
1198 result = audio->GetMuted ();
1199 audio->unref ();
1200 } else {
1201 fprintf (stderr, "MediaPlayer::GetMuted (): There's no audio.\n");
1202 result = false;
1205 return result;
1208 void
1209 MediaPlayer::SetMuted (bool muted)
1211 AudioSource *audio;
1213 LOG_MEDIAPLAYER ("MediaPlayer::SetMuted (%i)\n", muted);
1214 VERIFY_MAIN_THREAD;
1216 audio = GetAudio ();
1217 if (audio) {
1218 audio->SetMuted (true);
1219 audio->unref ();
1220 } else {
1221 //fprintf (stderr, "MediaPlayer::SetMuted (%i): There's no audio to mute.\n", muted);
1225 void
1226 MediaPlayer::SetBit (PlayerState s)
1228 mutex.Lock ();
1229 state_unlocked = (PlayerState) (s | state_unlocked);
1230 mutex.Unlock ();
1233 void
1234 MediaPlayer::RemoveBit (PlayerState s)
1236 mutex.Lock ();
1237 state_unlocked = (PlayerState) (~s & state_unlocked);
1238 mutex.Unlock ();
1241 void
1242 MediaPlayer::SetBitTo (PlayerState s, bool value)
1244 if (value) {
1245 SetBit (s);
1246 } else {
1247 RemoveBit (s);
1251 bool
1252 MediaPlayer::GetBit (PlayerState s)
1254 bool result;
1255 mutex.Lock ();
1256 result = (state_unlocked & s) == s;
1257 mutex.Unlock ();
1258 return result;
1261 void
1262 MediaPlayer::SetState (PlayerState s)
1264 mutex.Lock ();
1265 state_unlocked = (PlayerState) ((state_unlocked & ~StateMask) | s);
1266 mutex.Unlock ();
1269 MediaPlayer::PlayerState
1270 MediaPlayer::GetState ()
1272 PlayerState result;
1273 mutex.Lock ();
1274 result = state_unlocked;
1275 mutex.Unlock ();
1276 return result;
1279 bool
1280 MediaPlayer::IsBufferUnderflow ()
1282 return GetBit (BufferUnderflow);
1285 bool
1286 MediaPlayer::IsLoadFramePending ()
1288 return GetBit (LoadFramePending);
1291 bool
1292 MediaPlayer::IsSeeking ()
1294 return GetBit (Seeking);
1297 bool
1298 MediaPlayer::HasRenderedFrame ()
1300 return GetBit (RenderedFrame);
1303 bool
1304 MediaPlayer::IsPlaying ()
1306 return (GetState () & StateMask) == Playing;
1309 bool
1310 MediaPlayer::IsPaused ()
1312 return (GetState () & StateMask) == Paused;
1315 bool
1316 MediaPlayer::IsStopped ()
1318 return (GetState () & StateMask) == Stopped;
1321 void
1322 MediaPlayer::SetBufferUnderflow ()
1324 SetBitTo (BufferUnderflow, true);
1325 EmitBufferUnderflow ();
1328 void
1329 MediaPlayer::EmitBufferUnderflow ()
1331 if (Surface::InMainThread ()) {
1332 Emit (BufferUnderflowEvent);
1333 } else {
1334 AddTickCallSafe (EmitBufferUnderflowAsync);
1338 void
1339 MediaPlayer::EmitBufferUnderflowAsync (EventObject *obj)
1341 ((MediaPlayer *) obj)->EmitBufferUnderflow ();