惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
Recorded Future
Recorded Future
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
博客园 - Franky
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
T
Troy Hunt's Blog
The Register - Security
The Register - Security
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
S
Security Affairs
博客园 - 司徒正美
S
Schneier on Security
I
InfoQ
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
腾讯CDC
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
V
Vulnerabilities – Threatpost
C
Check Point Blog
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cisco Blogs
Schneier on Security
Schneier on Security
O
OpenAI News
K
Kaspersky official blog

博客园 - 至尊龙骑

Linux服务器离线安装Docker全攻略--离线安装(一件安装docker以及docker-compose--执行自定义脚本) C# 使用FFmpeg 命令 水印以及水印位置 录音录像时设置分辨率-分辨率一般是宽高比是 4:3 和16:9 少数是 5:4 FFmpeg 命令 水印以及水印位置 FFmpeg 命令录音录像时分辨率 FFmpeg 查看分辨率以及指定分辨率 C# 中监听 IPv6 回环地址----HttpListener C# 中监听 IPv6 回环地址(Loopback Address)----socket和tcp IPv6 地址 后端设置了跨域但是还是提示跨域问题,原因是这里有两个独立的安全策略在起作用:Chrome和Edge浏览器安全策略强制修改方案 开放所有跨域 ----前端和后端 Win11 上遇到的 WinForm 文件拖拽功能失效,但在 Win10 或其他系统上正常的问题 设置iis的后缀名可以下载 linux执行systemctl enable redis.service 报 Failed to execute operation: Bad message linux redis 8.2.1软件开机启动redis.service与etc下的rc.local配置2种方式 Linux redis 8.2.1源码编译 Linux开机启动设置全攻略 Linux 设置nginx 以及java jar自启动 Linux系统简单源码安装NGINX版本1.28.0 DistributedLock 实现.Net分布式锁 Windows 10\11 离线安装.NET Framework 3.5(包括.NET 2.0和3.0) - 至尊龙骑 跨域处理 utools无法搜索快捷方式和部分软件问题
C# 使用FFmpeg 命令录音录像时设置 水印以及水印位置
至尊龙骑 · 2026-03-05 · via 博客园 - 至尊龙骑
 /// <summary>
 /// 
 /// </summary>
 /// <param name="voiceText"></param>
 /// <returns></returns>
 public static bool PlaySpeech(string voiceText)
 {
     #region 初始化语音
     if (SpeechTxt == null)
     {
         try
         {
             SpeechTxt = new SpeechSynthesizer();
             // SpeechTxt.SelectVoice("Microsoft Lili");//设置播音员(中文)
             // SpeechTxt.SelectVoice("Microsoft Anna"); //英文
             // SpeechTxt.Volume = 100; //音量
             var cultureInfo = System.Windows.Forms.InputLanguage.CurrentInputLanguage.Culture ?? Thread.CurrentThread.CurrentCulture;
             VoiceInfo voiceInfo = SpeechTxt.GetInstalledVoices(cultureInfo)?.FirstOrDefault()?.VoiceInfo;
             SpeechTxt.SelectVoice(voiceInfo.Name ?? "Microsoft Lili");
         }
         catch (Exception ex)
         {
             logger.Error($"初始化语音出错,原因:{ex.InnerException}");
             SpeechTxt = null;
         }
     }
     #endregion

     bool result = false;

     if (string.IsNullOrWhiteSpace(voiceText))
     {
         return result;
     }

     try
     {
         //SpeechTxt.Speak(voiceText);
         SpeechTxt.SpeakAsync(voiceText);
         result = true;
     }
     catch (Exception ex)
     {
         logger.Error($"PlaySpeech,失败,原因:{ex.Message}");
         result = false;
     }
     return result;
 }

 public static bool KillProcess(string prcessName, string command = "taskkill /IM ffmpeg.exe /F")
 {
     bool result = false;
     System.Diagnostics.Process p = null;
     try
     {
         if (!string.IsNullOrWhiteSpace(prcessName))
         {
             System.Diagnostics.Process[] processList = System.Diagnostics.Process.GetProcesses();
             foreach (System.Diagnostics.Process process in processList)
             {

                 if (process.ProcessName.ToLower() == prcessName.ToLower())
                 {
                     process.Kill(); //结束进程                   
                 }
             }
             result = true;
         }
         else
         {
             ////ProcessRecord.StandardInput.WriteLine("taskkill /IM ffmpeg.exe /F");
             p = new System.Diagnostics.Process();
             p.StartInfo.FileName = "cmd.exe"; //设定程序名
             p.StartInfo.Arguments = "/c " + command; //设定程式执行参数
             p.StartInfo.UseShellExecute = false; //关闭Shell的使用
             p.StartInfo.RedirectStandardInput = true; //重定向标准输入
             p.StartInfo.RedirectStandardOutput = true; //重定向标准输出
             p.StartInfo.RedirectStandardError = true; //重定向错误输出
             p.StartInfo.CreateNoWindow = true; //设置不显示窗口
             p.Start(); //启动
             result = true;
         }
     }
     catch (Exception ex)
     {
         logger.Error($"KillProcess,失败,原因:{ex.Message}");
         //ProcessRecord.StandardInput.WriteLine("taskkill /IM ffmpeg.exe /F");
         result = false;
     }
     finally
     {
         p.WaitForExit();
         p.Close();
         p.Dispose();
     }
     return result;
 }

 /// <summary>
 /// 停止录像
 /// </summary>
 /// <returns></returns>
 public static bool StopRecord()
 {
     bool result = false;
     try
     {
         //string a = "q";

         ////ProcessRecord.StartInfo.RedirectStandardInput = true; // 重定向标准输入
         //// 写入数据到标准输入
         //using (StreamWriter sw = ProcessRecord.StandardInput)
         //{
         //    if (sw.BaseStream.CanWrite)
         //    {
         //        sw.WriteLine(a);
         //        sw.WriteLine("exit");
         //    }
         //}
         if (ProcessRecord == null)
         {
             PlaySpeech("录音录像未开启,请先开启录音录像设备!");
             return false;
         }
         ProcessRecord.StandardInput.WriteLine("q");
         ProcessRecord.StandardInput.WriteLine("exit");
         ProcessRecord.WaitForExit();
         ProcessRecord.Close();
         ProcessRecord.Dispose();
         Untils.PlaySpeech("录音录像已关闭");
         result = true;

     }
     catch (Exception ex)
     {
         logger.Error($"StopRecord,失败,原因:{ex.Message}");
         ProcessRecord.WaitForExit();
         ProcessRecord.Close();
         ProcessRecord.Dispose();
         //Untils.PlaySpeech("录音录像已关闭");
         //ProcessRecord.StandardInput.WriteLine("taskkill /IM ffmpeg.exe /F");
         result = false;
     }
     return result;
 }

 /// <summary>
 /// 开始录像
 /// </summary>
 /// <returns></returns>
 public static bool StartRecord(ref string recordvideoFilePath)
 {
     bool result = false;
     try
     {
         //StopRecord();
         KillProcess(null);

         //string videoInput = GetSetting("RecordVideo");
         //string audioInput = GetSetting("RecordAudio");
         //logger.Info($"StartRecord,录像:{videoInput}");
         //logger.Info($"StartRecord,录音:{audioInput}");

         string videoInput = GetSetting("RecordVideoPID");
         string audioInput = GetSetting("RecordAudioPID");
         //string videoInput = GetSetting("FisheyeRecordVideoCameraPID");
         videoInput = GetDeviceFromPID(videoInput, 2);
         //logger.Info($"StartRecord,录音录像-录像设备:{videoInput}");

         //string audioInput = GetSetting("FisheyeRecordAudioPID");
         audioInput = GetDeviceFromPID(audioInput, 1);
         ////audioInput = GetSetting("RecordAudio");
         //logger.Info($"StartRecord,录音录像-录音设备:{audioInput}");

         string videoSaveFormat = GetSetting("RecordAudio");
         if (string.IsNullOrWhiteSpace(videoInput))
         {
             //MessageBox.Show("未选择录像设备", "提示:");

             return result;
         }

         if (string.IsNullOrWhiteSpace(audioInput))
         {
             //MessageBox.Show("未选择录音设备", "提示:");

             return result;
         }

         if (string.IsNullOrWhiteSpace(videoSaveFormat))
         {
             //MessageBox.Show("未选择录音录像设备的视频录制格式", "提示:");

             return result;
         }


         #region C# 代码用于通过 Process 调用 FFmpeg 进行音视频采集(使用 dshow 设备)并叠加时间水印,
         //整体结构基本正确,但存在几个关键问题和优化建议,可能导致:
         //FFmpeg 启动失败
         //音视频设备无法识别
         //中文路径 / 特殊字符问题
         //进程无法正常结束或卡死
         //string ffmpegPath = "ffmpeg.exe";//ffmpeg路径
         ////string videoFilePath = $"{DateTime.Now.ToString("yyyyMMddHHmmss")}.mp4";//视频地址
         //string videoFilePath = "123.mp4";//视频地址
         //ProcessRecord = new Process();
         //ProcessStartInfo startInfo = new ProcessStartInfo();
         //startInfo.FileName = ffmpegPath;
         //string drawStr = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
         //startInfo.Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf  drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{drawStr}' -y {videoFilePath}";
         //startInfo.UseShellExecute = false;
         //startInfo.CreateNoWindow = true;
         //startInfo.RedirectStandardInput = true;
         //startInfo.RedirectStandardOutput = true;
         //startInfo.RedirectStandardError = true;
         //startInfo.RedirectStandardInput = true;
         //startInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
         //ProcessRecord.StartInfo = startInfo;
         //ProcessRecord.OutputDataReceived += Process_OutputDataReceived;
         //ProcessRecord.ErrorDataReceived += Process_ErrorDataReceived;
         ////process.StartInfo.Arguments = a;
         //ProcessRecord.Start();
         //ProcessRecord.BeginErrorReadLine();
         //ProcessRecord.BeginOutputReadLine(); 

         //lblffmepg.ForeColor = Color.Green;
         //lblffmepg.Text = "开始录音录像";
         #endregion

         //ffmpeg 指令执行格式:ffmpeg [输入选项] [滤镜选项] 输出文件路径
         string ffmpegPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe");
         if (!File.Exists(ffmpegPath))
         {
             logger.Error($"未找到 ffmpeg.exe--{ffmpegPath}");
             return false;
         }

         #region .NET Framework ok

         string drawStr = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
         // 转义文本中的单引号(FFmpeg drawtext 要求)--- 将 text 中的 ' 替换为 '\''(FFmpeg 要求)
         string safeText = drawStr.Replace("'", "'\\''");
         string videoFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"{DateTime.Now.ToString("yyyyMMddHHmmss")}.mp4");//视频地址
         // 输出路径加引号防空格 输出文件名(带引号防止空格或特殊字符问题) 更推荐:对整个文本值 值使用双引号 + 转义(FFmpeg 支持)
         string safeOutput = $"\"{videoFilePath ?? "123.mp4"}\"";
         recordvideoFilePath = videoFilePath;

         //string outputFileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"{DateTime.Now:yyyyMMddHHmmss}.mp4");//视频地址 DateTime.Now.ToString("yyyyMMddHHmmss") + ".mp4";
         //string safeOutputPath = $"\"{outputFileName}\""; // 手动加双引号,输出路径用 \"...\" 包裹
         //// 构建完整参数字符串(注意 drawtext 的 text 也要处理)text 中的 ' 要转义为 \'(FFmpeg 要求)
         //string text = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss").Replace("'", "\\'");
         ////水印时间---没有双引号 //整个 -vf 参数值用 \"...\" 包裹(因为含 : 和 =)
         ////string vf = $"drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{text}'";
         ////设置时间水印居中
         //string vf = $"drawtext=fontsize=56:fontcolor=red:text='{text}':x=(w-text_w)/2:y=(h-text_h)/2";
         ////w = 实际视频宽度(如 1280),h = 实际视频高度(如 720),text_w, text_h = 文字渲染后的像素尺寸, 无需知道具体分辨率,表达式会自动适配!
         var startInfo = new ProcessStartInfo
         {
             FileName = ffmpegPath,
             // 将 text 中的 ' 替换为 '\''(FFmpeg 要求)
             //Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{safeText}' -y {safeOutput}",
             //更推荐:对整个 text 值使用双引号 + 转义(FFmpeg 支持):string safeText = drawStr.Replace("\"", "\\\"");    ... text =\"{safeText}\" ...
             //Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:x=100:y=100:fontcolor=red:text=\""{safeText}\"" -y {safeOutput}",
             //设置时间水印居中
             //Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:fontcolor=red:text=\""{safeText}\"":x=(w-text_w)/2:y=(h-text_h)/2 -y {safeOutput}",
             //强制指定分辨率,设置时间水印居中 ,在 -i 参数前使用 -video_size强制指定分辨率,但是指定分辨率支持的值必须是设备实际支持的分辨率,否则会报错。
             //Arguments = $@"-rtbufsize 200M -f dshow -video_size 1280x720 -framerate 30 -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:fontcolor=red:text=\""{safeText}\"":x=(w-text_w)/2:y=(h-text_h)/2 -y {safeOutput}",

             UseShellExecute = false,
             CreateNoWindow = true,
             RedirectStandardInput = true,
             RedirectStandardOutput = true,
             RedirectStandardError = true,
             WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
         };
         string issetwatermark = GetSetting("issetwatermark");
         if (issetwatermark == "1")
         {
             // 3. 设置中文字体 (关键步骤,避免中文乱码)
             // 黑体(SimHei):simhei.ttf 宋体:simsun.ttc 微软雅黑(Microsoft YaHei):msyh.ttc / msyhbd.ttc 仿宋(FangSong):simfang.ttf 楷体(KaiTi):simkai.ttf
             string fontPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "Arial.ttf") ?? System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "arial.ttc");
             // 使用黑体->微软雅黑
             //string fontPath = @"C:\Windows\Fonts\simhei.ttf"; // 黑体--C:\Windows\Fonts\simhei.ttf 需要转换成-C\\:/Windows/Fonts/simhei.ttf 或者 C:/Windows/Fonts/simhei.ttf
             fontPath = fontPath.Replace('\\', '/');
             fontPath = fontPath.Replace("C:", "C\\:");
             //设置时间水印 设置时间水印居中string vf = $"drawtext=fontsize=56:fontcolor=red:text='{text}':x=(w-text_w)/2:y=(h-text_h)/2";
             //w = 实际视频宽度(如 1280),h = 实际视频高度(如 720),text_w, text_h = 文字渲染后的像素尺寸, 无需知道具体分辨率,表达式会自动适配!
             //startInfo.Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 \""-vf drawtext=fontfile={fontPath}:fontsize=56:fontcolor=red:text='{safeText}':x=(w-text_w)/2:y=(h-text_h)/2\"" -y {safeOutput}";
             //string text = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss").Replace("'", "\\'");
             //水印时间---没有双引号 //整个 -vf 参数值用 \"...\" 包裹(因为含 : 和 =)
             //string vf = $"drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{text}'";

             //设置 时间水印居中 字体 ---有双引号
             //string vf = $"drawtext=fontfile=\'{fontPath}\':fontsize=56:fontcolor=red:text=\\\"{safeText}\\\":x=(w-text_w)/2:y=(h-text_h)/2";
             //ok--- vf=drawtext=fontfile='C\:/Windows/Fonts/Arial.ttf':fontsize=56:fontcolor=red:text=\"2026-03-05-09-52-51\":x=(w-text_w)/2:y=(h-text_h)/2
             
             //设置 时间水印居中 字体 ---没有双引号
             string vf = $"drawtext=fontfile=\'{fontPath}\':fontsize=56:fontcolor=red:text=\\\'{safeText}\\\':x=(w-text_w)/2:y=(h-text_h)/2";
             
             startInfo.Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf {vf} -y {safeOutput}";
             //加字体下面这么写可以
             //cmd  ffmpeg -rtbufsize 200M -f dshow -i video="@device_pnp_\\?\usb#vid_1c76&pid_0676&mi_00#8&727dd20&4&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" -f dshow -i audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{8DC48485-A62F-48B8-AAB1-033DCF66B586}" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontfile='C\:/Windows/Fonts/Arial.ttf':fontsize=56:fontcolor=red:text=\"2026-03-05-09-52-51\":x=(w-text_w)/2:y=(h-text_h)/2 -y "C:\Users\Administrator\Desktop\20260305095251.mp4"
             
             //不加字体可以
             //startInfo.Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:fontcolor=red:text=\""{safeText}\"":x=(w-text_w)/2:y=(h-text_h)/2 -y {safeOutput}";

         }
         else
         {
             //不设置时间水印
             startInfo.Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -y {safeOutput}";
         }


         logger.Info($"startInfo.Arguments:{startInfo.Arguments}");
         ProcessRecord = new Process { StartInfo = startInfo };
         ProcessRecord.OutputDataReceived += Process_OutputDataReceived;
         ProcessRecord.ErrorDataReceived += Process_ErrorDataReceived;
         //process.StartInfo.Arguments = a;
         ProcessRecord.Start();
         ProcessRecord.BeginErrorReadLine();
         ProcessRecord.BeginOutputReadLine();

         //启动后不要立即 dispose!等 Exited 事件触发
         //ProcessRecord.EnableRaisingEvents = true;
         //ProcessRecord.Exited += (s, e) =>
         //{
         //    Console.WriteLine($"FFmpeg 退出,退出码: {ProcessRecord.ExitCode}");
         //    ProcessRecord.Dispose();
         //}; 
         #endregion

         #region 使用 ProcessStartInfo.ArgumentList(.NET Core 2.1+ / .NET 5+ 推荐)C# 中安全传递带引号的路径
         //var startInfo = new ProcessStartInfo
         //{
         //    FileName = "ffmpeg.exe",
         //    UseShellExecute = false,
         //    CreateNoWindow = true
         //};

         //// 直接添加参数,无需手动加引号!
         //startInfo.ArgumentList.Add("-rtbufsize");
         //startInfo.ArgumentList.Add("200M");

         //startInfo.ArgumentList.Add("-f");
         //startInfo.ArgumentList.Add("dshow");
         //startInfo.ArgumentList.Add("-i");
         //startInfo.ArgumentList.Add($"video={videoInput}"); // videoInput 已包含完整设备字符串

         //startInfo.ArgumentList.Add("-f");
         //startInfo.ArgumentList.Add("dshow");
         //startInfo.ArgumentList.Add("-i");
         //startInfo.ArgumentList.Add($"audio={audioInput}");

         //startInfo.ArgumentList.Add("-pix_fmt");
         //startInfo.ArgumentList.Add("yuv420p");
         //startInfo.ArgumentList.Add("-tune");
         //startInfo.ArgumentList.Add("zerolatency");
         //startInfo.ArgumentList.Add("-ac");
         //startInfo.ArgumentList.Add("1");
         //startInfo.ArgumentList.Add("-ar");
         //startInfo.ArgumentList.Add("8000");
         //startInfo.ArgumentList.Add("-ab");
         //startInfo.ArgumentList.Add("44100");

         //startInfo.ArgumentList.Add("-vf");
         //startInfo.ArgumentList.Add($"drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{DateTime.Now:yyyy-MM-dd-HH-mm-ss}'");

         //startInfo.ArgumentList.Add("-y");
         //startInfo.ArgumentList.Add(videoFilePath); // ← 直接传路径,自动处理引号!

         //Process.Start(startInfo); 
         #endregion

         #region 如果必须用 .Arguments(.NET Framework 或旧版)---ok

         //              var startInfo = new ProcessStartInfo
         //              {
         //                  FileName = ffmpegPath,
         //                  // 将 text 中的 ' 替换为 '\''(FFmpeg 要求)
         //                  //Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{safeText}' -y {safeOutput}",
         //                  //更推荐:对整个 text 值使用双引号 + 转义(FFmpeg 支持):string safeText = drawStr.Replace("\"", "\\\"");    ... text =\"{safeText}\" ...
         //                  //Arguments = $@"-rtbufsize 200M -f dshow -i video=""{videoInput}"" -f dshow -i audio=""{audioInput}"" -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 -vf drawtext=fontsize=56:x=100:y=100:fontcolor=red:text=\""{safeText}\"" -y {safeOutput}",

         //                  UseShellExecute = false,
         //                  CreateNoWindow = true,
         //                  RedirectStandardInput = true,
         //                  RedirectStandardOutput = true,
         //                  RedirectStandardError = true,
         //                  WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
         //              };
         //              string outputFileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"{DateTime.Now:yyyyMMddHHmmss}.mp4");//视频地址 DateTime.Now.ToString("yyyyMMddHHmmss") + ".mp4";
         //              string safeOutputPath = $"\"{outputFileName}\""; // 手动加双引号,输出路径用 \"...\" 包裹

         //              // 构建完整参数字符串(注意 drawtext 的 text 也要处理)text 中的 ' 要转义为 \'(FFmpeg 要求)
         //              string text = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss").Replace("'", "\\'");
         //              //水印时间---没有双引号 //整个 -vf 参数值用 \"...\" 包裹(因为含 : 和 =)
         //              //string vf = $"drawtext=fontsize=56:x=100:y=100:fontcolor=red:text='{text}'";
         //              //设置时间水印居中
         //              string vf = $"drawtext=fontsize=56:fontcolor=red:text='{text}':x=(w-text_w)/2:y=(h-text_h)/2";
         //              //w = 实际视频宽度(如 1280),h = 实际视频高度(如 720),text_w, text_h = 文字渲染后的像素尺寸, 无需知道具体分辨率,表达式会自动适配!

         //              #region MyRegion
         //              //FFmpeg 命令 水印 以及水印位置
         //              //水印时间---有双引号
         //              //string vf = $"drawtext=fontsize=56:x=100:y=100:fontcolor=red:text=\"{text}\"";//整个 -vf 参数值用 \"...\" 包裹(因为含 : 和 =)
         //              //要在 FFmpeg 的 drawtext 滤镜中将文字居中显示在视频正中心,不能使用固定的 x = 100:y = 100,而应使用 动态表达式,基于视频的宽度(w)和高度(h)以及文本自身的尺寸(text_w, text_h)来计算位置。
         //              //居中文本(水平 + 垂直居中)drawtext=fontsize=56:fontcolor=red:text='你的文字':x=(w-text_w)/2:y=(h-text_h)/2
         //              //参数说明:
         //              //| 表达式 | 含义 |
         //              //| --------| ------|
         //              //| `w` | 视频宽度(pixels) |
         //              //| `h` | 视频高度(pixels) |
         //              //| `text_w` | 当前文本渲染后的宽度 |
         //              //| `text_h` | 当前文本渲染后的高度 |
         //              //| `(w - text_w) / 2` | 水平居中:左边距 = (总宽 - 文字宽) ÷ 2 |
         //              //| `(h - text_h) / 2` | 垂直居中:上边距 = (总高 - 文字高) ÷ 2 |

         //              //其他常见居中需求
         //              //| 需求 | 表达式 |
         //              //| ------| --------|
         //              //| 水平居中,顶部对齐 | `x = (w - text_w) / 2:y = 0` |
         //              //| 水平居中,底部对齐 | `x = (w - text_w) / 2:y = h - text_h` |
         //              //| 垂直居中,左对齐 | `x = 0:y = (h - text_h) / 2` |
         //              //| 垂直居中,右对齐 | `x = w - text_w:y = (h - text_h) / 2` |
         //              //| 带边距的居中(如上下留 50px) | `x = (w - text_w) / 2:y = (h - text_h) / 2 - 50` |

         //              //⚠️注意事项
         //              //必须指定字体(可选但推荐)
         //              //默认字体可能不支持中文或样式不一致,建议指定:
         //              //bash
         //              //:fontfile =/ path / to / arial.ttf
         //              //Windows 示例:
         //              //bash
         //              //:fontfile = C\\:/ Windows / Fonts / arial.ttf
         //              //中文显示为方框?
         //              //确保字体支持中文(如 msyh.ttc 微软雅黑)
         //              //路径中的 \ 要转义为 \\ 或 /
         //              //bash
         //              //: fontfile = C\\:/ Windows / Fonts / msyh.ttc
         //              //实时流(dshow)中 text_w/ text_h 可能延迟生效
         //              //在录制开始的前几帧,文本尺寸可能未计算完成,导致短暂偏移(通常 1~2 帧后正常)。
         //              //✅ 最终命令示例(完整)
         //              //bash
         //              //ffmpeg -rtbufsize 200M -f dshow -i video="..." -f dshow -i audio="..." \
         //              //  -pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 \
         //              //  -vf "drawtext=fontsize=56:fontcolor=red:text='2026-03-04 14:30:00':x=(w-text_w)/2:y=(h-text_h)/2" \
         //              //  -y output.mp4
         //              //这样文字就会完美居中在视频画面中央!

         //              //FFmpeg 命令录音录像时分辨率
         //              //FFmpeg 命令录音录像时候是没有固定“默认”分辨率!它完全取决于你的 DirectShow 视频采集设备(摄像头)的当前输出格式。
         //              //🔍 详细解释
         //              //- f dshow 表示使用 Windows 的 DirectShow 接口采集音视频。
         //              //FFmpeg 不会自动设置分辨率,而是直接使用设备当前激活的媒体类型(media type)中的分辨率。
         //              //常见情况:
         //              //表格
         //              //设备类型    可能的默认分辨率
         //              //笔记本内置摄像头    640x480(VGA)、1280x720(HD)
         //              //USB 摄像头 640x480、1280x720、1920x1080(取决于驱动和设备能力)
         //              //虚拟摄像头(OBS - VirtualCam 等)    用户自定义(如 1920x1080)
         //              //⚠️ 同一个摄像头,在不同软件中可能输出不同分辨率(因为每个软件会请求不同的格式)。

         //              #endregion
         //              startInfo.Arguments = "-rtbufsize 200M " + $"-f dshow -i \"video={videoInput}\" " + $"-f dshow -i \"audio={audioInput}\" " + "-pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 " + $"-vf \"{vf}\" " + "-y " + safeOutputPath;
         //              //强制指定分辨率,设置时间水印居中 ,在 -i 参数前使用 -video_size强制指定分辨率
         //              //startInfo.Arguments = "-rtbufsize 200M " + $"-f dshow -video_size 1280x720 -framerate 30 -i \"video={videoInput}\" " + $"-f dshow -i \"audio={audioInput}\" " + "-pix_fmt yuv420p -tune zerolatency -ac 1 -ar 8000 -ab 44100 " + $"-vf \"{vf}\" " + "-y " + safeOutputPath;
         ////              var sss =
         ////                  ffmpeg - rtbufsize 200M \
         ////-f dshow - video_size 1280x720 - framerate 30 - i video = "..." \
         ////-f dshow - i audio = "..." \
         ////-pix_fmt yuv420p - tune zerolatency \
         ////-ac 1 - ar 8000 - ab 44100 \
         ////-vf "drawtext=fontsize=56:fontcolor=red:text='2026-03-04 14:30:00':x=(w-text_w)/2:y=(h-text_h)/2" \
         ////-y output.mp4

         //              //FFmepeg使用注意
         //              //1、text 中的 ' 要转义为 \'(FFmpeg 要求)
         //              //2、输出路径用 \"...\" 包裹
         //              //整个 -vf 参数值用 \"...\" 包裹(因为含 : 和 =)
         //              ProcessRecord = new Process { StartInfo = startInfo };
         //              ProcessRecord.OutputDataReceived += Process_OutputDataReceived;
         //              ProcessRecord.ErrorDataReceived += Process_ErrorDataReceived;
         //              //process.StartInfo.Arguments = a;
         //              ProcessRecord.Start();
         //              ProcessRecord.BeginErrorReadLine();
         //              ProcessRecord.BeginOutputReadLine();
         #endregion

         Untils.PlaySpeech("录音录像已开启");
         result = true;
     }
     catch (Exception ex)
     {
         logger.Error($"StartRecord,失败,原因:{ex.Message}");
         result = false;
     }
     return result;
 }

 public static void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
 {
     logger.Info($"StartRecord,录音录像 OutputDataReceived:{e.Data}");
     //Log(e.Data, MessageType.info);
 }

 public static void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
 {
     logger.Error($"StartRecord,录音录像 ErrorDataReceived,原因:{e.Data}");
 }