2009-11-12 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / mediaelement.cpp
blob1cf71e4e9118d0445f5cee5d90dea9da136c482d
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),
68 * MediaElement
71 MediaElement::MediaElement ()
73 SetObjectType (Type::MEDIAELEMENT);
75 streamed_markers = NULL;
76 streamed_markers_queue = NULL;
77 marker_closure = NULL;
78 mplayer = NULL;
79 playlist = NULL;
80 error_args = NULL;
81 flags = UseMediaWidth | UseMediaHeight;
83 marker_timeout = 0;
84 mplayer = NULL;
86 Reinitialize ();
88 providers [PropertyPrecedence_DynamicValue] = new MediaElementPropertyValueProvider (this, PropertyPrecedence_DynamicValue);
90 // Note: BufferingTime and Position need to be set in the ctor
91 // so that ReadLocalValue() will get these values.
92 SetValue (MediaElement::BufferingTimeProperty, Value (TimeSpan_FromSeconds (5), Type::TIMESPAN));
93 SetValue (MediaElement::PositionProperty, Value (TimeSpan_FromSeconds (0), Type::TIMESPAN));
95 GetDeployment ()->AddHandler (Deployment::ShuttingDownEvent, ShuttingDownCallback, this);
98 void
99 MediaElement::Dispose ()
101 LOG_MEDIAELEMENT ("MediaElement::Dispose ()\n");
102 VERIFY_MAIN_THREAD;
104 GetDeployment ()->RemoveHandler (Deployment::ShuttingDownEvent, ShuttingDownCallback, this);
106 Reinitialize ();
107 FrameworkElement::Dispose ();
110 void
111 MediaElement::ShuttingDownHandler (Deployment *sender, EventArgs *args)
113 LOG_MEDIAELEMENT ("MediaElement::ShuttingDownHandler ()\n");
115 Reinitialize ();
118 const char *
119 MediaElement::GetStateName (MediaState state)
121 return enums_int_to_str ("MediaState", state);
124 MediaResult
125 MediaElement::AddStreamedMarkerCallback (MediaClosure *c)
127 MediaMarkerFoundClosure *closure = (MediaMarkerFoundClosure *) c;
128 MediaElement *element = (MediaElement *) closure->GetContext ();
129 MediaMarker *mmarker = closure->GetMarker ();
131 // Thread-safe
133 if (mmarker == NULL)
134 return MEDIA_FAIL;
136 element->AddStreamedMarker (mmarker);
138 return MEDIA_SUCCESS;
141 void
142 MediaElement::AddStreamedMarker (MediaMarker *mmarker)
144 guint64 pts;
145 TimelineMarker *marker;
147 g_return_if_fail (mmarker != NULL);
149 pts = mmarker->Pts ();
151 marker = new TimelineMarker ();
152 marker->SetText (mmarker->Text ());
153 marker->SetType (mmarker->Type ());
154 marker->SetTime (pts);
156 AddStreamedMarker (marker);
157 marker->unref ();
160 void
161 MediaElement::AddStreamedMarker (TimelineMarker *marker)
163 LOG_MEDIAELEMENT ("MediaElement::AddStreamedMarker (): got marker %s, %s, %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
164 marker->GetText (), marker->GetType (), marker->GetTime (),
165 MilliSeconds_FromPts (marker->GetTime ()));
167 // thread-safe
169 mutex.Lock ();
170 if (streamed_markers_queue == NULL)
171 streamed_markers_queue = new List ();
172 streamed_markers_queue->Append (new TimelineMarkerNode (marker));
173 mutex.Unlock ();
176 void
177 MediaElement::ReadMarkers (Media *media, IMediaDemuxer *demuxer)
179 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers ()\n");
180 VERIFY_MAIN_THREAD;
182 g_return_if_fail (demuxer != NULL);
183 g_return_if_fail (media != NULL);
185 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
186 if (demuxer->GetStream (i)->GetType () == MediaTypeMarker) {
187 MarkerStream *stream = (MarkerStream *) demuxer->GetStream (i);
189 if (marker_closure == NULL)
190 marker_closure = new MediaMarkerFoundClosure (media, AddStreamedMarkerCallback, this);
192 stream->SetCallback (marker_closure);
194 MediaMarker *m = stream->Pop ();
195 while (m != NULL) {
196 AddStreamedMarker (m);
197 m->unref ();
198 m = stream->Pop ();
200 break;
204 TimelineMarkerCollection *markers = NULL;
205 MediaMarker::Node *current = (MediaMarker::Node *) media->GetMarkers ()->First ();
207 if (current == NULL) {
208 //printf ("MediaElement::ReadMarkers (): no markers.\n");
209 return;
212 markers = new TimelineMarkerCollection ();
213 while (current != NULL) {
214 TimelineMarker *new_marker = new TimelineMarker ();
215 MediaMarker *marker = current->marker;
217 new_marker->SetText (marker->Text ());
218 new_marker->SetType (marker->Type ());
219 new_marker->SetTime (TimeSpan_FromPts (marker->Pts ()));
221 Value v(new_marker);
222 markers->Add (&v);
224 new_marker->unref ();
226 current = (MediaMarker::Node *) current->next;
229 // Docs says we overwrite whatever's been loaded already.
230 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers (): setting %d markers.\n", markers->GetCount ());
231 SetMarkers (markers);
232 markers->unref ();
235 gboolean
236 MediaElement::MarkerTimeout (gpointer context)
238 VERIFY_MAIN_THREAD;
239 ((MediaElement *) context)->SetCurrentDeployment ();
240 ((MediaElement *) context)->CheckMarkers ();
241 return TRUE;
244 void
245 MediaElement::SetMarkerTimeout (bool start)
247 TimeManager *tm;
248 Surface *surface;
250 VERIFY_MAIN_THREAD;
252 surface = GetDeployment ()->GetSurface ();
254 if (surface == NULL)
255 return;
257 tm = surface->GetTimeManager ();
259 g_return_if_fail (tm != NULL);
261 if (start) {
262 if (marker_timeout == 0) {
263 marker_timeout = tm->AddTimeout (MOON_PRIORITY_DEFAULT, 33, MarkerTimeout, this);
265 } else { // stop
266 if (marker_timeout != 0) {
267 tm->RemoveTimeout (marker_timeout);
268 marker_timeout = 0;
273 void
274 MediaElement::CheckMarkers ()
276 guint64 current_position = GetPosition ();
278 LOG_MARKERS_EX ("MediaElement::CheckMarkers () current position: %" G_GUINT64_FORMAT ", previous position: %" G_GUINT64_FORMAT ")\n", current_position, previous_position);
279 VERIFY_MAIN_THREAD;
281 if (current_position > previous_position && seek_to_position == -1) {
282 guint64 tmp = previous_position;
283 // We need to set previous_position before calling CheckMarkers,
284 // as CheckMarkers may end up emitting events, causing seeks
285 // which will change previous_position.
286 previous_position = current_position;
287 CheckMarkers (tmp, current_position - 1);
291 void
292 MediaElement::CheckMarkers (guint64 from, guint64 to)
294 TimelineMarkerCollection *markers;
296 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ")\n", from, to);
297 VERIFY_MAIN_THREAD;
299 if (from == to) {
300 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). from == to\n", from, to);
301 return;
304 if (!(markers = GetMarkers ())) {
305 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). No markers\n", from, to);
306 return;
309 if (from > to) {
310 // if from > to we've seeked backwards (last played position is after this one)
311 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));
312 return;
315 /* Check if we've got streamed markers */
316 mutex.Lock ();
317 if (streamed_markers_queue != NULL) {
318 TimelineMarkerNode *node = (TimelineMarkerNode *) streamed_markers_queue->First ();
319 while (node != NULL) {
320 if (streamed_markers == NULL)
321 streamed_markers = new TimelineMarkerCollection ();
322 streamed_markers->Add (node->GetTimelineMarker ());
323 node = (TimelineMarkerNode *) node->next;
325 streamed_markers_queue->Clear (true);
327 mutex.Unlock ();
329 CheckMarkers (from, to, markers, false);
330 CheckMarkers (from, to, streamed_markers, true);
333 void
334 MediaElement::CheckMarkers (guint64 from, guint64 to, TimelineMarkerCollection *markers, bool remove)
336 TimelineMarker *marker;
337 ArrayList emit_list;
338 Value *val = NULL;
339 guint64 pts;
340 bool emit;
342 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %p, %i). count: %i\n", from, to, markers, remove, markers ? markers->GetCount () : -1);
343 VERIFY_MAIN_THREAD;
345 // We might want to use a more intelligent algorithm here,
346 // this code only loops through all markers on every frame.
348 if (markers != NULL) {
349 for (int i = 0; i < markers->GetCount (); i++) {
350 marker = markers->GetValueAt (i)->AsTimelineMarker ();
352 if (!(val = marker->GetValue (TimelineMarker::TimeProperty)))
353 break;
355 pts = (guint64) val->AsTimeSpan ();
357 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 ());
359 emit = false;
360 if (remove) {
361 // Streamed markers. Emit these even if we passed them with up to 1 s.
362 if (from <= MilliSeconds_ToPts (1000)) {
363 emit = pts >= 0 && pts <= to;
364 } else {
365 emit = pts >= (from - MilliSeconds_ToPts (1000)) && pts <= to;
368 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",
369 from <= MilliSeconds_ToPts (1000) ? 0 : from - MilliSeconds_ToPts (1000), to, emit, pts, marker->GetText (), marker->GetType ());
370 } else {
371 // Normal markers.
372 emit = pts >= from && pts <= to;
373 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "): Checking pts: %" G_GUINT64_FORMAT " in marker with Text = %s, Type = %s\n",
374 from, to, pts, marker->GetText (), marker->GetType ());
377 if (emit) {
378 marker->ref ();
379 emit_list.Add (marker);
381 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",
382 from, to, marker->GetText (), marker->GetType (), marker->GetTime (), MilliSeconds_FromPts (marker->GetTime ()), emit_list.GetCount ());
385 if (remove && (pts <= to || emit)) {
386 // Also delete markers we've passed by already
387 markers->RemoveAt (i);
388 i--;
393 for (int i = 0; i < emit_list.GetCount (); i++) {
394 marker = (TimelineMarker *) emit_list [i];
395 Emit (MarkerReachedEvent, new MarkerReachedEventArgs (marker));
396 marker->unref ();
400 void
401 MediaElement::SetSurface (Surface *s)
403 VERIFY_MAIN_THREAD;
405 if (GetSurface() == s)
406 return;
408 if (mplayer)
409 mplayer->SetSurface (s);
411 if (s == NULL) {
412 LOG_PIPELINE ("MediaElement::SetSurface (%p): Stopping media element since we're detached.\n", s);
413 if (mplayer)
414 mplayer->Stop (); /* this is immediate */
415 Stop (); /* this is async */
418 if (!SetSurfaceLock ())
419 return;
420 FrameworkElement::SetSurface (s);
421 SetSurfaceUnlock ();
424 void
425 MediaElement::Reinitialize ()
427 TimelineMarkerCollection *markers;
428 MediaAttributeCollection *attrs;
430 LOG_MEDIAELEMENT ("MediaElement::Reinitialize ()\n");
431 VERIFY_MAIN_THREAD;
433 if (mplayer) {
434 mplayer->Dispose ();
435 mplayer->unref ();
436 mplayer = NULL;
439 if (marker_closure) {
440 marker_closure->Dispose ();
441 marker_closure->unref ();
442 marker_closure = NULL;
445 if (playlist != NULL) {
446 playlist->Dispose ();
447 playlist->unref ();
448 playlist = NULL;
451 flags &= (PlayRequested | UseMediaHeight | UseMediaWidth);
452 flags |= RecalculateMatrix;
454 prev_state = MediaStateClosed;
455 state = MediaStateClosed;
457 last_played_pts = 0;
458 first_pts = G_MAXUINT64;
459 seek_to_position = -1;
460 seeked_to_position = 0;
461 paused_position = 0;
462 buffering_mode = 0;
464 mutex.Lock ();
465 delete streamed_markers_queue;
466 streamed_markers_queue = NULL;
467 if (streamed_markers) {
468 streamed_markers->unref ();
469 streamed_markers = NULL;
471 if (error_args) {
472 error_args->unref ();
473 error_args = NULL;
475 mutex.Unlock ();
477 previous_position = 0;
479 SetMarkerTimeout (false);
480 if ((markers = GetMarkers ()))
481 markers->Clear ();
483 if ((attrs = GetAttributes ()))
484 attrs->Clear ();
486 cairo_matrix_init_identity (&matrix);
489 bool
490 MediaElement::IsMissingCodecs ()
492 VERIFY_MAIN_THREAD;
493 return flags & MissingCodecs;
496 Point
497 MediaElement::GetTransformOrigin ()
499 Point *user_xform_origin = GetRenderTransformOrigin ();
500 double h = GetActualHeight();
501 double w = GetActualWidth ();
503 if (w == 0.0 && h == 0.0 && mplayer != NULL) {
504 h = (double) mplayer->GetVideoHeight ();
505 w = (double) mplayer->GetVideoWidth ();
508 return Point (user_xform_origin->x * w, user_xform_origin->y * h);
511 Size
512 MediaElement::ComputeActualSize ()
514 Size result = FrameworkElement::ComputeActualSize ();
515 Size specified = Size (GetWidth (), GetHeight ());
516 UIElement *parent = GetVisualParent ();
518 if (parent && !parent->Is (Type::CANVAS))
519 if (LayoutInformation::GetPreviousConstraint (this) || LayoutInformation::GetLayoutSlot (this))
520 return result;
522 if (mplayer) {
523 Size available = Size (INFINITY, INFINITY);
524 available = available.Min (specified);
525 result = MeasureOverride (available);
528 //g_warning ("actual is %g,%g", result.width, result.height);
530 return result;
533 Size
534 MediaElement::MeasureOverride (Size availableSize)
536 Size desired = availableSize;
537 Rect shape_bounds = Rect ();
538 double sx = 0.0;
539 double sy = 0.0;
541 if (mplayer)
542 shape_bounds = Rect (0,0,
543 mplayer->GetVideoWidth (),
544 mplayer->GetVideoHeight ());
546 if (GetStretch () == StretchNone)
547 return desired.Min (shape_bounds.width, shape_bounds.height);
549 /* don't stretch to infinite size */
550 if (isinf (desired.width))
551 desired.width = shape_bounds.width;
552 if (isinf (desired.height))
553 desired.height = shape_bounds.height;
555 /* compute the scaling */
556 if (shape_bounds.width > 0)
557 sx = desired.width / shape_bounds.width;
558 if (shape_bounds.height > 0)
559 sy = desired.height / shape_bounds.height;
561 switch (GetStretch ()) {
562 case StretchUniform:
563 sx = sy = MIN (sx, sy);
564 break;
565 case StretchUniformToFill:
566 sx = sy = MAX (sx, sy);
567 break;
568 default:
569 break;
572 desired = desired.Min (shape_bounds.width * sx, shape_bounds.height * sy);
574 return desired;
577 Size
578 MediaElement::ArrangeOverride (Size finalSize)
580 Size arranged = finalSize;
581 Rect shape_bounds = Rect ();
582 double sx = 1.0;
583 double sy = 1.0;
585 if (mplayer)
586 shape_bounds = Rect (0, 0,
587 mplayer->GetVideoWidth (),
588 mplayer->GetVideoHeight ());
590 if (GetStretch () == StretchNone) {
591 arranged = Size (shape_bounds.x + shape_bounds.width,
592 shape_bounds.y + shape_bounds.height);
594 if (GetHorizontalAlignment () == HorizontalAlignmentStretch)
595 arranged.width = MAX (arranged.width, finalSize.width);
597 if (GetVerticalAlignment () == VerticalAlignmentStretch)
598 arranged.height = MAX (arranged.height, finalSize.height);
600 return arranged;
604 /* compute the scaling */
605 if (shape_bounds.width == 0)
606 shape_bounds.width = arranged.width;
607 if (shape_bounds.height == 0)
608 shape_bounds.height = arranged.height;
610 if (shape_bounds.width != arranged.width)
611 sx = arranged.width / shape_bounds.width;
612 if (shape_bounds.height != arranged.height)
613 sy = arranged.height / shape_bounds.height;
615 switch (GetStretch ()) {
616 case StretchUniform:
617 sx = sy = MIN (sx, sy);
618 break;
619 case StretchUniformToFill:
620 sx = sy = MAX (sx, sy);
621 break;
622 default:
623 break;
626 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
628 return arranged;
631 Rect
632 MediaElement::GetCoverageBounds ()
634 MediaPlayer *mplayer = GetMediaPlayer ();
635 Stretch stretch = GetStretch ();
637 if (IsClosed () || !mplayer || !mplayer->HasRenderedFrame ())
638 return Rect ();
640 if (stretch == StretchFill || stretch == StretchUniformToFill)
641 return bounds;
643 Rect video = Rect (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
644 cairo_matrix_t brush_xform = matrix;
646 cairo_matrix_invert (&brush_xform);
647 cairo_matrix_multiply (&brush_xform, &brush_xform, &absolute_xform);
649 video = video.Transform (&brush_xform);
650 video = video.Intersection (bounds);
652 return video;
655 void
656 MediaElement::Render (cairo_t *cr, Region *region, bool path_only)
658 Stretch stretch = GetStretch ();
659 cairo_surface_t *surface;
660 cairo_pattern_t *pattern;
662 if (!mplayer || !(surface = mplayer->GetCairoSurface ()))
663 return;
665 cairo_save (cr);
666 cairo_set_matrix (cr, &absolute_xform);
667 cairo_new_path (cr);
669 Size framework (GetActualWidth (), GetActualHeight ());
670 Rect video (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
672 if (stretch != StretchNone)
673 framework = ApplySizeConstraints (framework);
675 Rect paint (0, 0, framework.width, framework.height);
678 if (absolute_xform.xy == 0 && absolute_xform.yx == 0) {
679 //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
680 cairo_matrix_t inv = absolute_xform;
681 cairo_matrix_invert (&inv);
682 paint = paint.Transform (&absolute_xform);
683 paint = paint.RoundIn ();
684 paint = paint.Transform (&inv);
688 image_brush_compute_pattern_matrix (&matrix,
689 paint.width, paint.height,
690 video.width, video.height,
691 stretch, AlignmentXCenter,
692 AlignmentYCenter, NULL, NULL);
694 pattern = cairo_pattern_create_for_surface (surface);
696 cairo_pattern_set_matrix (pattern, &matrix);
698 cairo_set_source (cr, pattern);
699 cairo_pattern_destroy (pattern);
701 if (IsPlaying ())
702 cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
704 if (!path_only)
705 RenderLayoutClip (cr);
707 paint.Draw (cr);
709 if (!path_only)
710 cairo_fill (cr);
712 cairo_restore (cr);
715 void
716 MediaElement::BufferUnderflowHandler (PlaylistRoot *sender, EventArgs *args)
718 LOG_MEDIAELEMENT ("MediaElement::BufferUnderflow (): Switching to 'Buffering', previous_position: %" G_GUINT64_FORMAT " ms, mplayer->GetPosition (): %" G_GUINT64_FORMAT " ms\n",
719 MilliSeconds_FromPts (previous_position), MilliSeconds_FromPts (mplayer->GetPosition ()));
721 flags |= PlayRequested;
722 SetBufferingProgress (0.0);
723 Emit (BufferingProgressChangedEvent);
724 SetState (MediaStateBuffering);
725 mplayer->Pause ();
728 void
729 MediaElement::EmitStateChangedAsync ()
731 AddTickCallSafe (EmitStateChanged);
734 void
735 MediaElement::EmitStateChanged (EventObject *obj)
737 ((MediaElement *) obj)->Emit (CurrentStateChangedEvent);
740 void
741 MediaElement::SetState (MediaState state)
743 bool emit = false;
745 LOG_MEDIAELEMENT ("MediaElement::SetState (%d): New state: %s, old state: %s\n",
746 state, GetStateName (state), GetStateName (this->state));
748 // thread-safe
750 mutex.Lock ();
751 if (this->state != state) {
752 prev_state = this->state;
753 this->state = state;
754 emit = true;
756 mutex.Unlock ();
758 if (emit) // Don't emit with mutex locked.
759 EmitStateChangedAsync ();
762 void
763 MediaElement::CreatePlaylist ()
765 g_return_if_fail (mplayer == NULL);
767 mplayer = new MediaPlayer (this);
768 SetPlaylist (new PlaylistRoot (this));
771 void
772 MediaElement::SetPlaylist (PlaylistRoot *value)
774 // if playlist is something, then value must be null (and vice versa)
775 g_return_if_fail ((playlist == NULL) != (value == NULL));
777 VERIFY_MAIN_THREAD;
779 if (playlist != NULL) {
780 playlist->RemoveAllHandlers (this);
781 playlist->Dispose ();
782 playlist->unref ();
783 playlist = NULL;
784 } else {
785 playlist = value; // We assume the caller gives us a reference to the playlist
786 playlist->AddHandler (PlaylistRoot::OpeningEvent, OpeningCallback, this);
787 playlist->AddHandler (PlaylistRoot::OpenCompletedEvent, OpenCompletedCallback, this);
788 playlist->AddHandler (PlaylistRoot::SeekingEvent, SeekingCallback, this);
789 playlist->AddHandler (PlaylistRoot::SeekCompletedEvent, SeekCompletedCallback, this);
790 playlist->AddHandler (PlaylistRoot::CurrentStateChangedEvent, CurrentStateChangedCallback, this);
791 playlist->AddHandler (PlaylistRoot::MediaErrorEvent, MediaErrorCallback, this);
792 playlist->AddHandler (PlaylistRoot::MediaEndedEvent, MediaEndedCallback, this);
793 playlist->AddHandler (PlaylistRoot::BufferUnderflowEvent, BufferUnderflowCallback, this);
794 playlist->AddHandler (PlaylistRoot::DownloadProgressChangedEvent, DownloadProgressChangedCallback, this);
795 playlist->AddHandler (PlaylistRoot::BufferingProgressChangedEvent, BufferingProgressChangedCallback, this);
796 playlist->AddHandler (PlaylistRoot::PlayEvent, PlayCallback, this);
797 playlist->AddHandler (PlaylistRoot::PauseEvent, PauseCallback, this);
798 playlist->AddHandler (PlaylistRoot::StopEvent, StopCallback, this);
799 playlist->AddHandler (PlaylistRoot::EntryChangedEvent, EntryChangedCallback, this);
803 void
804 MediaElement::EntryChangedHandler (PlaylistRoot *playlist, EventArgs *args)
806 LOG_MEDIAELEMENT ("MediaElement::EntryChangedHandler ()\n");
807 flags &= ~MediaOpenedEmitted;
810 void
811 MediaElement::OpeningHandler (PlaylistRoot *playlist, EventArgs *args)
813 LOG_MEDIAELEMENT ("MediaElement::OpeningHandler ()\n");
814 VERIFY_MAIN_THREAD;
816 SetState (MediaStateOpening);
819 void
820 MediaElement::OpenCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
822 IMediaDemuxer *demuxer;
823 const char *demuxer_name;
824 PlaylistEntry *entry;
825 Media *media;
827 VERIFY_MAIN_THREAD;
829 g_return_if_fail (playlist != NULL);
830 g_return_if_fail (mplayer != NULL);
832 entry = playlist->GetCurrentPlaylistEntry ();
834 g_return_if_fail (entry != NULL);
836 media = entry->GetMedia ();
838 g_return_if_fail (media != NULL);
840 demuxer = media->GetDemuxer ();
841 demuxer_name = demuxer->GetName ();
843 LOG_MEDIAELEMENT ("MediaElement::OpenCompletedHandler (%p), demuxer name: %s\n", media, demuxer_name);
845 // Try to figure out if we're missing codecs
846 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
847 IMediaStream *stream = demuxer->GetStream (i);
848 IMediaDecoder *decoder = stream->GetDecoder ();
849 const char *decoder_name = decoder ? decoder->GetName () : NULL;
850 if (decoder_name != NULL && strcmp (decoder_name, "NullDecoder") == 0) {
851 flags |= MissingCodecs;
852 break;
856 // check if we're missing the codecs *and* if they are not installed
857 // since we could already have downloaded/installed them without refreshing the browser (leading to a crash)
858 if ((flags & MissingCodecs) && !Media::IsMSCodecsInstalled ())
859 CodecDownloader::ShowUI (GetDeployment ()->GetSurface ());
861 entry->PopulateMediaAttributes ();
862 SetProperties (media);
864 if (!(flags & MediaOpenedEmitted)) {
865 flags |= MediaOpenedEmitted;
867 PlayOrStop ();
869 // This is a workaround for MS DRT #78: it tests that download progress has changed
870 // from the latest DownloadProgressChanged event to the MediaOpened event (unless
871 // DownloadProgress is 0.0 or 1.0).
872 double progress = media->GetDownloadProgress ();
873 progress = MAX (progress, GetDownloadProgress ());
874 progress = MIN (progress + 0.00000001, 1.0);
875 SetDownloadProgress (progress);
876 Emit (MediaOpenedEvent, new RoutedEventArgs ());
877 Emit (DownloadProgressChangedEvent);
881 void
882 MediaElement::SetProperties (Media *media)
884 IMediaDemuxer *demuxer;
885 PlaylistEntry *entry;
886 Duration *natural_duration;
887 bool can_seek = true;
888 bool can_pause = true;
890 LOG_MEDIAELEMENT ("MediaElement::SetProperties (%p)\n", media);
892 g_return_if_fail (media != NULL);
893 g_return_if_fail (playlist != NULL);
895 seeked_to_position = 0;
897 demuxer = media->GetDemuxer ();
898 entry = playlist->GetCurrentPlaylistEntry ();
900 g_return_if_fail (demuxer != NULL);
901 g_return_if_fail (entry != NULL);
903 ReadMarkers (media, demuxer);
906 if (entry->GetIsLive ()) {
907 can_seek = false;
908 can_pause = false;
909 } else {
910 can_seek = entry->GetClientSkip () && demuxer->GetCanSeek ();
911 can_pause = true;
914 natural_duration = new Duration (TimeSpan_FromPts (mplayer->GetDuration ()));
915 SetCanPause (can_pause);
916 SetCanSeek (can_seek);
917 SetNaturalDuration (natural_duration);
918 SetNaturalVideoHeight ((double) mplayer->GetVideoHeight ());
919 SetNaturalVideoWidth ((double) mplayer->GetVideoWidth ());
920 SetAudioStreamCount (mplayer->GetAudioStreamCount ());
922 mplayer->SetMuted (GetIsMuted ());
923 mplayer->SetVolume (GetVolume ());
925 UpdateBounds ();
926 InvalidateMeasure ();
927 InvalidateArrange ();
930 void
931 MediaElement::SeekingHandler (PlaylistRoot *playlist, EventArgs *args)
933 LOG_MEDIAELEMENT ("MediaElement::SeekingHandler ()\n");
934 VERIFY_MAIN_THREAD;
936 SetMarkerTimeout (false);
939 void
940 MediaElement::SeekCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
942 LOG_MEDIAELEMENT ("MediaElement::SeekCompletedHandler ()\n");
943 VERIFY_MAIN_THREAD;
945 if (state == MediaStatePlaying)
946 playlist->PlayAsync ();
948 seek_to_position = -1;
949 SetMarkerTimeout (true);
952 void
953 MediaElement::PlayHandler (PlaylistRoot *playlist, EventArgs *args)
955 LOG_MEDIAELEMENT ("MediaElement::PlayHandler ()\n");
956 VERIFY_MAIN_THREAD;
958 SetMarkerTimeout (true);
960 SetState (MediaStatePlaying);
963 void
964 MediaElement::PauseHandler (PlaylistRoot *playlist, EventArgs *args)
966 LOG_MEDIAELEMENT ("MediaElement::PauseHandler ()\n");
967 VERIFY_MAIN_THREAD;
969 SetMarkerTimeout (false);
971 SetState (MediaStatePaused);
974 void
975 MediaElement::StopHandler (PlaylistRoot *playlist, EventArgs *args)
977 PlaylistEntry *entry;
979 LOG_MEDIAELEMENT ("MediaElement::StopHandler ()\n");
980 VERIFY_MAIN_THREAD;
982 g_return_if_fail (playlist != NULL);
984 entry = playlist->GetCurrentPlaylistEntry ();
986 g_return_if_fail (entry != NULL);
987 seeked_to_position = 0;
989 SetProperties (entry->GetMedia ());
991 SetMarkerTimeout (false);
992 CheckMarkers (); // check one last time.
994 SetState (MediaStateStopped);
997 void
998 MediaElement::CurrentStateChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1000 LOG_MEDIAELEMENT ("MediaElement::CurrentStateChangedHandler ()\n");
1001 VERIFY_MAIN_THREAD;
1004 void
1005 MediaElement::MediaErrorHandler (PlaylistRoot *playlist, ErrorEventArgs *args)
1007 LOG_MEDIAELEMENT ("MediaElement::MediaErrorHandler (). State: %s Message: %s\n", GetStateName (state), args ? args->GetErrorMessage() : NULL);
1008 VERIFY_MAIN_THREAD;
1010 if (state == MediaStateClosed)
1011 return;
1013 // TODO: Should ClearValue be called on these instead?
1014 SetAudioStreamCount (0);
1015 SetNaturalVideoHeight (0);
1016 SetNaturalVideoWidth (0);
1017 SetNaturalDuration (0);
1018 SetCanPause (false);
1019 SetCanSeek (false);
1020 SetDownloadProgress (0);
1021 SetDownloadProgressOffset (0);
1022 SetRenderedFramesPerSecond (0);
1023 SetDroppedFramesPerSecond (0);
1025 UpdateBounds ();
1026 InvalidateMeasure ();
1027 InvalidateArrange ();
1029 SetState (MediaStateClosed);
1031 if (args)
1032 args->ref ();
1033 Emit (MediaFailedEvent, args);
1036 void
1037 MediaElement::MediaEndedHandler (PlaylistRoot *playlist, EventArgs *args)
1039 LOG_MEDIAELEMENT ("MediaElement::MediaEndedHandler ()\n");
1040 VERIFY_MAIN_THREAD;
1042 CheckMarkers ();
1043 paused_position = GetPosition ();
1044 SetState (MediaStatePaused);
1045 Emit (MediaEndedEvent);
1048 void
1049 MediaElement::DownloadProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1051 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1053 LOG_MEDIAELEMENT ("MediaElement::DownloadProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1054 VERIFY_MAIN_THREAD;
1056 g_return_if_fail (pea != NULL);
1058 SetDownloadProgress (pea->progress);
1059 Emit (DownloadProgressChangedEvent);
1062 void
1063 MediaElement::BufferingProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1065 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1067 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1068 VERIFY_MAIN_THREAD;
1070 g_return_if_fail (pea != NULL);
1072 if (GetBufferingProgress () < pea->progress) {
1073 SetBufferingProgress (pea->progress);
1074 Emit (BufferingProgressChangedEvent);
1077 if (pea->progress >= 1.0 && GetState () == MediaStateBuffering) {
1078 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, playing...\n");
1079 PlayOrStop ();
1083 void
1084 MediaElement::MediaInvalidate ()
1086 Emit (MediaInvalidatedEvent);
1087 Invalidate ();
1090 void
1091 MediaElement::SetUriSource (Uri *uri)
1093 LOG_MEDIAELEMENT ("MediaElement::SetUriSource ('%s')\n", uri ? uri->ToString () : NULL);
1094 VERIFY_MAIN_THREAD;
1096 Reinitialize ();
1098 g_return_if_fail (playlist == NULL);
1100 if (uri != NULL && uri->originalString != NULL && uri->originalString [0] != 0) {
1101 CreatePlaylist ();
1102 char *str = uri->ToString ();
1103 playlist->GetCurrentEntry ()->InitializeWithUri (str);
1104 g_free (str);
1105 } else {
1106 UpdateBounds ();
1107 InvalidateMeasure ();
1108 InvalidateArrange ();
1112 void
1113 MediaElement::SetSource (Downloader *downloader, const char *PartName)
1115 LOG_MEDIAELEMENT ("MediaElement::SetSource (%p, '%s')\n", downloader, PartName);
1116 VERIFY_MAIN_THREAD;
1118 Reinitialize ();
1120 g_return_if_fail (downloader != NULL);
1121 g_return_if_fail (playlist == NULL);
1123 CreatePlaylist ();
1124 playlist->GetCurrentEntry ()->InitializeWithDownloader (downloader, PartName);
1127 void
1128 MediaElement::SetStreamSource (ManagedStreamCallbacks *callbacks)
1130 LOG_MEDIAELEMENT ("MediaElement::SetStreamSource (%p)\n", callbacks);
1131 VERIFY_MAIN_THREAD;
1133 Reinitialize ();
1135 g_return_if_fail (callbacks != NULL);
1136 g_return_if_fail (playlist == NULL);
1138 CreatePlaylist ();
1139 playlist->GetCurrentEntry ()->InitializeWithStream (callbacks);
1141 SetDownloadProgress (1.0);
1144 IMediaDemuxer *
1145 MediaElement::SetDemuxerSource (void *context, CloseDemuxerCallback close_demuxer, GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample,
1146 OpenDemuxerAsyncCallback open_demuxer, SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
1148 ExternalDemuxer *demuxer;
1149 Media *media;
1151 LOG_MEDIAELEMENT ("MediaElement::SetDemuxerSource (%p)\n", demuxer);
1152 VERIFY_MAIN_THREAD;
1154 Reinitialize ();
1156 g_return_val_if_fail (context != NULL, NULL);
1157 g_return_val_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL, NULL);
1158 g_return_val_if_fail (playlist == NULL, NULL);
1160 CreatePlaylist ();
1161 media = new Media (playlist);
1162 demuxer = new ExternalDemuxer (media, context, close_demuxer, get_diagnostic, get_sample, open_demuxer, seek, switch_media_stream);
1163 playlist->GetCurrentEntry ()->InitializeWithDemuxer (demuxer);
1164 media->unref ();
1166 SetDownloadProgress (1.0);
1168 return demuxer;
1171 void
1172 MediaElement::SetPlayRequested ()
1174 LOG_MEDIAELEMENT ("MediaElement::SetPlayRequested ()\n");
1175 VERIFY_MAIN_THREAD;
1177 flags |= PlayRequested;
1180 void
1181 MediaElement::PlayOrStop ()
1183 LOG_MEDIAELEMENT ("MediaElement::PlayOrPause (): GetCanPause (): %s, PlayRequested: %s, GetAutoPlay: %s, AutoPlayed: %s\n",
1184 GetCanPause () ? "true" : "false", (flags & PlayRequested) ? "true" : "false",
1185 GetAutoPlay () ? "true" : "false", (flags & AutoPlayed) ? "true" : "false");
1186 VERIFY_MAIN_THREAD;
1188 if (!GetCanPause ()) {
1189 // If we can't pause, we play
1190 SetState (MediaStatePlaying);
1191 playlist->PlayAsync ();
1192 } else if (flags & PlayRequested) {
1193 // A Play has already been requested.
1194 SetState (MediaStatePlaying);
1195 playlist->PlayAsync ();
1196 } else if (GetAutoPlay () && !(flags & AutoPlayed)) {
1197 // Autoplay us.
1198 flags |= AutoPlayed;
1199 SetState (MediaStatePlaying);
1200 playlist->PlayAsync ();
1201 } else {
1202 SetState (MediaStatePaused);
1206 void
1207 MediaElement::Pause ()
1209 LOG_MEDIAELEMENT ("MediaElement::Pause (): current state: %s\n", GetStateName (state));
1210 VERIFY_MAIN_THREAD;
1212 g_return_if_fail (playlist != NULL);
1214 switch (state) {
1215 case MediaStateOpening:// docs: No specified behaviour
1216 flags &= ~PlayRequested;
1217 return;
1218 case MediaStateClosed: // docs: No specified behaviour
1219 return;
1220 case MediaStatePaused:// docs: no-op
1221 case MediaStateBuffering:
1222 case MediaStatePlaying:
1223 case MediaStateStopped: // docs: pause
1224 paused_position = GetPosition ();
1225 SetState (MediaStatePaused);
1226 playlist->PauseAsync ();
1227 break;
1228 case MediaStateIndividualizing:
1229 case MediaStateAcquiringLicense:
1230 g_warning ("MediaElement: Invalid state.");
1231 return;
1235 void
1236 MediaElement::Play ()
1238 LOG_MEDIAELEMENT ("MediaElement::Play (): current state: %s\n", GetStateName (state));
1239 VERIFY_MAIN_THREAD;
1241 g_return_if_fail (playlist != NULL);
1243 switch (state) {
1244 case MediaStateClosed: // docs: No specified behaviour
1245 case MediaStateOpening:// docs: No specified behaviour
1246 flags |= PlayRequested;
1247 break;
1248 case MediaStatePlaying:// docs: no-op
1249 case MediaStateBuffering:
1250 case MediaStatePaused:
1251 case MediaStateStopped:
1252 SetState (MediaStatePlaying);
1253 playlist->PlayAsync ();
1254 break;
1255 case MediaStateIndividualizing:
1256 case MediaStateAcquiringLicense:
1257 g_warning ("MediaElement: Invalid state.");
1258 return;
1262 void
1263 MediaElement::Stop ()
1265 LOG_MEDIAELEMENT ("MediaElement::Stop (): current state: %s\n", GetStateName (state));
1266 VERIFY_MAIN_THREAD;
1268 if (GetSurface () == NULL)
1269 return;
1271 switch (state) {
1272 case MediaStateOpening:// docs: No specified behaviour
1273 flags &= ~PlayRequested;
1274 return;
1275 case MediaStateClosed: // docs: No specified behaviour
1276 case MediaStateStopped:// docs: no-op
1277 return;
1278 case MediaStateBuffering:
1279 case MediaStatePlaying:
1280 case MediaStatePaused: // docs: stop
1281 flags &= ~PlayRequested;
1283 LOG_MEDIAELEMENT ("MediaElement::Stop (): state: %s, IsSingleFile: %i\n", GetStateName (state), playlist->IsSingleFile ());
1285 if (!playlist->IsSingleFile ())
1286 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).
1288 SetState (MediaStateStopped);
1289 playlist->StopAsync ();
1290 break;
1291 case MediaStateIndividualizing:
1292 case MediaStateAcquiringLicense:
1293 g_warning ("MediaElement: Invalid state.");
1294 return;
1298 void
1299 MediaElement::Seek (TimeSpan to, bool force)
1301 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms)\n", to, MilliSeconds_FromPts (to));
1302 VERIFY_MAIN_THREAD;
1304 if (GetSurface () == NULL)
1305 return;
1307 if (!force && !GetCanSeek ()) {
1308 LOG_MEDIAELEMENT ("MediaElement::Seek (): CanSeek is false, not seeking\n");
1309 return;
1312 switch (state) {
1313 case MediaStateIndividualizing:
1314 case MediaStateAcquiringLicense:
1315 g_warning ("MediaElement:Seek (): Invalid state %s\n", GetStateName (state));
1316 // Fall through
1317 case MediaStateOpening:
1318 case MediaStateClosed:
1319 if (!force)
1320 return;
1321 /* fall through */
1322 case MediaStateBuffering:
1323 case MediaStatePlaying:
1324 case MediaStatePaused:
1325 case MediaStateStopped:
1326 Duration *duration = GetNaturalDuration ();
1328 if (duration->HasTimeSpan () && to > duration->GetTimeSpan ())
1329 to = duration->GetTimeSpan ();
1330 else if (to < 0)
1331 to = 0;
1333 if (!force && to == TimeSpan_FromPts (mplayer->GetPosition ()))
1334 return;
1336 previous_position = to;
1337 seek_to_position = to;
1338 seeked_to_position = to;
1339 paused_position = to;
1341 mplayer->NotifySeek (TimeSpan_ToPts (to));
1342 playlist->SeekAsync (to);
1343 Emit (MediaInvalidatedEvent);
1344 Invalidate ();
1346 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms) previous position: %" G_GUINT64_FORMAT "\n", to, MilliSeconds_FromPts (to), previous_position);
1348 break;
1352 void
1353 MediaElement::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
1355 if (args->GetId () == MediaElement::SourceProperty) {
1356 DownloaderAccessPolicy policy = MediaPolicy;
1357 Uri *uri = GetSource ();
1358 const char *location;
1360 if (uri != NULL) {
1361 if (!(location = GetDeployment ()->GetXapLocation ()) && GetSurface ())
1362 location = GetSurface ()->GetSourceLocation ();
1364 if (uri->scheme && (!strcmp (uri->scheme, "mms") || !strcmp (uri->scheme, "rtsp") || !strcmp (uri->scheme, "rtsps")))
1365 policy = StreamingPolicy;
1367 if (uri->IsInvalidPath ()) {
1368 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri")));
1369 uri = NULL;
1370 } else if (!Downloader::ValidateDownloadPolicy (location, uri, policy)) {
1371 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation")));
1372 uri = NULL;
1376 flags |= RecalculateMatrix;
1377 SetUriSource (uri);
1378 } else if (args->GetId () == MediaElement::AudioStreamIndexProperty) {
1379 if (mplayer)
1380 mplayer->SetAudioStreamIndex (args->GetNewValue()->AsInt32 ());
1381 } else if (args->GetId () == MediaElement::AutoPlayProperty) {
1382 // no state to change
1383 } else if (args->GetId () == MediaElement::BalanceProperty) {
1384 if (mplayer)
1385 mplayer->SetBalance (args->GetNewValue()->AsDouble ());
1386 } else if (args->GetId () == MediaElement::BufferingProgressProperty) {
1387 // read-only property
1388 } else if (args->GetId () == MediaElement::BufferingTimeProperty) {
1389 // TODO
1390 // Not quite sure what to do here, we could:
1391 // a) store the buffering time on the mediaplayer and let the media request it whenever it needs it
1392 // b) let the media request the buffering time from mediaelement
1393 // 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)
1394 // note that thread-safety is easier with a) (media will do the request from another thread) or c)
1396 if (media)
1397 media->SetBufferingTime (TimeSpan_ToPts (GetBufferingTime ()));
1399 } else if (args->GetId () == MediaElement::CurrentStateProperty) {
1400 // read-only property
1401 // This should really not happen, we use a property provider for this property.
1402 } else if (args->GetId () == MediaElement::IsMutedProperty) {
1403 if (mplayer)
1404 mplayer->SetMuted (args->GetNewValue()->AsBool ());
1405 } else if (args->GetId () == MediaElement::MarkersProperty) {
1407 } else if (args->GetId () == MediaElement::NaturalVideoHeightProperty) {
1408 // read-only property
1409 flags |= RecalculateMatrix;
1410 } else if (args->GetId () == MediaElement::NaturalVideoWidthProperty) {
1411 // read-only property
1412 flags |= RecalculateMatrix;
1413 } else if (args->GetId () == MediaElement::PositionProperty) {
1414 Seek (args->GetNewValue()->AsTimeSpan (), false);
1415 ClearValue (MediaElement::PositionProperty, false); // We need this, otherwise our property system will return the seeked-to position forever (MediaElementPropertyValueProvider isn't called).
1416 } else if (args->GetId () == MediaElement::VolumeProperty) {
1417 if (mplayer)
1418 mplayer->SetVolume (args->GetNewValue()->AsDouble ());
1421 if (args->GetProperty ()->GetOwnerType() != Type::MEDIAELEMENT) {
1422 // propagate to parent class
1423 FrameworkElement::OnPropertyChanged (args, error);
1424 flags |= RecalculateMatrix;
1425 return;
1428 NotifyListenersOfPropertyChange (args, error);
1431 bool
1432 MediaElement::EnableAntiAlias (void)
1434 return !(absolute_xform.xx == absolute_xform.yy && /* no rotation */
1435 (absolute_xform.yx == 0 && absolute_xform.xy == 0)); /* no skew */
1439 void
1440 MediaElement::ReportErrorOccurredCallback (EventObject *obj)
1442 MediaElement *me = (MediaElement *) obj;
1443 ErrorEventArgs *args;
1445 me->mutex.Lock ();
1446 args = me->error_args;
1447 me->error_args = NULL;
1448 me->mutex.Unlock ();
1450 me->ReportErrorOccurred (args);
1451 if (args)
1452 args->unref ();
1455 void
1456 MediaElement::ReportErrorOccurred (ErrorEventArgs *args)
1458 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred (%p)\n", args);
1460 if (!Surface::InMainThread ()) {
1461 mutex.Lock ();
1462 if (error_args)
1463 error_args->unref ();
1464 error_args = args;
1465 if (error_args)
1466 error_args->ref ();
1467 mutex.Unlock ();
1468 AddTickCallSafe (ReportErrorOccurredCallback);
1469 return;
1472 VERIFY_MAIN_THREAD;
1473 MediaErrorHandler (NULL, args);
1476 void
1477 MediaElement::ReportErrorOccurred (const char *args)
1479 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred ('%s')\n", args);
1481 ErrorEventArgs *eea = new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, g_strdup (args)));
1482 ReportErrorOccurred (eea);
1483 eea->unref ();
1487 * MediaElementPropertyValueProvider
1490 MediaElementPropertyValueProvider::MediaElementPropertyValueProvider (MediaElement *element, PropertyPrecedence precedence)
1491 : FrameworkElementProvider (element, precedence)
1493 position = NULL;
1494 current_state = NULL;
1495 rendered_frames_per_second = NULL;
1496 dropped_frames_per_second = NULL;
1499 MediaElementPropertyValueProvider::~MediaElementPropertyValueProvider ()
1501 delete position;
1502 delete current_state;
1503 delete rendered_frames_per_second;
1504 delete dropped_frames_per_second;
1507 Value *
1508 MediaElementPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
1510 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1511 VERIFY_MAIN_THREAD;
1513 if (property->GetId () == MediaElement::PositionProperty)
1514 return GetPosition ();
1516 if (property->GetId () == MediaElement::CurrentStateProperty)
1517 return GetCurrentState ();
1519 if (property->GetId () == MediaElement::DroppedFramesPerSecondProperty)
1520 return GetDroppedFramesPerSecond ();
1522 if (property->GetId () == MediaElement::RenderedFramesPerSecondProperty)
1523 return GetRenderedFramesPerSecond ();
1525 return FrameworkElementProvider::GetPropertyValue (property);
1528 Value *
1529 MediaElementPropertyValueProvider::GetDroppedFramesPerSecond ()
1531 MediaElement *element = (MediaElement *) obj;
1532 MediaPlayer *mplayer = element->GetMediaPlayer ();
1534 delete dropped_frames_per_second;
1536 if (mplayer) {
1537 dropped_frames_per_second = new Value (mplayer->GetDroppedFramesPerSecond ());
1538 } else {
1539 dropped_frames_per_second = NULL;
1542 return dropped_frames_per_second;
1545 Value *
1546 MediaElementPropertyValueProvider::GetRenderedFramesPerSecond ()
1548 MediaElement *element = (MediaElement *) obj;
1549 MediaPlayer *mplayer = element->GetMediaPlayer ();
1551 delete rendered_frames_per_second;
1553 if (mplayer) {
1554 rendered_frames_per_second = new Value (mplayer->GetRenderedFramesPerSecond ());
1555 } else {
1556 rendered_frames_per_second = NULL;
1559 return rendered_frames_per_second;
1562 Value *
1563 MediaElementPropertyValueProvider::GetCurrentState ()
1565 MediaElement *element = (MediaElement *) obj;
1567 delete current_state;
1568 current_state = new Value (element->state);
1570 return current_state;
1573 Value *
1574 MediaElementPropertyValueProvider::GetPosition ()
1576 bool use_mplayer = false;;
1577 bool use_pause = false;
1578 MediaElement *element = (MediaElement *) obj;
1579 guint64 position = TimeSpan_ToPts (element->seek_to_position);
1581 delete this->position;
1582 this->position = NULL;
1584 switch (element->state) {
1585 case MediaStateIndividualizing:
1586 case MediaStateAcquiringLicense:
1587 g_warning ("MediaElementPropertyValueProvider::GetPosition (): Invalid state.\n");
1588 // Fall through
1589 case MediaStateOpening:
1590 case MediaStateClosed:
1591 use_mplayer = false;
1592 break;
1593 case MediaStateStopped:
1594 position = 0;
1595 break;
1596 case MediaStateBuffering:
1597 case MediaStatePlaying:
1598 use_mplayer = true;
1599 break;
1600 case MediaStatePaused:
1601 use_pause = true;
1602 break;
1605 // If a seek is pending, we need to return that position.
1606 if (use_mplayer) {
1607 if (TimeSpan_FromPts (position) == -1)
1608 position = element->mplayer->GetPosition ();
1609 } else if (use_pause) {
1610 position = element->paused_position;
1613 if (position < element->seeked_to_position)
1614 position = element->seeked_to_position;
1616 if (TimeSpan_FromPts (position) == -1) {
1617 position = 0;
1618 } else {
1619 position = MIN (position, element->mplayer->GetDuration ());
1622 this->position = new Value (TimeSpan_FromPts (position), Type::TIMESPAN);
1623 return this->position;