Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / System / Windows / Media / MediaPlayerState.cs / 1 / MediaPlayerState.cs
//------------------------------------------------------------------------------
// Microsoft Avalon
// Copyright (c) Microsoft Corporation, 2003
//
// File: MediaPlayerState.cs
//
//-----------------------------------------------------------------------------
using System;
using System.Threading;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics;
using System.ComponentModel;
using MS.Internal;
using MS.Internal.PresentationCore; // SecurityHelper
using MS.Win32;
using System.IO.Packaging;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows.Media.Composition;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Windows.Navigation;
using System.Runtime.InteropServices;
using System.IO;
using System.Security.AccessControl;//for semaphore access permissions
using System.Net;
using Microsoft.Win32;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
using UnsafeNativeMethods=MS.Win32.PresentationCore.UnsafeNativeMethods;
//
// Disable the warnings that C# emmits when it finds pragmas it does not recognize, this is to
// get rid of false positive PreSharp warning
//
#pragma warning disable 1634, 1691
namespace System.Windows.Media
{
#region MediaPlayerState
///
/// MediaPlayerState
/// Holds all of the local state that is required for playing media. This is
/// separated out into a separate class because MediaPlayer needs to be
/// Animatable, but then that means it needs to be Freezable. However, media
/// state cannot really be frozan (media piplines progress according to time
/// and are very expensive), so instead, we make the "Frozen" object copy the
/// state object around. Doing this will also help in the remote case where
/// we need to handle MediaPlayer quite differently on the channel in the
/// remote and local cases.
///
internal class MediaPlayerState
{
#region Constructors and Finalizers
///
/// Constructor
///
///
/// SecurityCritical: This code sets a critical data member (_captureRenderTargetBitmap).
/// SecurityTreatAsSafe: The critical member is set to a safe value (null).
///
[SecurityCritical, SecurityTreatAsSafe]
internal
MediaPlayerState(
MediaPlayer mediaPlayer
)
{
_dispatcher = mediaPlayer.Dispatcher;
_bitmapSource = null;
_bitmapSourceDuceHandle = new DUCE.MultiChannelResource();
_captureChannel = null;
_captureRenderTargetBitmap = null;
_mediaContext = null;
_renderTargetRoot = DUCE.Resource.Null;
Init();
CreateMedia(mediaPlayer);
//
// We need to know about new frames when they are sent so that we can
// capture the image data in the synchronous case.
//
_mediaEventsHelper.NewFrame += new EventHandler(OnNewFrame);
//
// Opened is actually fired when the media is prerolled.
//
_mediaEventsHelper.MediaPrerolled += new EventHandler(OnMediaOpened);
}
//
///
/// Initialize local variables to their default state. After a close we want to restore this too, same as
/// after construction.
///
private
void
Init()
{
_volume = DEFAULT_VOLUME;
_balance = DEFAULT_BALANCE;
_speedRatio = 1.0;
_paused = false;
_muted = false;
_sourceUri = null;
_scrubbingEnabled = false;
_isRemote = false;
//
// Need to clear our bitmap source.
//
ClearBitmapSource();
//
// Need to close our synchronous render infrastructure.
//
CloseCaptureResources();
}
#endregion
#region Public Methods
///
/// Internal IsBuffering
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia
/// TreatAsSafe: This information about whether buffering is on is safe to expose
///
internal bool IsBuffering
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool isBuffering = false;
HRESULT.Check(MILMedia.IsBuffering(_nativeMedia, ref isBuffering));
return isBuffering;
}
}
///
/// Internal CanPause
///
///
/// Critical: This is critical because it acceses _nativemedia and calls into unmanaged code
/// TreatAsSafe: This is safe to expose since giving out information about
/// whether media can be paused is safe
///
internal bool CanPause
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool canPause = false;
HRESULT.Check(MILMedia.CanPause(_nativeMedia, ref canPause));
return canPause;
}
}
///
/// Internal DownloadProgress
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning download progress is safe
///
internal double DownloadProgress
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
double downloadProgress = 0;
HRESULT.Check(MILMedia.GetDownloadProgress(_nativeMedia, ref downloadProgress));
return downloadProgress;
}
}
///
/// Internal BufferingProgress
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning buffering progress is safe
///
internal double BufferingProgress
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
double bufferingProgress = 0;
HRESULT.Check(MILMedia.GetBufferingProgress(_nativeMedia, ref bufferingProgress));
return bufferingProgress;
}
}
///
/// Returns the Height
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning natural video height is safe
///
internal Int32 NaturalVideoHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
UInt32 height = 0;
HRESULT.Check(MILMedia.GetNaturalHeight(_nativeMedia, ref height));
return (Int32)height;
}
}
///
/// Returns the Width
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning natural video width is safe
///
internal Int32 NaturalVideoWidth
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
UInt32 width = 0;
HRESULT.Check(MILMedia.GetNaturalWidth(_nativeMedia, ref width));
return (Int32)width;
}
}
///
/// If media has audio content
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning whether media has audio is safe
///
internal bool HasAudio
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool hasAudio = true;
HRESULT.Check(MILMedia.HasAudio(_nativeMedia, ref hasAudio));
return hasAudio;
}
}
///
/// If the media has video content
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning whether media has video is safe
///
internal bool HasVideo
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool hasVideo = false;
HRESULT.Check(MILMedia.HasVideo(_nativeMedia, ref hasVideo));
return hasVideo;
}
}
///
/// Location of the media to play. Open opens the media, this property
/// allows the source that is currently playing to be retrieved.
///
internal Uri Source
{
get
{
VerifyAPI();
return _sourceUri;
}
}
///
/// Internal Get Volume
///
///
/// Critical: This is critical because it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning volume ratio is safe and so also is setting it
///
internal double Volume
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
return _volume;
}
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (Double.IsNaN(value))
{
throw new ArgumentException(SR.Get(SRID.ParameterValueCannotBeNaN), "value");
}
if (DoubleUtil.GreaterThanOrClose(value, 1))
{
value = 1;
}
else if (DoubleUtil.LessThanOrClose(value, 0))
{
value = 0;
}
// We only want to set the volume if the current cached volume is not the same
// No need to do extra work.
if (!DoubleUtil.AreClose(_volume, value))
{
if (!_muted)
{
int hr = MILMedia.SetVolume(_nativeMedia, value);
HRESULT.Check(hr);
// value is changing
_volume = value;
}
else
{
// If we are muted, cache the volume
_volume = value;
}
}
}
}
///
/// Internal Get Balance
///
///
/// Critical: This is accesses the native media.
/// TreatAsSafe: This is safe because the act of adjusting the balance is a safe one.
///
internal double Balance
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
return _balance;
}
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (Double.IsNaN(value))
{
throw new ArgumentException(SR.Get(SRID.ParameterValueCannotBeNaN), "value");
}
if (DoubleUtil.GreaterThanOrClose(value, 1))
{
value = 1;
}
else if (DoubleUtil.LessThanOrClose(value, -1))
{
value = -1;
}
// We only want to set the balance if the current cached balance
// is not the same. No need to do extra work.
if (!DoubleUtil.AreClose(_balance, value))
{
int hr = MILMedia.SetBalance(_nativeMedia, value);
HRESULT.Check(hr);
// value is changing
_balance = value;
}
}
}
///
/// Whether or not scrubbing is enabled
///
///
/// Critical: This accesses the native media.
/// TreatAsSafe: This is safe because it's safe to adjust whether or not scrubbing is enabled
///
internal bool ScrubbingEnabled
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
return _scrubbingEnabled;
}
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (value != _scrubbingEnabled)
{
HRESULT.Check(MILMedia.SetIsScrubbingEnabled(_nativeMedia, value));
_scrubbingEnabled = value;
}
}
}
///
/// Internal Get Mute
///
internal bool IsMuted
{
get
{
VerifyAPI();
return _muted;
}
set
{
VerifyAPI();
// we need to store the volume since this.Volume will change the cached value
double volume = _volume;
if (value && !_muted)
{
// Going from Unmuted -> Muted
// Set the volume to 0
this.Volume = 0;
_muted = true;
// make sure cached volume is previous value
_volume = volume;
}
else if (!value && _muted)
{
// Going from Muted -> Unmuted
_muted = false;
// set cached volume to 0 since this. Volume will only change volume
// if cached volume and new volume differ
_volume = 0;
// set volume to old cached value, which will also update our current cached value
this.Volume = volume;
}
}
}
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning natural duration is safe
///
internal Duration NaturalDuration
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
long mediaLength = 0;
HRESULT.Check(MILMedia.GetMediaLength(_nativeMedia, ref mediaLength));
if (mediaLength == 0)
{
return Duration.Automatic;
}
else
{
return new Duration(TimeSpan.FromTicks(mediaLength));
}
}
}
///
/// Seek to specified position
///
internal TimeSpan Position
{
set
{
VerifyAPI();
VerifyNotControlledByClock();
SetPosition(value);
}
get
{
VerifyAPI();
return GetPosition();
}
}
///
/// The current speed. This cannot be changed if a clock is controlling this player
///
internal double SpeedRatio
{
get
{
VerifyAPI();
return _speedRatio;
}
set
{
VerifyAPI();
VerifyNotControlledByClock();
if (value < 0)
{
value = 0; // we clamp negative values to 0
}
SetSpeedRatio(value);
}
}
///
/// The dispatcher, this is actually derived from the media player
/// on construction.
///
internal Dispatcher Dispatcher
{
get
{
return _dispatcher;
}
}
#endregion
#region EventHandlers
///
/// Raised when there is an error opening or playing media
///
internal event EventHandler MediaFailed
{
add
{
VerifyAPI();
_mediaEventsHelper.MediaFailed += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.MediaFailed -= value;
}
}
///
/// Raised when the media has been opened.
///
internal event EventHandler MediaOpened
{
add
{
VerifyAPI();
_mediaOpenedHelper.AddEvent(value);
}
remove
{
VerifyAPI();
_mediaOpenedHelper.RemoveEvent(value);
}
}
///
/// Raised when the media has finished.
///
internal event EventHandler MediaEnded
{
add
{
VerifyAPI();
_mediaEventsHelper.MediaEnded += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.MediaEnded -= value;
}
}
///
/// Raised when media begins buffering.
///
internal event EventHandler BufferingStarted
{
add
{
VerifyAPI();
_mediaEventsHelper.BufferingStarted += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.BufferingStarted -= value;
}
}
///
/// Raised when media finishes buffering.
///
internal event EventHandler BufferingEnded
{
add
{
VerifyAPI();
_mediaEventsHelper.BufferingEnded += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.BufferingEnded -= value;
}
}
///
/// Raised when a script command embedded in the media is encountered.
///
internal event EventHandler ScriptCommand
{
add
{
VerifyAPI();
_mediaEventsHelper.ScriptCommand += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.ScriptCommand -= value;
}
}
///
/// Raised when a new frame in the media is encountered, we only
/// send one new frame per AddRefOnChannel in synchronous mode only.
///
internal event EventHandler NewFrame
{
add
{
VerifyAPI();
_newFrameHelper.AddEvent(value);
}
remove
{
VerifyAPI();
_newFrameHelper.RemoveEvent(value);
}
}
#endregion
#region Clock dependent properties and methods
///
/// The clock driving this instance of media
///
internal MediaClock Clock
{
get
{
VerifyAPI();
return _mediaClock;
}
}
internal
void
SetClock(
MediaClock clock,
MediaPlayer player
)
{
VerifyAPI();
MediaClock oldClock = _mediaClock;
MediaClock newClock = clock;
// Avoid infinite loops
if (oldClock != newClock)
{
_mediaClock = newClock;
// Disassociate the old clock
if (oldClock != null)
{
oldClock.Player = null;
}
// Associate the new clock;
if (newClock != null)
{
newClock.Player = player;
}
// According to the spec, setting the Clock to null
// should set the Source to null
if (newClock == null)
{
Open(null);
}
}
}
///
/// Open the media, at this point the underlying native resources are
/// created. The media player cannot be controlled when it isn't opened.
///
internal
void
Open(
Uri source
)
{
VerifyAPI();
VerifyNotControlledByClock();
SetSource(source);
// Workaround for bug 107397: Resuing one instance of MediaElement and
// calling play() wont result in seek to zero, Media Freezes. Ensure
// we set Media to play from the beginning.
SetPosition(TimeSpan.Zero);
}
///
/// Begin playback. This operation is not allowed if a clock is
/// controlling this player
///
internal void Play()
{
VerifyAPI();
VerifyNotControlledByClock();
_paused = false;
PrivateSpeedRatio = SpeedRatio;
}
///
/// Halt playback at current position. This operation is not allowed if
/// a clock is controlling this player
///
internal void Pause()
{
VerifyAPI();
VerifyNotControlledByClock();
_paused = true;
PrivateSpeedRatio = 0;
}
///
/// Halt playback and seek to the beginning of media. This operation is
/// not allowed if a clock is controlling this player
///
internal void Stop()
{
VerifyAPI();
VerifyNotControlledByClock();
Pause();
Position = TimeSpan.FromTicks(0);
}
///
/// Closes the underlying media. This de-allocates all of the native resources in
/// the media. The mediaplayer can be opened again by calling the Open method.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Intrinsically safe to close media.
///
[SecurityCritical, SecurityTreatAsSafe]
internal
void
Close()
{
VerifyAPI();
VerifyNotControlledByClock();
HRESULT.Check(MILMedia.Close(_nativeMedia));
//
// Once we successfully close, we don't have a clock anymore.
// Assign the property so that the clock is disconnected from the
// player as well as the player from the clock.
//
SetClock(null, null);
Init();
}
///
/// Sends a command to play the given media.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Media Command merely binds resource to native player.
///
[SecurityCritical, SecurityTreatAsSafe]
internal
void
SendCommandMedia(
DUCE.Channel channel,
DUCE.ResourceHandle handle,
bool notifyUceDirectly,
bool isRemote
)
{
_isRemote = isRemote;
//
// If this is remote, then we want to marshal over the
// bitmap source we captured from the media (if it
// is available).
//
if (isRemote)
{
//
// If we have a bitmap source and the bitmap source handle
// isn't on this channel, then create it.
//
if (_bitmapSource != null)
{
//
// If we have a bitmap source and we haven't created
// the the _duceHandle, then create it now.
//
if (!_bitmapSourceDuceHandle.IsOnChannel(channel))
{
//
// Create the bitap source duce handle.
//
_bitmapSourceDuceHandle.CreateOrAddRefOnChannel(channel, DUCE.ResourceType.TYPE_BITMAPSOURCE);
}
//
// Send the bitmap source down the channel.
//
channel.SendCommandBitmapSource(
_bitmapSourceDuceHandle.GetHandle(channel),
_bitmapSource,
true, // share the bitmap
true); // system memory bitmap.
}
}
else
{
//
// If we are actually moved onto a local channel, we don't
// wan't our bitmap source lieing around.
//
ClearBitmapSource();
//
// Make sure that the capture resources are closed.
//
CloseCaptureResources();
}
SendMediaPlayerCommand(
channel,
handle,
notifyUceDirectly,
isRemote);
//
// Independently, tell the native media that we need to update the UI, the
// reason we do this directly through the player is that effects can immediately
// remove the channel on us and hence media might not get a chance to see
// the media player resource.
//
if (!notifyUceDirectly)
{
NeedUIFrameUpdate();
//
// We need to force a present on each pass otherwise we won't get any
// frame updates.
//
//
// If this is remote, we want to start capturing the frame if we have
// been opened already.
//
if (isRemote && HasNonEmptyVideo)
{
OpenCaptureResources();
_captureChannel.Present();
_mediaContext.NotifySyncChannelMessage(_captureChannel);
}
}
}
private
bool
HasNonEmptyVideo
{
get
{
return NaturalVideoWidth != 0 && NaturalVideoHeight != 0;
}
}
///
/// Sends a request to the media player to reserve a UI frame for notification.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Asking for a frame update is inherently safe.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
NeedUIFrameUpdate()
{
VerifyAPI();
HRESULT.Check(MILMedia.NeedUIFrameUpdate(_nativeMedia));
}
///
/// Sends a command to play the given media.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Media Command merely binds resource to native player.
///
[SecurityCritical, SecurityTreatAsSafe]
internal
void
ReleaseOnChannel(
DUCE.Channel channel
)
{
//
// If the bitmap source handle is on this channel, then release it.
//
if (_bitmapSourceDuceHandle.IsOnChannel(channel))
{
_bitmapSourceDuceHandle.ReleaseOnChannel(channel);
}
//
// Remove the capture resources.
//
CloseCaptureResources();
}
#endregion
#region Private Methods
///
/// Create the unmanaged media resources
///
///
/// Critical - calls unmanaged code, access pointer parameters. It instantiates
/// windows media player
///
[SecurityCritical, SecurityTreatAsSafe]
private void CreateMedia(MediaPlayer mediaPlayer)
{
CheckMediaDisabledFlags();
SafeMILHandle unmanagedProxy = null;
MediaEventsHelper.CreateMediaEventsHelper(mediaPlayer, out _mediaEventsHelper, out unmanagedProxy);
try
{
using (FactoryMaker myFactory = new FactoryMaker())
{
HRESULT.Check(UnsafeNativeMethods.MILFactory2.CreateMediaPlayer(
myFactory.FactoryPtr,
unmanagedProxy,
SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.AllAudio, MediaPermissionVideo.AllVideo, MediaPermissionImage.NoImage),
MediaSystem.DeviceId,
out _nativeMedia
));
}
}
catch
{
if (_nativeMedia != null && !_nativeMedia.IsInvalid)
{
_nativeMedia.Close();
}
throw;
}
Helper helper = new Helper(_nativeMedia);
AppDomain.CurrentDomain.ProcessExit += helper.ProcessExitHandler;
}
///
/// Open Media
///
///
/// Critical - access critical resource (_nativeMedia), access local file system
/// and also stores the path to base directory in a local variable. It also asserts
/// to allow FileIO to local directory. It calls GetBaseDirectory, that returns sensitive information.
/// TreatAsSafe: This path is sent to the unmanaged layer to be opened. Also it demands
/// fileio for absolute paths and web permissions for files on a server. It only lets you access
/// files in current directory and does not expose the location of current directory
///
[SecurityCritical,SecurityTreatAsSafe]
private void OpenMedia(Uri source)
{
string toOpen = null;
if (source != null && source.IsAbsoluteUri && source.Scheme == PackUriHelper.UriSchemePack)
{
try
{
source = BaseUriHelper.ConvertPackUriToAbsoluteExternallyVisibleUri(source);
}
catch (InvalidOperationException)
{
source = null;
_mediaEventsHelper.RaiseMediaFailed(new System.NotSupportedException(SR.Get(SRID.Media_PackURIsAreNotSupported, null)));
}
}
// Setting a null source effectively disconects the MediaElement.
if (source != null)
{
// keep whether we asserted permissions or not
bool elevated = false;
// get the base directory of the application; never expose this
Uri appBase = SecurityHelper.GetBaseDirectory(AppDomain.CurrentDomain);
// this extracts the URI to open
Uri uriToOpen = ResolveUri(source, appBase);
// access is allowed in the following cases (only 1 & 2 require elevation):
// 1) to any HTTPS media if app is NOT coming from HTTPS
// 2) to URI in the current directory of the fusion cache
// 3) to site of origin media
if (SecurityHelper.AreStringTypesEqual(uriToOpen.Scheme, Uri.UriSchemeHttps))
{
// target is HTTPS. Then, elevate ONLY if we are NOT coming from HTTPS (=XDomain HTTPS app to HTTPS media disallowed)
Uri appDeploymentUri = SecurityHelper.ExtractUriForClickOnceDeployedApp();
if (!SecurityHelper.AreStringTypesEqual(appDeploymentUri.Scheme, Uri.UriSchemeHttps))
{
new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uriToOpen)).Assert();
elevated = true;
}
}
else
{
// elevate to allow access to media in the app's directory in the fusion cache.
new FileIOPermission(FileIOPermissionAccess.Read, appBase.LocalPath).Assert();// BlessedAssert
elevated = true;
}
// demand permissions. if demands succeds, it means we are in one of the cases above.
try
{
toOpen = DemandPermissions(uriToOpen);
}
finally
{
if (elevated)
{
CodeAccessPermission.RevertAssert();
}
}
}
else
{
toOpen = null;
}
// We pass in exact same URI for which we demanded permissions so that we can be sure
// there is no discrepancy between the two.
HRESULT.Check(MILMedia.Open(_nativeMedia, toOpen));
}
///
/// Critical: This code elevates to read registry
/// TreatAsSafe: Detecting whether media is disabled is a safe operation
///
[SecurityCritical,SecurityTreatAsSafe]
private void CheckMediaDisabledFlags()
{
if (SafeSecurityHelper.IsFeatureDisabled(SafeSecurityHelper.KeyToRead.MediaAudioOrVideoDisable))
{
// in case the registry key is '1' then demand
//Demand media permission here for Video or Audio
// Issue: 1232606 need to fix once clr has the media permissions
SecurityHelper.DemandMediaPermission(MediaPermissionAudio.AllAudio,
MediaPermissionVideo.AllVideo,
MediaPermissionImage.NoImage);
}
}
///
/// Critical: This code returns the base directory of the app as a URI
/// IT constructs a Uri based on relative and absolute URI
///
[SecurityCritical, SecurityTreatAsSafe]
private Uri ResolveUri(Uri uri, Uri appBase)
{
if (uri.IsAbsoluteUri)
{
return uri;
}
else
{
return new Uri(appBase, uri);
}
}
// returns the exact string on which we demanded permissions
///
/// Critical: This code is used to safeguard against various forms of attacks
/// to restrict access to loose file passed as relative path in the application
/// base direcory
///
[SecurityCritical, SecurityTreatAsSafe]
private string DemandPermissions(Uri absoluteUri)
{
Debug.Assert(absoluteUri.IsAbsoluteUri);
string toOpen = BindUriHelper.UriToString(absoluteUri);
int targetZone = SecurityHelper.MapUrlToZoneWrapper(absoluteUri);
if (targetZone == NativeMethods.URLZONE_LOCAL_MACHINE)
{
// go here only for files and not for UNC
if (absoluteUri.IsFile)
{
// Please note this pattern is unique and NEEDS TO EXIST , it prevents
// access to any folder but the one where the app is running from.
// PLEASE DO NOT REMOVE THIS DEMAND AND THE ASSERT IN THE CALLING CODE
toOpen = absoluteUri.LocalPath;
(new FileIOPermission(FileIOPermissionAccess.Read, toOpen)).Demand();
}
}
else //Any other zone
{
// UNC path pointing to a file (We filter for `http://intranet)
if (absoluteUri.IsFile && absoluteUri.IsUnc)
{
// perform checks for UNC content
SecurityHelper.EnforceUncContentAccessRules(absoluteUri);
// In this case we first check to see if the consumer has media permissions for
// safe media (Site of Origin + Cross domain).
if (!SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.SafeAudio,
MediaPermissionVideo.SafeVideo,
MediaPermissionImage.NoImage))
{
// if he does not then we demand web permission to allow access only to site of origin
(new FileIOPermission(FileIOPermissionAccess.Read, toOpen)).Demand();
}
}
else // Any other path
{
// In this case we first check to see if the consumer has media permissions for
// safe media (Site of Origin + Cross domain).
if (absoluteUri.Scheme != Uri.UriSchemeHttps)
{
//accessing non https content from an https app is disallowed
SecurityHelper.BlockCrossDomainForHttpsApps(absoluteUri);
if (!SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.SafeAudio,
MediaPermissionVideo.SafeVideo,
MediaPermissionImage.NoImage))
{
// if he does not then we demand web permission to allow access only to site of origin
(new WebPermission(NetworkAccess.Connect, toOpen)).Demand();
}
}
else// This is the case where target content is HTTPS
{
(new WebPermission(NetworkAccess.Connect, toOpen)).Demand();
}
}
}
return toOpen;
}
///
/// Seek to specified position (in 100 nanosecond ticks)
///
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
[SecurityCritical, SecurityTreatAsSafe]
internal void SetPosition(TimeSpan value)
{
VerifyAPI();
HRESULT.Check(MILMedia.SetPosition(_nativeMedia, value.Ticks));
}
///
/// get the current position (in 100 nanosecond ticks)
///
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
[SecurityCritical, SecurityTreatAsSafe]
private TimeSpan GetPosition()
{
VerifyAPI();
long position = 0;
HRESULT.Check(MILMedia.GetPosition(_nativeMedia, ref position));
return TimeSpan.FromTicks(position);
}
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
private double PrivateSpeedRatio
{
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (Double.IsNaN(value))
{
throw new ArgumentException(SR.Get(SRID.ParameterValueCannotBeNaN), "value");
}
HRESULT.Check(MILMedia.SetRate(_nativeMedia, value));
}
}
//
// Set the current speed.
//
internal void SetSpeedRatio(double value)
{
_speedRatio = value;
//
// We don't change the speed if we are paused, unless we are in
// clock mode, which overrides paused mode.
//
if (!_paused || _mediaClock != null)
{
PrivateSpeedRatio = _speedRatio;
}
}
///
/// Sets the source of the media (and opens it), without checking whether
/// we are under clock control. This is called by the clock.
///
internal
void
SetSource(
Uri source
)
{
if (source != _sourceUri)
{
OpenMedia(source);
//
// Only assign the source uri if the OpenMedia succeeds.
//
_sourceUri = source;
}
}
///
/// Verifies this object is in an accessible state, and that we
/// are being called from the correct thread. This method should
/// be the first thing called from any internal method.
///
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
[SecurityCritical, SecurityTreatAsSafe]
private void VerifyAPI()
{
//
// We create _nativeMedia in the constructor, so it should always
// be initialized.
//
Debug.Assert(_nativeMedia != null && !_nativeMedia.IsInvalid);
//
// We only allow calls to any media object on the UI thread.
//
_dispatcher.VerifyAccess();
if (_nativeMedia == null || _nativeMedia.IsInvalid)
{
throw new System.NotSupportedException(SR.Get(SRID.Image_BadVersion));
}
}
///
/// Verifies that this player is not currently controlled by a clock. Some actions are
/// invalid while we are under clock control.
///
private
void
VerifyNotControlledByClock()
{
if (Clock != null)
{
throw new InvalidOperationException(SR.Get(SRID.Media_NotAllowedWhileTimingEngineInControl));
}
}
///
/// Clears the bitmap source.
///
///
/// Critical - calls LinkDemand-protected SafeHandle.Close
/// TreatAsSafe - doesn't accept an arbitrary handle to close
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
ClearBitmapSource()
{
//
// Close the bitmap source,
//
if (_bitmapSource != null)
{
_bitmapSource.Close();
_bitmapSource = null;
}
}
///
/// SendMediaPlayerCommand
/// SecurityNote
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource is treated like any other image.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
SendMediaPlayerCommand(
DUCE.Channel channel,
DUCE.ResourceHandle handle,
bool notifyUceDirectly,
bool isRemote
)
{
//
// This is an interrop call, but, it does not set a last error being a COM call. So, suppress the
// presharp warning about losing last error.
//
#pragma warning disable 6523
//
// AddRef to ensure the media player stays alive during transport, even if the
// MediaPlayer goes away. The slave video resource takes ownership of this AddRef.
// Note there is still a gray danger zone here -- if the channel command is lost
// this reference won't be cleaned up.
//
//
if (!isRemote)
{
UnsafeNativeMethods.MILUnknown.AddRef(_nativeMedia);
}
channel.SendCommandMedia(
handle,
_nativeMedia,
isRemote ? _bitmapSourceDuceHandle.GetHandle(channel) : DUCE.ResourceHandle.Null,
notifyUceDirectly,
isRemote);
#pragma warning restore 6523
}
///
/// Close all of the capture resources.
///
///
/// SecurityCritical: This code references critical data (_captureRenderTargetBitmap).
/// SecurityTreatAsSafe: It just checks for a null value.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
CloseCaptureResources()
{
//
// We don't clear our bitmap source because this can result on us
// getting a channel change without a bitmap source. This is bad.
//
if (null != _captureRenderTargetBitmap)
{
CloseRenderTarget();
}
if (null != _captureChannel)
{
CloseSyncChannel();
}
}
///
/// Open all of the capture resources we need
///
///
/// SecurityCritical: This code references critical data (_captureRenderTargetBitmap).
/// SecurityTreatAsSafe: It just checks for a null value.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
OpenCaptureResources()
{
if (null == _captureChannel)
{
OpenSyncChannel();
}
if (null == _captureRenderTargetBitmap)
{
OpenRenderTarget();
}
}
///
/// Open the synchronous channel.
///
private
void
OpenSyncChannel()
{
_mediaContext = MediaContext.CurrentMediaContext;
//
// Allocate a synchronous channel to receive the result from
// the media player.
//
_captureChannel = _mediaContext.AllocateSyncChannel();
}
///
/// Close the synchronous channel.
///
private
void
CloseSyncChannel()
{
_mediaContext.ReleaseSyncChannel(_captureChannel);
_mediaContext = null;
_captureChannel = null;
}
///
/// Close the render target
///
///
/// Critical - calls LinkDemand-protected SafeHandle.Close. Accesses _captureRenderTargetBitmap.
/// TreatAsSafe - doesn't accept an arbitrary handle to close.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
CloseRenderTarget()
{
_captureRenderTargetBitmap.Close();
_captureRenderTargetBitmap = null;
if (!_renderTargetRoot.Handle.IsNull)
{
DUCE.CompositionNode.RemoveAllChildren(_renderTargetRoot.Handle, _captureChannel);
_renderTargetRoot.ReleaseOnChannel(_captureChannel);
}
}
///
/// Captures the frame from the native media by creating a synchrous channel
/// with a RTBitmap and acquiring the image source.
///
///
/// Critical - access critical resource (_nativeMedia, renderTargetBitmap, _captureRenderTargetBitmap).
/// TreatAsSafe - critical resource is treated like any other image (e.g. DrawImage code):
/// We are sending images because we need to send them across the wire if we are drawing video remotely.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
OpenRenderTarget()
{
uint width = (uint)NaturalVideoWidth;
uint height = (uint)NaturalVideoHeight;
Debug.Assert(width != 0 && height != 0);
using(FactoryMaker myFactory = new FactoryMaker())
{
HRESULT.Check(
UnsafeNativeMethods.MILFactory2.CreateBitmapRenderTarget(
myFactory.FactoryPtr,
width,
height,
PixelFormatEnum.Pbgra32,
_defaultDevicePixelsPerInch,
_defaultDevicePixelsPerInch,
MILRTInitializationFlags.MIL_RT_INITIALIZE_DEFAULT,
out _captureRenderTargetBitmap));
IntPtr pIRenderTargetBitmap = IntPtr.Zero;
Guid iidRenterTargetBitmap = MILGuidData.IID_IMILRenderTargetBitmap;
try
{
//
// We need the render target bitmap.
//
HRESULT.Check(
UnsafeNativeMethods.MILUnknown.QueryInterface(
_captureRenderTargetBitmap,
ref iidRenterTargetBitmap,
out pIRenderTargetBitmap));
//
// Render the frame.
//
RenderFrame(pIRenderTargetBitmap, width, height);
}
finally
{
UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref pIRenderTargetBitmap);
}
}
}
///
/// Renders the frame
///
///
/// Critical - access critical resource (_nativeMedia and renderTargetBitmap)
///
[SecurityCritical]
private
void
RenderFrame(
IntPtr pIRenderTargetBitmap,
uint width,
uint height
)
{
DUCE.Resource target = new DUCE.Resource();
DUCE.Resource mediaPlayer = new DUCE.Resource();
DUCE.Resource renderData = new DUCE.Resource();
DUCE.Resource root = new DUCE.Resource();
try
{
//
// Create the root handle first.
//
target.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_GENERICRENDERTARGET);
mediaPlayer.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_MEDIAPLAYER);
renderData.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_RENDERDATA);
root.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_VISUAL);
//
// Initialize our render target.
//
DUCE.CompositionTarget.PrintInitialize(
target.Handle,
pIRenderTargetBitmap,
(int)width,
(int)height,
_captureChannel);
//
// Create the synchronous video slave resource.
//
SendMediaPlayerCommand(
_captureChannel,
mediaPlayer.Handle,
false, // Don't notify the Uce directly
false); // player is not remote.
//
// Construct the render data command and serialize it to the composition engine.
//
unsafe
{
//
// Set the visual as the root for this generic render target.
//
DUCE.CompositionTarget.SetRoot(
target.Handle,
root.Handle,
_captureChannel);
uint cbData = (uint)(sizeof(RenderData.RecordHeader) + sizeof(MILCMD_DRAW_VIDEO));
DUCE.MILCMD_RENDERDATA renderDataCmd;
renderDataCmd.Type = MILCMD.MilCmdRenderData;
renderDataCmd.Handle = renderData.Handle;
renderDataCmd.cbData = cbData;
_captureChannel.BeginCommand(
(byte*)&renderDataCmd,
sizeof(DUCE.MILCMD_RENDERDATA),
(int)cbData);
RenderData.RecordHeader recordHeader;
recordHeader.Size = (int)cbData;
recordHeader.Id = MILCMD.MilDrawVideo;
_captureChannel.AppendCommandData((byte *)&recordHeader, sizeof(RenderData.RecordHeader));
//
// Provide the render data for the DrawVideo call.
//
MILCMD_DRAW_VIDEO drawVideo
= new MILCMD_DRAW_VIDEO(
(uint)mediaPlayer.Handle,
new Rect(0, 0, width, height));
//
// Send the command to draw the video.
//
_captureChannel.AppendCommandData((byte *)&drawVideo, sizeof(MILCMD_DRAW_VIDEO));
_captureChannel.EndCommand();
//
// Set the content of this composition node to be the render data.
//
DUCE.CompositionNode.SetContent(
root.Handle,
renderData.Handle,
_captureChannel);
}
//
// Commit the visual and render data down the capture channel.
//
_captureChannel.Commit();
//
// Present on the capture channel, this is so we can start getting
// frame updates.
//
_captureChannel.Present();
_mediaContext.NotifySyncChannelMessage(_captureChannel);
}
finally
{
_renderTargetRoot = root;
}
}
///
/// Captures the frame that has been rendered by
///
///
/// Critical - calls native code, accesses _captureRenderTargetBitmap.
/// TreatAsSafe - code just retrieves bitmap which could be done through any
/// renderTargetBitmap.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
CaptureFrame()
{
//
// Now get the bitmap source from the render target.
//
BitmapSourceSafeMILHandle bitmapSource = null;
HRESULT.Check(
MILRenderTargetBitmap.GetBitmap(
_captureRenderTargetBitmap,
out bitmapSource));
//
// Clear the old bitmao source.
//
ClearBitmapSource();
//
// This becomes our new bitmap source, when the resource on the main channel
// is updated,
//
_bitmapSource = bitmapSource;
}
#endregion
#region Event Handlers
///
/// When a new frame is received, we need to check if it is remote and then acquire the new frame
/// from the composition synchronously before passing the NewFrame event up to the
/// Media player.
///
private
void
OnNewFrame(
object sender,
EventArgs args
)
{
if (_isRemote && HasNonEmptyVideo)
{
OpenCaptureResources();
//
// Create synchronous channel here and a RT bitmap, to capture the frame.
//
CaptureFrame();
}
_newFrameHelper.InvokeEvents(sender, args);
}
///
/// Fired when the media is opened, we can't open our capture resources until the media is opened
/// because we won't know the size of the media.
///
private
void
OnMediaOpened(
object sender,
EventArgs args
)
{
//
// If this is on a remote channel, we need to open the capture resources.
//
if (_isRemote && HasNonEmptyVideo)
{
OpenCaptureResources();
}
_mediaOpenedHelper.InvokeEvents(sender, args);
}
#endregion
#region Data Members
///
/// Current volume (ranges from 0 to 1)
///
private double _volume;
///
/// Current balance (ranges from -1 (left) to 1 (right) )
///
private double _balance;
///
/// Current state of mute
///
private bool _muted;
///
/// Whether or not scrubbin is enabled
///
private bool _scrubbingEnabled;
///
/// Unamanaged Media object
///
///
/// Critical - this is a pointer to an unmanaged object that methods are called directly on
///
[SecurityCritical]
private SafeMediaHandle _nativeMedia;
private MediaEventsHelper _mediaEventsHelper;
///
/// Default volume
///
private const double DEFAULT_VOLUME = 0.5;
///
/// Default balance
///
private const double DEFAULT_BALANCE = 0;
private double _speedRatio;
private bool _paused;
private Uri _sourceUri;
private MediaClock _mediaClock = null;
private Dispatcher _dispatcher = null;
private BitmapSourceSafeMILHandle _bitmapSource;
private DUCE.MultiChannelResource _bitmapSourceDuceHandle;
private bool _isRemote;
//
// The channel and render data we use to capture the frames.
//
DUCE.Channel _captureChannel;
MediaContext _mediaContext;
DUCE.Resource _renderTargetRoot;
///
/// _captureRenderTargetBitmap should be marked as SecurityCritical,
/// since it's been obtained under elevation, and if disclosed, could
/// allow rendering on elements untrusted parties shouldn't be alloowed.
///
[SecurityCritical]
SafeMILHandle _captureRenderTargetBitmap;
private UniqueEventHelper _newFrameHelper = new UniqueEventHelper();
private UniqueEventHelper _mediaOpenedHelper = new UniqueEventHelper();
private const float _defaultDevicePixelsPerInch = 96.0F;
///
/// A separate class is needed to register for the ProcessExit event
/// because MediaPlayerState holds a strong reference to _nativeMedia.
/// If a MediaPlayerState method was registered for the ProcessExit
/// event then MediaPlayerState would not be garbage collected until
/// ProcessExit time.
///
private class Helper
{
///
/// Critical - this is a weak reference to a pointer to an unmanaged object
/// on which methods are called directly
///
[SecurityCritical]
private WeakReference _nativeMedia;
///
/// Accesses weak reference to pointer
///
[SecurityCritical]
internal
Helper(
SafeMediaHandle nativeMedia
)
{
_nativeMedia = new WeakReference(nativeMedia);
}
///
/// Accesses weak reference to pointer, calls unmanaged code
///
[SecurityCritical]
internal
void
ProcessExitHandler(
object sender,
EventArgs args
)
{
SafeMediaHandle nativeMedia = (SafeMediaHandle)_nativeMedia.Target;
if (nativeMedia != null)
{
MILMedia.ProcessExitHandler(nativeMedia);
}
}
};
#endregion
}
#endregion
};
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
// Microsoft Avalon
// Copyright (c) Microsoft Corporation, 2003
//
// File: MediaPlayerState.cs
//
//-----------------------------------------------------------------------------
using System;
using System.Threading;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics;
using System.ComponentModel;
using MS.Internal;
using MS.Internal.PresentationCore; // SecurityHelper
using MS.Win32;
using System.IO.Packaging;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows.Media.Composition;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Windows.Navigation;
using System.Runtime.InteropServices;
using System.IO;
using System.Security.AccessControl;//for semaphore access permissions
using System.Net;
using Microsoft.Win32;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
using UnsafeNativeMethods=MS.Win32.PresentationCore.UnsafeNativeMethods;
//
// Disable the warnings that C# emmits when it finds pragmas it does not recognize, this is to
// get rid of false positive PreSharp warning
//
#pragma warning disable 1634, 1691
namespace System.Windows.Media
{
#region MediaPlayerState
///
/// MediaPlayerState
/// Holds all of the local state that is required for playing media. This is
/// separated out into a separate class because MediaPlayer needs to be
/// Animatable, but then that means it needs to be Freezable. However, media
/// state cannot really be frozan (media piplines progress according to time
/// and are very expensive), so instead, we make the "Frozen" object copy the
/// state object around. Doing this will also help in the remote case where
/// we need to handle MediaPlayer quite differently on the channel in the
/// remote and local cases.
///
internal class MediaPlayerState
{
#region Constructors and Finalizers
///
/// Constructor
///
///
/// SecurityCritical: This code sets a critical data member (_captureRenderTargetBitmap).
/// SecurityTreatAsSafe: The critical member is set to a safe value (null).
///
[SecurityCritical, SecurityTreatAsSafe]
internal
MediaPlayerState(
MediaPlayer mediaPlayer
)
{
_dispatcher = mediaPlayer.Dispatcher;
_bitmapSource = null;
_bitmapSourceDuceHandle = new DUCE.MultiChannelResource();
_captureChannel = null;
_captureRenderTargetBitmap = null;
_mediaContext = null;
_renderTargetRoot = DUCE.Resource.Null;
Init();
CreateMedia(mediaPlayer);
//
// We need to know about new frames when they are sent so that we can
// capture the image data in the synchronous case.
//
_mediaEventsHelper.NewFrame += new EventHandler(OnNewFrame);
//
// Opened is actually fired when the media is prerolled.
//
_mediaEventsHelper.MediaPrerolled += new EventHandler(OnMediaOpened);
}
//
///
/// Initialize local variables to their default state. After a close we want to restore this too, same as
/// after construction.
///
private
void
Init()
{
_volume = DEFAULT_VOLUME;
_balance = DEFAULT_BALANCE;
_speedRatio = 1.0;
_paused = false;
_muted = false;
_sourceUri = null;
_scrubbingEnabled = false;
_isRemote = false;
//
// Need to clear our bitmap source.
//
ClearBitmapSource();
//
// Need to close our synchronous render infrastructure.
//
CloseCaptureResources();
}
#endregion
#region Public Methods
///
/// Internal IsBuffering
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia
/// TreatAsSafe: This information about whether buffering is on is safe to expose
///
internal bool IsBuffering
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool isBuffering = false;
HRESULT.Check(MILMedia.IsBuffering(_nativeMedia, ref isBuffering));
return isBuffering;
}
}
///
/// Internal CanPause
///
///
/// Critical: This is critical because it acceses _nativemedia and calls into unmanaged code
/// TreatAsSafe: This is safe to expose since giving out information about
/// whether media can be paused is safe
///
internal bool CanPause
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool canPause = false;
HRESULT.Check(MILMedia.CanPause(_nativeMedia, ref canPause));
return canPause;
}
}
///
/// Internal DownloadProgress
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning download progress is safe
///
internal double DownloadProgress
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
double downloadProgress = 0;
HRESULT.Check(MILMedia.GetDownloadProgress(_nativeMedia, ref downloadProgress));
return downloadProgress;
}
}
///
/// Internal BufferingProgress
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning buffering progress is safe
///
internal double BufferingProgress
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
double bufferingProgress = 0;
HRESULT.Check(MILMedia.GetBufferingProgress(_nativeMedia, ref bufferingProgress));
return bufferingProgress;
}
}
///
/// Returns the Height
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning natural video height is safe
///
internal Int32 NaturalVideoHeight
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
UInt32 height = 0;
HRESULT.Check(MILMedia.GetNaturalHeight(_nativeMedia, ref height));
return (Int32)height;
}
}
///
/// Returns the Width
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning natural video width is safe
///
internal Int32 NaturalVideoWidth
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
UInt32 width = 0;
HRESULT.Check(MILMedia.GetNaturalWidth(_nativeMedia, ref width));
return (Int32)width;
}
}
///
/// If media has audio content
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning whether media has audio is safe
///
internal bool HasAudio
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool hasAudio = true;
HRESULT.Check(MILMedia.HasAudio(_nativeMedia, ref hasAudio));
return hasAudio;
}
}
///
/// If the media has video content
///
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning whether media has video is safe
///
internal bool HasVideo
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
bool hasVideo = false;
HRESULT.Check(MILMedia.HasVideo(_nativeMedia, ref hasVideo));
return hasVideo;
}
}
///
/// Location of the media to play. Open opens the media, this property
/// allows the source that is currently playing to be retrieved.
///
internal Uri Source
{
get
{
VerifyAPI();
return _sourceUri;
}
}
///
/// Internal Get Volume
///
///
/// Critical: This is critical because it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning volume ratio is safe and so also is setting it
///
internal double Volume
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
return _volume;
}
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (Double.IsNaN(value))
{
throw new ArgumentException(SR.Get(SRID.ParameterValueCannotBeNaN), "value");
}
if (DoubleUtil.GreaterThanOrClose(value, 1))
{
value = 1;
}
else if (DoubleUtil.LessThanOrClose(value, 0))
{
value = 0;
}
// We only want to set the volume if the current cached volume is not the same
// No need to do extra work.
if (!DoubleUtil.AreClose(_volume, value))
{
if (!_muted)
{
int hr = MILMedia.SetVolume(_nativeMedia, value);
HRESULT.Check(hr);
// value is changing
_volume = value;
}
else
{
// If we are muted, cache the volume
_volume = value;
}
}
}
}
///
/// Internal Get Balance
///
///
/// Critical: This is accesses the native media.
/// TreatAsSafe: This is safe because the act of adjusting the balance is a safe one.
///
internal double Balance
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
return _balance;
}
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (Double.IsNaN(value))
{
throw new ArgumentException(SR.Get(SRID.ParameterValueCannotBeNaN), "value");
}
if (DoubleUtil.GreaterThanOrClose(value, 1))
{
value = 1;
}
else if (DoubleUtil.LessThanOrClose(value, -1))
{
value = -1;
}
// We only want to set the balance if the current cached balance
// is not the same. No need to do extra work.
if (!DoubleUtil.AreClose(_balance, value))
{
int hr = MILMedia.SetBalance(_nativeMedia, value);
HRESULT.Check(hr);
// value is changing
_balance = value;
}
}
}
///
/// Whether or not scrubbing is enabled
///
///
/// Critical: This accesses the native media.
/// TreatAsSafe: This is safe because it's safe to adjust whether or not scrubbing is enabled
///
internal bool ScrubbingEnabled
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
return _scrubbingEnabled;
}
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (value != _scrubbingEnabled)
{
HRESULT.Check(MILMedia.SetIsScrubbingEnabled(_nativeMedia, value));
_scrubbingEnabled = value;
}
}
}
///
/// Internal Get Mute
///
internal bool IsMuted
{
get
{
VerifyAPI();
return _muted;
}
set
{
VerifyAPI();
// we need to store the volume since this.Volume will change the cached value
double volume = _volume;
if (value && !_muted)
{
// Going from Unmuted -> Muted
// Set the volume to 0
this.Volume = 0;
_muted = true;
// make sure cached volume is previous value
_volume = volume;
}
else if (!value && _muted)
{
// Going from Muted -> Unmuted
_muted = false;
// set cached volume to 0 since this. Volume will only change volume
// if cached volume and new volume differ
_volume = 0;
// set volume to old cached value, which will also update our current cached value
this.Volume = volume;
}
}
}
///
/// Critical: This is critical it calls into unmanged code and accesses _nativeMedia
/// TreatAsSafe: This is safe because the critical resource is not exposed and
/// returning natural duration is safe
///
internal Duration NaturalDuration
{
[SecurityCritical, SecurityTreatAsSafe]
get
{
VerifyAPI();
long mediaLength = 0;
HRESULT.Check(MILMedia.GetMediaLength(_nativeMedia, ref mediaLength));
if (mediaLength == 0)
{
return Duration.Automatic;
}
else
{
return new Duration(TimeSpan.FromTicks(mediaLength));
}
}
}
///
/// Seek to specified position
///
internal TimeSpan Position
{
set
{
VerifyAPI();
VerifyNotControlledByClock();
SetPosition(value);
}
get
{
VerifyAPI();
return GetPosition();
}
}
///
/// The current speed. This cannot be changed if a clock is controlling this player
///
internal double SpeedRatio
{
get
{
VerifyAPI();
return _speedRatio;
}
set
{
VerifyAPI();
VerifyNotControlledByClock();
if (value < 0)
{
value = 0; // we clamp negative values to 0
}
SetSpeedRatio(value);
}
}
///
/// The dispatcher, this is actually derived from the media player
/// on construction.
///
internal Dispatcher Dispatcher
{
get
{
return _dispatcher;
}
}
#endregion
#region EventHandlers
///
/// Raised when there is an error opening or playing media
///
internal event EventHandler MediaFailed
{
add
{
VerifyAPI();
_mediaEventsHelper.MediaFailed += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.MediaFailed -= value;
}
}
///
/// Raised when the media has been opened.
///
internal event EventHandler MediaOpened
{
add
{
VerifyAPI();
_mediaOpenedHelper.AddEvent(value);
}
remove
{
VerifyAPI();
_mediaOpenedHelper.RemoveEvent(value);
}
}
///
/// Raised when the media has finished.
///
internal event EventHandler MediaEnded
{
add
{
VerifyAPI();
_mediaEventsHelper.MediaEnded += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.MediaEnded -= value;
}
}
///
/// Raised when media begins buffering.
///
internal event EventHandler BufferingStarted
{
add
{
VerifyAPI();
_mediaEventsHelper.BufferingStarted += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.BufferingStarted -= value;
}
}
///
/// Raised when media finishes buffering.
///
internal event EventHandler BufferingEnded
{
add
{
VerifyAPI();
_mediaEventsHelper.BufferingEnded += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.BufferingEnded -= value;
}
}
///
/// Raised when a script command embedded in the media is encountered.
///
internal event EventHandler ScriptCommand
{
add
{
VerifyAPI();
_mediaEventsHelper.ScriptCommand += value;
}
remove
{
VerifyAPI();
_mediaEventsHelper.ScriptCommand -= value;
}
}
///
/// Raised when a new frame in the media is encountered, we only
/// send one new frame per AddRefOnChannel in synchronous mode only.
///
internal event EventHandler NewFrame
{
add
{
VerifyAPI();
_newFrameHelper.AddEvent(value);
}
remove
{
VerifyAPI();
_newFrameHelper.RemoveEvent(value);
}
}
#endregion
#region Clock dependent properties and methods
///
/// The clock driving this instance of media
///
internal MediaClock Clock
{
get
{
VerifyAPI();
return _mediaClock;
}
}
internal
void
SetClock(
MediaClock clock,
MediaPlayer player
)
{
VerifyAPI();
MediaClock oldClock = _mediaClock;
MediaClock newClock = clock;
// Avoid infinite loops
if (oldClock != newClock)
{
_mediaClock = newClock;
// Disassociate the old clock
if (oldClock != null)
{
oldClock.Player = null;
}
// Associate the new clock;
if (newClock != null)
{
newClock.Player = player;
}
// According to the spec, setting the Clock to null
// should set the Source to null
if (newClock == null)
{
Open(null);
}
}
}
///
/// Open the media, at this point the underlying native resources are
/// created. The media player cannot be controlled when it isn't opened.
///
internal
void
Open(
Uri source
)
{
VerifyAPI();
VerifyNotControlledByClock();
SetSource(source);
// Workaround for bug 107397: Resuing one instance of MediaElement and
// calling play() wont result in seek to zero, Media Freezes. Ensure
// we set Media to play from the beginning.
SetPosition(TimeSpan.Zero);
}
///
/// Begin playback. This operation is not allowed if a clock is
/// controlling this player
///
internal void Play()
{
VerifyAPI();
VerifyNotControlledByClock();
_paused = false;
PrivateSpeedRatio = SpeedRatio;
}
///
/// Halt playback at current position. This operation is not allowed if
/// a clock is controlling this player
///
internal void Pause()
{
VerifyAPI();
VerifyNotControlledByClock();
_paused = true;
PrivateSpeedRatio = 0;
}
///
/// Halt playback and seek to the beginning of media. This operation is
/// not allowed if a clock is controlling this player
///
internal void Stop()
{
VerifyAPI();
VerifyNotControlledByClock();
Pause();
Position = TimeSpan.FromTicks(0);
}
///
/// Closes the underlying media. This de-allocates all of the native resources in
/// the media. The mediaplayer can be opened again by calling the Open method.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Intrinsically safe to close media.
///
[SecurityCritical, SecurityTreatAsSafe]
internal
void
Close()
{
VerifyAPI();
VerifyNotControlledByClock();
HRESULT.Check(MILMedia.Close(_nativeMedia));
//
// Once we successfully close, we don't have a clock anymore.
// Assign the property so that the clock is disconnected from the
// player as well as the player from the clock.
//
SetClock(null, null);
Init();
}
///
/// Sends a command to play the given media.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Media Command merely binds resource to native player.
///
[SecurityCritical, SecurityTreatAsSafe]
internal
void
SendCommandMedia(
DUCE.Channel channel,
DUCE.ResourceHandle handle,
bool notifyUceDirectly,
bool isRemote
)
{
_isRemote = isRemote;
//
// If this is remote, then we want to marshal over the
// bitmap source we captured from the media (if it
// is available).
//
if (isRemote)
{
//
// If we have a bitmap source and the bitmap source handle
// isn't on this channel, then create it.
//
if (_bitmapSource != null)
{
//
// If we have a bitmap source and we haven't created
// the the _duceHandle, then create it now.
//
if (!_bitmapSourceDuceHandle.IsOnChannel(channel))
{
//
// Create the bitap source duce handle.
//
_bitmapSourceDuceHandle.CreateOrAddRefOnChannel(channel, DUCE.ResourceType.TYPE_BITMAPSOURCE);
}
//
// Send the bitmap source down the channel.
//
channel.SendCommandBitmapSource(
_bitmapSourceDuceHandle.GetHandle(channel),
_bitmapSource,
true, // share the bitmap
true); // system memory bitmap.
}
}
else
{
//
// If we are actually moved onto a local channel, we don't
// wan't our bitmap source lieing around.
//
ClearBitmapSource();
//
// Make sure that the capture resources are closed.
//
CloseCaptureResources();
}
SendMediaPlayerCommand(
channel,
handle,
notifyUceDirectly,
isRemote);
//
// Independently, tell the native media that we need to update the UI, the
// reason we do this directly through the player is that effects can immediately
// remove the channel on us and hence media might not get a chance to see
// the media player resource.
//
if (!notifyUceDirectly)
{
NeedUIFrameUpdate();
//
// We need to force a present on each pass otherwise we won't get any
// frame updates.
//
//
// If this is remote, we want to start capturing the frame if we have
// been opened already.
//
if (isRemote && HasNonEmptyVideo)
{
OpenCaptureResources();
_captureChannel.Present();
_mediaContext.NotifySyncChannelMessage(_captureChannel);
}
}
}
private
bool
HasNonEmptyVideo
{
get
{
return NaturalVideoWidth != 0 && NaturalVideoHeight != 0;
}
}
///
/// Sends a request to the media player to reserve a UI frame for notification.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Asking for a frame update is inherently safe.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
NeedUIFrameUpdate()
{
VerifyAPI();
HRESULT.Check(MILMedia.NeedUIFrameUpdate(_nativeMedia));
}
///
/// Sends a command to play the given media.
///
///
/// Critical: This calls into unmanaged code and also acceses _nativemedia.
/// TreatAsSafe: Media Command merely binds resource to native player.
///
[SecurityCritical, SecurityTreatAsSafe]
internal
void
ReleaseOnChannel(
DUCE.Channel channel
)
{
//
// If the bitmap source handle is on this channel, then release it.
//
if (_bitmapSourceDuceHandle.IsOnChannel(channel))
{
_bitmapSourceDuceHandle.ReleaseOnChannel(channel);
}
//
// Remove the capture resources.
//
CloseCaptureResources();
}
#endregion
#region Private Methods
///
/// Create the unmanaged media resources
///
///
/// Critical - calls unmanaged code, access pointer parameters. It instantiates
/// windows media player
///
[SecurityCritical, SecurityTreatAsSafe]
private void CreateMedia(MediaPlayer mediaPlayer)
{
CheckMediaDisabledFlags();
SafeMILHandle unmanagedProxy = null;
MediaEventsHelper.CreateMediaEventsHelper(mediaPlayer, out _mediaEventsHelper, out unmanagedProxy);
try
{
using (FactoryMaker myFactory = new FactoryMaker())
{
HRESULT.Check(UnsafeNativeMethods.MILFactory2.CreateMediaPlayer(
myFactory.FactoryPtr,
unmanagedProxy,
SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.AllAudio, MediaPermissionVideo.AllVideo, MediaPermissionImage.NoImage),
MediaSystem.DeviceId,
out _nativeMedia
));
}
}
catch
{
if (_nativeMedia != null && !_nativeMedia.IsInvalid)
{
_nativeMedia.Close();
}
throw;
}
Helper helper = new Helper(_nativeMedia);
AppDomain.CurrentDomain.ProcessExit += helper.ProcessExitHandler;
}
///
/// Open Media
///
///
/// Critical - access critical resource (_nativeMedia), access local file system
/// and also stores the path to base directory in a local variable. It also asserts
/// to allow FileIO to local directory. It calls GetBaseDirectory, that returns sensitive information.
/// TreatAsSafe: This path is sent to the unmanaged layer to be opened. Also it demands
/// fileio for absolute paths and web permissions for files on a server. It only lets you access
/// files in current directory and does not expose the location of current directory
///
[SecurityCritical,SecurityTreatAsSafe]
private void OpenMedia(Uri source)
{
string toOpen = null;
if (source != null && source.IsAbsoluteUri && source.Scheme == PackUriHelper.UriSchemePack)
{
try
{
source = BaseUriHelper.ConvertPackUriToAbsoluteExternallyVisibleUri(source);
}
catch (InvalidOperationException)
{
source = null;
_mediaEventsHelper.RaiseMediaFailed(new System.NotSupportedException(SR.Get(SRID.Media_PackURIsAreNotSupported, null)));
}
}
// Setting a null source effectively disconects the MediaElement.
if (source != null)
{
// keep whether we asserted permissions or not
bool elevated = false;
// get the base directory of the application; never expose this
Uri appBase = SecurityHelper.GetBaseDirectory(AppDomain.CurrentDomain);
// this extracts the URI to open
Uri uriToOpen = ResolveUri(source, appBase);
// access is allowed in the following cases (only 1 & 2 require elevation):
// 1) to any HTTPS media if app is NOT coming from HTTPS
// 2) to URI in the current directory of the fusion cache
// 3) to site of origin media
if (SecurityHelper.AreStringTypesEqual(uriToOpen.Scheme, Uri.UriSchemeHttps))
{
// target is HTTPS. Then, elevate ONLY if we are NOT coming from HTTPS (=XDomain HTTPS app to HTTPS media disallowed)
Uri appDeploymentUri = SecurityHelper.ExtractUriForClickOnceDeployedApp();
if (!SecurityHelper.AreStringTypesEqual(appDeploymentUri.Scheme, Uri.UriSchemeHttps))
{
new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uriToOpen)).Assert();
elevated = true;
}
}
else
{
// elevate to allow access to media in the app's directory in the fusion cache.
new FileIOPermission(FileIOPermissionAccess.Read, appBase.LocalPath).Assert();// BlessedAssert
elevated = true;
}
// demand permissions. if demands succeds, it means we are in one of the cases above.
try
{
toOpen = DemandPermissions(uriToOpen);
}
finally
{
if (elevated)
{
CodeAccessPermission.RevertAssert();
}
}
}
else
{
toOpen = null;
}
// We pass in exact same URI for which we demanded permissions so that we can be sure
// there is no discrepancy between the two.
HRESULT.Check(MILMedia.Open(_nativeMedia, toOpen));
}
///
/// Critical: This code elevates to read registry
/// TreatAsSafe: Detecting whether media is disabled is a safe operation
///
[SecurityCritical,SecurityTreatAsSafe]
private void CheckMediaDisabledFlags()
{
if (SafeSecurityHelper.IsFeatureDisabled(SafeSecurityHelper.KeyToRead.MediaAudioOrVideoDisable))
{
// in case the registry key is '1' then demand
//Demand media permission here for Video or Audio
// Issue: 1232606 need to fix once clr has the media permissions
SecurityHelper.DemandMediaPermission(MediaPermissionAudio.AllAudio,
MediaPermissionVideo.AllVideo,
MediaPermissionImage.NoImage);
}
}
///
/// Critical: This code returns the base directory of the app as a URI
/// IT constructs a Uri based on relative and absolute URI
///
[SecurityCritical, SecurityTreatAsSafe]
private Uri ResolveUri(Uri uri, Uri appBase)
{
if (uri.IsAbsoluteUri)
{
return uri;
}
else
{
return new Uri(appBase, uri);
}
}
// returns the exact string on which we demanded permissions
///
/// Critical: This code is used to safeguard against various forms of attacks
/// to restrict access to loose file passed as relative path in the application
/// base direcory
///
[SecurityCritical, SecurityTreatAsSafe]
private string DemandPermissions(Uri absoluteUri)
{
Debug.Assert(absoluteUri.IsAbsoluteUri);
string toOpen = BindUriHelper.UriToString(absoluteUri);
int targetZone = SecurityHelper.MapUrlToZoneWrapper(absoluteUri);
if (targetZone == NativeMethods.URLZONE_LOCAL_MACHINE)
{
// go here only for files and not for UNC
if (absoluteUri.IsFile)
{
// Please note this pattern is unique and NEEDS TO EXIST , it prevents
// access to any folder but the one where the app is running from.
// PLEASE DO NOT REMOVE THIS DEMAND AND THE ASSERT IN THE CALLING CODE
toOpen = absoluteUri.LocalPath;
(new FileIOPermission(FileIOPermissionAccess.Read, toOpen)).Demand();
}
}
else //Any other zone
{
// UNC path pointing to a file (We filter for `http://intranet)
if (absoluteUri.IsFile && absoluteUri.IsUnc)
{
// perform checks for UNC content
SecurityHelper.EnforceUncContentAccessRules(absoluteUri);
// In this case we first check to see if the consumer has media permissions for
// safe media (Site of Origin + Cross domain).
if (!SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.SafeAudio,
MediaPermissionVideo.SafeVideo,
MediaPermissionImage.NoImage))
{
// if he does not then we demand web permission to allow access only to site of origin
(new FileIOPermission(FileIOPermissionAccess.Read, toOpen)).Demand();
}
}
else // Any other path
{
// In this case we first check to see if the consumer has media permissions for
// safe media (Site of Origin + Cross domain).
if (absoluteUri.Scheme != Uri.UriSchemeHttps)
{
//accessing non https content from an https app is disallowed
SecurityHelper.BlockCrossDomainForHttpsApps(absoluteUri);
if (!SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.SafeAudio,
MediaPermissionVideo.SafeVideo,
MediaPermissionImage.NoImage))
{
// if he does not then we demand web permission to allow access only to site of origin
(new WebPermission(NetworkAccess.Connect, toOpen)).Demand();
}
}
else// This is the case where target content is HTTPS
{
(new WebPermission(NetworkAccess.Connect, toOpen)).Demand();
}
}
}
return toOpen;
}
///
/// Seek to specified position (in 100 nanosecond ticks)
///
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
[SecurityCritical, SecurityTreatAsSafe]
internal void SetPosition(TimeSpan value)
{
VerifyAPI();
HRESULT.Check(MILMedia.SetPosition(_nativeMedia, value.Ticks));
}
///
/// get the current position (in 100 nanosecond ticks)
///
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
[SecurityCritical, SecurityTreatAsSafe]
private TimeSpan GetPosition()
{
VerifyAPI();
long position = 0;
HRESULT.Check(MILMedia.GetPosition(_nativeMedia, ref position));
return TimeSpan.FromTicks(position);
}
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
private double PrivateSpeedRatio
{
[SecurityCritical, SecurityTreatAsSafe]
set
{
VerifyAPI();
if (Double.IsNaN(value))
{
throw new ArgumentException(SR.Get(SRID.ParameterValueCannotBeNaN), "value");
}
HRESULT.Check(MILMedia.SetRate(_nativeMedia, value));
}
}
//
// Set the current speed.
//
internal void SetSpeedRatio(double value)
{
_speedRatio = value;
//
// We don't change the speed if we are paused, unless we are in
// clock mode, which overrides paused mode.
//
if (!_paused || _mediaClock != null)
{
PrivateSpeedRatio = _speedRatio;
}
}
///
/// Sets the source of the media (and opens it), without checking whether
/// we are under clock control. This is called by the clock.
///
internal
void
SetSource(
Uri source
)
{
if (source != _sourceUri)
{
OpenMedia(source);
//
// Only assign the source uri if the OpenMedia succeeds.
//
_sourceUri = source;
}
}
///
/// Verifies this object is in an accessible state, and that we
/// are being called from the correct thread. This method should
/// be the first thing called from any internal method.
///
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource isn't modified or handed out
///
[SecurityCritical, SecurityTreatAsSafe]
private void VerifyAPI()
{
//
// We create _nativeMedia in the constructor, so it should always
// be initialized.
//
Debug.Assert(_nativeMedia != null && !_nativeMedia.IsInvalid);
//
// We only allow calls to any media object on the UI thread.
//
_dispatcher.VerifyAccess();
if (_nativeMedia == null || _nativeMedia.IsInvalid)
{
throw new System.NotSupportedException(SR.Get(SRID.Image_BadVersion));
}
}
///
/// Verifies that this player is not currently controlled by a clock. Some actions are
/// invalid while we are under clock control.
///
private
void
VerifyNotControlledByClock()
{
if (Clock != null)
{
throw new InvalidOperationException(SR.Get(SRID.Media_NotAllowedWhileTimingEngineInControl));
}
}
///
/// Clears the bitmap source.
///
///
/// Critical - calls LinkDemand-protected SafeHandle.Close
/// TreatAsSafe - doesn't accept an arbitrary handle to close
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
ClearBitmapSource()
{
//
// Close the bitmap source,
//
if (_bitmapSource != null)
{
_bitmapSource.Close();
_bitmapSource = null;
}
}
///
/// SendMediaPlayerCommand
/// SecurityNote
///
/// Critical - access critical resource (_nativeMedia)
/// TreatAsSafe - critical resource is treated like any other image.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
SendMediaPlayerCommand(
DUCE.Channel channel,
DUCE.ResourceHandle handle,
bool notifyUceDirectly,
bool isRemote
)
{
//
// This is an interrop call, but, it does not set a last error being a COM call. So, suppress the
// presharp warning about losing last error.
//
#pragma warning disable 6523
//
// AddRef to ensure the media player stays alive during transport, even if the
// MediaPlayer goes away. The slave video resource takes ownership of this AddRef.
// Note there is still a gray danger zone here -- if the channel command is lost
// this reference won't be cleaned up.
//
//
if (!isRemote)
{
UnsafeNativeMethods.MILUnknown.AddRef(_nativeMedia);
}
channel.SendCommandMedia(
handle,
_nativeMedia,
isRemote ? _bitmapSourceDuceHandle.GetHandle(channel) : DUCE.ResourceHandle.Null,
notifyUceDirectly,
isRemote);
#pragma warning restore 6523
}
///
/// Close all of the capture resources.
///
///
/// SecurityCritical: This code references critical data (_captureRenderTargetBitmap).
/// SecurityTreatAsSafe: It just checks for a null value.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
CloseCaptureResources()
{
//
// We don't clear our bitmap source because this can result on us
// getting a channel change without a bitmap source. This is bad.
//
if (null != _captureRenderTargetBitmap)
{
CloseRenderTarget();
}
if (null != _captureChannel)
{
CloseSyncChannel();
}
}
///
/// Open all of the capture resources we need
///
///
/// SecurityCritical: This code references critical data (_captureRenderTargetBitmap).
/// SecurityTreatAsSafe: It just checks for a null value.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
OpenCaptureResources()
{
if (null == _captureChannel)
{
OpenSyncChannel();
}
if (null == _captureRenderTargetBitmap)
{
OpenRenderTarget();
}
}
///
/// Open the synchronous channel.
///
private
void
OpenSyncChannel()
{
_mediaContext = MediaContext.CurrentMediaContext;
//
// Allocate a synchronous channel to receive the result from
// the media player.
//
_captureChannel = _mediaContext.AllocateSyncChannel();
}
///
/// Close the synchronous channel.
///
private
void
CloseSyncChannel()
{
_mediaContext.ReleaseSyncChannel(_captureChannel);
_mediaContext = null;
_captureChannel = null;
}
///
/// Close the render target
///
///
/// Critical - calls LinkDemand-protected SafeHandle.Close. Accesses _captureRenderTargetBitmap.
/// TreatAsSafe - doesn't accept an arbitrary handle to close.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
CloseRenderTarget()
{
_captureRenderTargetBitmap.Close();
_captureRenderTargetBitmap = null;
if (!_renderTargetRoot.Handle.IsNull)
{
DUCE.CompositionNode.RemoveAllChildren(_renderTargetRoot.Handle, _captureChannel);
_renderTargetRoot.ReleaseOnChannel(_captureChannel);
}
}
///
/// Captures the frame from the native media by creating a synchrous channel
/// with a RTBitmap and acquiring the image source.
///
///
/// Critical - access critical resource (_nativeMedia, renderTargetBitmap, _captureRenderTargetBitmap).
/// TreatAsSafe - critical resource is treated like any other image (e.g. DrawImage code):
/// We are sending images because we need to send them across the wire if we are drawing video remotely.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
OpenRenderTarget()
{
uint width = (uint)NaturalVideoWidth;
uint height = (uint)NaturalVideoHeight;
Debug.Assert(width != 0 && height != 0);
using(FactoryMaker myFactory = new FactoryMaker())
{
HRESULT.Check(
UnsafeNativeMethods.MILFactory2.CreateBitmapRenderTarget(
myFactory.FactoryPtr,
width,
height,
PixelFormatEnum.Pbgra32,
_defaultDevicePixelsPerInch,
_defaultDevicePixelsPerInch,
MILRTInitializationFlags.MIL_RT_INITIALIZE_DEFAULT,
out _captureRenderTargetBitmap));
IntPtr pIRenderTargetBitmap = IntPtr.Zero;
Guid iidRenterTargetBitmap = MILGuidData.IID_IMILRenderTargetBitmap;
try
{
//
// We need the render target bitmap.
//
HRESULT.Check(
UnsafeNativeMethods.MILUnknown.QueryInterface(
_captureRenderTargetBitmap,
ref iidRenterTargetBitmap,
out pIRenderTargetBitmap));
//
// Render the frame.
//
RenderFrame(pIRenderTargetBitmap, width, height);
}
finally
{
UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref pIRenderTargetBitmap);
}
}
}
///
/// Renders the frame
///
///
/// Critical - access critical resource (_nativeMedia and renderTargetBitmap)
///
[SecurityCritical]
private
void
RenderFrame(
IntPtr pIRenderTargetBitmap,
uint width,
uint height
)
{
DUCE.Resource target = new DUCE.Resource();
DUCE.Resource mediaPlayer = new DUCE.Resource();
DUCE.Resource renderData = new DUCE.Resource();
DUCE.Resource root = new DUCE.Resource();
try
{
//
// Create the root handle first.
//
target.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_GENERICRENDERTARGET);
mediaPlayer.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_MEDIAPLAYER);
renderData.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_RENDERDATA);
root.CreateOrAddRefOnChannel(_captureChannel, DUCE.ResourceType.TYPE_VISUAL);
//
// Initialize our render target.
//
DUCE.CompositionTarget.PrintInitialize(
target.Handle,
pIRenderTargetBitmap,
(int)width,
(int)height,
_captureChannel);
//
// Create the synchronous video slave resource.
//
SendMediaPlayerCommand(
_captureChannel,
mediaPlayer.Handle,
false, // Don't notify the Uce directly
false); // player is not remote.
//
// Construct the render data command and serialize it to the composition engine.
//
unsafe
{
//
// Set the visual as the root for this generic render target.
//
DUCE.CompositionTarget.SetRoot(
target.Handle,
root.Handle,
_captureChannel);
uint cbData = (uint)(sizeof(RenderData.RecordHeader) + sizeof(MILCMD_DRAW_VIDEO));
DUCE.MILCMD_RENDERDATA renderDataCmd;
renderDataCmd.Type = MILCMD.MilCmdRenderData;
renderDataCmd.Handle = renderData.Handle;
renderDataCmd.cbData = cbData;
_captureChannel.BeginCommand(
(byte*)&renderDataCmd,
sizeof(DUCE.MILCMD_RENDERDATA),
(int)cbData);
RenderData.RecordHeader recordHeader;
recordHeader.Size = (int)cbData;
recordHeader.Id = MILCMD.MilDrawVideo;
_captureChannel.AppendCommandData((byte *)&recordHeader, sizeof(RenderData.RecordHeader));
//
// Provide the render data for the DrawVideo call.
//
MILCMD_DRAW_VIDEO drawVideo
= new MILCMD_DRAW_VIDEO(
(uint)mediaPlayer.Handle,
new Rect(0, 0, width, height));
//
// Send the command to draw the video.
//
_captureChannel.AppendCommandData((byte *)&drawVideo, sizeof(MILCMD_DRAW_VIDEO));
_captureChannel.EndCommand();
//
// Set the content of this composition node to be the render data.
//
DUCE.CompositionNode.SetContent(
root.Handle,
renderData.Handle,
_captureChannel);
}
//
// Commit the visual and render data down the capture channel.
//
_captureChannel.Commit();
//
// Present on the capture channel, this is so we can start getting
// frame updates.
//
_captureChannel.Present();
_mediaContext.NotifySyncChannelMessage(_captureChannel);
}
finally
{
_renderTargetRoot = root;
}
}
///
/// Captures the frame that has been rendered by
///
///
/// Critical - calls native code, accesses _captureRenderTargetBitmap.
/// TreatAsSafe - code just retrieves bitmap which could be done through any
/// renderTargetBitmap.
///
[SecurityCritical, SecurityTreatAsSafe]
private
void
CaptureFrame()
{
//
// Now get the bitmap source from the render target.
//
BitmapSourceSafeMILHandle bitmapSource = null;
HRESULT.Check(
MILRenderTargetBitmap.GetBitmap(
_captureRenderTargetBitmap,
out bitmapSource));
//
// Clear the old bitmao source.
//
ClearBitmapSource();
//
// This becomes our new bitmap source, when the resource on the main channel
// is updated,
//
_bitmapSource = bitmapSource;
}
#endregion
#region Event Handlers
///
/// When a new frame is received, we need to check if it is remote and then acquire the new frame
/// from the composition synchronously before passing the NewFrame event up to the
/// Media player.
///
private
void
OnNewFrame(
object sender,
EventArgs args
)
{
if (_isRemote && HasNonEmptyVideo)
{
OpenCaptureResources();
//
// Create synchronous channel here and a RT bitmap, to capture the frame.
//
CaptureFrame();
}
_newFrameHelper.InvokeEvents(sender, args);
}
///
/// Fired when the media is opened, we can't open our capture resources until the media is opened
/// because we won't know the size of the media.
///
private
void
OnMediaOpened(
object sender,
EventArgs args
)
{
//
// If this is on a remote channel, we need to open the capture resources.
//
if (_isRemote && HasNonEmptyVideo)
{
OpenCaptureResources();
}
_mediaOpenedHelper.InvokeEvents(sender, args);
}
#endregion
#region Data Members
///
/// Current volume (ranges from 0 to 1)
///
private double _volume;
///
/// Current balance (ranges from -1 (left) to 1 (right) )
///
private double _balance;
///
/// Current state of mute
///
private bool _muted;
///
/// Whether or not scrubbin is enabled
///
private bool _scrubbingEnabled;
///
/// Unamanaged Media object
///
///
/// Critical - this is a pointer to an unmanaged object that methods are called directly on
///
[SecurityCritical]
private SafeMediaHandle _nativeMedia;
private MediaEventsHelper _mediaEventsHelper;
///
/// Default volume
///
private const double DEFAULT_VOLUME = 0.5;
///
/// Default balance
///
private const double DEFAULT_BALANCE = 0;
private double _speedRatio;
private bool _paused;
private Uri _sourceUri;
private MediaClock _mediaClock = null;
private Dispatcher _dispatcher = null;
private BitmapSourceSafeMILHandle _bitmapSource;
private DUCE.MultiChannelResource _bitmapSourceDuceHandle;
private bool _isRemote;
//
// The channel and render data we use to capture the frames.
//
DUCE.Channel _captureChannel;
MediaContext _mediaContext;
DUCE.Resource _renderTargetRoot;
///
/// _captureRenderTargetBitmap should be marked as SecurityCritical,
/// since it's been obtained under elevation, and if disclosed, could
/// allow rendering on elements untrusted parties shouldn't be alloowed.
///
[SecurityCritical]
SafeMILHandle _captureRenderTargetBitmap;
private UniqueEventHelper _newFrameHelper = new UniqueEventHelper();
private UniqueEventHelper _mediaOpenedHelper = new UniqueEventHelper();
private const float _defaultDevicePixelsPerInch = 96.0F;
///
/// A separate class is needed to register for the ProcessExit event
/// because MediaPlayerState holds a strong reference to _nativeMedia.
/// If a MediaPlayerState method was registered for the ProcessExit
/// event then MediaPlayerState would not be garbage collected until
/// ProcessExit time.
///
private class Helper
{
///
/// Critical - this is a weak reference to a pointer to an unmanaged object
/// on which methods are called directly
///
[SecurityCritical]
private WeakReference _nativeMedia;
///
/// Accesses weak reference to pointer
///
[SecurityCritical]
internal
Helper(
SafeMediaHandle nativeMedia
)
{
_nativeMedia = new WeakReference(nativeMedia);
}
///
/// Accesses weak reference to pointer, calls unmanaged code
///
[SecurityCritical]
internal
void
ProcessExitHandler(
object sender,
EventArgs args
)
{
SafeMediaHandle nativeMedia = (SafeMediaHandle)_nativeMedia.Target;
if (nativeMedia != null)
{
MILMedia.ProcessExitHandler(nativeMedia);
}
}
};
#endregion
}
#endregion
};
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TextServicesCompartment.cs
- PageHandlerFactory.cs
- BoolExpressionVisitors.cs
- SafeFileMapViewHandle.cs
- Schema.cs
- SoapException.cs
- InfoCardAsymmetricCrypto.cs
- TypeSource.cs
- GridViewColumnHeaderAutomationPeer.cs
- TypeLibConverter.cs
- PageAction.cs
- QilStrConcatenator.cs
- DropShadowBitmapEffect.cs
- XPathParser.cs
- XPathItem.cs
- WebPartUserCapability.cs
- ELinqQueryState.cs
- Attribute.cs
- Encoding.cs
- RoutedEventHandlerInfo.cs
- KnownTypesHelper.cs
- StructuralObject.cs
- PenContext.cs
- TransactionsSectionGroup.cs
- glyphs.cs
- Pair.cs
- NamedPermissionSet.cs
- UshortList2.cs
- StatusBarPanelClickEvent.cs
- PrimitiveType.cs
- PackageProperties.cs
- FontFamilyConverter.cs
- HttpResponseInternalBase.cs
- Deserializer.cs
- Point4D.cs
- WindowsFormsSectionHandler.cs
- CodeAttributeArgumentCollection.cs
- CodeDomDecompiler.cs
- FtpRequestCacheValidator.cs
- DataTableReaderListener.cs
- Bezier.cs
- TypeSource.cs
- Deflater.cs
- XmlIgnoreAttribute.cs
- KnownTypes.cs
- _HelperAsyncResults.cs
- AlphaSortedEnumConverter.cs
- ReflectEventDescriptor.cs
- VersionedStream.cs
- XPathNavigatorReader.cs
- ExpandoObject.cs
- Int32Rect.cs
- GridViewRowPresenter.cs
- StorageEntityTypeMapping.cs
- FormView.cs
- HandlerWithFactory.cs
- ExpressionConverter.cs
- HttpRequest.cs
- WinInet.cs
- Nullable.cs
- Int16.cs
- DbParameterHelper.cs
- XmlNodeReader.cs
- XmlSchemaImporter.cs
- RequestDescription.cs
- Ray3DHitTestResult.cs
- TableAdapterManagerGenerator.cs
- COMException.cs
- StyleXamlTreeBuilder.cs
- DrawingContextWalker.cs
- FormatterServicesNoSerializableCheck.cs
- RevocationPoint.cs
- WorkflowTransactionService.cs
- TdsParserStaticMethods.cs
- PartitionedDataSource.cs
- NavigationProgressEventArgs.cs
- DefaultCompensation.cs
- ProxyWebPart.cs
- WpfGeneratedKnownProperties.cs
- TextUtf8RawTextWriter.cs
- XmlSchemaAnyAttribute.cs
- SmtpNegotiateAuthenticationModule.cs
- CapabilitiesUse.cs
- BuildManagerHost.cs
- LoginName.cs
- FactoryGenerator.cs
- ProfileGroupSettings.cs
- EventDriven.cs
- dsa.cs
- SupportingTokenDuplexChannel.cs
- FacetChecker.cs
- X509Certificate.cs
- IconHelper.cs
- Query.cs
- SimpleTextLine.cs
- oledbconnectionstring.cs
- Size3DConverter.cs
- XMLUtil.cs
- EndpointDiscoveryElement.cs
- RemoteWebConfigurationHostStream.cs