diff --git a/Program.cs b/Program.cs index 6d9f035..6af3512 100644 --- a/Program.cs +++ b/Program.cs @@ -13,7 +13,7 @@ namespace DualScreenDemo private static PrimaryForm primaryForm; // 儲存實例的參考 public static Room room = new Room(); - public static string verSion = "Server V2.7 202508041725"; + public static string verSion = "Server V2.8 202508071811"; [STAThread] static void Main() @@ -82,9 +82,6 @@ namespace DualScreenDemo // 显示 videoPlayerForm 在第二显示器 primaryForm.videoPlayerForm.Show(); - - // 初始化公共播放列表 - primaryForm.videoPlayerForm.PlayNextSong(); } } primaryForm.Show(); diff --git a/Room.cs b/Room.cs index 2f76870..92591d5 100644 --- a/Room.cs +++ b/Room.cs @@ -65,22 +65,26 @@ namespace DualScreenDemo } public void set(string value) { - State =getDB(); - Console.WriteLine($"hostname status: {hostName},{State},{startedAt},{endedAt}"); + string StateDB=getDB(); + + Console.WriteLine($"hostname status: {hostName},{StateDB},{startedAt},{endedAt} ,{State}"); string marqueeMessage = "歡迎使用超級巨星歡唱,與你共度美好時光。"; Color c = Color.White; - if (State.Equals("fire")) + if (StateDB.Equals("fire")) { PrimaryForm.Instance.ShowSendOffScreen(); VideoPlayerForm.Instance.Pause(); marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!"; c = Color.Red; } - else if (State.Equals("active")) + else if (StateDB.Equals("active")) { - DBObj.SongList.clearSong(); - PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty); - PrimaryForm.Instance.HideSendOffScreen(); + if (!State.Equals(StateDB)) + { + DBObj.SongList.clearSong(); + PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty); + PrimaryForm.Instance.HideSendOffScreen(); + } } else { diff --git a/Services/MediaService.cs b/Services/MediaService.cs new file mode 100644 index 0000000..dc19cd5 --- /dev/null +++ b/Services/MediaService.cs @@ -0,0 +1,370 @@ +using DirectShowLib; +using System; +using System.Runtime.InteropServices; + +namespace DualScreenDemo.Services +{ + public class MediaService + { + public IGraphBuilder graphBuilder; + private IMediaControl mediaControl; + private IBaseFilter videoRenderer; + private IBaseFilter lavSplitter; + private IBaseFilter lavVideoDecoder; + private IBaseFilter lavAudioDecoder; + private IPin outputPin; + private IBaseFilter audioRenderer; + private IVideoWindow videoWindow; + + + 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 lavAudioDecoder); + SafeRelease(ref outputPin); + SafeRelease(ref lavVideoDecoder); + SafeRelease(ref lavSplitter); + SafeRelease(ref graphBuilder); + + } + public bool isAtEnd() + { + bool isAtEnd = false; + if (mediaControl == null) { return false; } + IMediaSeeking mediaSeeking = graphBuilder as IMediaSeeking; + if (mediaSeeking != null) + { + long currentPosition; + long duration; + if (mediaSeeking.GetCurrentPosition(out currentPosition) >= 0 && + mediaSeeking.GetDuration(out duration) >= 0) + { + double currentSeconds = currentPosition / 10000000.0; + double durationSeconds = duration / 10000000.0; + + // 添加更严格的结束条件判断 + isAtEnd = durationSeconds > 0 && currentSeconds > 0 && + Math.Abs(currentSeconds - durationSeconds) < 0.1; // 确保真的到了结尾 + //Console.WriteLine($"檢測到歌曲 - 當前位置: {currentSeconds:F2}秒, 總時長: {durationSeconds:F2}秒"); + if (isAtEnd) + { + Console.WriteLine($"檢測到歌曲 -結束: {currentSeconds:F2}秒, 總時長: {durationSeconds:F2}秒"); + } + } + } + return isAtEnd; + } + public void RenderMediaFile(string filePath, nint Handle, int Width, int Height) + { + StopAndReleaseResources(); + if (videoWindow != null) videoWindow.put_Visible(OABool.False); + int hr = 0; + graphBuilder = (IGraphBuilder)new FilterGraph(); + if (graphBuilder == null) + { + Console.WriteLine("Failed to create FilterGraph"); + throw new Exception("Failed to create FilterGraph"); + } + try + { + lavSplitter = AddFilterByClsid(graphBuilder, "LAV Splitter", Clsid.LAVSplitter); + lavVideoDecoder = AddFilterByClsid(graphBuilder, "LAV Video Decoder", Clsid.LAVVideoDecoder); + lavAudioDecoder = AddFilterByClsid(graphBuilder, "LAV Audio Decoder", Clsid.LAVAudioDecoder); + outputPin = FindPin(lavAudioDecoder, "Output"); + videoRenderer = AddFilterByClsid(graphBuilder, "Secondary Video Renderer", Clsid.VideoRenderer); + if (videoRenderer == null) + { + Console.WriteLine("Failed to initialize Secondary Video Renderer."); + return; + } + hr = graphBuilder.AddFilter(videoRenderer, "Secondary Video Renderer"); + DsError.ThrowExceptionForHR(hr); + var clsidAudioRenderer = new Guid("79376820-07D0-11CF-A24D-0020AFD79767"); // CLSID for DirectSound Renderer + audioRenderer = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(clsidAudioRenderer)); + hr = graphBuilder.AddFilter(audioRenderer, "Default DirectSound Device"); + DsError.ThrowExceptionForHR(hr); + + mediaControl = (IMediaControl)graphBuilder; + if (mediaControl == null) + { + Console.WriteLine("Failed to get Media Control"); + return; + } + } + catch (Exception ex) + { + Console.WriteLine("Error initializing graph builder with second monitor: " + ex.Message); + } + hr = graphBuilder.RenderFile(filePath, null); + if (hr == 0) + { + Console.WriteLine("Secondary File rendered successfully."); + SetAudioTrackTo(1); + } + else + { + Console.WriteLine("Failed to render secondary file."); + } + // 绑定视频窗口到副屏幕 + videoWindow = videoRenderer as IVideoWindow; + if (videoWindow != null) + { + videoWindow.put_Owner(Handle); // 副屏幕窗口句柄 + videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings); + videoWindow.SetWindowPosition(0, 0, Width, Height); + videoWindow.put_Visible(OABool.True); // 显示窗口 + } + } + + 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 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; + } + public void SetVolume(int volume) + { + if (audioRenderer != null) + { + IBasicAudio basicAudio = audioRenderer as IBasicAudio; + if (basicAudio != null) + { + basicAudio.put_Volume(volume); + } + } + } + public int GetVolume() + { + if (audioRenderer != null) + { + IBasicAudio basicAudio = audioRenderer as IBasicAudio; + if (basicAudio != null) + { + int volume; + basicAudio.get_Volume(out volume); + return volume; + } + } + return -10000; + } + private bool isVocalRemoved = false; + public bool ToggleVocalRemoval() + { + try + { + IAMStreamSelect streamSelect = lavSplitter as IAMStreamSelect; + + if (streamSelect != null) + { + int trackCount; + if (streamSelect.Count(out trackCount) == 0 && trackCount > 0) + { + int currentTrackIndex = -1; + int audioTrack1 = -1; + int audioTrack2 = -1; + + for (int i = 0; i < trackCount; i++) + { + // 獲取音軌信息 + AMMediaType mediaType; + AMStreamSelectInfoFlags flags; + int lcid, dwGroup; + string name; + object pObject, pUnk; + + streamSelect.Info(i, out mediaType, out flags, out lcid, out dwGroup, out name, out pObject, out pUnk); + + if (mediaType.majorType == MediaType.Audio) + { + if (audioTrack1 == -1) + { + audioTrack1 = i; + } + else if (audioTrack2 == -1) + { + audioTrack2 = i; + } + + if ((flags & AMStreamSelectInfoFlags.Enabled) != 0) + { + currentTrackIndex = i; + } + } + + DsUtils.FreeAMMediaType(mediaType); + } + + // 切換音軌 + if (currentTrackIndex == audioTrack1 && audioTrack2 != -1) + { + streamSelect.Enable(audioTrack2, AMStreamSelectEnableFlags.Enable); + isVocalRemoved = true; + } + else if (currentTrackIndex == audioTrack2 && audioTrack1 != -1) + { + streamSelect.Enable(audioTrack1, AMStreamSelectEnableFlags.Enable); + isVocalRemoved = false; + } + + + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + return isVocalRemoved; + } + public void SetAudioTrackTo(int trackIndex) + { + try + { + IAMStreamSelect streamSelect = lavSplitter as IAMStreamSelect; + + if (streamSelect != null) + { + int trackCount; + if (streamSelect.Count(out trackCount) == 0 && trackCount > 0) + { + int audioTrackIndex = -1; + + for (int i = 0; i < trackCount; i++) + { + AMMediaType mediaType; + AMStreamSelectInfoFlags flags; + int lcid, dwGroup; + string name; + object pObject, pUnk; + + streamSelect.Info(i, out mediaType, out flags, out lcid, out dwGroup, out name, out pObject, out pUnk); + + if (mediaType.majorType == MediaType.Audio) + { + audioTrackIndex++; + if (audioTrackIndex == trackIndex) + { + streamSelect.Enable(i, AMStreamSelectEnableFlags.Enable); + } + else + { + streamSelect.Enable(i, AMStreamSelectEnableFlags.DisableAll); + } + } + + DsUtils.FreeAMMediaType(mediaType); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + private void SafeRelease(ref T comObject) where T : class + { + if (comObject != null) + { + Marshal.ReleaseComObject(comObject); + comObject = null; + } + } + } +} \ No newline at end of file diff --git a/Services/MediaServicePrimary.cs b/Services/MediaServicePrimary.cs new file mode 100644 index 0000000..e1855ff --- /dev/null +++ b/Services/MediaServicePrimary.cs @@ -0,0 +1,210 @@ +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; + } + } + } +} \ No newline at end of file diff --git a/VideoPlayerForm.cs b/VideoPlayerForm.cs index 2ac9724..e19d116 100644 --- a/VideoPlayerForm.cs +++ b/VideoPlayerForm.cs @@ -1,8 +1,10 @@ using System.IO; // For StreamWriter using System.Runtime.InteropServices; +using System.Windows.Media; using DirectShowLib; using DBObj; using OverlayFormObj; +using DualScreenDemo.Services; namespace DualScreenDemo { public class VideoPlayerForm : Form @@ -67,27 +69,8 @@ namespace DualScreenDemo private const int GWL_EXSTYLE = -20; private const int WS_EX_TOPMOST = 0x00000008; private const uint SWP_NOZORDER = 0x0004; - - private IGraphBuilder graphBuilderPrimary; - private IGraphBuilder graphBuilderSecondary; - private IMediaControl mediaControlPrimary; - private IMediaControl mediaControlSecondary; - private static IBaseFilter videoRendererSecondary; - private static IBaseFilter videoRendererPrimary; - private IBaseFilter lavSplitterPrimary; - private IBaseFilter lavSplitterSecondary; - private IBaseFilter lavVideoDecoderPrimary; - private IBaseFilter lavVideoDecoderSecondary; - private static IBaseFilter lavAudioDecoderSecondary; - private IPin outputPinSecondary; - private static IBaseFilter audioRenderer; - private IVideoWindow videoWindowSecondary; - private IVideoWindow videoWindowPrimary; - private IMediaEventEx mediaEventExPrimary; - private IMediaEventEx mediaEventExSecondary; - //private int videoWidth; - //private int videoHeight; - private static bool isInitializationComplete = false; + private MediaServicePrimary primary = new MediaServicePrimary(); + private MediaService secondary = new MediaService(); public static OverlayForm overlayForm; public bool isMuted = false; @@ -102,9 +85,12 @@ namespace DualScreenDemo } private static Screen secondMonitor; + private int newWidth; + private int newHeight; public VideoPlayerForm() { + Lold(); Instance = this; // this.DoubleBuffered = true; this.Load += VideoPlayerForm_Load; @@ -112,10 +98,35 @@ namespace DualScreenDemo this.FormClosing += VideoPlayerForm_FormClosing; InitializeOverlayForm(secondMonitor); BringOverlayToFront(); - HttpServer.OnDisplayBarrage += DisplayBarrageOnOverlay; MonitorMediaEvents(); } + void Lold(){ + int designWidth = 1620; + int designHeight = 1080; + + int actualWidth = PrimaryForm.Instance.primaryScreenPanel.Width; + int actualHeight = PrimaryForm.Instance.primaryScreenPanel.Height; + + // 等比例縮放(不會超出,fit) + float scaleX = (float)actualWidth / designWidth; + float scaleY = (float)actualHeight / designHeight; + float scale = Math.Min(scaleX, scaleY); + + // 計算縮放後的尺寸(但起點仍是 0,0) + newWidth = (int)(designWidth * scale); + newHeight = (int)(designHeight * scale); + + // trash location with trash flexible of screen size. + if (actualWidth == 1024) + { + newWidth = (int)(newWidth * 0.84f); + } + else if (actualWidth == 1440) + { + newWidth = (int)(newWidth * 0.9f); + } + } private void VideoPlayerForm_Load(object sender, EventArgs e) { @@ -128,38 +139,27 @@ namespace DualScreenDemo this.Size = secondMonitor.Bounds.Size; // this.DoubleBuffered = true; } - CheckMonitor(); + Screen screen = Screen.FromHandle(this.Handle); } private void VideoPlayerForm_Shown(object sender, EventArgs e) { int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED); - if (hr < 0) { + if (hr < 0) + { Console.WriteLine("Failed to initialize COM library."); return; } - - InitializeGraphBuilderPrimary(); - InitializeGraphBuilderSecondary(); + else + { + PlayNextSong(); + } } private void VideoPlayerForm_FormClosing(object sender, FormClosingEventArgs e) { - if (videoWindowPrimary != null) - { - videoWindowPrimary.put_Visible(OABool.False); - videoWindowPrimary.put_Owner(IntPtr.Zero); - Marshal.ReleaseComObject(videoWindowPrimary); - videoWindowPrimary = null; - } - - if (videoWindowSecondary != null) - { - videoWindowSecondary.put_Visible(OABool.False); - videoWindowSecondary.put_Owner(IntPtr.Zero); - Marshal.ReleaseComObject(videoWindowSecondary); - videoWindowSecondary = null; - } + primary.VideoPlayerFormClosing(); + secondary.VideoPlayerFormClosing(); // 清理COM CoUninitialize(); } @@ -177,176 +177,7 @@ namespace DualScreenDemo APARTMENTTHREADED = 0x2, MULTITHREADED = 0x0 } - - private void InitializeGraphBuilderPrimary() - { - graphBuilderPrimary = (IGraphBuilder)new FilterGraph(); - if (graphBuilderPrimary == null) - { - Console.WriteLine("Failed to create FilterGraph for primary monitor."); - throw new Exception("Failed to create FilterGraph for primary monitor."); - } - try - { - lavSplitterPrimary = AddFilterByClsid(graphBuilderPrimary, "LAV Splitter", Clsid.LAVSplitter); - - lavVideoDecoderPrimary = AddFilterByClsid(graphBuilderPrimary, "LAV Video Decoder", Clsid.LAVVideoDecoder); - - videoRendererPrimary = AddFilterByClsid(graphBuilderPrimary, "Primary Video Renderer", Clsid.VideoRenderer); - int hr = graphBuilderPrimary.AddFilter(videoRendererPrimary, "Primary Video Renderer"); - DsError.ThrowExceptionForHR(hr); - - mediaControlPrimary = (IMediaControl)graphBuilderPrimary; - if (mediaControlPrimary == null) - { - Console.WriteLine("Failed to get Media Control for primary monitor."); - return; - } - - mediaEventExPrimary = (IMediaEventEx)graphBuilderPrimary; - if (mediaEventExPrimary == null) - { - Console.WriteLine("Failed to get Media Event Ex for primary monitor."); - return; - } - } - catch (Exception ex) - { - Console.WriteLine("Error initializing graph builder for primary monitor: " + ex.Message); - } - } - - private void InitializeGraphBuilderSecondary() - { - graphBuilderSecondary = (IGraphBuilder)new FilterGraph(); - if (graphBuilderSecondary == null) - { - Console.WriteLine("Failed to create FilterGraph"); - throw new Exception("Failed to create FilterGraph"); - } - try - { - lavSplitterSecondary = AddFilterByClsid(graphBuilderSecondary, "LAV Splitter", Clsid.LAVSplitter); - lavVideoDecoderSecondary = AddFilterByClsid(graphBuilderSecondary, "LAV Video Decoder", Clsid.LAVVideoDecoder); - lavAudioDecoderSecondary = AddFilterByClsid(graphBuilderSecondary, "LAV Audio Decoder", Clsid.LAVAudioDecoder); - outputPinSecondary = FindPin(lavAudioDecoderSecondary, "Output"); - videoRendererSecondary = AddFilterByClsid(graphBuilderSecondary, "Secondary Video Renderer", Clsid.VideoRenderer); - if (videoRendererSecondary == null) - { - Console.WriteLine("Failed to initialize Secondary Video Renderer."); - return; - } - int hr = graphBuilderSecondary.AddFilter(videoRendererSecondary, "Secondary Video Renderer"); - DsError.ThrowExceptionForHR(hr); - var clsidAudioRenderer = new Guid("79376820-07D0-11CF-A24D-0020AFD79767"); // CLSID for DirectSound Renderer - audioRenderer = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(clsidAudioRenderer)); - hr = graphBuilderSecondary.AddFilter(audioRenderer, "Default DirectSound Device"); - DsError.ThrowExceptionForHR(hr); - - mediaControlSecondary = (IMediaControl)graphBuilderSecondary; - if (mediaControlSecondary == null) - { - Console.WriteLine("Failed to get Media Control"); - return; - } - mediaEventExSecondary = (IMediaEventEx)graphBuilderSecondary; - if (mediaEventExSecondary == null) - { - Console.WriteLine("Failed to get Media Event Ex"); - return; - } - - isInitializationComplete = true; - } - catch (Exception ex) - { - Console.WriteLine("Error initializing graph builder with second monitor: " + ex.Message); - } - } - - 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 CheckMonitor() - { - Screen screen = Screen.FromHandle(this.Handle); - } - - 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 - } - } + // 同步畫面事件 public void SyncToPrimaryMonitor() { @@ -382,53 +213,7 @@ namespace DualScreenDemo PrimaryForm.Instance.syncMicDownButton.BringToFront(); PrimaryForm.Instance.syncCloseButton.Visible = true; PrimaryForm.Instance.syncCloseButton.BringToFront(); - try - { - if (videoRendererPrimary == null) - { - // Console.WriteLine("VMR9 is not initialized."); - return; - } - int designWidth = 1620; - int designHeight = 1080; - int actualWidth = PrimaryForm.Instance.primaryScreenPanel.Width; - int actualHeight = PrimaryForm.Instance.primaryScreenPanel.Height; - - // 等比例縮放(不會超出,fit) - float scaleX = (float)actualWidth / designWidth; - float scaleY = (float)actualHeight / designHeight; - float scale = Math.Min(scaleX, scaleY); - - // 計算縮放後的尺寸(但起點仍是 0,0) - int newWidth = (int)(designWidth * scale); - int newHeight = (int)(designHeight * scale); - - // trash location with trash flexible of screen size. - if (actualWidth == 1024) - { - newWidth = (int)(newWidth * 0.84f); - } - else if (actualWidth == 1440) - { - newWidth = (int)(newWidth * 0.9f); - } - videoWindowPrimary = (IVideoWindow)videoRendererPrimary; - videoWindowPrimary.put_Owner(PrimaryForm.Instance.primaryScreenPanel.Handle); - videoWindowPrimary.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren); - - // ⬅ 左上角對齊(固定從 0,0 開始) - videoWindowPrimary.SetWindowPosition(0, 0, newWidth, newHeight); - - videoWindowPrimary.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)); - } } public void ClosePrimaryScreenPanel() { @@ -447,12 +232,6 @@ namespace DualScreenDemo PrimaryForm.Instance.syncMicUpButton.Visible = false; PrimaryForm.Instance.syncMicDownButton.Visible = false; PrimaryForm.Instance.syncCloseButton.Visible = false; - if (videoWindowPrimary != null) - { - videoWindowPrimary.put_Owner(PrimaryForm.Instance.primaryScreenPanel.Handle); // 绑定到主屏幕的特定区域 - videoWindowPrimary.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings); - videoWindowPrimary.put_Visible(OABool.False); // 初始化时隐藏 - } IsSyncToPrimaryMonitor = false; } catch (Exception ex) @@ -483,15 +262,10 @@ namespace DualScreenDemo public async Task PlayNextSong() { // 等待初始化完成(例如播放器、串口等資源尚未就緒時) - while (!isInitializationComplete) - { - await Task.Delay(100); - } - StopAndReleaseResources(); - await Task.Delay(100); Console.WriteLine("開始播放下一首歌曲..."); var songToPlay = SongList.Next(); - if (!songToPlay.isPublicSong) { + if (!songToPlay.isPublicSong) + { // 若是使用者點播模式,先送出升Key的串口指令 if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen) { @@ -512,281 +286,96 @@ namespace DualScreenDemo // 隱藏「暫停中」標籤 overlayForm.HidePauseLabel(); - + await _InitializeAndPlayMedia(pathToPlay); + + + } + public async Task ReplayCurrentSong() + { + var songToPlay = SongList.Current(); + var pathToPlay = songToPlay.getFile(); + // UpdateMarqueeTextForCurrentSong(songToPlay); + await _InitializeAndPlayMedia(pathToPlay); + } + private async Task _InitializeAndPlayMedia(string pathToPlay) + { try { - // 確保在UI線程上執行COM對象操作 - if (this.InvokeRequired) - this.Invoke(new Action(async () => { await InitializeAndPlayMedia(pathToPlay); })); + if (InvokeRequired) + { + await InvokeAsync(() => InitializeAndPlayMedia(pathToPlay)); + } else + { await InitializeAndPlayMedia(pathToPlay); + } } catch (Exception ex) { Console.WriteLine($"播放時發生錯誤: {ex.Message}"); - // 嘗試重新初始化並重播 - try - { - await Task.Delay(100); - StopAndReleaseResources(); - await Task.Delay(100); - - // 重新初始化 COM - int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED); - if (hr >= 0) - { - InitializeGraphBuilderPrimary(); - InitializeGraphBuilderSecondary(); - await InitializeAndPlayMedia(pathToPlay); - } - } - catch (Exception retryEx) - { - Console.WriteLine($"重試播放時發生錯誤: {retryEx.Message}"); - } + await RetryInitializeAndPlayMedia(pathToPlay); } } - private async Task InitializeAndPlayMedia(string pathToPlay) + private async Task RetryInitializeAndPlayMedia(string path) { - - if (videoWindowPrimary != null) videoWindowPrimary.put_Visible(OABool.False); - if (videoWindowSecondary != null) videoWindowSecondary.put_Visible(OABool.False); - - // 清理並初始化 DirectShow 圖表 - RemoveAllFilters(graphBuilderPrimary); - RemoveAllFilters(graphBuilderSecondary); - InitializeGraphBuilderPrimary(); - InitializeGraphBuilderSecondary(); - - // 渲染媒體文件 - RenderMediaFilePrimary(pathToPlay); - RenderMediaFileSecondary(pathToPlay); - // 綁定視頻窗口到副屏幕 - videoWindowSecondary = (IVideoWindow)videoRendererSecondary; - if (videoWindowSecondary != null) + try { - videoWindowSecondary.put_Owner(this.Handle); - videoWindowSecondary.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings); - videoWindowSecondary.SetWindowPosition(0, 0, secondMonitor.Bounds.Width, secondMonitor.Bounds.Height); - await Task.Delay(100); // 給予視窗一些時間進行設置 - videoWindowSecondary.put_Visible(OABool.True); + int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED); + if (hr >= 0) + { + await InitializeAndPlayMedia(path); + } + else + { + Console.WriteLine("CoInitializeEx 失敗,無法重新初始化 COM。"); + } } + catch (Exception retryEx) + { + Console.WriteLine($"重試播放時發生錯誤: {retryEx.Message}"); + } + } + + // 通用的 async invoke 方法(避免重複寫) + private Task InvokeAsync(Func func) + { + var tcs = new TaskCompletionSource(); + BeginInvoke(async () => + { + try + { + await func(); + tcs.SetResult(true); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + return tcs.Task; + } + + private Task InitializeAndPlayMedia(string pathToPlay) + { + // 渲染媒體文件 + primary.RenderMediaFile(pathToPlay,PrimaryForm.Instance.primaryScreenPanel.Handle,newWidth,newHeight); + secondary.RenderMediaFile(pathToPlay,this.Handle,secondMonitor.Bounds.Width,secondMonitor.Bounds.Height); + // 音量處理 if (isMuted) { SetVolume(-10000); } - // 開始播放 - if (mediaControlPrimary != null) mediaControlPrimary.Run(); - if (mediaControlSecondary != null) mediaControlSecondary.Run(); - - if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor(); - } - - public void ReplayCurrentSong() - { - var songToPlay = SongList.Current(); - var pathToPlay = songToPlay.getFile(); - // UpdateMarqueeTextForCurrentSong(songToPlay); - - try - { - if (mediaControlPrimary != null) mediaControlPrimary.Stop(); - if (mediaControlSecondary != null) mediaControlSecondary.Stop(); - if (videoWindowPrimary != null) videoWindowPrimary.put_Visible(OABool.False); // 隐藏主屏幕窗口,避免干扰 - if (videoWindowSecondary != null) videoWindowSecondary.put_Visible(OABool.False); // 隐藏副屏幕窗口,避免闪烁 - - // 清理并初始化 DirectShow 图表 - RemoveAllFilters(graphBuilderPrimary); - RemoveAllFilters(graphBuilderSecondary); - InitializeGraphBuilderPrimary(); - InitializeGraphBuilderSecondary(); - - // 渲染媒体文件 - RenderMediaFilePrimary(pathToPlay); - RenderMediaFileSecondary(pathToPlay); - - // 绑定视频窗口到副屏幕 - videoWindowSecondary = videoRendererSecondary as IVideoWindow; - if (videoWindowSecondary != null) - { - videoWindowSecondary.put_Owner(this.Handle); // 副屏幕窗口句柄 - videoWindowSecondary.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings); - videoWindowSecondary.SetWindowPosition(0, 0, secondMonitor.Bounds.Width, secondMonitor.Bounds.Height); - videoWindowSecondary.put_Visible(OABool.True); // 显示窗口 - } - - // 音量处理 - if (isMuted) SetVolume(-10000); // 静音 - - // 开始播放 - mediaControlPrimary?.Run(); - mediaControlSecondary?.Run(); - - if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor(); - } - catch (Exception ex) - { - Console.WriteLine(String.Format("Error replaying song: {0}", ex.Message)); - MessageBox.Show(String.Format("Error replaying song: {0}", ex.Message)); - } - } - - - public void StopAndReleaseResources() - { - try - { - if (mediaControlPrimary != null) - { - mediaControlPrimary.Stop(); - Marshal.ReleaseComObject(mediaControlPrimary); - mediaControlPrimary = null; - } - if (mediaControlSecondary != null) - { - mediaControlSecondary.Stop(); - Marshal.ReleaseComObject(mediaControlSecondary); - mediaControlSecondary = null; - } - - // 释放其他资源 - if (lavSplitterPrimary != null) - { - Marshal.ReleaseComObject(lavSplitterPrimary); - lavSplitterPrimary = null; - } - if (lavSplitterSecondary != null) - { - Marshal.ReleaseComObject(lavSplitterSecondary); - lavSplitterSecondary = null; - } - if (lavVideoDecoderPrimary != null) - { - Marshal.ReleaseComObject(lavVideoDecoderPrimary); - lavVideoDecoderPrimary = null; - } - if (lavVideoDecoderSecondary != null) - { - Marshal.ReleaseComObject(lavVideoDecoderSecondary); - lavVideoDecoderSecondary = null; - } - if (lavAudioDecoderSecondary != null) - { - Marshal.ReleaseComObject(lavAudioDecoderSecondary); - lavAudioDecoderSecondary = null; - } - if (outputPinSecondary != null) - { - Marshal.ReleaseComObject(outputPinSecondary); - outputPinSecondary = null; - } - - // 强制进行垃圾回收 - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - catch (Exception ex) - { - Console.WriteLine($"釋放資源時發生錯誤: {ex.Message}"); - } - } - - public void RenderMediaFilePrimary(string filePath) - { - try { - int hr; - IBaseFilter sourceFilter; - hr = graphBuilderPrimary.AddSourceFilter(filePath, "Source", out sourceFilter); - DsError.ThrowExceptionForHR(hr); - videoWindowPrimary = (IVideoWindow)videoRendererPrimary; - videoWindowPrimary.put_Visible(OABool.False); - hr = ConnectFilters(graphBuilderPrimary, sourceFilter, "Output", lavSplitterPrimary, "Input"); - DsError.ThrowExceptionForHR(hr); - hr = ConnectFilters(graphBuilderPrimary, lavSplitterPrimary, "Video", lavVideoDecoderPrimary, "Input"); - DsError.ThrowExceptionForHR(hr); - hr = ConnectFilters(graphBuilderPrimary, lavVideoDecoderPrimary, "Output", videoRendererPrimary, "VMR Input0"); - DsError.ThrowExceptionForHR(hr); - videoWindowPrimary = (IVideoWindow)videoRendererPrimary; - videoWindowPrimary.put_Owner(PrimaryForm.Instance.primaryScreenPanel.Handle); // 设置为 primaryScreenPanel 的句柄 - videoWindowPrimary.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren); - videoWindowPrimary.SetWindowPosition(0, 0, 1500, 1000); // 调整视频窗口大小以填满黑色区域 - //Task.Delay(100).Wait(); - videoWindowPrimary.put_Visible(OABool.True); - SaveGraphFile(graphBuilderPrimary, "primary_graph.grf"); - Console.WriteLine("主檔案 " + ((hr == 0) ? "成功" : "失敗")); - - } catch (Exception ex) { - Console.WriteLine("主檔案失敗2: " + ex.Message); - } - } - - public void RenderMediaFileSecondary(string filePath) - { - int hr = graphBuilderSecondary.RenderFile(filePath, null); - DsError.ThrowExceptionForHR(hr); - SaveGraphFile(graphBuilderSecondary, "secondary_graph.grf"); - if (hr == 0) - { - Console.WriteLine("Secondary File rendered successfully."); - SetAudioTrackTo(1); - } else { - Console.WriteLine("Failed to render secondary file."); + SetVolume(previousVolume); } - } - - public static void SaveGraphFile(IGraphBuilder graph, string filename) - { - var writer = new StreamWriter(filename); - IFilterGraph2 graph2 = graph as IFilterGraph2; - - if (graph2 != null) - { - IEnumFilters enumFilters; - graph2.EnumFilters(out enumFilters); - - enumFilters.Reset(); - IBaseFilter[] filters = new IBaseFilter[1]; - while (enumFilters.Next(1, filters, IntPtr.Zero) == 0) - { - FilterInfo filterInfo; - filters[0].QueryFilterInfo(out filterInfo); - writer.WriteLine("Filter: " + filterInfo.achName); - IEnumPins enumPins; - filters[0].EnumPins(out enumPins); - enumPins.Reset(); - IPin[] pins = new IPin[1]; - while (enumPins.Next(1, pins, IntPtr.Zero) == 0) - { - PinInfo pinInfo; - pins[0].QueryPinInfo(out pinInfo); - writer.WriteLine(" Pin: " + pinInfo.name); - Marshal.ReleaseComObject(pins[0]); - } - Marshal.ReleaseComObject(enumPins); - Marshal.ReleaseComObject(filters[0]); - } - - Marshal.ReleaseComObject(enumFilters); - } - - writer.Close(); - } - - private static void RemoveAllFilters(IGraphBuilder graph) - { - IEnumFilters enumFilters; - graph.EnumFilters(out enumFilters); - IBaseFilter[] filters = new IBaseFilter[1]; - while (enumFilters.Next(1, filters, IntPtr.Zero) == 0) - { - graph.RemoveFilter(filters[0]); - Marshal.ReleaseComObject(filters[0]); - } - Marshal.ReleaseComObject(enumFilters); + // 開始播放 + primary.Run(); + secondary.Run(); + if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor(); + return Task.CompletedTask; } private void InitializeOverlayForm(Screen secondaryScreen) @@ -805,67 +394,31 @@ namespace DualScreenDemo this.Focus(); } - private bool isPlayingNext = false; - public void MonitorMediaEvents() { - Console.WriteLine("開始監聽媒體事件..."); - Task.Run(async () => { + Console.WriteLine("開始監聽媒體事件..."); while (true) { try { - if (mediaControlSecondary == null) + bool isAtEnd = secondary.isAtEnd() ; + if (isAtEnd && !isPaused) { - await Task.Delay(500); - continue; - } - - // 只檢查播放進度 - IMediaSeeking mediaSeekingSecondary = graphBuilderSecondary as IMediaSeeking; - if (mediaSeekingSecondary != null && !isPlayingNext) - { - long currentPosition = 0; - long duration = 0; - - if (mediaSeekingSecondary.GetCurrentPosition(out currentPosition) >= 0 && - mediaSeekingSecondary.GetDuration(out duration) >= 0) + BeginInvoke(new Action(async () => { - double currentSeconds = currentPosition / 10000000.0; - double durationSeconds = duration / 10000000.0; - - // 添加更严格的结束条件判断 - bool isAtEnd = durationSeconds > 0 && currentSeconds > 0 && - Math.Abs(currentSeconds - durationSeconds) < 0.1 && // 确保真的到了结尾 - !isPaused; - - if (isAtEnd && !isPlayingNext) - { - Console.WriteLine($"檢測到歌曲結束 - 當前位置: {currentSeconds:F2}秒, 總時長: {durationSeconds:F2}秒"); - - - BeginInvoke(new Action(async () => - { - StopAndReleaseResources(); - await Task.Delay(100); // 给予足够的时间释放资源 - await PlayNextSong(); - })); - - } - } + await PlayNextSong(); + })); } } catch (Exception ex) { Console.WriteLine($"監控媒體事件時發生錯誤: {ex.Message}"); - isPlayingNext = false; - await Task.Delay(1000); } - await Task.Delay(500); + await Task.Delay(1000); } }); } @@ -886,28 +439,22 @@ namespace DualScreenDemo public void Play() { - if (mediaControlPrimary != null) - mediaControlPrimary.Run(); - if (mediaControlSecondary != null) - mediaControlSecondary.Run(); + primary.Run(); + secondary.Run(); isPaused = false; OverlayForm.MainForm.HidePauseLabel(); } public void Stop() { - if (mediaControlPrimary != null) - mediaControlPrimary.Stop(); - if (mediaControlSecondary != null) - mediaControlSecondary.Stop(); + primary.Stop(); + secondary.Stop(); } public void Pause() { - if (mediaControlPrimary != null) - mediaControlPrimary.Pause(); - if (mediaControlSecondary != null) - mediaControlSecondary.Pause(); + primary.Pause(); + secondary.Pause(); isPaused = true; OverlayForm.MainForm.ShowPauseLabel(); } @@ -946,149 +493,17 @@ namespace DualScreenDemo } public void SetVolume(int volume) { - if (audioRenderer != null) - { - IBasicAudio basicAudio = audioRenderer as IBasicAudio; - if (basicAudio != null) - { - basicAudio.put_Volume(volume); - } - } + secondary.SetVolume(volume); } public int GetVolume() { - if (audioRenderer != null) - { - IBasicAudio basicAudio = audioRenderer as IBasicAudio; - if (basicAudio != null) - { - int volume; - basicAudio.get_Volume(out volume); - return volume; - } - } - return -10000; + return secondary.GetVolume(); } - private bool isVocalRemoved = false; + public void ToggleVocalRemoval() { - try - { - IAMStreamSelect streamSelect = lavSplitterSecondary as IAMStreamSelect; - - if (streamSelect != null) - { - int trackCount; - if (streamSelect.Count(out trackCount) == 0 && trackCount > 0) - { - int currentTrackIndex = -1; - int audioTrack1 = -1; - int audioTrack2 = -1; - - for (int i = 0; i < trackCount; i++) - { - // 獲取音軌信息 - AMMediaType mediaType; - AMStreamSelectInfoFlags flags; - int lcid, dwGroup; - string name; - object pObject, pUnk; - - streamSelect.Info(i, out mediaType, out flags, out lcid, out dwGroup, out name, out pObject, out pUnk); - - if (mediaType.majorType == MediaType.Audio) - { - if (audioTrack1 == -1) - { - audioTrack1 = i; - } else if (audioTrack2 == -1) { - audioTrack2 = i; - } - - if ((flags & AMStreamSelectInfoFlags.Enabled) != 0) - { - currentTrackIndex = i; - } - } - - DsUtils.FreeAMMediaType(mediaType); - } - - // 切換音軌 - if (currentTrackIndex == audioTrack1 && audioTrack2 != -1) - { - streamSelect.Enable(audioTrack2, AMStreamSelectEnableFlags.Enable); - isVocalRemoved = true; - } - else if (currentTrackIndex == audioTrack2 && audioTrack1 != -1) - { - streamSelect.Enable(audioTrack1, AMStreamSelectEnableFlags.Enable); - isVocalRemoved = false; - } - //OverlayForm.MainForm.ShowOriginalSongLabel(); - string labelText = isVocalRemoved ? "無人聲" : "有人聲"; - // 显示标签 - // 修改成新標籤 - OverlayForm.MainForm.ShowTopRightLabel(labelText); - //await Task.Delay(3000); - // 隐藏标签 - //OverlayForm.MainForm.HideOriginalSongLabel(); - } - } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } + OverlayForm.MainForm.ShowTopRightLabelTime(secondary.ToggleVocalRemoval() ? "無人聲" : "有人聲"); } - - public void SetAudioTrackTo(int trackIndex) - { - try - { - IAMStreamSelect streamSelect = lavSplitterSecondary as IAMStreamSelect; - - if (streamSelect != null) - { - int trackCount; - if (streamSelect.Count(out trackCount) == 0 && trackCount > 0) - { - int audioTrackIndex = -1; - - for (int i = 0; i < trackCount; i++) - { - AMMediaType mediaType; - AMStreamSelectInfoFlags flags; - int lcid, dwGroup; - string name; - object pObject, pUnk; - - streamSelect.Info(i, out mediaType, out flags, out lcid, out dwGroup, out name, out pObject, out pUnk); - - if (mediaType.majorType == MediaType.Audio) - { - audioTrackIndex++; - if (audioTrackIndex == trackIndex) - { - streamSelect.Enable(i, AMStreamSelectEnableFlags.Enable); - } - else - { - streamSelect.Enable(i, AMStreamSelectEnableFlags.DisableAll); - } - } - - DsUtils.FreeAMMediaType(mediaType); - } - } - } - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - } }