C#录制音频

  github上有个开源的RecordWin的demo,代码生成能直接运行,这点是真的好,有的demo下载下来真的各种dll要自己下载,要么折腾半天要么最后运行不了,各位大佬们走点心啊。

  原理就是用aforge这个组件录屏不带声音的,然后用naudio这个组件录音,最后用ffmpeg.exe把视频和声音组合到一起就可以了,有一个缺陷就是录屏最后一定要正常关闭,否则不但不会合成音频文件,原有没声音的文件也提示损坏,暂没找到方法;

  这个也是找的几个开源中相对最好的,开源还有一个kogle.record的这个,功能也能实现,但录的声音杂音很大,感觉都不能用,还有就是内存占用太高了,能飚到1G多,这就很夸张了。

  RecordWin中需要音频的在设置里把类型从mp4改成avi就可以了。

  RecordWin项目下载地址: https://github.com/yangjinming1062/RecordWin,再次感谢这位大佬

  demo是wpf写的,改成winform不难,里面改动好像就绘制鼠标那点,直接贴上我的代码吧,我的是winform里写的,供参考:

 

using AForge.Video;
using AForge.Video.FFMPEG;
using Common;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Eams_Form
{
     public class VideoHelper
    {
        Timer videoTimer;
        System.Timers.Timer videoClearTimer;
        private int frameRate = 5; // 采集视频的帧频
        private int videoQuality = 5;


        private readonly VideoFileWriter VideoWriter = new VideoFileWriter();
        private ScreenCaptureStream VideoStreamer;
        private WaveInEvent AudioStreamer;
        private WaveFileWriter AudioWriter;

        private string CurrentVideoPath;
        private string CurrentAudioPath;
        private Screen CurrentScreen;
        private DateTime beginTime;
        /// <summary>
        /// 已经录制帧数:统计帧数计算时长,每凑够一个帧率的数时长加1s并重置回0
        /// </summary>
        private int FrameCount;

        #region 鼠标捕获
        [DllImport("user32.dll")]
        private static extern bool GetCursorInfo(out CURSORINFO pci);

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CURSORINFO
        {
            public int cbSize;
            public int flags;
            public IntPtr hCursor;
            public POINT ptScreenPos;
        }
        #endregion

        public VideoHelper()
        {
            videoTimer = new Timer();
            videoTimer.Tick += new EventHandler(RecordEventSave); //录屏音频保存
            //videoTimer.Tick += new EventHandler(RecordEventDel); //录屏音频删除
            videoTimer.Interval = 59800;

            videoClearTimer = new System.Timers.Timer();
            videoClearTimer.Elapsed += RecordEventDel;
            videoClearTimer.Start();
            Task.Run(()=> {
                DeleteMp4AndWav();
            });
        }

        /// <summary>
        /// 删除MP4和Wav
        /// </summary>
        private void DeleteMp4AndWav()
        {
            try
            {
                DirectoryInfo dyInfo = new DirectoryInfo(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "VideoFile");
                //获取文件夹下所有的文件
                foreach (FileInfo feInfo in dyInfo.GetFiles())
                {
                    if (feInfo.Extension.ToLower() == ".mp4" || feInfo.Extension.ToLower() == ".wav")
                    {
                        feInfo.Delete();
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("删除MP4和wav文件出错", ex);
            }
        }

        /// <summary>
        /// 定时录屏音频保存
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void RecordEventSave(object source, EventArgs e)
        {
            try
            {
                if (DateTime.Now.Minute % 5 == 0)
                {
                    RecordStop();
                    RecordStart();
                }                
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("录屏定时异常:", ex);
            }
        }

        /// <summary>
        /// 录屏音频删除
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void RecordEventDel(object source, EventArgs e)
        {
            try
            {
                if (DateTime.Now.Minute % 12 == 0)
                {
                    DirectoryInfo dyInfo = new DirectoryInfo(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "VideoFile");
                    //获取文件夹下所有的文件
                    foreach (FileInfo feInfo in dyInfo.GetFiles())
                    {
                        //判断文件日期是否是指定小时前的,是则删除
                        int deletetime = Convert.ToInt32(ConfigurationManager.AppSettings["deletevideo"]);
                        if (feInfo.CreationTime < DateTime.Now.AddHours(deletetime))
                            feInfo.Delete();
                    }
                    LogHelper.getLogHelper().WriteLog("删除" + ConfigurationManager.AppSettings["deletevideo"] + "小时之前的视频文件");
                }
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("定时删除录屏数据异常:", ex);
            }
        }


        /// <summary>
        /// 录屏开始
        /// </summary>
        public void RecordStart()
        {
            try
            {
                #region 初始化录屏组件
                int RecordWidth = 0, RecordHeight = 0, RecordTop = 0, RecordLeft = 0;
                foreach (var s in System.Windows.Forms.Screen.AllScreens)
                {
                    RecordWidth += Math.Abs(s.Bounds.Width);
                    if (Math.Abs(s.Bounds.Height) > RecordHeight)
                        RecordHeight = Math.Abs(s.Bounds.Height);
                    RecordLeft = Math.Min(s.Bounds.X, RecordLeft);
                    RecordTop = Math.Min(s.Bounds.Y, RecordTop);
                }
                CurrentScreen = Screen.PrimaryScreen;
                //重编码体积更小,但清晰度受影响,不录制声音时直接输出MP4不再ffmpeg处理
                CurrentVideoPath = AppDomain.CurrentDomain.BaseDirectory + "VideoFile\\video_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".mp4";
                CurrentAudioPath = CurrentVideoPath.Replace(".mp4", ".wav");//使音频文件和视频文件同名


                lock (this)
                {
                    VideoWriter.Open(CurrentVideoPath, RecordWidth, RecordHeight, frameRate, VideoCodec.MPEG4,
                        CurrentScreen.Bounds.Width * CurrentScreen.Bounds.Height * videoQuality);
                }
                Rectangle rec = new Rectangle(RecordLeft, RecordTop, RecordWidth, RecordHeight);
                VideoStreamer = new ScreenCaptureStream(rec, 1000 / frameRate);//帧间隔需要和帧率关联,不然录的10秒视频文件不是10s
                VideoStreamer.NewFrame += VideoNewFrameHandle;
                beginTime = DateTime.Now;
                VideoStreamer.Start();


                AudioStreamer = new WaveInEvent();
                AudioStreamer.DataAvailable += AudioDataAvailableHandle;
                AudioWriter = new WaveFileWriter(CurrentAudioPath, AudioStreamer.WaveFormat);
                AudioStreamer.StartRecording();

                SystemConfig.IsRecording = true;
                #endregion
                videoTimer.Start();
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("开启录屏出错", ex);
            }
        }

        private void AudioDataAvailableHandle(object sender, WaveInEventArgs e)
        {
            try
            {
                if (SystemConfig.IsRecording)
                {
                    AudioWriter.Write(e.Buffer, 0, e.BytesRecorded);
                }
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("录音回调函数出错", ex);
            }
        }

        private void VideoNewFrameHandle(object sender, NewFrameEventArgs e)
        {
            try
            {
                if (SystemConfig.IsRecording)
                {
                    TimeSpan time = DateTime.Now - beginTime;
                    CURSORINFO pci;
                    pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
                    GetCursorInfo(out pci);
                    try
                    {
                        System.Windows.Forms.Cursor cur = new System.Windows.Forms.Cursor(pci.hCursor);//在每一帧上手动绘制鼠标
                        cur.Draw(Graphics.FromImage(e.Frame), new Rectangle(System.Windows.Forms.Cursor.Position.X - 10 - CurrentScreen.Bounds.X,
                            System.Windows.Forms.Cursor.Position.Y - 10 - CurrentScreen.Bounds.Y, cur.Size.Width, cur.Size.Height));
                    }
                    catch { }//打开任务管理器时会导致异常
                    VideoWriter.WriteVideoFrame(e.Frame, time);//处理视频时长和实际物理时长不符,用帧间隔时长的办法指定每一帧的间隔
                    FrameCount += 1;
                    if (FrameCount == frameRate)
                    {
                        FrameCount = 0;
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("录屏每帧回调函数出错", ex);
            }
        }

        internal void RecordStop()
        {
            try
            {
                SystemConfig.IsRecording = false;
                videoTimer.Stop();
                try
                {
                    VideoStreamer.Stop();//.Net Core时该方法异常,统一都加一个异常捕获
                }
                catch { }
                VideoWriter.Close();

                AudioStreamer.StopRecording();
                AudioStreamer.Dispose();
                AudioWriter.Close();

                //有视频有声音的时候再进行ffmpeg合成
                string tempVideo = CurrentVideoPath;
                string addAudio = $"-i \"{CurrentAudioPath}\"";
                string tempAudio = CurrentAudioPath;

                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {                    
                    string outfile = AppDomain.CurrentDomain.BaseDirectory + "VideoFile\\video_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".avi";
                    Functions.CMD($"ffmpeg -i {tempVideo} {addAudio} -acodec copy {outfile} -crf 12");
                    DeleteFile(tempVideo, tempAudio, true, true);
                });
            }
            catch (Exception ex)
            {
                LogHelper.getLogHelper().WriteLog("保存并合成avi视频文件出错", ex);
            }
        }
        /// <summary>
        /// 600秒内每1秒钟尝试一次删除指定原始音视频文件,直至全部删除
        /// (防止因占用等原因导致没有一次删除成功)
        /// </summary>
        private void DeleteFile(string tempVideo, string tempAudio, bool DelVideo = true, bool DelAudio = true)
        {
            for (int i = 0; i < 600; i++)
            {
                try
                {
                    if (File.Exists(tempVideo) && DelVideo) File.Delete(tempVideo);
                    if (File.Exists(tempAudio) && DelAudio) File.Delete(tempAudio);
                    break;
                }
                catch
                {
                    System.Threading.Thread.Sleep(1000);
                }
            }
        }
    }
}

 

posted @ 2022-05-20 11:11  卡萨丁·周  阅读(1259)  评论(0编辑  收藏  举报