2009-11-05 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / mediaelement.cpp
blob84f6a3362789f379367e2e1b1b3e71645e1bc890
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 ());
517 if (mplayer) {
518 Size available = Size (INFINITY, INFINITY);
519 available = available.Min (specified);
520 result = MeasureOverride (available);
523 //g_warning ("actual is %g,%g", result.width, result.height);
525 return result;
528 Size
529 MediaElement::MeasureOverride (Size availableSize)
531 Size desired = availableSize;
532 Rect shape_bounds = Rect ();
533 double sx = 0.0;
534 double sy = 0.0;
536 if (mplayer)
537 shape_bounds = Rect (0,0,
538 mplayer->GetVideoWidth (),
539 mplayer->GetVideoHeight ());
541 if (GetStretch () == StretchNone)
542 return desired.Min (shape_bounds.width, shape_bounds.height);
544 /* don't stretch to infinite size */
545 if (isinf (desired.width))
546 desired.width = shape_bounds.width;
547 if (isinf (desired.height))
548 desired.height = shape_bounds.height;
550 /* compute the scaling */
551 if (shape_bounds.width > 0)
552 sx = desired.width / shape_bounds.width;
553 if (shape_bounds.height > 0)
554 sy = desired.height / shape_bounds.height;
556 switch (GetStretch ()) {
557 case StretchUniform:
558 sx = sy = MIN (sx, sy);
559 break;
560 case StretchUniformToFill:
561 sx = sy = MAX (sx, sy);
562 break;
563 default:
564 break;
567 desired = desired.Min (shape_bounds.width * sx, shape_bounds.height * sy);
569 return desired;
572 Size
573 MediaElement::ArrangeOverride (Size finalSize)
575 Size arranged = finalSize;
576 Rect shape_bounds = Rect ();
577 double sx = 1.0;
578 double sy = 1.0;
580 if (mplayer)
581 shape_bounds = Rect (0, 0,
582 mplayer->GetVideoWidth (),
583 mplayer->GetVideoHeight ());
585 if (GetStretch () == StretchNone) {
586 arranged = Size (shape_bounds.x + shape_bounds.width,
587 shape_bounds.y + shape_bounds.height);
589 if (GetHorizontalAlignment () == HorizontalAlignmentStretch)
590 arranged.width = MAX (arranged.width, finalSize.width);
592 if (GetVerticalAlignment () == VerticalAlignmentStretch)
593 arranged.height = MAX (arranged.height, finalSize.height);
595 return arranged;
599 /* compute the scaling */
600 if (shape_bounds.width == 0)
601 shape_bounds.width = arranged.width;
602 if (shape_bounds.height == 0)
603 shape_bounds.height = arranged.height;
605 if (shape_bounds.width != arranged.width)
606 sx = arranged.width / shape_bounds.width;
607 if (shape_bounds.height != arranged.height)
608 sy = arranged.height / shape_bounds.height;
610 switch (GetStretch ()) {
611 case StretchUniform:
612 sx = sy = MIN (sx, sy);
613 break;
614 case StretchUniformToFill:
615 sx = sy = MAX (sx, sy);
616 break;
617 default:
618 break;
621 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
623 return arranged;
626 Rect
627 MediaElement::GetCoverageBounds ()
629 MediaPlayer *mplayer = GetMediaPlayer ();
630 Stretch stretch = GetStretch ();
632 if (IsClosed () || !mplayer || !mplayer->HasRenderedFrame ())
633 return Rect ();
635 if (stretch == StretchFill || stretch == StretchUniformToFill)
636 return bounds;
638 Rect video = Rect (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
639 cairo_matrix_t brush_xform = matrix;
641 cairo_matrix_invert (&brush_xform);
642 cairo_matrix_multiply (&brush_xform, &brush_xform, &absolute_xform);
644 video = video.Transform (&brush_xform);
645 video = video.Intersection (bounds);
647 return video;
650 void
651 MediaElement::Render (cairo_t *cr, Region *region, bool path_only)
653 Stretch stretch = GetStretch ();
654 cairo_surface_t *surface;
655 cairo_pattern_t *pattern;
657 if (!mplayer || !(surface = mplayer->GetCairoSurface ()))
658 return;
660 cairo_save (cr);
661 cairo_set_matrix (cr, &absolute_xform);
662 cairo_new_path (cr);
664 Size framework (GetActualWidth (), GetActualHeight ());
665 Rect video (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
667 if (stretch != StretchNone)
668 framework = ApplySizeConstraints (framework);
670 Rect paint (0, 0, framework.width, framework.height);
673 if (absolute_xform.xy == 0 && absolute_xform.yx == 0) {
674 //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
675 cairo_matrix_t inv = absolute_xform;
676 cairo_matrix_invert (&inv);
677 paint = paint.Transform (&absolute_xform);
678 paint = paint.RoundIn ();
679 paint = paint.Transform (&inv);
683 image_brush_compute_pattern_matrix (&matrix,
684 paint.width, paint.height,
685 video.width, video.height,
686 stretch, AlignmentXCenter,
687 AlignmentYCenter, NULL, NULL);
689 pattern = cairo_pattern_create_for_surface (surface);
691 cairo_pattern_set_matrix (pattern, &matrix);
693 cairo_set_source (cr, pattern);
694 cairo_pattern_destroy (pattern);
696 if (IsPlaying ())
697 cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
699 if (!path_only)
700 RenderLayoutClip (cr);
702 paint.Draw (cr);
704 if (!path_only)
705 cairo_fill (cr);
707 cairo_restore (cr);
710 void
711 MediaElement::BufferUnderflowHandler (PlaylistRoot *sender, EventArgs *args)
713 LOG_MEDIAELEMENT ("MediaElement::BufferUnderflow (): Switching to 'Buffering', previous_position: %" G_GUINT64_FORMAT " ms, mplayer->GetPosition (): %" G_GUINT64_FORMAT " ms\n",
714 MilliSeconds_FromPts (previous_position), MilliSeconds_FromPts (mplayer->GetPosition ()));
716 flags |= PlayRequested;
717 SetBufferingProgress (0.0);
718 Emit (BufferingProgressChangedEvent);
719 SetState (MediaStateBuffering);
720 mplayer->Pause ();
723 void
724 MediaElement::EmitStateChangedAsync ()
726 AddTickCallSafe (EmitStateChanged);
729 void
730 MediaElement::EmitStateChanged (EventObject *obj)
732 ((MediaElement *) obj)->Emit (CurrentStateChangedEvent);
735 void
736 MediaElement::SetState (MediaState state)
738 bool emit = false;
740 LOG_MEDIAELEMENT ("MediaElement::SetState (%d): New state: %s, old state: %s\n",
741 state, GetStateName (state), GetStateName (this->state));
743 // thread-safe
745 mutex.Lock ();
746 if (this->state != state) {
747 prev_state = this->state;
748 this->state = state;
749 emit = true;
751 mutex.Unlock ();
753 if (emit) // Don't emit with mutex locked.
754 EmitStateChangedAsync ();
757 void
758 MediaElement::CreatePlaylist ()
760 g_return_if_fail (mplayer == NULL);
762 mplayer = new MediaPlayer (this);
763 SetPlaylist (new PlaylistRoot (this));
766 void
767 MediaElement::SetPlaylist (PlaylistRoot *value)
769 // if playlist is something, then value must be null (and vice versa)
770 g_return_if_fail ((playlist == NULL) != (value == NULL));
772 VERIFY_MAIN_THREAD;
774 if (playlist != NULL) {
775 playlist->RemoveAllHandlers (this);
776 playlist->Dispose ();
777 playlist->unref ();
778 playlist = NULL;
779 } else {
780 playlist = value; // We assume the caller gives us a reference to the playlist
781 playlist->AddHandler (PlaylistRoot::OpeningEvent, OpeningCallback, this);
782 playlist->AddHandler (PlaylistRoot::OpenCompletedEvent, OpenCompletedCallback, this);
783 playlist->AddHandler (PlaylistRoot::SeekingEvent, SeekingCallback, this);
784 playlist->AddHandler (PlaylistRoot::SeekCompletedEvent, SeekCompletedCallback, this);
785 playlist->AddHandler (PlaylistRoot::CurrentStateChangedEvent, CurrentStateChangedCallback, this);
786 playlist->AddHandler (PlaylistRoot::MediaErrorEvent, MediaErrorCallback, this);
787 playlist->AddHandler (PlaylistRoot::MediaEndedEvent, MediaEndedCallback, this);
788 playlist->AddHandler (PlaylistRoot::BufferUnderflowEvent, BufferUnderflowCallback, this);
789 playlist->AddHandler (PlaylistRoot::DownloadProgressChangedEvent, DownloadProgressChangedCallback, this);
790 playlist->AddHandler (PlaylistRoot::BufferingProgressChangedEvent, BufferingProgressChangedCallback, this);
791 playlist->AddHandler (PlaylistRoot::PlayEvent, PlayCallback, this);
792 playlist->AddHandler (PlaylistRoot::PauseEvent, PauseCallback, this);
793 playlist->AddHandler (PlaylistRoot::StopEvent, StopCallback, this);
794 playlist->AddHandler (PlaylistRoot::EntryChangedEvent, EntryChangedCallback, this);
798 void
799 MediaElement::EntryChangedHandler (PlaylistRoot *playlist, EventArgs *args)
801 LOG_MEDIAELEMENT ("MediaElement::EntryChangedHandler ()\n");
802 flags &= ~MediaOpenedEmitted;
805 void
806 MediaElement::OpeningHandler (PlaylistRoot *playlist, EventArgs *args)
808 LOG_MEDIAELEMENT ("MediaElement::OpeningHandler ()\n");
809 VERIFY_MAIN_THREAD;
811 SetState (MediaStateOpening);
814 void
815 MediaElement::OpenCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
817 IMediaDemuxer *demuxer;
818 const char *demuxer_name;
819 PlaylistEntry *entry;
820 Media *media;
822 VERIFY_MAIN_THREAD;
824 g_return_if_fail (playlist != NULL);
825 g_return_if_fail (mplayer != NULL);
827 entry = playlist->GetCurrentPlaylistEntry ();
829 g_return_if_fail (entry != NULL);
831 media = entry->GetMedia ();
833 g_return_if_fail (media != NULL);
835 demuxer = media->GetDemuxer ();
836 demuxer_name = demuxer->GetName ();
838 LOG_MEDIAELEMENT ("MediaElement::OpenCompletedHandler (%p), demuxer name: %s\n", media, demuxer_name);
840 // Try to figure out if we're missing codecs
841 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
842 IMediaStream *stream = demuxer->GetStream (i);
843 IMediaDecoder *decoder = stream->GetDecoder ();
844 const char *decoder_name = decoder ? decoder->GetName () : NULL;
845 if (decoder_name != NULL && strcmp (decoder_name, "NullDecoder") == 0) {
846 flags |= MissingCodecs;
847 break;
851 // check if we're missing the codecs *and* if they are not installed
852 // since we could already have downloaded/installed them without refreshing the browser (leading to a crash)
853 if ((flags & MissingCodecs) && !Media::IsMSCodecsInstalled ())
854 CodecDownloader::ShowUI (GetDeployment ()->GetSurface ());
856 entry->PopulateMediaAttributes ();
857 SetProperties (media);
859 if (!(flags & MediaOpenedEmitted)) {
860 flags |= MediaOpenedEmitted;
862 PlayOrStop ();
864 // This is a workaround for MS DRT #78: it tests that download progress has changed
865 // from the latest DownloadProgressChanged event to the MediaOpened event (unless
866 // DownloadProgress is 0.0 or 1.0).
867 double progress = media->GetDownloadProgress ();
868 progress = MAX (progress, GetDownloadProgress ());
869 progress = MIN (progress + 0.00000001, 1.0);
870 SetDownloadProgress (progress);
871 Emit (MediaOpenedEvent, new RoutedEventArgs ());
872 Emit (DownloadProgressChangedEvent);
876 void
877 MediaElement::SetProperties (Media *media)
879 IMediaDemuxer *demuxer;
880 PlaylistEntry *entry;
881 Duration *natural_duration;
882 bool can_seek = true;
883 bool can_pause = true;
885 LOG_MEDIAELEMENT ("MediaElement::SetProperties (%p)\n", media);
887 g_return_if_fail (media != NULL);
888 g_return_if_fail (playlist != NULL);
890 seeked_to_position = 0;
892 demuxer = media->GetDemuxer ();
893 entry = playlist->GetCurrentPlaylistEntry ();
895 g_return_if_fail (demuxer != NULL);
896 g_return_if_fail (entry != NULL);
898 ReadMarkers (media, demuxer);
901 if (entry->GetIsLive ()) {
902 can_seek = false;
903 can_pause = false;
904 } else {
905 can_seek = entry->GetClientSkip () && demuxer->GetCanSeek ();
906 can_pause = true;
909 natural_duration = new Duration (TimeSpan_FromPts (mplayer->GetDuration ()));
910 SetCanPause (can_pause);
911 SetCanSeek (can_seek);
912 SetNaturalDuration (natural_duration);
913 SetNaturalVideoHeight ((double) mplayer->GetVideoHeight ());
914 SetNaturalVideoWidth ((double) mplayer->GetVideoWidth ());
915 SetAudioStreamCount (mplayer->GetAudioStreamCount ());
917 mplayer->SetMuted (GetIsMuted ());
918 mplayer->SetVolume (GetVolume ());
920 UpdateBounds ();
921 InvalidateMeasure ();
922 InvalidateArrange ();
925 void
926 MediaElement::SeekingHandler (PlaylistRoot *playlist, EventArgs *args)
928 LOG_MEDIAELEMENT ("MediaElement::SeekingHandler ()\n");
929 VERIFY_MAIN_THREAD;
931 SetMarkerTimeout (false);
934 void
935 MediaElement::SeekCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
937 LOG_MEDIAELEMENT ("MediaElement::SeekCompletedHandler ()\n");
938 VERIFY_MAIN_THREAD;
940 if (state == MediaStatePlaying)
941 playlist->PlayAsync ();
943 seek_to_position = -1;
944 SetMarkerTimeout (true);
947 void
948 MediaElement::PlayHandler (PlaylistRoot *playlist, EventArgs *args)
950 LOG_MEDIAELEMENT ("MediaElement::PlayHandler ()\n");
951 VERIFY_MAIN_THREAD;
953 SetMarkerTimeout (true);
955 SetState (MediaStatePlaying);
958 void
959 MediaElement::PauseHandler (PlaylistRoot *playlist, EventArgs *args)
961 LOG_MEDIAELEMENT ("MediaElement::PauseHandler ()\n");
962 VERIFY_MAIN_THREAD;
964 SetMarkerTimeout (false);
966 SetState (MediaStatePaused);
969 void
970 MediaElement::StopHandler (PlaylistRoot *playlist, EventArgs *args)
972 PlaylistEntry *entry;
974 LOG_MEDIAELEMENT ("MediaElement::StopHandler ()\n");
975 VERIFY_MAIN_THREAD;
977 g_return_if_fail (playlist != NULL);
979 entry = playlist->GetCurrentPlaylistEntry ();
981 g_return_if_fail (entry != NULL);
982 seeked_to_position = 0;
984 SetProperties (entry->GetMedia ());
986 SetMarkerTimeout (false);
987 CheckMarkers (); // check one last time.
989 SetState (MediaStateStopped);
992 void
993 MediaElement::CurrentStateChangedHandler (PlaylistRoot *playlist, EventArgs *args)
995 LOG_MEDIAELEMENT ("MediaElement::CurrentStateChangedHandler ()\n");
996 VERIFY_MAIN_THREAD;
999 void
1000 MediaElement::MediaErrorHandler (PlaylistRoot *playlist, ErrorEventArgs *args)
1002 LOG_MEDIAELEMENT ("MediaElement::MediaErrorHandler (). State: %s Message: %s\n", GetStateName (state), args ? args->GetErrorMessage() : NULL);
1003 VERIFY_MAIN_THREAD;
1005 if (state == MediaStateClosed)
1006 return;
1008 // TODO: Should ClearValue be called on these instead?
1009 SetAudioStreamCount (0);
1010 SetNaturalVideoHeight (0);
1011 SetNaturalVideoWidth (0);
1012 SetNaturalDuration (0);
1013 SetCanPause (false);
1014 SetCanSeek (false);
1015 SetDownloadProgress (0);
1016 SetDownloadProgressOffset (0);
1017 SetRenderedFramesPerSecond (0);
1018 SetDroppedFramesPerSecond (0);
1020 UpdateBounds ();
1021 InvalidateMeasure ();
1022 InvalidateArrange ();
1024 SetState (MediaStateClosed);
1026 if (args)
1027 args->ref ();
1028 Emit (MediaFailedEvent, args);
1031 void
1032 MediaElement::MediaEndedHandler (PlaylistRoot *playlist, EventArgs *args)
1034 LOG_MEDIAELEMENT ("MediaElement::MediaEndedHandler ()\n");
1035 VERIFY_MAIN_THREAD;
1037 CheckMarkers ();
1038 paused_position = GetPosition ();
1039 SetState (MediaStatePaused);
1040 Emit (MediaEndedEvent);
1043 void
1044 MediaElement::DownloadProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1046 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1048 LOG_MEDIAELEMENT ("MediaElement::DownloadProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1049 VERIFY_MAIN_THREAD;
1051 g_return_if_fail (pea != NULL);
1053 SetDownloadProgress (pea->progress);
1054 Emit (DownloadProgressChangedEvent);
1057 void
1058 MediaElement::BufferingProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1060 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1062 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1063 VERIFY_MAIN_THREAD;
1065 g_return_if_fail (pea != NULL);
1067 if (GetBufferingProgress () < pea->progress) {
1068 SetBufferingProgress (pea->progress);
1069 Emit (BufferingProgressChangedEvent);
1072 if (pea->progress >= 1.0 && GetState () == MediaStateBuffering) {
1073 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, playing...\n");
1074 PlayOrStop ();
1078 void
1079 MediaElement::MediaInvalidate ()
1081 Emit (MediaInvalidatedEvent);
1082 Invalidate ();
1085 void
1086 MediaElement::SetUriSource (Uri *uri)
1088 LOG_MEDIAELEMENT ("MediaElement::SetUriSource ('%s')\n", uri ? uri->ToString () : NULL);
1089 VERIFY_MAIN_THREAD;
1091 Reinitialize ();
1093 g_return_if_fail (playlist == NULL);
1095 if (uri != NULL && uri->originalString != NULL && uri->originalString [0] != 0) {
1096 CreatePlaylist ();
1097 char *str = uri->ToString ();
1098 playlist->GetCurrentEntry ()->InitializeWithUri (str);
1099 g_free (str);
1100 } else {
1101 UpdateBounds ();
1102 InvalidateMeasure ();
1103 InvalidateArrange ();
1107 void
1108 MediaElement::SetSource (Downloader *downloader, const char *PartName)
1110 LOG_MEDIAELEMENT ("MediaElement::SetSource (%p, '%s')\n", downloader, PartName);
1111 VERIFY_MAIN_THREAD;
1113 Reinitialize ();
1115 g_return_if_fail (downloader != NULL);
1116 g_return_if_fail (playlist == NULL);
1118 CreatePlaylist ();
1119 playlist->GetCurrentEntry ()->InitializeWithDownloader (downloader, PartName);
1122 void
1123 MediaElement::SetStreamSource (ManagedStreamCallbacks *callbacks)
1125 LOG_MEDIAELEMENT ("MediaElement::SetStreamSource (%p)\n", callbacks);
1126 VERIFY_MAIN_THREAD;
1128 Reinitialize ();
1130 g_return_if_fail (callbacks != NULL);
1131 g_return_if_fail (playlist == NULL);
1133 CreatePlaylist ();
1134 playlist->GetCurrentEntry ()->InitializeWithStream (callbacks);
1136 SetDownloadProgress (1.0);
1139 IMediaDemuxer *
1140 MediaElement::SetDemuxerSource (void *context, CloseDemuxerCallback close_demuxer, GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample,
1141 OpenDemuxerAsyncCallback open_demuxer, SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
1143 ExternalDemuxer *demuxer;
1144 Media *media;
1146 LOG_MEDIAELEMENT ("MediaElement::SetDemuxerSource (%p)\n", demuxer);
1147 VERIFY_MAIN_THREAD;
1149 Reinitialize ();
1151 g_return_val_if_fail (context != NULL, NULL);
1152 g_return_val_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL, NULL);
1153 g_return_val_if_fail (playlist == NULL, NULL);
1155 CreatePlaylist ();
1156 media = new Media (playlist);
1157 demuxer = new ExternalDemuxer (media, context, close_demuxer, get_diagnostic, get_sample, open_demuxer, seek, switch_media_stream);
1158 playlist->GetCurrentEntry ()->InitializeWithDemuxer (demuxer);
1159 media->unref ();
1161 SetDownloadProgress (1.0);
1163 return demuxer;
1166 void
1167 MediaElement::SetPlayRequested ()
1169 LOG_MEDIAELEMENT ("MediaElement::SetPlayRequested ()\n");
1170 VERIFY_MAIN_THREAD;
1172 flags |= PlayRequested;
1175 void
1176 MediaElement::PlayOrStop ()
1178 LOG_MEDIAELEMENT ("MediaElement::PlayOrPause (): GetCanPause (): %s, PlayRequested: %s, GetAutoPlay: %s, AutoPlayed: %s\n",
1179 GetCanPause () ? "true" : "false", (flags & PlayRequested) ? "true" : "false",
1180 GetAutoPlay () ? "true" : "false", (flags & AutoPlayed) ? "true" : "false");
1181 VERIFY_MAIN_THREAD;
1183 if (!GetCanPause ()) {
1184 // If we can't pause, we play
1185 SetState (MediaStatePlaying);
1186 playlist->PlayAsync ();
1187 } else if (flags & PlayRequested) {
1188 // A Play has already been requested.
1189 SetState (MediaStatePlaying);
1190 playlist->PlayAsync ();
1191 } else if (GetAutoPlay () && !(flags & AutoPlayed)) {
1192 // Autoplay us.
1193 flags |= AutoPlayed;
1194 SetState (MediaStatePlaying);
1195 playlist->PlayAsync ();
1196 } else {
1197 SetState (MediaStatePaused);
1201 void
1202 MediaElement::Pause ()
1204 LOG_MEDIAELEMENT ("MediaElement::Pause (): current state: %s\n", GetStateName (state));
1205 VERIFY_MAIN_THREAD;
1207 g_return_if_fail (playlist != NULL);
1209 switch (state) {
1210 case MediaStateOpening:// docs: No specified behaviour
1211 flags &= ~PlayRequested;
1212 return;
1213 case MediaStateClosed: // docs: No specified behaviour
1214 return;
1215 case MediaStatePaused:// docs: no-op
1216 case MediaStateBuffering:
1217 case MediaStatePlaying:
1218 case MediaStateStopped: // docs: pause
1219 paused_position = GetPosition ();
1220 SetState (MediaStatePaused);
1221 playlist->PauseAsync ();
1222 break;
1223 case MediaStateIndividualizing:
1224 case MediaStateAcquiringLicense:
1225 g_warning ("MediaElement: Invalid state.");
1226 return;
1230 void
1231 MediaElement::Play ()
1233 LOG_MEDIAELEMENT ("MediaElement::Play (): current state: %s\n", GetStateName (state));
1234 VERIFY_MAIN_THREAD;
1236 g_return_if_fail (playlist != NULL);
1238 switch (state) {
1239 case MediaStateClosed: // docs: No specified behaviour
1240 case MediaStateOpening:// docs: No specified behaviour
1241 flags |= PlayRequested;
1242 break;
1243 case MediaStatePlaying:// docs: no-op
1244 case MediaStateBuffering:
1245 case MediaStatePaused:
1246 case MediaStateStopped:
1247 SetState (MediaStatePlaying);
1248 playlist->PlayAsync ();
1249 break;
1250 case MediaStateIndividualizing:
1251 case MediaStateAcquiringLicense:
1252 g_warning ("MediaElement: Invalid state.");
1253 return;
1257 void
1258 MediaElement::Stop ()
1260 LOG_MEDIAELEMENT ("MediaElement::Stop (): current state: %s\n", GetStateName (state));
1261 VERIFY_MAIN_THREAD;
1263 if (GetSurface () == NULL)
1264 return;
1266 switch (state) {
1267 case MediaStateOpening:// docs: No specified behaviour
1268 flags &= ~PlayRequested;
1269 return;
1270 case MediaStateClosed: // docs: No specified behaviour
1271 case MediaStateStopped:// docs: no-op
1272 return;
1273 case MediaStateBuffering:
1274 case MediaStatePlaying:
1275 case MediaStatePaused: // docs: stop
1276 flags &= ~PlayRequested;
1278 LOG_MEDIAELEMENT ("MediaElement::Stop (): state: %s, IsSingleFile: %i\n", GetStateName (state), playlist->IsSingleFile ());
1280 if (!playlist->IsSingleFile ())
1281 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).
1283 SetState (MediaStateStopped);
1284 playlist->StopAsync ();
1285 break;
1286 case MediaStateIndividualizing:
1287 case MediaStateAcquiringLicense:
1288 g_warning ("MediaElement: Invalid state.");
1289 return;
1293 void
1294 MediaElement::Seek (TimeSpan to, bool force)
1296 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms)\n", to, MilliSeconds_FromPts (to));
1297 VERIFY_MAIN_THREAD;
1299 if (GetSurface () == NULL)
1300 return;
1302 if (!force && !GetCanSeek ()) {
1303 LOG_MEDIAELEMENT ("MediaElement::Seek (): CanSeek is false, not seeking\n");
1304 return;
1307 switch (state) {
1308 case MediaStateIndividualizing:
1309 case MediaStateAcquiringLicense:
1310 g_warning ("MediaElement:Seek (): Invalid state %s\n", GetStateName (state));
1311 // Fall through
1312 case MediaStateOpening:
1313 case MediaStateClosed:
1314 if (!force)
1315 return;
1316 /* fall through */
1317 case MediaStateBuffering:
1318 case MediaStatePlaying:
1319 case MediaStatePaused:
1320 case MediaStateStopped:
1321 Duration *duration = GetNaturalDuration ();
1323 if (duration->HasTimeSpan () && to > duration->GetTimeSpan ())
1324 to = duration->GetTimeSpan ();
1325 else if (to < 0)
1326 to = 0;
1328 if (!force && to == TimeSpan_FromPts (mplayer->GetPosition ()))
1329 return;
1331 previous_position = to;
1332 seek_to_position = to;
1333 seeked_to_position = to;
1334 paused_position = to;
1336 mplayer->NotifySeek (TimeSpan_ToPts (to));
1337 playlist->SeekAsync (to);
1338 Emit (MediaInvalidatedEvent);
1339 Invalidate ();
1341 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms) previous position: %" G_GUINT64_FORMAT "\n", to, MilliSeconds_FromPts (to), previous_position);
1343 break;
1347 void
1348 MediaElement::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
1350 if (args->GetId () == MediaElement::SourceProperty) {
1351 DownloaderAccessPolicy policy = MediaPolicy;
1352 Uri *uri = GetSource ();
1353 const char *location;
1355 if (uri != NULL) {
1356 if (!(location = GetDeployment ()->GetXapLocation ()) && GetSurface ())
1357 location = GetSurface ()->GetSourceLocation ();
1359 if (uri->scheme && (!strcmp (uri->scheme, "mms") || !strcmp (uri->scheme, "rtsp") || !strcmp (uri->scheme, "rtsps")))
1360 policy = StreamingPolicy;
1362 if (uri->IsInvalidPath ()) {
1363 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri")));
1364 uri = NULL;
1365 } else if (!Downloader::ValidateDownloadPolicy (location, uri, policy)) {
1366 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation")));
1367 uri = NULL;
1371 flags |= RecalculateMatrix;
1372 SetUriSource (uri);
1373 } else if (args->GetId () == MediaElement::AudioStreamIndexProperty) {
1374 if (mplayer)
1375 mplayer->SetAudioStreamIndex (args->GetNewValue()->AsInt32 ());
1376 } else if (args->GetId () == MediaElement::AutoPlayProperty) {
1377 // no state to change
1378 } else if (args->GetId () == MediaElement::BalanceProperty) {
1379 if (mplayer)
1380 mplayer->SetBalance (args->GetNewValue()->AsDouble ());
1381 } else if (args->GetId () == MediaElement::BufferingProgressProperty) {
1382 // read-only property
1383 } else if (args->GetId () == MediaElement::BufferingTimeProperty) {
1384 // TODO
1385 // Not quite sure what to do here, we could:
1386 // a) store the buffering time on the mediaplayer and let the media request it whenever it needs it
1387 // b) let the media request the buffering time from mediaelement
1388 // 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)
1389 // note that thread-safety is easier with a) (media will do the request from another thread) or c)
1391 if (media)
1392 media->SetBufferingTime (TimeSpan_ToPts (GetBufferingTime ()));
1394 } else if (args->GetId () == MediaElement::CurrentStateProperty) {
1395 // read-only property
1396 // This should really not happen, we use a property provider for this property.
1397 } else if (args->GetId () == MediaElement::IsMutedProperty) {
1398 if (mplayer)
1399 mplayer->SetMuted (args->GetNewValue()->AsBool ());
1400 } else if (args->GetId () == MediaElement::MarkersProperty) {
1402 } else if (args->GetId () == MediaElement::NaturalVideoHeightProperty) {
1403 // read-only property
1404 flags |= RecalculateMatrix;
1405 } else if (args->GetId () == MediaElement::NaturalVideoWidthProperty) {
1406 // read-only property
1407 flags |= RecalculateMatrix;
1408 } else if (args->GetId () == MediaElement::PositionProperty) {
1409 Seek (args->GetNewValue()->AsTimeSpan (), false);
1410 ClearValue (MediaElement::PositionProperty, false); // We need this, otherwise our property system will return the seeked-to position forever (MediaElementPropertyValueProvider isn't called).
1411 } else if (args->GetId () == MediaElement::VolumeProperty) {
1412 if (mplayer)
1413 mplayer->SetVolume (args->GetNewValue()->AsDouble ());
1416 if (args->GetProperty ()->GetOwnerType() != Type::MEDIAELEMENT) {
1417 // propagate to parent class
1418 FrameworkElement::OnPropertyChanged (args, error);
1419 flags |= RecalculateMatrix;
1420 return;
1423 NotifyListenersOfPropertyChange (args, error);
1426 bool
1427 MediaElement::EnableAntiAlias (void)
1429 return !(absolute_xform.xx == absolute_xform.yy && /* no rotation */
1430 (absolute_xform.yx == 0 && absolute_xform.xy == 0)); /* no skew */
1434 void
1435 MediaElement::ReportErrorOccurredCallback (EventObject *obj)
1437 MediaElement *me = (MediaElement *) obj;
1438 ErrorEventArgs *args;
1440 me->mutex.Lock ();
1441 args = me->error_args;
1442 me->error_args = NULL;
1443 me->mutex.Unlock ();
1445 me->ReportErrorOccurred (args);
1446 if (args)
1447 args->unref ();
1450 void
1451 MediaElement::ReportErrorOccurred (ErrorEventArgs *args)
1453 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred (%p)\n", args);
1455 if (!Surface::InMainThread ()) {
1456 mutex.Lock ();
1457 if (error_args)
1458 error_args->unref ();
1459 error_args = args;
1460 if (error_args)
1461 error_args->ref ();
1462 mutex.Unlock ();
1463 AddTickCallSafe (ReportErrorOccurredCallback);
1464 return;
1467 VERIFY_MAIN_THREAD;
1468 MediaErrorHandler (NULL, args);
1471 void
1472 MediaElement::ReportErrorOccurred (const char *args)
1474 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred ('%s')\n", args);
1476 ErrorEventArgs *eea = new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, g_strdup (args)));
1477 ReportErrorOccurred (eea);
1478 eea->unref ();
1482 * MediaElementPropertyValueProvider
1485 MediaElementPropertyValueProvider::MediaElementPropertyValueProvider (MediaElement *element, PropertyPrecedence precedence)
1486 : FrameworkElementProvider (element, precedence)
1488 position = NULL;
1489 current_state = NULL;
1490 rendered_frames_per_second = NULL;
1491 dropped_frames_per_second = NULL;
1494 MediaElementPropertyValueProvider::~MediaElementPropertyValueProvider ()
1496 delete position;
1497 delete current_state;
1498 delete rendered_frames_per_second;
1499 delete dropped_frames_per_second;
1502 Value *
1503 MediaElementPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
1505 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1506 VERIFY_MAIN_THREAD;
1508 if (property->GetId () == MediaElement::PositionProperty)
1509 return GetPosition ();
1511 if (property->GetId () == MediaElement::CurrentStateProperty)
1512 return GetCurrentState ();
1514 if (property->GetId () == MediaElement::DroppedFramesPerSecondProperty)
1515 return GetDroppedFramesPerSecond ();
1517 if (property->GetId () == MediaElement::RenderedFramesPerSecondProperty)
1518 return GetRenderedFramesPerSecond ();
1520 return FrameworkElementProvider::GetPropertyValue (property);
1523 Value *
1524 MediaElementPropertyValueProvider::GetDroppedFramesPerSecond ()
1526 MediaElement *element = (MediaElement *) obj;
1527 MediaPlayer *mplayer = element->GetMediaPlayer ();
1529 delete dropped_frames_per_second;
1531 if (mplayer) {
1532 dropped_frames_per_second = new Value (mplayer->GetDroppedFramesPerSecond ());
1533 } else {
1534 dropped_frames_per_second = NULL;
1537 return dropped_frames_per_second;
1540 Value *
1541 MediaElementPropertyValueProvider::GetRenderedFramesPerSecond ()
1543 MediaElement *element = (MediaElement *) obj;
1544 MediaPlayer *mplayer = element->GetMediaPlayer ();
1546 delete rendered_frames_per_second;
1548 if (mplayer) {
1549 rendered_frames_per_second = new Value (mplayer->GetRenderedFramesPerSecond ());
1550 } else {
1551 rendered_frames_per_second = NULL;
1554 return rendered_frames_per_second;
1557 Value *
1558 MediaElementPropertyValueProvider::GetCurrentState ()
1560 MediaElement *element = (MediaElement *) obj;
1562 delete current_state;
1563 current_state = new Value (element->state);
1565 return current_state;
1568 Value *
1569 MediaElementPropertyValueProvider::GetPosition ()
1571 bool use_mplayer = false;;
1572 bool use_pause = false;
1573 MediaElement *element = (MediaElement *) obj;
1574 guint64 position = TimeSpan_ToPts (element->seek_to_position);
1576 delete this->position;
1577 this->position = NULL;
1579 switch (element->state) {
1580 case MediaStateIndividualizing:
1581 case MediaStateAcquiringLicense:
1582 g_warning ("MediaElementPropertyValueProvider::GetPosition (): Invalid state.\n");
1583 // Fall through
1584 case MediaStateOpening:
1585 case MediaStateClosed:
1586 use_mplayer = false;
1587 break;
1588 case MediaStateStopped:
1589 position = 0;
1590 break;
1591 case MediaStateBuffering:
1592 case MediaStatePlaying:
1593 use_mplayer = true;
1594 break;
1595 case MediaStatePaused:
1596 use_pause = true;
1597 break;
1600 // If a seek is pending, we need to return that position.
1601 if (use_mplayer) {
1602 if (TimeSpan_FromPts (position) == -1)
1603 position = element->mplayer->GetPosition ();
1604 } else if (use_pause) {
1605 position = element->paused_position;
1608 if (position < element->seeked_to_position)
1609 position = element->seeked_to_position;
1611 if (TimeSpan_FromPts (position) == -1) {
1612 position = 0;
1613 } else {
1614 position = MIN (position, element->mplayer->GetDuration ());
1617 this->position = new Value (TimeSpan_FromPts (position), Type::TIMESPAN);
1618 return this->position;