1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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.
18 #include "downloader.h"
21 #include "pipeline-asf.h"
22 #include "pipeline-ui.h"
23 #include "mediaelement.h"
25 #include "deployment.h"
26 #include "mediaplayer.h"
28 #include "timemanager.h"
33 class TimelineMarkerNode
: public List::Node
{
35 TimelineMarker
*marker
;
38 TimelineMarkerNode (TimelineMarker
*marker
)
40 this->marker
= marker
;
43 virtual ~TimelineMarkerNode ()
47 TimelineMarker
*GetTimelineMarker () { return marker
; }
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),
72 MediaElement::MediaElement ()
74 SetObjectType (Type::MEDIAELEMENT
);
77 last_quality_level_change_position
= 0;
78 streamed_markers
= NULL
;
79 streamed_markers_queue
= NULL
;
80 marker_closure
= NULL
;
84 flags
= UseMediaWidth
| UseMediaHeight
;
85 detached_state
= MediaStateClosed
;
92 providers
[PropertyPrecedence_DynamicValue
] = new MediaElementPropertyValueProvider (this, PropertyPrecedence_DynamicValue
);
94 // Note: BufferingTime and Position need to be set in the ctor
95 // so that ReadLocalValue() will get these values.
96 SetValue (MediaElement::BufferingTimeProperty
, Value (TimeSpan_FromSeconds (5), Type::TIMESPAN
));
97 SetValue (MediaElement::PositionProperty
, Value (TimeSpan_FromSeconds (0), Type::TIMESPAN
));
99 GetDeployment ()->AddHandler (Deployment::ShuttingDownEvent
, ShuttingDownCallback
, this);
103 MediaElement::Dispose ()
105 LOG_MEDIAELEMENT ("MediaElement::Dispose ()\n");
108 GetDeployment ()->RemoveHandler (Deployment::ShuttingDownEvent
, ShuttingDownCallback
, this);
111 FrameworkElement::Dispose ();
115 MediaElement::ShuttingDownHandler (Deployment
*sender
, EventArgs
*args
)
117 LOG_MEDIAELEMENT ("MediaElement::ShuttingDownHandler ()\n");
123 MediaElement::GetStateName (MediaState state
)
125 return enums_int_to_str ("MediaState", state
);
129 MediaElement::AddStreamedMarkerCallback (MediaClosure
*c
)
131 MediaMarkerFoundClosure
*closure
= (MediaMarkerFoundClosure
*) c
;
132 MediaElement
*element
= (MediaElement
*) closure
->GetContext ();
133 MediaMarker
*mmarker
= closure
->GetMarker ();
140 element
->AddStreamedMarker (mmarker
);
142 return MEDIA_SUCCESS
;
146 MediaElement::AddStreamedMarker (MediaMarker
*mmarker
)
149 TimelineMarker
*marker
;
151 g_return_if_fail (mmarker
!= NULL
);
153 pts
= mmarker
->Pts ();
155 marker
= new TimelineMarker ();
156 marker
->SetText (mmarker
->Text ());
157 marker
->SetType (mmarker
->Type ());
158 marker
->SetTime (pts
);
160 AddStreamedMarker (marker
);
165 MediaElement::AddStreamedMarker (TimelineMarker
*marker
)
167 LOG_MEDIAELEMENT ("MediaElement::AddStreamedMarker (): got marker %s, %s, %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms\n",
168 marker
->GetText (), marker
->GetType (), marker
->GetTime (),
169 MilliSeconds_FromPts (marker
->GetTime ()));
174 if (streamed_markers_queue
== NULL
)
175 streamed_markers_queue
= new List ();
176 streamed_markers_queue
->Append (new TimelineMarkerNode (marker
));
181 MediaElement::ReadMarkers (Media
*media
, IMediaDemuxer
*demuxer
)
183 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers ()\n");
186 g_return_if_fail (demuxer
!= NULL
);
187 g_return_if_fail (media
!= NULL
);
189 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
190 if (demuxer
->GetStream (i
)->GetType () == MediaTypeMarker
) {
191 MarkerStream
*stream
= (MarkerStream
*) demuxer
->GetStream (i
);
193 if (marker_closure
== NULL
)
194 marker_closure
= new MediaMarkerFoundClosure (media
, AddStreamedMarkerCallback
, this);
196 stream
->SetCallback (marker_closure
);
198 MediaMarker
*m
= stream
->Pop ();
200 AddStreamedMarker (m
);
208 TimelineMarkerCollection
*markers
= NULL
;
209 MediaMarker::Node
*current
= (MediaMarker::Node
*) media
->GetMarkers ()->First ();
211 if (current
== NULL
) {
212 //printf ("MediaElement::ReadMarkers (): no markers.\n");
216 markers
= new TimelineMarkerCollection ();
217 while (current
!= NULL
) {
218 TimelineMarker
*new_marker
= new TimelineMarker ();
219 MediaMarker
*marker
= current
->marker
;
221 new_marker
->SetText (marker
->Text ());
222 new_marker
->SetType (marker
->Type ());
223 new_marker
->SetTime (TimeSpan_FromPts (marker
->Pts ()));
228 new_marker
->unref ();
230 current
= (MediaMarker::Node
*) current
->next
;
233 // Docs says we overwrite whatever's been loaded already.
234 LOG_MEDIAELEMENT ("MediaElement::ReadMarkers (): setting %d markers.\n", markers
->GetCount ());
235 SetMarkers (markers
);
240 MediaElement::MarkerTimeout (gpointer context
)
243 ((MediaElement
*) context
)->SetCurrentDeployment ();
244 ((MediaElement
*) context
)->CheckMarkers ();
249 MediaElement::SetMarkerTimeout (bool start
)
256 surface
= GetDeployment ()->GetSurface ();
261 tm
= surface
->GetTimeManager ();
263 g_return_if_fail (tm
!= NULL
);
266 if (marker_timeout
== 0) {
267 marker_timeout
= tm
->AddTimeout (MOON_PRIORITY_DEFAULT
, 33, MarkerTimeout
, this);
270 if (marker_timeout
!= 0) {
271 tm
->RemoveTimeout (marker_timeout
);
278 MediaElement::CheckMarkers ()
280 guint64 current_position
= GetPosition ();
282 LOG_MARKERS_EX ("MediaElement::CheckMarkers () current position: %" G_GUINT64_FORMAT
", previous position: %" G_GUINT64_FORMAT
")\n", current_position
, previous_position
);
285 if (current_position
> previous_position
&& seek_to_position
== -1) {
286 guint64 tmp
= previous_position
;
287 // We need to set previous_position before calling CheckMarkers,
288 // as CheckMarkers may end up emitting events, causing seeks
289 // which will change previous_position.
290 previous_position
= current_position
;
291 CheckMarkers (tmp
, current_position
- 1);
296 MediaElement::CheckMarkers (guint64 from
, guint64 to
)
298 TimelineMarkerCollection
*markers
;
300 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
")\n", from
, to
);
304 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"). from == to\n", from
, to
);
308 if (!(markers
= GetMarkers ())) {
309 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"). No markers\n", from
, to
);
314 // if from > to we've seeked backwards (last played position is after this one)
315 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"). from > to (diff: %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms).\n", from
, to
, from
- to
, MilliSeconds_FromPts (from
- to
));
319 /* Check if we've got streamed markers */
321 if (streamed_markers_queue
!= NULL
) {
322 TimelineMarkerNode
*node
= (TimelineMarkerNode
*) streamed_markers_queue
->First ();
323 while (node
!= NULL
) {
324 if (streamed_markers
== NULL
)
325 streamed_markers
= new TimelineMarkerCollection ();
326 streamed_markers
->Add (node
->GetTimelineMarker ());
327 node
= (TimelineMarkerNode
*) node
->next
;
329 streamed_markers_queue
->Clear (true);
333 CheckMarkers (from
, to
, markers
, false);
334 CheckMarkers (from
, to
, streamed_markers
, true);
338 MediaElement::CheckMarkers (guint64 from
, guint64 to
, TimelineMarkerCollection
*markers
, bool remove
)
340 TimelineMarker
*marker
;
346 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
", %p, %i). count: %i\n", from
, to
, markers
, remove
, markers
? markers
->GetCount () : -1);
349 // We might want to use a more intelligent algorithm here,
350 // this code only loops through all markers on every frame.
352 if (markers
!= NULL
) {
353 for (int i
= 0; i
< markers
->GetCount (); i
++) {
354 marker
= markers
->GetValueAt (i
)->AsTimelineMarker ();
356 if (!(val
= marker
->GetValue (TimelineMarker::TimeProperty
)))
359 pts
= (guint64
) val
->AsTimeSpan ();
361 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"): Checking pts: %" G_GUINT64_FORMAT
", enqueued %i elements\n", from
, to
, pts
, emit_list
.GetCount ());
365 // Streamed markers. Emit these even if we passed them with up to 1 s.
366 if (from
<= MilliSeconds_ToPts (1000)) {
367 emit
= pts
>= 0 && pts
<= to
;
369 emit
= pts
>= (from
- MilliSeconds_ToPts (1000)) && pts
<= to
;
372 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"): emit: %i, Checking pts: %" G_GUINT64_FORMAT
" in marker with Text = %s, Type = %s (removed from from)\n",
373 from
<= MilliSeconds_ToPts (1000) ? 0 : from
- MilliSeconds_ToPts (1000), to
, emit
, pts
, marker
->GetText (), marker
->GetType ());
376 emit
= pts
>= from
&& pts
<= to
;
377 LOG_MARKERS_EX ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"): Checking pts: %" G_GUINT64_FORMAT
" in marker with Text = %s, Type = %s\n",
378 from
, to
, pts
, marker
->GetText (), marker
->GetType ());
383 emit_list
.Add (marker
);
385 LOG_MARKERS ("MediaElement::CheckMarkers (%" G_GUINT64_FORMAT
", %" G_GUINT64_FORMAT
"): Emitting: Text = %s, Type = %s, Time = %" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms, count: %in",
386 from
, to
, marker
->GetText (), marker
->GetType (), marker
->GetTime (), MilliSeconds_FromPts (marker
->GetTime ()), emit_list
.GetCount ());
389 if (remove
&& (pts
<= to
|| emit
)) {
390 // Also delete markers we've passed by already
391 markers
->RemoveAt (i
);
397 for (int i
= 0; i
< emit_list
.GetCount (); i
++) {
398 marker
= (TimelineMarker
*) emit_list
[i
];
399 Emit (MarkerReachedEvent
, new TimelineMarkerRoutedEventArgs (marker
));
405 MediaElement::SetIsAttached (bool value
)
409 if (IsAttached () == value
)
413 LOG_MEDIAELEMENT ("MediaElement::SetIsAttached (%i): Stopping media element since we're detached.\n", value
);
414 detached_state
= state
;
416 mplayer
->Stop (); /* this is immediate */
417 Stop (); /* this is async */
418 flags
&= ~MediaOpenedEmitted
;
419 SetBufferingProgress (0.0);
422 SetNaturalDuration (new Duration (0));
425 FrameworkElement::SetIsAttached (value
);
428 LOG_MEDIAELEMENT ("MediaElement reattached, detached state: %s state: %s\n", GetStateName (detached_state
), GetStateName (state
));
429 if (detached_state
== MediaStatePlaying
) {
436 MediaElement::Reinitialize ()
438 TimelineMarkerCollection
*markers
;
439 MediaAttributeCollection
*attrs
;
441 LOG_MEDIAELEMENT ("MediaElement::Reinitialize ()\n");
450 if (marker_closure
) {
451 marker_closure
->Dispose ();
452 marker_closure
->unref ();
453 marker_closure
= NULL
;
456 if (playlist
!= NULL
) {
457 playlist
->Dispose ();
462 flags
&= (PlayRequested
| UseMediaHeight
| UseMediaWidth
);
463 flags
|= RecalculateMatrix
;
465 prev_state
= MediaStateClosed
;
466 state
= MediaStateClosed
;
468 first_pts
= G_MAXUINT64
;
469 seek_to_position
= -1;
470 seeked_to_position
= 0;
475 delete streamed_markers_queue
;
476 streamed_markers_queue
= NULL
;
477 if (streamed_markers
) {
478 streamed_markers
->unref ();
479 streamed_markers
= NULL
;
482 error_args
->unref ();
487 previous_position
= 0;
489 SetMarkerTimeout (false);
490 if ((markers
= GetMarkers ()))
493 if ((attrs
= GetAttributes ()))
496 cairo_matrix_init_identity (&matrix
);
500 MediaElement::IsMissingCodecs ()
503 return flags
& MissingCodecs
;
507 MediaElement::GetTransformOrigin ()
509 Point
*user_xform_origin
= GetRenderTransformOrigin ();
510 double h
= GetActualHeight();
511 double w
= GetActualWidth ();
513 if (w
== 0.0 && h
== 0.0 && mplayer
!= NULL
) {
514 h
= (double) mplayer
->GetVideoHeight ();
515 w
= (double) mplayer
->GetVideoWidth ();
518 return Point (user_xform_origin
->x
* w
, user_xform_origin
->y
* h
);
522 MediaElement::ComputeActualSize ()
524 Size result
= FrameworkElement::ComputeActualSize ();
525 Size specified
= Size (GetWidth (), GetHeight ());
526 UIElement
*parent
= GetVisualParent ();
528 if (parent
&& !parent
->Is (Type::CANVAS
))
529 if (LayoutInformation::GetPreviousConstraint (this) || LayoutInformation::GetLayoutSlot (this))
533 Size available
= Size (INFINITY
, INFINITY
);
534 available
= available
.Min (specified
);
535 result
= MeasureOverride (available
);
536 result
= ApplySizeConstraints (result
);
539 //g_warning ("actual is %g,%g", result.width, result.height);
545 MediaElement::MeasureOverride (Size availableSize
)
547 Size desired
= availableSize
;
548 Rect shape_bounds
= Rect ();
553 shape_bounds
= Rect (0,0,
554 mplayer
->GetVideoWidth (),
555 mplayer
->GetVideoHeight ());
557 /* don't stretch to infinite size */
558 if (isinf (desired
.width
))
559 desired
.width
= shape_bounds
.width
;
560 if (isinf (desired
.height
))
561 desired
.height
= shape_bounds
.height
;
563 /* compute the scaling */
564 if (shape_bounds
.width
> 0)
565 sx
= desired
.width
/ shape_bounds
.width
;
566 if (shape_bounds
.height
> 0)
567 sy
= desired
.height
/ shape_bounds
.height
;
569 /* don't use infinite dimensions as constraints */
570 if (isinf (availableSize
.width
))
572 if (isinf (availableSize
.height
))
575 switch (GetStretch ()) {
577 sx
= sy
= MIN (sx
, sy
);
579 case StretchUniformToFill
:
580 sx
= sy
= MAX (sx
, sy
);
583 if (isinf (availableSize
.width
))
585 if (isinf (availableSize
.height
))
593 desired
= Size (shape_bounds
.width
* sx
, shape_bounds
.height
* sy
);
599 MediaElement::ArrangeOverride (Size finalSize
)
601 Size arranged
= finalSize
;
602 Rect shape_bounds
= Rect ();
607 shape_bounds
= Rect (0, 0,
608 mplayer
->GetVideoWidth (),
609 mplayer
->GetVideoHeight ());
611 /* compute the scaling */
612 if (shape_bounds
.width
== 0)
613 shape_bounds
.width
= arranged
.width
;
614 if (shape_bounds
.height
== 0)
615 shape_bounds
.height
= arranged
.height
;
617 if (shape_bounds
.width
!= arranged
.width
)
618 sx
= arranged
.width
/ shape_bounds
.width
;
619 if (shape_bounds
.height
!= arranged
.height
)
620 sy
= arranged
.height
/ shape_bounds
.height
;
622 switch (GetStretch ()) {
624 sx
= sy
= MIN (sx
, sy
);
626 case StretchUniformToFill
:
627 sx
= sy
= MAX (sx
, sy
);
635 arranged
= Size (shape_bounds
.width
* sx
, shape_bounds
.height
* sy
);
641 MediaElement::GetCoverageBounds ()
643 MediaPlayer
*mplayer
= GetMediaPlayer ();
644 Stretch stretch
= GetStretch ();
646 if (IsClosed () || !mplayer
|| !mplayer
->HasRenderedFrame ())
649 if (stretch
== StretchFill
|| stretch
== StretchUniformToFill
)
652 Rect video
= Rect (0, 0, mplayer
->GetVideoWidth (), mplayer
->GetVideoHeight ());
653 cairo_matrix_t brush_xform
= matrix
;
655 cairo_matrix_invert (&brush_xform
);
656 cairo_matrix_multiply (&brush_xform
, &brush_xform
, &absolute_xform
);
658 video
= video
.Transform (&brush_xform
);
659 video
= video
.Intersection (bounds
);
665 MediaElement::GetQualityLevel (int min
, int max
)
671 current_pts
= mplayer
->GetPosition ();
672 diff
= (gint64
) current_pts
- (gint64
) last_quality_level_change_position
;
674 // we check the absolute diff so that we'll still change quality if the user seeks backwards in the video
675 if (llabs (diff
) > (gint64
) MilliSeconds_ToPts (1000)) {
676 // not changed within a second, candidate for another change.
677 double dropped_frames
= mplayer
->GetDroppedFramesPerSecond ();
678 if (dropped_frames
== 0) {
679 if (quality_level
< max
) {
682 last_quality_level_change_position
= current_pts
;
683 LOG_MEDIAELEMENT ("MediaElement::GetQualityLevel (): increased rendering quality to %i (%i-%i, higher better) - no dropped frames\n", quality_level
, min
, max
);
685 } else if (dropped_frames
> 5.0) {
686 if (quality_level
> 0) {
689 last_quality_level_change_position
= current_pts
;
690 LOG_MEDIAELEMENT ("MediaElement::GetQualityLevel (): decreased rendering quality to %i (%i-%i, higher better) - %.2f dropped frames per second with current level\n", quality_level
, min
, max
, dropped_frames
);
696 return MIN (max
, quality_level
+ min
);
700 MediaElement::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
702 Stretch stretch
= GetStretch ();
703 cairo_surface_t
*surface
;
704 cairo_pattern_t
*pattern
;
706 if (!mplayer
|| !(surface
= mplayer
->GetCairoSurface ()))
710 cairo_set_matrix (cr
, &absolute_xform
);
712 Size
specified (GetActualWidth (), GetActualHeight ());
713 Size stretched
= ApplySizeConstraints (specified
);
715 if (stretch
!= StretchUniformToFill
)
716 specified
= specified
.Min (stretched
);
718 Rect
paint (0, 0, specified
.width
, specified
.height
);
721 if (absolute_xform.xy == 0 && absolute_xform.yx == 0) {
722 //cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
723 cairo_matrix_t inv = absolute_xform;
724 cairo_matrix_invert (&inv);
725 paint = paint.Transform (&absolute_xform);
726 paint = paint.RoundIn ();
727 paint = paint.Transform (&inv);
732 Rect
video (0, 0, mplayer
->GetVideoWidth (), mplayer
->GetVideoHeight ());
734 if (GetStretch () == StretchNone
)
735 paint
= paint
.Union (video
);
737 if (video
.width
== 0.0 && video
.height
== 0.0)
740 pattern
= cairo_pattern_create_for_surface (surface
);
742 image_brush_compute_pattern_matrix (&matrix
,
743 paint
.width
, paint
.height
,
744 video
.width
, video
.height
,
745 stretch
, AlignmentXCenter
,
746 AlignmentYCenter
, NULL
, NULL
);
748 cairo_pattern_set_matrix (pattern
, &matrix
);
749 cairo_set_source (cr
, pattern
);
750 cairo_pattern_destroy (pattern
);
754 cairo_filter_t filter
;
755 switch (GetQualityLevel (0, 3)) {
756 case 0: filter
= CAIRO_FILTER_FAST
; break;
757 case 1: filter
= CAIRO_FILTER_GOOD
; break;
758 case 2: filter
= CAIRO_FILTER_BILINEAR
; break;
759 default: filter
= CAIRO_FILTER_BEST
; break;
761 cairo_pattern_set_filter (cairo_get_source (cr
), filter
);
765 RenderLayoutClip (cr
);
767 paint
= paint
.Intersection (Rect (0, 0, stretched
.width
, stretched
.height
));
777 MediaElement::BufferUnderflowHandler (PlaylistRoot
*sender
, EventArgs
*args
)
779 LOG_MEDIAELEMENT ("MediaElement::BufferUnderflow (): Switching to 'Buffering', previous_position: %" G_GUINT64_FORMAT
" ms, mplayer->GetPosition (): %" G_GUINT64_FORMAT
" ms\n",
780 MilliSeconds_FromPts (previous_position
), MilliSeconds_FromPts (mplayer
->GetPosition ()));
782 flags
|= PlayRequested
;
783 SetBufferingProgress (0.0);
784 Emit (BufferingProgressChangedEvent
);
785 SetState (MediaStateBuffering
);
790 MediaElement::EmitStateChangedAsync ()
792 AddTickCall (EmitStateChanged
);
796 MediaElement::EmitStateChanged (EventObject
*obj
)
798 MediaElement
*mel
= (MediaElement
*) obj
;
799 if (mel
->IsAttached ())
800 mel
->Emit (CurrentStateChangedEvent
);
804 MediaElement::SetState (MediaState state
)
808 LOG_MEDIAELEMENT ("MediaElement::SetState (%d): New state: %s, old state: %s\n",
809 state
, GetStateName (state
), GetStateName (this->state
));
814 if (this->state
!= state
) {
815 prev_state
= this->state
;
821 if (emit
) // Don't emit with mutex locked.
822 EmitStateChangedAsync ();
826 MediaElement::CreatePlaylist ()
828 g_return_if_fail (mplayer
== NULL
);
830 mplayer
= new MediaPlayer (this);
831 SetPlaylist (new PlaylistRoot (this));
835 MediaElement::SetPlaylist (PlaylistRoot
*value
)
837 // if playlist is something, then value must be null (and vice versa)
838 g_return_if_fail ((playlist
== NULL
) != (value
== NULL
));
842 if (playlist
!= NULL
) {
843 playlist
->RemoveAllHandlers (this);
844 playlist
->Dispose ();
848 playlist
= value
; // We assume the caller gives us a reference to the playlist
849 playlist
->AddHandler (PlaylistRoot::OpeningEvent
, OpeningCallback
, this);
850 playlist
->AddHandler (PlaylistRoot::OpenCompletedEvent
, OpenCompletedCallback
, this);
851 playlist
->AddHandler (PlaylistRoot::SeekingEvent
, SeekingCallback
, this);
852 playlist
->AddHandler (PlaylistRoot::SeekCompletedEvent
, SeekCompletedCallback
, this);
853 playlist
->AddHandler (PlaylistRoot::CurrentStateChangedEvent
, CurrentStateChangedCallback
, this);
854 playlist
->AddHandler (PlaylistRoot::MediaErrorEvent
, MediaErrorCallback
, this);
855 playlist
->AddHandler (PlaylistRoot::MediaEndedEvent
, MediaEndedCallback
, this);
856 playlist
->AddHandler (PlaylistRoot::BufferUnderflowEvent
, BufferUnderflowCallback
, this);
857 playlist
->AddHandler (PlaylistRoot::DownloadProgressChangedEvent
, DownloadProgressChangedCallback
, this);
858 playlist
->AddHandler (PlaylistRoot::BufferingProgressChangedEvent
, BufferingProgressChangedCallback
, this);
859 playlist
->AddHandler (PlaylistRoot::PlayEvent
, PlayCallback
, this);
860 playlist
->AddHandler (PlaylistRoot::PauseEvent
, PauseCallback
, this);
861 playlist
->AddHandler (PlaylistRoot::StopEvent
, StopCallback
, this);
862 playlist
->AddHandler (PlaylistRoot::EntryChangedEvent
, EntryChangedCallback
, this);
867 MediaElement::EntryChangedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
869 LOG_MEDIAELEMENT ("MediaElement::EntryChangedHandler ()\n");
870 flags
&= ~MediaOpenedEmitted
;
874 MediaElement::OpeningHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
876 LOG_MEDIAELEMENT ("MediaElement::OpeningHandler ()\n");
879 flags
&= ~Initializing
;
880 SetState (MediaStateOpening
);
884 MediaElement::OpenCompletedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
886 IMediaDemuxer
*demuxer
;
887 const char *demuxer_name
;
888 PlaylistEntry
*entry
;
893 g_return_if_fail (playlist
!= NULL
);
894 g_return_if_fail (mplayer
!= NULL
);
896 entry
= playlist
->GetCurrentPlaylistEntry ();
898 g_return_if_fail (entry
!= NULL
);
900 media
= entry
->GetMedia ();
902 g_return_if_fail (media
!= NULL
);
904 demuxer
= media
->GetDemuxerReffed ();
905 demuxer_name
= demuxer
->GetName ();
907 LOG_MEDIAELEMENT ("MediaElement::OpenCompletedHandler (%p), demuxer name: %s\n", media
, demuxer_name
);
909 // Try to figure out if we're missing codecs
910 for (int i
= 0; i
< demuxer
->GetStreamCount (); i
++) {
911 IMediaStream
*stream
= demuxer
->GetStream (i
);
912 IMediaDecoder
*decoder
= stream
->GetDecoder ();
913 const char *decoder_name
= decoder
? decoder
->GetName () : NULL
;
914 if (decoder_name
!= NULL
&& strcmp (decoder_name
, "NullDecoder") == 0) {
915 flags
|= MissingCodecs
;
923 // check if we're missing the codecs *and* if they are not installed
924 // since we could already have downloaded/installed them without refreshing the browser (leading to a crash)
925 if ((flags
& MissingCodecs
) && !Media::IsMSCodecsInstalled ())
926 CodecDownloader::ShowUI (GetDeployment ()->GetSurface (), false);
928 entry
->PopulateMediaAttributes ();
929 SetProperties (media
);
931 if (!(flags
& MediaOpenedEmitted
)) {
932 flags
|= MediaOpenedEmitted
;
936 // This is a workaround for MS DRT #78: it tests that download progress has changed
937 // from the latest DownloadProgressChanged event to the MediaOpened event (unless
938 // DownloadProgress is 0.0 or 1.0).
939 double progress
= media
->GetDownloadProgress ();
940 progress
= MAX (progress
, GetDownloadProgress ());
941 progress
= MIN (progress
+ 0.00000001, 1.0);
942 SetDownloadProgress (progress
);
943 Emit (MediaOpenedEvent
, new RoutedEventArgs ());
944 Emit (DownloadProgressChangedEvent
);
949 MediaElement::SetProperties (Media
*media
)
951 IMediaDemuxer
*demuxer
= NULL
;
952 PlaylistEntry
*entry
;
953 Duration
*natural_duration
;
954 bool can_seek
= true;
955 bool can_pause
= true;
957 LOG_MEDIAELEMENT ("MediaElement::SetProperties (%p)\n", media
);
959 g_return_if_fail (media
!= NULL
);
960 g_return_if_fail (playlist
!= NULL
);
962 seeked_to_position
= 0;
964 demuxer
= media
->GetDemuxerReffed ();
965 entry
= playlist
->GetCurrentPlaylistEntry ();
967 if (demuxer
== NULL
|| entry
== NULL
) {
969 g_warning ("MediaElement::SetProperties (%p): demuxer is null or entry is null (demuxer: %p entry: %p)\n", media
, demuxer
, entry
);
974 ReadMarkers (media
, demuxer
);
976 if (entry
->GetIsLive ()) {
980 can_seek
= entry
->GetClientSkip () && demuxer
->GetCanSeek ();
984 natural_duration
= new Duration (TimeSpan_FromPts (mplayer
->GetDuration ()));
985 SetCanPause (can_pause
);
986 SetCanSeek (can_seek
);
987 SetNaturalDuration (natural_duration
);
988 SetNaturalVideoHeight ((double) mplayer
->GetVideoHeight ());
989 SetNaturalVideoWidth ((double) mplayer
->GetVideoWidth ());
990 SetAudioStreamCount (mplayer
->GetAudioStreamCount ());
992 mplayer
->SetMuted (GetIsMuted ());
993 mplayer
->SetVolume (GetVolume ());
996 InvalidateMeasure ();
997 InvalidateArrange ();
1005 MediaElement::SeekingHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1007 LOG_MEDIAELEMENT ("MediaElement::SeekingHandler () state: %s\n", GetStateName (state
));
1010 SetMarkerTimeout (false);
1012 if (GetBufferingProgress () != 0.0) {
1013 SetBufferingProgress (0.0);
1014 Emit (BufferingProgressChangedEvent
);
1019 MediaElement::SeekCompletedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1021 LOG_MEDIAELEMENT ("MediaElement::SeekCompletedHandler () state: %s\n", GetStateName (state
));
1024 seek_to_position
= -1;
1025 SetMarkerTimeout (true);
1029 MediaElement::PlayHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1031 LOG_MEDIAELEMENT ("MediaElement::PlayHandler ()\n");
1034 SetMarkerTimeout (true);
1036 SetState (MediaStatePlaying
);
1040 MediaElement::PauseHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1042 LOG_MEDIAELEMENT ("MediaElement::PauseHandler ()\n");
1045 SetMarkerTimeout (false);
1047 SetState (MediaStatePaused
);
1051 MediaElement::StopHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1053 PlaylistEntry
*entry
;
1055 LOG_MEDIAELEMENT ("MediaElement::StopHandler ()\n");
1058 g_return_if_fail (playlist
!= NULL
);
1060 entry
= playlist
->GetCurrentPlaylistEntry ();
1062 g_return_if_fail (entry
!= NULL
);
1063 seeked_to_position
= 0;
1065 SetProperties (entry
->GetMedia ());
1067 SetMarkerTimeout (false);
1068 CheckMarkers (); // check one last time.
1070 SetState (MediaStateStopped
);
1074 MediaElement::CurrentStateChangedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1076 LOG_MEDIAELEMENT ("MediaElement::CurrentStateChangedHandler ()\n");
1081 MediaElement::MediaErrorHandler (PlaylistRoot
*playlist
, ErrorEventArgs
*args
)
1083 LOG_MEDIAELEMENT ("MediaElement::MediaErrorHandler (). State: %s Message: %s\n", GetStateName (state
), args
? args
->GetErrorMessage() : NULL
);
1086 if (state
== MediaStateClosed
&& !(flags
& Initializing
))
1089 // TODO: Should ClearValue be called on these instead?
1090 SetAudioStreamCount (0);
1091 SetNaturalVideoHeight (0);
1092 SetNaturalVideoWidth (0);
1093 SetNaturalDuration (0);
1094 SetCanPause (false);
1096 SetDownloadProgress (0);
1097 SetDownloadProgressOffset (0);
1098 SetRenderedFramesPerSecond (0);
1099 SetDroppedFramesPerSecond (0);
1102 InvalidateMeasure ();
1103 InvalidateArrange ();
1105 SetState (MediaStateClosed
);
1110 if (flags
& Initializing
)
1111 EmitAsync (MediaFailedEvent
, args
);
1113 Emit (MediaFailedEvent
, args
);
1115 flags
&= ~Initializing
;
1119 MediaElement::MediaEndedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1121 LOG_MEDIAELEMENT ("MediaElement::MediaEndedHandler () state: %s position: %" G_GUINT64_FORMAT
"\n", GetStateName (state
), MilliSeconds_FromPts (GetPosition ()));
1125 paused_position
= GetNaturalDuration ()->GetTimeSpan ();
1126 SetState (MediaStatePaused
);
1127 Emit (MediaEndedEvent
);
1131 MediaElement::DownloadProgressChangedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1133 ProgressEventArgs
*pea
= (ProgressEventArgs
*) args
;
1135 LOG_MEDIAELEMENT ("MediaElement::DownloadProgressChangedHandler (): %f\n", pea
? pea
->progress
: -1.0);
1138 g_return_if_fail (pea
!= NULL
);
1140 SetDownloadProgress (pea
->progress
);
1141 Emit (DownloadProgressChangedEvent
);
1145 MediaElement::BufferingProgressChangedHandler (PlaylistRoot
*playlist
, EventArgs
*args
)
1147 ProgressEventArgs
*pea
= (ProgressEventArgs
*) args
;
1149 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): %f state: %s\n", pea
? pea
->progress
: -1.0, GetStateName (state
));
1152 g_return_if_fail (pea
!= NULL
);
1154 if (GetBufferingProgress () < pea
->progress
) {
1155 if (state
== MediaStatePlaying
|| state
== MediaStatePaused
) {
1156 if (state
== MediaStatePlaying
)
1157 flags
|= PlayRequested
;
1158 /* this is wrong when the user calls Play while we're still buffering because we'd jump back to the buffering state later (but we'd continue playing) */
1159 /* if we set this earlier though (SeekCompletedHandler) MS DRT #115 fails */
1160 SetState (MediaStateBuffering
);
1162 SetBufferingProgress (pea
->progress
);
1164 Emit (BufferingProgressChangedEvent
);
1167 if (pea
->progress
>= 1.0) {
1168 if (GetState () == MediaStateBuffering
) {
1169 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, playing...\n");
1171 } else if (flags
& PlayRequested
) {
1172 LOG_MEDIAELEMENT ("MediaElement::BufferingProgressChangedHandler (): buffer full, state: %s PlayRequested: 1\n", GetStateName (state
));
1179 MediaElement::MediaInvalidate ()
1181 Emit (MediaInvalidatedEvent
);
1186 MediaElement::SetUriSource (Uri
*uri
)
1188 LOG_MEDIAELEMENT ("MediaElement::SetUriSource ('%s')\n", uri
? uri
->ToString () : NULL
);
1193 g_return_if_fail (playlist
== NULL
);
1195 flags
|= Initializing
;
1197 if (uri
!= NULL
&& uri
->originalString
!= NULL
&& uri
->originalString
[0] != 0) {
1199 char *str
= uri
->ToString ();
1200 playlist
->GetCurrentEntry ()->InitializeWithUri (str
);
1204 InvalidateMeasure ();
1205 InvalidateArrange ();
1208 flags
&= ~Initializing
;
1212 MediaElement::SetSource (Downloader
*downloader
, const char *PartName
)
1214 LOG_MEDIAELEMENT ("MediaElement::SetSource (%p, '%s')\n", downloader
, PartName
);
1219 g_return_if_fail (downloader
!= NULL
);
1220 g_return_if_fail (playlist
== NULL
);
1222 flags
|= Initializing
;
1225 playlist
->GetCurrentEntry ()->InitializeWithDownloader (downloader
, PartName
);
1227 flags
&= ~Initializing
;
1231 MediaElement::SetStreamSource (ManagedStreamCallbacks
*callbacks
)
1233 LOG_MEDIAELEMENT ("MediaElement::SetStreamSource (%p)\n", callbacks
);
1238 g_return_if_fail (callbacks
!= NULL
);
1239 g_return_if_fail (playlist
== NULL
);
1241 flags
|= Initializing
;
1244 playlist
->GetCurrentEntry ()->InitializeWithStream (callbacks
);
1246 SetDownloadProgress (1.0);
1248 flags
&= ~Initializing
;
1252 MediaElement::SetDemuxerSource (void *context
, CloseDemuxerCallback close_demuxer
, GetDiagnosticAsyncCallback get_diagnostic
, GetFrameAsyncCallback get_sample
,
1253 OpenDemuxerAsyncCallback open_demuxer
, SeekAsyncCallback seek
, SwitchMediaStreamAsyncCallback switch_media_stream
)
1255 ExternalDemuxer
*demuxer
;
1258 LOG_MEDIAELEMENT ("MediaElement::SetDemuxerSource ()\n");
1263 g_return_val_if_fail (context
!= NULL
, NULL
);
1264 g_return_val_if_fail (close_demuxer
!= NULL
&& get_diagnostic
!= NULL
&& get_sample
!= NULL
&& open_demuxer
!= NULL
&& seek
!= NULL
&& switch_media_stream
!= NULL
, NULL
);
1265 g_return_val_if_fail (playlist
== NULL
, NULL
);
1267 flags
|= Initializing
;
1270 media
= new Media (playlist
);
1271 demuxer
= new ExternalDemuxer (media
, context
, close_demuxer
, get_diagnostic
, get_sample
, open_demuxer
, seek
, switch_media_stream
);
1272 playlist
->GetCurrentEntry ()->InitializeWithDemuxer (demuxer
);
1275 SetDownloadProgress (1.0);
1277 flags
&= ~Initializing
;
1283 MediaElement::SetPlayRequested ()
1285 LOG_MEDIAELEMENT ("MediaElement::SetPlayRequested ()\n");
1288 flags
|= PlayRequested
;
1292 MediaElement::PlayOrStop ()
1294 LOG_MEDIAELEMENT ("MediaElement::PlayOrPause (): GetCanPause (): %s, PlayRequested: %s, GetAutoPlay: %s, AutoPlayed: %s\n",
1295 GetCanPause () ? "true" : "false", (flags
& PlayRequested
) ? "true" : "false",
1296 GetAutoPlay () ? "true" : "false", (flags
& AutoPlayed
) ? "true" : "false");
1299 if (!GetCanPause ()) {
1300 // If we can't pause, we play
1301 SetState (MediaStatePlaying
);
1302 playlist
->PlayAsync ();
1303 } else if (flags
& PlayRequested
) {
1304 // A Play has already been requested.
1305 SetState (MediaStatePlaying
);
1306 playlist
->PlayAsync ();
1307 } else if (GetAutoPlay () && !(flags
& AutoPlayed
)) {
1309 flags
|= AutoPlayed
;
1310 SetState (MediaStatePlaying
);
1311 playlist
->PlayAsync ();
1313 SetState (MediaStatePaused
);
1318 MediaElement::Pause ()
1320 LOG_MEDIAELEMENT ("MediaElement::Pause (): current state: %s\n", GetStateName (state
));
1323 if (playlist
== NULL
)
1327 case MediaStateOpening
:// docs: No specified behaviour
1328 flags
&= ~PlayRequested
;
1330 case MediaStateClosed
: // docs: No specified behaviour
1332 case MediaStatePaused
:// docs: no-op
1333 case MediaStateBuffering
:
1334 case MediaStatePlaying
:
1335 case MediaStateStopped
: // docs: pause
1336 flags
&= ~PlayRequested
;
1337 paused_position
= GetPosition ();
1338 SetState (MediaStatePaused
);
1339 playlist
->PauseAsync ();
1341 case MediaStateIndividualizing
:
1342 case MediaStateAcquiringLicense
:
1343 g_warning ("MediaElement: Invalid state.");
1349 MediaElement::Play ()
1351 LOG_MEDIAELEMENT ("MediaElement::Play (): current state: %s\n", GetStateName (state
));
1354 g_return_if_fail (playlist
!= NULL
);
1357 case MediaStateClosed
: // docs: No specified behaviour
1358 case MediaStateOpening
:// docs: No specified behaviour
1359 flags
|= PlayRequested
;
1361 case MediaStatePlaying
:// docs: no-op
1362 case MediaStateBuffering
:
1363 case MediaStatePaused
:
1364 case MediaStateStopped
:
1365 SetState (MediaStatePlaying
);
1366 playlist
->PlayAsync ();
1368 case MediaStateIndividualizing
:
1369 case MediaStateAcquiringLicense
:
1370 g_warning ("MediaElement: Invalid state.");
1376 MediaElement::Stop ()
1378 LOG_MEDIAELEMENT ("MediaElement::Stop (): current state: %s\n", GetStateName (state
));
1385 case MediaStateOpening
:// docs: No specified behaviour
1386 flags
&= ~PlayRequested
;
1388 case MediaStateClosed
: // docs: No specified behaviour
1389 case MediaStateStopped
:// docs: no-op
1391 case MediaStateBuffering
:
1392 case MediaStatePlaying
:
1393 case MediaStatePaused
: // docs: stop
1394 flags
&= ~PlayRequested
;
1396 LOG_MEDIAELEMENT ("MediaElement::Stop (): state: %s, IsSingleFile: %i\n", GetStateName (state
), playlist
->IsSingleFile ());
1398 if (!playlist
->IsSingleFile ())
1399 flags
&= ~MediaOpenedEmitted
; // for playlists we must re-emit MediaOpened when the first entry/media has been re-loaded (even though we stop the first entry).
1401 SetState (MediaStateStopped
);
1402 playlist
->StopAsync ();
1404 case MediaStateIndividualizing
:
1405 case MediaStateAcquiringLicense
:
1406 g_warning ("MediaElement: Invalid state.");
1412 MediaElement::Seek (TimeSpan to
, bool force
)
1414 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms) state: %s\n", to
, MilliSeconds_FromPts (to
), GetStateName (state
));
1417 if (!force
&& !IsAttached ()) {
1418 LOG_MEDIAELEMENT ("MediaElement::Seek (): not attached.\n");
1422 if (!force
&& !GetCanSeek ()) {
1423 LOG_MEDIAELEMENT ("MediaElement::Seek (): CanSeek is false, not seeking\n");
1428 case MediaStateIndividualizing
:
1429 case MediaStateAcquiringLicense
:
1430 g_warning ("MediaElement:Seek (): Invalid state %s\n", GetStateName (state
));
1432 case MediaStateOpening
:
1433 case MediaStateClosed
:
1437 case MediaStateBuffering
:
1438 case MediaStatePlaying
:
1439 case MediaStatePaused
:
1440 case MediaStateStopped
:
1441 Duration
*duration
= GetNaturalDuration ();
1443 if (duration
->HasTimeSpan () && to
> duration
->GetTimeSpan ())
1444 to
= duration
->GetTimeSpan ();
1448 if (!force
&& to
== TimeSpan_FromPts (mplayer
->GetPosition ()))
1451 previous_position
= to
;
1452 seek_to_position
= to
;
1453 seeked_to_position
= to
;
1454 paused_position
= to
;
1456 if (state
== MediaStatePlaying
)
1457 flags
|= PlayRequested
;
1459 mplayer
->NotifySeek (TimeSpan_ToPts (to
));
1460 playlist
->SeekAsync (to
);
1461 Emit (MediaInvalidatedEvent
);
1464 LOG_MEDIAELEMENT ("MediaElement::Seek (%" G_GUINT64_FORMAT
" = %" G_GUINT64_FORMAT
" ms) previous position: %" G_GUINT64_FORMAT
"\n", to
, MilliSeconds_FromPts (to
), previous_position
);
1471 MediaElement::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
1473 if (args
->GetId () == MediaElement::SourceProperty
) {
1474 DownloaderAccessPolicy policy
= MediaPolicy
;
1475 Uri
*uri
= GetSource ();
1476 const char *location
;
1479 if (!(location
= GetDeployment ()->GetXapLocation ()) && IsAttached ())
1480 location
= GetDeployment ()->GetSurface ()->GetSourceLocation ();
1482 if (uri
->scheme
&& (!strcmp (uri
->scheme
, "mms") || !strcmp (uri
->scheme
, "rtsp") || !strcmp (uri
->scheme
, "rtsps")))
1483 policy
= StreamingPolicy
;
1485 if (uri
->IsInvalidPath ()) {
1486 EmitAsync (MediaFailedEvent
, new ErrorEventArgs (MediaError
, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE
, 0, "invalid path found in uri")));
1488 } else if (!Downloader::ValidateDownloadPolicy (location
, uri
, policy
)) {
1489 EmitAsync (MediaFailedEvent
, new ErrorEventArgs (MediaError
, MoonError (MoonError::ARGUMENT_OUT_OF_RANGE
, 0, "Security Policy Violation")));
1494 flags
|= RecalculateMatrix
;
1496 } else if (args
->GetId () == MediaElement::AudioStreamIndexProperty
) {
1498 mplayer
->SetAudioStreamIndex (args
->GetNewValue()->AsInt32 ());
1499 } else if (args
->GetId () == MediaElement::AutoPlayProperty
) {
1500 // no state to change
1501 } else if (args
->GetId () == MediaElement::BalanceProperty
) {
1503 mplayer
->SetBalance (args
->GetNewValue()->AsDouble ());
1504 } else if (args
->GetId () == MediaElement::BufferingProgressProperty
) {
1505 // read-only property
1506 } else if (args
->GetId () == MediaElement::BufferingTimeProperty
) {
1508 // Not quite sure what to do here, we could:
1509 // a) store the buffering time on the mediaplayer and let the media request it whenever it needs it
1510 // b) let the media request the buffering time from mediaelement
1511 // c) always keep the current media up to date (this is harder to do right when we get multiple media downloading at the same time)
1512 // note that thread-safety is easier with a) (media will do the request from another thread) or c)
1515 media->SetBufferingTime (TimeSpan_ToPts (GetBufferingTime ()));
1517 } else if (args
->GetId () == MediaElement::CurrentStateProperty
) {
1518 // read-only property
1519 // This should really not happen, we use a property provider for this property.
1520 } else if (args
->GetId () == MediaElement::IsMutedProperty
) {
1522 mplayer
->SetMuted (args
->GetNewValue()->AsBool ());
1523 } else if (args
->GetId () == MediaElement::MarkersProperty
) {
1525 } else if (args
->GetId () == MediaElement::NaturalVideoHeightProperty
) {
1526 // read-only property
1527 flags
|= RecalculateMatrix
;
1528 } else if (args
->GetId () == MediaElement::NaturalVideoWidthProperty
) {
1529 // read-only property
1530 flags
|= RecalculateMatrix
;
1531 } else if (args
->GetId () == MediaElement::PositionProperty
) {
1532 Seek (args
->GetNewValue()->AsTimeSpan (), false);
1533 ClearValue (MediaElement::PositionProperty
, false); // We need this, otherwise our property system will return the seeked-to position forever (MediaElementPropertyValueProvider isn't called).
1534 } else if (args
->GetId () == MediaElement::VolumeProperty
) {
1536 mplayer
->SetVolume (args
->GetNewValue()->AsDouble ());
1539 if (args
->GetProperty ()->GetOwnerType() != Type::MEDIAELEMENT
) {
1540 // propagate to parent class
1541 FrameworkElement::OnPropertyChanged (args
, error
);
1542 flags
|= RecalculateMatrix
;
1546 NotifyListenersOfPropertyChange (args
, error
);
1550 MediaElement::EnableAntiAlias (void)
1552 return !(absolute_xform
.xx
== absolute_xform
.yy
&& /* no rotation */
1553 (absolute_xform
.yx
== 0 && absolute_xform
.xy
== 0)); /* no skew */
1558 MediaElement::ReportErrorOccurredCallback (EventObject
*obj
)
1560 MediaElement
*me
= (MediaElement
*) obj
;
1561 ErrorEventArgs
*args
;
1564 args
= me
->error_args
;
1565 me
->error_args
= NULL
;
1566 me
->mutex
.Unlock ();
1568 me
->ReportErrorOccurred (args
);
1574 MediaElement::ReportErrorOccurred (ErrorEventArgs
*args
)
1576 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred (%p)\n", args
);
1578 if (!Surface::InMainThread ()) {
1581 error_args
->unref ();
1586 AddTickCall (ReportErrorOccurredCallback
);
1591 MediaErrorHandler (NULL
, args
);
1595 MediaElement::ReportErrorOccurred (const char *args
)
1597 LOG_MEDIAELEMENT ("MediaElement::ReportErrorOccurred ('%s')\n", args
);
1599 ErrorEventArgs
*eea
= new ErrorEventArgs (MediaError
, MoonError (MoonError::EXCEPTION
, 3001, g_strdup (args
)));
1600 ReportErrorOccurred (eea
);
1605 * MediaElementPropertyValueProvider
1608 MediaElementPropertyValueProvider::MediaElementPropertyValueProvider (MediaElement
*element
, PropertyPrecedence precedence
)
1609 : FrameworkElementProvider (element
, precedence
)
1612 current_state
= NULL
;
1613 rendered_frames_per_second
= NULL
;
1614 dropped_frames_per_second
= NULL
;
1617 MediaElementPropertyValueProvider::~MediaElementPropertyValueProvider ()
1620 delete current_state
;
1621 delete rendered_frames_per_second
;
1622 delete dropped_frames_per_second
;
1626 MediaElementPropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
1628 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1631 if (property
->GetId () == MediaElement::PositionProperty
)
1632 return GetPosition ();
1634 if (property
->GetId () == MediaElement::CurrentStateProperty
)
1635 return GetCurrentState ();
1637 if (property
->GetId () == MediaElement::DroppedFramesPerSecondProperty
)
1638 return GetDroppedFramesPerSecond ();
1640 if (property
->GetId () == MediaElement::RenderedFramesPerSecondProperty
)
1641 return GetRenderedFramesPerSecond ();
1643 return FrameworkElementProvider::GetPropertyValue (property
);
1647 MediaElementPropertyValueProvider::GetDroppedFramesPerSecond ()
1649 MediaElement
*element
= (MediaElement
*) obj
;
1650 MediaPlayer
*mplayer
= element
->GetMediaPlayer ();
1652 delete dropped_frames_per_second
;
1655 dropped_frames_per_second
= new Value (mplayer
->GetDroppedFramesPerSecond ());
1657 dropped_frames_per_second
= NULL
;
1660 return dropped_frames_per_second
;
1664 MediaElementPropertyValueProvider::GetRenderedFramesPerSecond ()
1666 MediaElement
*element
= (MediaElement
*) obj
;
1667 MediaPlayer
*mplayer
= element
->GetMediaPlayer ();
1669 delete rendered_frames_per_second
;
1672 rendered_frames_per_second
= new Value (mplayer
->GetRenderedFramesPerSecond ());
1674 rendered_frames_per_second
= NULL
;
1677 return rendered_frames_per_second
;
1681 MediaElementPropertyValueProvider::GetCurrentState ()
1683 MediaElement
*element
= (MediaElement
*) obj
;
1685 delete current_state
;
1686 current_state
= new Value (element
->IsAttached () ? element
->state
: element
->detached_state
);
1688 return current_state
;
1692 MediaElementPropertyValueProvider::GetPosition ()
1694 bool use_mplayer
= false;;
1695 bool use_pause
= false;
1696 MediaElement
*element
= (MediaElement
*) obj
;
1697 guint64 position
= TimeSpan_ToPts (element
->seek_to_position
);
1699 delete this->position
;
1700 this->position
= NULL
;
1702 switch (element
->state
) {
1703 case MediaStateIndividualizing
:
1704 case MediaStateAcquiringLicense
:
1705 g_warning ("MediaElementPropertyValueProvider::GetPosition (): Invalid state.\n");
1707 case MediaStateOpening
:
1708 case MediaStateClosed
:
1709 use_mplayer
= false;
1711 case MediaStateStopped
:
1714 case MediaStateBuffering
:
1715 case MediaStatePlaying
:
1718 case MediaStatePaused
:
1723 // If a seek is pending, we need to return that position.
1725 if (TimeSpan_FromPts (position
) == -1)
1726 position
= element
->mplayer
->GetPosition ();
1727 } else if (use_pause
) {
1728 position
= element
->paused_position
;
1731 if (position
< element
->seeked_to_position
)
1732 position
= element
->seeked_to_position
;
1734 if (TimeSpan_FromPts (position
) == -1) {
1737 position
= MIN (position
, element
->mplayer
->GetDuration ());
1740 this->position
= new Value (TimeSpan_FromPts (position
), Type::TIMESPAN
);
1741 return this->position
;