210 lines
7.6 KiB
C#
210 lines
7.6 KiB
C#
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<T>(ref T comObject) where T : class
|
|
{
|
|
if (comObject != null)
|
|
{
|
|
Marshal.ReleaseComObject(comObject);
|
|
comObject = null;
|
|
}
|
|
}
|
|
}
|
|
} |