123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- using DirectShowLib;
- using System;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Threading;
- using System.Threading.Tasks;
- using Vinno.FIS.TRTCClient.Common.Enum;
- using Vinno.FIS.TRTCClient.Common.Log;
- namespace Vinno.FIS.TRTCClient.Captures
- {
- internal class DirectShowCapturer : ISampleGrabberCB
- {
- private IMediaControl mediaControl = null;
- private IFilterGraph2 graphBuilder = null;
- private ICaptureGraphBuilder2 captureGraphBuilder = null;
- private DsROTEntry rot = null;
- private TRTCImageFrameData _imageData;
- private int _fps;
- private string _id;
- private readonly int _outputWidth;
- private readonly int _outputHeight;
- private EnumLiveChannelCategory _liveChannelCategory;
- private readonly ManualResetEvent _handlingImageEvent = new ManualResetEvent(true);
- private bool _isCapturing;
- public string Id => _id;
- public int OutputWidth => _outputWidth;
- public int OutputHeight => _outputHeight;
- public bool IsCapturing => _isCapturing;
- public event EventHandler<TRTCImageFrameData> ImageFrameReceived;
- public DirectShowCapturer(string id, int width, int height, int fps, EnumLiveChannelCategory liveChannelCategory)
- {
- _id = id;
- _fps = fps;
- _outputWidth = width;
- _outputHeight = height;
- _liveChannelCategory = liveChannelCategory;
- _imageData = new TRTCImageFrameData(width, height);
- Logger.WriteLineInfo($"DirectShowCapturer {_id}: Resolution:{width} {height} {liveChannelCategory}");
- }
- public void StartCapture()
- {
- if (_isCapturing)
- {
- Logger.WriteLineWarn($"DirectShowCapturer {_id}: Has Been In Capturing!");
- return;
- }
- var device = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice).FirstOrDefault(x => x.DevicePath.Contains(_id) || _id.Contains(x.DevicePath));
- if (device != null)
- {
- if (CaptureVideo(device))
- {
- _isCapturing = true;
- return;
- }
- else
- {
- _isCapturing = false;
- return;
- }
- }
- _isCapturing = false;
- Logger.WriteLineError($"DirectShowCapturer {_id}:Can not find device {_id},Start Capture Error!");
- }
- public void StopCapture()
- {
- try
- {
- if (_isCapturing)
- {
- CloseInterfaces();
- _isCapturing = false;
- }
- }
- catch (Exception e)
- {
- Logger.WriteLineError($"DirectShowCapturer {_id}:Stop Capture Error:{e}");
- }
- }
- private bool CaptureVideo(DsDevice device)
- {
- int step = 0;
- int hr = 0;
- IBaseFilter sourceFilter = null;
- ISampleGrabber sampleGrabber = null;
- try
- {
- // Get DirectShow interfaces
- GetInterfaces();
- // Attach the filter graph to the capture graph
- hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder);
- DsError.ThrowExceptionForHR(hr);
- step++;
- // Use the system device enumerator and class enumerator to find
- // a video capture/preview device, such as a desktop USB video camera.
- sourceFilter = SelectCaptureDevice(device);
- // Add Capture filter to graph.
- hr = this.graphBuilder.AddFilter(sourceFilter, "Video Capture");
- DsError.ThrowExceptionForHR(hr);
- step++;
- // Initialize SampleGrabber.
- sampleGrabber = new SampleGrabber() as ISampleGrabber;
- // Configure SampleGrabber. Add preview callback.
- ConfigureSampleGrabber(sampleGrabber);
- // Add SampleGrabber to graph.
- hr = this.graphBuilder.AddFilter(sampleGrabber as IBaseFilter, "Frame Callback");
- DsError.ThrowExceptionForHR(hr);
- step++;
- // Configure preview settings.
- SetConfigParams(this.captureGraphBuilder, sourceFilter, _fps, _imageData.Width, _imageData.Height);
- // Render the preview
- hr = this.captureGraphBuilder.RenderStream(PinCategory.Capture, MediaType.Video, sourceFilter, null, (sampleGrabber as IBaseFilter));
- DsError.ThrowExceptionForHR(hr);
- step++;
- // Add our graph to the running object table, which will allow
- // the GraphEdit application to "spy" on our graph
- rot = new DsROTEntry(this.graphBuilder);
- // Start previewing video data
- hr = this.mediaControl.Run();
- DsError.ThrowExceptionForHR(hr);
- step++;
- return true;
- }
- catch (Exception e)
- {
- Logger.WriteLineError($"DirectShowCapturer:An unrecoverable error has occurred.Step:{step},Error:{hr},{e}");
- return false;
- }
- finally
- {
- if (sourceFilter != null)
- {
- Marshal.ReleaseComObject(sourceFilter);
- sourceFilter = null;
- }
- if (sampleGrabber != null)
- {
- Marshal.ReleaseComObject(sampleGrabber);
- sampleGrabber = null;
- }
- }
- }
- private IBaseFilter SelectCaptureDevice(DsDevice device)
- {
- object source = null;
- Guid iid = typeof(IBaseFilter).GUID;
- device.Mon.BindToObject(null, null, ref iid, out source);
- return (IBaseFilter)source;
- }
- private void GetInterfaces()
- {
- int hr = 0;
- // An exception is thrown if cast fail
- this.graphBuilder = (IFilterGraph2)new FilterGraph();
- this.captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
- this.mediaControl = (IMediaControl)this.graphBuilder;
- // this.videoWindow = (IVideoWindow)this.graphBuilder;
- DsError.ThrowExceptionForHR(hr);
- }
- private void CloseInterfaces()
- {
- if (mediaControl != null)
- {
- int hr = mediaControl.StopWhenReady();
- DsError.ThrowExceptionForHR(hr);
- }
- // Remove filter graph from the running object table.
- if (rot != null)
- {
- rot.Dispose();
- rot = null;
- }
- // Release DirectShow interfaces.
- if (mediaControl != null)
- {
- Marshal.ReleaseComObject(mediaControl);
- mediaControl = null;
- }
- if (graphBuilder != null)
- {
- Marshal.ReleaseComObject(graphBuilder);
- graphBuilder = null;
- }
- if (captureGraphBuilder != null)
- {
- Marshal.ReleaseComObject(captureGraphBuilder);
- captureGraphBuilder = null;
- }
- }
- private void SetConfigParams(ICaptureGraphBuilder2 capGraph, IBaseFilter capFilter, int iFrameRate, int iWidth, int iHeight)
- {
- int hr;
- object config;
- AMMediaType mediaType;
- // Find the stream config interface
- hr = capGraph.FindInterface(
- PinCategory.Capture, MediaType.Video, capFilter, typeof(IAMStreamConfig).GUID, out config);
- IAMStreamConfig videoStreamConfig = config as IAMStreamConfig;
- if (videoStreamConfig == null)
- {
- throw new Exception("Failed to get IAMStreamConfig");
- }
- // Get the existing format block
- hr = videoStreamConfig.GetFormat(out mediaType);
- DsError.ThrowExceptionForHR(hr);
- // copy out the videoinfoheader
- VideoInfoHeader videoInfoHeader = new VideoInfoHeader();
- Marshal.PtrToStructure(mediaType.formatPtr, videoInfoHeader);
- // if overriding the framerate, set the frame rate
- if (iFrameRate > 0)
- {
- videoInfoHeader.AvgTimePerFrame = 10000000 / iFrameRate;
- }
- // if overriding the width, set the width
- if (iWidth > 0)
- {
- videoInfoHeader.BmiHeader.Width = iWidth;
- }
- // if overriding the Height, set the Height
- if (iHeight > 0)
- {
- videoInfoHeader.BmiHeader.Height = iHeight;
- }
- // Copy the media structure back
- Marshal.StructureToPtr(videoInfoHeader, mediaType.formatPtr, false);
- // Set the new format
- hr = videoStreamConfig.SetFormat(mediaType);
- DsError.ThrowExceptionForHR(hr);
- DsUtils.FreeAMMediaType(mediaType);
- mediaType = null;
- }
- private void ConfigureSampleGrabber(ISampleGrabber sampleGrabber)
- {
- AMMediaType media;
- int hr;
- // Set the media type to Video/ARGB32
- media = new AMMediaType();
- media.majorType = MediaType.Video;
- media.subType = MediaSubType.ARGB32;
- media.formatType = FormatType.VideoInfo;
- hr = sampleGrabber.SetMediaType(media);
- DsError.ThrowExceptionForHR(hr);
- DsUtils.FreeAMMediaType(media);
- media = null;
- hr = sampleGrabber.SetCallback(this, 1);
- DsError.ThrowExceptionForHR(hr);
- }
- public int SampleCB(double SampleTime, IMediaSample pSample)
- {
- return 0;
- }
- public unsafe int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
- {
- try
- {
- _handlingImageEvent.Reset();
- //上下翻转
- var stride = 4 * _outputWidth;
- var height = _imageData.Height;
- var tmp = IntPtr.Add(pBuffer, stride * (height - 1));
- Parallel.For(0, height, row =>
- {
- Buffer.MemoryCopy(IntPtr.Subtract(tmp, row * stride).ToPointer(), IntPtr.Add(_imageData.Data, row * stride).ToPointer(), stride, stride);
- });
- ImageFrameReceived?.Invoke(this, _imageData);
- return 0;
- }
- catch (Exception ex)
- {
- Logger.WriteLineError($"DirectShowCapturer BufferCB Error:{ex}");
- return 0;
- }
- finally
- {
- _handlingImageEvent.Set();
- }
- }
- public void Dispose()
- {
- StopCapture();
- _handlingImageEvent.WaitOne();
- _imageData?.Dispose();
- }
- }
- }
|