最近由于工程需要开始研发基于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 = -20;
 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
18
19
20         #endregion
21
22         #region Public Methods and Operators
23
24         /// <summary>
25         /// 窗口前置透明设置命令
26         /// </summary>
27         /// <param name="hwnd">
28         /// The hwnd.
29         /// </param>
30         public static void SetWindowExTransparent(IntPtr hwnd)
31         {
32             var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
33             SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
34         }
35
36         #endregion
37
38         #region Methods
39
40         /// <summary>
41         /// The get window long.
42         /// </summary>
43         /// <param name="hwnd">
44         /// The hwnd.
45         /// </param>
46         /// <param name="index">
47         /// The index.
48         /// </param>
49         /// <returns>
50         /// The <see cref="int"/>.
51         /// </returns>
52         [DllImport("user32.dll")]
53         private static extern int GetWindowLong(IntPtr hwnd, int index);
54
55         /// <summary>
56         /// The set window long.
57         /// </summary>
58         /// <param name="hwnd">
59         /// The hwnd.
60         /// </param>
61         /// <param name="index">
62         /// The index.
63         /// </param>
64         /// <param name="newStyle">
65         /// The new style.
66         /// </param>
67         /// <returns>
68         /// The <see cref="int"/>.
69         /// </returns>
70         [DllImport("user32.dll")]
71         private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
72
73         #endregion

API方法

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

<Slider x:Name="volumeSlider" Grid.Column="7" Grid.ColumnSpan="3" Grid.Row="1" Width="100" Height="20" Minimum="0" Maximum="100" Value="100" 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 > 360 || AnAngle == 0)
 9             {
10                 AnAngle = 90;
11             }
12             TransformGroup transformGroup = new TransformGroup();
13
14             ScaleTransform scaleTransform = new ScaleTransform();
15             scaleTransform.ScaleX = -1;
16             transformGroup.Children.Add(scaleTransform);
17
18             RotateTransform rotateTransform = new RotateTransform(AnAngle);
19             transformGroup.Children.Add(rotateTransform);
20             videoPlayer.RenderTransform = transformGroup;
21             AnAngle += 90;
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
10             var fileName = string.Format("Recording {0}.mp4", DateTime.Now.ToString("yy-MM-dd HH-mm-ss"));
11             var outputFilePath = Path.Combine(this.settings.StoragePath, fileName);
12             this.fileViewModel = new ScreenGunFileViewModel(outputFilePath, RecordingStage.DoingNothing);
13
14             var opts = new ScreenRecorderOptions(this.RecordingRegion)
15             {
16                 DeleteMaterialWhenDone = true,
17                 OutputFilePath = outputFilePath,
18                 RecordMicrophone = this.UseMicrophone,
19                 AudioRecordingDeviceNumber = this.settings.RecordingDeviceNumber
20             };
21
22             var progress = new Progress<RecorderState>(state => this.fileViewModel.RecordingStage = state.Stage);
23             this.recorder.Start(opts, progress);
24         }

录屏代码

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 / 2;
15                 var regionHeight = (double)screen.Bounds.Height / 2;
16                 double x = ((double)screen.Bounds.Width / 2) - (regionWidth / 2);
17                 double y = ((double)screen.Bounds.Height / 2) - (regionHeight / 2);
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 == 180 || AnAngle == 360)
  5             {
  6                 #region --竖直拉伸--
  7                 double Image_xx = 0;
  8                 double Image_yy = 0;
  9                 double point_xx = 0;
 10                 double point_yy = 0;
 11                 double point_center = Math.Abs(this.MainGrid.Width / 2 - this.MainGrid.Height / 2);//当前中心点值
 12                 double actual_center = Math.Abs(this.videoPlayer.MinWidth / 2 - this.videoPlayer.MinHeight / 2);//实际中心点,用于比较初始值
 13
 14                 if (Math.Abs(Image_PointX) == 0)
 15                 {
 16                     point_xx = -25;//初始化原点未动
 17                 }
 18                 else
 19                 {
 20                     //拖动到其他位置时偏移量(必须是固定值)
 21                     if (Image_PointX < -25)
 22                     {
 23                         point_xx = -25;
 24                     }
 25                     else
 26                     {
 27                         point_xx = Image_PointX;
 28                     }
 29                 }
 30
 31                 if (Math.Abs(Image_PointY) == 0)
 32                 {
 33                     point_yy = -25;//初始化原点未动
 34                 }
 35                 else
 36                 {
 37                     //拖动到其他位置时偏移量(必须是固定值)
 38                     if (Image_PointY < -25)
 39                     {
 40                         point_yy = -25;
 41                     }
 42                     else
 43                     {
 44                         point_yy = Image_PointY;
 45                     }
 46
 47                 }
 48                 if (Math.Abs(point_xx) == 25)
 49                 {
 50                     Image_xx = videoPlayer.ActualHeight;
 51                 }
 52                 else
 53                 {
 54                     Image_xx = videoPlayer.ActualHeight + Math.Abs(Image_PointX) - 25;
 55                 }
 56                 if (Math.Abs(point_yy) == 25)
 57                 {
 58                     Image_yy = videoPlayer.ActualWidth;
 59                 }
 60                 else
 61                 {
 62                     Image_yy = Math.Abs(Image_PointY) + videoPlayer.ActualWidth - 25;
 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
111                 //初始值(-25,-25)-->(x,y)
112                 if ((Image_xx >= RecordingArea.Width) || Image_yy >= RecordingArea.Height)
113                 {
114
115                 }
116                 else
117                 {
118                     if (point_center >= actual_center)
119                     {
120                         // (point_center - actual_center)为x-y轴偏移量
121                         //point_xx--point_yy为当前x,y轴坐标
122                         m.OffsetX = point_xx - (point_center - actual_center);
123                         m.OffsetY = point_yy - (point_center - actual_center);
124
125                     }
126                 }
127                 MainGrid.RenderTransform = new MatrixTransform(m);//重新定位
128
129                 #endregion
130
131
132
133                 #endregion
134             }
135             else
136             {
137                 #region --正常拉伸--
138                 //左右拉伸(只能往右拉伸)
139                 if (e.LeftDirection.HasValue)
140                 {
141                     var value = videoPlayer.Width + e.HorizontalChange;
142                     if (value > videoPlayer.MinWidth)
143                     {
144                         videoPlayer.Width = value;
145                         MainGrid.Width = value;
146                         if (videoPlayer.ActualWidth < value)
147                         {
148                             MainGrid.Width = videoPlayer.ActualWidth;
149                         }
150                         if (Image_PointX + videoPlayer.ActualWidth >= RecordingArea.Width)
151                         {
152                             MainGrid.Width = videoPlayer.ActualWidth;
153                             videoPlayer.Width = videoPlayer.ActualWidth;
154                         }
155                     }
156                 }
157                 //上下拉伸(只能往上拉伸)
158                 if (e.TopDirection.HasValue)
159                 {
160                     var value = videoPlayer.Height + e.VerticalChange;
161                     if (value > videoPlayer.MinHeight)
162                     {
163                         videoPlayer.Height = value;
164                         MainGrid.Height = value;
165                         if (videoPlayer.ActualHeight < value)
166                         {
167                             MainGrid.Height = videoPlayer.ActualHeight;
168                         }
169
170                         if (Math.Abs(Image_PointY) + videoPlayer.ActualHeight >= RecordingArea.Height)
171                         {
172                             MainGrid.Height = videoPlayer.ActualHeight;
173                             videoPlayer.Height = videoPlayer.ActualHeight;
174                         }
175
176                     }
177                 }
178                 #endregion
179             }
180         }

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

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

  1        private void RotateCamera_bt(object sender, RoutedEventArgs e)
  2         {
  3             if (MainGrid.ActualWidth > SystemParameters.PrimaryScreenHeight)
  4             {
  5                 return;
  6             }
  7             if (AnAngle > 360 || AnAngle == 0)
  8             {
  9                 AnAngle = 90;
 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) / 2 - (this.MainGrid.ActualHeight) / 2;
 21             // Image_PointX,Image_Point为当前坐标
 22             if (AnAngle == 90 || AnAngle == 270)
 23             {
 24                 if (AnAngle == 270)
 25                 {
 26                     RectangleU.VerticalAlignment = VerticalAlignment.Bottom;
 27                     RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
 28                     RectangleU.BorderThickness = new Thickness(0, 0, 8, 8);
 29                     RectangleU.CornerRadius = new CornerRadius(0, 0, 1, 0);
 30
 31                 }
 32                 else
 33                 {
 34                     RectangleU.VerticalAlignment = VerticalAlignment.Top;
 35                     RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
 36                     RectangleU.BorderThickness = new Thickness(8, 8, 0, 0);
 37                     RectangleU.CornerRadius = new CornerRadius(1, 0, 0, 0);
 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 * 2;
 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             }
101             else
102             {
103                 if (AnAngle == 180)
104                 {
105                     RectangleU.VerticalAlignment = VerticalAlignment.Bottom;
106                     RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
107                     RectangleU.BorderThickness = new Thickness(8, 0, 0, 8);
108                     RectangleU.CornerRadius = new CornerRadius(0, 0, 0, 1);
109
110                 }
111                 else
112                 {
113                     RectangleU.VerticalAlignment = VerticalAlignment.Top;
114                     RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
115                     RectangleU.BorderThickness = new Thickness(0, 8, 8, 0);
116                     RectangleU.CornerRadius = new CornerRadius(0, 1, 0, 0);
117
118                 }
119                 if (IschangeAngle)
120                 {
121                     if (this.MainGrid.Width >= this.RecordingArea.Height)
122                     {
123                         Image_PointX = 0;
124                     }
125                     else
126                     {
127                         if (Image_PointX + this.videoPlayer.Width > this.RecordingArea.Width)
128                         {
129                             m.OffsetX = Image_PointX - point_xx;
130                         }
131                         else
132                         {
133                             m.OffsetX = Image_PointX + point_xx;
134                         }
135                     }
136                     //旋转为竖直拉到某个坐标时触发
137                     if (Image_PointY >= -point_xx)
138                     {
139                         m.OffsetY = 0;
140                     }
141                     else
142                     {
143                         m.OffsetY = Image_PointY - point_xx;
144                     }
145
146                 }
147                 else
148                 {
149
150                     if (this.MainGrid.Width >= this.RecordingArea.Width)
151                     {
152                         m.OffsetX = 0;
153                         m.OffsetY = 0;
154                     }
155                     else
156                     {
157                         //正常情况
158                         if (Image_PointX <= 0)
159                         {
160                             m.OffsetX = 0;
161                         }
162                         else
163                         {
164                             m.OffsetX = Image_PointX;
165                         }
166                         if (Image_PointY >= 0)
167                         {
168                             m.OffsetY = 0;
169                         }
170                         else
171                         {
172                             m.OffsetY = Image_PointY;
173                         }
174                     }
175                 }
176             }
177             //IschangeAngle = false;
178             //更换坐标位置
179             MainGrid.RenderTransform = new MatrixTransform(m);
180
181
182             var x = Math.Min(this.startPosition.X, this.endPosition.X);
183             var y = Math.Min(this.startPosition.Y, this.endPosition.Y);
184             if (AnAngle == 90 || AnAngle == 270)
185             {
186                 if (this.MainGrid.Width >= this.RecordingArea.Height)
187                 {
188                     this.relativeRecordingArea = new Rect(x, y, this.MainGrid.Height, this.MainGrid.Width);
189                     this.UpdateUI();
190                 }
191             }
192             else
193             {
194                 if (this.MainGrid.Width >= this.RecordingArea.Width)
195                 {
196                     this.relativeRecordingArea = new Rect(x, y, this.MainGrid.Width, this.MainGrid.Height);
197                     this.UpdateUI();
198                 }
199             }
200             //UpdatePosition();
201
202             AnAngle += 90;
203             #endregion
204         }

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

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

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

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

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

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

  private void video_NewFrame(object sender, NewFrameEventArgs e){//if (this.IScreenRecording)//{this.videoWriter.WriteVideoFrame(e.Frame);//异步执行时间this.stopWatchLabel.Dispatcher.Invoke(new Action(() => this.stopWatchLabel.Text = string.Format(@"{0}", this.stopWatch.Elapsed.ToString("hh\\:mm\\:ss"))));//}//else//{//    stopWatch.Reset();//    videoStreamer.Stop();//    videoWriter.Close();//}}

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

   /// <summary>/// 点击之后更换图标并判断是否需要停止or启用/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void PauseOrRecording_Checked(object sender, RoutedEventArgs e){//暂停计时//暂停保存图片//暂停保存麦克风      if (IScreenRecording){this.stopWatch.Stop();//时间表//this.videoStreamer.Stop();this.videoStreamer.SignalToStop();if (PassMediaMessage.IsrecordingSound){//暂停
PassMediaMessage.IsrecordingSound = false;PassMediaMessage.Is_Recording = false;}IScreenRecording = false;}else{if (this.IsMicrophone.IsChecked == true){PassMediaMessage.IsrecordingSound = true;PassMediaMessage.Is_Recording = true;}//启用(只是暂停并没有真正的释放)this.stopWatch.Start();//时间表this.videoStreamer.Start();IScreenRecording = true;}}

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

13-整体效果展示:

 

转载于:https://www.cnblogs.com/BarryJames/p/6679407.html

WPF 录屏软件研发心得及思路分享(已结束开发)相关推荐

  1. 盘点B站up主常用的免费录屏软件,自用工具分享

    盘点B站up主常用的免费录屏软件,自用工具分享! 今天分享 4 款超好用的免费录屏软件,能够满足大部分同学录制教学视频.游戏视频.网络课程等录制场景的需求. 而且,这 4 款录屏软件还是主播.自媒体达 ...

  2. Win7系统自带的录屏工具怎么打开操作教学分享

    Win7系统自带的录屏工具怎么打开操作教学分享.很多的用户不知道Win7系统其实有自带了一个录屏工具.我们通过代码打开它之后,就可以非常方便的进行内容录制了.一起来看看如何开启它的操作教学吧. 操作步 ...

  3. 录屏工具有哪些?分享:电脑录制微课的工具和方法

    在录制微课的时候,人们在所难免都要录制电脑内的操作步骤,此时就不得不用录屏工具去录制这些内容.虽然现目前软件市场上的录屏工具非常之多,但有些软件录制的画面并不是很清晰,操作也不是很方便,那么那些录屏工 ...

  4. 怎样录屏不带水印?分享一款无水印录制视频软件!

    案例:怎样录制无水印的视频? [我平常录制的录屏带有软件自带水印,这样十分影响观感.怎样才能录制无水印的视频?] 一款好的录屏软件,可以更好地帮助我们录制电脑屏幕上的操作或是制作教学视频.然而,很多录 ...

  5. 录屏软件哪个好?分享:好用的录屏软件,一款就够!

    生活中有非常多地方都需要用到录屏软件,如上网课如果我们没有时间听,往往会选择将课程录制下来听回放:如线上会议我们为了更好的做会议纪要,也会选择将会议录制下来:再比如我们在打游戏时,想记录自己的高光时刻 ...

  6. oppo手机如何录屏?好的方法分享一波

    OPPO手机是一款非常流行的手机品牌,它有许多实用的功能,其中一个是录屏功能.通过录屏,你可以轻松地记录你的手机屏幕内容并与别人分享,因此在这篇文章中,我们将重点介绍如何在OPPO手机上录屏,以及其他 ...

  7. 录录(高清录屏) - Video321 如何快速分享视频

    大家好,今天我们录录录屏又和大家见面啦- 录录录屏-Video321是一款通过视频录制.视频分享的方式提高办公效率的SaaS型产品.通过使用"录录录屏 - Video321",你可 ...

  8. 录屏软件有哪些?分享几种简单实用的录屏工具

    有哪些好用的录屏软件呢?如果你需要向他人展示如何操作一个软件或者如何进行某项任务,录制一个演示视频是非常有用的.你可以在视频中演示每个步骤,然后分享给你的观众.这对于教育和培训领域非常有用,因为它可以 ...

  9. 电脑录像软件哪个好用?录屏大神的技巧分享

    身边有一些人做游戏视频解说和直播,看到他们都让我用超级捕快. 然后跟我说还能用来录制微课视频.录制网上的在线电影. 方法确实小白都能学会,然后录制的画质好,体积也小. 我直接教大家怎么快速录制高清无损 ...

最新文章

  1. JSP标准标签库(JSTL)--简介
  2. CF思维联系–CodeForces - 222 C Reducing Fractions(数学+有技巧的枚举)
  3. 使用matlab播放特定频率的声音
  4. Harvard-X免费生物信息课程 (代码、文档、数据) - 适合系统学习
  5. SqlBulkCopy类进行大数据(一万条以上)插入测试
  6. VSCode自定义代码片段13——Vue的状态大管家
  7. 3分钟了解ServiceStage 应用智能化运维【华为云分享】
  8. python实现数据恢复_数据恢复/电子取证 非常有用的python库——Construct | 学步园...
  9. 解决Spring JdbcTemplate调用queryForObject()方法结果集为空时报异常
  10. 浏览器下载文件时一共发送2次请求,如何把“下载次数”只记录为1次?
  11. Linux命令(二)
  12. Pulseaudio之nemo(二十二)
  13. 前景检测算法(六)--平均背景原理
  14. 据说,上次获奖的同学拿了奖金泡了班花还get到了2个offer
  15. 中小企业网络推广如何找到切入点
  16. Panabit安装snmp插件
  17. mapxtreme java_用mapXtreme Java开发web gis应用 (下)
  18. 活动报名 | 保护儿童,保护未来!智源发布《面向儿童的人工智能北京共识》
  19. 第四批入围企业公示——年度TOP100智能网联供应商评选
  20. 【洛谷P4826】Superbull S【最大生成树】

热门文章

  1. 原理c语言for循环延时1s,for循环实现C语言精确延时
  2. Windows bat批处理 结束进程
  3. error: cannot lock ref 'refs/remotes/origin/test/pressure-test': 'refs/remotes/origin/test' exists;
  4. 2019杭电多校第七场 Kejin Player HDU - 6656 (期望)
  5. ActiveSync与设备连接
  6. C语言中的连等式解析
  7. 小学生图片_2020中秋节对家人的祝福语 送手抄报小学生图片大全简单又漂亮
  8. 百度搜索公正性彻底调查
  9. Mysql-mmm高可用集群
  10. 华为鸿蒙os多少钱一部手机,华为的鸿蒙OS,你了解多少?