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 ,暫停的話直接用線程阻塞就成.

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM