• 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);
                    }
                }
            }
        }
    }
  • 相关阅读:
    关于软件设计中遇到的问题
    关于power shell
    如果TChart的发生异常
    重温gof版《设计模式》中的创建型模式
    如何更好的使用cvs
    存储过程返回临时表的问题
    关于bugzilla与svn结合的配置注意事项
    VC知识点:如何用vc6调试CGI程序
    如何让应用程序托盘化
    符号表
  • 原文地址:https://www.cnblogs.com/dachuang/p/16291663.html
Copyright © 2020-2023  润新知