From af475371a0828945aa69b437413f31dc10c8214b Mon Sep 17 00:00:00 2001 From: Ondra Medek Date: Thu, 9 Nov 2017 09:04:03 +0100 Subject: [PATCH 1/5] MediaSeekingElement.OnMediaPlayerOpened set MediaPosition to 0 Fix #39 --- .../DirectShow/Controls/MediaSeekingElement.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Source/DirectShow/Controls/MediaSeekingElement.cs b/Source/DirectShow/Controls/MediaSeekingElement.cs index c5da02b..c75aa47 100644 --- a/Source/DirectShow/Controls/MediaSeekingElement.cs +++ b/Source/DirectShow/Controls/MediaSeekingElement.cs @@ -298,27 +298,19 @@ protected virtual void OnMediaPlayerPositionChanged() /// protected override void OnMediaPlayerOpened() { - /* Pull out our values of our properties */ + MediaPositionFormat positionFormat = MediaSeekingPlayer.CurrentPositionFormat; long duration = MediaSeekingPlayer.Duration; - long position = 0;// MediaSeekingPlayer.MediaPosition; - double rate = 1; // MediaSeekingPlayer.SpeedRatio; - double volume = 1; - - var positionFormat = MediaSeekingPlayer.CurrentPositionFormat; Dispatcher.BeginInvoke((Action)delegate { - position = MediaPosition; /* Set our DP values */ SetCurrentPositionFormat(positionFormat); + SetMediaPositionInternal(0); SetMediaDuration(duration); - //SetMediaPositionInternal(position); - //SpeedRatio = rate; - rate = SpeedRatio; - volume = Volume; + double rate = SpeedRatio; + double volume = Volume; MediaSeekingPlayer.Dispatcher.BeginInvoke((Action) delegate { - //MediaSeekingPlayer.MediaPosition = position; MediaSeekingPlayer.SpeedRatio = rate; MediaPlayerBase.Volume = volume; }); From 76158bd2c4affbb315d89ef2ee819fb297567572 Mon Sep 17 00:00:00 2001 From: Markus Plobner Date: Sun, 3 Dec 2017 13:17:35 +0100 Subject: [PATCH 2/5] framestep added, framerate added to mediadetector --- Source/DirectShow/Controls/MediaUriElement.cs | 16 ++++++ .../DirectShow/MediaPlayers/MediaDetector.cs | 17 ++++++- .../DirectShow/MediaPlayers/MediaUriPlayer.cs | 51 +++++++++++++++++++ Test Application/MainWindow.xaml | 13 ++++- Test Application/MainWindow.xaml.cs | 8 +++ 5 files changed, 101 insertions(+), 4 deletions(-) diff --git a/Source/DirectShow/Controls/MediaUriElement.cs b/Source/DirectShow/Controls/MediaUriElement.cs index 6bb6182..5a6f086 100644 --- a/Source/DirectShow/Controls/MediaUriElement.cs +++ b/Source/DirectShow/Controls/MediaUriElement.cs @@ -59,6 +59,22 @@ private void PlayerSetVideoRenderer() #endregion + /// + /// Step the count of frames. + /// this does only work correctly when the play method is called before + /// for example + /// Player.Play(); + /// Player.FrameStep(1); + /// + /// count of frames to step + public void FrameStep(int framecount) + { + MediaUriPlayer.Dispatcher.BeginInvoke((Action)delegate + { + MediaUriPlayer.StepFrame(framecount); + }); + } + #region AudioRenderer public static readonly DependencyProperty AudioRendererProperty = diff --git a/Source/DirectShow/MediaPlayers/MediaDetector.cs b/Source/DirectShow/MediaPlayers/MediaDetector.cs index 8af2dcf..db685f5 100644 --- a/Source/DirectShow/MediaPlayers/MediaDetector.cs +++ b/Source/DirectShow/MediaPlayers/MediaDetector.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; @@ -35,8 +35,17 @@ public class MediaDetector : DispatcherObject, IDisposable private Size m_videoResolution; private double m_videoStreamLength; private Guid m_videoSubType; + private double m_videoFrameRate; #endregion - + + /// + /// The video framerate + /// + public double VideoFrameRate + { + get { return m_videoFrameRate; } + } + /// /// The video CODEC tag /// @@ -169,6 +178,7 @@ private void FreeResources() m_videoResolution = Size.Empty; m_videoStreamLength = 0; m_videoSubType = Guid.Empty; + m_videoFrameRate = 0; HasAudio = false; HasVideo = false; @@ -248,6 +258,7 @@ private void LoadMedia() DsError.ThrowExceptionForHR(hr); var mediaType = new AMMediaType(); + double framerate; /* Gets the AMMediaType so we can read some * metadata on the stream */ @@ -257,6 +268,8 @@ private void LoadMedia() if(majorType == MediaType.Video) { ReadVideoFormat(mediaType); + hr = m_mediaDet.get_FrameRate(out framerate); + m_videoFrameRate = framerate; } else if(majorType == MediaType.Audio) { diff --git a/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs b/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs index e4bbed9..c42c3b4 100644 --- a/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs +++ b/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs @@ -118,6 +118,52 @@ public VideoRendererType VideoRenderer get; set; } + + /// + /// Implementation of framestepinterface to step video forward by minimum of one frame e.g. by mousewheel + /// + private IVideoFrameStep frameStep; + + /// + /// step the frames + /// + public void StepFrame(int framecount) + { + int i = frameStep.CanStep(0, null); + if (i == 0) + { + this.Play(); + frameStep.Step(framecount, null); + } + } + + // + // Some video renderers support stepping media frame by frame with the + // IVideoFrameStep interface. See the interface documentation for more + // details on frame stepping. + // + private bool GetFrameStepInterface() + { + int hr = 0; + + IVideoFrameStep frameStepTest = null; + + // Get the frame step interface, if supported + frameStepTest = (IVideoFrameStep)this.m_graph; + + // Check if this decoder can step + hr = frameStepTest.CanStep(0, null); + if (hr == 0) + { + this.frameStep = frameStepTest; + return true; + } + else + { + this.frameStep = null; + return false; + } + } /// /// The name of the audio renderer device @@ -419,6 +465,11 @@ protected virtual void OpenSource() #endif /* Configure the graph in the base class */ SetupFilterGraph(m_graph); + + HasVideo = true; + + GetFrameStepInterface(); + } catch (Exception ex) { diff --git a/Test Application/MainWindow.xaml b/Test Application/MainWindow.xaml index ac63391..466bb93 100644 --- a/Test Application/MainWindow.xaml +++ b/Test Application/MainWindow.xaml @@ -7,7 +7,7 @@ xmlns:controls="clr-namespace:WPFMediaKit.DirectShow.Controls;assembly=WPFMediaKit" xmlns:players="clr-namespace:WPFMediaKit.DirectShow.MediaPlayers;assembly=WPFMediaKit" mc:Ignorable="d" - Title="MainWindow" Height="450" Width="825"> + Title="MainWindow" Height="450" Width="825" > @@ -42,7 +42,7 @@ + Margin="4" SpeedRatio="{Binding Path=Value, ElementName=ratioSlider}" /> @@ -50,6 +50,15 @@ + + + + + + + + \ No newline at end of file diff --git a/Test Application/MainWindow.xaml.cs b/Test Application/MainWindow.xaml.cs index 33ae659..7481500 100644 --- a/Test Application/MainWindow.xaml.cs +++ b/Test Application/MainWindow.xaml.cs @@ -137,5 +137,13 @@ private void cobVideoSource_SelectionChanged(object sender, System.Windows.Contr SetCameraCaptureElementVisible(true); cameraCaptureElement.VideoCaptureDevice = MultimediaUtil.VideoInputDevices[cobVideoSource.SelectedIndex]; } + + private void btnFrameStep_Click(object sender, RoutedEventArgs e) + { + if (mediaUriElement.Source != null) + { + mediaUriElement.FrameStep(1); + } + } } } \ No newline at end of file From a7216f3dd30a2d2e3acb202454a8ae607a81b85e Mon Sep 17 00:00:00 2001 From: skaterlui Date: Sun, 3 Dec 2017 21:46:40 +0100 Subject: [PATCH 3/5] Update MediaUriElement.cs --- Source/DirectShow/Controls/MediaUriElement.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/DirectShow/Controls/MediaUriElement.cs b/Source/DirectShow/Controls/MediaUriElement.cs index 5a6f086..ac724d7 100644 --- a/Source/DirectShow/Controls/MediaUriElement.cs +++ b/Source/DirectShow/Controls/MediaUriElement.cs @@ -61,10 +61,6 @@ private void PlayerSetVideoRenderer() /// /// Step the count of frames. - /// this does only work correctly when the play method is called before - /// for example - /// Player.Play(); - /// Player.FrameStep(1); /// /// count of frames to step public void FrameStep(int framecount) From 21fdf4baa5f2948095711856df58cd46a5de2495 Mon Sep 17 00:00:00 2001 From: Markus Plobner Date: Sun, 3 Dec 2017 21:49:02 +0100 Subject: [PATCH 4/5] playerstate added --- .../DirectShow/Controls/MediaElementBase.cs | 26 +++++++++++ .../Controls/MediaSeekingElement.cs | 24 +++++++++- Source/DirectShow/Controls/MediaUriElement.cs | 4 -- Source/DirectShow/MediaPlayers/BaseClasses.cs | 45 ++++++++++++++++++- .../DirectShow/MediaPlayers/MediaUriPlayer.cs | 1 + Test Application/MainWindow.xaml | 4 ++ 6 files changed, 97 insertions(+), 7 deletions(-) diff --git a/Source/DirectShow/Controls/MediaElementBase.cs b/Source/DirectShow/Controls/MediaElementBase.cs index 56bc071..aaff0aa 100644 --- a/Source/DirectShow/Controls/MediaElementBase.cs +++ b/Source/DirectShow/Controls/MediaElementBase.cs @@ -79,6 +79,21 @@ public event RoutedEventHandler MediaEnded #endregion #region Dependency Properties + #region PlayerState + public static readonly DependencyProperty PlayerStateProperty = + DependencyProperty.Register("PlayerState", typeof(PlayerState), typeof(MediaElementBase), + new FrameworkPropertyMetadata(PlayerState.Closed)); + + /// + /// Get the current state of the media player + /// + public PlayerState PlayerState + { + get { return (PlayerState)GetValue(PlayerStateProperty); } + protected set { SetValue(PlayerStateProperty, value); } + } + #endregion + #region UnloadedBehavior public static readonly DependencyProperty UnloadedBehaviorProperty = @@ -297,6 +312,7 @@ protected virtual void InitializeMediaPlayer() MediaPlayerBase.MediaClosed += OnMediaPlayerClosedPrivate; MediaPlayerBase.MediaFailed += OnMediaPlayerFailedPrivate; MediaPlayerBase.MediaEnded += OnMediaPlayerEndedPrivate; + MediaPlayerBase.PlayerStateChanged += OnPlayerStateChanged; /* These events fire when we get new D3Dsurfaces or frames */ MediaPlayerBase.NewAllocatorFrame += OnMediaPlayerNewAllocatorFramePrivate; @@ -372,6 +388,16 @@ protected virtual void OnMediaPlayerNewAllocatorFrame() InvalidateVideoImage(); } + /// + /// /// Called when the state of the player has changed + /// + /// Previous state + /// New State + protected virtual void OnPlayerStateChanged(PlayerState oldState, PlayerState newState) + { + Dispatcher.BeginInvoke((Action)((a, b) => b.PlayerState = a), newState, this); + } + /// /// Called when the media has been closed /// diff --git a/Source/DirectShow/Controls/MediaSeekingElement.cs b/Source/DirectShow/Controls/MediaSeekingElement.cs index c5da02b..92ac3df 100644 --- a/Source/DirectShow/Controls/MediaSeekingElement.cs +++ b/Source/DirectShow/Controls/MediaSeekingElement.cs @@ -24,6 +24,24 @@ public abstract class MediaSeekingElement : MediaElementBase new FrameworkPropertyMetadata((long)0, new PropertyChangedCallback(OnMediaPositionChanged))); + public static readonly RoutedEvent MediaPositionChangedEvent = EventManager.RegisterRoutedEvent("MediaPositionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MediaSeekingElement)); + + /// + /// Is invoked whenever the current media position is changed. + /// + public event RoutedEventHandler MediaPositionChanged + { + add + { + this.AddHandler(MediaPositionChangedEvent, value); + } + remove + { + this.RemoveHandler(MediaPositionChangedEvent, value); + } + } + + /// /// Gets or sets the media position in units of CurrentPositionFormat /// @@ -59,10 +77,12 @@ protected virtual void OnMediaPositionChanged(DependencyPropertyChangedEventArgs protected void SetMediaPositionInternal(long value) { /* Flag that we want to ignore the next - * PropertyChangedCallback */ - m_ignorePropertyChangedCallback = true; + *PropertyChangedCallback + * If the player is not currently paused!(otherwise it would only react every second seek) */ + m_ignorePropertyChangedCallback = this.PlayerState != PlayerState.Paused; MediaPosition = value; + RaiseEvent(new RoutedEventArgs(MediaPositionChangedEvent, this)); } private void PlayerSetMediaPosition() diff --git a/Source/DirectShow/Controls/MediaUriElement.cs b/Source/DirectShow/Controls/MediaUriElement.cs index 5a6f086..ac724d7 100644 --- a/Source/DirectShow/Controls/MediaUriElement.cs +++ b/Source/DirectShow/Controls/MediaUriElement.cs @@ -61,10 +61,6 @@ private void PlayerSetVideoRenderer() /// /// Step the count of frames. - /// this does only work correctly when the play method is called before - /// for example - /// Player.Play(); - /// Player.FrameStep(1); /// /// count of frames to step public void FrameStep(int framecount) diff --git a/Source/DirectShow/MediaPlayers/BaseClasses.cs b/Source/DirectShow/MediaPlayers/BaseClasses.cs index dd843ee..c213e22 100644 --- a/Source/DirectShow/MediaPlayers/BaseClasses.cs +++ b/Source/DirectShow/MediaPlayers/BaseClasses.cs @@ -25,6 +25,15 @@ public enum MediaState Pause } + public enum PlayerState + { + Closed, + Playing, + Paused, + Stopped, + SteppingFrames + } + /// /// The types of position formats that /// are available for seeking media @@ -255,6 +264,11 @@ public abstract class MediaPlayerBase : WorkDispatcherObject /// private System.Timers.Timer m_timer; + /// + /// The current state of the player + /// + private PlayerState m_playerState = PlayerState.Closed; + /// /// This objects last stand /// @@ -463,6 +477,22 @@ public virtual double Balance } } + /// + /// Get the current state of the player + /// + public virtual PlayerState PlayerState + { + get { return this.m_playerState; } + protected set + { + PlayerState oldVal = m_playerState; + m_playerState = value; + + if (PlayerStateChanged != null && oldVal != value) + PlayerStateChanged(oldVal, value); + } + } + /// /// Event notifies when there is a new video frame /// to be rendered @@ -474,6 +504,11 @@ public virtual double Balance /// public event NewAllocatorSurfaceDelegate NewAllocatorSurface; + /// + /// Event notifies when the player changes state + /// + public event Action PlayerStateChanged; + /// /// Frees any remaining memory /// @@ -987,7 +1022,11 @@ public virtual void Play() } if (m_mediaControl != null) + { m_mediaControl.Run(); + PlayerState = PlayerState.Playing; + } + } /// @@ -1015,7 +1054,9 @@ protected void StopInternal() m_mediaControl.GetState(0, out filterState); while (filterState != FilterState.Stopped) - m_mediaControl.GetState(0, out filterState); + m_mediaControl.GetState(2, out filterState); + + PlayerState = PlayerState.Stopped; } } @@ -1027,6 +1068,7 @@ public virtual void Close() VerifyAccess(); StopInternal(); FreeResources(); + PlayerState = PlayerState.Closed; } /// @@ -1039,6 +1081,7 @@ public virtual void Pause() if (m_mediaControl != null) { m_mediaControl.Pause(); + PlayerState = PlayerState.Paused; } } diff --git a/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs b/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs index c42c3b4..8dd4657 100644 --- a/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs +++ b/Source/DirectShow/MediaPlayers/MediaUriPlayer.cs @@ -134,6 +134,7 @@ public void StepFrame(int framecount) { this.Play(); frameStep.Step(framecount, null); + this.PlayerState = PlayerState.SteppingFrames; } } diff --git a/Test Application/MainWindow.xaml b/Test Application/MainWindow.xaml index 466bb93..309affd 100644 --- a/Test Application/MainWindow.xaml +++ b/Test Application/MainWindow.xaml @@ -29,6 +29,10 @@ public abstract class MediaPlayerBase : WorkDispatcherObject { - private const string VMR9_ERROR = "Do you have the grahics driver and DirectX properly installed?"; - [DllImport("user32.dll", SetLastError = false)] private static extern IntPtr GetDesktopWindow(); @@ -923,7 +921,7 @@ private IBaseFilter CreateVideoMixingRenderer9(IGraphBuilder graph, int streamCo } catch (COMException ex) { - throw new WPFMediaKitException("Could not create VMR9. " + VMR9_ERROR, ex); + throw new WPFMediaKitException("Could not create VMR9. " + Vmr9Allocator.VMR9_ERROR, ex); } } @@ -938,7 +936,7 @@ private IBaseFilter CreateVideoMixingRenderer9Inner(IGraphBuilder graph, int str IBaseFilter vmr9 = new VideoMixingRenderer9() as IBaseFilter; var filterConfig = vmr9 as IVMRFilterConfig9; if (filterConfig == null) - throw new WPFMediaKitException("Could not query VMR9 filter configuration. " + VMR9_ERROR); + throw new WPFMediaKitException("Could not query VMR9 filter configuration. " + Vmr9Allocator.VMR9_ERROR); /* We will only have one video stream connected to the filter */ int hr = filterConfig.SetNumberOfStreams(streamCount); @@ -953,7 +951,7 @@ private IBaseFilter CreateVideoMixingRenderer9Inner(IGraphBuilder graph, int str /* Query the allocator interface */ var vmrSurfAllocNotify = vmr9 as IVMRSurfaceAllocatorNotify9; if (vmrSurfAllocNotify == null) - throw new WPFMediaKitException("Could not query the VMR surface allocator. " + VMR9_ERROR); + throw new WPFMediaKitException("Could not query the VMR surface allocator. " + Vmr9Allocator.VMR9_ERROR); var allocator = new Vmr9Allocator(); diff --git a/Source/DirectShow/MediaPlayers/Vmr9Allocator.cs b/Source/DirectShow/MediaPlayers/Vmr9Allocator.cs index 8756c10..fac9e41 100644 --- a/Source/DirectShow/MediaPlayers/Vmr9Allocator.cs +++ b/Source/DirectShow/MediaPlayers/Vmr9Allocator.cs @@ -13,6 +13,8 @@ namespace WPFMediaKit.DirectShow.MediaPlayers [ComVisible(true)] public class Vmr9Allocator: IVMRSurfaceAllocator9, IVMRImagePresenter9, ICustomAllocator { + internal const string VMR9_ERROR = "Do you have the graphics driver and DirectX properly installed?"; + /// /// Base constant for FAIL error codes /// @@ -93,8 +95,6 @@ public class Vmr9Allocator: IVMRSurfaceAllocator9, IVMRImagePresenter9, ICustomA static Vmr9Allocator() { m_hWnd = GetDesktopWindow(); - - } /// @@ -103,11 +103,18 @@ static Vmr9Allocator() public Vmr9Allocator() { /* Use the 9Ex for Vista */ + int hr = 0; if (IsVistaOrBetter) - Direct3D.Direct3DCreate9Ex(D3D_SDK_VERSION, out m_d3dEx); + hr = Direct3D.Direct3DCreate9Ex(D3D_SDK_VERSION, out m_d3dEx); else /* For XP */ m_d3d = Direct3D.Direct3DCreate9(D3D_SDK_VERSION); + if (m_d3dEx == null && m_d3d == null) + { + string hrStr = hr == 0 ? "" : $"({hr:X})"; + throw new WPFMediaKitException($"Could not create IDirect3D9 {hrStr}. " + VMR9_ERROR); + } + CreateDevice(); } @@ -302,7 +309,7 @@ private void TestRestoreLostDevice() /// A pointer to the adaptor monitor private IntPtr GetAdapterMonitor(uint adapterOrdinal) { - IntPtr pMonitor = IsVistaOrBetter ? m_d3dEx.GetAdapterMonitor(adapterOrdinal) : m_d3d.GetAdapterMonitor(adapterOrdinal); + IntPtr pMonitor = m_d3dEx != null ? m_d3dEx.GetAdapterMonitor(adapterOrdinal) : m_d3d.GetAdapterMonitor(adapterOrdinal); return pMonitor; } @@ -546,19 +553,23 @@ private void CreateDevice() IntPtr dev; /* Windows Vista runs much more performant with the IDirect3DDevice9Ex */ - if (IsVistaOrBetter) + int hr = 0; + if (m_d3dEx != null) { - m_d3dEx.CreateDeviceEx(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd, + hr = m_d3dEx.CreateDeviceEx(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd, CreateFlags.D3DCREATE_SOFTWARE_VERTEXPROCESSING | CreateFlags.D3DCREATE_MULTITHREADED, ref param, IntPtr.Zero, out dev); } else/* Windows XP */ { - m_d3d.CreateDevice(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd, + hr = m_d3d.CreateDevice(0, D3DDEVTYPE.D3DDEVTYPE_HAL, m_hWnd, CreateFlags.D3DCREATE_SOFTWARE_VERTEXPROCESSING | CreateFlags.D3DCREATE_MULTITHREADED, ref param, out dev); } + if (dev == IntPtr.Zero) + throw new WPFMediaKitException($"Cannot create D3D device ({hr:X}). Do you have D3D acceleration enabled for your graphics card?"); + m_device = (IDirect3DDevice9)Marshal.GetObjectForIUnknown(dev); Marshal.Release(dev); }