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

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 至尊龙骑

FFmpeg 命令 水印以及水印位置 FFmpeg 命令录音录像时分辨率 FFmpeg 查看分辨率以及指定分辨率 C# 使用FFmpeg 命令 水印以及水印位置 录音录像时设置分辨率-分辨率一般是宽高比是 4:3 和16:9 少数是 5:4 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}");
 }