2009-08-26 Chris Toshok <toshok@ximian.com>
[moon.git] / src / mediaplayer.cpp
blobcaf878a6e3994293994b557a366b32a0b29a4d42
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 if (start_pts > 0) {
328 printf ("TODO: Seek to start pts\n");
329 // SeekInternal (start_pts);
332 if (entry->GetIsLive ())
333 SetBit (IsLive);
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 ());
351 if (HasVideo ()) {
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
355 LoadVideoFrame ();
358 return true;
361 void
362 MediaPlayer::SetVideoBufferSize (gint32 width, gint32 height)
364 gint32 stride;
366 LOG_MEDIAPLAYER ("MediaPlayer::SetVideoBufferSize (%i, %i). buffer_width: %i, buffer_height: %i\n", width, height, buffer_width, buffer_height);
367 VERIFY_MAIN_THREAD;
369 if (surface) {
370 cairo_surface_destroy (surface);
371 surface = NULL;
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));
377 if (stride % 64) {
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");
384 free (rgb_buffer);
385 // for conversion to rgb32 format needed for rendering with 16 byte alignment
386 if (posix_memalign ((void **)(&rgb_buffer), 16, height * stride)) {
387 rgb_buffer = NULL;
388 g_warning ("Could not allocate memory for video RGB buffer");
389 return;
391 memset (rgb_buffer, 0, height * stride);
393 buffer_width = width;
394 buffer_height = height;
397 // rendering surface
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);
403 void
404 MediaPlayer::Initialize ()
406 LOG_MEDIAPLAYER ("MediaPlayer::Initialize ()\n");
407 VERIFY_MAIN_THREAD;
409 // Clear out any state, bits, etc
410 state_unlocked = (PlayerState) 0;
411 // Set initial states and bits
412 SetState (Stopped);
413 SetBit (SeekSynched);
414 SetBit (CanSeek);
415 SetBit (CanPause);
417 start_time = 0;
418 start_pts = 0;
419 current_pts = 0;
420 target_pts = 0;
421 first_live_pts = G_MAXUINT64;
423 audio_stream_count = 0;
424 height = 0;
425 width = 0;
427 frames_update_timestamp = 0;
428 rendered_frames = 0;
429 dropped_frames = 0;
430 rendered_frames_per_second = 0.0;
431 dropped_frames_per_second = 0.0;
434 void
435 MediaPlayer::Close ()
437 LOG_MEDIAPLAYER ("MediaPlayer::Close ()\n");
438 VERIFY_MAIN_THREAD;
440 mutex.Lock ();
441 if (audio_unlocked) {
442 AudioPlayer::Remove (audio_unlocked);
443 audio_unlocked->Dispose ();
444 audio_unlocked->unref ();
445 audio_unlocked = NULL;
447 mutex.Unlock ();
449 Stop ();
451 // Reset state back to what it was at instantiation
453 if (rgb_buffer != NULL) {
454 free (rgb_buffer);
455 rgb_buffer = NULL;
457 buffer_width = 0;
458 buffer_height = 0;
460 if (surface != NULL) {
461 cairo_surface_destroy (surface);
462 surface = NULL;
465 if (video_stream) {
466 video_stream->RemoveSafeHandlers (this);
467 video_stream->unref ();
468 video_stream = NULL;
471 if (media) {
472 media->unref ();
473 media = NULL;
476 Initialize ();
480 // Puts the data into our rgb buffer.
481 // If necessary converts the data from its source format to rgb first.
484 void
485 MediaPlayer::RenderFrame (MediaFrame *frame)
487 VideoStream *stream = (VideoStream *) frame->stream;
489 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 ());
490 VERIFY_MAIN_THREAD;
492 if (!frame->IsDecoded ()) {
493 fprintf (stderr, "MediaPlayer::RenderFrame (): Trying to render a frame which hasn't been decoded yet.\n");
494 return;
497 if ((frame->width > 0 && frame->width != width) || (frame->height > 0 && frame->height != height) || (format != stream->GetDecoder ()->GetPixelFormat ())) {
498 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",
499 frame->width, frame->height, video_stream->width, video_stream->height, width, height);
501 if (frame->width > 0)
502 width = frame->width;
503 if (frame->height > 0)
504 height = frame->height;
506 format = stream->GetDecoder ()->GetPixelFormat ();
508 SetVideoBufferSize (width, height);
511 if (!frame->IsPlanar ()) {
512 // Just copy the data
513 guint32 stride = cairo_image_surface_get_stride (surface);
514 for (int i = 0; i < height; i++)
515 memcpy (rgb_buffer + stride * i, frame->buffer + i * width * 4, width * 4);
516 SetBit (RenderedFrame);
517 element->MediaInvalidate ();
518 return;
521 if (frame->data_stride == NULL ||
522 frame->data_stride[1] == NULL ||
523 frame->data_stride[2] == NULL) {
524 return;
527 guint8 *rgb_dest [3] = { rgb_buffer, NULL, NULL };
528 int rgb_stride [3] = { cairo_image_surface_get_stride (surface), 0, 0 };
530 stream->converter->Convert (frame->data_stride, frame->srcStride, frame->srcSlideY,
531 frame->srcSlideH, rgb_dest, rgb_stride);
533 SetBit (RenderedFrame);
534 element->MediaInvalidate ();
537 #define LOG_RS(x) \
538 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, \
539 MilliSeconds_FromPts (frame->pts), \
540 MilliSeconds_FromPts (target_pts), \
541 (gint64) MilliSeconds_FromPts (frame->pts) - (gint64) MilliSeconds_FromPts (target_pts), \
542 rendered_frames_per_second, \
543 dropped_frames_per_second, \
544 dropped_frames_per_second + rendered_frames_per_second);
546 void
547 MediaPlayer::AdvanceFrame ()
549 MediaFrame *frame = NULL;
550 IMediaStream *stream;
551 guint64 target_pts = 0;
552 guint64 target_pts_start = 0;
553 guint64 target_pts_end = 0;
554 guint64 target_pts_delta = MilliSeconds_ToPts (100);
555 bool update = false;
556 double dropped_frames_per_second = -1;
557 double rendered_frames_per_second = -1;
558 AudioSource *audio;
560 guint64 now = 0;
562 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",
563 state_unlocked, current_pts, IsPaused (), IsSeeking (), GetBit (VideoEnded), GetBit (AudioEnded), HasVideo (), HasAudio ());
564 VERIFY_MAIN_THREAD;
566 RemoveBit (LoadFramePending);
568 if (IsPaused ())
569 return;
571 if (IsSeeking ())
572 return;
574 if (GetBit (VideoEnded))
575 return;
577 if (!HasVideo ())
578 return;
580 // If the audio isn't playing, there might be slight length-difference between
581 // audio and video streams (the audio is shorted and finished earlier for instance)
582 // Treat this case as if there's no audio at all.
583 audio = GetAudio ();
584 if (audio != NULL && audio->GetState () == AudioPlaying) {
585 // use target_pts as set by audio thread
586 target_pts = GetTargetPts ();
587 if (target_pts == G_MAXUINT64) {
588 // This might happen if we've called Play on the audio source, but it hasn't actually played anything yet.
589 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): invalid target pts from the audio stream.\n");
590 audio->unref ();
591 return;
593 } else {
594 // no audio to sync to
595 guint64 now = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
596 guint64 elapsed_pts = now - start_time;
598 target_pts = elapsed_pts;
601 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",
602 target_pts, MilliSeconds_FromPts (target_pts), elapsed_pts, MilliSeconds_FromPts (elapsed_pts), start_time, MilliSeconds_FromPts (start_time));
605 if (audio != NULL) {
606 audio->unref ();
607 audio = NULL;
610 this->target_pts = target_pts;
612 target_pts_start = target_pts_delta > target_pts ? 0 : target_pts - target_pts_delta;
613 target_pts_end = target_pts + target_pts_delta;
615 if (current_pts >= target_pts_end && GetBit (SeekSynched) && !(HasAudio () && GetBit (AudioEnded))) {
616 #if DEBUG_ADVANCEFRAME
617 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",
618 MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (target_pts), MilliSeconds_FromPts (target_pts_delta), current_pts - target_pts, MilliSeconds_FromPts (current_pts - target_pts));
619 #endif
620 return;
623 #if DEBUG_ADVANCEFRAME
624 printf ("MediaPlayer::AdvanceFrame (): target pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n", target_pts, MilliSeconds_FromPts (target_pts));
625 #endif
627 while (true) {
628 frame = video_stream->PopFrame ();
629 if (frame == NULL) {
630 if (video_stream->GetOutputEnded ()) {
631 if (!HasAudio ()) {
632 // Set the target pts to the last pts we showed, since target_pts is what's reported as our current position.
633 this->target_pts = current_pts;
635 VideoFinished ();
636 return;
638 if (!HasAudio ())
639 SetBufferUnderflow ();
640 // If we have audio, we keep playing (and loosing frames) until the audio playing stops due to buffer underflow
641 // TODO: determine if we don't have video due to not having enough data (in which case we should start buffering),
642 // or if it is because the decoder can't keep up (in which case we should drop frames).
643 break;
646 stream = frame->stream;
647 current_pts = frame->pts;
648 update = true;
650 //printf ("MediaPlayer::AdvanceFrame (): current_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, duration: %llu = %llu ms\n",
651 // current_pts, MilliSeconds_FromPts (current_pts),
652 // duration, MilliSeconds_FromPts (duration));
654 if (GetBit (IsLive)) {
655 first_live_pts = MIN (current_pts, first_live_pts);
658 if (GetBit (FixedDuration)) {
660 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",
661 GetBit (IsLive), MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (duration), MilliSeconds_FromPts (first_live_pts), MilliSeconds_FromPts (current_pts - first_live_pts));
663 if (GetBit (IsLive)) {
664 if (current_pts - first_live_pts > duration) {
665 // TODO: Move this out of AdvanceFrame, here it only works for media which has video, not for audio-only media.
666 StopAudio ();
667 AudioFinished ();
668 VideoFinished ();
670 } else {
671 if (current_pts > duration) {
672 StopAudio ();
673 AudioFinished ();
674 VideoFinished ();
677 if (GetBit (VideoEnded)) {
678 //printf ("MediaPlayer::AdvanceFrame (): Reached end of duration.\n");
679 update = false;
680 break;
684 if (!frame->IsDecoded ()) {
685 printf ("MediaPlayer::AdvanceFrame (): Got a non-decoded frame.\n");
686 update = false;
689 if (update && current_pts >= target_pts_start) {
690 if (!GetBit (SeekSynched)) {
691 SetBit (SeekSynched);
692 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));
694 // we are in sync (or ahead) of audio playback
695 break;
698 if (video_stream->IsQueueEmpty ()) {
699 // no more packets in queue, this frame is the most recent we have available
700 break;
703 // we are lagging behind, drop this frame
704 dropped_frames++;
706 //LOG_RS ("[SKIPPED]");
707 media->DisposeObject (frame);
708 frame->unref ();
709 frame = NULL;
712 if (update && frame && GetBit (SeekSynched)) {
713 rendered_frames++;
714 //LOG_RS ("[RENDER]");
716 RenderFrame (frame);
719 if (frame) {
720 media->DisposeObject (frame);
721 frame->unref ();
722 frame = NULL;
725 now = get_now ();
726 if (frames_update_timestamp == 0) {
727 frames_update_timestamp = now;
728 } else if ((now - frames_update_timestamp) > TIMESPANTICKS_IN_SECOND) {
729 double time_elapsed = (double) (now - frames_update_timestamp) / (double) TIMESPANTICKS_IN_SECOND;
730 dropped_frames_per_second = (double) dropped_frames / time_elapsed;
731 rendered_frames_per_second = (double) rendered_frames / time_elapsed;
732 dropped_frames = rendered_frames = 0;
733 frames_update_timestamp = now;
735 this->dropped_frames_per_second = dropped_frames_per_second;
736 this->rendered_frames_per_second = rendered_frames_per_second;
739 return;
742 void
743 MediaPlayer::FirstFrameEnqueuedHandler (EventObject *sender, EventArgs *args)
745 LoadVideoFrame ();
748 void
749 MediaPlayer::LoadVideoFrameCallback (EventObject *object)
751 ((MediaPlayer *) object)->LoadVideoFrame ();
754 void
755 MediaPlayer::LoadVideoFrame ()
757 guint64 target_pts;
758 MediaFrame *frame;
760 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), HasVideo: %i, LoadFramePending: %i\n", HasVideo (), state_unlocked & LoadFramePending);
761 VERIFY_MAIN_THREAD;
763 if (!HasVideo ())
764 return;
766 if (!IsLoadFramePending ())
767 return;
769 frame = video_stream->PopFrame ();
771 if (frame == NULL)
772 return;
774 target_pts = GetTargetPts ();
776 if (target_pts == G_MAXUINT64)
777 target_pts = 0;
779 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);
781 if (frame->pts + video_stream->pts_per_frame >= target_pts) {
782 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (): rendering.\n");
783 RemoveBit (LoadFramePending);
784 RenderFrame (frame);
785 element->MediaInvalidate ();
786 } else {
787 AddTickCallSafe (LoadVideoFrameCallback);
790 media->DisposeObject (frame);
791 frame->unref ();
793 return;
796 gboolean
797 MediaPlayer::AdvanceFrameCallback (void *user_data)
799 MediaPlayer *mplayer = (MediaPlayer *) user_data;
800 mplayer->SetCurrentDeployment ();
801 mplayer->AdvanceFrame ();
802 #if SANITY
803 Deployment::SetCurrent (NULL);
804 #endif
805 return true;
808 void
809 MediaPlayer::SetTimeout (gint32 timeout /* set to 0 to clear */)
811 TimeManager *time_manager = element ? element->GetTimeManager () : NULL;
812 bool clear = timeout == 0 || advance_frame_timeout_id != 0;
814 LOG_MEDIAPLAYER ("MediaPlayer::SetTimeout (%i) time_manager: %p id: %i\n", timeout, time_manager, GET_OBJ_ID (time_manager));
816 if (clear && advance_frame_timeout_id != 0) {
817 if (time_manager != NULL) {
818 time_manager->RemoveTimeout (advance_frame_timeout_id);
819 } else {
820 g_warning ("MediaPlayer::SetTimeout (): Could not clear timeout. Leaking ourselves to not crash.\n");
821 ref (); // This will prevent us from getting destroyed.
823 advance_frame_timeout_id = 0;
826 if (timeout != 0) {
827 if (time_manager == NULL) {
828 g_warning ("MediaPlayer::SetTimeout (): Could not set timeout (no time manager).\n");
829 } else {
830 advance_frame_timeout_id = time_manager->AddTimeout (MOON_PRIORITY_DEFAULT, timeout, AdvanceFrameCallback, this);
835 void
836 MediaPlayer::Play ()
838 AudioSource *audio;
840 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i, IsPlaying: %i, IsSeeking: %i\n", state_unlocked, IsPlaying (), IsSeeking ());
841 VERIFY_MAIN_THREAD;
843 if (IsSeeking ())
844 return;
846 SetState (Playing);
847 RemoveBit (BufferUnderflow);
848 start_time = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
849 start_time -= target_pts;
851 audio = GetAudio ();
852 if (audio) {
853 audio->Play ();
854 audio->unref ();
857 SetTimeout (GetTimeoutInterval ());
859 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i [Done]\n", state_unlocked);
862 gint32
863 MediaPlayer::GetTimeoutInterval ()
865 gint32 result; // ms between timeouts
866 guint64 pts_per_frame = 0;
868 VERIFY_MAIN_THREAD;
870 if (HasVideo ()) {
871 pts_per_frame = video_stream->pts_per_frame;
872 // there are 10000 pts in a millisecond, anything less than that will result in 0 (and an endless loop)
873 if (pts_per_frame < PTS_PER_MILLISECOND || pts_per_frame >= (guint64) G_MAXINT32) {
874 // If the stream doesn't know its frame rate, use a default of 60 fps
875 result = (gint32) (1000.0 / 60.0);
876 } else {
877 result = (gint32) MilliSeconds_FromPts (pts_per_frame);
879 } else {
880 result = 33;
883 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);
885 return result;
888 void
889 MediaPlayer::SetAudioStreamIndex (gint32 index)
891 IMediaDemuxer *demuxer = NULL;
892 IMediaStream *stream = NULL;
893 AudioStream *next_stream = NULL;
894 AudioStream *prev_stream = NULL;
895 gint32 audio_streams_found = 0;
896 AudioSource *audio;
898 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i).\n", index);
899 VERIFY_MAIN_THREAD;
901 if (index < 0 || index >= audio_stream_count) {
902 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Invalid audio stream index.\n", index);
903 return;
906 if (media == NULL) {
907 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No media.\n", index);
908 return;
911 audio = GetAudio ();
912 if (audio == NULL) {
913 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No audio source.\n", index);
914 return;
917 demuxer = media->GetDemuxer ();
919 if (demuxer == NULL) {
920 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Media doesn't have a demuxer.\n", index);
921 return;
924 prev_stream = audio->GetAudioStream ();
926 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
927 stream = demuxer->GetStream (i);
929 if (stream->GetType () != MediaTypeAudio)
930 continue;
932 if (audio_streams_found == index) {
933 next_stream = (AudioStream *) stream;
934 break;
937 audio_streams_found++;
940 if (next_stream != NULL) {
941 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i). Switched stream from #%i to #%i\n", index, audio_streams_found++, index);
942 prev_stream->SetSelected (false);
943 next_stream->SetSelected (true);
944 audio->SetAudioStream (next_stream);
947 audio->unref ();
950 bool
951 MediaPlayer::GetCanPause ()
953 // FIXME: should return false if it is streaming media
954 VERIFY_MAIN_THREAD;
955 return GetBit (CanPause);
958 void
959 MediaPlayer::SetCanPause (bool value)
961 VERIFY_MAIN_THREAD;
962 SetBitTo (CanPause, value);
965 void
966 MediaPlayer::Pause ()
968 AudioSource *audio;
970 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i\n", state_unlocked);
971 VERIFY_MAIN_THREAD;
973 if (IsPaused ())
974 return;
976 SetState (Paused);
978 audio = GetAudio ();
979 if (audio) {
980 audio->Pause ();
981 audio->unref ();
984 SetTimeout (0);
986 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i [Done]\n", state_unlocked);
989 guint64
990 MediaPlayer::GetTargetPts ()
992 AudioSource *audio;
993 guint64 result;
995 VERIFY_MAIN_THREAD;
997 audio = GetAudio ();
999 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);
1001 if (audio != NULL && audio->GetState () == AudioPlaying)
1002 result = audio->GetCurrentPts ();
1003 else
1004 result = target_pts;
1006 if (audio)
1007 audio->unref ();
1009 return result;
1012 void
1013 MediaPlayer::SeekCompletedHandler (Media *media, EventArgs *args)
1015 LOG_MEDIAPLAYER ("MediaPlayer::SeekCompletedHandler ()\n");
1016 VERIFY_MAIN_THREAD;
1018 RemoveBit (Seeking);
1019 if (HasVideo ()) {
1020 SetBit (LoadFramePending);
1021 LoadVideoFrame ();
1025 void
1026 MediaPlayer::NotifySeek (guint64 pts)
1028 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 ());
1029 VERIFY_MAIN_THREAD;
1031 guint64 duration = GetDuration ();
1033 g_return_if_fail (GetCanSeek ());
1035 if (pts > start_pts + duration)
1036 pts = start_pts + duration;
1038 if (pts < start_pts)
1039 pts = start_pts;
1041 StopAudio ();
1042 SetTimeout (0);
1044 SetBit (Seeking);
1045 SetBit (Seeking);
1046 SetBit (LoadFramePending);
1047 RemoveBit (SeekSynched);
1048 RemoveBit (AudioEnded);
1049 RemoveBit (VideoEnded);
1051 start_time = 0;
1052 current_pts = pts;
1053 target_pts = pts;
1055 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);
1058 bool
1059 MediaPlayer::GetCanSeek ()
1061 VERIFY_MAIN_THREAD;
1062 return GetBit (CanSeek);
1065 void
1066 MediaPlayer::SetCanSeek (bool value)
1068 VERIFY_MAIN_THREAD;
1069 SetBitTo (CanSeek, value);
1072 void
1073 MediaPlayer::Stop ()
1075 LOG_MEDIAPLAYER ("MediaPlayer::Stop (), state: %i\n", state_unlocked);
1076 VERIFY_MAIN_THREAD;
1078 StopAudio ();
1080 SetTimeout (0);
1082 start_time = 0;
1083 current_pts = 0;
1084 target_pts = 0;
1085 SetState (Stopped);
1086 RemoveBit (AudioEnded);
1087 RemoveBit (VideoEnded);
1090 void
1091 MediaPlayer::StopAudio ()
1093 AudioSource *audio;
1095 LOG_MEDIAPLAYER ("MediaPlayer::StopAudio (), state: %i\n", state_unlocked);
1096 VERIFY_MAIN_THREAD;
1098 audio = GetAudio (); // This returns a reffed AudioSource
1099 if (audio) {
1100 audio->Stop ();
1101 audio->unref ();
1105 double
1106 MediaPlayer::GetBalance ()
1108 double result;
1109 AudioSource *audio;
1111 VERIFY_MAIN_THREAD;
1113 audio = GetAudio ();
1114 if (audio) {
1115 result = audio->GetBalance ();
1116 audio->unref ();
1117 } else {
1118 fprintf (stderr, "MediaPlayer::GetBalance (): There's no audio source to get the balance from\n");
1119 result = 0.0;
1122 return result;
1125 void
1126 MediaPlayer::SetBalance (double balance)
1128 AudioSource *audio;
1130 LOG_MEDIAPLAYER ("MediaPlayer::SetBalance (%f)\n", balance);
1131 VERIFY_MAIN_THREAD;
1133 if (balance < -1.0)
1134 balance = -1.0;
1135 else if (balance > 1.0)
1136 balance = 1.0;
1138 audio = GetAudio ();
1139 if (audio) {
1140 audio->SetBalance (balance);
1141 audio->unref ();
1142 } else {
1143 //fprintf (stderr, "MediaPlayer::SetBalance (%f): There's no audio source to set the balance\n", balance);
1147 double
1148 MediaPlayer::GetVolume ()
1150 AudioSource *audio;
1151 double result;
1153 VERIFY_MAIN_THREAD;
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 ();