






















/// <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}"); }
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。