WPF之SharpAvi视频录制(AVI)

提供一个工具类,复制即可用,但是首先要去下载个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 ,暂停的话直接用线程阻塞就成.

原文地址:https://www.cnblogs.com/ACBJTR/p/11854885.html