最近想把之前一个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 }
使用非常简单:
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:\test.jpg");
这个代码本身也是封装了direct show,但它只封装了摄像头相关的部分,只有1k多行,可以直接嵌入到项目中。 另外,这个截图本身不依赖于STA线程的,可以非常方便的封装成一个远程usb拍照的功能的,也可以供web使用。 如果不太要求效果的话,做个远程的摄像头也是可以的。