提供一个工具类,复制即可用,但是首先要去下载个NuGet包,直接搜索SharpAvi就OK了
public class VideoTranscribe { private readonly static object lockObj = new object(); private static VideoTranscribe instance = null; public static VideoTranscribe Instance { get { lock (lockObj) { if (instance == null) { instance = new VideoTranscribe(); } } return instance; } } public VideoTranscribe() { } #region Settings(配置) 初始化 private string outputFolder;//输出过滤器 private FourCC encoder;//编码器 private int encodingQuality;//编码质量 private int VideoFramesPerSecond;//视频帧数 // private int audioSourceIndex;//音频资料索引 // private SupportedWaveFormat audioWaveFormat;//音频波格式 // private bool encodeAudio;//音频编码 // private int audioQuality;//音频质量 /// <summary> /// 初始化默认值 /// </summary> public void InitDefaultSettings(int Quality, int FramesPerSecond, string path) { try { Log.Error("开始初始化录屏配置"); if (!System.IO.Directory.Exists(path)) { System.IO.Directory.CreateDirectory(path);//不存在就创建目录 } outputFolder = path; encoder = KnownFourCCs.Codecs.MotionJpeg; encodingQuality = Quality; VideoFramesPerSecond = FramesPerSecond; // audioSourceIndex = -1; // audioWaveFormat = SupportedWaveFormat.WAVE_FORMAT_44M16; // encodeAudio = true; // audioQuality = (Mp3AudioEncoderLame.SupportedBitRates.Length + 1) / 2; } catch (Exception ex) { Log.Error("初始化设置异常:" + ex.Message); } } #endregion #region Recording private readonly Stopwatch recordingStopwatch = new Stopwatch(); private Recorder recorder; private string lastFileName; /// <summary> /// 开始录频 /// </summary> public void StartRecording(string Name) { try { Log.Debug("开始录制视频"); lastFileName = System.IO.Path.Combine(outputFolder, Name + "" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".avi"); //var bitRate = Mp3AudioEncoderLame.SupportedBitRates.OrderBy(br => br).ElementAt(audioQuality); recorder = new Recorder(lastFileName,encoder, encodingQuality, VideoFramesPerSecond); recordingStopwatch.Start(); } catch (Exception ex) { StopRecording(); Log.Error("视频录制异常:" + ex.Message); } } /// <summary> /// 停止 /// </summary> public void StopRecording() { Log.Debug("执行视频停止"); try { if (recorder != null) { recorder.Dispose(); recorder = null; } } finally { recordingStopwatch.Stop(); } } /// <summary> /// 暂停 /// </summary> public void SuspendRecording() { try { Log.Debug("视频暂定"); if (recorder != null) { recorder.VideoSuspend(); } } catch (Exception ex) { Log.Error("视频暂停异常:" + ex.Message); } } /// <summary> /// 继续 /// </summary> public void ContinueRecording() { try { Log.Debug("视频继续"); if (recorder != null) { recorder.VideoContinue(); } } catch (Exception ex) { Log.Error("视频继续异常:" + ex.Message); } } /// <summary> /// 定位到avi视频上 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void GoToLastScreencast() { Process.Start("explorer.exe", string.Format("/select, \"{0}\"", lastFileName)); } #endregion }
internal class Recorder : IDisposable { Bitmap lastBitmap = new Bitmap(10, 10); private readonly int screenWidth; private readonly int screenHeight; private readonly AviWriter writer; private readonly IAviVideoStream videoStream; private readonly IAviAudioStream audioStream; private readonly WaveInEvent audioSource; private readonly Thread screenThread; private readonly ManualResetEvent stopThread = new ManualResetEvent(false); private readonly AutoResetEvent videoFrameWritten = new AutoResetEvent(false); private readonly AutoResetEvent audioBlockWritten = new AutoResetEvent(false); static CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; ManualResetEvent resetEvent = new ManualResetEvent(true); public Recorder(string fileName, FourCC codec, int quality, int VideoFramesPerSecond) { Matrix toDevice = new Matrix(); System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => { using (var source = new HwndSource(new HwndSourceParameters())) { toDevice = source.CompositionTarget.TransformToDevice; } })); screenWidth = (int)Math.Round(SystemParameters.PrimaryScreenWidth * toDevice.M11);//屏幕的宽度 screenHeight = (int)Math.Round(SystemParameters.PrimaryScreenHeight * toDevice.M22);//屏幕的高度 // Create AVI writer and specify FPS ///创建AVI写入器并指定FPS writer = new AviWriter(fileName) { FramesPerSecond = VideoFramesPerSecond, EmitIndex1 = true, }; // Create video stream //创建视频 videoStream = CreateVideoStream(codec, quality); // Set only name. Other properties were when creating stream, //只有名字。其他属性是在创建流时, // either explicitly by arguments or implicitly by the encoder used//无论是显式的参数或隐式的编码器使用 videoStream.Name = "Screencast"; //if (audioSourceIndex >= 0) //{ // var waveFormat = ToWaveFormat(audioWaveFormat);//告诉视频你的音频格式 // audioStream = CreateAudioStream(waveFormat, encodeAudio, audioBitRate);//创建音频 // // Set only name. Other properties were when creating stream, //只有名字。其他属性是在创建流时设置的 // // either explicitly by arguments or implicitly by the encoder used //无论是显式的参数或隐式的编码器使用 // audioStream.Name = "Voice"; // audioSource = new WaveInEvent // { // DeviceNumber = audioSourceIndex, // WaveFormat = waveFormat, // // Buffer size to store duration of 1 frame // BufferMilliseconds = (int)Math.Ceiling(1000 / writer.FramesPerSecond), // NumberOfBuffers = 3, // }; // audioSource.DataAvailable += audioSource_DataAvailable; //} screenThread = new Thread(RecordScreen)//开一个线程去执行,录制 { Name = typeof(Recorder).Name + ".RecordScreen", IsBackground = true }; if (audioSource != null) { videoFrameWritten.Set(); audioBlockWritten.Reset(); audioSource.StartRecording(); } screenThread.Start(); } /// <summary> /// 暂停 /// </summary> public void VideoSuspend() { resetEvent.Reset(); } /// <summary> /// 继续 /// </summary> public void VideoContinue() { resetEvent.Set(); } /// <summary> /// 创建视频流 /// </summary> /// <param name="codec"></param> /// <param name="quality"></param> /// <returns></returns> private IAviVideoStream CreateVideoStream(FourCC codec, int quality) { // Select encoder type based on FOURCC of codec if (codec == KnownFourCCs.Codecs.Uncompressed) { return writer.AddUncompressedVideoStream(screenWidth, screenHeight); } else if (codec == KnownFourCCs.Codecs.MotionJpeg) { return writer.AddMotionJpegVideoStream(screenWidth, screenHeight, quality #if !FX45 // Implementation of this encoder for .NET 3.5 requires single-threaded access , forceSingleThreadedAccess: true #endif ); } else { return writer.AddMpeg4VideoStream(screenWidth, screenHeight, (double)writer.FramesPerSecond, // It seems that all tested MPEG-4 VfW codecs ignore the quality affecting parameters passed through VfW API // They only respect the settings from their own configuration dialogs, and Mpeg4VideoEncoder currently has no support for this quality: quality, codec: codec, // Most of VfW codecs expect single-threaded use, so we wrap this encoder to special wrapper // Thus all calls to the encoder (including its instantiation) will be invoked on a single thread although encoding (and writing) is performed asynchronously forceSingleThreadedAccess: true); } } /// <summary> /// 创建音频流 /// </summary> /// <param name="waveFormat"></param> /// <param name="encode"></param> /// <param name="bitRate"></param> /// <returns></returns> private IAviAudioStream CreateAudioStream(WaveFormat waveFormat, bool encode, int bitRate) { // Create encoding or simple stream based on settings if (encode) { // LAME DLL path is set in App.OnStartup() return writer.AddMp3AudioStream(waveFormat.Channels, waveFormat.SampleRate, bitRate); } else { return writer.AddAudioStream( channelCount: waveFormat.Channels, samplesPerSecond: waveFormat.SampleRate, bitsPerSample: waveFormat.BitsPerSample); } } /// <summary> /// 获取音频格式 /// </summary> /// <param name="waveFormat"></param> /// <returns></returns> private static WaveFormat ToWaveFormat(SupportedWaveFormat waveFormat) { switch (waveFormat) { case SupportedWaveFormat.WAVE_FORMAT_44M16: return new WaveFormat(44100, 16, 1); case SupportedWaveFormat.WAVE_FORMAT_44S16: return new WaveFormat(44100, 16, 2); default: throw new NotSupportedException("Wave formats other than ‘16-bit 44.1kHz‘ are not currently supported."); } } /// <summary> /// 释放 /// </summary> public void Dispose() { VideoContinue(); stopThread.Set(); screenThread.Join(); if (audioSource != null) { audioSource.StopRecording(); audioSource.DataAvailable -= audioSource_DataAvailable; } // Close writer: the remaining data is written to a file and file is closed writer.Close(); stopThread.Close(); } /// <summary> /// 录制视频 /// </summary> private void RecordScreen() { var stopwatch = new Stopwatch(); #if FX45 Task videoWriteTask = null; #else IAsyncResult videoWriteResult = null; #endif var isFirstFrame = true; var shotsTaken = 0; var timeTillNextFrame = TimeSpan.Zero; stopwatch.Start(); while (!stopThread.WaitOne(timeTillNextFrame)) { var buffer = GetScreenshot(); shotsTaken++; // Wait for the previous frame is written //等待写入前一帧 if (!isFirstFrame) { #if FX45 videoWriteTask.Wait(); #else videoStream.EndWriteFrame(videoWriteResult); #endif videoFrameWritten.Set(); } if (token.IsCancellationRequested) { return; } // 初始化为true时执行WaitOne不阻塞 resetEvent.WaitOne(); if (audioStream != null) { var signalled = WaitHandle.WaitAny(new WaitHandle[] { audioBlockWritten, stopThread }); if (signalled == 1) break; } // Start asynchronous (encoding and) writing of the new frame ///开始异步(编码和)写入新帧 #if FX45 videoWriteTask = videoStream.WriteFrameAsync(true, buffer, 0, buffer.Length); #else videoWriteResult = videoStream.BeginWriteFrame(true, buffer, 0, buffer.Length, null, null); #endif timeTillNextFrame = TimeSpan.FromSeconds(shotsTaken / (double)writer.FramesPerSecond - stopwatch.Elapsed.TotalSeconds); if (timeTillNextFrame < TimeSpan.Zero) timeTillNextFrame = TimeSpan.Zero; isFirstFrame = false; } stopwatch.Stop(); // Wait for the last frame is written if (!isFirstFrame) { #if FX45 videoWriteTask.Wait(); #else videoStream.EndWriteFrame(videoWriteResult); #endif } } private byte[] GetScreenshot() { using (Bitmap avibitmap = GetScreen()) { System.Drawing.Point mouseXY = new System.Drawing.Point(); mouseXY = Control.MousePosition; using (Graphics mouseGraphics = Graphics.FromImage(avibitmap)) { mouseGraphics.DrawEllipse(new System.Drawing.Pen(new SolidBrush(System.Drawing.Color.Green), 5), new Rectangle(mouseXY.X - 10, mouseXY.Y - 10, 20, 20)); var buffer = new byte[avibitmap.Width * avibitmap.Height * 4]; var bits = avibitmap.LockBits(new Rectangle(0, 0, avibitmap.Width, avibitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Marshal.Copy(bits.Scan0, buffer, 0, buffer.Length); avibitmap.UnlockBits(bits); return buffer; } } } private Bitmap GetScreen() { try { var bitmap = new Bitmap(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height); var graphics = Graphics.FromImage(bitmap); graphics.CopyFromScreen(0, 0, 0, 0, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size); graphics.Dispose(); lastBitmap.Dispose(); lastBitmap = bitmap; } catch { } return (Bitmap)lastBitmap.Clone(); } private void audioSource_DataAvailable(object sender, WaveInEventArgs e) { var signalled = WaitHandle.WaitAny(new WaitHandle[] { videoFrameWritten, stopThread }); if (signalled == 0) { audioStream.WriteBlock(e.Buffer, 0, e.BytesRecorded); audioBlockWritten.Set(); } } }
如果,发现以上工具不能用了,那么就请你直接去 https://github.com/baSSiLL/SharpAvi ,暂停的话直接用线程阻塞就成.