Guest User

Untitled

a guest
May 23rd, 2018
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.20 KB | None | 0 0
  1. using System;
  2. using System.IO;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using System.Windows.Interop;
  6. using WPFMediaKit.DirectShow.Controls;
  7. using WPFMediaKit.DirectShow.MediaPlayers;
  8.  
  9. namespace Test_Application
  10. {
  11. /// <summary>
  12. /// Class to simplify video screen grabbing by the async tasks.
  13. /// </summary>
  14. public class VideoScreenGrabber : IDisposable
  15. {
  16. private TaskCompletionSource<bool> taskCompletionOpen;
  17. private TaskCompletionSource<IntPtr> taskCompletionGrab;
  18. private CancellationTokenRegistration cancellationRegistrationGrab;
  19.  
  20. public MediaUriPlayer Player { get; private set; }
  21.  
  22. /// <summary>
  23. /// Current back buffer.
  24. /// </summary>
  25. public IntPtr BackBuffer { get; private set; }
  26.  
  27. /// <summary>
  28. /// Media duration in 100ns units.
  29. /// </summary>
  30. public long MediaDuration
  31. {
  32. get
  33. {
  34. CheckPlayer();
  35. return Player.Duration;
  36. }
  37. }
  38.  
  39. /// <summary>
  40. /// Media duration [sec].
  41. /// </summary>
  42. public double MediaDurationSecond
  43. => (double)MediaDuration / MediaPlayerBase.DSHOW_ONE_SECOND_UNIT;
  44.  
  45. /// <summary>
  46. /// Busy with grabbing.
  47. /// </summary>
  48. public bool IsGrabbing
  49. => taskCompletionGrab != null;
  50.  
  51. /// <summary>
  52. /// Open given source. Fails with exception if the file cannot be played.
  53. /// </summary>
  54. public Task Open(Uri source)
  55. {
  56. if (Player != null)
  57. throw new ArgumentException("Cannot open twice!");
  58. taskCompletionOpen = new TaskCompletionSource<bool>();
  59.  
  60. Player = new MediaUriPlayer();
  61. Player.EnsureThread(ApartmentState.MTA);
  62. Player.MediaOpened += MediaUriPlayer_MediaOpened;
  63. Player.MediaFailed += MediaUriPlayer_MediaFailed;
  64. Player.NewAllocatorFrame += Player_NewAllocatorFrame;
  65. Player.NewAllocatorSurface += Player_NewAllocatorSurface;
  66.  
  67. Player.Dispatcher.BeginInvoke(new Action(() =>
  68. {
  69. Player.AudioDecoder = null;
  70. Player.AudioRenderer = null;
  71. Player.Source = source;
  72. }));
  73.  
  74. return taskCompletionOpen.Task;
  75. }
  76.  
  77. /// <summary>
  78. /// Grab a buffer at given position [sec].
  79. /// See <see cref="GrabAtPosition(long)"/>.
  80. /// </summary>
  81. public Task<IntPtr> GrabAtSecond(double second)
  82. => GrabAtPosition((long)(second * MediaPlayerBase.DSHOW_ONE_SECOND_UNIT));
  83.  
  84. /// <summary>
  85. /// Grab a buffer at given position.
  86. /// </summary>
  87. /// <param name="position">Video position in 100ns units.</param>
  88. /// <param name="cancellationToken">CancellationToken, may be used for timeout.</param>
  89. /// <returns>Buffer for D3DImage.</returns>
  90. public Task<IntPtr> GrabAtPosition(long position, CancellationToken cancellationToken = default(CancellationToken))
  91. {
  92. CheckPlayer();
  93. if (taskCompletionGrab != null)
  94. throw new InvalidOperationException("Still grabbing previous frame.");
  95. if (position < 0)
  96. throw new ArgumentException("position negative.");
  97. if (position > MediaDuration)
  98. throw new ArgumentException("position beyond the media duration.");
  99.  
  100. taskCompletionGrab = new TaskCompletionSource<IntPtr>();
  101. cancellationRegistrationGrab = cancellationToken.Register(CancelGrab);
  102. Player.Dispatcher.BeginInvoke(new Action(() =>
  103. {
  104. Player.MediaPosition = position;
  105. Player.Pause();
  106. }));
  107.  
  108. return taskCompletionGrab.Task;
  109. }
  110.  
  111. public void CancelGrab()
  112. {
  113. taskCompletionGrab?.TrySetCanceled();
  114. taskCompletionGrab = null;
  115. cancellationRegistrationGrab.Dispose();
  116. }
  117.  
  118. private void CheckPlayer()
  119. {
  120. if (Player == null)
  121. throw new InvalidOperationException("Player not opened, call Open() first.");
  122. }
  123.  
  124. private void MediaUriPlayer_MediaFailed(object sender, MediaFailedEventArgs e)
  125. {
  126. Exception exc = e.Exception;
  127. if (exc == null)
  128. exc = new WPFMediaKit.WPFMediaKitException(e.Message);
  129. taskCompletionOpen.TrySetException(exc);
  130. }
  131.  
  132. private void MediaUriPlayer_MediaOpened()
  133. => taskCompletionOpen.TrySetResult(true);
  134.  
  135. private void Player_NewAllocatorSurface(object sender, IntPtr pSurface)
  136. => BackBuffer = pSurface;
  137.  
  138. private void Player_NewAllocatorFrame()
  139. {
  140. if (taskCompletionGrab == null)
  141. return;
  142. taskCompletionGrab.TrySetResult(BackBuffer);
  143. // not exactly thread safe, but do the job in common scenarios
  144. taskCompletionGrab = null;
  145. }
  146.  
  147. public void Dispose()
  148. {
  149. Dispose(true);
  150. }
  151.  
  152. protected virtual void Dispose(bool disposing)
  153. {
  154. if (!disposing)
  155. return;
  156. CancelGrab();
  157. if (Player != null)
  158. {
  159. Player.Dispose();
  160. Player = null;
  161. }
  162. }
  163. }
  164.  
  165. /// <summary>
  166. /// Helper class for one time frame grabbing.
  167. /// </summary>
  168. public static class VideoScreenGrabberUtils
  169. {
  170. private const int TIMEOUT_MS = 5000;
  171.  
  172. /// <summary>
  173. /// Grab a D3DImage with timeout.
  174. /// </summary>
  175. /// <param name="source">Video source Uri.</param>
  176. /// <param name="position">Position in DSHOW_ONE_SECOND_UNIT units.</param>
  177. /// <param name="timeout">Timeout in millis. If zero, then wait infinitely.</param>
  178. /// <returns>Created D3DImage.</returns>
  179. /// <exception cref="TimeoutException">When the opearion has timeed out.</exception>
  180. public static async Task<D3DImage> GrabScreenAtPosition(Uri source, long position, int timeout = TIMEOUT_MS)
  181. {
  182. using (VideoScreenGrabber grabber = new VideoScreenGrabber())
  183. {
  184. await grabber.Open(source);
  185. long duration = grabber.MediaDuration;
  186. // if the video is too short, grab the screen at half
  187. if (position > duration - (MediaPlayerBase.DSHOW_ONE_SECOND_UNIT / 2))
  188. position = duration / 2;
  189. IntPtr buffer;
  190. if (timeout > 0)
  191. {
  192. try
  193. {
  194. buffer = await grabber.GrabAtPosition(position, new CancellationTokenSource(timeout).Token);
  195. }
  196. catch (TaskCanceledException)
  197. {
  198. throw new TimeoutException("The operation has timed-out.");
  199. }
  200. }
  201. else
  202. {
  203. buffer = await grabber.GrabAtPosition(position);
  204. }
  205. D3DImage d3d = new D3DImage();
  206. D3DImageUtils.SetBackBufferWithLock(d3d, buffer);
  207. return d3d;
  208. }
  209. }
  210. }
  211. }
Add Comment
Please, Sign In to add comment