using DirectShowLib; using System; using System.Runtime.InteropServices; namespace DualScreenDemo.Services { public class MediaServicePrimary { IGraphBuilder graphBuilder; IBaseFilter sourceFilter; IBaseFilter lavSplitter; IBaseFilter lavVideoDecoder; IBaseFilter videoRenderer; IVideoWindow videoWindow; IMediaControl mediaControl; public MediaServicePrimary(){} public int Run() { if (mediaControl != null) return mediaControl.Run(); else return 0; } public int Stop() { if (mediaControl != null) return mediaControl.Stop(); else return 0; } public int Pause() { if (mediaControl != null) return mediaControl.Pause(); else return 0; } public void VideoPlayerFormClosing() { if (videoWindow != null) { videoWindow.put_Visible(OABool.False); videoWindow.put_Owner(IntPtr.Zero); Marshal.ReleaseComObject(videoWindow); videoWindow = null; } } public void StopAndReleaseResources() { if (mediaControl != null) { mediaControl.Stop(); SafeRelease(ref mediaControl); } SafeRelease(ref videoWindow); SafeRelease(ref videoRenderer); SafeRelease(ref sourceFilter); SafeRelease(ref lavVideoDecoder); SafeRelease(ref lavSplitter); SafeRelease(ref graphBuilder); } public void RenderMediaFile(string filePath, nint Handle, int Width, int Height) { StopAndReleaseResources(); if (videoWindow != null) videoWindow.put_Visible(OABool.False); int hr; graphBuilder = (IGraphBuilder)new FilterGraph(); if (graphBuilder == null) throw new Exception("Failed to create FilterGraph for primary monitor."); graphBuilder.AddSourceFilter(filePath, "Source", out sourceFilter); try { lavSplitter = AddFilterByClsid(graphBuilder, "LAV Splitter", Clsid.LAVSplitter); lavVideoDecoder = AddFilterByClsid(graphBuilder, "LAV Video Decoder", Clsid.LAVVideoDecoder); videoRenderer = AddFilterByClsid(graphBuilder, "Primary Video Renderer", Clsid.VideoRenderer); hr = graphBuilder.AddFilter(videoRenderer, "Primary Video Renderer"); DsError.ThrowExceptionForHR(hr); mediaControl = (IMediaControl)graphBuilder; if (mediaControl == null) { Console.WriteLine("Failed to get Media Control for primary monitor."); return; } hr = ConnectFilters(graphBuilder, sourceFilter, "Output", lavSplitter, "Input"); DsError.ThrowExceptionForHR(hr); hr = ConnectFilters(graphBuilder, lavSplitter, "Video", lavVideoDecoder, "Input"); DsError.ThrowExceptionForHR(hr); hr = ConnectFilters(graphBuilder, lavVideoDecoder, "Output", videoRenderer, "VMR Input0"); DsError.ThrowExceptionForHR(hr); try { IVideoWindow videoWindow = (IVideoWindow)videoRenderer; videoWindow.put_Owner(Handle); videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren); // ⬅ 左上角對齊(固定從 0,0 開始) videoWindow.SetWindowPosition(0, 0, Width, Height); videoWindow.put_Visible(OABool.True); // Console.WriteLine("Video window configured successfully."); } catch (Exception ex) { Console.WriteLine(String.Format("Error syncing to primary monitor: {0}", ex.Message)); MessageBox.Show(String.Format("Error syncing to primary monitor: {0}", ex.Message)); } } catch (Exception ex) { Console.WriteLine("Error initializing graph builder for primary monitor: " + ex.Message); } } private static IBaseFilter AddFilterByClsid(IGraphBuilder graphBuilder, string name, Guid clsid) { try { // 获取 CLSID 对应的类型 Type filterType = Type.GetTypeFromCLSID(clsid); //Console.WriteLine($"Attempting to create filter of type: {filterType.FullName}"); // 创建实例 object filterObject = Activator.CreateInstance(filterType); // 尝试转换为 IBaseFilter IBaseFilter filter = filterObject as IBaseFilter; if (filter == null) { // 如果转换失败,使用 IUnknown 获取并转换为 IBaseFilter IntPtr comObjectPointer = Marshal.GetIUnknownForObject(filterObject); filter = (IBaseFilter)Marshal.GetObjectForIUnknown(comObjectPointer); // Console.WriteLine($"Successfully converted COM object to IBaseFilter via IUnknown."); } else { // Console.WriteLine($"Successfully created IBaseFilter directly."); } // 添加过滤器到图形构建器 int hr = graphBuilder.AddFilter(filter, name); if (hr != 0) { // Console.WriteLine($"Failed to add filter {name} with CLSID {clsid}, HRESULT: {hr}"); } DsError.ThrowExceptionForHR(hr); // Console.WriteLine($"Successfully added filter {name} with CLSID {clsid}"); return filter; } catch (Exception ex) { Console.WriteLine($"Exception in AddFilterByClsid: {ex.Message}"); throw; // Rethrow the exception to handle it further up the call stack } } private int ConnectFilters(IGraphBuilder graphBuilder, IBaseFilter sourceFilter, string sourcePinName, IBaseFilter destFilter, string destPinName) { IPin outPin = FindPin(sourceFilter, sourcePinName); IPin inPin = FindPin(destFilter, destPinName); if (outPin == null || inPin == null) { Console.WriteLine(String.Format("Cannot find pins: {0} or {1}", sourcePinName, destPinName)); return -1; } int hr = graphBuilder.Connect(outPin, inPin); return hr; } private IPin FindPin(IBaseFilter filter, string pinName) { IEnumPins enumPins; IPin[] pins = new IPin[1]; filter.EnumPins(out enumPins); enumPins.Reset(); while (enumPins.Next(1, pins, IntPtr.Zero) == 0) { PinInfo pinInfo; pins[0].QueryPinInfo(out pinInfo); Console.WriteLine(pinInfo); if (pinInfo.name == pinName) { return pins[0]; } } return null; } private void SafeRelease(ref T comObject) where T : class { if (comObject != null) { Marshal.ReleaseComObject(comObject); comObject = null; } } } }