2009-11-13 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / mediaelement.cpp
blob93e129dda102b30435dc9880361ca630b5346f3c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * mediaelement.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include "geometry.h"
16 #include "runtime.h"
17 #include "media.h"
18 #include "downloader.h"
19 #include "playlist.h"
20 #include "pipeline.h"
21 #include "pipeline-asf.h"
22 #include "pipeline-ui.h"
23 #include "mediaelement.h"
24 #include "debug.h"
25 #include "deployment.h"
26 #include "mediaplayer.h"
27 #include "timeline.h"
28 #include "timemanager.h"
31 * TimelineMarkerNode
33 class TimelineMarkerNode : public List::Node {
34 private:
35 TimelineMarker *marker;
37 public:
38 TimelineMarkerNode (TimelineMarker *marker)
40 this->marker = marker;
41 marker->ref ();
43 virtual ~TimelineMarkerNode ()
45 marker->unref ();
47 TimelineMarker *GetTimelineMarker () { return marker; }
51 * MediaElementFlags
54 enum MediaElementFlags {
55 PlayRequested = (1 << 2), // set if Play() has been requested prior to being ready
56 BufferingFailed = (1 << 3), // set if TryOpen failed to buffer the media.
57 DisableBuffering = (1 << 4), // set if we cannot give useful buffering progress
58 RecalculateMatrix = (1 << 7), // set if the patern matrix needs to be recomputed
59 MediaOpenedEmitted = (1 << 9), // set if MediaOpened has been emitted.
60 MissingCodecs = (1 << 11), // set if we have no video codecs
61 AutoPlayed = (1 << 12), // set if we've autoplayed
62 UpdatingSizeFromMedia = (1 << 13),
63 UseMediaHeight = (1 << 14),
64 UseMediaWidth = (1 << 15),
65 Initializing = (1 << 16),
69 * MediaElement
72 MediaElement::MediaElement ()
74 SetObjectType (Type::MEDIAELEMENT);
76 streamed_markers = NULL;
77 streamed_markers_queue = NULL;
78 marker_closure = NULL;
79 mplayer = NULL;
80 playlist = NULL;
81 error_args = NULL;
82 flags = UseMediaWidth | UseMediaHeight;
84 marker_timeout = 0;
85 mplayer = NULL;
87 Reinitialize ();
89 providers [PropertyPrecedence_DynamicValue] = new MediaElementPropertyValueProvider (this, PropertyPrecedence_DynamicValue);
91 // Note: BufferingTime and Position need to be set in the ctor
92 // so that ReadLocalValue() will get these values.
93 SetValue (MediaElement::BufferingTimeProperty, Value (TimeSpan_FromSeconds (5), Type::TIMESPAN));
94 SetValue (MediaElement::PositionProperty, Value (TimeSpan_FromSeconds (0), Type::TIMESPAN));
96 GetDeployment ()->AddHandler (Deployment::ShuttingDownEvent, ShuttingDownCallback, this);
99 void
100 MediaElement::Dispose ()
102 LOG_MEDIAELEMENT ("MediaElement::Dispose ()\n");
103 VERIFY_MAIN_THREAD;
105 GetDeployment ()->RemoveHandler (Deployment::ShuttingDownEvent, ShuttingDownCallback, this);
107 Reinitialize ();
108 FrameworkElement::Dispose ();
111 void
112 MediaElement::ShuttingDownHandler (Deployment *sender, EventArgs *args)
114 LOG_MEDIAELEMENT ("MediaElement::ShuttingDownHandler ()\n");
116 Reinitialize ();
119 const char *
120 MediaElement::GetStateName (MediaState state)
122 return enums_int_to_str ("MediaState", state);
125 MediaResult
126 MediaElement::AddStreamedMarkerCallback (MediaClosure *c)
128 MediaMarkerFoundClosure *closure = (MediaMarkerFoundClosure *) c;
129 MediaElement *element = (MediaElement *) closure->GetContext ();
130 MediaMarker *mmarker = closure->GetMarker ();
132 // Thread-safe
134 if (mmarker == NULL)
135 return MEDIA_FAIL;
137 element->AddStreamedMarker (mmarker);
139 return MEDIA_SUCCESS;
142 void
143 MediaElement::AddStreamedMarker (MediaMarker *mmarker)
145 guint64 pts;
146 TimelineMarker *marker;
148 g_return_if_fail (mmarker != NULL);
150 pts = mmarker->Pts ();
152 marker = new TimelineMarker ();
153 marker->SetText (mmarker->Text ());
154 marker->SetType (mmarker->Type ());
155 marker->SetTime (pts);
157 AddStreamedMarker (marker);
158 marker->unref ();
161 void
162 MediaElement::AddStreamedMarker (TimelineMarker *marker)
164 LOG_MEDIAELEMENT ("MediaElement::AddStreamedMarker (): got marker %s, %s, %" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms\n",
165 marker->GetText (), marker->GetType (), marker->GetTime (),
166 MilliSeconds_FromPts (marker->GetTime ()));
168 // thread-safe
170 mutex.Lock ();
171 if (streamed_markers_queue == NULL)
172 streamed_markers_queue = new List ();
173 streamed_markers_queue->Append (new TimelineMarkerNode (marker));
174 mutex.Unlock ();
177 void
178 MediaElement::ReadMarkers (Media *media, IMediaDemuxer *demuxer)
180 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers ()\n");
181 VERIFY_MAIN_THREAD;
183 g_return_if_fail (demuxer != NULL);
184 g_return_if_fail (media != NULL);
186 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
187 if (demuxer->GetStream (i)->GetType () == MediaTypeMarker) {
188 MarkerStream *stream = (MarkerStream *) demuxer->GetStream (i);
190 if (marker_closure == NULL)
191 marker_closure = new MediaMarkerFoundClosure (media, AddStreamedMarkerCallback, this);
193 stream->SetCallback (marker_closure);
195 MediaMarker *m = stream->Pop ();
196 while (m != NULL) {
197 AddStreamedMarker (m);
198 m->unref ();
199 m = stream->Pop ();
201 break;
205 TimelineMarkerCollection *markers = NULL;
206 MediaMarker::Node *current = (MediaMarker::Node *) media->GetMarkers ()->First ();
208 if (current == NULL) {
209 //printf ("MediaElement::ReadMarkers (): no markers.\n");
210 return;
213 markers = new TimelineMarkerCollection ();
214 while (current != NULL) {
215 TimelineMarker *new_marker = new TimelineMarker ();
216 MediaMarker *marker = current->marker;
218 new_marker->SetText (marker->Text ());
219 new_marker->SetType (marker->Type ());
220 new_marker->SetTime (TimeSpan_FromPts (marker->Pts ()));
222 Value v(new_marker);
223 markers->Add (&v);
225 new_marker->unref ();
227 current = (MediaMarker::Node *) current->next;
230 // Docs says we overwrite whatever's been loaded already.
231 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers (): setting %d markers.\n", markers->GetCount ());
232 SetMarkers (markers);
233 markers->unref ();
236 gboolean
237 MediaElement::MarkerTimeout (gpointer context)
239 VERIFY_MAIN_THREAD;
240 ((MediaElement *) context)->SetCurrentDeployment ();
241 ((MediaElement *) context)->CheckMarkers ();
242 return TRUE;
245 void
246 MediaElement::SetMarkerTimeout (bool start)
248 TimeManager *tm;
249 Surface *surface;
251 VERIFY_MAIN_THREAD;
253 surface = GetDeployment ()->GetSurface ();
255 if (surface == NULL)
256 return;
258 tm = surface->GetTimeManager ();
260 g_return_if_fail (tm != NULL);
262 if (start) {
263 if (marker_timeout == 0) {
264 marker_timeout = tm->AddTimeout (MOON_PRIORITY_DEFAULT, 33, MarkerTimeout, this);
266 } else { // stop
267 if (marker_timeout != 0) {
268 tm->RemoveTimeout (marker_timeout);
269 marker_timeout = 0;
274 void
275 MediaElement::CheckMarkers ()
277 guint64 current_position = GetPosition ();
279 LOG_MARKERS_EX ("MediaElement::CheckMarkers () current position: %" G_GUINT64_FORMAT ", previous position: %" G_GUINT64_FORMAT ")\n", current_position, previous_position);
280 VERIFY_MAIN_THREAD;
282 if (current_position > previous_position && seek_to_position == -1) {
283 guint64 tmp = previous_position;
284 // We need to set previous_position before calling CheckMarkers,
285 // as CheckMarkers may end up emitting events, causing seeks
286 // which will change previous_position.
287 previous_position = current_position;
288 CheckMarkers (tmp, current_position - 1);
292 void
293 MediaElement::CheckMarkers (guint64 from, guint64 to)
295 TimelineMarkerCollection *markers;
297 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ")\n", from, to);
298 VERIFY_MAIN_THREAD;
300 if (from == to) {
301 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). from == to\n", from, to);
302 return;
305 if (!(markers = GetMarkers ())) {
306 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "). No markers\n", from, to);
307 return;
310 if (from > to) {
311 // if from > to we've seeked backwards (last played position is after this one)
312 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));
313 return;
316 /* Check if we've got streamed markers */
317 mutex.Lock ();
318 if (streamed_markers_queue != NULL) {
319 TimelineMarkerNode *node = (TimelineMarkerNode *) streamed_markers_queue->First ();
320 while (node != NULL) {
321 if (streamed_markers == NULL)
322 streamed_markers = new TimelineMarkerCollection ();
323 streamed_markers->Add (node->GetTimelineMarker ());
324 node = (TimelineMarkerNode *) node->next;
326 streamed_markers_queue->Clear (true);
328 mutex.Unlock ();
330 CheckMarkers (from, to, markers, false);
331 CheckMarkers (from, to, streamed_markers, true);
334 void
335 MediaElement::CheckMarkers (guint64 from, guint64 to, TimelineMarkerCollection *markers, bool remove)
337 TimelineMarker *marker;
338 ArrayList emit_list;
339 Value *val = NULL;
340 guint64 pts;
341 bool emit;
343 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %p, %i). count: %i\n", from, to, markers, remove, markers ? markers->GetCount () : -1);
344 VERIFY_MAIN_THREAD;
346 // We might want to use a more intelligent algorithm here,
347 // this code only loops through all markers on every frame.
349 if (markers != NULL) {
350 for (int i = 0; i < markers->GetCount (); i++) {
351 marker = markers->GetValueAt (i)->AsTimelineMarker ();
353 if (!(val = marker->GetValue (TimelineMarker::TimeProperty)))
354 break;
356 pts = (guint64) val->AsTimeSpan ();
358 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 ());
360 emit = false;
361 if (remove) {
362 // Streamed markers. Emit these even if we passed them with up to 1 s.
363 if (from <= MilliSeconds_ToPts (1000)) {
364 emit = pts >= 0 && pts <= to;
365 } else {
366 emit = pts >= (from - MilliSeconds_ToPts (1000)) && pts <= to;
369 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",
370 from <= MilliSeconds_ToPts (1000) ? 0 : from - MilliSeconds_ToPts (1000), to, emit, pts, marker->GetText (), marker->GetType ());
371 } else {
372 // Normal markers.
373 emit = pts >= from && pts <= to;
374 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT "): Checking pts: %" G_GUINT64_FORMAT " in marker with Text = %s, Type = %s\n",
375 from, to, pts, marker->GetText (), marker->GetType ());
378 if (emit) {
379 marker->ref ();
380 emit_list.Add (marker);
382 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",
383 from, to, marker->GetText (), marker->GetType (), marker->GetTime (), MilliSeconds_FromPts (marker->GetTime ()), emit_list.GetCount ());
386 if (remove && (pts <= to || emit)) {
387 // Also delete markers we've passed by already
388 markers->RemoveAt (i);
389 i--;
394 for (int i = 0; i < emit_list.GetCount (); i++) {
395 marker = (TimelineMarker *) emit_list [i];
396 Emit (MarkerReachedEvent, new MarkerReachedEventArgs (marker));
397 marker->unref ();
401 void
402 MediaElement::SetSurface (Surface *s)
404 VERIFY_MAIN_THREAD;
406 if (GetSurface() == s)
407 return;
409 if (mplayer)
410 mplayer->SetSurface (s);
412 if (s == NULL) {
413 LOG_PIPELINE ("MediaElement::SetSurface (%p): Stopping media element since we're detached.\n", s);
414 if (mplayer)
415 mplayer->Stop (); /* this is immediate */
416 Stop (); /* this is async */
419 if (!SetSurfaceLock ())
420 return;
421 FrameworkElement::SetSurface (s);
422 SetSurfaceUnlock ();
425 void
426 MediaElement::Reinitialize ()
428 TimelineMarkerCollection *markers;
429 MediaAttributeCollection *attrs;
431 LOG_MEDIAELEMENT ("MediaElement::Reinitialize ()\n");
432 VERIFY_MAIN_THREAD;
434 if (mplayer) {
435 mplayer->Dispose ();
436 mplayer->unref ();
437 mplayer = NULL;
440 if (marker_closure) {
441 marker_closure->Dispose ();
442 marker_closure->unref ();
443 marker_closure = NULL;
446 if (playlist != NULL) {
447 playlist->Dispose ();
448 playlist->unref ();
449 playlist = NULL;
452 flags &= (PlayRequested | UseMediaHeight | UseMediaWidth);
453 flags |= RecalculateMatrix;
455 prev_state = MediaStateClosed;
456 state = MediaStateClosed;
458 last_played_pts = 0;
459 first_pts = G_MAXUINT64;
460 seek_to_position = -1;
461 seeked_to_position = 0;
462 paused_position = 0;
463 buffering_mode = 0;
465 mutex.Lock ();
466 delete streamed_markers_queue;
467 streamed_markers_queue = NULL;
468 if (streamed_markers) {
469 streamed_markers->unref ();
470 streamed_markers = NULL;
472 if (error_args) {
473 error_args->unref ();
474 error_args = NULL;
476 mutex.Unlock ();
478 previous_position = 0;
480 SetMarkerTimeout (false);
481 if ((markers = GetMarkers ()))
482 markers->Clear ();
484 if ((attrs = GetAttributes ()))
485 attrs->Clear ();
487 cairo_matrix_init_identity (&matrix);
490 bool
491 MediaElement::IsMissingCodecs ()
493 VERIFY_MAIN_THREAD;
494 return flags & MissingCodecs;
497 Point
498 MediaElement::GetTransformOrigin ()
500 Point *user_xform_origin = GetRenderTransformOrigin ();
501 double h = GetActualHeight();
502 double w = GetActualWidth ();
504 if (w == 0.0 && h == 0.0 && mplayer != NULL) {
505 h = (double) mplayer->GetVideoHeight ();
506 w = (double) mplayer->GetVideoWidth ();
509 return Point (user_xform_origin->x * w, user_xform_origin->y * h);
512 Size
513 MediaElement::ComputeActualSize ()
515 Size result = FrameworkElement::ComputeActualSize ();
516 Size specified = Size (GetWidth (), GetHeight ());
517 UIElement *parent = GetVisualParent ();
519 if (parent && !parent->Is (Type::CANVAS))
520 if (LayoutInformation::GetPreviousConstraint (this) || LayoutInformation::GetLayoutSlot (this))
521 return result;
523 if (mplayer) {
524 Size available = Size (INFINITY, INFINITY);
525 available = available.Min (specified);
526 result = MeasureOverride (available);
529 //g_warning ("actual is %g,%g", result.width, result.height);
531 return result;
534 Size
535 MediaElement::MeasureOverride (Size availableSize)
537 Size desired = availableSize;
538 Rect shape_bounds = Rect ();
539 double sx = 0.0;
540 double sy = 0.0;
542 if (mplayer)
543 shape_bounds = Rect (0,0,
544 mplayer->GetVideoWidth (),
545 mplayer->GetVideoHeight ());
547 if (GetStretch () == StretchNone)
548 return desired.Min (shape_bounds.width, shape_bounds.height);
550 /* don't stretch to infinite size */
551 if (isinf (desired.width))
552 desired.width = shape_bounds.width;
553 if (isinf (desired.height))
554 desired.height = shape_bounds.height;
556 /* compute the scaling */
557 if (shape_bounds.width > 0)
558 sx = desired.width / shape_bounds.width;
559 if (shape_bounds.height > 0)
560 sy = desired.height / shape_bounds.height;
562 switch (GetStretch ()) {
563 case StretchUniform:
564 sx = sy = MIN (sx, sy);
565 break;
566 case StretchUniformToFill:
567 sx = sy = MAX (sx, sy);
568 break;
569 default:
570 break;
573 desired = desired.Min (shape_bounds.width * sx, shape_bounds.height * sy);
575 return desired;
578 Size
579 MediaElement::ArrangeOverride (Size finalSize)
581 Size arranged = finalSize;
582 Rect shape_bounds = Rect ();
583 double sx = 1.0;
584 double sy = 1.0;
586 if (mplayer)
587 shape_bounds = Rect (0, 0,
588 mplayer->GetVideoWidth (),
589 mplayer->GetVideoHeight ());
591 if (GetStretch () == StretchNone) {
592 arranged = Size (shape_bounds.x + shape_bounds.width,
593 shape_bounds.y + shape_bounds.height);
595 if (GetHorizontalAlignment () == HorizontalAlignmentStretch)
596 arranged.width = MAX (arranged.width, finalSize.width);
598 if (GetVerticalAlignment () == VerticalAlignmentStretch)
599 arranged.height = MAX (arranged.height, finalSize.height);
601 return arranged;
605 /* compute the scaling */
606 if (shape_bounds.width == 0)
607 shape_bounds.width = arranged.width;
608 if (shape_bounds.height == 0)
609 shape_bounds.height = arranged.height;
611 if (shape_bounds.width != arranged.width)
612 sx = arranged.width / shape_bounds.width;
613 if (shape_bounds.height != arranged.height)
614 sy = arranged.height / shape_bounds.height;
616 switch (GetStretch ()) {
617 case StretchUniform:
618 sx = sy = MIN (sx, sy);
619 break;
620 case StretchUniformToFill:
621 sx = sy = MAX (sx, sy);
622 break;
623 default:
624 break;
627 arranged = Size (shape_bounds.width * sx, shape_bounds.height * sy);
629 return arranged;
632 Rect
633 MediaElement::GetCoverageBounds ()
635 MediaPlayer *mplayer = GetMediaPlayer ();
636 Stretch stretch = GetStretch ();
638 if (IsClosed () || !mplayer || !mplayer->HasRenderedFrame ())
639 return Rect ();
641 if (stretch == StretchFill || stretch == StretchUniformToFill)
642 return bounds;
644 Rect video = Rect (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
645 cairo_matrix_t brush_xform = matrix;
647 cairo_matrix_invert (&brush_xform);
648 cairo_matrix_multiply (&brush_xform, &brush_xform, &absolute_xform);
650 video = video.Transform (&brush_xform);
651 video = video.Intersection (bounds);
653 return video;
656 void
657 MediaElement::Render (cairo_t *cr, Region *region, bool path_only)
659 Stretch stretch = GetStretch ();
660 cairo_surface_t *surface;
661 cairo_pattern_t *pattern;
663 if (!mplayer || !(surface = mplayer->GetCairoSurface ()))
664 return;
666 cairo_save (cr);
667 cairo_set_matrix (cr, &absolute_xform);
668 cairo_new_path (cr);
670 Size framework (GetActualWidth (), GetActualHeight ());
671 Rect video (0, 0, mplayer->GetVideoWidth (), mplayer->GetVideoHeight ());
673 if (stretch != StretchNone)
674 framework = ApplySizeConstraints (framework);
676 Rect paint (0, 0, framework.width, framework.height);
679 if (absolute_xform.xy == 0 && absolute_xform.yx == 0) {
680 //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
681 cairo_matrix_t inv = absolute_xform;
682 cairo_matrix_invert (&inv);
683 paint = paint.Transform (&absolute_xform);
684 paint = paint.RoundIn ();
685 paint = paint.Transform (&inv);
689 image_brush_compute_pattern_matrix (&matrix,
690 paint.width, paint.height,
691 video.width, video.height,
692 stretch, AlignmentXCenter,
693 AlignmentYCenter, NULL, NULL);
695 pattern = cairo_pattern_create_for_surface (surface);
697 cairo_pattern_set_matrix (pattern, &matrix);
699 cairo_set_source (cr, pattern);
700 cairo_pattern_destroy (pattern);
702 if (IsPlaying ())
703 cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
705 if (!path_only)
706 RenderLayoutClip (cr);
708 paint.Draw (cr);
710 if (!path_only)
711 cairo_fill (cr);
713 cairo_restore (cr);
716 void
717 MediaElement::BufferUnderflowHandler (PlaylistRoot *sender, EventArgs *args)
719 LOG_MEDIAELEMENT ("MediaElement::BufferUnderflow (): Switching to 'Buffering', previous_position: %" G_GUINT64_FORMAT " ms, mplayer->GetPosition (): %" G_GUINT64_FORMAT " ms\n",
720 MilliSeconds_FromPts (previous_position), MilliSeconds_FromPts (mplayer->GetPosition ()));
722 flags |= PlayRequested;
723 SetBufferingProgress (0.0);
724 Emit (BufferingProgressChangedEvent);
725 SetState (MediaStateBuffering);
726 mplayer->Pause ();
729 void
730 MediaElement::EmitStateChangedAsync ()
732 AddTickCallSafe (EmitStateChanged);
735 void
736 MediaElement::EmitStateChanged (EventObject *obj)
738 ((MediaElement *) obj)->Emit (CurrentStateChangedEvent);
741 void
742 MediaElement::SetState (MediaState state)
744 bool emit = false;
746 LOG_MEDIAELEMENT ("MediaElement::SetState (%d): New state: %s, old state: %s\n",
747 state, GetStateName (state), GetStateName (this->state));
749 // thread-safe
751 mutex.Lock ();
752 if (this->state != state) {
753 prev_state = this->state;
754 this->state = state;
755 emit = true;
757 mutex.Unlock ();
759 if (emit) // Don't emit with mutex locked.
760 EmitStateChangedAsync ();
763 void
764 MediaElement::CreatePlaylist ()
766 g_return_if_fail (mplayer == NULL);
768 mplayer = new MediaPlayer (this);
769 SetPlaylist (new PlaylistRoot (this));
772 void
773 MediaElement::SetPlaylist (PlaylistRoot *value)
775 // if playlist is something, then value must be null (and vice versa)
776 g_return_if_fail ((playlist == NULL) != (value == NULL));
778 VERIFY_MAIN_THREAD;
780 if (playlist != NULL) {
781 playlist->RemoveAllHandlers (this);
782 playlist->Dispose ();
783 playlist->unref ();
784 playlist = NULL;
785 } else {
786 playlist = value; // We assume the caller gives us a reference to the playlist
787 playlist->AddHandler (PlaylistRoot::OpeningEvent, OpeningCallback, this);
788 playlist->AddHandler (PlaylistRoot::OpenCompletedEvent, OpenCompletedCallback, this);
789 playlist->AddHandler (PlaylistRoot::SeekingEvent, SeekingCallback, this);
790 playlist->AddHandler (PlaylistRoot::SeekCompletedEvent, SeekCompletedCallback, this);
791 playlist->AddHandler (PlaylistRoot::CurrentStateChangedEvent, CurrentStateChangedCallback, this);
792 playlist->AddHandler (PlaylistRoot::MediaErrorEvent, MediaErrorCallback, this);
793 playlist->AddHandler (PlaylistRoot::MediaEndedEvent, MediaEndedCallback, this);
794 playlist->AddHandler (PlaylistRoot::BufferUnderflowEvent, BufferUnderflowCallback, this);
795 playlist->AddHandler (PlaylistRoot::DownloadProgressChangedEvent, DownloadProgressChangedCallback, this);
796 playlist->AddHandler (PlaylistRoot::BufferingProgressChangedEvent, BufferingProgressChangedCallback, this);
797 playlist->AddHandler (PlaylistRoot::PlayEvent, PlayCallback, this);
798 playlist->AddHandler (PlaylistRoot::PauseEvent, PauseCallback, this);
799 playlist->AddHandler (PlaylistRoot::StopEvent, StopCallback, this);
800 playlist->AddHandler (PlaylistRoot::EntryChangedEvent, EntryChangedCallback, this);
804 void
805 MediaElement::EntryChangedHandler (PlaylistRoot *playlist, EventArgs *args)
807 LOG_MEDIAELEMENT ("MediaElement::EntryChangedHandler ()\n");
808 flags &= ~MediaOpenedEmitted;
811 void
812 MediaElement::OpeningHandler (PlaylistRoot *playlist, EventArgs *args)
814 LOG_MEDIAELEMENT ("MediaElement::OpeningHandler ()\n");
815 VERIFY_MAIN_THREAD;
817 flags &= ~Initializing;
818 SetState (MediaStateOpening);
821 void
822 MediaElement::OpenCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
824 IMediaDemuxer *demuxer;
825 const char *demuxer_name;
826 PlaylistEntry *entry;
827 Media *media;
829 VERIFY_MAIN_THREAD;
831 g_return_if_fail (playlist != NULL);
832 g_return_if_fail (mplayer != NULL);
834 entry = playlist->GetCurrentPlaylistEntry ();
836 g_return_if_fail (entry != NULL);
838 media = entry->GetMedia ();
840 g_return_if_fail (media != NULL);
842 demuxer = media->GetDemuxer ();
843 demuxer_name = demuxer->GetName ();
845 LOG_MEDIAELEMENT ("MediaElement::OpenCompletedHandler (%p), demuxer name: %s\n", media, demuxer_name);
847 // Try to figure out if we're missing codecs
848 for (int i = 0; i < demuxer->GetStreamCount (); i++) {
849 IMediaStream *stream = demuxer->GetStream (i);
850 IMediaDecoder *decoder = stream->GetDecoder ();
851 const char *decoder_name = decoder ? decoder->GetName () : NULL;
852 if (decoder_name != NULL && strcmp (decoder_name, "NullDecoder") == 0) {
853 flags |= MissingCodecs;
854 break;
858 // check if we're missing the codecs *and* if they are not installed
859 // since we could already have downloaded/installed them without refreshing the browser (leading to a crash)
860 if ((flags & MissingCodecs) && !Media::IsMSCodecsInstalled ())
861 CodecDownloader::ShowUI (GetDeployment ()->GetSurface ());
863 entry->PopulateMediaAttributes ();
864 SetProperties (media);
866 if (!(flags & MediaOpenedEmitted)) {
867 flags |= MediaOpenedEmitted;
869 PlayOrStop ();
871 // This is a workaround for MS DRT #78: it tests that download progress has changed
872 // from the latest DownloadProgressChanged event to the MediaOpened event (unless
873 // DownloadProgress is 0.0 or 1.0).
874 double progress = media->GetDownloadProgress ();
875 progress = MAX (progress, GetDownloadProgress ());
876 progress = MIN (progress + 0.00000001, 1.0);
877 SetDownloadProgress (progress);
878 Emit (MediaOpenedEvent, new RoutedEventArgs ());
879 Emit (DownloadProgressChangedEvent);
883 void
884 MediaElement::SetProperties (Media *media)
886 IMediaDemuxer *demuxer;
887 PlaylistEntry *entry;
888 Duration *natural_duration;
889 bool can_seek = true;
890 bool can_pause = true;
892 LOG_MEDIAELEMENT ("MediaElement::SetProperties (%p)\n", media);
894 g_return_if_fail (media != NULL);
895 g_return_if_fail (playlist != NULL);
897 seeked_to_position = 0;
899 demuxer = media->GetDemuxer ();
900 entry = playlist->GetCurrentPlaylistEntry ();
902 g_return_if_fail (demuxer != NULL);
903 g_return_if_fail (entry != NULL);
905 ReadMarkers (media, demuxer);
908 if (entry->GetIsLive ()) {
909 can_seek = false;
910 can_pause = false;
911 } else {
912 can_seek = entry->GetClientSkip () && demuxer->GetCanSeek ();
913 can_pause = true;
916 natural_duration = new Duration (TimeSpan_FromPts (mplayer->GetDuration ()));
917 SetCanPause (can_pause);
918 SetCanSeek (can_seek);
919 SetNaturalDuration (natural_duration);
920 SetNaturalVideoHeight ((double) mplayer->GetVideoHeight ());
921 SetNaturalVideoWidth ((double) mplayer->GetVideoWidth ());
922 SetAudioStreamCount (mplayer->GetAudioStreamCount ());
924 mplayer->SetMuted (GetIsMuted ());
925 mplayer->SetVolume (GetVolume ());
927 UpdateBounds ();
928 InvalidateMeasure ();
929 InvalidateArrange ();
932 void
933 MediaElement::SeekingHandler (PlaylistRoot *playlist, EventArgs *args)
935 LOG_MEDIAELEMENT ("MediaElement::SeekingHandler ()\n");
936 VERIFY_MAIN_THREAD;
938 SetMarkerTimeout (false);
941 void
942 MediaElement::SeekCompletedHandler (PlaylistRoot *playlist, EventArgs *args)
944 LOG_MEDIAELEMENT ("MediaElement::SeekCompletedHandler ()\n");
945 VERIFY_MAIN_THREAD;
947 if (state == MediaStatePlaying)
948 playlist->PlayAsync ();
950 seek_to_position = -1;
951 SetMarkerTimeout (true);
954 void
955 MediaElement::PlayHandler (PlaylistRoot *playlist, EventArgs *args)
957 LOG_MEDIAELEMENT ("MediaElement::PlayHandler ()\n");
958 VERIFY_MAIN_THREAD;
960 SetMarkerTimeout (true);
962 SetState (MediaStatePlaying);
965 void
966 MediaElement::PauseHandler (PlaylistRoot *playlist, EventArgs *args)
968 LOG_MEDIAELEMENT ("MediaElement::PauseHandler ()\n");
969 VERIFY_MAIN_THREAD;
971 SetMarkerTimeout (false);
973 SetState (MediaStatePaused);
976 void
977 MediaElement::StopHandler (PlaylistRoot *playlist, EventArgs *args)
979 PlaylistEntry *entry;
981 LOG_MEDIAELEMENT ("MediaElement::StopHandler ()\n");
982 VERIFY_MAIN_THREAD;
984 g_return_if_fail (playlist != NULL);
986 entry = playlist->GetCurrentPlaylistEntry ();
988 g_return_if_fail (entry != NULL);
989 seeked_to_position = 0;
991 SetProperties (entry->GetMedia ());
993 SetMarkerTimeout (false);
994 CheckMarkers (); // check one last time.
996 SetState (MediaStateStopped);
999 void
1000 MediaElement::CurrentStateChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1002 LOG_MEDIAELEMENT ("MediaElement::CurrentStateChangedHandler ()\n");
1003 VERIFY_MAIN_THREAD;
1006 void
1007 MediaElement::MediaErrorHandler (PlaylistRoot *playlist, ErrorEventArgs *args)
1009 LOG_MEDIAELEMENT ("MediaElement::MediaErrorHandler (). State: %s Message: %s\n", GetStateName (state), args ? args->GetErrorMessage() : NULL);
1010 VERIFY_MAIN_THREAD;
1012 if (state == MediaStateClosed && !(flags & Initializing))
1013 return;
1015 // TODO: Should ClearValue be called on these instead?
1016 SetAudioStreamCount (0);
1017 SetNaturalVideoHeight (0);
1018 SetNaturalVideoWidth (0);
1019 SetNaturalDuration (0);
1020 SetCanPause (false);
1021 SetCanSeek (false);
1022 SetDownloadProgress (0);
1023 SetDownloadProgressOffset (0);
1024 SetRenderedFramesPerSecond (0);
1025 SetDroppedFramesPerSecond (0);
1027 UpdateBounds ();
1028 InvalidateMeasure ();
1029 InvalidateArrange ();
1031 SetState (MediaStateClosed);
1033 if (args)
1034 args->ref ();
1036 if (flags & Initializing)
1037 EmitAsync (MediaFailedEvent, args);
1038 else
1039 Emit (MediaFailedEvent, args);
1041 flags &= ~Initializing;
1044 void
1045 MediaElement::MediaEndedHandler (PlaylistRoot *playlist, EventArgs *args)
1047 LOG_MEDIAELEMENT ("MediaElement::MediaEndedHandler ()\n");
1048 VERIFY_MAIN_THREAD;
1050 CheckMarkers ();
1051 paused_position = GetPosition ();
1052 SetState (MediaStatePaused);
1053 Emit (MediaEndedEvent);
1056 void
1057 MediaElement::DownloadProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1059 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1061 LOG_MEDIAELEMENT ("MediaElement::DownloadProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1062 VERIFY_MAIN_THREAD;
1064 g_return_if_fail (pea != NULL);
1066 SetDownloadProgress (pea->progress);
1067 Emit (DownloadProgressChangedEvent);
1070 void
1071 MediaElement::BufferingProgressChangedHandler (PlaylistRoot *playlist, EventArgs *args)
1073 ProgressEventArgs *pea = (ProgressEventArgs *) args;
1075 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): %f\n", pea ? pea->progress : -1.0);
1076 VERIFY_MAIN_THREAD;
1078 g_return_if_fail (pea != NULL);
1080 if (GetBufferingProgress () < pea->progress) {
1081 SetBufferingProgress (pea->progress);
1082 Emit (BufferingProgressChangedEvent);
1085 if (pea->progress >= 1.0 && GetState () == MediaStateBuffering) {
1086 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, playing...\n");
1087 PlayOrStop ();
1091 void
1092 MediaElement::MediaInvalidate ()
1094 Emit (MediaInvalidatedEvent);
1095 Invalidate ();
1098 void
1099 MediaElement::SetUriSource (Uri *uri)
1101 LOG_MEDIAELEMENT ("MediaElement::SetUriSource ('%s')\n", uri ? uri->ToString () : NULL);
1102 VERIFY_MAIN_THREAD;
1104 Reinitialize ();
1106 g_return_if_fail (playlist == NULL);
1108 flags |= Initializing;
1110 if (uri != NULL && uri->originalString != NULL && uri->originalString [0] != 0) {
1111 CreatePlaylist ();
1112 char *str = uri->ToString ();
1113 playlist->GetCurrentEntry ()->InitializeWithUri (str);
1114 g_free (str);
1115 } else {
1116 UpdateBounds ();
1117 InvalidateMeasure ();
1118 InvalidateArrange ();
1121 flags &= ~Initializing;
1124 void
1125 MediaElement::SetSource (Downloader *downloader, const char *PartName)
1127 LOG_MEDIAELEMENT ("MediaElement::SetSource (%p, '%s')\n", downloader, PartName);
1128 VERIFY_MAIN_THREAD;
1130 Reinitialize ();
1132 g_return_if_fail (downloader != NULL);
1133 g_return_if_fail (playlist == NULL);
1135 flags |= Initializing;
1137 CreatePlaylist ();
1138 playlist->GetCurrentEntry ()->InitializeWithDownloader (downloader, PartName);
1140 flags &= ~Initializing;
1143 void
1144 MediaElement::SetStreamSource (ManagedStreamCallbacks *callbacks)
1146 LOG_MEDIAELEMENT ("MediaElement::SetStreamSource (%p)\n", callbacks);
1147 VERIFY_MAIN_THREAD;
1149 Reinitialize ();
1151 g_return_if_fail (callbacks != NULL);
1152 g_return_if_fail (playlist == NULL);
1154 flags |= Initializing;
1156 CreatePlaylist ();
1157 playlist->GetCurrentEntry ()->InitializeWithStream (callbacks);
1159 SetDownloadProgress (1.0);
1161 flags &= ~Initializing;
1164 IMediaDemuxer *
1165 MediaElement::SetDemuxerSource (void *context, CloseDemuxerCallback close_demuxer, GetDiagnosticAsyncCallback get_diagnostic, GetFrameAsyncCallback get_sample,
1166 OpenDemuxerAsyncCallback open_demuxer, SeekAsyncCallback seek, SwitchMediaStreamAsyncCallback switch_media_stream)
1168 ExternalDemuxer *demuxer;
1169 Media *media;
1171 LOG_MEDIAELEMENT ("MediaElement::SetDemuxerSource (%p)\n", demuxer);
1172 VERIFY_MAIN_THREAD;
1174 Reinitialize ();
1176 g_return_val_if_fail (context != NULL, NULL);
1177 g_return_val_if_fail (close_demuxer != NULL && get_diagnostic != NULL && get_sample != NULL && open_demuxer != NULL && seek != NULL && switch_media_stream != NULL, NULL);
1178 g_return_val_if_fail (playlist == NULL, NULL);
1180 flags |= Initializing;
1182 CreatePlaylist ();
1183 media = new Media (playlist);
1184 demuxer = new ExternalDemuxer (media, context, close_demuxer, get_diagnostic, get_sample, open_demuxer, seek, switch_media_stream);
1185 playlist->GetCurrentEntry ()->InitializeWithDemuxer (demuxer);
1186 media->unref ();
1188 SetDownloadProgress (1.0);
1190 flags &= ~Initializing;
1192 return demuxer;
1195 void
1196 MediaElement::SetPlayRequested ()
1198 LOG_MEDIAELEMENT ("MediaElement::SetPlayRequested ()\n");
1199 VERIFY_MAIN_THREAD;
1201 flags |= PlayRequested;
1204 void
1205 MediaElement::PlayOrStop ()
1207 LOG_MEDIAELEMENT ("MediaElement::PlayOrPause (): GetCanPause (): %s, PlayRequested: %s, GetAutoPlay: %s, AutoPlayed: %s\n",
1208 GetCanPause () ? "true" : "false", (flags & PlayRequested) ? "true" : "false",
1209 GetAutoPlay () ? "true" : "false", (flags & AutoPlayed) ? "true" : "false");
1210 VERIFY_MAIN_THREAD;
1212 if (!GetCanPause ()) {
1213 // If we can't pause, we play
1214 SetState (MediaStatePlaying);
1215 playlist->PlayAsync ();
1216 } else if (flags & PlayRequested) {
1217 // A Play has already been requested.
1218 SetState (MediaStatePlaying);
1219 playlist->PlayAsync ();
1220 } else if (GetAutoPlay () && !(flags & AutoPlayed)) {
1221 // Autoplay us.
1222 flags |= AutoPlayed;
1223 SetState (MediaStatePlaying);
1224 playlist->PlayAsync ();
1225 } else {
1226 SetState (MediaStatePaused);
1230 void
1231 MediaElement::Pause ()
1233 LOG_MEDIAELEMENT ("MediaElement::Pause (): current state: %s\n", GetStateName (state));
1234 VERIFY_MAIN_THREAD;
1236 if (playlist == NULL)
1237 return;
1239 switch (state) {
1240 case MediaStateOpening:// docs: No specified behaviour
1241 flags &= ~PlayRequested;
1242 return;
1243 case MediaStateClosed: // docs: No specified behaviour
1244 return;
1245 case MediaStatePaused:// docs: no-op
1246 case MediaStateBuffering:
1247 case MediaStatePlaying:
1248 case MediaStateStopped: // docs: pause
1249 paused_position = GetPosition ();
1250 SetState (MediaStatePaused);
1251 playlist->PauseAsync ();
1252 break;
1253 case MediaStateIndividualizing:
1254 case MediaStateAcquiringLicense:
1255 g_warning ("MediaElement: Invalid state.");
1256 return;
1260 void
1261 MediaElement::Play ()
1263 LOG_MEDIAELEMENT ("MediaElement::Play (): current state: %s\n", GetStateName (state));
1264 VERIFY_MAIN_THREAD;
1266 g_return_if_fail (playlist != NULL);
1268 switch (state) {
1269 case MediaStateClosed: // docs: No specified behaviour
1270 case MediaStateOpening:// docs: No specified behaviour
1271 flags |= PlayRequested;
1272 break;
1273 case MediaStatePlaying:// docs: no-op
1274 case MediaStateBuffering:
1275 case MediaStatePaused:
1276 case MediaStateStopped:
1277 SetState (MediaStatePlaying);
1278 playlist->PlayAsync ();
1279 break;
1280 case MediaStateIndividualizing:
1281 case MediaStateAcquiringLicense:
1282 g_warning ("MediaElement: Invalid state.");
1283 return;
1287 void
1288 MediaElement::Stop ()
1290 LOG_MEDIAELEMENT ("MediaElement::Stop (): current state: %s\n", GetStateName (state));
1291 VERIFY_MAIN_THREAD;
1293 if (GetSurface () == NULL)
1294 return;
1296 switch (state) {
1297 case MediaStateOpening:// docs: No specified behaviour
1298 flags &= ~PlayRequested;
1299 return;
1300 case MediaStateClosed: // docs: No specified behaviour
1301 case MediaStateStopped:// docs: no-op
1302 return;
1303 case MediaStateBuffering:
1304 case MediaStatePlaying:
1305 case MediaStatePaused: // docs: stop
1306 flags &= ~PlayRequested;
1308 LOG_MEDIAELEMENT ("MediaElement::Stop (): state: %s, IsSingleFile: %i\n", GetStateName (state), playlist->IsSingleFile ());
1310 if (!playlist->IsSingleFile ())
1311 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).
1313 SetState (MediaStateStopped);
1314 playlist->StopAsync ();
1315 break;
1316 case MediaStateIndividualizing:
1317 case MediaStateAcquiringLicense:
1318 g_warning ("MediaElement: Invalid state.");
1319 return;
1323 void
1324 MediaElement::Seek (TimeSpan to, bool force)
1326 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms)\n", to, MilliSeconds_FromPts (to));
1327 VERIFY_MAIN_THREAD;
1329 if (GetSurface () == NULL)
1330 return;
1332 if (!force && !GetCanSeek ()) {
1333 LOG_MEDIAELEMENT ("MediaElement::Seek (): CanSeek is false, not seeking\n");
1334 return;
1337 switch (state) {
1338 case MediaStateIndividualizing:
1339 case MediaStateAcquiringLicense:
1340 g_warning ("MediaElement:Seek (): Invalid state %s\n", GetStateName (state));
1341 // Fall through
1342 case MediaStateOpening:
1343 case MediaStateClosed:
1344 if (!force)
1345 return;
1346 /* fall through */
1347 case MediaStateBuffering:
1348 case MediaStatePlaying:
1349 case MediaStatePaused:
1350 case MediaStateStopped:
1351 Duration *duration = GetNaturalDuration ();
1353 if (duration->HasTimeSpan () && to > duration->GetTimeSpan ())
1354 to = duration->GetTimeSpan ();
1355 else if (to < 0)
1356 to = 0;
1358 if (!force && to == TimeSpan_FromPts (mplayer->GetPosition ()))
1359 return;
1361 previous_position = to;
1362 seek_to_position = to;
1363 seeked_to_position = to;
1364 paused_position = to;
1366 mplayer->NotifySeek (TimeSpan_ToPts (to));
1367 playlist->SeekAsync (to);
1368 Emit (MediaInvalidatedEvent);
1369 Invalidate ();
1371 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT " = %" G_GUINT64_FORMAT " ms) previous position: %" G_GUINT64_FORMAT "\n", to, MilliSeconds_FromPts (to), previous_position);
1373 break;
1377 void
1378 MediaElement::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
1380 if (args->GetId () == MediaElement::SourceProperty) {
1381 DownloaderAccessPolicy policy = MediaPolicy;
1382 Uri *uri = GetSource ();
1383 const char *location;
1385 if (uri != NULL) {
1386 if (!(location = GetDeployment ()->GetXapLocation ()) && GetSurface ())
1387 location = GetSurface ()->GetSourceLocation ();
1389 if (uri->scheme && (!strcmp (uri->scheme, "mms") || !strcmp (uri->scheme, "rtsp") || !strcmp (uri->scheme, "rtsps")))
1390 policy = StreamingPolicy;
1392 if (uri->IsInvalidPath ()) {
1393 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri")));
1394 uri = NULL;
1395 } else if (!Downloader::ValidateDownloadPolicy (location, uri, policy)) {
1396 EmitAsync (MediaFailedEvent, new ErrorEventArgs (MediaError, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE, 0, "Security Policy Violation")));
1397 uri = NULL;
1401 flags |= RecalculateMatrix;
1402 SetUriSource (uri);
1403 } else if (args->GetId () == MediaElement::AudioStreamIndexProperty) {
1404 if (mplayer)
1405 mplayer->SetAudioStreamIndex (args->GetNewValue()->AsInt32 ());
1406 } else if (args->GetId () == MediaElement::AutoPlayProperty) {
1407 // no state to change
1408 } else if (args->GetId () == MediaElement::BalanceProperty) {
1409 if (mplayer)
1410 mplayer->SetBalance (args->GetNewValue()->AsDouble ());
1411 } else if (args->GetId () == MediaElement::BufferingProgressProperty) {
1412 // read-only property
1413 } else if (args->GetId () == MediaElement::BufferingTimeProperty) {
1414 // TODO
1415 // Not quite sure what to do here, we could:
1416 // a) store the buffering time on the mediaplayer and let the media request it whenever it needs it
1417 // b) let the media request the buffering time from mediaelement
1418 // 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)
1419 // note that thread-safety is easier with a) (media will do the request from another thread) or c)
1421 if (media)
1422 media->SetBufferingTime (TimeSpan_ToPts (GetBufferingTime ()));
1424 } else if (args->GetId () == MediaElement::CurrentStateProperty) {
1425 // read-only property
1426 // This should really not happen, we use a property provider for this property.
1427 } else if (args->GetId () == MediaElement::IsMutedProperty) {
1428 if (mplayer)
1429 mplayer->SetMuted (args->GetNewValue()->AsBool ());
1430 } else if (args->GetId () == MediaElement::MarkersProperty) {
1432 } else if (args->GetId () == MediaElement::NaturalVideoHeightProperty) {
1433 // read-only property
1434 flags |= RecalculateMatrix;
1435 } else if (args->GetId () == MediaElement::NaturalVideoWidthProperty) {
1436 // read-only property
1437 flags |= RecalculateMatrix;
1438 } else if (args->GetId () == MediaElement::PositionProperty) {
1439 Seek (args->GetNewValue()->AsTimeSpan (), false);
1440 ClearValue (MediaElement::PositionProperty, false); // We need this, otherwise our property system will return the seeked-to position forever (MediaElementPropertyValueProvider isn't called).
1441 } else if (args->GetId () == MediaElement::VolumeProperty) {
1442 if (mplayer)
1443 mplayer->SetVolume (args->GetNewValue()->AsDouble ());
1446 if (args->GetProperty ()->GetOwnerType() != Type::MEDIAELEMENT) {
1447 // propagate to parent class
1448 FrameworkElement::OnPropertyChanged (args, error);
1449 flags |= RecalculateMatrix;
1450 return;
1453 NotifyListenersOfPropertyChange (args, error);
1456 bool
1457 MediaElement::EnableAntiAlias (void)
1459 return !(absolute_xform.xx == absolute_xform.yy && /* no rotation */
1460 (absolute_xform.yx == 0 && absolute_xform.xy == 0)); /* no skew */
1464 void
1465 MediaElement::ReportErrorOccurredCallback (EventObject *obj)
1467 MediaElement *me = (MediaElement *) obj;
1468 ErrorEventArgs *args;
1470 me->mutex.Lock ();
1471 args = me->error_args;
1472 me->error_args = NULL;
1473 me->mutex.Unlock ();
1475 me->ReportErrorOccurred (args);
1476 if (args)
1477 args->unref ();
1480 void
1481 MediaElement::ReportErrorOccurred (ErrorEventArgs *args)
1483 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred (%p)\n", args);
1485 if (!Surface::InMainThread ()) {
1486 mutex.Lock ();
1487 if (error_args)
1488 error_args->unref ();
1489 error_args = args;
1490 if (error_args)
1491 error_args->ref ();
1492 mutex.Unlock ();
1493 AddTickCallSafe (ReportErrorOccurredCallback);
1494 return;
1497 VERIFY_MAIN_THREAD;
1498 MediaErrorHandler (NULL, args);
1501 void
1502 MediaElement::ReportErrorOccurred (const char *args)
1504 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred ('%s')\n", args);
1506 ErrorEventArgs *eea = new ErrorEventArgs (MediaError, MoonError (MoonError::EXCEPTION, 3001, g_strdup (args)));
1507 ReportErrorOccurred (eea);
1508 eea->unref ();
1512 * MediaElementPropertyValueProvider
1515 MediaElementPropertyValueProvider::MediaElementPropertyValueProvider (MediaElement *element, PropertyPrecedence precedence)
1516 : FrameworkElementProvider (element, precedence)
1518 position = NULL;
1519 current_state = NULL;
1520 rendered_frames_per_second = NULL;
1521 dropped_frames_per_second = NULL;
1524 MediaElementPropertyValueProvider::~MediaElementPropertyValueProvider ()
1526 delete position;
1527 delete current_state;
1528 delete rendered_frames_per_second;
1529 delete dropped_frames_per_second;
1532 Value *
1533 MediaElementPropertyValueProvider::GetPropertyValue (DependencyProperty *property)
1535 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1536 VERIFY_MAIN_THREAD;
1538 if (property->GetId () == MediaElement::PositionProperty)
1539 return GetPosition ();
1541 if (property->GetId () == MediaElement::CurrentStateProperty)
1542 return GetCurrentState ();
1544 if (property->GetId () == MediaElement::DroppedFramesPerSecondProperty)
1545 return GetDroppedFramesPerSecond ();
1547 if (property->GetId () == MediaElement::RenderedFramesPerSecondProperty)
1548 return GetRenderedFramesPerSecond ();
1550 return FrameworkElementProvider::GetPropertyValue (property);
1553 Value *
1554 MediaElementPropertyValueProvider::GetDroppedFramesPerSecond ()
1556 MediaElement *element = (MediaElement *) obj;
1557 MediaPlayer *mplayer = element->GetMediaPlayer ();
1559 delete dropped_frames_per_second;
1561 if (mplayer) {
1562 dropped_frames_per_second = new Value (mplayer->GetDroppedFramesPerSecond ());
1563 } else {
1564 dropped_frames_per_second = NULL;
1567 return dropped_frames_per_second;
1570 Value *
1571 MediaElementPropertyValueProvider::GetRenderedFramesPerSecond ()
1573 MediaElement *element = (MediaElement *) obj;
1574 MediaPlayer *mplayer = element->GetMediaPlayer ();
1576 delete rendered_frames_per_second;
1578 if (mplayer) {
1579 rendered_frames_per_second = new Value (mplayer->GetRenderedFramesPerSecond ());
1580 } else {
1581 rendered_frames_per_second = NULL;
1584 return rendered_frames_per_second;
1587 Value *
1588 MediaElementPropertyValueProvider::GetCurrentState ()
1590 MediaElement *element = (MediaElement *) obj;
1592 delete current_state;
1593 current_state = new Value (element->state);
1595 return current_state;
1598 Value *
1599 MediaElementPropertyValueProvider::GetPosition ()
1601 bool use_mplayer = false;;
1602 bool use_pause = false;
1603 MediaElement *element = (MediaElement *) obj;
1604 guint64 position = TimeSpan_ToPts (element->seek_to_position);
1606 delete this->position;
1607 this->position = NULL;
1609 switch (element->state) {
1610 case MediaStateIndividualizing:
1611 case MediaStateAcquiringLicense:
1612 g_warning ("MediaElementPropertyValueProvider::GetPosition (): Invalid state.\n");
1613 // Fall through
1614 case MediaStateOpening:
1615 case MediaStateClosed:
1616 use_mplayer = false;
1617 break;
1618 case MediaStateStopped:
1619 position = 0;
1620 break;
1621 case MediaStateBuffering:
1622 case MediaStatePlaying:
1623 use_mplayer = true;
1624 break;
1625 case MediaStatePaused:
1626 use_pause = true;
1627 break;
1630 // If a seek is pending, we need to return that position.
1631 if (use_mplayer) {
1632 if (TimeSpan_FromPts (position) == -1)
1633 position = element->mplayer->GetPosition ();
1634 } else if (use_pause) {
1635 position = element->paused_position;
1638 if (position < element->seeked_to_position)
1639 position = element->seeked_to_position;
1641 if (TimeSpan_FromPts (position) == -1) {
1642 position = 0;
1643 } else {
1644 position = MIN (position, element->mplayer->GetDuration ());
1647 this->position = new Value (TimeSpan_FromPts (position), Type::TIMESPAN);
1648 return this->position;