2009-12-01 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / mediaplayer.cpp
blobacead77211e431fbf4e3a99b74741a48654a81dd
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"
30 * MediaPlayer
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));
38 VERIFY_MAIN_THREAD;
40 element = el;
42 video_stream = NULL;
43 surface = NULL;
44 rgb_buffer = NULL;
45 buffer_width = 0;
46 buffer_height = 0;
47 format = MoonPixelFormatRGB32;
48 advance_frame_timeout_id = 0;
49 seeks = 0;
51 media = NULL;
52 audio_unlocked = NULL;
54 Initialize ();
57 MediaPlayer::~MediaPlayer ()
59 LOG_MEDIAPLAYER ("MediaPlayer::~MediaPlayer (), id=%i\n", GET_OBJ_ID (this));
60 VERIFY_MAIN_THREAD;
63 void
64 MediaPlayer::Dispose ()
66 LOG_MEDIAPLAYER ("MediaPlayer::Dispose (), id=%i\n", GET_OBJ_ID (this));
68 VERIFY_MAIN_THREAD;
70 Close ();
72 element = NULL;
74 EventObject::Dispose ();
77 AudioSource *
78 MediaPlayer::GetAudio ()
80 AudioSource *result = NULL;
82 // Thread-safe
84 mutex.Lock ();
85 if (audio_unlocked != NULL) {
86 result = audio_unlocked;
87 result->ref ();
89 mutex.Unlock ();
91 return result;
94 void
95 MediaPlayer::AudioFinishedCallback (EventObject *user_data)
97 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinishedCallback ()\n");
98 VERIFY_MAIN_THREAD;
100 MediaPlayer *mplayer = (MediaPlayer *) user_data;
101 mplayer->AudioFinished ();
104 void
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);
113 return;
116 VERIFY_MAIN_THREAD;
117 if (!GetBit (AudioEnded)) {
118 SetBit (AudioEnded);
119 CheckFinished ();
123 void
124 MediaPlayer::VideoFinished ()
126 LOG_MEDIAPLAYER ("MediaPlayer::VideoFinished () VideoEnded: %i, AudioEnded: %i\n", GetBit (VideoEnded), GetBit (AudioEnded));
127 VERIFY_MAIN_THREAD;
129 if (!GetBit (VideoEnded)) {
130 SetBit (VideoEnded);
131 CheckFinished ();
135 void
136 MediaPlayer::CheckFinished ()
138 LOG_MEDIAPLAYER ("MediaPlayer::CheckFinished (), HasVideo: %i, VideoEnded: %i, HasAudio: %i, AudioEnded: %i\n",
139 HasVideo (), GetBit (VideoEnded), HasAudio (), GetBit (AudioEnded));
140 VERIFY_MAIN_THREAD;
142 if (HasVideo () && !GetBit (VideoEnded))
143 return;
145 if (HasAudio () && !GetBit (AudioEnded))
146 return;
148 Emit (MediaEndedEvent);
151 void
152 MediaPlayer::AudioFailed (AudioSource *source)
154 // This method must be thread-safe
156 mutex.Lock ();
157 if (this->audio_unlocked == source) {
158 AudioPlayer::Remove (this->audio_unlocked);
159 this->audio_unlocked->unref ();
160 this->audio_unlocked = NULL;
162 mutex.Unlock ();
165 bool
166 MediaPlayer::Open (Media *media, PlaylistEntry *entry)
168 IMediaDecoder *encoding;
169 IMediaStream *stream;
170 guint64 asx_duration;
171 gint32 *audio_stream_index = NULL;
172 AudioSource *audio;
174 LOG_MEDIAPLAYER ("MediaPlayer::Open (%p), current media: %p\n", media, this->media);
175 VERIFY_MAIN_THREAD;
177 Close ();
179 if (media == NULL) {
180 printf ("MediaPlayer::Open (): media is NULL.\n");
181 return false;
184 if (!media->IsOpened ()) {
185 printf ("MediaPlayer::Open (): media isn't opened.\n");
186 return false;
189 this->media = media;
190 this->media->ref ();
192 SetState (Opened);
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");
201 return false;
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 ()) {
214 case MediaTypeAudio:
215 audio_stream_count++;
216 if (audio_stream_index != NULL){
217 if (*audio_stream_index == audio_stream_count - 1) {
218 astream = (AudioStream *) stream;
220 } else {
221 astream2 = (AudioStream *) stream;
223 if (astream == NULL || astream->GetBitRate () < astream2->GetBitRate ())
224 astream = astream2;
227 break;
228 case MediaTypeVideo:
229 vstream = (VideoStream *) stream;
231 if (video_stream != NULL && vstream->GetBitRate () < video_stream->GetBitRate ())
232 break;
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);
242 break;
243 case MediaTypeMarker:
244 LOG_MEDIAPLAYER ("MediaPlayer::Open (): Found a marker stream, selecting it.\n");
245 stream->SetSelected (true);
246 default:
247 break;
251 if (astream != NULL) {
252 audio = AudioPlayer::Add (this, astream);
253 if (audio != NULL) {
254 // Only select the audio stream if we can actually play it
255 astream->SetSelected (true);
256 audio->ref ();
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"
263 "\tcodec_id: 0x%x\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) {
273 int n;
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");
279 mutex.Lock ();
280 this->audio_unlocked = audio;
281 mutex.Unlock ();
284 if (video_stream != NULL) {
285 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected Video stream (%d) properties:\n"
286 "\twidth: %d\n"
287 "\theight: %d\n"
288 "\tbits_per_sample: %d\n"
289 "\tbit_rate: %d\n"
290 "\tcodec_id: 0x%x\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) {
300 int n;
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");
308 current_pts = 0;
309 target_pts = 0;
310 start_pts = 0;
312 if (entry != NULL) {
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 ())
320 SetBit (IsLive);
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 ());
338 if (HasVideo ()) {
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
342 LoadVideoFrame ();
345 demuxer->unref ();
347 return true;
350 void
351 MediaPlayer::SetVideoBufferSize (gint32 width, gint32 height)
353 gint32 stride;
355 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (%i, %i). buffer_width: %i, buffer_height: %i\n", width, height, buffer_width, buffer_height);
356 VERIFY_MAIN_THREAD;
358 if (surface) {
359 cairo_surface_destroy (surface);
360 surface = NULL;
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));
366 if (stride % 64) {
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");
373 free (rgb_buffer);
374 // for conversion to rgb32 format needed for rendering with 16 byte alignment
375 if (posix_memalign ((void **)(&rgb_buffer), 16, height * stride)) {
376 rgb_buffer = NULL;
377 g_warning ("Could not allocate memory for video RGB buffer");
378 return;
380 memset (rgb_buffer, 0, height * stride);
382 buffer_width = width;
383 buffer_height = height;
386 // rendering surface
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);
392 void
393 MediaPlayer::Initialize ()
395 LOG_MEDIAPLAYER ("MediaPlayer::Initialize ()\n");
396 VERIFY_MAIN_THREAD;
398 // Clear out any state, bits, etc
399 state_unlocked = (PlayerState) 0;
400 // Set initial states and bits
401 SetState (Stopped);
402 SetBit (SeekSynched);
403 SetBit (CanSeek);
404 SetBit (CanPause);
406 seeks = 0;
407 start_time = 0;
408 duration = 0;
409 start_pts = 0;
410 current_pts = 0;
411 target_pts = 0;
412 first_live_pts = G_MAXUINT64;
414 audio_stream_count = 0;
415 height = 0;
416 width = 0;
418 frames_update_timestamp = 0;
419 rendered_frames = 0;
420 dropped_frames = 0;
421 rendered_frames_per_second = 0.0;
422 dropped_frames_per_second = 0.0;
425 void
426 MediaPlayer::Close ()
428 LOG_MEDIAPLAYER ("MediaPlayer::Close ()\n");
429 VERIFY_MAIN_THREAD;
431 mutex.Lock ();
432 if (audio_unlocked) {
433 AudioPlayer::Remove (audio_unlocked);
434 audio_unlocked->Dispose ();
435 audio_unlocked->unref ();
436 audio_unlocked = NULL;
438 mutex.Unlock ();
440 Stop ();
442 // Reset state back to what it was at instantiation
444 if (rgb_buffer != NULL) {
445 free (rgb_buffer);
446 rgb_buffer = NULL;
448 buffer_width = 0;
449 buffer_height = 0;
451 if (surface != NULL) {
452 cairo_surface_destroy (surface);
453 surface = NULL;
456 if (video_stream) {
457 video_stream->RemoveSafeHandlers (this);
458 video_stream->unref ();
459 video_stream = NULL;
462 if (media) {
463 media->unref ();
464 media = NULL;
467 Initialize ();
471 // Puts the data into our rgb buffer.
472 // If necessary converts the data from its source format to rgb first.
475 void
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 ());
481 VERIFY_MAIN_THREAD;
483 if (!frame->IsDecoded ()) {
484 fprintf (stderr, "MediaPlayer::RenderFrame (): Trying to render a frame which hasn't been decoded yet.\n");
485 return;
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 ();
509 return;
512 if (frame->data_stride == NULL ||
513 frame->data_stride[1] == NULL ||
514 frame->data_stride[2] == NULL) {
515 return;
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 ();
528 #define LOG_RS(x) \
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);
537 void
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);
546 bool update = false;
547 double dropped_frames_per_second = -1;
548 double rendered_frames_per_second = -1;
549 AudioSource *audio;
551 guint64 now = 0;
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 ());
555 VERIFY_MAIN_THREAD;
557 RemoveBit (LoadFramePending);
559 if (IsPaused ())
560 return;
562 if (IsSeeking ())
563 return;
565 if (GetBit (VideoEnded))
566 return;
568 if (!HasVideo ())
569 return;
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.
574 audio = GetAudio ();
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");
581 audio->unref ();
582 return;
584 } else {
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));
596 if (audio != NULL) {
597 audio->unref ();
598 audio = NULL;
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));
609 return;
612 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): target pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n", target_pts, MilliSeconds_FromPts (target_pts));
614 while (true) {
615 frame = video_stream->PopFrame ();
616 if (frame == NULL) {
617 if (video_stream->GetOutputEnded ()) {
618 if (!HasAudio ()) {
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;
622 VideoFinished ();
623 return;
625 if (!HasAudio ())
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).
630 break;
633 stream = frame->stream;
634 current_pts = frame->pts;
635 update = true;
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.
653 StopAudio ();
654 AudioFinished ();
655 VideoFinished ();
657 } else {
658 if (current_pts > duration) {
659 StopAudio ();
660 AudioFinished ();
661 VideoFinished ();
664 if (GetBit (VideoEnded)) {
665 //printf ("MediaPlayer::AdvanceFrame (): Reached end of duration.\n");
666 update = false;
667 break;
671 if (!frame->IsDecoded ()) {
672 printf ("MediaPlayer::AdvanceFrame (): Got a non-decoded frame.\n");
673 update = false;
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
682 break;
685 if (video_stream->IsQueueEmpty ()) {
686 // no more packets in queue, this frame is the most recent we have available
687 break;
690 // we are lagging behind, drop this frame
691 dropped_frames++;
693 //LOG_RS ("[SKIPPED]");
694 media->DisposeObject (frame);
695 frame->unref ();
696 frame = NULL;
699 if (update && frame && GetBit (SeekSynched)) {
700 rendered_frames++;
701 //LOG_RS ("[RENDER]");
703 RenderFrame (frame);
706 if (frame) {
707 media->DisposeObject (frame);
708 frame->unref ();
709 frame = NULL;
712 now = get_now ();
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;
726 return;
729 void
730 MediaPlayer::FirstFrameEnqueuedHandler (EventObject *sender, EventArgs *args)
732 LoadVideoFrame ();
735 void
736 MediaPlayer::LoadVideoFrameCallback (EventObject *object)
738 ((MediaPlayer *) object)->LoadVideoFrame ();
741 void
742 MediaPlayer::LoadVideoFrame ()
744 guint64 target_pts;
745 MediaFrame *frame;
747 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), HasVideo: %i, LoadFramePending: %i\n", HasVideo (), state_unlocked & LoadFramePending);
748 VERIFY_MAIN_THREAD;
750 if (!HasVideo ())
751 return;
753 if (!IsLoadFramePending ())
754 return;
756 frame = video_stream->PopFrame ();
758 if (frame == NULL)
759 return;
761 target_pts = GetTargetPts ();
763 if (target_pts == G_MAXUINT64)
764 target_pts = 0;
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);
771 RenderFrame (frame);
772 element->MediaInvalidate ();
773 } else {
774 AddTickCall (LoadVideoFrameCallback);
777 media->DisposeObject (frame);
778 frame->unref ();
780 return;
783 gboolean
784 MediaPlayer::AdvanceFrameCallback (void *user_data)
786 MediaPlayer *mplayer = (MediaPlayer *) user_data;
787 mplayer->SetCurrentDeployment ();
788 mplayer->AdvanceFrame ();
789 #if SANITY
790 Deployment::SetCurrent (NULL);
791 #endif
792 return true;
795 void
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);
806 } else {
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;
813 if (timeout != 0) {
814 if (time_manager == NULL) {
815 g_warning ("MediaPlayer::SetTimeout (): Could not set timeout (no time manager).\n");
816 } else {
817 advance_frame_timeout_id = time_manager->AddTimeout (MOON_PRIORITY_DEFAULT, timeout, AdvanceFrameCallback, this);
822 void
823 MediaPlayer::Play ()
825 AudioSource *audio;
827 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i, IsPlaying: %i, IsSeeking: %i\n", state_unlocked, IsPlaying (), IsSeeking ());
828 VERIFY_MAIN_THREAD;
830 if (IsSeeking ())
831 return;
833 SetState (Playing);
834 RemoveBit (BufferUnderflow);
835 start_time = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
836 start_time -= target_pts;
838 audio = GetAudio ();
839 if (audio) {
840 audio->Play ();
841 audio->unref ();
844 SetTimeout (GetTimeoutInterval ());
846 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i [Done]\n", state_unlocked);
849 gint32
850 MediaPlayer::GetTimeoutInterval ()
852 gint32 result; // ms between timeouts
853 guint64 pts_per_frame = 0;
855 VERIFY_MAIN_THREAD;
857 if (HasVideo ()) {
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);
863 } else {
864 result = (gint32) MilliSeconds_FromPts (pts_per_frame);
866 } else {
867 result = 33;
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);
872 return result;
875 void
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;
883 AudioSource *audio;
885 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i).\n", index);
886 VERIFY_MAIN_THREAD;
888 if (index < 0 || index >= audio_stream_count) {
889 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Invalid audio stream index.\n", index);
890 return;
893 if (media == NULL) {
894 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No media.\n", index);
895 return;
898 audio = GetAudio ();
899 if (audio == NULL) {
900 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No audio source.\n", index);
901 return;
904 demuxer = media->GetDemuxerReffed ();
906 if (demuxer == NULL) {
907 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Media doesn't have a demuxer.\n", index);
908 return;
911 prev_stream = audio->GetAudioStream ();
913 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
914 stream = demuxer->GetStream (i);
916 if (stream->GetType () != MediaTypeAudio)
917 continue;
919 if (audio_streams_found == index) {
920 next_stream = (AudioStream *) stream;
921 break;
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);
934 audio->unref ();
935 demuxer->unref ();
938 bool
939 MediaPlayer::GetCanPause ()
941 // FIXME: should return false if it is streaming media
942 VERIFY_MAIN_THREAD;
943 return GetBit (CanPause);
946 void
947 MediaPlayer::SetCanPause (bool value)
949 VERIFY_MAIN_THREAD;
950 SetBitTo (CanPause, value);
953 void
954 MediaPlayer::Pause ()
956 AudioSource *audio;
958 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i\n", state_unlocked);
959 VERIFY_MAIN_THREAD;
961 if (IsPaused ())
962 return;
964 SetState (Paused);
966 audio = GetAudio ();
967 if (audio) {
968 audio->Pause ();
969 audio->unref ();
972 SetTimeout (0);
974 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i [Done]\n", state_unlocked);
977 guint64
978 MediaPlayer::GetTargetPts ()
980 AudioSource *audio;
981 guint64 result;
983 VERIFY_MAIN_THREAD;
985 audio = GetAudio ();
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 ();
991 else
992 result = target_pts;
994 if (audio)
995 audio->unref ();
997 return result;
1000 void
1001 MediaPlayer::SeekCompletedHandler (Media *media, EventArgs *args)
1003 LOG_MEDIAPLAYER ("MediaPlayer::SeekCompletedHandler () seeks: %i\n", seeks);
1004 VERIFY_MAIN_THREAD;
1006 seeks--;
1007 if (seeks != 0)
1008 return;
1010 if (HasVideo ()) {
1011 SetBit (LoadFramePending);
1012 LoadVideoFrame ();
1016 void
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);
1020 VERIFY_MAIN_THREAD;
1022 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)
1031 pts = start_pts;
1033 StopAudio ();
1034 SetTimeout (0);
1036 SetBit (LoadFramePending);
1037 RemoveBit (SeekSynched);
1038 RemoveBit (AudioEnded);
1039 RemoveBit (VideoEnded);
1041 start_time = 0;
1042 current_pts = pts;
1043 target_pts = pts;
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);
1048 bool
1049 MediaPlayer::GetCanSeek ()
1051 VERIFY_MAIN_THREAD;
1052 return GetBit (CanSeek);
1055 void
1056 MediaPlayer::SetCanSeek (bool value)
1058 VERIFY_MAIN_THREAD;
1059 SetBitTo (CanSeek, value);
1062 void
1063 MediaPlayer::Stop ()
1065 LOG_MEDIAPLAYER ("MediaPlayer::Stop (), state: %i\n", state_unlocked);
1066 VERIFY_MAIN_THREAD;
1068 StopAudio ();
1070 SetTimeout (0);
1072 start_time = 0;
1073 current_pts = 0;
1074 target_pts = 0;
1075 SetState (Stopped);
1076 RemoveBit (AudioEnded);
1077 RemoveBit (VideoEnded);
1080 void
1081 MediaPlayer::StopAudio ()
1083 AudioSource *audio;
1085 LOG_MEDIAPLAYER ("MediaPlayer::StopAudio (), state: %i\n", state_unlocked);
1086 VERIFY_MAIN_THREAD;
1088 audio = GetAudio (); // This returns a reffed AudioSource
1089 if (audio) {
1090 audio->Stop ();
1091 audio->unref ();
1095 double
1096 MediaPlayer::GetBalance ()
1098 double result;
1099 AudioSource *audio;
1101 VERIFY_MAIN_THREAD;
1103 audio = GetAudio ();
1104 if (audio) {
1105 result = audio->GetBalance ();
1106 audio->unref ();
1107 } else {
1108 fprintf (stderr, "MediaPlayer::GetBalance (): There's no audio source to get the balance from\n");
1109 result = 0.0;
1112 return result;
1115 void
1116 MediaPlayer::SetBalance (double balance)
1118 AudioSource *audio;
1120 LOG_MEDIAPLAYER ("MediaPlayer::SetBalance (%f)\n", balance);
1121 VERIFY_MAIN_THREAD;
1123 if (balance < -1.0)
1124 balance = -1.0;
1125 else if (balance > 1.0)
1126 balance = 1.0;
1128 audio = GetAudio ();
1129 if (audio) {
1130 audio->SetBalance (balance);
1131 audio->unref ();
1132 } else {
1133 //fprintf (stderr, "MediaPlayer::SetBalance (%f): There's no audio source to set the balance\n", balance);
1137 double
1138 MediaPlayer::GetVolume ()
1140 AudioSource *audio;
1141 double result;
1143 VERIFY_MAIN_THREAD;
1145 audio = GetAudio ();
1146 if (audio) {
1147 result = audio->GetVolume ();
1148 audio->unref ();
1149 } else {
1150 fprintf (stderr, "MediaPlayer::GetVolume (): There's no audio source to get the volume from\n");
1151 result = 0.0;
1154 return result;
1157 void
1158 MediaPlayer::SetVolume (double volume)
1160 AudioSource *audio;
1162 LOG_MEDIAPLAYER ("MediaPlayer::SetVolume (%f)\n", volume);
1163 VERIFY_MAIN_THREAD;
1165 if (volume < -1.0)
1166 volume = -1.0;
1167 else if (volume > 1.0)
1168 volume = 1.0;
1170 audio = GetAudio ();
1171 if (audio) {
1172 audio->SetVolume (volume);
1173 audio->unref ();
1174 } else {
1175 //fprintf (stderr, "MediaPlayer::SetVolume (%f): There's no audio source to set the volume\n", volume);
1179 bool
1180 MediaPlayer::GetMuted ()
1182 bool result;
1183 AudioSource *audio;
1185 VERIFY_MAIN_THREAD;
1187 audio = GetAudio ();
1188 if (audio) {
1189 result = audio->GetMuted ();
1190 audio->unref ();
1191 } else {
1192 fprintf (stderr, "MediaPlayer::GetMuted (): There's no audio.\n");
1193 result = false;
1196 return result;
1199 void
1200 MediaPlayer::SetMuted (bool muted)
1202 AudioSource *audio;
1204 LOG_MEDIAPLAYER ("MediaPlayer::SetMuted (%i)\n", muted);
1205 VERIFY_MAIN_THREAD;
1207 audio = GetAudio ();
1208 if (audio) {
1209 audio->SetMuted (true);
1210 audio->unref ();
1211 } else {
1212 //fprintf (stderr, "MediaPlayer::SetMuted (%i): There's no audio to mute.\n", muted);
1216 void
1217 MediaPlayer::SetBit (PlayerState s)
1219 mutex.Lock ();
1220 state_unlocked = (PlayerState) (s | state_unlocked);
1221 mutex.Unlock ();
1224 void
1225 MediaPlayer::RemoveBit (PlayerState s)
1227 mutex.Lock ();
1228 state_unlocked = (PlayerState) (~s & state_unlocked);
1229 mutex.Unlock ();
1232 void
1233 MediaPlayer::SetBitTo (PlayerState s, bool value)
1235 if (value) {
1236 SetBit (s);
1237 } else {
1238 RemoveBit (s);
1242 bool
1243 MediaPlayer::GetBit (PlayerState s)
1245 bool result;
1246 mutex.Lock ();
1247 result = (state_unlocked & s) == s;
1248 mutex.Unlock ();
1249 return result;
1252 void
1253 MediaPlayer::SetState (PlayerState s)
1255 mutex.Lock ();
1256 state_unlocked = (PlayerState) ((state_unlocked & ~StateMask) | s);
1257 mutex.Unlock ();
1260 MediaPlayer::PlayerState
1261 MediaPlayer::GetState ()
1263 PlayerState result;
1264 mutex.Lock ();
1265 result = state_unlocked;
1266 mutex.Unlock ();
1267 return result;
1270 bool
1271 MediaPlayer::IsBufferUnderflow ()
1273 return GetBit (BufferUnderflow);
1276 bool
1277 MediaPlayer::IsLoadFramePending ()
1279 return GetBit (LoadFramePending);
1282 bool
1283 MediaPlayer::IsSeeking ()
1285 return seeks > 0;
1288 bool
1289 MediaPlayer::HasRenderedFrame ()
1291 return GetBit (RenderedFrame);
1294 bool
1295 MediaPlayer::IsPlaying ()
1297 return (GetState () & StateMask) == Playing;
1300 bool
1301 MediaPlayer::IsPaused ()
1303 return (GetState () & StateMask) == Paused;
1306 bool
1307 MediaPlayer::IsStopped ()
1309 return (GetState () & StateMask) == Stopped;
1312 void
1313 MediaPlayer::SetBufferUnderflow ()
1315 SetBitTo (BufferUnderflow, true);
1316 EmitBufferUnderflow ();
1319 void
1320 MediaPlayer::EmitBufferUnderflow ()
1322 if (Surface::InMainThread ()) {
1323 Emit (BufferUnderflowEvent);
1324 } else {
1325 AddTickCall (EmitBufferUnderflowAsync);
1329 void
1330 MediaPlayer::EmitBufferUnderflowAsync (EventObject *obj)
1332 ((MediaPlayer *) obj)->EmitBufferUnderflow ();