.Net Core下的USB摄像头截图

最近想把之前一个USB摄像头拍照的winform程序迁移到.net 5下,首先找了一下,网上有通过DirectShow.NET读取摄像头的例子,Read Barcode from Webcam Viewer with DirectShow.NET

这个程序依赖着DirectShow.NET的,虽然它是.net framework版本的。试了一下,迁移到.net 5还是无任何障碍的。但我觉得这些原始api使用起来还是不是很方便,便继续找了一下,找到了一个更加简单的类

   1     class UsbCamera:IDisposable
   2     {
   3         /// <summary>Usb camera image size.</summary>
   4         public Size Size { get; private set; }
   5 
   6         /// <summary>Start.</summary>
   7         public Action Start { get; private set; }
   8 
   9         /// <summary>Stop.</summary>
  10         public Action Stop { get; private set; }
  11 
  12         /// <summary>Release resource.</summary>
  13         public Action Release { get; private set; }
  14 
  15         /// <summary>Get image.</summary>
  16         /// <remarks>Immediately after starting, fails because image buffer is not prepared yet.</remarks>
  17         public Func<Bitmap> GetBitmap { get; private set; }
  18 
  19         /// <summary>
  20         /// Get available USB camera list.
  21         /// </summary>
  22         /// <returns>Array of camera name, or if no device found, zero length array.</returns>
  23         public static string[] FindDevices()
  24         {
  25             return DirectShow.GetFiltes(DirectShow.DsGuid.CLSID_VideoInputDeviceCategory).ToArray();
  26         }
  27 
  28         /// <summary>
  29         /// Get video formats.
  30         /// </summary>
  31         public static VideoFormat[] GetVideoFormat(int cameraIndex)
  32         {
  33             var filter = DirectShow.CreateFilter(DirectShow.DsGuid.CLSID_VideoInputDeviceCategory, cameraIndex);
  34             var pin = DirectShow.FindPin(filter, 0, DirectShow.PIN_DIRECTION.PINDIR_OUTPUT);
  35             return GetVideoOutputFormat(pin);
  36         }
  37 
  38         /// <summary>
  39         /// Create USB Camera. If device do not support the size, default size will applied.
  40         /// </summary>
  41         /// <param name="cameraIndex">Camera index in FindDevices() result.</param>
  42         /// <param name="size">
  43         /// Size you want to create. Normally use Size property of VideoFormat in GetVideoFormat() result.
  44         /// </param>
  45         public UsbCamera(int cameraIndex, Size size) : this(cameraIndex, new VideoFormat() { Size = size })
  46         {
  47         }
  48 
  49         /// <summary>
  50         /// Create USB Camera. If device do not support the format, default format will applied.
  51         /// </summary>
  52         /// <param name="cameraIndex">Camera index in FindDevices() result.</param>
  53         /// <param name="format">
  54         /// Normally use GetVideoFormat() result.
  55         /// You can change TimePerFrame value from Caps.MinFrameInterval to Caps.MaxFrameInterval.
  56         /// TimePerFrame = 10,000,000 / frame duration. (ex: 333333 in case 30fps).
  57         /// You can change Size value in case Caps.MaxOutputSize > Caps.MinOutputSize and OutputGranularityX/Y is not zero.
  58         /// Size = any value from Caps.MinOutputSize to Caps.MaxOutputSize step with OutputGranularityX/Y.
  59         /// </param>
  60         public UsbCamera(int cameraIndex, VideoFormat format)
  61         {
  62             var camera_list = FindDevices();
  63             if (cameraIndex >= camera_list.Length) throw new ArgumentException("USB camera is not available.", "cameraIndex");
  64             Init(cameraIndex, format);
  65         }
  66 
  67         private void Init(int index, VideoFormat format)
  68         {
  69             //----------------------------------
  70             // Create Filter Graph
  71             //----------------------------------
  72             // +--------------------+  +----------------+  +---------------+
  73             // |Video Capture Source|→| Sample Grabber |→| Null Renderer |
  74             // +--------------------+  +----------------+  +---------------+
  75             //                                 ↓GetBitmap()
  76 
  77             var graph = DirectShow.CreateGraph();
  78 
  79             //----------------------------------
  80             // VideoCaptureSource
  81             //----------------------------------
  82             var vcap_source = CreateVideoCaptureSource(index, format);
  83             graph.AddFilter(vcap_source, "VideoCapture");
  84 
  85             //------------------------------
  86             // SampleGrabber
  87             //------------------------------
  88             var grabber = CreateSampleGrabber();
  89             graph.AddFilter(grabber, "SampleGrabber");
  90             var i_grabber = (DirectShow.ISampleGrabber)grabber;
  91             i_grabber.SetBufferSamples(true); 
  92 
  93             //---------------------------------------------------
  94             // Null Renderer
  95             //---------------------------------------------------
  96             var renderer = DirectShow.CoCreateInstance(DirectShow.DsGuid.CLSID_NullRenderer) as DirectShow.IBaseFilter;
  97             graph.AddFilter(renderer, "NullRenderer");
  98 
  99             //---------------------------------------------------
 100             // Create Filter Graph
 101             //---------------------------------------------------
 102             var builder = DirectShow.CoCreateInstance(DirectShow.DsGuid.CLSID_CaptureGraphBuilder2) as DirectShow.ICaptureGraphBuilder2;
 103             builder.SetFiltergraph(graph);
 104             var pinCategory = DirectShow.DsGuid.PIN_CATEGORY_CAPTURE;
 105             var mediaType = DirectShow.DsGuid.MEDIATYPE_Video;
 106             builder.RenderStream(ref pinCategory, ref mediaType, vcap_source, grabber, renderer);
 107 
 108             // SampleGrabber Format.
 109             {
 110                 var mt = new DirectShow.AM_MEDIA_TYPE();
 111                 i_grabber.GetConnectedMediaType(mt);
 112                 var header = (DirectShow.VIDEOINFOHEADER)Marshal.PtrToStructure(mt.pbFormat, typeof(DirectShow.VIDEOINFOHEADER));
 113                 var width = header.bmiHeader.biWidth;
 114                 var height = header.bmiHeader.biHeight;
 115                 var stride = width * (header.bmiHeader.biBitCount / 8);
 116                 DirectShow.DeleteMediaType(ref mt);
 117 
 118                 Size = new Size(width, height);
 119 
 120                 // fix screen tearing problem(issure #2)
 121                 // you can use previous method if you swap the comment line below.
 122                 // GetBitmap = () => GetBitmapFromSampleGrabberBuffer(i_grabber, width, height, stride);
 123                 GetBitmap = GetBitmapFromSampleGrabberCallback(i_grabber, width, height, stride);
 124             }
 125 
 126             // Assign Delegates.
 127             Start = () => DirectShow.PlayGraph(graph, DirectShow.FILTER_STATE.Running);
 128             Stop = () => DirectShow.PlayGraph(graph, DirectShow.FILTER_STATE.Stopped);
 129             Release = () =>
 130             {
 131                 Stop();
 132 
 133                 DirectShow.ReleaseInstance(ref i_grabber);
 134                 DirectShow.ReleaseInstance(ref builder);
 135                 DirectShow.ReleaseInstance(ref graph);
 136             };
 137 
 138             // Properties.
 139             Properties = new PropertyItems(vcap_source);
 140         }
 141 
 142         public void Dispose()
 143         {
 144             Release?.Invoke();
 145         }
 146 
 147         /// <summary>Properties user can adjust.</summary>
 148         public PropertyItems Properties { get; private set; }
 149         public class PropertyItems
 150         {
 151             public PropertyItems(DirectShow.IBaseFilter vcap_source)
 152             {
 153                 // Pan, Tilt, Roll, Zoom, Exposure, Iris, Focus
 154                 this.CameraControl = Enum.GetValues(typeof(DirectShow.CameraControlProperty)).Cast<DirectShow.CameraControlProperty>()
 155                     .Select(item =>
 156                     {
 157                         PropertyItems.Property prop = null;
 158                         try
 159                         {
 160                             var cam_ctrl = vcap_source as DirectShow.IAMCameraControl;
 161                             if (cam_ctrl == null) throw new NotSupportedException("no IAMCameraControl Interface."); // will catched.
 162                             int min = 0, max = 0, step = 0, def = 0, flags = 0;
 163                             cam_ctrl.GetRange(item, ref min, ref max, ref step, ref def, ref flags); // COMException if not supports.
 164                             prop = new Property(min, max, step, def, flags, (flag, value) => cam_ctrl.Set(item, value, (int)flag));
 165                         }
 166                         catch (Exception) { prop = new Property(); } // available = false
 167                         return new { Key = item, Value = prop };
 168                     }).ToDictionary(x => x.Key, x => x.Value);
 169 
 170                 // Brightness, Contrast, Hue, Saturation, Sharpness, Gamma, ColorEnable, WhiteBalance, BacklightCompensation, Gain
 171                 this.VideoProcAmp = Enum.GetValues(typeof(DirectShow.VideoProcAmpProperty)).Cast<DirectShow.VideoProcAmpProperty>()
 172                     .Select(item =>
 173                     {
 174                         PropertyItems.Property prop = null;
 175                         try
 176                         {
 177                             var vid_ctrl = vcap_source as DirectShow.IAMVideoProcAmp;
 178                             if (vid_ctrl == null) throw new NotSupportedException("no IAMVideoProcAmp Interface."); // will catched.
 179                             int min = 0, max = 0, step = 0, def = 0, flags = 0;
 180                             vid_ctrl.GetRange(item, ref min, ref max, ref step, ref def, ref flags); // COMException if not supports.
 181                             prop = new Property(min, max, step, def, flags, (flag, value) => vid_ctrl.Set(item, value, (int)flag));
 182                         }
 183                         catch (Exception) { prop = new Property(); } // available = false
 184                         return new { Key = item, Value = prop };
 185                     }).ToDictionary(x => x.Key, x => x.Value);
 186             }
 187 
 188             /// <summary>Camera Control properties.</summary>
 189             private Dictionary<DirectShow.CameraControlProperty, Property> CameraControl;
 190 
 191             /// <summary>Video Processing Amplifier properties.</summary>
 192             private Dictionary<DirectShow.VideoProcAmpProperty, Property> VideoProcAmp;
 193 
 194             /// <summary>Get CameraControl Property. Check Available before use.</summary>
 195             public Property this[DirectShow.CameraControlProperty item] { get { return CameraControl[item]; } }
 196 
 197             /// <summary>Get VideoProcAmp Property. Check Available before use.</summary>
 198             public Property this[DirectShow.VideoProcAmpProperty item] { get { return VideoProcAmp[item]; } }
 199 
 200             public class Property
 201             {
 202                 public int Min { get; private set; }
 203                 public int Max { get; private set; }
 204                 public int Step { get; private set; }
 205                 public int Default { get; private set; }
 206                 public DirectShow.CameraControlFlags Flags { get; private set; }
 207                 public Action<DirectShow.CameraControlFlags, int> SetValue { get; private set; }
 208                 public bool Available { get; private set; }
 209                 public bool CanAuto { get; private set; }
 210 
 211                 public Property()
 212                 {
 213                     this.SetValue = (flag, value) => { };
 214                     this.Available = false;
 215                 }
 216 
 217                 public Property(int min, int max, int step, int @default, int flags, Action<DirectShow.CameraControlFlags, int> set)
 218                 {
 219                     this.Min = min;
 220                     this.Max = max;
 221                     this.Step = step;
 222                     this.Default = @default;
 223                     this.Flags = (DirectShow.CameraControlFlags)flags;
 224                     this.CanAuto = (Flags & DirectShow.CameraControlFlags.Auto) == DirectShow.CameraControlFlags.Auto;
 225                     this.SetValue = set;
 226                     this.Available = true;
 227                 }
 228 
 229                 public override string ToString()
 230                 {
 231                     return string.Format("Available={0}, Min={1}, Max={2}, Step={3}, Default={4}, Flags={5}", Available, Min, Max, Step, Default, Flags);
 232                 }
 233             }
 234         }
 235 
 236         private class SampleGrabberCallback : DirectShow.ISampleGrabberCB
 237         {
 238             private byte[] Buffer;
 239             private object BufferLock = new object();
 240 
 241             public Bitmap GetBitmap(int width, int height, int stride)
 242             {
 243                 var result = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
 244                 if (Buffer == null) return result;
 245 
 246                 var bmp_data = result.LockBits(new Rectangle(Point.Empty, result.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
 247                 lock (BufferLock)
 248                 {
 249                     // copy from last row.
 250                     for (int y = 0; y < height; y++)
 251                     {
 252                         var src_idx = Buffer.Length - (stride * (y + 1));
 253                         var dst = IntPtr.Add(bmp_data.Scan0, stride * y);
 254                         Marshal.Copy(Buffer, src_idx, dst, stride);
 255                     }
 256                 }
 257                 result.UnlockBits(bmp_data);
 258 
 259                 return result;
 260             }
 261 
 262             // called when each sample completed.
 263             // The data processing thread blocks until the callback method returns. If the callback does not return quickly, it can interfere with playback.
 264             public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
 265             {
 266                 if (Buffer == null || Buffer.Length != BufferLen)
 267                 {
 268                     Buffer = new byte[BufferLen];
 269                 }
 270 
 271                 lock (BufferLock)
 272                 {
 273                     Marshal.Copy(pBuffer, Buffer, 0, BufferLen);
 274                 }
 275                 return 0;
 276             }
 277 
 278             // never called.
 279             public int SampleCB(double SampleTime, DirectShow.IMediaSample pSample)
 280             {
 281                 throw new NotImplementedException();
 282             }
 283         }
 284 
 285         private Func<Bitmap> GetBitmapFromSampleGrabberCallback(DirectShow.ISampleGrabber i_grabber, int width, int height, int stride)
 286         {
 287             var sampler = new SampleGrabberCallback();
 288             i_grabber.SetCallback(sampler, 1); // WhichMethodToCallback = BufferCB
 289             return () => sampler.GetBitmap(width, height, stride);
 290         }
 291 
 292         /// <summary>Get Bitmap from Sample Grabber Current Buffer</summary>
 293         private Bitmap GetBitmapFromSampleGrabberBuffer(DirectShow.ISampleGrabber i_grabber, int width, int height, int stride)
 294         {
 295             try
 296             {
 297                 return GetBitmapFromSampleGrabberBufferMain(i_grabber, width, height, stride);
 298             }
 299             catch (COMException ex)
 300             {
 301                 const uint VFW_E_WRONG_STATE = 0x80040227;
 302                 if ((uint)ex.ErrorCode == VFW_E_WRONG_STATE)
 303                 {
 304                     // image data is not ready yet. return empty bitmap.
 305                     return new Bitmap(width, height);
 306                 }
 307 
 308                 throw;
 309             }
 310         }
 311 
 312         /// <summary>Get Bitmap from Sample Grabber Current Buffer</summary>
 313         private Bitmap GetBitmapFromSampleGrabberBufferMain(DirectShow.ISampleGrabber i_grabber, int width, int height, int stride)
 314         {
 315 
 316             int sz = 0;
 317             i_grabber.GetCurrentBuffer(ref sz, IntPtr.Zero); // IntPtr.Zeroで呼び出してバッファサイズ取得
 318             if (sz == 0) return null;
 319 
 320             var ptr = Marshal.AllocCoTaskMem(sz);
 321             i_grabber.GetCurrentBuffer(ref sz, ptr);
 322 
 323             var data = new byte[sz];
 324             Marshal.Copy(ptr, data, 0, sz);
 325 
 326             var result = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
 327             var bmp_data = result.LockBits(new Rectangle(Point.Empty, result.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
 328 
 329             for (int y = 0; y < height; y++)
 330             {
 331                 var src_idx = sz - (stride * (y + 1)); 
 332                 var dst = IntPtr.Add(bmp_data.Scan0, stride * y);
 333                 Marshal.Copy(data, src_idx, dst, stride);
 334             }
 335             result.UnlockBits(bmp_data);
 336             Marshal.FreeCoTaskMem(ptr);
 337 
 338             return result;
 339         }
 340 
 341 
 342         private DirectShow.IBaseFilter CreateSampleGrabber()
 343         {
 344             var filter = DirectShow.CreateFilter(DirectShow.DsGuid.CLSID_SampleGrabber);
 345             var ismp = filter as DirectShow.ISampleGrabber;
 346 
 347             var mt = new DirectShow.AM_MEDIA_TYPE();
 348             mt.MajorType = DirectShow.DsGuid.MEDIATYPE_Video;
 349             mt.SubType = DirectShow.DsGuid.MEDIASUBTYPE_RGB24;
 350             ismp.SetMediaType(mt);
 351             return filter;
 352         }
 353 
 354         /// <summary>
 355         /// Video Capture Sourceフィルタを作成する
 356         /// </summary>
 357         private DirectShow.IBaseFilter CreateVideoCaptureSource(int index, VideoFormat format)
 358         {
 359             var filter = DirectShow.CreateFilter(DirectShow.DsGuid.CLSID_VideoInputDeviceCategory, index);
 360             var pin = DirectShow.FindPin(filter, 0, DirectShow.PIN_DIRECTION.PINDIR_OUTPUT);
 361             SetVideoOutputFormat(pin, format);
 362             return filter;
 363         }
 364 
 365         /// <summary>
 366         /// ビデオキャプチャデバイスの出力形式を選択する。
 367         /// </summary>
 368         private static void SetVideoOutputFormat(DirectShow.IPin pin, VideoFormat format)
 369         {
 370             var formats = GetVideoOutputFormat(pin);
 371    
 372             for (int i = 0; i < formats.Length; i++)
 373             {
 374                 var item = formats[i];
 375 
 376                 if (item.MajorType != DirectShow.DsGuid.GetNickname(DirectShow.DsGuid.MEDIATYPE_Video)) continue;
 377                 if (string.IsNullOrEmpty(format.SubType) == false && format.SubType != item.SubType) continue;
 378                 if (item.Caps.Guid != DirectShow.DsGuid.FORMAT_VideoInfo) continue;
 379 
 380                 if (item.Size.Width == format.Size.Width && item.Size.Height == format.Size.Height)
 381                 {
 382                     SetVideoOutputFormat(pin, i, format.Size, format.TimePerFrame);
 383                     return;
 384                 }
 385             }
 386 
 387             // Not found fixed size, search for variable size.
 388             for (int i = 0; i < formats.Length; i++)
 389             {
 390                 var item = formats[i];
 391 
 392 
 393  
 394                 if (item.MajorType != DirectShow.DsGuid.GetNickname(DirectShow.DsGuid.MEDIATYPE_Video)) continue;
 395                 if (string.IsNullOrEmpty(format.SubType) == false && format.SubType != item.SubType) continue;
 396                 if (item.Caps.Guid != DirectShow.DsGuid.FORMAT_VideoInfo) continue;
 397 
 398                 if (item.Caps.OutputGranularityX == 0) continue;
 399                 if (item.Caps.OutputGranularityY == 0) continue;
 400 
 401                 for (int w = item.Caps.MinOutputSize.cx; w < item.Caps.MaxOutputSize.cx; w += item.Caps.OutputGranularityX)
 402                 {
 403                     for (int h = item.Caps.MinOutputSize.cy; h < item.Caps.MaxOutputSize.cy; h += item.Caps.OutputGranularityY)
 404                     {
 405                         if (w == format.Size.Width && h == format.Size.Height)
 406                         {
 407                             SetVideoOutputFormat(pin, i, format.Size, format.TimePerFrame);
 408                             return;
 409                         }
 410                     }
 411                 }
 412             }
 413 
 414             // Not found, use default size.
 415             SetVideoOutputFormat(pin, 0, Size.Empty, 0);
 416         }
 417 
 418 
 419         private static VideoFormat[] GetVideoOutputFormat(DirectShow.IPin pin)
 420         {
 421             var config = pin as DirectShow.IAMStreamConfig;
 422             if (config == null)
 423             {
 424                 throw new InvalidOperationException("no IAMStreamConfig interface.");
 425             }
 426 
 427             int cap_count = 0, cap_size = 0;
 428             config.GetNumberOfCapabilities(ref cap_count, ref cap_size);
 429             if (cap_size != Marshal.SizeOf(typeof(DirectShow.VIDEO_STREAM_CONFIG_CAPS)))
 430             {
 431                 throw new InvalidOperationException("no VIDEO_STREAM_CONFIG_CAPS.");
 432             }
 433 
 434             var result = new VideoFormat[cap_count];
 435 
 436             var cap_data = Marshal.AllocHGlobal(cap_size);
 437 
 438             for (int i = 0; i < cap_count; i++)
 439             {
 440                 var entry = new VideoFormat();
 441 
 442                 DirectShow.AM_MEDIA_TYPE mt = null;
 443                 config.GetStreamCaps(i, ref mt, cap_data);
 444                 entry.Caps = PtrToStructure<DirectShow.VIDEO_STREAM_CONFIG_CAPS>(cap_data);
 445 
 446                 entry.MajorType = DirectShow.DsGuid.GetNickname(mt.MajorType);
 447                 entry.SubType = DirectShow.DsGuid.GetNickname(mt.SubType);
 448 
 449                 if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo)
 450                 {
 451                     var vinfo = PtrToStructure<DirectShow.VIDEOINFOHEADER>(mt.pbFormat);
 452                     entry.Size = new Size(vinfo.bmiHeader.biWidth, vinfo.bmiHeader.biHeight);
 453                     entry.TimePerFrame = vinfo.AvgTimePerFrame;
 454                 }
 455                 else if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo2)
 456                 {
 457                     var vinfo = PtrToStructure<DirectShow.VIDEOINFOHEADER2>(mt.pbFormat);
 458                     entry.Size = new Size(vinfo.bmiHeader.biWidth, vinfo.bmiHeader.biHeight);
 459                     entry.TimePerFrame = vinfo.AvgTimePerFrame;
 460                 }
 461 
 462                 // 解放
 463                 DirectShow.DeleteMediaType(ref mt);
 464 
 465                 result[i] = entry;
 466             }
 467 
 468             // 解放
 469             Marshal.FreeHGlobal(cap_data);
 470 
 471             return result;
 472         }
 473 
 474         private static void SetVideoOutputFormat(DirectShow.IPin pin, int index, Size size, long timePerFrame)
 475         {
 476             var config = pin as DirectShow.IAMStreamConfig;
 477             if (config == null)
 478             {
 479                 throw new InvalidOperationException("no IAMStreamConfig interface.");
 480             }
 481 
 482             int cap_count = 0, cap_size = 0;
 483             config.GetNumberOfCapabilities(ref cap_count, ref cap_size);
 484             if (cap_size != Marshal.SizeOf(typeof(DirectShow.VIDEO_STREAM_CONFIG_CAPS)))
 485             {
 486                 throw new InvalidOperationException("no VIDEO_STREAM_CONFIG_CAPS.");
 487             }
 488 
 489             var cap_data = Marshal.AllocHGlobal(cap_size);
 490 
 491             DirectShow.AM_MEDIA_TYPE mt = null;
 492             config.GetStreamCaps(index, ref mt, cap_data);
 493             var cap = PtrToStructure<DirectShow.VIDEO_STREAM_CONFIG_CAPS>(cap_data);
 494 
 495             if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo)
 496             {
 497                 var vinfo = PtrToStructure<DirectShow.VIDEOINFOHEADER>(mt.pbFormat);
 498                 if (!size.IsEmpty) { vinfo.bmiHeader.biWidth = size.Width; vinfo.bmiHeader.biHeight = size.Height; }
 499                 if (timePerFrame > 0) { vinfo.AvgTimePerFrame = timePerFrame; }
 500                 Marshal.StructureToPtr(vinfo, mt.pbFormat, true);
 501             }
 502             else if (mt.FormatType == DirectShow.DsGuid.FORMAT_VideoInfo2)
 503             {
 504                 var vinfo = PtrToStructure<DirectShow.VIDEOINFOHEADER2>(mt.pbFormat);
 505                 if (!size.IsEmpty) { vinfo.bmiHeader.biWidth = size.Width; vinfo.bmiHeader.biHeight = size.Height; }
 506                 if (timePerFrame > 0) { vinfo.AvgTimePerFrame = timePerFrame; }
 507                 Marshal.StructureToPtr(vinfo, mt.pbFormat, true);
 508             }
 509 
 510             config.SetFormat(mt);
 511 
 512             if (cap_data != System.IntPtr.Zero) Marshal.FreeHGlobal(cap_data);
 513             if (mt != null) DirectShow.DeleteMediaType(ref mt);
 514         }
 515 
 516         private static T PtrToStructure<T>(IntPtr ptr)
 517         {
 518             return (T)Marshal.PtrToStructure(ptr, typeof(T));
 519         }
 520 
 521         public class VideoFormat
 522         {
 523             public string MajorType { get; set; } 
 524             public string SubType { get; set; } 
 525             public Size Size { get; set; } 
 526             public long TimePerFrame { get; set; } 
 527             public DirectShow.VIDEO_STREAM_CONFIG_CAPS Caps { get; set; }
 528 
 529             public override string ToString()
 530             {
 531                 return string.Format("{0}, {1}, {2}, {3}, {4}", MajorType, SubType, Size, TimePerFrame, CapsString());
 532             }
 533 
 534             private string CapsString()
 535             {
 536                 var sb = new StringBuilder();
 537                 sb.AppendFormat("{0}, ", DirectShow.DsGuid.GetNickname(Caps.Guid));
 538                 foreach (var info in Caps.GetType().GetFields())
 539                 {
 540                     sb.AppendFormat("{0}={1}, ", info.Name, info.GetValue(Caps));
 541                 }
 542                 return sb.ToString();
 543             }
 544         }
 545 
 546 
 547         public static class DirectShow
 548         {
 549             #region Function
 550 
 551             public static object CoCreateInstance(Guid clsid)
 552             {
 553                 return Activator.CreateInstance(Type.GetTypeFromCLSID(clsid));
 554             }
 555 
 556             public static void ReleaseInstance<T>(ref T com) where T : class
 557             {
 558                 if (com != null)
 559                 {
 560                     Marshal.ReleaseComObject(com);
 561                     com = null;
 562                 }
 563             }
 564 
 565             public static IGraphBuilder CreateGraph()
 566             {
 567                 return CoCreateInstance(DsGuid.CLSID_FilterGraph) as IGraphBuilder;
 568             }
 569 
 570             public static void PlayGraph(IGraphBuilder graph, FILTER_STATE state)
 571             {
 572                 var mediaControl = graph as IMediaControl;
 573                 if (mediaControl == null) return;
 574 
 575                 switch (state)
 576                 {
 577                     case FILTER_STATE.Paused: mediaControl.Pause(); break;
 578                     case FILTER_STATE.Stopped: mediaControl.Stop(); break;
 579                     default: mediaControl.Run(); break;
 580                 }
 581             }
 582 
 583             public static List<string> GetFiltes(Guid category)
 584             {
 585                 var result = new List<string>();
 586 
 587                 EnumMonikers(category, (moniker, prop) =>
 588                 {
 589                     object value = null;
 590                     prop.Read("FriendlyName", ref value, 0);
 591                     var name = (string)value;
 592 
 593                     result.Add(name);
 594 
 595                     return false; // 継続。
 596                 });
 597 
 598                 return result;
 599             }
 600 
 601             public static IBaseFilter CreateFilter(Guid clsid)
 602             {
 603                 return CoCreateInstance(clsid) as IBaseFilter;
 604             }
 605 
 606             public static IBaseFilter CreateFilter(Guid category, int index)
 607             {
 608                 IBaseFilter result = null;
 609 
 610                 int curr_index = 0;
 611                 EnumMonikers(category, (moniker, prop) =>
 612                 {
 613                     if (index != curr_index++) return false;
 614 
 615                     {
 616                         object value = null;
 617                         Guid guid = DirectShow.DsGuid.IID_IBaseFilter;
 618                         moniker.BindToObject(null, null, ref guid, out value);
 619                         result = value as IBaseFilter;
 620                         return true;
 621                     }
 622                 });
 623 
 624                 if (result == null) throw new ArgumentException("can't create filter.");
 625                 return result;
 626             }
 627 
 628             private static void EnumMonikers(Guid category, Func<IMoniker, IPropertyBag, bool> func)
 629             {
 630                 IEnumMoniker enumerator = null;
 631                 ICreateDevEnum device = null;
 632 
 633                 try
 634                 {
 635                     device = (ICreateDevEnum)Activator.CreateInstance(Type.GetTypeFromCLSID(DsGuid.CLSID_SystemDeviceEnum));
 636                     device.CreateClassEnumerator(ref category, ref enumerator, 0);
 637 
 638                     if (enumerator == null) return;
 639 
 640                     var monikers = new IMoniker[1];
 641                     var fetched = IntPtr.Zero;
 642 
 643                     while (enumerator.Next(monikers.Length, monikers, fetched) == 0)
 644                     {
 645                         var moniker = monikers[0];
 646 
 647                         object value = null;
 648                         Guid guid = DsGuid.IID_IPropertyBag;
 649                         moniker.BindToStorage(null, null, ref guid, out value);
 650                         var prop = (IPropertyBag)value;
 651 
 652                         try
 653                         {
 654                             var rc = func(moniker, prop);
 655                             if (rc == true) break;
 656                         }
 657                         finally
 658                         {
 659                             Marshal.ReleaseComObject(prop);
 660                             if (moniker != null) Marshal.ReleaseComObject(moniker);
 661                         }
 662                     }
 663                 }
 664                 finally
 665                 {
 666                     if (enumerator != null) Marshal.ReleaseComObject(enumerator);
 667                     if (device != null) Marshal.ReleaseComObject(device);
 668                 }
 669             }
 670 
 671             public static IPin FindPin(IBaseFilter filter, string name)
 672             {
 673                 var result = EnumPins(filter, (info) =>
 674                 {
 675                     return (info.achName == name);
 676                 });
 677 
 678                 if (result == null) throw new ArgumentException("can't fild pin.");
 679                 return result;
 680             }
 681             public static IPin FindPin(IBaseFilter filter, int index, PIN_DIRECTION direction)
 682             {
 683                 int curr_index = 0;
 684                 var result = EnumPins(filter, (info) =>
 685                 {
 686                     if (info.dir != direction) return false;
 687 
 688                     return (index == curr_index++);
 689                 });
 690 
 691                 if (result == null) throw new ArgumentException("can't fild pin.");
 692                 return result;
 693             }
 694 
 695             private static IPin EnumPins(IBaseFilter filter, Func<PIN_INFO, bool> func)
 696             {
 697                 IEnumPins pins = null;
 698                 IPin ipin = null;
 699 
 700                 try
 701                 {
 702                     filter.EnumPins(ref pins);
 703 
 704                     int fetched = 0;
 705                     while (pins.Next(1, ref ipin, ref fetched) == 0)
 706                     {
 707                         if (fetched == 0) break;
 708 
 709                         var info = new PIN_INFO();
 710                         try
 711                         {
 712                             ipin.QueryPinInfo(info);
 713                             var rc = func(info);
 714                             if (rc) return ipin;
 715                         }
 716                         finally
 717                         {
 718                             if (info.pFilter != null) Marshal.ReleaseComObject(info.pFilter);
 719                         }
 720                     }
 721                 }
 722                 catch
 723                 {
 724                     if (ipin != null) Marshal.ReleaseComObject(ipin);
 725                     throw;
 726                 }
 727                 finally
 728                 {
 729                     if (pins != null) Marshal.ReleaseComObject(pins);
 730                 }
 731 
 732                 return null;
 733             }
 734 
 735             public static void ConnectFilter(IGraphBuilder graph, IBaseFilter out_flt, int out_no, IBaseFilter in_flt, int in_no)
 736             {
 737                 var out_pin = FindPin(out_flt, out_no, PIN_DIRECTION.PINDIR_OUTPUT);
 738                 var inp_pin = FindPin(in_flt, in_no, PIN_DIRECTION.PINDIR_INPUT);
 739                 graph.Connect(out_pin, inp_pin);
 740             }
 741 
 742             public static void DeleteMediaType(ref AM_MEDIA_TYPE mt)
 743             {
 744                 if (mt.lSampleSize != 0) Marshal.FreeCoTaskMem(mt.pbFormat);
 745                 if (mt.pUnk != IntPtr.Zero) Marshal.FreeCoTaskMem(mt.pUnk);
 746                 mt = null;
 747             }
 748 
 749             #endregion
 750 
 751 
 752             #region Interface
 753 
 754             [ComVisible(true), ComImport(), Guid("56a8689f-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 755             public interface IFilterGraph
 756             {
 757                 int AddFilter([In] IBaseFilter pFilter, [In, MarshalAs(UnmanagedType.LPWStr)] string pName);
 758                 int RemoveFilter([In] IBaseFilter pFilter);
 759                 int EnumFilters([In, Out] ref IEnumFilters ppEnum);
 760                 int FindFilterByName([In, MarshalAs(UnmanagedType.LPWStr)] string pName, [In, Out] ref IBaseFilter ppFilter);
 761                 int ConnectDirect([In] IPin ppinOut, [In] IPin ppinIn, [In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 762                 int Reconnect([In] IPin ppin);
 763                 int Disconnect([In] IPin ppin);
 764                 int SetDefaultSyncSource();
 765             }
 766 
 767             [ComVisible(true), ComImport(), Guid("56a868a9-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 768             public interface IGraphBuilder : IFilterGraph
 769             {
 770                 int Connect([In] IPin ppinOut, [In] IPin ppinIn);
 771                 int Render([In] IPin ppinOut);
 772                 int RenderFile([In, MarshalAs(UnmanagedType.LPWStr)] string lpcwstrFile, [In, MarshalAs(UnmanagedType.LPWStr)] string lpcwstrPlayList);
 773                 int AddSourceFilter([In, MarshalAs(UnmanagedType.LPWStr)] string lpcwstrFileName, [In, MarshalAs(UnmanagedType.LPWStr)] string lpcwstrFilterName, [In, Out] ref IBaseFilter ppFilter);
 774                 int SetLogFile(IntPtr hFile);
 775                 int Abort();
 776                 int ShouldOperationContinue();
 777             }
 778 
 779             [ComVisible(true), ComImport(), Guid("56a868b1-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
 780             public interface IMediaControl
 781             {
 782                 int Run();
 783                 int Pause();
 784                 int Stop();
 785                 int GetState(int msTimeout, out int pfs);
 786                 int RenderFile(string strFilename);
 787                 int AddSourceFilter([In] string strFilename, [In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppUnk);
 788                 int get_FilterCollection([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppUnk);
 789                 int get_RegFilterCollection([In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppUnk);
 790                 int StopWhenReady();
 791             }
 792 
 793             [ComVisible(true), ComImport(), Guid("93E5A4E0-2D50-11d2-ABFA-00A0C9C6E38D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 794             public interface ICaptureGraphBuilder2
 795             {
 796                 int SetFiltergraph([In] IGraphBuilder pfg);
 797                 int GetFiltergraph([In, Out] ref IGraphBuilder ppfg);
 798                 int SetOutputFileName([In] ref Guid pType, [In, MarshalAs(UnmanagedType.LPWStr)] string lpstrFile, [In, Out] ref IBaseFilter ppbf, [In, Out] ref IFileSinkFilter ppSink);
 799                 int FindInterface([In] ref Guid pCategory, [In] ref Guid pType, [In] IBaseFilter pbf, [In] IntPtr riid, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppint);
 800                 int RenderStream([In] ref Guid pCategory, [In] ref Guid pType, [In, MarshalAs(UnmanagedType.IUnknown)] object pSource, [In] IBaseFilter pfCompressor, [In] IBaseFilter pfRenderer);
 801                 int ControlStream([In] ref Guid pCategory, [In] ref Guid pType, [In] IBaseFilter pFilter, [In] IntPtr pstart, [In] IntPtr pstop, [In] short wStartCookie, [In] short wStopCookie);
 802                 int AllocCapFile([In, MarshalAs(UnmanagedType.LPWStr)] string lpstrFile, [In] long dwlSize);
 803                 int CopyCaptureFile([In, MarshalAs(UnmanagedType.LPWStr)] string lpwstrOld, [In, MarshalAs(UnmanagedType.LPWStr)] string lpwstrNew, [In] int fAllowEscAbort, [In] IAMCopyCaptureFileProgress pFilter);
 804                 int FindPin([In] object pSource, [In] int pindir, [In] ref Guid pCategory, [In] ref Guid pType, [In, MarshalAs(UnmanagedType.Bool)] bool fUnconnected, [In] int num, [Out] out IntPtr ppPin);
 805             }
 806 
 807             [ComVisible(true), ComImport(), Guid("a2104830-7c70-11cf-8bce-00aa00a3f1a6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 808             public interface IFileSinkFilter
 809             {
 810                 int SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 811                 int GetCurFile([In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string pszFileName, [Out, MarshalAs(UnmanagedType.LPStruct)] out AM_MEDIA_TYPE pmt);
 812             }
 813 
 814             [ComVisible(true), ComImport(), Guid("670d1d20-a068-11d0-b3f0-00aa003761c5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 815             public interface IAMCopyCaptureFileProgress
 816             {
 817                 int Progress(int iProgress);
 818             }
 819 
 820 
 821             [ComVisible(true), ComImport(), Guid("C6E13370-30AC-11d0-A18C-00A0C9118956"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 822             public interface IAMCameraControl
 823             {
 824                 int GetRange([In] CameraControlProperty Property, [In, Out] ref int pMin, [In, Out] ref int pMax, [In, Out] ref int pSteppingDelta, [In, Out] ref int pDefault, [In, Out] ref int pCapsFlag);
 825                 int Set([In] CameraControlProperty Property, [In] int lValue, [In] int Flags);
 826                 int Get([In] CameraControlProperty Property, [In, Out] ref int lValue, [In, Out] ref int Flags);
 827             }
 828 
 829 
 830             [ComVisible(true), ComImport(), Guid("C6E13360-30AC-11d0-A18C-00A0C9118956"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 831             public interface IAMVideoProcAmp
 832             {
 833                 int GetRange([In] VideoProcAmpProperty Property, [In, Out] ref int pMin, [In, Out] ref int pMax, [In, Out] ref int pSteppingDelta, [In, Out] ref int pDefault, [In, Out] ref int pCapsFlag);
 834                 int Set([In] VideoProcAmpProperty Property, [In] int lValue, [In] int Flags);
 835                 int Get([In] VideoProcAmpProperty Property, [In, Out] ref int lValue, [In, Out] ref int Flags);
 836             }
 837 
 838 
 839             [ComVisible(true), ComImport(), Guid("6A2E0670-28E4-11D0-A18C-00A0C9118956"), System.Security.SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 840             public interface IAMVideoControl
 841             {
 842                 int GetCaps([In] IPin pPin, [Out] out int pCapsFlags);
 843                 int SetMode([In] IPin pPin, [In] int Mode);
 844                 int GetMode([In] IPin pPin, [Out] out int Mode);
 845                 int GetCurrentActualFrameRate([In] IPin pPin, [Out] out long ActualFrameRate);
 846                 int GetMaxAvailableFrameRate([In] IPin pPin, [In] int iIndex, [In] Size Dimensions, [Out] out long MaxAvailableFrameRate);
 847                 int GetFrameRateList([In] IPin pPin, [In] int iIndex, [In] Size Dimensions, [Out] out int ListSize, [Out] out IntPtr FrameRates);
 848             }
 849 
 850             [ComVisible(true), ComImport(), Guid("56a86895-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 851             public interface IBaseFilter
 852             {
 853                 // Inherits IPersist
 854                 int GetClassID([Out] out Guid pClassID);
 855 
 856                 // Inherits IMediaControl
 857                 int Stop();
 858                 int Pause();
 859                 int Run(long tStart);
 860                 int GetState(int dwMilliSecsTimeout, [In, Out] ref int filtState);
 861                 int SetSyncSource([In] IReferenceClock pClock);
 862                 int GetSyncSource([In, Out] ref IReferenceClock pClock);
 863 
 864                 // -----
 865                 int EnumPins([In, Out] ref IEnumPins ppEnum);
 866                 int FindPin([In, MarshalAs(UnmanagedType.LPWStr)] string Id, [In, Out] ref IPin ppPin);
 867                 int QueryFilterInfo([Out] FILTER_INFO pInfo);
 868                 int JoinFilterGraph([In] IFilterGraph pGraph, [In, MarshalAs(UnmanagedType.LPWStr)] string pName);
 869                 int QueryVendorInfo([In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string pVendorInfo);
 870             }
 871 
 872 
 873             [ComVisible(true), ComImport(), Guid("56a86893-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 874             public interface IEnumFilters
 875             {
 876                 int Next([In] int cFilters, [In, Out] ref IBaseFilter ppFilter, [In, Out] ref int pcFetched);
 877                 int Skip([In] int cFilters);
 878                 void Reset();
 879                 void Clone([In, Out] ref IEnumFilters ppEnum);
 880             }
 881 
 882             [ComVisible(true), ComImport(), Guid("C6E13340-30AC-11d0-A18C-00A0C9118956"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 883             public interface IAMStreamConfig
 884             {
 885                 int SetFormat([In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 886                 int GetFormat([In, Out, MarshalAs(UnmanagedType.LPStruct)] ref AM_MEDIA_TYPE ppmt);
 887                 int GetNumberOfCapabilities(ref int piCount, ref int piSize);
 888                 int GetStreamCaps(int iIndex, [In, Out, MarshalAs(UnmanagedType.LPStruct)] ref AM_MEDIA_TYPE ppmt, IntPtr pSCC);
 889             }
 890 
 891             [ComVisible(true), ComImport(), Guid("56a8689a-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 892             public interface IMediaSample
 893             {
 894                 int GetPointer(ref IntPtr ppBuffer);
 895                 int GetSize();
 896                 int GetTime(ref long pTimeStart, ref long pTimeEnd);
 897                 int SetTime([In, MarshalAs(UnmanagedType.LPStruct)] UInt64 pTimeStart, [In, MarshalAs(UnmanagedType.LPStruct)] UInt64 pTimeEnd);
 898                 int IsSyncPoint();
 899                 int SetSyncPoint([In, MarshalAs(UnmanagedType.Bool)] bool bIsSyncPoint);
 900                 int IsPreroll();
 901                 int SetPreroll([In, MarshalAs(UnmanagedType.Bool)] bool bIsPreroll);
 902                 int GetActualDataLength();
 903                 int SetActualDataLength(int len);
 904                 int GetMediaType([In, Out, MarshalAs(UnmanagedType.LPStruct)] ref AM_MEDIA_TYPE ppMediaType);
 905                 int SetMediaType([In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pMediaType);
 906                 int IsDiscontinuity();
 907                 int SetDiscontinuity([In, MarshalAs(UnmanagedType.Bool)] bool bDiscontinuity);
 908                 int GetMediaTime(ref long pTimeStart, ref long pTimeEnd);
 909                 int SetMediaTime([In, MarshalAs(UnmanagedType.LPStruct)] UInt64 pTimeStart, [In, MarshalAs(UnmanagedType.LPStruct)] UInt64 pTimeEnd);
 910             }
 911 
 912             [ComVisible(true), ComImport(), Guid("89c31040-846b-11ce-97d3-00aa0055595a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 913             public interface IEnumMediaTypes
 914             {
 915                 int Next([In] int cMediaTypes, [In, Out, MarshalAs(UnmanagedType.LPStruct)] ref AM_MEDIA_TYPE ppMediaTypes, [In, Out] ref int pcFetched);
 916                 int Skip([In] int cMediaTypes);
 917                 int Reset();
 918                 int Clone([In, Out] ref IEnumMediaTypes ppEnum);
 919             }
 920 
 921             [ComVisible(true), ComImport(), Guid("56a86891-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 922             public interface IPin
 923             {
 924                 int Connect([In] IPin pReceivePin, [In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 925                 int ReceiveConnection([In] IPin pReceivePin, [In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 926                 int Disconnect();
 927                 int ConnectedTo([In, Out] ref IPin ppPin);
 928                 int ConnectionMediaType([Out, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 929                 int QueryPinInfo([Out] PIN_INFO pInfo);
 930                 int QueryDirection(ref PIN_DIRECTION pPinDir);
 931                 int QueryId([In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string Id);
 932                 int QueryAccept([In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 933                 int EnumMediaTypes([In, Out] ref IEnumMediaTypes ppEnum);
 934                 int QueryInternalConnections(IntPtr apPin, [In, Out] ref int nPin);
 935                 int EndOfStream();
 936                 int BeginFlush();
 937                 int EndFlush();
 938                 int NewSegment(long tStart, long tStop, double dRate);
 939             }
 940 
 941             [ComVisible(true), ComImport(), Guid("56a86892-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 942             public interface IEnumPins
 943             {
 944                 int Next([In] int cPins, [In, Out] ref IPin ppPins, [In, Out] ref int pcFetched);
 945                 int Skip([In] int cPins);
 946                 void Reset();
 947                 void Clone([In, Out] ref IEnumPins ppEnum);
 948             }
 949 
 950             [ComVisible(true), ComImport(), Guid("56a86897-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 951             public interface IReferenceClock
 952             {
 953                 int GetTime(ref long pTime);
 954                 int AdviseTime(long baseTime, long streamTime, IntPtr hEvent, ref int pdwAdviseCookie);
 955                 int AdvisePeriodic(long startTime, long periodTime, IntPtr hSemaphore, ref int pdwAdviseCookie);
 956                 int Unadvise(int dwAdviseCookie);
 957             }
 958 
 959             [ComVisible(true), ComImport(), Guid("29840822-5B84-11D0-BD3B-00A0C911CE86"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 960             public interface ICreateDevEnum
 961             {
 962                 int CreateClassEnumerator([In] ref Guid pType, [In, Out] ref System.Runtime.InteropServices.ComTypes.IEnumMoniker ppEnumMoniker, [In] int dwFlags);
 963             }
 964 
 965             [ComVisible(true), ComImport(), Guid("55272A00-42CB-11CE-8135-00AA004BB851"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 966             public interface IPropertyBag
 967             {
 968                 int Read([MarshalAs(UnmanagedType.LPWStr)] string PropName, ref object Var, int ErrorLog);
 969                 int Write(string PropName, ref object Var);
 970             }
 971 
 972             [ComVisible(true), ComImport(), Guid("6B652FFF-11FE-4fce-92AD-0266B5D7C78F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 973             public interface ISampleGrabber
 974             {
 975                 int SetOneShot([In, MarshalAs(UnmanagedType.Bool)] bool OneShot);
 976                 int SetMediaType([In, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 977                 int GetConnectedMediaType([Out, MarshalAs(UnmanagedType.LPStruct)] AM_MEDIA_TYPE pmt);
 978                 int SetBufferSamples([In, MarshalAs(UnmanagedType.Bool)] bool BufferThem);
 979                 int GetCurrentBuffer(ref int pBufferSize, IntPtr pBuffer);
 980                 int GetCurrentSample(IntPtr ppSample);
 981                 int SetCallback(ISampleGrabberCB pCallback, int WhichMethodToCallback);
 982             }
 983 
 984             [ComVisible(true), ComImport(), Guid("0579154A-2B53-4994-B0D0-E773148EFF85"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 985             public interface ISampleGrabberCB
 986             {
 987                 [PreserveSig()]
 988                 int SampleCB(double SampleTime, IMediaSample pSample);
 989                 [PreserveSig()]
 990                 int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen);
 991             }
 992 
 993             #endregion
 994 
 995 
 996             #region Structure
 997 
 998             [Serializable]
 999             [StructLayout(LayoutKind.Sequential), ComVisible(false)]
1000             public class AM_MEDIA_TYPE
1001             {
1002                 public Guid MajorType;
1003                 public Guid SubType;
1004                 [MarshalAs(UnmanagedType.Bool)]
1005                 public bool bFixedSizeSamples;
1006                 [MarshalAs(UnmanagedType.Bool)]
1007                 public bool bTemporalCompression;
1008                 public uint lSampleSize;
1009                 public Guid FormatType;
1010                 public IntPtr pUnk;
1011                 public uint cbFormat;
1012                 public IntPtr pbFormat;
1013             }
1014 
1015             [Serializable]
1016             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), ComVisible(false)]
1017             public class FILTER_INFO
1018             {
1019                 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
1020                 public string achName;
1021                 [MarshalAs(UnmanagedType.IUnknown)]
1022                 public object pGraph;
1023             }
1024 
1025             [Serializable]
1026             [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), ComVisible(false)]
1027             public class PIN_INFO
1028             {
1029                 public IBaseFilter pFilter;
1030                 public PIN_DIRECTION dir;
1031                 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
1032                 public string achName;
1033             }
1034 
1035             [Serializable]
1036             [StructLayout(LayoutKind.Sequential, Pack = 8), ComVisible(false)]
1037             public struct VIDEO_STREAM_CONFIG_CAPS
1038             {
1039                 public Guid Guid;
1040                 public uint VideoStandard;
1041                 public SIZE InputSize;
1042                 public SIZE MinCroppingSize;
1043                 public SIZE MaxCroppingSize;
1044                 public int CropGranularityX;
1045                 public int CropGranularityY;
1046                 public int CropAlignX;
1047                 public int CropAlignY;
1048                 public SIZE MinOutputSize;
1049                 public SIZE MaxOutputSize;
1050                 public int OutputGranularityX;
1051                 public int OutputGranularityY;
1052                 public int StretchTapsX;
1053                 public int StretchTapsY;
1054                 public int ShrinkTapsX;
1055                 public int ShrinkTapsY;
1056                 public long MinFrameInterval;
1057                 public long MaxFrameInterval;
1058                 public int MinBitsPerSecond;
1059                 public int MaxBitsPerSecond;
1060             }
1061 
1062             [Serializable]
1063             [StructLayout(LayoutKind.Sequential), ComVisible(false)]
1064             public struct VIDEOINFOHEADER
1065             {
1066                 public RECT SrcRect;
1067                 public RECT TrgRect;
1068                 public int BitRate;
1069                 public int BitErrorRate;
1070                 public long AvgTimePerFrame;
1071                 public BITMAPINFOHEADER bmiHeader;
1072             }
1073 
1074             [Serializable]
1075             [StructLayout(LayoutKind.Sequential), ComVisible(false)]
1076             public struct VIDEOINFOHEADER2
1077             {
1078                 public RECT SrcRect;
1079                 public RECT TrgRect;
1080                 public int BitRate;
1081                 public int BitErrorRate;
1082                 public long AvgTimePerFrame;
1083                 public int InterlaceFlags;
1084                 public int CopyProtectFlags;
1085                 public int PictAspectRatioX;
1086                 public int PictAspectRatioY;
1087                 public int ControlFlags; // or Reserved1
1088                 public int Reserved2;
1089                 public BITMAPINFOHEADER bmiHeader;
1090             }
1091 
1092             [Serializable]
1093             [StructLayout(LayoutKind.Sequential, Pack = 2), ComVisible(false)]
1094             public struct BITMAPINFOHEADER
1095             {
1096                 public int biSize;
1097                 public int biWidth;
1098                 public int biHeight;
1099                 public short biPlanes;
1100                 public short biBitCount;
1101                 public int biCompression;
1102                 public int biSizeImage;
1103                 public int biXPelsPerMeter;
1104                 public int biYPelsPerMeter;
1105                 public int biClrUsed;
1106                 public int biClrImportant;
1107             }
1108 
1109             [Serializable]
1110             [StructLayout(LayoutKind.Sequential), ComVisible(false)]
1111             public struct WAVEFORMATEX
1112             {
1113                 public ushort wFormatTag;
1114                 public ushort nChannels;
1115                 public uint nSamplesPerSec;
1116                 public uint nAvgBytesPerSec;
1117                 public short nBlockAlign;
1118                 public short wBitsPerSample;
1119                 public short cbSize;
1120             }
1121 
1122             [Serializable]
1123             [StructLayout(LayoutKind.Sequential, Pack = 8), ComVisible(false)]
1124             public struct SIZE
1125             {
1126                 public int cx;
1127                 public int cy;
1128                 public override string ToString() { return string.Format("{{{0}, {1}}}", cx, cy); } // for debugging.
1129             }
1130 
1131             [Serializable]
1132             [StructLayout(LayoutKind.Sequential), ComVisible(false)]
1133             public struct RECT
1134             {
1135                 public int Left;
1136                 public int Top;
1137                 public int Right;
1138                 public int Bottom;
1139                 public override string ToString() { return string.Format("{{{0}, {1}, {2}, {3}}}", Left, Top, Right, Bottom); } // for debugging.
1140             }
1141             #endregion
1142 
1143 
1144             #region Enum
1145 
1146             [ComVisible(false)]
1147             public enum PIN_DIRECTION
1148             {
1149                 PINDIR_INPUT = 0,
1150                 PINDIR_OUTPUT = 1,
1151             }
1152 
1153             [ComVisible(false)]
1154             public enum FILTER_STATE : int
1155             {
1156                 Stopped = 0,
1157                 Paused = 1,
1158                 Running = 2,
1159             }
1160 
1161             [ComVisible(false)]
1162             public enum CameraControlProperty
1163             {
1164                 Pan = 0,
1165                 Tilt = 1,
1166                 Roll = 2,
1167                 Zoom = 3,
1168                 Exposure = 4,
1169                 Iris = 5,
1170                 Focus = 6,
1171             }
1172 
1173             [ComVisible(false), Flags()]
1174             public enum CameraControlFlags
1175             {
1176                 Auto = 0x0001,
1177                 Manual = 0x0002,
1178             }
1179 
1180             [ComVisible(false)]
1181             public enum VideoProcAmpProperty
1182             {
1183                 Brightness = 0,
1184                 Contrast = 1,
1185                 Hue = 2,
1186                 Saturation = 3,
1187                 Sharpness = 4,
1188                 Gamma = 5,
1189                 ColorEnable = 6,
1190                 WhiteBalance = 7,
1191                 BacklightCompensation = 8,
1192                 Gain = 9
1193             }
1194 
1195             #endregion
1196 
1197 
1198             #region Guid
1199 
1200             public static class DsGuid
1201             {
1202                 // MediaType
1203                 public static readonly Guid MEDIATYPE_Video = new Guid("{73646976-0000-0010-8000-00AA00389B71}");
1204                 public static readonly Guid MEDIATYPE_Audio = new Guid("{73647561-0000-0010-8000-00AA00389B71}");
1205 
1206                 // SubType
1207                 public static readonly Guid MEDIASUBTYPE_None = new Guid("{E436EB8E-524F-11CE-9F53-0020AF0BA770}");
1208                 public static readonly Guid MEDIASUBTYPE_YUYV = new Guid("{56595559-0000-0010-8000-00AA00389B71}");
1209                 public static readonly Guid MEDIASUBTYPE_IYUV = new Guid("{56555949-0000-0010-8000-00AA00389B71}");
1210                 public static readonly Guid MEDIASUBTYPE_YVU9 = new Guid("{39555659-0000-0010-8000-00AA00389B71}");
1211                 public static readonly Guid MEDIASUBTYPE_YUY2 = new Guid("{32595559-0000-0010-8000-00AA00389B71}");
1212                 public static readonly Guid MEDIASUBTYPE_YVYU = new Guid("{55595659-0000-0010-8000-00AA00389B71}");
1213                 public static readonly Guid MEDIASUBTYPE_UYVY = new Guid("{59565955-0000-0010-8000-00AA00389B71}");
1214                 public static readonly Guid MEDIASUBTYPE_MJPG = new Guid("{47504A4D-0000-0010-8000-00AA00389B71}");
1215                 public static readonly Guid MEDIASUBTYPE_RGB565 = new Guid("{E436EB7B-524F-11CE-9F53-0020AF0BA770}");
1216                 public static readonly Guid MEDIASUBTYPE_RGB555 = new Guid("{E436EB7C-524F-11CE-9F53-0020AF0BA770}");
1217                 public static readonly Guid MEDIASUBTYPE_RGB24 = new Guid("{E436EB7D-524F-11CE-9F53-0020AF0BA770}");
1218                 public static readonly Guid MEDIASUBTYPE_RGB32 = new Guid("{E436EB7E-524F-11CE-9F53-0020AF0BA770}");
1219                 public static readonly Guid MEDIASUBTYPE_ARGB32 = new Guid("{773C9AC0-3274-11D0-B724-00AA006C1A01}");
1220                 public static readonly Guid MEDIASUBTYPE_PCM = new Guid("{00000001-0000-0010-8000-00AA00389B71}");
1221                 public static readonly Guid MEDIASUBTYPE_WAVE = new Guid("{E436EB8B-524F-11CE-9F53-0020AF0BA770}");
1222 
1223                 // FormatType
1224                 public static readonly Guid FORMAT_None = new Guid("{0F6417D6-C318-11D0-A43F-00A0C9223196}");
1225                 public static readonly Guid FORMAT_VideoInfo = new Guid("{05589F80-C356-11CE-BF01-00AA0055595A}");
1226                 public static readonly Guid FORMAT_VideoInfo2 = new Guid("{F72A76A0-EB0A-11d0-ACE4-0000C0CC16BA}");
1227                 public static readonly Guid FORMAT_WaveFormatEx = new Guid("{05589F81-C356-11CE-BF01-00AA0055595A}");
1228 
1229                 // CLSID
1230                 public static readonly Guid CLSID_AudioInputDeviceCategory = new Guid("{33D9A762-90C8-11d0-BD43-00A0C911CE86}");
1231                 public static readonly Guid CLSID_AudioRendererCategory = new Guid("{E0F158E1-CB04-11d0-BD4E-00A0C911CE86}");
1232                 public static readonly Guid CLSID_VideoInputDeviceCategory = new Guid("{860BB310-5D01-11d0-BD3B-00A0C911CE86}");
1233                 public static readonly Guid CLSID_VideoCompressorCategory = new Guid("{33D9A760-90C8-11d0-BD43-00A0C911CE86}");
1234 
1235                 public static readonly Guid CLSID_NullRenderer = new Guid("{C1F400A4-3F08-11D3-9F0B-006008039E37}");
1236                 public static readonly Guid CLSID_SampleGrabber = new Guid("{C1F400A0-3F08-11D3-9F0B-006008039E37}");
1237 
1238                 public static readonly Guid CLSID_FilterGraph = new Guid("{E436EBB3-524F-11CE-9F53-0020AF0BA770}");
1239                 public static readonly Guid CLSID_SystemDeviceEnum = new Guid("{62BE5D10-60EB-11d0-BD3B-00A0C911CE86}");
1240                 public static readonly Guid CLSID_CaptureGraphBuilder2 = new Guid("{BF87B6E1-8C27-11d0-B3F0-00AA003761C5}");
1241 
1242                 public static readonly Guid IID_IPropertyBag = new Guid("{55272A00-42CB-11CE-8135-00AA004BB851}");
1243                 public static readonly Guid IID_IBaseFilter = new Guid("{56a86895-0ad4-11ce-b03a-0020af0ba770}");
1244                 public static readonly Guid IID_IAMStreamConfig = new Guid("{C6E13340-30AC-11d0-A18C-00A0C9118956}");
1245 
1246                 public static readonly Guid PIN_CATEGORY_CAPTURE = new Guid("{fb6c4281-0353-11d1-905f-0000c0cc16ba}");
1247                 public static readonly Guid PIN_CATEGORY_PREVIEW = new Guid("{fb6c4282-0353-11d1-905f-0000c0cc16ba}");
1248                 public static readonly Guid PIN_CATEGORY_STILL = new Guid("{fb6c428a-0353-11d1-905f-0000c0cc16ba}");
1249 
1250                 private static Dictionary<Guid, string> NicknameCache = null;
1251 
1252                 public static string GetNickname(Guid guid)
1253                 {
1254                     if (NicknameCache == null)
1255                     {
1256                         NicknameCache = typeof(DsGuid).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
1257                             .Where(x => x.FieldType == typeof(Guid))
1258                             .ToDictionary(x => (Guid)x.GetValue(null), x => x.Name);
1259                     }
1260 
1261                     if (NicknameCache.ContainsKey(guid))
1262                     {
1263                         var name = NicknameCache[guid];
1264                         var elem = name.Split('_');
1265 
1266                         if (elem.Length >= 2)
1267                         {
1268                             var text = string.Join("_", elem.Skip(1).ToArray());
1269                             return string.Format("[{0}]", text);
1270                         }
1271                         else
1272                         {
1273                             return name;
1274                         }
1275                     }
1276 
1277                     return guid.ToString();
1278                 }
1279             }
1280             #endregion
1281         }
1282     }
View Code

使用非常简单: 

 1     //获取所有的摄像头
 2     string[] devices = UsbCamera.FindDevices();
 3 
 4     //获取摄像头支持的分辨率
 5     int cameraIndex = 0;
 6     UsbCamera.VideoFormat[] formats = UsbCamera.GetVideoFormat(cameraIndex);
 7     for (int i = 0; i < formats.Length; i++) 
 8         Console.WriteLine("{0}:{1}", i, formats[i]);
 9 
10     // create usb camera and start.
11     using var camera = new UsbCamera(cameraIndex, formats[0]);
12     camera.Start();
13 
14     //第一次截图不延迟的话,会出现黑屏
15     await Task.Delay(100);
16     var bmp = camera.GetBitmap();
17     bmp.Save(@$"r:	est.jpg");

这个代码本身也是封装了direct show,但它只封装了摄像头相关的部分,只有1k多行,可以直接嵌入到项目中。 另外,这个截图本身不依赖于STA线程的,可以非常方便的封装成一个远程usb拍照的功能的,也可以供web使用。 如果不太要求效果的话,做个远程的摄像头也是可以的。

原文地址:https://www.cnblogs.com/TianFang/p/14192823.html