提供一個工具類,復制即可用,但是首先要去下載個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 ,暫停的話直接用線程阻塞就成.