2009-12-03 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / mediaelement.cpp
blob2a48a637b4eb7f370b98dcbd9a35958a60a05e41
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * mediaelement.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include "geometry.h"
16 #include "runtime.h"
17 #include "media.h"
18 #include "downloader.h"
19 #include "playlist.h"
20 #include "pipeline.h"
21 #include "pipeline-asf.h"
22 #include "pipeline-ui.h"
23 #include "mediaelement.h"
24 #include "debug.h"
25 #include "deployment.h"
26 #include "mediaplayer.h"
27 #include "timeline.h"
28 #include "timemanager.h"
31 * TimelineMarkerNode
33 class TimelineMarkerNode : public List::Node {
34 private:
35 TimelineMarker *marker;
37 public:
38 TimelineMarkerNode (TimelineMarker *marker)
40 this->marker = marker;
41 marker->ref ();
43 virtual ~TimelineMarkerNode ()
45 marker->unref ();
47 TimelineMarker *GetTimelineMarker () { return marker; }
51 * MediaElementFlags
54 enum MediaElementFlags {
55 PlayRequested = (1 << 2), // set if Play() has been requested prior to being ready
56 BufferingFailed = (1 << 3), // set if TryOpen failed to buffer the media.
57 DisableBuffering = (1 << 4), // set if we cannot give useful buffering progress
58 RecalculateMatrix = (1 << 7), // set if the patern matrix needs to be recomputed
59 MediaOpenedEmitted = (1 << 9), // set if MediaOpened has been emitted.
60 MissingCodecs = (1 << 11), // set if we have no video codecs
61 AutoPlayed = (1 << 12), // set if we've autoplayed
62 UpdatingSizeFromMedia = (1 << 13),
63 UseMediaHeight = (1 << 14),
64 UseMediaWidth = (1 << 15),
65 Initializing = (1 << 16),
69 * MediaElement
72 MediaElement::MediaElement ()
74 SetObjectType (Type::MEDIAELEMENT);
76 quality_level = 0;
77 last_quality_level_change_position = 0;
78 streamed_markers = NULL;
79 streamed_markers_queue = NULL;
80 marker_closure = NULL;
81 mplayer = NULL;
82 playlist = NULL;
83 error_args = NULL;
84 flags = UseMediaWidth | UseMediaHeight;
85 detached_state = MediaStateClosed;
87 marker_timeout = 0;
88 mplayer = NULL;
90 Reinitialize ();
92 providers [PropertyPrecedence_DynamicValue] = new MediaElementPropertyValueProvider (this, PropertyPrecedence_DynamicValue);
94 // Note: BufferingTime and Position need to be set in the ctor
95 // so that ReadLocalValue() will get these values.
96 SetValue (MediaElement::BufferingTimeProperty, Value (TimeSpan_FromSeconds (5), Type::TIMESPAN));
97 SetValue (MediaElement::PositionProperty, Value (TimeSpan_FromSeconds (0), Type::TIMESPAN));
99 GetDeployment ()->AddHandler (Deployment::ShuttingDownEvent, ShuttingDownCallback, this);
102 void
103 MediaElement::Dispose ()
105 LOG_MEDIAELEMENT ("MediaElement::Dispose ()\n");
106 VERIFY_MAIN_THREAD;
108 GetDeployment ()->RemoveHandler (Deployment::ShuttingDownEvent, ShuttingDownCallback, this);
110 Reinitialize ();
111 FrameworkElement::Dispose ();
114 void
115 MediaElement::ShuttingDownHandler (Deployment *sender, EventArgs *args)
117 LOG_MEDIAELEMENT ("MediaElement::ShuttingDownHandler ()\n");
119 Reinitialize ();
122 const char *
123 MediaElement::GetStateName (MediaState state)
125 return enums_int_to_str ("MediaState", state);
128 MediaResult
129 MediaElement::AddStreamedMarkerCallback (MediaClosure *c)
131 MediaMarkerFoundClosure *closure = (MediaMarkerFoundClosure *) c;
132 MediaElement *element = (MediaElement *) closure->GetContext ();
133 MediaMarker *mmarker = closure->GetMarker ();
135 // Thread-safe
137 if (mmarker == NULL)
138 return MEDIA_FAIL;
140 element->AddStreamedMarker (mmarker);
142 return MEDIA_SUCCESS;
145 void
146 MediaElement::AddStreamedMarker (MediaMarker *mmarker)
148 guint64 pts;
149 TimelineMarker *marker;
151 g_return_if_fail (mmarker != NULL);
153 pts = mmarker->Pts ();
155 marker = new TimelineMarker ();
156 marker->SetText (mmarker->Text ());
157 marker->SetType (mmarker->Type ());
158 marker->SetTime (pts);
160 AddStreamedMarker (marker);
161 marker->unref ();
164 void
165 MediaElement::AddStreamedMarker (TimelineMarker *marker)
167 LOG_MEDIAELEMENT ("MediaElement::AddStreamedMarker (): got marker %s, %s, %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
168 marker->GetText (), marker->GetType (), marker->GetTime (),
169 MilliSeconds_FromPts (marker->GetTime ()));
171 // thread-safe
173 mutex.Lock ();
174 if (streamed_markers_queue == NULL)
175 streamed_markers_queue = new List ();
176 streamed_markers_queue->Append (new TimelineMarkerNode (marker));
177 mutex.Unlock ();
180 void
181 MediaElement::ReadMarkers (Media *media, IMediaDemuxer *demuxer)
183 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers ()\n");
184 VERIFY_MAIN_THREAD;
186 g_return_if_fail (demuxer != NULL);
187 g_return_if_fail (media != NULL);
189 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
190 if (demuxer->GetStream (i)->GetType () == MediaTypeMarker) {
191 MarkerStream *stream = (MarkerStream *) demuxer->GetStream (i);
193 if (marker_closure == NULL)
194 marker_closure = new MediaMarkerFoundClosure (media, AddStreamedMarkerCallback, this);
196 stream->SetCallback (marker_closure);
198 MediaMarker *m = stream->Pop ();
199 while (m != NULL) {
200 AddStreamedMarker (m);
201 m->unref ();
202 m = stream->Pop ();
204 break;
208 TimelineMarkerCollection *markers = NULL;
209 MediaMarker::Node *current = (MediaMarker::Node *) media->GetMarkers ()->First ();
211 if (current == NULL) {
212 //printf ("MediaElement::ReadMarkers (): no markers.\n");
213 return;
216 markers = new TimelineMarkerCollection ();
217 while (current != NULL) {
218 TimelineMarker *new_marker = new TimelineMarker ();
219 MediaMarker *marker = current->marker;
221 new_marker->SetText (marker->Text ());
222 new_marker->SetType (marker->Type ());
223 new_marker->SetTime (TimeSpan_FromPts (marker->Pts ()));
225 Value v(new_marker);
226 markers->Add (&v);
228 new_marker->unref ();
230 current = (MediaMarker::Node *) current->next;
233 // Docs says we overwrite whatever's been loaded already.
234 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers (): setting %d markers.\n", markers->GetCount ());
235 SetMarkers (markers);
236 markers->unref ();
239 gboolean
240 MediaElement::MarkerTimeout (gpointer context)
242 VERIFY_MAIN_THREAD;
243 ((MediaElement *) context)->SetCurrentDeployment ();
244 ((MediaElement *) context)->CheckMarkers ();
245 return TRUE;
248 void
249 MediaElement::SetMarkerTimeout (bool start)
251 TimeManager *tm;
252 Surface *surface;
254 VERIFY_MAIN_THREAD;
256 surface = GetDeployment ()->GetSurface ();
258 if (surface == NULL)
259 return;
261 tm = surface->GetTimeManager ();
263 g_return_if_fail (tm != NULL);
265 if (start) {
266 if (marker_timeout == 0) {
267 marker_timeout = tm->AddTimeout (MOON_PRIORITY_DEFAULT, 33, MarkerTimeout, this);
269 } else { // stop
270 if (marker_timeout != 0) {
271 tm->RemoveTimeout (marker_timeout);
272 marker_timeout = 0;
277 void
278 MediaElement::CheckMarkers ()
280 guint64 current_position = GetPosition ();
282 LOG_MARKERS_EX ("MediaElement::CheckMarkers () current position: %" G_GUINT64_FORMAT ", previous position: %" G_GUINT64_FORMAT ")\n", current_position, previous_position);
283 VERIFY_MAIN_THREAD;
285 if (current_position > previous_position && seek_to_position == -1) {
286 guint64 tmp = previous_position;
287 // We need to set previous_position before calling CheckMarkers,
288 // as CheckMarkers may end up emitting events, causing seeks
289 // which will change previous_position.
290 previous_position = current_position;
291 CheckMarkers (tmp, current_position - 1);
295 void
296 MediaElement::CheckMarkers (guint64 from, guint64 to)
298 TimelineMarkerCollection *markers;
300 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ")\n", from, to);
301 VERIFY_MAIN_THREAD;
303 if (from == to) {
304 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). from == to\n", from, to);
305 return;
308 if (!(markers = GetMarkers ())) {
309 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). No markers\n", from, to);
310 return;
313 if (from > to) {
314 // if from > to we've seeked backwards (last played position is after this one)
315 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). from > to (diff: %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms).\n", from, to, from - to, MilliSeconds_FromPts (from - to));
316 return;
319 /* Check if we've got streamed markers */
320 mutex.Lock ();
321 if (streamed_markers_queue != NULL) {
322 TimelineMarkerNode *node = (TimelineMarkerNode *) streamed_markers_queue->First ();
323 while (node != NULL) {
324 if (streamed_markers == NULL)
325 streamed_markers = new TimelineMarkerCollection ();
326 streamed_markers->Add (node->GetTimelineMarker ());
327 node = (TimelineMarkerNode *) node->next;
329 streamed_markers_queue->Clear (true);
331 mutex.Unlock ();
333 CheckMarkers (from, to, markers, false);
334 CheckMarkers (from, to, streamed_markers, true);
337 void
338 MediaElement::CheckMarkers (guint64 from, guint64 to, TimelineMarkerCollection *markers, bool remove)
340 TimelineMarker *marker;
341 ArrayList emit_list;
342 Value *val = NULL;
343 guint64 pts;
344 bool emit;
346 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %p, %i). count: %i\n", from, to, markers, remove, markers ? markers->GetCount () : -1);
347 VERIFY_MAIN_THREAD;
349 // We might want to use a more intelligent algorithm here,
350 // this code only loops through all markers on every frame.
352 if (markers != NULL) {
353 for (int i = 0; i < markers->GetCount (); i++) {
354 marker = markers->GetValueAt (i)->AsTimelineMarker ();
356 if (!(val = marker->GetValue (TimelineMarker::TimeProperty)))
357 break;
359 pts = (guint64) val->AsTimeSpan ();
361 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "): Checking pts: %" G_GUINT64_FORMAT ", enqueued %i elements\n", from, to, pts, emit_list.GetCount ());
363 emit = false;
364 if (remove) {
365 // Streamed markers. Emit these even if we passed them with up to 1 s.
366 if (from <= MilliSeconds_ToPts (1000)) {
367 emit = pts >= 0 && pts <= to;
368 } else {
369 emit = pts >= (from - MilliSeconds_ToPts (1000)) && pts <= to;
372 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "): emit: %i, Checking pts: %" G_GUINT64_FORMAT " in marker with Text = %s, Type = %s (removed from from)\n",
373 from <= MilliSeconds_ToPts (1000) ? 0 : from - MilliSeconds_ToPts (1000), to, emit, pts, marker->GetText (), marker->GetType ());
374 } else {
375 // Normal markers.
376 emit = pts >= from && pts <= to;
377 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "): Checking pts: %" G_GUINT64_FORMAT " in marker with Text = %s, Type = %s\n",
378 from, to, pts, marker->GetText (), marker->GetType ());
381 if (emit) {
382 marker->ref ();
383 emit_list.Add (marker);
385 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "): Emitting: Text = %s, Type = %s, Time = %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms, count: %in",
386 from, to, marker->GetText (), marker->GetType (), marker->GetTime (), MilliSeconds_FromPts (marker->GetTime ()), emit_list.GetCount ());
389 if (remove && (pts <= to || emit)) {
390 // Also delete markers we've passed by already
391 markers->RemoveAt (i);
392 i--;
397 for (int i = 0; i < emit_list.GetCount (); i++) {
398 marker = (TimelineMarker *) emit_list [i];
399 Emit (MarkerReachedEvent, new TimelineMarkerRoutedEventArgs (marker));
400 marker->unref ();
404 void
405 MediaElement::SetIsAttached (bool value)
407 VERIFY_MAIN_THREAD;
409 if (IsAttached () == value)
410 return;
412 if (!value) {
413 LOG_MEDIAELEMENT ("MediaElement::SetIsAttached (%i): Stopping media element since we're detached.\n", value);
414 detached_state = state;
415 if (mplayer)
416 mplayer->Stop (); /* this is immediate */
417 Stop (); /* this is async */
418 flags &= ~MediaOpenedEmitted;
419 SetBufferingProgress (0.0);
420 SetCanPause (false);
421 SetCanSeek (false);
422 SetNaturalDuration (new Duration (0));
425 FrameworkElement::SetIsAttached (value);
427 if (value) {
428 LOG_MEDIAELEMENT ("MediaElement reattached, detached state: %s state: %s\n", GetStateName (detached_state), GetStateName (state));
429 if (detached_state == MediaStatePlaying) {
430 Play ();
435 void
436 MediaElement::Reinitialize ()
438 TimelineMarkerCollection *markers;
439 MediaAttributeCollection *attrs;
441 LOG_MEDIAELEMENT ("MediaElement::Reinitialize ()\n");
442 VERIFY_MAIN_THREAD;
444 if (mplayer) {
445 mplayer->Dispose ();
446 mplayer->unref ();
447 mplayer = NULL;
450 if (marker_closure) {
451 marker_closure->Dispose ();
452 marker_closure->unref ();
453 marker_closure = NULL;
456 if (playlist != NULL) {
457 playlist->Dispose ();
458 playlist->unref ();
459 playlist = NULL;
462 flags &= (PlayRequested | UseMediaHeight | UseMediaWidth);
463 flags |= RecalculateMatrix;
465 prev_state = MediaStateClosed;
466 state = MediaStateClosed;
468 first_pts = G_MAXUINT64;
469 seek_to_position = -1;
470 seeked_to_position = 0;
471 paused_position = 0;
472 buffering_mode = 0;
474 mutex.Lock ();
475 delete streamed_markers_queue;
476 streamed_markers_queue = NULL;
477 if (streamed_markers) {
478 streamed_markers->unref ();
479 streamed_markers = NULL;
481 if (error_args) {
482 error_args->unref ();
483 error_args = NULL;
485 mutex.Unlock ();
487 previous_position = 0;
489 SetMarkerTimeout (false);
490 if ((markers = GetMarkers ()))
491 markers->Clear ();
493 if ((attrs = GetAttributes ()))
494 attrs->Clear ();
496 cairo_matrix_init_identity (&matrix);
499 bool
500 MediaElement::IsMissingCodecs ()
502 VERIFY_MAIN_THREAD;
503 return flags & MissingCodecs;
506 Point
507 MediaElement::GetTransformOrigin ()
509 Point *user_xform_origin = GetRenderTransformOrigin ();
510 double h = GetActualHeight();
511 double w = GetActualWidth ();
513 if (w == 0.0 && h == 0.0 && mplayer != NULL) {
514 h = (double) mplayer->GetVideoHeight ();
515 w = (double) mplayer->GetVideoWidth ();
518 return Point (user_xform_origin->x * w, user_xform_origin->y * h);
521 Size
522 MediaElement::ComputeActualSize ()
524 Size result = FrameworkElement::ComputeActualSize ();
525 Size specified = Size (GetWidth (), GetHeight ());
526 UIElement *parent = GetVisualParent ();
528 if (parent && !parent->Is (Type::CANVAS))
529 if (LayoutInformation::GetPreviousConstraint (this) || LayoutInformation::GetLayoutSlot (this))
530 return result;
532 if (mplayer) {
533 Size available = Size (INFINITY, INFINITY);
534 available = available.Min (specified);
535 result = MeasureOverride (available);
536 result = ApplySizeConstraints (result);
539 //g_warning ("actual is %g,%g", result.width, result.height);
541 return result;
544 Size
545 MediaElement::MeasureOverride (Size availableSize)
547 Size desired = availableSize;
548 Rect shape_bounds = Rect ();
549 double sx = 0.0;
550 double sy = 0.0;
552 if (mplayer)
553 shape_bounds = Rect (0,0,
554 mplayer->GetVideoWidth (),
555 mplayer->GetVideoHeight ());
557 /* don't stretch to infinite size */
558 if (isinf (desired.width))
559 desired.width = shape_bounds.width;
560 if (isinf (desired.height))
561 desired.height = shape_bounds.height;
563 /* compute the scaling */
564 if (shape_bounds.width > 0)
565 sx = desired.width / shape_bounds.width;
566 if (shape_bounds.height > 0)
567 sy = desired.height / shape_bounds.height;
569 /* don't use infinite dimensions as constraints */
570 if (isinf (availableSize.width))
571 sx = sy;
572 if (isinf (availableSize.height))
573 sy = sx;
575 switch (GetStretch ()) {
576 case StretchUniform:
577 sx = sy = MIN (sx, sy);
578 break;
579 case StretchUniformToFill:
580 sx = sy = MAX (sx, sy);
581 break;
582 case StretchFill:
583 if (isinf (availableSize.width))
584 sx = sy;
585 if (isinf (availableSize.height))
586 sy = sx;
587 break;
588 case StretchNone:
589 sx = sy = 1.0;
590 break;
593 desired = Size (shape_bounds.width * sx, shape_bounds.height * sy);
595 return desired;
598 Size
599 MediaElement::ArrangeOverride (Size finalSize)
601 Size arranged = finalSize;
602 Rect shape_bounds = Rect ();
603 double sx = 1.0;
604 double sy = 1.0;
606 if (mplayer)
607 shape_bounds = Rect (0, 0,
608 mplayer->GetVideoWidth (),
609 mplayer->GetVideoHeight ());
611 /* compute the scaling */
612 if (shape_bounds.width == 0)
613 shape_bounds.width = arranged.width;
614 if (shape_bounds.height == 0)
615 shape_bounds.height = arranged.height;
617 if (shape_bounds.width != arranged.width)
618 sx = arranged.width / shape_bounds.width;
619 if (shape_bounds.height != arranged.height)
620 sy = arranged.height / shape_bounds.height;
622 switch (GetStretch ()) {
623 case StretchUniform:
624 sx = sy = MIN (sx, sy);
625 break;
626 case StretchUniformToFill:
627 sx = sy = MAX (sx, sy);
628 break;
629 case StretchNone:
630 sx = sy = 1.0;
631 default:
632 break;
635 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
637 return arranged;
640 Rect
641 MediaElement::GetCoverageBounds ()
643 MediaPlayer *mplayer = GetMediaPlayer ();
644 Stretch stretch = GetStretch ();
646 if (IsClosed () || !mplayer || !mplayer->HasRenderedFrame ())
647 return Rect ();
649 if (stretch == StretchFill || stretch == StretchUniformToFill)
650 return bounds;
652 Rect video = Rect (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
653 cairo_matrix_t brush_xform = matrix;
655 cairo_matrix_invert (&brush_xform);
656 cairo_matrix_multiply (&brush_xform, &brush_xform, &absolute_xform);
658 video = video.Transform (&brush_xform);
659 video = video.Intersection (bounds);
661 return video;
665 MediaElement::GetQualityLevel (int min, int max)
667 guint64 current_pts;
668 gint64 diff;
670 if (IsPlaying ()) {
671 current_pts = mplayer->GetPosition ();
672 diff = (gint64) current_pts - (gint64) last_quality_level_change_position;
674 // we check the absolute diff so that we'll still change quality if the user seeks backwards in the video
675 if (llabs (diff) > (gint64) MilliSeconds_ToPts (1000)) {
676 // not changed within a second, candidate for another change.
677 double dropped_frames = mplayer->GetDroppedFramesPerSecond ();
678 if (dropped_frames == 0) {
679 if (quality_level < max) {
680 // better quality
681 quality_level++;
682 last_quality_level_change_position = current_pts;
683 LOG_MEDIAELEMENT ("MediaElement::GetQualityLevel (): increased rendering quality to %i (%i-%i, higher better) - no dropped frames\n", quality_level, min, max);
685 } else if (dropped_frames > 5.0) {
686 if (quality_level > 0) {
687 // worse quality
688 quality_level--;
689 last_quality_level_change_position = current_pts;
690 LOG_MEDIAELEMENT ("MediaElement::GetQualityLevel (): decreased rendering quality to %i (%i-%i, higher better) - %.2f dropped frames per second with current level\n", quality_level, min, max, dropped_frames);
696 return MIN (max, quality_level + min);
699 void
700 MediaElement::Render (cairo_t *cr, Region *region, bool path_only)
702 Stretch stretch = GetStretch ();
703 cairo_surface_t *surface;
704 cairo_pattern_t *pattern;
706 if (!mplayer || !(surface = mplayer->GetCairoSurface ()))
707 return;
709 cairo_save (cr);
710 cairo_set_matrix (cr, &absolute_xform);
712 Size specified (GetActualWidth (), GetActualHeight ());
713 Size stretched = ApplySizeConstraints (specified);
715 if (stretch != StretchUniformToFill)
716 specified = specified.Min (stretched);
718 Rect paint (0, 0, specified.width, specified.height);
721 if (absolute_xform.xy == 0 && absolute_xform.yx == 0) {
722 //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
723 cairo_matrix_t inv = absolute_xform;
724 cairo_matrix_invert (&inv);
725 paint = paint.Transform (&absolute_xform);
726 paint = paint.RoundIn ();
727 paint = paint.Transform (&inv);
731 if (!path_only) {
732 Rect video (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
734 if (GetStretch () == StretchNone)
735 paint = paint.Union (video);
737 if (video.width == 0.0 && video.height == 0.0)
738 return;
740 pattern = cairo_pattern_create_for_surface (surface);
742 image_brush_compute_pattern_matrix (&matrix,
743 paint.width, paint.height,
744 video.width, video.height,
745 stretch, AlignmentXCenter,
746 AlignmentYCenter, NULL, NULL);
748 cairo_pattern_set_matrix (pattern, &matrix);
749 cairo_set_source (cr, pattern);
750 cairo_pattern_destroy (pattern);
753 if (IsPlaying ()) {
754 cairo_filter_t filter;
755 switch (GetQualityLevel (0, 3)) {
756 case 0: filter = CAIRO_FILTER_FAST; break;
757 case 1: filter = CAIRO_FILTER_GOOD; break;
758 case 2: filter = CAIRO_FILTER_BILINEAR; break;
759 default: filter = CAIRO_FILTER_BEST; break;
761 cairo_pattern_set_filter (cairo_get_source (cr), filter);
764 if (!path_only)
765 RenderLayoutClip (cr);
767 paint = paint.Intersection (Rect (0, 0, stretched.width, stretched.height));
768 paint.Draw (cr);
770 if (!path_only)
771 cairo_fill (cr);
773 cairo_restore (cr);
776 void
777 MediaElement::BufferUnderflowHandler (PlaylistRoot *sender, EventArgs *args)
779 LOG_MEDIAELEMENT ("MediaElement::BufferUnderflow (): Switching to 'Buffering', previous_position: %" G_GUINT64_FORMAT " ms, mplayer->GetPosition (): %" G_GUINT64_FORMAT " ms\n",
780 MilliSeconds_FromPts (previous_position), MilliSeconds_FromPts (mplayer->GetPosition ()));
782 flags |= PlayRequested;
783 SetBufferingProgress (0.0);
784 Emit (BufferingProgressChangedEvent);
785 SetState (MediaStateBuffering);
786 mplayer->Pause ();
789 void
790 MediaElement::EmitStateChangedAsync ()
792 AddTickCall (EmitStateChanged);
795 void
796 MediaElement::EmitStateChanged (EventObject *obj)
798 MediaElement *mel = (MediaElement *) obj;
799 if (mel->IsAttached ())
800 mel->Emit (CurrentStateChangedEvent);
803 void
804 MediaElement::SetState (MediaState state)
806 bool emit = false;
808 LOG_MEDIAELEMENT ("MediaElement::SetState (%d): New state: %s, old state: %s\n",
809 state, GetStateName (state), GetStateName (this->state));
811 // thread-safe
813 mutex.Lock ();
814 if (this->state != state) {
815 prev_state = this->state;
816 this->state = state;
817 emit = true;
819 mutex.Unlock ();
821 if (emit) // Don't emit with mutex locked.
822 EmitStateChangedAsync ();
825 void
826 MediaElement::CreatePlaylist ()
828 g_return_if_fail (mplayer == NULL);
830 mplayer = new MediaPlayer (this);
831 SetPlaylist (new PlaylistRoot (this));
834 void
835 MediaElement::SetPlaylist (PlaylistRoot *value)
837 // if playlist is something, then value must be null (and vice versa)
838 g_return_if_fail ((playlist == NULL) != (value == NULL));
840 VERIFY_MAIN_THREAD;
842 if (playlist != NULL) {
843 playlist->RemoveAllHandlers (this);
844 playlist->Dispose ();
845 playlist->unref ();
846 playlist = NULL;
847 } else {
848 playlist = value; // We assume the caller gives us a reference to the playlist
849 playlist->AddHandler (PlaylistRoot::OpeningEvent, OpeningCallback, this);
850 playlist->AddHandler (PlaylistRoot::OpenCompletedEvent, OpenCompletedCallback, this);
851 playlist->AddHandler (PlaylistRoot::SeekingEvent, SeekingCallback, this);
852 playlist->AddHandler (PlaylistRoot::SeekCompletedEvent, SeekCompletedCallback, this);
853 playlist->AddHandler (PlaylistRoot::CurrentStateChangedEvent, CurrentStateChangedCallback, this);
854 playlist->AddHandler (PlaylistRoot::MediaErrorEvent, MediaErrorCallback, this);
855 playlist->AddHandler (PlaylistRoot::MediaEndedEvent, MediaEndedCallback, this);
856 playlist->AddHandler (PlaylistRoot::BufferUnderflowEvent, BufferUnderflowCallback, this);
857 playlist->AddHandler (PlaylistRoot::DownloadProgressChangedEvent, DownloadProgressChangedCallback, this);
858 playlist->AddHandler (PlaylistRoot::BufferingProgressChangedEvent, BufferingProgressChangedCallback, this);
859 playlist->AddHandler (PlaylistRoot::PlayEvent, PlayCallback, this);
860 playlist->AddHandler (PlaylistRoot::PauseEvent, PauseCallback, this);
861 playlist->AddHandler (PlaylistRoot::StopEvent, StopCallback, this);
862 playlist->AddHandler (PlaylistRoot::EntryChangedEvent, EntryChangedCallback, this);
866 void
867 MediaElement::EntryChangedHandler (PlaylistRoot *playlist, EventArgs *args)
869 LOG_MEDIAELEMENT ("MediaElement::EntryChangedHandler ()\n");
870 flags &= ~MediaOpenedEmitted;
873 void
874 MediaElement::OpeningHandler (PlaylistRoot *playlist, EventArgs *args)
876 LOG_MEDIAELEMENT ("MediaElement::OpeningHandler ()\n");
877 VERIFY_MAIN_THREAD;
879 flags &= ~Initializing;
880 SetState (MediaStateOpening);
883 void
884 MediaElement::OpenCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
886 IMediaDemuxer *demuxer;
887 const char *demuxer_name;
888 PlaylistEntry *entry;
889 Media *media;
891 VERIFY_MAIN_THREAD;
893 g_return_if_fail (playlist != NULL);
894 g_return_if_fail (mplayer != NULL);
896 entry = playlist->GetCurrentPlaylistEntry ();
898 g_return_if_fail (entry != NULL);
900 media = entry->GetMedia ();
902 g_return_if_fail (media != NULL);
904 demuxer = media->GetDemuxerReffed ();
905 demuxer_name = demuxer->GetName ();
907 LOG_MEDIAELEMENT ("MediaElement::OpenCompletedHandler (%p), demuxer name: %s\n", media, demuxer_name);
909 // Try to figure out if we're missing codecs
910 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
911 IMediaStream *stream = demuxer->GetStream (i);
912 IMediaDecoder *decoder = stream->GetDecoder ();
913 const char *decoder_name = decoder ? decoder->GetName () : NULL;
914 if (decoder_name != NULL && strcmp (decoder_name, "NullDecoder") == 0) {
915 flags |= MissingCodecs;
916 break;
920 demuxer->unref ();
921 demuxer = NULL;
923 // check if we're missing the codecs *and* if they are not installed
924 // since we could already have downloaded/installed them without refreshing the browser (leading to a crash)
925 if ((flags & MissingCodecs) && !Media::IsMSCodecsInstalled ())
926 CodecDownloader::ShowUI (GetDeployment ()->GetSurface (), false);
928 entry->PopulateMediaAttributes ();
929 SetProperties (media);
931 if (!(flags & MediaOpenedEmitted)) {
932 flags |= MediaOpenedEmitted;
934 PlayOrStop ();
936 // This is a workaround for MS DRT #78: it tests that download progress has changed
937 // from the latest DownloadProgressChanged event to the MediaOpened event (unless
938 // DownloadProgress is 0.0 or 1.0).
939 double progress = media->GetDownloadProgress ();
940 progress = MAX (progress, GetDownloadProgress ());
941 progress = MIN (progress + 0.00000001, 1.0);
942 SetDownloadProgress (progress);
943 Emit (MediaOpenedEvent, new RoutedEventArgs ());
944 Emit (DownloadProgressChangedEvent);
948 void
949 MediaElement::SetProperties (Media *media)
951 IMediaDemuxer *demuxer = NULL;
952 PlaylistEntry *entry;
953 Duration *natural_duration;
954 bool can_seek = true;
955 bool can_pause = true;
957 LOG_MEDIAELEMENT ("MediaElement::SetProperties (%p)\n", media);
959 g_return_if_fail (media != NULL);
960 g_return_if_fail (playlist != NULL);
962 seeked_to_position = 0;
964 demuxer = media->GetDemuxerReffed ();
965 entry = playlist->GetCurrentPlaylistEntry ();
967 if (demuxer == NULL || entry == NULL) {
968 #if SANITY
969 g_warning ("MediaElement::SetProperties (%p): demuxer is null or entry is null (demuxer: %p entry: %p)\n", media, demuxer, entry);
970 #endif
971 goto cleanup;
974 ReadMarkers (media, demuxer);
976 if (entry->GetIsLive ()) {
977 can_seek = false;
978 can_pause = false;
979 } else {
980 can_seek = entry->GetClientSkip () && demuxer->GetCanSeek ();
981 can_pause = true;
984 natural_duration = new Duration (TimeSpan_FromPts (mplayer->GetDuration ()));
985 SetCanPause (can_pause);
986 SetCanSeek (can_seek);
987 SetNaturalDuration (natural_duration);
988 SetNaturalVideoHeight ((double) mplayer->GetVideoHeight ());
989 SetNaturalVideoWidth ((double) mplayer->GetVideoWidth ());
990 SetAudioStreamCount (mplayer->GetAudioStreamCount ());
992 mplayer->SetMuted (GetIsMuted ());
993 mplayer->SetVolume (GetVolume ());
995 UpdateBounds ();
996 InvalidateMeasure ();
997 InvalidateArrange ();
999 cleanup:
1000 if (demuxer)
1001 demuxer->unref ();
1004 void
1005 MediaElement::SeekingHandler (PlaylistRoot *playlist, EventArgs *args)
1007 LOG_MEDIAELEMENT ("MediaElement::SeekingHandler () state: %s\n", GetStateName (state));
1008 VERIFY_MAIN_THREAD;
1010 SetMarkerTimeout (false);
1012 if (GetBufferingProgress () != 0.0) {
1013 SetBufferingProgress (0.0);
1014 Emit (BufferingProgressChangedEvent);
1018 void
1019 MediaElement::SeekCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
1021 LOG_MEDIAELEMENT ("MediaElement::SeekCompletedHandler () state: %s\n", GetStateName (state));
1022 VERIFY_MAIN_THREAD;
1024 seek_to_position = -1;
1025 SetMarkerTimeout (true);
1028 void
1029 MediaElement::PlayHandler (PlaylistRoot *playlist, EventArgs *args)
1031 LOG_MEDIAELEMENT ("MediaElement::PlayHandler ()\n");
1032 VERIFY_MAIN_THREAD;
1034 SetMarkerTimeout (true);
1036 SetState (MediaStatePlaying);
1039 void
1040 MediaElement::PauseHandler (PlaylistRoot *playlist, EventArgs *args)
1042 LOG_MEDIAELEMENT ("MediaElement::PauseHandler ()\n");
1043 VERIFY_MAIN_THREAD;
1045 SetMarkerTimeout (false);
1047 SetState (MediaStatePaused);
1050 void
1051 MediaElement::StopHandler (PlaylistRoot *playlist, EventArgs *args)
1053 PlaylistEntry *entry;
1055 LOG_MEDIAELEMENT ("MediaElement::StopHandler ()\n");
1056 VERIFY_MAIN_THREAD;
1058 g_return_if_fail (playlist != NULL);
1060 entry = playlist->GetCurrentPlaylistEntry ();
1062 g_return_if_fail (entry != NULL);
1063 seeked_to_position = 0;
1065 SetProperties (entry->GetMedia ());
1067 SetMarkerTimeout (false);
1068 CheckMarkers (); // check one last time.
1070 SetState (MediaStateStopped);
1073 void
1074 MediaElement::CurrentStateChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1076 LOG_MEDIAELEMENT ("MediaElement::CurrentStateChangedHandler ()\n");
1077 VERIFY_MAIN_THREAD;
1080 void
1081 MediaElement::MediaErrorHandler (PlaylistRoot *playlist, ErrorEventArgs *args)
1083 LOG_MEDIAELEMENT ("MediaElement::MediaErrorHandler (). State: %s Message: %s\n", GetStateName (state), args ? args->GetErrorMessage() : NULL);
1084 VERIFY_MAIN_THREAD;
1086 if (state == MediaStateClosed && !(flags & Initializing))
1087 return;
1089 // TODO: Should ClearValue be called on these instead?
1090 SetAudioStreamCount (0);
1091 SetNaturalVideoHeight (0);
1092 SetNaturalVideoWidth (0);
1093 SetNaturalDuration (0);
1094 SetCanPause (false);
1095 SetCanSeek (false);
1096 SetDownloadProgress (0);
1097 SetDownloadProgressOffset (0);
1098 SetRenderedFramesPerSecond (0);
1099 SetDroppedFramesPerSecond (0);
1101 UpdateBounds ();
1102 InvalidateMeasure ();
1103 InvalidateArrange ();
1105 SetState (MediaStateClosed);
1107 if (args)
1108 args->ref ();
1110 if (flags & Initializing)
1111 EmitAsync (MediaFailedEvent, args);
1112 else
1113 Emit (MediaFailedEvent, args);
1115 flags &= ~Initializing;
1118 void
1119 MediaElement::MediaEndedHandler (PlaylistRoot *playlist, EventArgs *args)
1121 LOG_MEDIAELEMENT ("MediaElement::MediaEndedHandler () state: %s position: %" G_GUINT64_FORMAT "\n", GetStateName (state), MilliSeconds_FromPts (GetPosition ()));
1122 VERIFY_MAIN_THREAD;
1124 CheckMarkers ();
1125 paused_position = GetNaturalDuration ()->GetTimeSpan ();
1126 SetState (MediaStatePaused);
1127 Emit (MediaEndedEvent);
1130 void
1131 MediaElement::DownloadProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1133 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1135 LOG_MEDIAELEMENT ("MediaElement::DownloadProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1136 VERIFY_MAIN_THREAD;
1138 g_return_if_fail (pea != NULL);
1140 SetDownloadProgress (pea->progress);
1141 Emit (DownloadProgressChangedEvent);
1144 void
1145 MediaElement::BufferingProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1147 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1149 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): %f state: %s\n", pea ? pea->progress : -1.0, GetStateName (state));
1150 VERIFY_MAIN_THREAD;
1152 g_return_if_fail (pea != NULL);
1154 if (GetBufferingProgress () < pea->progress) {
1155 if (state == MediaStatePlaying || state == MediaStatePaused) {
1156 if (state == MediaStatePlaying)
1157 flags |= PlayRequested;
1158 /* this is wrong when the user calls Play while we're still buffering because we'd jump back to the buffering state later (but we'd continue playing) */
1159 /* if we set this earlier though (SeekCompletedHandler) MS DRT #115 fails */
1160 SetState (MediaStateBuffering);
1162 SetBufferingProgress (pea->progress);
1163 if (IsAttached ())
1164 Emit (BufferingProgressChangedEvent);
1167 if (pea->progress >= 1.0) {
1168 if (GetState () == MediaStateBuffering) {
1169 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, playing...\n");
1170 PlayOrStop ();
1171 } else if (flags & PlayRequested) {
1172 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, state: %s PlayRequested: 1\n", GetStateName (state));
1173 Play ();
1178 void
1179 MediaElement::MediaInvalidate ()
1181 Emit (MediaInvalidatedEvent);
1182 Invalidate ();
1185 void
1186 MediaElement::SetUriSource (Uri *uri)
1188 LOG_MEDIAELEMENT ("MediaElement::SetUriSource ('%s')\n", uri ? uri->ToString () : NULL);
1189 VERIFY_MAIN_THREAD;
1191 Reinitialize ();
1193 g_return_if_fail (playlist == NULL);
1195 flags |= Initializing;
1197 if (uri != NULL && uri->originalString != NULL && uri->originalString [0] != 0) {
1198 CreatePlaylist ();
1199 char *str = uri->ToString ();
1200 playlist->GetCurrentEntry ()->InitializeWithUri (str);
1201 g_free (str);
1202 } else {
1203 UpdateBounds ();
1204 InvalidateMeasure ();
1205 InvalidateArrange ();
1208 flags &= ~Initializing;
1211 void
1212 MediaElement::SetSource (Downloader *downloader, const char *PartName)
1214 LOG_MEDIAELEMENT ("MediaElement::SetSource (%p, '%s')\n", downloader, PartName);
1215 VERIFY_MAIN_THREAD;
1217 Reinitialize ();
1219 g_return_if_fail (downloader != NULL);
1220 g_return_if_fail (playlist == NULL);
1222 flags |= Initializing;
1224 CreatePlaylist ();
1225 playlist->GetCurrentEntry ()->InitializeWithDownloader (downloader, PartName);
1227 flags &= ~Initializing;
1230 void
1231 MediaElement::SetStreamSource (ManagedStreamCallbacks *callbacks)
1233 LOG_MEDIAELEMENT ("MediaElement::SetStreamSource (%p)\n", callbacks);
1234 VERIFY_MAIN_THREAD;
1236 Reinitialize ();
1238 g_return_if_fail (callbacks != NULL);
1239 g_return_if_fail (playlist == NULL);
1241 flags |= Initializing;
1243 CreatePlaylist ();
1244 playlist->GetCurrentEntry ()->InitializeWithStream (callbacks);
1246 SetDownloadProgress (1.0);
1248 flags &= ~Initializing;
1251 IMediaDemuxer *
1252 MediaElement::SetDemuxerSource (void *context, CloseDemuxerCallback close_demuxer, GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample,
1253 OpenDemuxerAsyncCallback open_demuxer, SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
1255 ExternalDemuxer *demuxer;
1256 Media *media;
1258 LOG_MEDIAELEMENT ("MediaElement::SetDemuxerSource ()\n");
1259 VERIFY_MAIN_THREAD;
1261 Reinitialize ();
1263 g_return_val_if_fail (context != NULL, NULL);
1264 g_return_val_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL, NULL);
1265 g_return_val_if_fail (playlist == NULL, NULL);
1267 flags |= Initializing;
1269 CreatePlaylist ();
1270 media = new Media (playlist);
1271 demuxer = new ExternalDemuxer (media, context, close_demuxer, get_diagnostic, get_sample, open_demuxer, seek, switch_media_stream);
1272 playlist->GetCurrentEntry ()->InitializeWithDemuxer (demuxer);
1273 media->unref ();
1275 SetDownloadProgress (1.0);
1277 flags &= ~Initializing;
1279 return demuxer;
1282 void
1283 MediaElement::SetPlayRequested ()
1285 LOG_MEDIAELEMENT ("MediaElement::SetPlayRequested ()\n");
1286 VERIFY_MAIN_THREAD;
1288 flags |= PlayRequested;
1291 void
1292 MediaElement::PlayOrStop ()
1294 LOG_MEDIAELEMENT ("MediaElement::PlayOrPause (): GetCanPause (): %s, PlayRequested: %s, GetAutoPlay: %s, AutoPlayed: %s\n",
1295 GetCanPause () ? "true" : "false", (flags & PlayRequested) ? "true" : "false",
1296 GetAutoPlay () ? "true" : "false", (flags & AutoPlayed) ? "true" : "false");
1297 VERIFY_MAIN_THREAD;
1299 if (!GetCanPause ()) {
1300 // If we can't pause, we play
1301 SetState (MediaStatePlaying);
1302 playlist->PlayAsync ();
1303 } else if (flags & PlayRequested) {
1304 // A Play has already been requested.
1305 SetState (MediaStatePlaying);
1306 playlist->PlayAsync ();
1307 } else if (GetAutoPlay () && !(flags & AutoPlayed)) {
1308 // Autoplay us.
1309 flags |= AutoPlayed;
1310 SetState (MediaStatePlaying);
1311 playlist->PlayAsync ();
1312 } else {
1313 SetState (MediaStatePaused);
1317 void
1318 MediaElement::Pause ()
1320 LOG_MEDIAELEMENT ("MediaElement::Pause (): current state: %s\n", GetStateName (state));
1321 VERIFY_MAIN_THREAD;
1323 if (playlist == NULL)
1324 return;
1326 switch (state) {
1327 case MediaStateOpening:// docs: No specified behaviour
1328 flags &= ~PlayRequested;
1329 return;
1330 case MediaStateClosed: // docs: No specified behaviour
1331 return;
1332 case MediaStatePaused:// docs: no-op
1333 case MediaStateBuffering:
1334 case MediaStatePlaying:
1335 case MediaStateStopped: // docs: pause
1336 flags &= ~PlayRequested;
1337 paused_position = GetPosition ();
1338 SetState (MediaStatePaused);
1339 playlist->PauseAsync ();
1340 break;
1341 case MediaStateIndividualizing:
1342 case MediaStateAcquiringLicense:
1343 g_warning ("MediaElement: Invalid state.");
1344 return;
1348 void
1349 MediaElement::Play ()
1351 LOG_MEDIAELEMENT ("MediaElement::Play (): current state: %s\n", GetStateName (state));
1352 VERIFY_MAIN_THREAD;
1354 g_return_if_fail (playlist != NULL);
1356 switch (state) {
1357 case MediaStateClosed: // docs: No specified behaviour
1358 case MediaStateOpening:// docs: No specified behaviour
1359 flags |= PlayRequested;
1360 break;
1361 case MediaStatePlaying:// docs: no-op
1362 case MediaStateBuffering:
1363 case MediaStatePaused:
1364 case MediaStateStopped:
1365 SetState (MediaStatePlaying);
1366 playlist->PlayAsync ();
1367 break;
1368 case MediaStateIndividualizing:
1369 case MediaStateAcquiringLicense:
1370 g_warning ("MediaElement: Invalid state.");
1371 return;
1375 void
1376 MediaElement::Stop ()
1378 LOG_MEDIAELEMENT ("MediaElement::Stop (): current state: %s\n", GetStateName (state));
1379 VERIFY_MAIN_THREAD;
1381 if (!IsAttached ())
1382 return;
1384 switch (state) {
1385 case MediaStateOpening:// docs: No specified behaviour
1386 flags &= ~PlayRequested;
1387 return;
1388 case MediaStateClosed: // docs: No specified behaviour
1389 case MediaStateStopped:// docs: no-op
1390 return;
1391 case MediaStateBuffering:
1392 case MediaStatePlaying:
1393 case MediaStatePaused: // docs: stop
1394 flags &= ~PlayRequested;
1396 LOG_MEDIAELEMENT ("MediaElement::Stop (): state: %s, IsSingleFile: %i\n", GetStateName (state), playlist->IsSingleFile ());
1398 if (!playlist->IsSingleFile ())
1399 flags &= ~MediaOpenedEmitted; // for playlists we must re-emit MediaOpened when the first entry/media has been re-loaded (even though we stop the first entry).
1401 SetState (MediaStateStopped);
1402 playlist->StopAsync ();
1403 break;
1404 case MediaStateIndividualizing:
1405 case MediaStateAcquiringLicense:
1406 g_warning ("MediaElement: Invalid state.");
1407 return;
1411 void
1412 MediaElement::Seek (TimeSpan to, bool force)
1414 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms) state: %s\n", to, MilliSeconds_FromPts (to), GetStateName (state));
1415 VERIFY_MAIN_THREAD;
1417 if (!force && !IsAttached ()) {
1418 LOG_MEDIAELEMENT ("MediaElement::Seek (): not attached.\n");
1419 return;
1422 if (!force && !GetCanSeek ()) {
1423 LOG_MEDIAELEMENT ("MediaElement::Seek (): CanSeek is false, not seeking\n");
1424 return;
1427 switch (state) {
1428 case MediaStateIndividualizing:
1429 case MediaStateAcquiringLicense:
1430 g_warning ("MediaElement:Seek (): Invalid state %s\n", GetStateName (state));
1431 // Fall through
1432 case MediaStateOpening:
1433 case MediaStateClosed:
1434 if (!force)
1435 return;
1436 /* fall through */
1437 case MediaStateBuffering:
1438 case MediaStatePlaying:
1439 case MediaStatePaused:
1440 case MediaStateStopped:
1441 Duration *duration = GetNaturalDuration ();
1443 if (duration->HasTimeSpan () && to > duration->GetTimeSpan ())
1444 to = duration->GetTimeSpan ();
1445 else if (to < 0)
1446 to = 0;
1448 if (!force && to == TimeSpan_FromPts (mplayer->GetPosition ()))
1449 return;
1451 previous_position = to;
1452 seek_to_position = to;
1453 seeked_to_position = to;
1454 paused_position = to;
1456 if (state == MediaStatePlaying)
1457 flags |= PlayRequested;
1459 mplayer->NotifySeek (TimeSpan_ToPts (to));
1460 playlist->SeekAsync (to);
1461 Emit (MediaInvalidatedEvent);
1462 Invalidate ();
1464 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms) previous position: %" G_GUINT64_FORMAT "\n", to, MilliSeconds_FromPts (to), previous_position);
1466 break;
1470 void
1471 MediaElement::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
1473 if (args->GetId () == MediaElement::SourceProperty) {
1474 DownloaderAccessPolicy policy = MediaPolicy;
1475 Uri *uri = GetSource ();
1476 const char *location;
1478 if (uri != NULL) {
1479 if (!(location = GetDeployment ()->GetXapLocation ()) && IsAttached ())
1480 location = GetDeployment ()->GetSurface ()->GetSourceLocation ();
1482 if (uri->scheme && (!strcmp (uri->scheme, "mms") || !strcmp (uri->scheme, "rtsp") || !strcmp (uri->scheme, "rtsps")))
1483 policy = StreamingPolicy;
1485 if (uri->IsInvalidPath ()) {
1486 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri")));
1487 uri = NULL;
1488 } else if (!Downloader::ValidateDownloadPolicy (location, uri, policy)) {
1489 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation")));
1490 uri = NULL;
1494 flags |= RecalculateMatrix;
1495 SetUriSource (uri);
1496 } else if (args->GetId () == MediaElement::AudioStreamIndexProperty) {
1497 if (mplayer)
1498 mplayer->SetAudioStreamIndex (args->GetNewValue()->AsInt32 ());
1499 } else if (args->GetId () == MediaElement::AutoPlayProperty) {
1500 // no state to change
1501 } else if (args->GetId () == MediaElement::BalanceProperty) {
1502 if (mplayer)
1503 mplayer->SetBalance (args->GetNewValue()->AsDouble ());
1504 } else if (args->GetId () == MediaElement::BufferingProgressProperty) {
1505 // read-only property
1506 } else if (args->GetId () == MediaElement::BufferingTimeProperty) {
1507 // TODO
1508 // Not quite sure what to do here, we could:
1509 // a) store the buffering time on the mediaplayer and let the media request it whenever it needs it
1510 // b) let the media request the buffering time from mediaelement
1511 // c) always keep the current media up to date (this is harder to do right when we get multiple media downloading at the same time)
1512 // note that thread-safety is easier with a) (media will do the request from another thread) or c)
1514 if (media)
1515 media->SetBufferingTime (TimeSpan_ToPts (GetBufferingTime ()));
1517 } else if (args->GetId () == MediaElement::CurrentStateProperty) {
1518 // read-only property
1519 // This should really not happen, we use a property provider for this property.
1520 } else if (args->GetId () == MediaElement::IsMutedProperty) {
1521 if (mplayer)
1522 mplayer->SetMuted (args->GetNewValue()->AsBool ());
1523 } else if (args->GetId () == MediaElement::MarkersProperty) {
1525 } else if (args->GetId () == MediaElement::NaturalVideoHeightProperty) {
1526 // read-only property
1527 flags |= RecalculateMatrix;
1528 } else if (args->GetId () == MediaElement::NaturalVideoWidthProperty) {
1529 // read-only property
1530 flags |= RecalculateMatrix;
1531 } else if (args->GetId () == MediaElement::PositionProperty) {
1532 Seek (args->GetNewValue()->AsTimeSpan (), false);
1533 ClearValue (MediaElement::PositionProperty, false); // We need this, otherwise our property system will return the seeked-to position forever (MediaElementPropertyValueProvider isn't called).
1534 } else if (args->GetId () == MediaElement::VolumeProperty) {
1535 if (mplayer)
1536 mplayer->SetVolume (args->GetNewValue()->AsDouble ());
1539 if (args->GetProperty ()->GetOwnerType() != Type::MEDIAELEMENT) {
1540 // propagate to parent class
1541 FrameworkElement::OnPropertyChanged (args, error);
1542 flags |= RecalculateMatrix;
1543 return;
1546 NotifyListenersOfPropertyChange (args, error);
1549 bool
1550 MediaElement::EnableAntiAlias (void)
1552 return !(absolute_xform.xx == absolute_xform.yy && /* no rotation */
1553 (absolute_xform.yx == 0 && absolute_xform.xy == 0)); /* no skew */
1557 void
1558 MediaElement::ReportErrorOccurredCallback (EventObject *obj)
1560 MediaElement *me = (MediaElement *) obj;
1561 ErrorEventArgs *args;
1563 me->mutex.Lock ();
1564 args = me->error_args;
1565 me->error_args = NULL;
1566 me->mutex.Unlock ();
1568 me->ReportErrorOccurred (args);
1569 if (args)
1570 args->unref ();
1573 void
1574 MediaElement::ReportErrorOccurred (ErrorEventArgs *args)
1576 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred (%p)\n", args);
1578 if (!Surface::InMainThread ()) {
1579 mutex.Lock ();
1580 if (error_args)
1581 error_args->unref ();
1582 error_args = args;
1583 if (error_args)
1584 error_args->ref ();
1585 mutex.Unlock ();
1586 AddTickCall (ReportErrorOccurredCallback);
1587 return;
1590 VERIFY_MAIN_THREAD;
1591 MediaErrorHandler (NULL, args);
1594 void
1595 MediaElement::ReportErrorOccurred (const char *args)
1597 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred ('%s')\n", args);
1599 ErrorEventArgs *eea = new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, g_strdup (args)));
1600 ReportErrorOccurred (eea);
1601 eea->unref ();
1605 * MediaElementPropertyValueProvider
1608 MediaElementPropertyValueProvider::MediaElementPropertyValueProvider (MediaElement *element, PropertyPrecedence precedence)
1609 : FrameworkElementProvider (element, precedence)
1611 position = NULL;
1612 current_state = NULL;
1613 rendered_frames_per_second = NULL;
1614 dropped_frames_per_second = NULL;
1617 MediaElementPropertyValueProvider::~MediaElementPropertyValueProvider ()
1619 delete position;
1620 delete current_state;
1621 delete rendered_frames_per_second;
1622 delete dropped_frames_per_second;
1625 Value *
1626 MediaElementPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
1628 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1629 VERIFY_MAIN_THREAD;
1631 if (property->GetId () == MediaElement::PositionProperty)
1632 return GetPosition ();
1634 if (property->GetId () == MediaElement::CurrentStateProperty)
1635 return GetCurrentState ();
1637 if (property->GetId () == MediaElement::DroppedFramesPerSecondProperty)
1638 return GetDroppedFramesPerSecond ();
1640 if (property->GetId () == MediaElement::RenderedFramesPerSecondProperty)
1641 return GetRenderedFramesPerSecond ();
1643 return FrameworkElementProvider::GetPropertyValue (property);
1646 Value *
1647 MediaElementPropertyValueProvider::GetDroppedFramesPerSecond ()
1649 MediaElement *element = (MediaElement *) obj;
1650 MediaPlayer *mplayer = element->GetMediaPlayer ();
1652 delete dropped_frames_per_second;
1654 if (mplayer) {
1655 dropped_frames_per_second = new Value (mplayer->GetDroppedFramesPerSecond ());
1656 } else {
1657 dropped_frames_per_second = NULL;
1660 return dropped_frames_per_second;
1663 Value *
1664 MediaElementPropertyValueProvider::GetRenderedFramesPerSecond ()
1666 MediaElement *element = (MediaElement *) obj;
1667 MediaPlayer *mplayer = element->GetMediaPlayer ();
1669 delete rendered_frames_per_second;
1671 if (mplayer) {
1672 rendered_frames_per_second = new Value (mplayer->GetRenderedFramesPerSecond ());
1673 } else {
1674 rendered_frames_per_second = NULL;
1677 return rendered_frames_per_second;
1680 Value *
1681 MediaElementPropertyValueProvider::GetCurrentState ()
1683 MediaElement *element = (MediaElement *) obj;
1685 delete current_state;
1686 current_state = new Value (element->IsAttached () ? element->state : element->detached_state);
1688 return current_state;
1691 Value *
1692 MediaElementPropertyValueProvider::GetPosition ()
1694 bool use_mplayer = false;;
1695 bool use_pause = false;
1696 MediaElement *element = (MediaElement *) obj;
1697 guint64 position = TimeSpan_ToPts (element->seek_to_position);
1699 delete this->position;
1700 this->position = NULL;
1702 switch (element->state) {
1703 case MediaStateIndividualizing:
1704 case MediaStateAcquiringLicense:
1705 g_warning ("MediaElementPropertyValueProvider::GetPosition (): Invalid state.\n");
1706 // Fall through
1707 case MediaStateOpening:
1708 case MediaStateClosed:
1709 use_mplayer = false;
1710 break;
1711 case MediaStateStopped:
1712 position = 0;
1713 break;
1714 case MediaStateBuffering:
1715 case MediaStatePlaying:
1716 use_mplayer = true;
1717 break;
1718 case MediaStatePaused:
1719 use_pause = true;
1720 break;
1723 // If a seek is pending, we need to return that position.
1724 if (use_mplayer) {
1725 if (TimeSpan_FromPts (position) == -1)
1726 position = element->mplayer->GetPosition ();
1727 } else if (use_pause) {
1728 position = element->paused_position;
1731 if (position < element->seeked_to_position)
1732 position = element->seeked_to_position;
1734 if (TimeSpan_FromPts (position) == -1) {
1735 position = 0;
1736 } else {
1737 position = MIN (position, element->mplayer->GetDuration ());
1740 this->position = new Value (TimeSpan_FromPts (position), Type::TIMESPAN);
1741 return this->position;