|
//------------------------------------------------------------------------------
// Microsoft Avalon
// Copyright (c) Microsoft Corporation, 2003
//
// File: mediaclock.cs
//
//------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.ComponentModel;
using MS.Internal;
using MS.Win32;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows.Media.Composition;
using System.Windows.Markup;
using System.Security.Permissions;
using System.Security;
using MS.Internal.PresentationCore; // SecurityHelper
using System.Windows.Threading;
using System.Runtime.InteropServices;
using System.IO;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
using UnsafeNativeMethods=MS.Win32.PresentationCore.UnsafeNativeMethods;
namespace System.Windows.Media
{
#region MediaClock
/// <summary>
/// Maintains run-time timing state for media (audio/video) objects.
/// </summary>
public class MediaClock :
Clock
{
#region Constructors and Finalizers
/// <summary>
/// Creates a MediaClock object.
/// </summary>
/// <param name="media">
/// The MediaTimeline to use as a template.
/// </param>
/// <remarks>
/// The returned MediaClock doesn't have any children.
/// </remarks>
protected internal MediaClock(MediaTimeline media)
: base(media)
{}
#endregion
#region Properties
/// <summary>
/// Gets the MediaTimeline object that holds the description controlling the
/// behavior of this clock.
/// </summary>
/// <value>
/// The MediaTimeline object that holds the description controlling the
/// behavior of this clock.
/// </value>
public new MediaTimeline Timeline
{
get
{
return (MediaTimeline)base.Timeline;
}
}
#endregion
#region Clock Overrides
/// <summary>
/// Returns True because Media has the potential to slip.
/// </summary>
/// <returns>True</returns>
protected override bool GetCanSlip()
{
return true;
}
/// <summary>
/// Get the actual media time for slip synchronization
/// </summary>
protected override TimeSpan GetCurrentTimeCore()
{
if (_mediaPlayer != null)
{
return _mediaPlayer.Position;
}
else // Otherwise use base implementation
{
return base.GetCurrentTimeCore();
}
}
/// <summary>
/// Called when we are stopped. This is the same as pausing and seeking
/// to the beginning.
/// </summary>
protected override void Stopped()
{
// Only perform the operation if we're controlling a player
if (_mediaPlayer != null)
{
_mediaPlayer.SetSpeedRatio(0);
_mediaPlayer.SetPosition(TimeSpan.FromTicks(0));
}
}
/// <summary>
/// Called when our speed changes. A discontinuous time movement may or
/// may not have occurred.
/// </summary>
protected override void SpeedChanged()
{
Sync();
}
/// <summary>
/// Called when we have a discontinuous time movement, but no change in
/// speed
/// </summary>
protected override void DiscontinuousTimeMovement()
{
Sync();
}
private void Sync()
{
// Only perform the operation if we're controlling a player
if (_mediaPlayer != null)
{
double? currentSpeedProperty = this.CurrentGlobalSpeed;
double currentSpeedValue = currentSpeedProperty.HasValue ? currentSpeedProperty.Value : 0;
TimeSpan? currentTimeProperty = this.CurrentTime;
TimeSpan currentTimeValue = currentTimeProperty.HasValue ? currentTimeProperty.Value : TimeSpan.Zero;
// If speed was potentially changed to 0, make sure we set media's speed to 0 (e.g. pause) before
// setting the position to the target frame. Otherwise, the media's scrubbing mechanism would
// not work correctly, because scrubbing requires media to be paused by the time it is seeked.
if (currentSpeedValue == 0)
{
_mediaPlayer.SetSpeedRatio(currentSpeedValue);
_mediaPlayer.SetPosition(currentTimeValue);
}
else
{
// In the case where speed != 0, we first want to set the position and then the speed.
// This is because if we were previously paused, we want to be at the right position
// before we begin to play.
_mediaPlayer.SetPosition(currentTimeValue);
_mediaPlayer.SetSpeedRatio(currentSpeedValue);
}
}
}
/// <summary>
/// Returns true if this timeline needs continuous frames.
/// This is a hint that we should keep updating our time during the active period.
/// </summary>
/// <returns></returns>
internal override bool NeedsTicksWhenActive
{
get
{
return true;
}
}
/// <summary>
/// The instance of media that this clock is driving
/// </summary>
internal MediaPlayer Player
{
get
{
return _mediaPlayer;
}
set
{
MediaPlayer oldPlayer = _mediaPlayer;
MediaPlayer newPlayer = value;
// avoid inifite loops
if (newPlayer != oldPlayer)
{
_mediaPlayer = newPlayer;
// Disassociate the old player
if (oldPlayer != null)
{
oldPlayer.Clock = null;
}
// Associate the new player
if (newPlayer != null)
{
newPlayer.Clock = this;
Uri baseUri = ((IUriContext)Timeline).BaseUri;
Uri toPlay = null;
// ignore pack URIs for now (see work items 45396 and 41636)
if (baseUri != null
&& baseUri.Scheme != System.IO.Packaging.PackUriHelper.UriSchemePack
&& !Timeline.Source.IsAbsoluteUri)
{
toPlay = new Uri(baseUri, Timeline.Source);
}
else
{
//
// defaults to app domain base if Timeline.Source is
// relative
//
toPlay = Timeline.Source;
}
// we need to sync to the current state of the clock
newPlayer.SetSource(toPlay);
SpeedChanged();
}
}
}
}
#endregion
#region Private Data members
/// <summary>
/// MediaPlayer -- holds all the precious resource references
/// </summary>
private MediaPlayer _mediaPlayer;
#endregion
}
#endregion
};
|