fixed the build
[moon.git] / src / mediaplayer.cpp
blob2cc8ee9a4d400ef86f36dbfc28e3d6ac771989a9
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;
51 seeks = 0;
53 media = NULL;
54 audio_unlocked = NULL;
56 Initialize ();
59 MediaPlayer::~MediaPlayer ()
61 LOG_MEDIAPLAYER ("MediaPlayer::~MediaPlayer (), id=%i\n", GET_OBJ_ID (this));
62 VERIFY_MAIN_THREAD;
65 void
66 MediaPlayer::Dispose ()
68 LOG_MEDIAPLAYER ("MediaPlayer::Dispose (), id=%i\n", GET_OBJ_ID (this));
70 VERIFY_MAIN_THREAD;
72 Close ();
74 element = NULL;
76 EventObject::Dispose ();
79 AudioSource *
80 MediaPlayer::GetAudio ()
82 AudioSource *result = NULL;
84 // Thread-safe
86 mutex.Lock ();
87 if (audio_unlocked != NULL) {
88 result = audio_unlocked;
89 result->ref ();
91 mutex.Unlock ();
93 return result;
96 void
97 MediaPlayer::SetSurface (Surface *s)
99 if (!SetSurfaceLock ())
100 return;
102 EventObject::SetSurface (s);
104 SetSurfaceUnlock ();
107 void
108 MediaPlayer::AudioFinishedCallback (EventObject *user_data)
110 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinishedCallback ()\n");
111 VERIFY_MAIN_THREAD;
113 MediaPlayer *mplayer = (MediaPlayer *) user_data;
114 mplayer->AudioFinished ();
117 void
118 MediaPlayer::AudioFinished ()
120 LOG_MEDIAPLAYER ("MediaPlayer::AudioFinished () VideoEnded: %i, AudioEnded: %i AudioSource id: %i\n", GetBit (VideoEnded), GetBit (AudioEnded), GET_OBJ_ID (audio_unlocked));
122 // This method must be thread-safe
124 if (!Surface::InMainThread ()) {
125 AddTickCallSafe (AudioFinishedCallback);
126 return;
129 VERIFY_MAIN_THREAD;
130 if (!GetBit (AudioEnded)) {
131 SetBit (AudioEnded);
132 CheckFinished ();
136 void
137 MediaPlayer::VideoFinished ()
139 LOG_MEDIAPLAYER ("MediaPlayer::VideoFinished () VideoEnded: %i, AudioEnded: %i\n", GetBit (VideoEnded), GetBit (AudioEnded));
140 VERIFY_MAIN_THREAD;
142 if (!GetBit (VideoEnded)) {
143 SetBit (VideoEnded);
144 CheckFinished ();
148 void
149 MediaPlayer::CheckFinished ()
151 LOG_MEDIAPLAYER ("MediaPlayer::CheckFinished (), HasVideo: %i, VideoEnded: %i, HasAudio: %i, AudioEnded: %i\n",
152 HasVideo (), GetBit (VideoEnded), HasAudio (), GetBit (AudioEnded));
153 VERIFY_MAIN_THREAD;
155 if (HasVideo () && !GetBit (VideoEnded))
156 return;
158 if (HasAudio () && !GetBit (AudioEnded))
159 return;
161 Emit (MediaEndedEvent);
164 void
165 MediaPlayer::AudioFailed (AudioSource *source)
167 // This method must be thread-safe
169 mutex.Lock ();
170 if (this->audio_unlocked == source) {
171 AudioPlayer::Remove (this->audio_unlocked);
172 this->audio_unlocked->unref ();
173 this->audio_unlocked = NULL;
175 mutex.Unlock ();
178 bool
179 MediaPlayer::Open (Media *media, PlaylistEntry *entry)
181 IMediaDecoder *encoding;
182 IMediaStream *stream;
183 guint64 asx_duration;
184 gint32 *audio_stream_index = NULL;
185 AudioSource *audio;
187 LOG_MEDIAPLAYER ("MediaPlayer::Open (%p), current media: %p\n", media, this->media);
188 VERIFY_MAIN_THREAD;
190 Close ();
192 if (media == NULL) {
193 printf ("MediaPlayer::Open (): media is NULL.\n");
194 return false;
197 if (!media->IsOpened ()) {
198 printf ("MediaPlayer::Open (): media isn't opened.\n");
199 return false;
202 this->media = media;
203 this->media->ref ();
205 SetState (Opened);
207 // Find audio/video streams
208 IMediaDemuxer *demuxer = media->GetDemuxer ();
209 VideoStream *vstream = NULL;
210 AudioStream *astream = NULL, *astream2 = NULL;
212 if (demuxer == NULL) {
213 fprintf (stderr, "MediaPlayer::Open (): media doesn't have a demuxer.\n");
214 return false;
217 audio_stream_index = element->GetAudioStreamIndex ();
219 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
220 stream = demuxer->GetStream (i);
221 encoding = stream->GetDecoder (); //stream->codec;
223 if (encoding == NULL)
224 continue; // No encoding was found for the stream.
226 switch (stream->GetType ()) {
227 case MediaTypeAudio:
228 audio_stream_count++;
229 if (audio_stream_index != NULL){
230 if (*audio_stream_index == audio_stream_count - 1) {
231 astream = (AudioStream *) stream;
233 } else {
234 astream2 = (AudioStream *) stream;
236 if (astream == NULL || astream->GetBitRate () < astream2->GetBitRate ())
237 astream = astream2;
240 break;
241 case MediaTypeVideo:
242 vstream = (VideoStream *) stream;
244 if (video_stream != NULL && vstream->GetBitRate () < video_stream->GetBitRate ())
245 break;
247 video_stream = vstream;
248 video_stream->SetSelected (true);
249 video_stream->ref ();
251 height = video_stream->height;
252 width = video_stream->width;
254 SetVideoBufferSize (width, height);
256 // printf ("video size: %i, %i\n", video_stream->width, video_stream->height);
257 break;
258 case MediaTypeMarker:
259 LOG_MEDIAPLAYER ("MediaPlayer::Open (): Found a marker stream, selecting it.\n");
260 stream->SetSelected (true);
261 default:
262 break;
266 if (astream != NULL) {
267 audio = AudioPlayer::Add (this, astream);
268 if (audio != NULL) {
269 // Only select the audio stream if we can actually play it
270 astream->SetSelected (true);
271 audio->ref ();
272 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected audio stream (%d) properties:\n"
273 "\tchannels: Input: %d Output: %d\n"
274 "\tsample_rate: Input: %d Output: %d\n"
275 "\tbit_rate: Input: %d Output: %d\n"
276 "\tblock_align: Input: %d Output: %d\n"
277 "\tbits_per_sample: Input: %d Output: %d\n"
278 "\tcodec_id: 0x%x\n"
279 "\tduration: %" G_GUINT64_FORMAT "\n"
280 "\textra data size: %d\n",
281 astream->index, astream->GetChannels (), astream->GetOutputChannels (),
282 astream->GetSampleRate (), astream->GetOutputSampleRate (),
283 astream->GetBitRate (), astream->GetOutputBitRate (),
284 astream->GetBlockAlign (), astream->GetOutputBlockAlign (),
285 astream->GetBitsPerSample (), astream->GetOutputBitsPerSample (),
286 astream->GetCodecId (), astream->GetDuration (), astream->GetExtraDataSize ());
287 if (astream->extra_data_size > 0) {
288 int n;
289 LOG_MEDIAPLAYER ("\textra data: ");
290 for (n = 0; n < astream->extra_data_size; n++)
291 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8*)astream->extra_data)[n]);
292 LOG_MEDIAPLAYER ("\n");
294 mutex.Lock ();
295 this->audio_unlocked = audio;
296 mutex.Unlock ();
299 if (video_stream != NULL) {
300 LOG_MEDIAPLAYER ("MediaPlayer::Open(): Selected Video stream (%d) properties:\n"
301 "\twidth: %d\n"
302 "\theight: %d\n"
303 "\tbits_per_sample: %d\n"
304 "\tbit_rate: %d\n"
305 "\tcodec_id: 0x%x\n"
306 "\tpts_per_frame: %" G_GUINT64_FORMAT "\n"
307 "\tduration: %" G_GUINT64_FORMAT "\n"
308 "\textra data size: %d\n",
309 video_stream->index, video_stream->width, video_stream->height, video_stream->bits_per_sample,
310 video_stream->bit_rate, video_stream->codec_id, video_stream->pts_per_frame,
311 video_stream->duration, video_stream->extra_data_size);
312 if (video_stream->extra_data_size > 0) {
313 int n;
314 LOG_MEDIAPLAYER ("\textra data: ");
315 for (n = 0; n < video_stream->extra_data_size; n++)
316 LOG_MEDIAPLAYER ("[0x%x] ", ((gint8*)video_stream->extra_data)[n]);
317 LOG_MEDIAPLAYER ("\n");
321 current_pts = 0;
322 target_pts = 0;
323 start_pts = 0;
325 if (entry != NULL) {
326 start_pts = TimeSpan_ToPts (entry->GetStartTime ());
327 LOG_MEDIAPLAYER ("MediaPlayer::Open (), setting start_pts to: %" G_GUINT64_FORMAT " (%" G_GUINT64_FORMAT " ms).\n", start_pts, MilliSeconds_FromPts (start_pts));
328 // note that we might be re-opening a media which is not at position 0,
329 // so it is not possible to optimize away the case where start_pts = 0.
330 element->Seek (start_pts, true);
332 if (entry->GetIsLive ())
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 seeks = 0;
418 start_time = 0;
419 start_pts = 0;
420 current_pts = 0;
421 target_pts = 0;
422 first_live_pts = G_MAXUINT64;
424 audio_stream_count = 0;
425 height = 0;
426 width = 0;
428 frames_update_timestamp = 0;
429 rendered_frames = 0;
430 dropped_frames = 0;
431 rendered_frames_per_second = 0.0;
432 dropped_frames_per_second = 0.0;
435 void
436 MediaPlayer::Close ()
438 LOG_MEDIAPLAYER ("MediaPlayer::Close ()\n");
439 VERIFY_MAIN_THREAD;
441 mutex.Lock ();
442 if (audio_unlocked) {
443 AudioPlayer::Remove (audio_unlocked);
444 audio_unlocked->Dispose ();
445 audio_unlocked->unref ();
446 audio_unlocked = NULL;
448 mutex.Unlock ();
450 Stop ();
452 // Reset state back to what it was at instantiation
454 if (rgb_buffer != NULL) {
455 free (rgb_buffer);
456 rgb_buffer = NULL;
458 buffer_width = 0;
459 buffer_height = 0;
461 if (surface != NULL) {
462 cairo_surface_destroy (surface);
463 surface = NULL;
466 if (video_stream) {
467 video_stream->RemoveSafeHandlers (this);
468 video_stream->unref ();
469 video_stream = NULL;
472 if (media) {
473 media->unref ();
474 media = NULL;
477 Initialize ();
481 // Puts the data into our rgb buffer.
482 // If necessary converts the data from its source format to rgb first.
485 void
486 MediaPlayer::RenderFrame (MediaFrame *frame)
488 VideoStream *stream = (VideoStream *) frame->stream;
490 LOG_MEDIAPLAYER_EX ("MediaPlayer::RenderFrame (%p), pts: %" G_GUINT64_FORMAT " ms, buflen: %i, buffer: %p, IsPlanar: %i\n", frame, MilliSeconds_FromPts (frame->pts), frame->buflen, frame->buffer, frame->IsPlanar ());
491 VERIFY_MAIN_THREAD;
493 if (!frame->IsDecoded ()) {
494 fprintf (stderr, "MediaPlayer::RenderFrame (): Trying to render a frame which hasn't been decoded yet.\n");
495 return;
498 if ((frame->width > 0 && frame->width != width) || (frame->height > 0 && frame->height != height) || (format != stream->GetDecoder ()->GetPixelFormat ())) {
499 LOG_MEDIAPLAYER ("MediaPlayer::RenderFrame () frame width: %i, frame height: %i, stream width: %i, stream height: %i, previous frame width: %i, previous frame height: %i\n",
500 frame->width, frame->height, video_stream->width, video_stream->height, width, height);
502 if (frame->width > 0)
503 width = frame->width;
504 if (frame->height > 0)
505 height = frame->height;
507 format = stream->GetDecoder ()->GetPixelFormat ();
509 SetVideoBufferSize (width, height);
512 if (!frame->IsPlanar ()) {
513 // Just copy the data
514 guint32 stride = cairo_image_surface_get_stride (surface);
515 for (int i = 0; i < height; i++)
516 memcpy (rgb_buffer + stride * i, frame->buffer + i * width * 4, width * 4);
517 SetBit (RenderedFrame);
518 element->MediaInvalidate ();
519 return;
522 if (frame->data_stride == NULL ||
523 frame->data_stride[1] == NULL ||
524 frame->data_stride[2] == NULL) {
525 return;
528 guint8 *rgb_dest [3] = { rgb_buffer, NULL, NULL };
529 int rgb_stride [3] = { cairo_image_surface_get_stride (surface), 0, 0 };
531 stream->converter->Convert (frame->data_stride, frame->srcStride, frame->srcSlideY,
532 frame->srcSlideH, rgb_dest, rgb_stride);
534 SetBit (RenderedFrame);
535 element->MediaInvalidate ();
538 #define LOG_RS(x) \
539 printf ("MediaPlayer::AdvanceFrame (), %10s frame pts: %6llu ms, target pts: %6llu ms, diff: %+5lld, rendered fps: %5.2f, dropped fps: %5.2f, total: %5.2f\n", x, \
540 MilliSeconds_FromPts (frame->pts), \
541 MilliSeconds_FromPts (target_pts), \
542 (gint64) MilliSeconds_FromPts (frame->pts) - (gint64) MilliSeconds_FromPts (target_pts), \
543 rendered_frames_per_second, \
544 dropped_frames_per_second, \
545 dropped_frames_per_second + rendered_frames_per_second);
547 void
548 MediaPlayer::AdvanceFrame ()
550 MediaFrame *frame = NULL;
551 IMediaStream *stream;
552 guint64 target_pts = 0;
553 guint64 target_pts_start = 0;
554 guint64 target_pts_end = 0;
555 guint64 target_pts_delta = MilliSeconds_ToPts (100);
556 bool update = false;
557 double dropped_frames_per_second = -1;
558 double rendered_frames_per_second = -1;
559 AudioSource *audio;
561 guint64 now = 0;
563 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame () state: %i, current_pts = %" G_GUINT64_FORMAT ", IsPaused: %i, IsSeeking: %i, VideoEnded: %i, AudioEnded: %i, HasVideo: %i, HasAudio: %i\n",
564 state_unlocked, current_pts, IsPaused (), IsSeeking (), GetBit (VideoEnded), GetBit (AudioEnded), HasVideo (), HasAudio ());
565 VERIFY_MAIN_THREAD;
567 RemoveBit (LoadFramePending);
569 if (IsPaused ())
570 return;
572 if (IsSeeking ())
573 return;
575 if (GetBit (VideoEnded))
576 return;
578 if (!HasVideo ())
579 return;
581 // If the audio isn't playing, there might be slight length-difference between
582 // audio and video streams (the audio is shorted and finished earlier for instance)
583 // Treat this case as if there's no audio at all.
584 audio = GetAudio ();
585 if (audio != NULL && audio->GetState () == AudioPlaying) {
586 // use target_pts as set by audio thread
587 target_pts = GetTargetPts ();
588 if (target_pts == G_MAXUINT64) {
589 // This might happen if we've called Play on the audio source, but it hasn't actually played anything yet.
590 LOG_MEDIAPLAYER_EX ("MediaPlayer::AdvanceFrame (): invalid target pts from the audio stream.\n");
591 audio->unref ();
592 return;
594 } else {
595 // no audio to sync to
596 guint64 now = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
597 guint64 elapsed_pts = now - start_time;
599 target_pts = elapsed_pts;
602 printf ("MediaPlayer::AdvanceFrame (): determined target_pts to be: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, elapsed_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, start_time: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
603 target_pts, MilliSeconds_FromPts (target_pts), elapsed_pts, MilliSeconds_FromPts (elapsed_pts), start_time, MilliSeconds_FromPts (start_time));
606 if (audio != NULL) {
607 audio->unref ();
608 audio = NULL;
611 this->target_pts = target_pts;
613 target_pts_start = target_pts_delta > target_pts ? 0 : target_pts - target_pts_delta;
614 target_pts_end = target_pts + target_pts_delta;
616 if (current_pts >= target_pts_end && GetBit (SeekSynched) && !(HasAudio () && GetBit (AudioEnded))) {
617 #if DEBUG_ADVANCEFRAME
618 printf ("MediaPlayer::AdvanceFrame (): video is running too fast, wait a bit (current_pts: %" G_GUINT64_FORMAT " ms, target_pts: %" G_GUINT64_FORMAT " ms, delta: %" G_GUINT64_FORMAT " ms, diff: %" G_GINT64_FORMAT " (%" G_GINT64_FORMAT " ms)).\n",
619 MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (target_pts), MilliSeconds_FromPts (target_pts_delta), current_pts - target_pts, MilliSeconds_FromPts (current_pts - target_pts));
620 #endif
621 return;
624 #if DEBUG_ADVANCEFRAME
625 printf ("MediaPlayer::AdvanceFrame (): target pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n", target_pts, MilliSeconds_FromPts (target_pts));
626 #endif
628 while (true) {
629 frame = video_stream->PopFrame ();
630 if (frame == NULL) {
631 if (video_stream->GetOutputEnded ()) {
632 if (!HasAudio ()) {
633 // Set the target pts to the last pts we showed, since target_pts is what's reported as our current position.
634 this->target_pts = current_pts;
636 VideoFinished ();
637 return;
639 if (!HasAudio ())
640 SetBufferUnderflow ();
641 // If we have audio, we keep playing (and loosing frames) until the audio playing stops due to buffer underflow
642 // TODO: determine if we don't have video due to not having enough data (in which case we should start buffering),
643 // or if it is because the decoder can't keep up (in which case we should drop frames).
644 break;
647 stream = frame->stream;
648 current_pts = frame->pts;
649 update = true;
651 //printf ("MediaPlayer::AdvanceFrame (): current_pts: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, duration: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
652 // current_pts, MilliSeconds_FromPts (current_pts),
653 // duration, MilliSeconds_FromPts (duration));
655 if (GetBit (IsLive)) {
656 first_live_pts = MIN (current_pts, first_live_pts);
659 if (GetBit (FixedDuration)) {
661 printf ("MediaPlayer::AdvanceFrame (): (fixed duration, live: %i) current_pts: %" G_GUINT64_FORMAT " ms, duration: %" G_GUINT64_FORMAT " ms, first_live_pts: %" G_GUINT64_FORMAT " ms, diff: %" G_GUINT64_FORMAT "ms\n",
662 GetBit (IsLive), MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (duration), MilliSeconds_FromPts (first_live_pts), MilliSeconds_FromPts (current_pts - first_live_pts));
664 if (GetBit (IsLive)) {
665 if (current_pts - first_live_pts > duration) {
666 // TODO: Move this out of AdvanceFrame, here it only works for media which has video, not for audio-only media.
667 StopAudio ();
668 AudioFinished ();
669 VideoFinished ();
671 } else {
672 if (current_pts > duration) {
673 StopAudio ();
674 AudioFinished ();
675 VideoFinished ();
678 if (GetBit (VideoEnded)) {
679 //printf ("MediaPlayer::AdvanceFrame (): Reached end of duration.\n");
680 update = false;
681 break;
685 if (!frame->IsDecoded ()) {
686 printf ("MediaPlayer::AdvanceFrame (): Got a non-decoded frame.\n");
687 update = false;
690 if (update && current_pts >= target_pts_start) {
691 if (!GetBit (SeekSynched)) {
692 SetBit (SeekSynched);
693 LOG_MEDIAPLAYER ("MediaPlayer::AdvanceFrame (): We have now successfully synched with the audio after the seek, current_pts: %" G_GUINT64_FORMAT ", target_pts_start: %" G_GUINT64_FORMAT "\n", MilliSeconds_FromPts (current_pts), MilliSeconds_FromPts (target_pts_start));
695 // we are in sync (or ahead) of audio playback
696 break;
699 if (video_stream->IsQueueEmpty ()) {
700 // no more packets in queue, this frame is the most recent we have available
701 break;
704 // we are lagging behind, drop this frame
705 dropped_frames++;
707 //LOG_RS ("[SKIPPED]");
708 media->DisposeObject (frame);
709 frame->unref ();
710 frame = NULL;
713 if (update && frame && GetBit (SeekSynched)) {
714 rendered_frames++;
715 //LOG_RS ("[RENDER]");
717 RenderFrame (frame);
720 if (frame) {
721 media->DisposeObject (frame);
722 frame->unref ();
723 frame = NULL;
726 now = get_now ();
727 if (frames_update_timestamp == 0) {
728 frames_update_timestamp = now;
729 } else if ((now - frames_update_timestamp) > TIMESPANTICKS_IN_SECOND) {
730 double time_elapsed = (double) (now - frames_update_timestamp) / (double) TIMESPANTICKS_IN_SECOND;
731 dropped_frames_per_second = (double) dropped_frames / time_elapsed;
732 rendered_frames_per_second = (double) rendered_frames / time_elapsed;
733 dropped_frames = rendered_frames = 0;
734 frames_update_timestamp = now;
736 this->dropped_frames_per_second = dropped_frames_per_second;
737 this->rendered_frames_per_second = rendered_frames_per_second;
740 return;
743 void
744 MediaPlayer::FirstFrameEnqueuedHandler (EventObject *sender, EventArgs *args)
746 LoadVideoFrame ();
749 void
750 MediaPlayer::LoadVideoFrameCallback (EventObject *object)
752 ((MediaPlayer *) object)->LoadVideoFrame ();
755 void
756 MediaPlayer::LoadVideoFrame ()
758 guint64 target_pts;
759 MediaFrame *frame;
761 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), HasVideo: %i, LoadFramePending: %i\n", HasVideo (), state_unlocked & LoadFramePending);
762 VERIFY_MAIN_THREAD;
764 if (!HasVideo ())
765 return;
767 if (!IsLoadFramePending ())
768 return;
770 frame = video_stream->PopFrame ();
772 if (frame == NULL)
773 return;
775 target_pts = GetTargetPts ();
777 if (target_pts == G_MAXUINT64)
778 target_pts = 0;
780 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (), packet pts: %" G_GUINT64_FORMAT ", target pts: %" G_GUINT64_FORMAT ", pts_per_frame: %" G_GUINT64_FORMAT ", buflen: %i\n", frame->pts, GetTargetPts (), video_stream->pts_per_frame, frame->buflen);
782 if (frame->pts + video_stream->pts_per_frame >= target_pts) {
783 LOG_MEDIAPLAYER ("MediaPlayer::LoadVideoFrame (): rendering.\n");
784 RemoveBit (LoadFramePending);
785 RenderFrame (frame);
786 element->MediaInvalidate ();
787 } else {
788 AddTickCallSafe (LoadVideoFrameCallback);
791 media->DisposeObject (frame);
792 frame->unref ();
794 return;
797 gboolean
798 MediaPlayer::AdvanceFrameCallback (void *user_data)
800 MediaPlayer *mplayer = (MediaPlayer *) user_data;
801 mplayer->SetCurrentDeployment ();
802 mplayer->AdvanceFrame ();
803 #if SANITY
804 Deployment::SetCurrent (NULL);
805 #endif
806 return true;
809 void
810 MediaPlayer::SetTimeout (gint32 timeout /* set to 0 to clear */)
812 TimeManager *time_manager = element ? element->GetTimeManager () : NULL;
813 bool clear = timeout == 0 || advance_frame_timeout_id != 0;
815 LOG_MEDIAPLAYER ("MediaPlayer::SetTimeout (%i) time_manager: %p id: %i\n", timeout, time_manager, GET_OBJ_ID (time_manager));
817 if (clear && advance_frame_timeout_id != 0) {
818 if (time_manager != NULL) {
819 time_manager->RemoveTimeout (advance_frame_timeout_id);
820 } else {
821 g_warning ("MediaPlayer::SetTimeout (): Could not clear timeout. Leaking ourselves to not crash.\n");
822 ref (); // This will prevent us from getting destroyed.
824 advance_frame_timeout_id = 0;
827 if (timeout != 0) {
828 if (time_manager == NULL) {
829 g_warning ("MediaPlayer::SetTimeout (): Could not set timeout (no time manager).\n");
830 } else {
831 advance_frame_timeout_id = time_manager->AddTimeout (MOON_PRIORITY_DEFAULT, timeout, AdvanceFrameCallback, this);
836 void
837 MediaPlayer::Play ()
839 AudioSource *audio;
841 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i, IsPlaying: %i, IsSeeking: %i\n", state_unlocked, IsPlaying (), IsSeeking ());
842 VERIFY_MAIN_THREAD;
844 if (IsSeeking ())
845 return;
847 SetState (Playing);
848 RemoveBit (BufferUnderflow);
849 start_time = TimeSpan_ToPts (element->GetTimeManager()->GetCurrentTime ());
850 start_time -= target_pts;
852 audio = GetAudio ();
853 if (audio) {
854 audio->Play ();
855 audio->unref ();
858 SetTimeout (GetTimeoutInterval ());
860 LOG_MEDIAPLAYER ("MediaPlayer::Play (), state: %i [Done]\n", state_unlocked);
863 gint32
864 MediaPlayer::GetTimeoutInterval ()
866 gint32 result; // ms between timeouts
867 guint64 pts_per_frame = 0;
869 VERIFY_MAIN_THREAD;
871 if (HasVideo ()) {
872 pts_per_frame = video_stream->pts_per_frame;
873 // there are 10000 pts in a millisecond, anything less than that will result in 0 (and an endless loop)
874 if (pts_per_frame < PTS_PER_MILLISECOND || pts_per_frame >= (guint64) G_MAXINT32) {
875 // If the stream doesn't know its frame rate, use a default of 60 fps
876 result = (gint32) (1000.0 / 60.0);
877 } else {
878 result = (gint32) MilliSeconds_FromPts (pts_per_frame);
880 } else {
881 result = 33;
884 LOG_MEDIAPLAYER ("MediaPlayer::GetTimeoutInterval (): %i ms between frames gives fps: %.1f, pts_per_frame: %" G_GUINT64_FORMAT ", exact fps: %f\n", result, 1000.0 / result, pts_per_frame, TIMESPANTICKS_IN_SECOND / (double) pts_per_frame);
886 return result;
889 void
890 MediaPlayer::SetAudioStreamIndex (gint32 index)
892 IMediaDemuxer *demuxer = NULL;
893 IMediaStream *stream = NULL;
894 AudioStream *next_stream = NULL;
895 AudioStream *prev_stream = NULL;
896 gint32 audio_streams_found = 0;
897 AudioSource *audio;
899 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i).\n", index);
900 VERIFY_MAIN_THREAD;
902 if (index < 0 || index >= audio_stream_count) {
903 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Invalid audio stream index.\n", index);
904 return;
907 if (media == NULL) {
908 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No media.\n", index);
909 return;
912 audio = GetAudio ();
913 if (audio == NULL) {
914 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): No audio source.\n", index);
915 return;
918 demuxer = media->GetDemuxer ();
920 if (demuxer == NULL) {
921 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i): Media doesn't have a demuxer.\n", index);
922 return;
925 prev_stream = audio->GetAudioStream ();
927 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
928 stream = demuxer->GetStream (i);
930 if (stream->GetType () != MediaTypeAudio)
931 continue;
933 if (audio_streams_found == index) {
934 next_stream = (AudioStream *) stream;
935 break;
938 audio_streams_found++;
941 if (next_stream != NULL) {
942 LOG_MEDIAPLAYER ("MediaPlayer::SetAudioStreamIndex (%i). Switched stream from #%i to #%i\n", index, audio_streams_found++, index);
943 prev_stream->SetSelected (false);
944 next_stream->SetSelected (true);
945 audio->SetAudioStream (next_stream);
948 audio->unref ();
951 bool
952 MediaPlayer::GetCanPause ()
954 // FIXME: should return false if it is streaming media
955 VERIFY_MAIN_THREAD;
956 return GetBit (CanPause);
959 void
960 MediaPlayer::SetCanPause (bool value)
962 VERIFY_MAIN_THREAD;
963 SetBitTo (CanPause, value);
966 void
967 MediaPlayer::Pause ()
969 AudioSource *audio;
971 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i\n", state_unlocked);
972 VERIFY_MAIN_THREAD;
974 if (IsPaused ())
975 return;
977 SetState (Paused);
979 audio = GetAudio ();
980 if (audio) {
981 audio->Pause ();
982 audio->unref ();
985 SetTimeout (0);
987 LOG_MEDIAPLAYER ("MediaPlayer::Pause (), state: %i [Done]\n", state_unlocked);
990 guint64
991 MediaPlayer::GetTargetPts ()
993 AudioSource *audio;
994 guint64 result;
996 VERIFY_MAIN_THREAD;
998 audio = GetAudio ();
1000 LOG_MEDIAPLAYER_EX ("MediaPlayer::GetTargetPts (): target_pts: %" G_GUINT64_FORMAT ", HasAudio (): %i, audio->GetCurrentPts (): %" G_GUINT64_FORMAT "\n", target_pts, audio != NULL, audio != NULL ? audio->GetCurrentPts () : 0);
1002 if (audio != NULL && audio->GetState () == AudioPlaying)
1003 result = audio->GetCurrentPts ();
1004 else
1005 result = target_pts;
1007 if (audio)
1008 audio->unref ();
1010 return result;
1013 void
1014 MediaPlayer::SeekCompletedHandler (Media *media, EventArgs *args)
1016 LOG_MEDIAPLAYER ("MediaPlayer::SeekCompletedHandler () seeks: %i\n", seeks);
1017 VERIFY_MAIN_THREAD;
1019 seeks--;
1020 if (seeks != 0)
1021 return;
1023 if (HasVideo ()) {
1024 SetBit (LoadFramePending);
1025 LoadVideoFrame ();
1029 void
1030 MediaPlayer::NotifySeek (guint64 pts)
1032 LOG_MEDIAPLAYER ("MediaPlayer::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms), media: %p, state: %i, current_pts: %" G_GUINT64_FORMAT ", IsPlaying (): %i, seeks: %i\n", pts, MilliSeconds_FromPts (pts), media, state_unlocked, current_pts, IsPlaying (), seeks);
1033 VERIFY_MAIN_THREAD;
1035 seeks++;
1036 guint64 duration = GetDuration ();
1038 g_return_if_fail (GetCanSeek ());
1040 if (pts > start_pts + duration)
1041 pts = start_pts + duration;
1043 if (pts < start_pts)
1044 pts = start_pts;
1046 StopAudio ();
1047 SetTimeout (0);
1049 SetBit (LoadFramePending);
1050 RemoveBit (SeekSynched);
1051 RemoveBit (AudioEnded);
1052 RemoveBit (VideoEnded);
1054 start_time = 0;
1055 current_pts = pts;
1056 target_pts = pts;
1058 LOG_MEDIAPLAYER ("MediaPlayer::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms), media: %p, state: %i, current_pts: %" G_GUINT64_FORMAT " [END]\n", pts, MilliSeconds_FromPts (pts), media, state_unlocked, current_pts);
1061 bool
1062 MediaPlayer::GetCanSeek ()
1064 VERIFY_MAIN_THREAD;
1065 return GetBit (CanSeek);
1068 void
1069 MediaPlayer::SetCanSeek (bool value)
1071 VERIFY_MAIN_THREAD;
1072 SetBitTo (CanSeek, value);
1075 void
1076 MediaPlayer::Stop ()
1078 LOG_MEDIAPLAYER ("MediaPlayer::Stop (), state: %i\n", state_unlocked);
1079 VERIFY_MAIN_THREAD;
1081 StopAudio ();
1083 SetTimeout (0);
1085 start_time = 0;
1086 current_pts = 0;
1087 target_pts = 0;
1088 SetState (Stopped);
1089 RemoveBit (AudioEnded);
1090 RemoveBit (VideoEnded);
1093 void
1094 MediaPlayer::StopAudio ()
1096 AudioSource *audio;
1098 LOG_MEDIAPLAYER ("MediaPlayer::StopAudio (), state: %i\n", state_unlocked);
1099 VERIFY_MAIN_THREAD;
1101 audio = GetAudio (); // This returns a reffed AudioSource
1102 if (audio) {
1103 audio->Stop ();
1104 audio->unref ();
1108 double
1109 MediaPlayer::GetBalance ()
1111 double result;
1112 AudioSource *audio;
1114 VERIFY_MAIN_THREAD;
1116 audio = GetAudio ();
1117 if (audio) {
1118 result = audio->GetBalance ();
1119 audio->unref ();
1120 } else {
1121 fprintf (stderr, "MediaPlayer::GetBalance (): There's no audio source to get the balance from\n");
1122 result = 0.0;
1125 return result;
1128 void
1129 MediaPlayer::SetBalance (double balance)
1131 AudioSource *audio;
1133 LOG_MEDIAPLAYER ("MediaPlayer::SetBalance (%f)\n", balance);
1134 VERIFY_MAIN_THREAD;
1136 if (balance < -1.0)
1137 balance = -1.0;
1138 else if (balance > 1.0)
1139 balance = 1.0;
1141 audio = GetAudio ();
1142 if (audio) {
1143 audio->SetBalance (balance);
1144 audio->unref ();
1145 } else {
1146 //fprintf (stderr, "MediaPlayer::SetBalance (%f): There's no audio source to set the balance\n", balance);
1150 double
1151 MediaPlayer::GetVolume ()
1153 AudioSource *audio;
1154 double result;
1156 VERIFY_MAIN_THREAD;
1158 audio = GetAudio ();
1159 if (audio) {
1160 result = audio->GetVolume ();
1161 audio->unref ();
1162 } else {
1163 fprintf (stderr, "MediaPlayer::GetVolume (): There's no audio source to get the volume from\n");
1164 result = 0.0;
1167 return result;
1170 void
1171 MediaPlayer::SetVolume (double volume)
1173 AudioSource *audio;
1175 LOG_MEDIAPLAYER ("MediaPlayer::SetVolume (%f)\n", volume);
1176 VERIFY_MAIN_THREAD;
1178 if (volume < -1.0)
1179 volume = -1.0;
1180 else if (volume > 1.0)
1181 volume = 1.0;
1183 audio = GetAudio ();
1184 if (audio) {
1185 audio->SetVolume (volume);
1186 audio->unref ();
1187 } else {
1188 //fprintf (stderr, "MediaPlayer::SetVolume (%f): There's no audio source to set the volume\n", volume);
1192 bool
1193 MediaPlayer::GetMuted ()
1195 bool result;
1196 AudioSource *audio;
1198 VERIFY_MAIN_THREAD;
1200 audio = GetAudio ();
1201 if (audio) {
1202 result = audio->GetMuted ();
1203 audio->unref ();
1204 } else {
1205 fprintf (stderr, "MediaPlayer::GetMuted (): There's no audio.\n");
1206 result = false;
1209 return result;
1212 void
1213 MediaPlayer::SetMuted (bool muted)
1215 AudioSource *audio;
1217 LOG_MEDIAPLAYER ("MediaPlayer::SetMuted (%i)\n", muted);
1218 VERIFY_MAIN_THREAD;
1220 audio = GetAudio ();
1221 if (audio) {
1222 audio->SetMuted (true);
1223 audio->unref ();
1224 } else {
1225 //fprintf (stderr, "MediaPlayer::SetMuted (%i): There's no audio to mute.\n", muted);
1229 void
1230 MediaPlayer::SetBit (PlayerState s)
1232 mutex.Lock ();
1233 state_unlocked = (PlayerState) (s | state_unlocked);
1234 mutex.Unlock ();
1237 void
1238 MediaPlayer::RemoveBit (PlayerState s)
1240 mutex.Lock ();
1241 state_unlocked = (PlayerState) (~s & state_unlocked);
1242 mutex.Unlock ();
1245 void
1246 MediaPlayer::SetBitTo (PlayerState s, bool value)
1248 if (value) {
1249 SetBit (s);
1250 } else {
1251 RemoveBit (s);
1255 bool
1256 MediaPlayer::GetBit (PlayerState s)
1258 bool result;
1259 mutex.Lock ();
1260 result = (state_unlocked & s) == s;
1261 mutex.Unlock ();
1262 return result;
1265 void
1266 MediaPlayer::SetState (PlayerState s)
1268 mutex.Lock ();
1269 state_unlocked = (PlayerState) ((state_unlocked & ~StateMask) | s);
1270 mutex.Unlock ();
1273 MediaPlayer::PlayerState
1274 MediaPlayer::GetState ()
1276 PlayerState result;
1277 mutex.Lock ();
1278 result = state_unlocked;
1279 mutex.Unlock ();
1280 return result;
1283 bool
1284 MediaPlayer::IsBufferUnderflow ()
1286 return GetBit (BufferUnderflow);
1289 bool
1290 MediaPlayer::IsLoadFramePending ()
1292 return GetBit (LoadFramePending);
1295 bool
1296 MediaPlayer::IsSeeking ()
1298 return seeks > 0;
1301 bool
1302 MediaPlayer::HasRenderedFrame ()
1304 return GetBit (RenderedFrame);
1307 bool
1308 MediaPlayer::IsPlaying ()
1310 return (GetState () & StateMask) == Playing;
1313 bool
1314 MediaPlayer::IsPaused ()
1316 return (GetState () & StateMask) == Paused;
1319 bool
1320 MediaPlayer::IsStopped ()
1322 return (GetState () & StateMask) == Stopped;
1325 void
1326 MediaPlayer::SetBufferUnderflow ()
1328 SetBitTo (BufferUnderflow, true);
1329 EmitBufferUnderflow ();
1332 void
1333 MediaPlayer::EmitBufferUnderflow ()
1335 if (Surface::InMainThread ()) {
1336 Emit (BufferUnderflowEvent);
1337 } else {
1338 AddTickCallSafe (EmitBufferUnderflowAsync);
1342 void
1343 MediaPlayer::EmitBufferUnderflowAsync (EventObject *obj)
1345 ((MediaPlayer *) obj)->EmitBufferUnderflow ();