using MvCamCtrl.NET; using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; using Accord.Video; using Accord.Video.FFMPEG; namespace BasicDemo { public partial class AutoCameraForm : Form { [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); // 支持最多3个相机 private const int MAX_CAMERAS = 3; // 相机相关对象数组 private MyCamera[] cameras = new MyCamera[MAX_CAMERAS]; private bool[] grabbingFlags = new bool[MAX_CAMERAS]; private Thread[] grabThreads = new Thread[MAX_CAMERAS]; private MyCamera.MV_FRAME_OUT_INFO_EX[] frameInfos = new MyCamera.MV_FRAME_OUT_INFO_EX[MAX_CAMERAS]; // 图像显示相关 private Bitmap[] displayBitmaps = new Bitmap[MAX_CAMERAS]; private IntPtr[] displayBufs = new IntPtr[MAX_CAMERAS]; private UInt32[] displayBufSizes = new UInt32[MAX_CAMERAS]; // 设备列表 private MyCamera.MV_CC_DEVICE_INFO_LIST deviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST(); // UI控件 private PictureBox[] pictureBoxes = new PictureBox[MAX_CAMERAS]; private Label[] statusLabels = new Label[MAX_CAMERAS]; // 锁对象,用于线程同步 private static readonly object[] locks = new object[MAX_CAMERAS] { new object(), new object(), new object() }; // 状态回调 public delegate bool GetRunStatusDelegate(); private GetRunStatusDelegate _getRunStatusCallback; private Thread _statusCheckThread; // 添加:定义返回事件委托 public delegate void ReturnToPreviousFormDelegate(); public event ReturnToPreviousFormDelegate OnReturnRequested; // 控制变量 private bool _shouldCapture = false; private bool _camerasInitialized = false; // 视频保存配置(新增) private string _videoSavePath = @"D:\CameraRecordings\"; // 修改为你的保存路径 private double _frameRate = 30.0; // 默认帧率 // 视频录制相关(新增) private VideoFileWriter[] videoWriters = new VideoFileWriter[MAX_CAMERAS]; private string[] videoFilePaths = new string[MAX_CAMERAS]; private bool[] isRecording = new bool[MAX_CAMERAS]; // 构造函数 public AutoCameraForm(GetRunStatusDelegate getRunStatusCallback, ReturnToPreviousFormDelegate returnCallback = null, string videoSavePath = null) { _getRunStatusCallback = getRunStatusCallback; // 如果提供了返回回调,就订阅 if (returnCallback != null) { OnReturnRequested += returnCallback; } // 设置视频保存路径 if (!string.IsNullOrEmpty(videoSavePath)) { _videoSavePath = videoSavePath; } // 确保保存目录存在 Directory.CreateDirectory(_videoSavePath); InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; // 设置窗体大小 this.Size = new Size(1900, 1000); this.Text = "三相机自动监控系统"; this.StartPosition = FormStartPosition.CenterScreen; this.FormClosing += AutoCameraForm_FormClosing; // 初始化UI InitializeUI(); // 窗体加载时初始化相机 this.Load += (s, e) => InitializeCameras(); } /// /// 初始化UI界面 /// private void InitializeUI() { this.BackColor = Color.FromArgb(240, 240, 240); //Button btnDiagnose = new Button //{ // Text = "诊断", // Size = new Size(80, 30), // Location = new Point(1800, 30), // BackColor = Color.Yellow, // ForeColor = Color.Black //}; //btnDiagnose.Click += (s, e) => DiagnoseCameras(); //this.Controls.Add(btnDiagnose); // 创建标题 Label titleLabel = new Label { Text = "三相机自动监控系统", Font = new Font("Microsoft YaHei", 20, FontStyle.Bold), ForeColor = Color.FromArgb(0, 112, 192), Size = new Size(500, 40), Location = new Point(700, 20), TextAlign = ContentAlignment.MiddleCenter }; this.Controls.Add(titleLabel); // 创建状态栏 Panel statusBar = new Panel { BackColor = Color.FromArgb(0, 112, 192), Size = new Size(1900, 30), Location = new Point(0, 970) }; this.Controls.Add(statusBar); Label statusText = new Label { Text = "系统状态:正在初始化...", Font = new Font("Microsoft YaHei", 10), ForeColor = Color.White, Size = new Size(300, 25), Location = new Point(10, 2), TextAlign = ContentAlignment.MiddleLeft }; statusBar.Controls.Add(statusText); // 创建三个相机显示区域 int pictureWidth = 620; int pictureHeight = 850; int margin = 20; for (int i = 0; i < MAX_CAMERAS; i++) { // 创建容器面板 Panel cameraPanel = new Panel { BackColor = Color.White, BorderStyle = BorderStyle.FixedSingle, Size = new Size(pictureWidth, 900), Location = new Point(margin + i * (pictureWidth + margin), 80) }; this.Controls.Add(cameraPanel); // 相机标题 Label cameraTitle = new Label { Text = $"相机 {i + 1}", Font = new Font("Microsoft YaHei", 14, FontStyle.Bold), ForeColor = Color.FromArgb(0, 112, 192), Size = new Size(200, 30), Location = new Point(10, 10), TextAlign = ContentAlignment.MiddleLeft }; cameraPanel.Controls.Add(cameraTitle); // 状态标签 statusLabels[i] = new Label { Text = "状态:未连接", Font = new Font("Microsoft YaHei", 10), ForeColor = Color.Red, Size = new Size(200, 25), Location = new Point(10, 45), TextAlign = ContentAlignment.MiddleLeft }; cameraPanel.Controls.Add(statusLabels[i]); // 图像显示区域 pictureBoxes[i] = new PictureBox { Size = new Size(pictureWidth - 40, pictureHeight), Location = new Point(20, 80), BackColor = Color.Black, SizeMode = PictureBoxSizeMode.StretchImage, BorderStyle = BorderStyle.FixedSingle }; cameraPanel.Controls.Add(pictureBoxes[i]); } AddBackButton(); } private void AddBackButton() { Button btnBack = new Button { Text = "返回", Size = new Size(100, 40), Location = new Point(1750, 30), Font = new Font("Microsoft YaHei", 12), BackColor = Color.FromArgb(0, 112, 192), ForeColor = Color.White, FlatStyle = FlatStyle.Flat }; btnBack.Click += (s, e) => { // 触发返回事件 OnReturnRequested?.Invoke(); // 关闭当前窗体 this.Close(); }; this.Controls.Add(btnBack); } /// /// 初始化相机系统 /// private void InitializeCameras() { try { UpdateSystemStatus("正在初始化SDK..."); // 等待窗体完全加载 Application.DoEvents(); Thread.Sleep(500); // 1. 初始化SDK int ret = MyCamera.MV_CC_Initialize_NET(); if (ret != MyCamera.MV_OK) { MessageBox.Show($"SDK初始化失败,错误代码: {ret}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } UpdateSystemStatus("正在枚举设备..."); // 2. 枚举设备 ret = MyCamera.MV_CC_EnumDevices_NET( MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref deviceList); if (ret != MyCamera.MV_OK) { MessageBox.Show($"设备枚举失败,错误代码: {ret}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // 3. 检查设备数量 if (deviceList.nDeviceNum < MAX_CAMERAS) { MessageBox.Show($"检测到 {deviceList.nDeviceNum} 个设备,需要至少 {MAX_CAMERAS} 个相机", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } UpdateSystemStatus($"检测到 {deviceList.nDeviceNum} 个设备,准备初始化前 {MAX_CAMERAS} 个相机..."); // 4. 逐个初始化相机 - 增加间隔时间 for (int i = 0; i < MAX_CAMERAS; i++) { if (InitializeSingleCamera(i)) { UpdateCameraStatus(i, "设备已就绪", Color.Blue); } Thread.Sleep(1000); // 增加间隔,避免同时打开冲突 } _camerasInitialized = true; UpdateSystemStatus("所有相机已初始化就绪,等待测试开始..."); // 5. 启动状态检查线程 - 延迟启动 Thread.Sleep(1000); StartStatusCheckThread(); } catch (Exception ex) { MessageBox.Show($"初始化系统时发生异常: {ex.Message}", "异常", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// 启动状态检查线程 /// private void StartStatusCheckThread() { _statusCheckThread = new Thread(CheckRunStatusLoop) { IsBackground = true }; _statusCheckThread.Start(); } /// /// 状态检查循环 - 添加异常处理 /// private void CheckRunStatusLoop() { bool lastStatus = false; while (!this.IsDisposed && _camerasInitialized) { try { bool currentStatus = _getRunStatusCallback?.Invoke() ?? false; if (currentStatus != lastStatus) { lastStatus = currentStatus; // 使用TryInvoke避免窗体已关闭时的异常 if (!this.IsDisposed && this.IsHandleCreated) { this.BeginInvoke(new Action(() => { try { if (currentStatus) { StartAllCamerasCapture(); } else { StopAllCamerasCapture(); } } catch (Exception ex) { Console.WriteLine($"状态切换异常: {ex.Message}"); } })); } } Thread.Sleep(1000); // 延长检查间隔,减少CPU使用 } catch { // 忽略异常 } } } /// /// 开始所有相机采集 /// private void StartAllCamerasCapture() { if (!_camerasInitialized) return; UpdateSystemStatus("测试开始,正在启动相机采集..."); for (int i = 0; i < MAX_CAMERAS; i++) { if (cameras[i] != null && !grabbingFlags[i]) { StartSingleCameraCapture(i); } } UpdateSystemStatus("所有相机已开始采集"); } /// /// 停止所有相机采集 /// private void StopAllCamerasCapture() { if (!_camerasInitialized) return; UpdateSystemStatus("测试结束,正在停止相机采集..."); for (int i = 0; i < MAX_CAMERAS; i++) { if (cameras[i] != null && grabbingFlags[i]) { StopSingleCameraCapture(i); } } UpdateSystemStatus("所有相机已停止采集"); } /// /// 初始化单个相机 /// private bool InitializeSingleCamera(int cameraIndex) { try { UpdateCameraStatus(cameraIndex, "正在打开...", Color.Orange); // 1. 创建设备信息 MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure( deviceList.pDeviceInfo[cameraIndex], typeof(MyCamera.MV_CC_DEVICE_INFO)); // 2. 创建相机对象 cameras[cameraIndex] = new MyCamera(); // 3. 创建设备 int ret = cameras[cameraIndex].MV_CC_CreateDevice_NET(ref device); if (ret != MyCamera.MV_OK) { UpdateCameraStatus(cameraIndex, $"创建设备失败: {ret}", Color.Red); return false; } // 4. 打开设备 ret = cameras[cameraIndex].MV_CC_OpenDevice_NET(); if (ret != MyCamera.MV_OK) { cameras[cameraIndex].MV_CC_DestroyDevice_NET(); UpdateCameraStatus(cameraIndex, $"打开设备失败: {ret}", Color.Red); return false; } // 5. 如果是千兆网相机,设置最优包大小 if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { int packetSize = cameras[cameraIndex].MV_CC_GetOptimalPacketSize_NET(); if (packetSize > 0) { cameras[cameraIndex].MV_CC_SetIntValueEx_NET("GevSCPSPacketSize", packetSize); } } // 6. 设置采集模式 cameras[cameraIndex].MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS); cameras[cameraIndex].MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF); return true; } catch (Exception ex) { UpdateCameraStatus(cameraIndex, $"异常: {ex.Message}", Color.Red); return false; } } private void StartSingleCameraCapture(int cameraIndex) { try { if (cameras[cameraIndex] == null || grabbingFlags[cameraIndex]) return; // 检查相机是否已正确打开 if (!IsCameraValid(cameraIndex)) { UpdateCameraStatus(cameraIndex, "相机未正确初始化", Color.Red); return; } // 1. 准备采集资源 if (!PrepareForGrabbing(cameraIndex)) { UpdateCameraStatus(cameraIndex, "采集准备失败", Color.Red); return; } // 2. 初始化视频录制(新增) InitializeVideoRecording(cameraIndex); // 2. 设置采集标志 grabbingFlags[cameraIndex] = true; // 3. 创建采集线程 int index = cameraIndex; grabThreads[cameraIndex] = new Thread(() => GrabThreadProc(index)); grabThreads[cameraIndex].Name = $"CameraGrabThread_{cameraIndex}"; grabThreads[cameraIndex].IsBackground = true; // 启动线程 grabThreads[cameraIndex].Start(); // 等待线程启动 Thread.Sleep(100); // 4. 开始采集 int ret = cameras[cameraIndex].MV_CC_StartGrabbing_NET(); if (ret != MyCamera.MV_OK) { UpdateCameraStatus(cameraIndex, $"开始采集失败: {ret}", Color.Red); grabbingFlags[cameraIndex] = false; return; } // 6. 开始录制视频(新增) StartVideoRecording(cameraIndex); UpdateCameraStatus(cameraIndex, "正在采集...", Color.Green); } catch (Exception ex) { UpdateCameraStatus(cameraIndex, $"开始采集异常: {ex.Message}", Color.Red); Console.WriteLine($"启动相机{cameraIndex + 1}异常: {ex.ToString()}"); } } /// /// 初始化视频录制(新增) /// private void InitializeVideoRecording(int cameraIndex) { try { // 生成唯一的视频文件名 string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string fileName = $"Camera{cameraIndex + 1}_{timestamp}.mp4"; videoFilePaths[cameraIndex] = Path.Combine(_videoSavePath, fileName); // 创建 VideoFileWriter 实例 videoWriters[cameraIndex] = new VideoFileWriter(); // 从相机获取帧率(如果支持) try { MyCamera.MVCC_FLOATVALUE frameRate = new MyCamera.MVCC_FLOATVALUE(); int ret = cameras[cameraIndex].MV_CC_GetFloatValue_NET("AcquisitionFrameRate", ref frameRate); if (ret == MyCamera.MV_OK && frameRate.fCurValue > 0) { _frameRate = frameRate.fCurValue; } } catch { // 如果获取失败,使用默认帧率 _frameRate = 30.0; } Console.WriteLine($"相机 {cameraIndex + 1} 准备录制到: {videoFilePaths[cameraIndex]}, 帧率: {_frameRate}FPS"); } catch (Exception ex) { Console.WriteLine($"初始化视频录制失败: {ex.Message}"); videoWriters[cameraIndex] = null; } } /// /// 开始录制视频(新增)- 简化版本 /// private void StartVideoRecording(int cameraIndex) { if (videoWriters[cameraIndex] == null || displayBitmaps[cameraIndex] == null) return; try { int width = displayBitmaps[cameraIndex].Width; int height = displayBitmaps[cameraIndex].Height; // 将帧率转换为整数(四舍五入) int fps = (int)Math.Round(_frameRate); // 确保帧率至少为1 if (fps < 1) fps = 30; // 创建 Rational 帧率 Accord.Math.Rational frameRateRational = new Accord.Math.Rational(fps, 1); // 打开视频文件 videoWriters[cameraIndex].Open(videoFilePaths[cameraIndex], width, height, frameRateRational, VideoCodec.H264); isRecording[cameraIndex] = true; UpdateCameraStatus(cameraIndex, $"录制中: {Path.GetFileName(videoFilePaths[cameraIndex])}", Color.Green); } catch (Exception ex) { Console.WriteLine($"开始录制视频失败: {ex.Message}"); isRecording[cameraIndex] = false; } } /// /// 检查相机是否有效 /// private bool IsCameraValid(int cameraIndex) { try { return cameras[cameraIndex] != null; } catch { return false; } } /// /// 停止单个相机采集 /// private void StopSingleCameraCapture(int cameraIndex) { try { if (cameras[cameraIndex] == null || !grabbingFlags[cameraIndex]) return; // 1. 停止采集标志 grabbingFlags[cameraIndex] = false; // 2. 等待采集线程结束 if (grabThreads[cameraIndex] != null && grabThreads[cameraIndex].IsAlive) { grabThreads[cameraIndex].Join(1000); } // 3. 停止相机采集 cameras[cameraIndex].MV_CC_StopGrabbing_NET(); // 4. 停止视频录制(新增) StopVideoRecording(cameraIndex); // 4. 清理资源 CleanupCameraResources(cameraIndex); UpdateCameraStatus(cameraIndex, "已停止采集", Color.Blue); } catch (Exception ex) { UpdateCameraStatus(cameraIndex, $"停止采集异常: {ex.Message}", Color.Red); } } /// /// 停止视频录制(新增) /// private void StopVideoRecording(int cameraIndex) { try { if (videoWriters[cameraIndex] != null && isRecording[cameraIndex]) { videoWriters[cameraIndex].Close(); isRecording[cameraIndex] = false; Console.WriteLine($"相机 {cameraIndex + 1} 视频已保存到: {videoFilePaths[cameraIndex]}"); // 可选:显示保存路径 UpdateCameraStatus(cameraIndex, $"视频已保存", Color.Blue); } } catch (Exception ex) { Console.WriteLine($"停止视频录制异常: {ex.Message}"); } finally { if (videoWriters[cameraIndex] != null) { videoWriters[cameraIndex].Dispose(); videoWriters[cameraIndex] = null; } } } private bool PrepareForGrabbing(int cameraIndex) { try { // 获取图像参数 MyCamera.MVCC_INTVALUE_EX width = new MyCamera.MVCC_INTVALUE_EX(); int ret = cameras[cameraIndex].MV_CC_GetIntValueEx_NET("Width", ref width); if (ret != MyCamera.MV_OK) return false; MyCamera.MVCC_INTVALUE_EX height = new MyCamera.MVCC_INTVALUE_EX(); ret = cameras[cameraIndex].MV_CC_GetIntValueEx_NET("Height", ref height); if (ret != MyCamera.MV_OK) return false; MyCamera.MVCC_ENUMVALUE pixelFormat = new MyCamera.MVCC_ENUMVALUE(); ret = cameras[cameraIndex].MV_CC_GetEnumValue_NET("PixelFormat", ref pixelFormat); if (ret != MyCamera.MV_OK) return false; // 创建Bitmap PixelFormat bitmapFormat = IsMonoPixelFormat(pixelFormat.nCurValue) ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb; // 确保清理旧的Bitmap if (displayBitmaps[cameraIndex] != null) { displayBitmaps[cameraIndex].Dispose(); displayBitmaps[cameraIndex] = null; } // 创建新的Bitmap displayBitmaps[cameraIndex] = new Bitmap((int)width.nCurValue, (int)height.nCurValue, bitmapFormat); // 如果是8位灰度图,设置调色板 if (bitmapFormat == PixelFormat.Format8bppIndexed) { ColorPalette palette = displayBitmaps[cameraIndex].Palette; for (int i = 0; i < palette.Entries.Length; i++) { palette.Entries[i] = Color.FromArgb(i, i, i); } displayBitmaps[cameraIndex].Palette = palette; } // 分配显示缓冲区 uint bufferSize = bitmapFormat == PixelFormat.Format8bppIndexed ? (uint)(width.nCurValue * height.nCurValue) : (uint)(width.nCurValue * height.nCurValue * 3); // 确保清理旧的缓冲区 if (displayBufs[cameraIndex] != IntPtr.Zero) { Marshal.FreeHGlobal(displayBufs[cameraIndex]); displayBufs[cameraIndex] = IntPtr.Zero; } displayBufs[cameraIndex] = Marshal.AllocHGlobal((int)bufferSize); displayBufSizes[cameraIndex] = bufferSize; frameInfos[cameraIndex] = new MyCamera.MV_FRAME_OUT_INFO_EX(); return true; } catch (Exception ex) { Console.WriteLine($"准备相机{cameraIndex + 1}失败: {ex.Message}"); return false; } } /// /// 判断是否为单色像素格式 /// private bool IsMonoPixelFormat(uint pixelFormat) { switch (pixelFormat) { case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8: case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10: case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12: case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed: case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed: return true; default: return false; } } ///// ///// 采集线程处理函数 - 简化版本 ///// //private void GrabThreadProc(int cameraIndex) //{ // MyCamera camera = cameras[cameraIndex]; // MyCamera.MV_FRAME_OUT frameOut = new MyCamera.MV_FRAME_OUT(); // MyCamera.MV_PIXEL_CONVERT_PARAM convertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM(); // MyCamera.MV_DISPLAY_FRAME_INFO displayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO(); // // 帧率控制变量(新增) // int framesWritten = 0; // DateTime lastWriteTime = DateTime.Now; // while (grabbingFlags[cameraIndex]) // { // try // { // // 获取图像缓冲区 // int ret = camera.MV_CC_GetImageBuffer_NET(ref frameOut, 1000); // if (ret == MyCamera.MV_OK) // { // lock (locks[cameraIndex]) // { // // 更新帧信息 // frameInfos[cameraIndex] = frameOut.stFrameInfo; // // 像素格式转换 // convertParam.nWidth = frameOut.stFrameInfo.nWidth; // convertParam.nHeight = frameOut.stFrameInfo.nHeight; // convertParam.enSrcPixelType = frameOut.stFrameInfo.enPixelType; // convertParam.pSrcData = frameOut.pBufAddr; // convertParam.nSrcDataLen = frameOut.stFrameInfo.nFrameLen; // convertParam.pDstBuffer = displayBufs[cameraIndex]; // convertParam.nDstBufferSize = displayBufSizes[cameraIndex]; // if (displayBitmaps[cameraIndex].PixelFormat == PixelFormat.Format8bppIndexed) // { // convertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8; // } // else // { // convertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed; // } // camera.MV_CC_ConvertPixelType_NET(ref convertParam); // // 简单方案:直接更新现有的Bitmap,不创建新实例 // BitmapData bitmapData = displayBitmaps[cameraIndex].LockBits( // new Rectangle(0, 0, (int)frameOut.stFrameInfo.nWidth, (int)frameOut.stFrameInfo.nHeight), // ImageLockMode.ReadWrite, displayBitmaps[cameraIndex].PixelFormat); // CopyMemory(bitmapData.Scan0, convertParam.pDstBuffer, // (uint)(bitmapData.Stride * displayBitmaps[cameraIndex].Height)); // displayBitmaps[cameraIndex].UnlockBits(bitmapData); // } // // 使用SDK显示 - 这是最稳定的方式 // displayInfo.hWnd = pictureBoxes[cameraIndex].Handle; // displayInfo.pData = frameOut.pBufAddr; // displayInfo.nDataLen = frameOut.stFrameInfo.nFrameLen; // displayInfo.nWidth = frameOut.stFrameInfo.nWidth; // displayInfo.nHeight = frameOut.stFrameInfo.nHeight; // displayInfo.enPixelType = frameOut.stFrameInfo.enPixelType; // // 在UI线程中调用显示 // if (this.InvokeRequired) // { // this.Invoke(new Action(() => // { // camera.MV_CC_DisplayOneFrame_NET(ref displayInfo); // })); // } // else // { // camera.MV_CC_DisplayOneFrame_NET(ref displayInfo); // } // // 释放图像缓冲区 // camera.MV_CC_FreeImageBuffer_NET(ref frameOut); // } // } // catch (Exception ex) // { // Console.WriteLine($"相机{cameraIndex + 1}采集异常: {ex.Message}"); // // 继续循环,不退出 // } // } //} /// /// 采集线程处理函数 - 添加视频录制(修改) /// private void GrabThreadProc(int cameraIndex) { MyCamera camera = cameras[cameraIndex]; MyCamera.MV_FRAME_OUT frameOut = new MyCamera.MV_FRAME_OUT(); MyCamera.MV_PIXEL_CONVERT_PARAM convertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM(); MyCamera.MV_DISPLAY_FRAME_INFO displayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO(); // 帧率控制变量(新增) int framesWritten = 0; DateTime lastWriteTime = DateTime.Now; while (grabbingFlags[cameraIndex]) { try { // 获取图像缓冲区 int ret = camera.MV_CC_GetImageBuffer_NET(ref frameOut, 1000); if (ret == MyCamera.MV_OK) { lock (locks[cameraIndex]) { // 更新帧信息 frameInfos[cameraIndex] = frameOut.stFrameInfo; // 像素格式转换 convertParam.nWidth = frameOut.stFrameInfo.nWidth; convertParam.nHeight = frameOut.stFrameInfo.nHeight; convertParam.enSrcPixelType = frameOut.stFrameInfo.enPixelType; convertParam.pSrcData = frameOut.pBufAddr; convertParam.nSrcDataLen = frameOut.stFrameInfo.nFrameLen; convertParam.pDstBuffer = displayBufs[cameraIndex]; convertParam.nDstBufferSize = displayBufSizes[cameraIndex]; if (displayBitmaps[cameraIndex].PixelFormat == PixelFormat.Format8bppIndexed) { convertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8; } else { convertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed; } camera.MV_CC_ConvertPixelType_NET(ref convertParam); // 更新Bitmap BitmapData bitmapData = displayBitmaps[cameraIndex].LockBits( new Rectangle(0, 0, (int)frameOut.stFrameInfo.nWidth, (int)frameOut.stFrameInfo.nHeight), ImageLockMode.ReadWrite, displayBitmaps[cameraIndex].PixelFormat); CopyMemory(bitmapData.Scan0, convertParam.pDstBuffer, (uint)(bitmapData.Stride * displayBitmaps[cameraIndex].Height)); displayBitmaps[cameraIndex].UnlockBits(bitmapData); } // 录制视频(新增) if (isRecording[cameraIndex] && videoWriters[cameraIndex] != null && videoWriters[cameraIndex].IsOpen) { // 简单的帧率控制 DateTime now = DateTime.Now; if ((now - lastWriteTime).TotalMilliseconds >= (1000.0 / _frameRate) || framesWritten == 0) { try { // 锁定以避免线程冲突 Bitmap frameToWrite; lock (locks[cameraIndex]) { // 克隆Bitmap用于写入,避免资源冲突 frameToWrite = (Bitmap)displayBitmaps[cameraIndex].Clone(); } videoWriters[cameraIndex].WriteVideoFrame(frameToWrite); frameToWrite.Dispose(); framesWritten++; lastWriteTime = now; } catch (Exception ex) { Console.WriteLine($"写入视频帧失败: {ex.Message}"); } } } // 使用SDK显示 displayInfo.hWnd = pictureBoxes[cameraIndex].Handle; displayInfo.pData = frameOut.pBufAddr; displayInfo.nDataLen = frameOut.stFrameInfo.nFrameLen; displayInfo.nWidth = frameOut.stFrameInfo.nWidth; displayInfo.nHeight = frameOut.stFrameInfo.nHeight; displayInfo.enPixelType = frameOut.stFrameInfo.enPixelType; // 在UI线程中调用显示 if (this.InvokeRequired) { this.Invoke(new Action(() => { camera.MV_CC_DisplayOneFrame_NET(ref displayInfo); })); } else { camera.MV_CC_DisplayOneFrame_NET(ref displayInfo); } // 释放图像缓冲区 camera.MV_CC_FreeImageBuffer_NET(ref frameOut); } } catch (Exception ex) { Console.WriteLine($"相机{cameraIndex + 1}采集异常: {ex.Message}"); // 继续循环,不退出 } } } /// /// 清理相机资源(修改) /// private void CleanupCameraResources(int cameraIndex) { // 清理显示缓冲区 if (displayBufs[cameraIndex] != IntPtr.Zero) { Marshal.FreeHGlobal(displayBufs[cameraIndex]); displayBufs[cameraIndex] = IntPtr.Zero; } // 清理Bitmap if (displayBitmaps[cameraIndex] != null) { displayBitmaps[cameraIndex].Dispose(); displayBitmaps[cameraIndex] = null; } displayBufSizes[cameraIndex] = 0; // 清理视频录制资源(新增) if (videoWriters[cameraIndex] != null) { try { if (isRecording[cameraIndex]) { videoWriters[cameraIndex].Close(); isRecording[cameraIndex] = false; } videoWriters[cameraIndex].Dispose(); } catch (Exception ex) { Console.WriteLine($"清理视频录制资源异常: {ex.Message}"); } finally { videoWriters[cameraIndex] = null; } } } /// /// 更新相机状态显示 /// private void UpdateCameraStatus(int cameraIndex, string message, Color color) { if (statusLabels[cameraIndex].InvokeRequired) { statusLabels[cameraIndex].Invoke(new Action(() => { statusLabels[cameraIndex].Text = $"状态:{message}"; statusLabels[cameraIndex].ForeColor = color; })); } else { statusLabels[cameraIndex].Text = $"状态:{message}"; statusLabels[cameraIndex].ForeColor = color; } } /// /// 更新系统状态 /// private void UpdateSystemStatus(string message) { // 这里可以添加系统状态栏更新逻辑 Console.WriteLine($"系统状态: {message}"); } /// /// 窗体关闭时的清理工作 /// private void AutoCameraForm_FormClosing(object sender, FormClosingEventArgs e) { try { // 停止状态检查线程 if (_statusCheckThread != null && _statusCheckThread.IsAlive) { _statusCheckThread.Join(1000); } // 停止所有采集线程 for (int i = 0; i < MAX_CAMERAS; i++) { grabbingFlags[i] = false; if (grabThreads[i] != null && grabThreads[i].IsAlive) { grabThreads[i].Join(1000); } if (cameras[i] != null) { cameras[i].MV_CC_StopGrabbing_NET(); cameras[i].MV_CC_CloseDevice_NET(); cameras[i].MV_CC_DestroyDevice_NET(); } CleanupCameraResources(i); } // 反初始化SDK MyCamera.MV_CC_Finalize_NET(); } catch (Exception ex) { MessageBox.Show($"清理资源时发生异常: {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } }