Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / 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 EventHandlerMediaFailed { 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 EventHandlerScriptCommand { 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 EventHandlerMediaFailed { 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 EventHandlerScriptCommand { 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
- RequiredFieldValidator.cs
- Comparer.cs
- LogicalExpr.cs
- DebuggerAttributes.cs
- EditingCommands.cs
- WorkflowItemPresenter.cs
- PreviewPageInfo.cs
- BlockCollection.cs
- TypeSystemProvider.cs
- UIElement.cs
- Rectangle.cs
- ApplicationFileCodeDomTreeGenerator.cs
- SerializationStore.cs
- HttpCookie.cs
- SqlDependencyListener.cs
- ParallelForEach.cs
- PropertyChangedEventManager.cs
- MessageEncoder.cs
- DocobjHost.cs
- Path.cs
- ApplicationDirectory.cs
- NotSupportedException.cs
- regiisutil.cs
- ProtocolsConfigurationEntry.cs
- Repeater.cs
- Int16Converter.cs
- PersonalizationStateInfo.cs
- ReadOnlyDataSource.cs
- List.cs
- PolyQuadraticBezierSegment.cs
- AbstractSvcMapFileLoader.cs
- NullableLongAverageAggregationOperator.cs
- VoiceObjectToken.cs
- ZipFileInfo.cs
- HashRepartitionEnumerator.cs
- GlyphRunDrawing.cs
- SourceFileBuildProvider.cs
- Site.cs
- UIElementPropertyUndoUnit.cs
- NativeMethods.cs
- ListBoxItemWrapperAutomationPeer.cs
- MultiView.cs
- JsonReaderDelegator.cs
- OleServicesContext.cs
- NotifyIcon.cs
- GeometryConverter.cs
- TransformerInfoCollection.cs
- InstanceDescriptor.cs
- Viewport3DVisual.cs
- ThaiBuddhistCalendar.cs
- HttpTransportSecurity.cs
- WindowsComboBox.cs
- HtmlInputReset.cs
- CounterCreationDataConverter.cs
- CommandValueSerializer.cs
- Evaluator.cs
- CompensatableTransactionScopeActivity.cs
- StylusPointPropertyInfoDefaults.cs
- GraphicsContainer.cs
- BoolExpr.cs
- GPPOINTF.cs
- FileChangeNotifier.cs
- FormViewInsertEventArgs.cs
- FocusChangedEventArgs.cs
- MimeTypePropertyAttribute.cs
- OrderedDictionaryStateHelper.cs
- DescendantOverDescendantQuery.cs
- ButtonFlatAdapter.cs
- DoubleKeyFrameCollection.cs
- HostedController.cs
- TimeSpan.cs
- HoistedLocals.cs
- TagPrefixAttribute.cs
- ControlAdapter.cs
- BasicHttpBindingCollectionElement.cs
- XmlChoiceIdentifierAttribute.cs
- CollectionBuilder.cs
- RSACryptoServiceProvider.cs
- WindowsListViewScroll.cs
- ProxySimple.cs
- CodeAttachEventStatement.cs
- EntityStoreSchemaGenerator.cs
- SHA1CryptoServiceProvider.cs
- MessageQueueException.cs
- ToolboxDataAttribute.cs
- ErrorTableItemStyle.cs
- MenuItemStyleCollection.cs
- DbConnectionPoolCounters.cs
- DataGridViewCellCollection.cs
- RightNameExpirationInfoPair.cs
- FrameworkElement.cs
- ResourceContainer.cs
- TouchFrameEventArgs.cs
- FrameworkRichTextComposition.cs
- SqlTypesSchemaImporter.cs
- Globals.cs
- AttachedProperty.cs
- UTF7Encoding.cs
- GeometryCombineModeValidation.cs
- safelinkcollection.cs