123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- namespace SinMaiLauncher.Interops;
- using System.Runtime.InteropServices;
- using DirectShowLib;
- internal static class CameraInterops
- {
- public static DsDevice[] GetAllCamera() => DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
- internal class CameraCaptureWrap : IDisposable
- {
- private readonly Control _container;
- private readonly IFilterGraph2 graphBuilder;
- private readonly ICaptureGraphBuilder2 captureGraphBuilder;
- private readonly IMediaControl mediaControl;
- private readonly IVideoWindow videoWindow;
- private readonly IBaseFilter captureFilter;
- private readonly IAMStreamConfig streamConfig;
- public IReadOnlyList<ICameraFormat> SupportedFormats { get; private set; }
- public CameraCaptureWrap(DsDevice camera, Control container)
- {
- _container = container;
- // 创建滤镜图
- graphBuilder = (IFilterGraph2)new FilterGraph();
- mediaControl = (IMediaControl)graphBuilder;
- videoWindow = (IVideoWindow)graphBuilder;
- // 创建捕获图构建器并设置滤镜图
- captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
- captureGraphBuilder.SetFiltergraph(graphBuilder);
- // 使用指定的摄像头设备
- var iid = typeof(IBaseFilter).GUID;
- camera.Mon.BindToObject(null, null, ref iid, out var source);
- captureFilter = (IBaseFilter)source;
- graphBuilder.AddFilter(captureFilter, "Video Capture");
- // 获取 IAMStreamConfig 接口
- captureGraphBuilder.FindInterface(
- PinCategory.Capture, MediaType.Video, captureFilter,
- typeof(IAMStreamConfig).GUID, out var configObj);
- streamConfig = configObj as IAMStreamConfig;
- // 枚举支持的视频格式
- streamConfig.GetNumberOfCapabilities(out var count, out var size);
- var fmtLst = new List<CameraFormat>(count);
- for (var i = 0; i < count; i++)
- {
- // 分配内存并获取指向 VideoStreamConfigCaps 的指针
- var capsPtr = Marshal.AllocHGlobal(size);
- try
- {
- // 调用 GetStreamCaps
- var hr = streamConfig.GetStreamCaps(i, out var mediaType, capsPtr);
- if (hr < 0)
- {
- Console.WriteLine($"GetStreamCaps failed with error: {hr}");
- continue;
- }
- // 解析媒体类型和 caps
- if (mediaType.formatType == FormatType.VideoInfo)
- {
- // 将指针转换回结构
- var caps = (VideoStreamConfigCaps)Marshal.PtrToStructure(capsPtr, typeof(VideoStreamConfigCaps));
- //TO DO: 使用caps,如果需要
- var fmt = new CameraFormat(mediaType);
- fmtLst.Add(fmt);
- }
- }
- finally
- {
- // 释放分配的内存
- Marshal.FreeHGlobal(capsPtr);
- }
- }
- SupportedFormats = fmtLst.ToArray();
- }
- public interface ICameraFormat
- {
- public int Width { get; }
- public int Height { get; }
- public double Fps { get; }
- public int Bits { get; }
- public string Name => $"{Width}x{Height} {Bits}Bit {Fps:N2}FPS";
- }
- public class CameraFormat : ICameraFormat
- {
- public AMMediaType MediaType { get; }
- public int Width { get; }
- public int Height { get; }
- public double Fps { get; }
- public int Bits { get; }
- public string Name => $"{Width}x{Height} {Bits}Bit {Fps:N2}FPS";
- public CameraFormat(AMMediaType mediaType)
- {
- MediaType = mediaType;
- var vih = (VideoInfoHeader)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader));
- Width = vih.BmiHeader.Width;
- Height = vih.BmiHeader.Height;
- Bits = vih.BmiHeader.BitCount;
- Fps = 10000000.0 / vih.AvgTimePerFrame;
- }
- }
- public void Dispose()
- {
- var pfs = FilterState.Stopped;
- mediaControl?.GetState(100, out pfs);
- if (pfs != FilterState.Stopped) mediaControl?.Stop();
- if (videoWindow != null) videoWindow.put_Visible(OABool.False);
- Marshal.ReleaseComObject(mediaControl);
- Marshal.ReleaseComObject(videoWindow);
- Marshal.ReleaseComObject(graphBuilder);
- Marshal.ReleaseComObject(captureGraphBuilder);
- Marshal.ReleaseComObject(captureFilter);
- Marshal.ReleaseComObject(streamConfig);
- // 释放 AMMediaType
- foreach (var format in SupportedFormats)
- {
- DsUtils.FreeAMMediaType(((CameraFormat)format).MediaType);
- }
- SupportedFormats = Array.Empty<ICameraFormat>();
- }
- public void ApplySelectedFormat(ICameraFormat selectedItem)
- {
- mediaControl.GetState(100, out var pfs);
- if (pfs != FilterState.Stopped) mediaControl.Stop();
- var fmt = (CameraFormat)selectedItem;
- streamConfig.SetFormat(fmt.MediaType);
- // 渲染预览流
- captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, captureFilter, null, null);
- mediaControl.Run();
- // 设置视频窗口
- videoWindow.put_Owner(_container.Handle);
- videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
- videoWindow.put_Visible(OABool.True); // 确保窗口可见
- videoWindow.SetWindowPosition(0, 0, _container.Width, _container.Height); // 设置初始大小
- //TODO: 估计要跟随 Resize 事件
- }
- public ICameraFormat? FindBestFormat(int targetWidth, int targetHeight, double targetRefreshRate)
- {
- ICameraFormat? bestMatch = null;
- var minScore = double.MaxValue;
- foreach (var format in SupportedFormats)
- {
- var fmt = (CameraFormat)format;
- var resolutionScore = (fmt.Width == targetWidth && fmt.Height == targetHeight) ? 0 : Math.Abs(fmt.Width - targetWidth) + Math.Abs(fmt.Height - targetHeight);
- var frameRateScore = Math.Abs(fmt.Fps - targetRefreshRate);
- var totalScore = resolutionScore * 1000 + frameRateScore; // 分辨率优先
- if (totalScore < minScore)
- {
- minScore = totalScore;
- bestMatch = format;
- }
- }
- return bestMatch;
- }
- }
- }
|