最近由于工程需要开始研发基于Windows的自动录屏软件,很多细节很多功能需要处理,毕竟一个完美的录屏软件不是你随随便便就可以写出来的。首先参考了大部分的录屏软件,在研发的过程中遇到了很多的问题;比如-视频加载、麦克风加载、麦克风音量调节、视频播放进度控、视频音量控制、等等很多细节部分都需要好好规划才能开始做。录屏采用的是视频帧的思维逻辑进行编写的。

目前已经基本上成型,基于WPF采用了Model - View框架进行动态加载,每个线程与线程之间采用Async异步执行,并使用线程等待;录屏基本功能包含了(展示历史录屏记录、删除、录屏、视频编码、视频播放及删除、麦克风调用(音量调节-跟随系统)、加载视频(拖拉-旋转)、系统遮罩 等);编码的核心是采用FFMPEG(这个工具真的非常强大);

这边提供几个核心代码仅供参考:

1-难点:系统遮罩核心方法(使用Windows API):

  1. /// <summary>
  2. /// 视图模型属性改变
  3. /// </summary>
  4. /// <param name="sender">
  5. /// The sender.
  6. /// </param>
  7. /// <param name="propertyChangedEventArgs">
  8. /// 属性改变事件参数
  9. /// </param>
  10. private void ViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
  11. {
  12. if (propertyChangedEventArgs.PropertyName == "IsRecording")
  13. {
  14. this.Locked = this.ViewModel.IsRecording;
  15. if (this.ViewModel.IsRecording)
  16. {
  17. var hwnd = new WindowInteropHelper(this).Handle;
  18. NativeWindowHelper.SetWindowExTransparent(hwnd);
  19. }
  20. }
  21.  
  22. if (propertyChangedEventArgs.PropertyName == "IsFullScreen")
  23. {
  24. this.IsFullScreen = this.ViewModel.IsFullScreen;
  25. }
  26. }

改变属性的时候触发

  1. #region Constants
  2.  
  3. /// <summary>
  4. /// The gw l_ exstyle.
  5. /// </summary>
  6. [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore",
  7. Justification = "Reviewed. Suppression is OK here.")]
  8. private const int GWL_EXSTYLE = -;
  9.  
  10. /// <summary>
  11. /// The w s_ e x_ transparent.
  12. /// </summary>
  13. [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore",
  14. Justification = "Reviewed. Suppression is OK here.")]
  15. private const int WS_EX_TRANSPARENT = 0x00000020;
  16.  
  17. #endregion
  18.  
  19. #region Public Methods and Operators
  20.  
  21. /// <summary>
  22. /// 窗口前置透明设置命令
  23. /// </summary>
  24. /// <param name="hwnd">
  25. /// The hwnd.
  26. /// </param>
  27. public static void SetWindowExTransparent(IntPtr hwnd)
  28. {
  29. var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
  30. SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
  31. }
  32.  
  33. #endregion
  34.  
  35. #region Methods
  36.  
  37. /// <summary>
  38. /// The get window long.
  39. /// </summary>
  40. /// <param name="hwnd">
  41. /// The hwnd.
  42. /// </param>
  43. /// <param name="index">
  44. /// The index.
  45. /// </param>
  46. /// <returns>
  47. /// The <see cref="int"/>.
  48. /// </returns>
  49. [DllImport("user32.dll")]
  50. private static extern int GetWindowLong(IntPtr hwnd, int index);
  51.  
  52. /// <summary>
  53. /// The set window long.
  54. /// </summary>
  55. /// <param name="hwnd">
  56. /// The hwnd.
  57. /// </param>
  58. /// <param name="index">
  59. /// The index.
  60. /// </param>
  61. /// <param name="newStyle">
  62. /// The new style.
  63. /// </param>
  64. /// <returns>
  65. /// The <see cref="int"/>.
  66. /// </returns>
  67. [DllImport("user32.dll")]
  68. private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
  69.  
  70. #endregion

API方法

2-难点:麦克风获取及控制

  1. <Slider x:Name="volumeSlider" Grid.Column="" Grid.ColumnSpan="" Grid.Row="" Width="" Height="" Minimum="" Maximum="" Value="" VerticalAlignment="Center" />
  1. //定义一个获取之前拉动时候的value值,然后跟当前的value对比,选择触发
  2. private bool isUserChangeVolume = true;
  3. private VolumeControl volumeControl;
  4. //private DispatcherTimer volumeControlTimer;
  5.  
  6. /// <summary>
  7. /// 加载拖动条的事件
  8. /// </summary>
  9. /// <param name="sender"></param>
  10. /// <param name="e"></param>
  11. private void volumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
  12. {
  13. if (isUserChangeVolume)
  14. {
  15. volumeControl.MasterVolume = volumeSlider.Value;
  16. }
  17. }
  18.  
  19. private void InitializeAudioControl()
  20. {
  21. volumeControl = VolumeControl.Instance;
  22. volumeControl.OnAudioNotification += volumeControl_OnAudioNotification;
  23. volumeControl_OnAudioNotification(null, new AudioNotificationEventArgs() { MasterVolume = volumeControl.MasterVolume });
  24.  
  25. //volumeControlTimer = new DispatcherTimer();
  26. //volumeControlTimer.Interval = TimeSpan.FromTicks(150);
  27. //volumeControlTimer.Tick += volumeControlTimer_Tick;
  28. }
  29.  
  30. void volumeControl_OnAudioNotification(object sender, AudioNotificationEventArgs e)
  31. {
  32. this.isUserChangeVolume = false;
  33. try
  34. {
  35. this.Dispatcher.Invoke(new Action(() => { volumeSlider.Value = e.MasterVolume; }));
  36. }
  37. catch { }
  38. this.isUserChangeVolume = true;
  39. }
  40.  
  41. void volumeControlTimer_Tick(object sender, EventArgs e)
  42. {
  43. //获取系统主声道、左声道、右声道音量值
  44. //double[] information = volumeControl.AudioMeterInformation;
  45. //mMasterPBar.Value = information[0];
  46. //mLeftPBar.Value = information[1];
  47. //mRightPBar.Value = information[2];
  48. }

3-难点:系统遮罩(其实也不能算难点,这个是API调用的时候颜色控制);

4-难点:视频旋转核心代码(已更新为方法8)

  1. /// <summary>
  2. /// 旋转视频
  3. /// </summary>
  4. /// <param name="sender"></param>
  5. /// <param name="e"></param>
  6. private void RotateCamera_bt(object sender, RoutedEventArgs e)
  7. {
  8. if (AnAngle > || AnAngle == )
  9. {
  10. AnAngle = ;
  11. }
  12. TransformGroup transformGroup = new TransformGroup();
  13.  
  14. ScaleTransform scaleTransform = new ScaleTransform();
  15. scaleTransform.ScaleX = -;
  16. transformGroup.Children.Add(scaleTransform);
  17.  
  18. RotateTransform rotateTransform = new RotateTransform(AnAngle);
  19. transformGroup.Children.Add(rotateTransform);
  20. videoPlayer.RenderTransform = transformGroup;
  21. AnAngle += ;
  22. }

旋转视频代码

5-难点:录屏核心代码(这部分代码视频格式可以自行调整,颜色代码原理已经理解。)--已更新为方法10

  1. /// <summary>
  2. /// Starts the recording.
  3. /// </summary>
  4. public void StartRecording()
  5. {
  6. this.notifyIcon.HideBalloonTip();
  7. this.IsRecording = true;
  8.  
  9. var fileName = string.Format("Recording {0}.mp4", DateTime.Now.ToString("yy-MM-dd HH-mm-ss"));
  10. var outputFilePath = Path.Combine(this.settings.StoragePath, fileName);
  11. this.fileViewModel = new ScreenGunFileViewModel(outputFilePath, RecordingStage.DoingNothing);
  12.  
  13. var opts = new ScreenRecorderOptions(this.RecordingRegion)
  14. {
  15. DeleteMaterialWhenDone = true,
  16. OutputFilePath = outputFilePath,
  17. RecordMicrophone = this.UseMicrophone,
  18. AudioRecordingDeviceNumber = this.settings.RecordingDeviceNumber
  19. };
  20.  
  21. var progress = new Progress<RecorderState>(state => this.fileViewModel.RecordingStage = state.Stage);
  22. this.recorder.Start(opts, progress);
  23. }

录屏代码

6-难点:屏幕画框代码(采集X,Y坐标及遮幕的宽,高)

  1. /// <summary>
  2. /// 设置初始区域
  3. /// </summary>
  4. private void SetupInitialRegion()
  5. {
  6. var cursorPos = System.Windows.Forms.Cursor.Position;
  7. foreach (var screen in Screen.AllScreens)
  8. {
  9. if (screen.Bounds.Contains(cursorPos) == false)
  10. {
  11. continue;
  12. }
  13.  
  14. var regionWidth = (double)screen.Bounds.Width / ;
  15. var regionHeight = (double)screen.Bounds.Height / ;
  16. double x = ((double)screen.Bounds.Width / ) - (regionWidth / );
  17. double y = ((double)screen.Bounds.Height / ) - (regionHeight / );
  18. x -= this.virtualScreen.X - screen.Bounds.X;
  19. y -= this.virtualScreen.Y - screen.Bounds.Y;
  20.  
  21. this.startPosition = new Point(x, y);
  22. this.endPosition = new Point(x + regionWidth, y + regionHeight);
  23. this.UpdatePosition();
  24. break;
  25. }
  26. }

7-放大缩小(根据屏幕大小范围随意拉伸缩小)  核心代码如下:

当你有摄像头长跟宽不一样的时候,旋转-缩小-放大然后根据给定的边缘坐标是一个非常头疼的事情,单单这个问题就使我加班到凌晨4点了,不过最终还是解决了;

  1. void resizer_Resize(object sender, ControlResizeEventArgs e)
  2. {
  3. if (!this.RectangleU.IsMouseCaptured) return;
  4. if (AnAngle == || AnAngle == )
  5. {
  6. #region --竖直拉伸--
  7. double Image_xx = ;
  8. double Image_yy = ;
  9. double point_xx = ;
  10. double point_yy = ;
  11. double point_center = Math.Abs(this.MainGrid.Width / - this.MainGrid.Height / );//当前中心点值
  12. double actual_center = Math.Abs(this.videoPlayer.MinWidth / - this.videoPlayer.MinHeight / );//实际中心点,用于比较初始值
  13.  
  14. if (Math.Abs(Image_PointX) == )
  15. {
  16. point_xx = -;//初始化原点未动
  17. }
  18. else
  19. {
  20. //拖动到其他位置时偏移量(必须是固定值)
  21. if (Image_PointX < -)
  22. {
  23. point_xx = -;
  24. }
  25. else
  26. {
  27. point_xx = Image_PointX;
  28. }
  29. }
  30.  
  31. if (Math.Abs(Image_PointY) == )
  32. {
  33. point_yy = -;//初始化原点未动
  34. }
  35. else
  36. {
  37. //拖动到其他位置时偏移量(必须是固定值)
  38. if (Image_PointY < -)
  39. {
  40. point_yy = -;
  41. }
  42. else
  43. {
  44. point_yy = Image_PointY;
  45. }
  46.  
  47. }
  48. if (Math.Abs(point_xx) == )
  49. {
  50. Image_xx = videoPlayer.ActualHeight;
  51. }
  52. else
  53. {
  54. Image_xx = videoPlayer.ActualHeight + Math.Abs(Image_PointX) - ;
  55. }
  56. if (Math.Abs(point_yy) == )
  57. {
  58. Image_yy = videoPlayer.ActualWidth;
  59. }
  60. else
  61. {
  62. Image_yy = Math.Abs(Image_PointY) + videoPlayer.ActualWidth - ;
  63. }
  64.  
  65. //左右拉伸(只能往右拉伸)
  66. if (e.LeftDirection.HasValue)
  67. {
  68. var value = videoPlayer.Height + e.HorizontalChange;
  69. if (value > videoPlayer.MinHeight)
  70. {
  71. videoPlayer.Height = value;
  72. MainGrid.Height = value;
  73. if (videoPlayer.ActualHeight < value)
  74. {
  75. MainGrid.Height = videoPlayer.ActualHeight;
  76. }
  77. if (Image_xx >= RecordingArea.Width)
  78. {
  79. MainGrid.Height = videoPlayer.ActualHeight;
  80. videoPlayer.Height = videoPlayer.ActualHeight;
  81. }
  82. }
  83. }
  84. //上下拉伸(只能往上拉伸)
  85. if (e.TopDirection.HasValue)
  86. {
  87. var value = videoPlayer.Width + e.VerticalChange;
  88. if (value > videoPlayer.MinWidth)
  89. {
  90. videoPlayer.Width = value;
  91. MainGrid.Width = value;
  92. if (videoPlayer.ActualWidth < value)
  93. {
  94. MainGrid.Width = videoPlayer.ActualWidth;
  95. }
  96.  
  97. if (Image_yy >= RecordingArea.Height)
  98. {
  99. MainGrid.Width = videoPlayer.ActualWidth;
  100. videoPlayer.Width = videoPlayer.ActualWidth;
  101. }
  102.  
  103. }
  104. }
  105.  
  106. #region 调整位置
  107.  
  108. Matrix m = MainGrid.RenderTransform.Value;
  109.  
  110. //初始值(-25,-25)-->(x,y)
  111. if ((Image_xx >= RecordingArea.Width) || Image_yy >= RecordingArea.Height)
  112. {
  113.  
  114. }
  115. else
  116. {
  117. if (point_center >= actual_center)
  118. {
  119. // (point_center - actual_center)为x-y轴偏移量
  120. //point_xx--point_yy为当前x,y轴坐标
  121. m.OffsetX = point_xx - (point_center - actual_center);
  122. m.OffsetY = point_yy - (point_center - actual_center);
  123.  
  124. }
  125. }
  126. MainGrid.RenderTransform = new MatrixTransform(m);//重新定位
  127.  
  128. #endregion
  129.  
  130. #endregion
  131. }
  132. else
  133. {
  134. #region --正常拉伸--
  135. //左右拉伸(只能往右拉伸)
  136. if (e.LeftDirection.HasValue)
  137. {
  138. var value = videoPlayer.Width + e.HorizontalChange;
  139. if (value > videoPlayer.MinWidth)
  140. {
  141. videoPlayer.Width = value;
  142. MainGrid.Width = value;
  143. if (videoPlayer.ActualWidth < value)
  144. {
  145. MainGrid.Width = videoPlayer.ActualWidth;
  146. }
  147. if (Image_PointX + videoPlayer.ActualWidth >= RecordingArea.Width)
  148. {
  149. MainGrid.Width = videoPlayer.ActualWidth;
  150. videoPlayer.Width = videoPlayer.ActualWidth;
  151. }
  152. }
  153. }
  154. //上下拉伸(只能往上拉伸)
  155. if (e.TopDirection.HasValue)
  156. {
  157. var value = videoPlayer.Height + e.VerticalChange;
  158. if (value > videoPlayer.MinHeight)
  159. {
  160. videoPlayer.Height = value;
  161. MainGrid.Height = value;
  162. if (videoPlayer.ActualHeight < value)
  163. {
  164. MainGrid.Height = videoPlayer.ActualHeight;
  165. }
  166.  
  167. if (Math.Abs(Image_PointY) + videoPlayer.ActualHeight >= RecordingArea.Height)
  168. {
  169. MainGrid.Height = videoPlayer.ActualHeight;
  170. videoPlayer.Height = videoPlayer.ActualHeight;
  171. }
  172.  
  173. }
  174. }
  175. #endregion
  176. }
  177. }

放大缩小-分长宽不一致情况

8-旋转,核心代码如下:

  1. private void RotateCamera_bt(object sender, RoutedEventArgs e)
  2. {
  3. if (MainGrid.ActualWidth > SystemParameters.PrimaryScreenHeight)
  4. {
  5. return;
  6. }
  7. if (AnAngle > || AnAngle == )
  8. {
  9. AnAngle = ;
  10. }
  11.  
  12. TransformGroup transformGroup = new TransformGroup();
  13. RotateTransform rotateTransform = new RotateTransform(AnAngle);
  14. transformGroup.Children.Add(rotateTransform);
  15. MainGrid.RenderTransform = transformGroup;
  16. #region 特殊四个角反转需要变换长跟宽
  17. //重新调整坐标坐标位置
  18. Matrix m = MainGrid.RenderTransform.Value;
  19. //求出中心点坐标
  20. double point_xx = (this.MainGrid.ActualWidth) / - (this.MainGrid.ActualHeight) / ;
  21. // Image_PointX,Image_Point为当前坐标
  22. if (AnAngle == || AnAngle == )
  23. {
  24. if (AnAngle == )
  25. {
  26. RectangleU.VerticalAlignment = VerticalAlignment.Bottom;
  27. RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
  28. RectangleU.BorderThickness = new Thickness(, , , );
  29. RectangleU.CornerRadius = new CornerRadius(, , , );
  30.  
  31. }
  32. else
  33. {
  34. RectangleU.VerticalAlignment = VerticalAlignment.Top;
  35. RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
  36. RectangleU.BorderThickness = new Thickness(, , , );
  37. RectangleU.CornerRadius = new CornerRadius(, , , );
  38.  
  39. }
  40. if (!IschangeAngle)
  41. {
  42. if (Image_PointX <= -point_xx)
  43. {
  44. m.OffsetX = -point_xx;
  45. }
  46. else
  47. {
  48. m.OffsetX = Image_PointX - point_xx;
  49. }
  50. if (Image_PointY >= -point_xx)
  51. {
  52. m.OffsetY = -point_xx;
  53. }
  54. else
  55. {
  56. m.OffsetY = Image_PointY + point_xx;
  57. }
  58.  
  59. if (m.OffsetX >= this.RecordingArea.Width - this.videoPlayer.Width - point_xx)
  60. {
  61. m.OffsetX = m.OffsetX + point_xx * ;
  62.  
  63. }
  64. if (m.OffsetY >= -point_xx)
  65. {
  66. m.OffsetY = -point_xx;
  67. }
  68. }
  69. else
  70. {
  71. //旋转为竖直拉到某个坐标时触发
  72. if (Image_PointX <= -point_xx)
  73. {
  74. m.OffsetX = -point_xx;
  75. }
  76. else
  77. {
  78. m.OffsetX = Image_PointX;
  79. }
  80. if (Image_PointY >= point_xx)
  81. {
  82. m.OffsetY = -point_xx;
  83. }
  84. else
  85. {
  86. m.OffsetY = Image_PointY;
  87. }
  88. }
  89.  
  90. if (this.MainGrid.Width >= this.RecordingArea.Height)
  91. {
  92. //触发
  93. //相对于屏幕的x,y轴不变
  94. m.OffsetX = -point_xx;
  95. m.OffsetY = -point_xx;
  96.  
  97. }
  98.  
  99. }
  100. else
  101. {
  102. if (AnAngle == )
  103. {
  104. RectangleU.VerticalAlignment = VerticalAlignment.Bottom;
  105. RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
  106. RectangleU.BorderThickness = new Thickness(, , , );
  107. RectangleU.CornerRadius = new CornerRadius(, , , );
  108.  
  109. }
  110. else
  111. {
  112. RectangleU.VerticalAlignment = VerticalAlignment.Top;
  113. RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
  114. RectangleU.BorderThickness = new Thickness(, , , );
  115. RectangleU.CornerRadius = new CornerRadius(, , , );
  116.  
  117. }
  118. if (IschangeAngle)
  119. {
  120. if (this.MainGrid.Width >= this.RecordingArea.Height)
  121. {
  122. Image_PointX = ;
  123. }
  124. else
  125. {
  126. if (Image_PointX + this.videoPlayer.Width > this.RecordingArea.Width)
  127. {
  128. m.OffsetX = Image_PointX - point_xx;
  129. }
  130. else
  131. {
  132. m.OffsetX = Image_PointX + point_xx;
  133. }
  134. }
  135. //旋转为竖直拉到某个坐标时触发
  136. if (Image_PointY >= -point_xx)
  137. {
  138. m.OffsetY = ;
  139. }
  140. else
  141. {
  142. m.OffsetY = Image_PointY - point_xx;
  143. }
  144.  
  145. }
  146. else
  147. {
  148.  
  149. if (this.MainGrid.Width >= this.RecordingArea.Width)
  150. {
  151. m.OffsetX = ;
  152. m.OffsetY = ;
  153. }
  154. else
  155. {
  156. //正常情况
  157. if (Image_PointX <= )
  158. {
  159. m.OffsetX = ;
  160. }
  161. else
  162. {
  163. m.OffsetX = Image_PointX;
  164. }
  165. if (Image_PointY >= )
  166. {
  167. m.OffsetY = ;
  168. }
  169. else
  170. {
  171. m.OffsetY = Image_PointY;
  172. }
  173. }
  174. }
  175. }
  176. //IschangeAngle = false;
  177. //更换坐标位置
  178. MainGrid.RenderTransform = new MatrixTransform(m);
  179.  
  180. var x = Math.Min(this.startPosition.X, this.endPosition.X);
  181. var y = Math.Min(this.startPosition.Y, this.endPosition.Y);
  182. if (AnAngle == || AnAngle == )
  183. {
  184. if (this.MainGrid.Width >= this.RecordingArea.Height)
  185. {
  186. this.relativeRecordingArea = new Rect(x, y, this.MainGrid.Height, this.MainGrid.Width);
  187. this.UpdateUI();
  188. }
  189. }
  190. else
  191. {
  192. if (this.MainGrid.Width >= this.RecordingArea.Width)
  193. {
  194. this.relativeRecordingArea = new Rect(x, y, this.MainGrid.Width, this.MainGrid.Height);
  195. this.UpdateUI();
  196. }
  197. }
  198. //UpdatePosition();
  199.  
  200. AnAngle += ;
  201. #endregion
  202. }

旋转代码-分长宽不一致情况

9-不同屏幕百分比自适应边框-采用DPIX

这个稍微简单点:只要获取出每个屏幕差值即可。

dpiX = graphics.DpiX / 96;//当前屏幕的DPI然后除以正常值96得出的值即为扩展百分比

10-录屏核心代码:(不采用之前的位图编译,直接通过引用第三方插件)

通过AForge对FFMPEG进行录屏封装,我们可以轻松的录制想要录制的内容,关于录屏时间上则采用的是异步执行Timer。

  1. private void video_NewFrame(object sender, NewFrameEventArgs e)
  2. {
  3. //if (this.IScreenRecording)
  4. //{
  5. this.videoWriter.WriteVideoFrame(e.Frame);
  6. //异步执行时间
  7. this.stopWatchLabel.Dispatcher.Invoke(new Action
  8. (() => this.stopWatchLabel.Text = string.Format
  9. (@"{0}", this.stopWatch.Elapsed.ToString("hh\\:mm\\:ss"))));
  10. //}
  11. //else
  12. //{
  13. // stopWatch.Reset();
  14. // videoStreamer.Stop();
  15. // videoWriter.Close();
  16. //}
  17. }

11-比较重要的一步:任何商用的录屏软件都需要实现播放、暂停、继续功能,这款软件也不例外:

  1. /// <summary>
  2. /// 点击之后更换图标并判断是否需要停止or启用
  3. /// </summary>
  4. /// <param name="sender"></param>
  5. /// <param name="e"></param>
  6. private void PauseOrRecording_Checked(object sender, RoutedEventArgs e)
  7. {
  8.  
  9. //暂停计时
  10. //暂停保存图片
  11. //暂停保存麦克风
  12. if (IScreenRecording)
  13. {
  14. this.stopWatch.Stop();//时间表
  15. //this.videoStreamer.Stop();
  16. this.videoStreamer.SignalToStop();
  17.  
  18. if (PassMediaMessage.IsrecordingSound)
  19. {
  20. //暂停
  21.  
  22. PassMediaMessage.IsrecordingSound = false;
  23. PassMediaMessage.Is_Recording = false;
  24. }
  25. IScreenRecording = false;
  26. }
  27. else
  28. {
  29. if (this.IsMicrophone.IsChecked == true)
  30. {
  31. PassMediaMessage.IsrecordingSound = true;
  32. PassMediaMessage.Is_Recording = true;
  33. }
  34. //启用(只是暂停并没有真正的释放)
  35. this.stopWatch.Start();//时间表
  36. this.videoStreamer.Start();
  37. IScreenRecording = true;
  38. }
  39. }

12-由于我们软件是面向世界的,所以必须有增加世界12国语言支持,这边就不再详细贴出代码了。

13-整体效果展示:

 

WPF 截屏软件开发的更多相关文章

  1. 老话题:自己编写只截窗口客户区的截屏软件(VB2010)

    现在能实现截屏的软件很多,就不一一列举了,连WIN7都自带截屏软件,甚至OFFICE2010开始都有截屏的功能. 截屏软件虽多,无外乎三种截屏方式:全屏截图.窗口截图.自定义矩形截图. 其中,窗口截图 ...

  2. 【WPF】WPF截屏

    原文:[WPF]WPF截屏 引言 .NET的截图控件在网上流传得不多啊,难得发现一个精品截图控件( 传送门),但是无奈是winform的.后来又找到一个周银辉做的WPF截图(继续传送门),发现截屏是实 ...

  3. 团队软件开发_基于windows下截屏软件关于NABC框架的特点

    经过我们小组数次的激烈讨论,就自己的能力和时间而言,我们小组的初步的计划是开发一款基于windows下的截图软件. 关于这个软件的功能,我们初步的想法如下: 1.能在windows下后台运行,有相应的 ...

  4. 动态截屏软件jpg格式

    软件下载地址:https://github.com/weibanggang/jiedu 开始截屏 保存路径 生成图片 预览

  5. Snipaste强大离线/在线截屏软件的下载、安装和使用

    步骤一: https://zh.snipaste.com/  ,去此官网下载. 步骤二:由于此是个绿色软件,直接解压即可. 步骤三:使用,见官网.ttps://zh.snipaste.com  按F1 ...

  6. picpick截屏软件脱壳

    0x01 准备 OD 基本查壳软件 picpick可执行文件(不是快捷方式) 0x02 查壳 软件是2018年9月,还是比较新的 显示EP区段是.vmp1,没见过,不知道是压缩壳还是加密壳,搜索所示y ...

  7. Kubuntu上截屏的小技巧

    Kubuntu上自带了截屏软件ksnapshot,只需要按Print Screen就会自动调起,实际上挺方便的:但是,Print Screen的默认行为是截下整个屏幕,这往往不是我们需要的. 实际上, ...

  8. [Tool]截屏利器FSCapture7.6下载

    下载地址:https://pan.baidu.com/s/1XQ1P5hHwZd0NE7bdz_znQQ 或是:https://files.cnblogs.com/files/xiandedanten ...

  9. 动画录屏软件-LICEcap

    越来越觉得脑子不够用了,不记博客根本找不到自己以前都干了些啥,自己学的东西用不到就特别难受,学完不用,重新捡起来跟嚼蜡一样难受. 接下来推荐一款动画截屏软件,短小精悍: https://www.coc ...

随机推荐

  1. 最常用的UML工具介绍

    最常用的UML工具介绍 1.Rational Rose.大名鼎鼎,史上最有名.最无可替代的UML产品,以至于,大多数将之等同于UML工具,正如将可乐等同于可口可乐.需要指出的是,自从 Rational ...

  2. SQL SERVER的事务日志

    1 基本介绍 每个数据库都具有事务日志,用于记录所有事物以及每个事物对数据库所作的操作. 日志的记录形式需要根据数据库的恢复模式来确定,数据库恢复模式有三种: 完整模式,完全记录事物日志,需要定期进行 ...

  3. 初探Lambda表达式/Java多核编程【4】Lambda变量捕获

    这周开学,上了两天感觉课好多,学校现在还停水,宿舍网络也还没通,简直爆炸,感觉能静下心看书的时间越来越少了...寒假还有些看过书之后的存货,现在写一点发出来.加上假期两个月左右都过去了书才看了1/7都 ...

  4. 从C#到TypeScript - Proxy

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  5. 每天一个Linux命令(11)--nl命令

    nl命令在Linux系统中用来计算文件中行号.nl可以将输出的文件内容自动的加上行号,其默认的结果与cat -n 有点不太一样,nl可以将行号做比较多的显示设计,包括位数与是否自动不起0等等的功能. ...

  6. 【经验】AngularJS

    1.关于ng-model <textarea id="feature_name" class="col-sm-3" placeholder="软 ...

  7. jquery考试纠错笔记.

    1. 获取元素范围大小顺序依次为: $(#one).siblings("div")>$("#one~div")>$("#one +div& ...

  8. Xpath定位总结

    先贴上练习xpath的地址:http://www.w3school.com.cn/example/xmle/books.xml 或则也可以使用百度进行练习 1.相对定位与绝对定位 //表示相对定位,对 ...

  9. 深入理解ajax系列第三篇——响应解码

    前面的话 我们接收到的响应主体类型可以是多种形式的,包括字符串String.ArrayBuffer对象.二进制Blob对象.JSON对象.javascirpt文件及表示XML文档的Document对象 ...

  10. 跨专业学习编程的苦逼生活 QWQ嘤嘤嘤

    一串串小小的代码,竟然可以做出辣么多的东西,彻底颠覆了我的世界观.人生观.价值观. 话不多说,一个例子证明一切>>>> <!DOCTYPE html> <ht ...