superstar_v2/VideoPlayerForm.cs

1096 lines
43 KiB
C#
Raw Permalink Normal View History

2025-04-07 16:54:10 +08:00
using System.IO; // For StreamWriter
using System.Runtime.InteropServices;
using DirectShowLib;
using DBObj;
using OverlayFormObj;
namespace DualScreenDemo
{
public class VideoPlayerForm : Form
{
#region
2025-04-07 16:54:10 +08:00
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
2025-06-20 13:12:10 +08:00
#endregion
2025-04-07 16:54:10 +08:00
// 单例实例
public static VideoPlayerForm Instance { get; private set; }
2025-06-20 13:12:10 +08:00
2025-04-07 16:54:10 +08:00
// 导入user32.dll API
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
// Windows API 函數
[DllImport("user32.dll")]
static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
// MONITORINFO 結構
[StructLayout(LayoutKind.Sequential)]
struct MONITORINFO
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
// RECT 結構
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
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;
public static OverlayForm overlayForm;
public bool isMuted = false;
public int previousVolume = -1000;
public bool isPaused = false;
private bool isSyncToPrimaryMonitor = false;
public bool IsSyncToPrimaryMonitor
{
get { return isSyncToPrimaryMonitor; }
set { isSyncToPrimaryMonitor = value; }
}
private static Screen secondMonitor;
public VideoPlayerForm()
{
Instance = this;
// this.DoubleBuffered = true;
this.Load += VideoPlayerForm_Load;
this.Shown += VideoPlayerForm_Shown;
this.FormClosing += VideoPlayerForm_FormClosing;
InitializeOverlayForm(secondMonitor);
BringOverlayToFront();
HttpServer.OnDisplayBarrage += DisplayBarrageOnOverlay;
MonitorMediaEvents();
}
private void VideoPlayerForm_Load(object sender, EventArgs e)
{
secondMonitor = ScreenHelper.GetSecondMonitor();
if (secondMonitor != null)
{
this.FormBorderStyle = FormBorderStyle.None; // 设置窗体没有边框
this.StartPosition = FormStartPosition.Manual;
this.Location = secondMonitor.Bounds.Location;
this.Size = secondMonitor.Bounds.Size;
// this.DoubleBuffered = true;
}
CheckMonitor();
}
2025-07-21 18:36:09 +08:00
2025-04-07 16:54:10 +08:00
private void VideoPlayerForm_Shown(object sender, EventArgs e)
{
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
if (hr < 0) {
2025-04-07 16:54:10 +08:00
Console.WriteLine("Failed to initialize COM library.");
return;
}
InitializeGraphBuilderPrimary();
InitializeGraphBuilderSecondary();
}
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;
}
// 清理COM
CoUninitialize();
}
// COM API函数声明
[DllImport("ole32.dll")]
private static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
[DllImport("ole32.dll")]
private static extern void CoUninitialize();
// CoInitializeEx() 可以选择的参数
private enum COINIT : int
{
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);
2025-04-14 14:17:06 +08:00
//Console.WriteLine($"Attempting to create filter of type: {filterType.FullName}");
2025-04-07 16:54:10 +08:00
// 创建实例
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)
{
2025-06-20 13:12:10 +08:00
Console.WriteLine($"Exception in AddFilterByClsid: {ex.Message}");
2025-04-07 16:54:10 +08:00
throw; // Rethrow the exception to handle it further up the call stack
}
}
2025-05-08 16:49:32 +08:00
// 同步畫面事件
2025-04-07 16:54:10 +08:00
public void SyncToPrimaryMonitor()
{
PrimaryForm.Instance.primaryScreenPanel.Visible = true;
PrimaryForm.Instance.primaryScreenPanel.BringToFront();
PrimaryForm.Instance.syncServiceBellButton.Visible = true;
PrimaryForm.Instance.syncServiceBellButton.BringToFront();
PrimaryForm.Instance.syncCutSongButton.Visible = true;
PrimaryForm.Instance.syncCutSongButton.BringToFront();
PrimaryForm.Instance.syncReplayButton.Visible = true;
PrimaryForm.Instance.syncReplayButton.BringToFront();
PrimaryForm.Instance.syncOriginalSongButton.Visible = true;
PrimaryForm.Instance.syncOriginalSongButton.BringToFront();
PrimaryForm.Instance.syncMuteButton.Visible = true;
PrimaryForm.Instance.syncMuteButton.BringToFront();
if (isPaused)
{
PrimaryForm.Instance.syncPlayButton.Visible = true;
PrimaryForm.Instance.syncPlayButton.BringToFront();
}
else
{
PrimaryForm.Instance.syncPauseButton.Visible = true;
PrimaryForm.Instance.syncPauseButton.BringToFront();
}
PrimaryForm.Instance.syncVolumeUpButton.Visible = true;
PrimaryForm.Instance.syncVolumeUpButton.BringToFront();
PrimaryForm.Instance.syncVolumeDownButton.Visible = true;
PrimaryForm.Instance.syncVolumeDownButton.BringToFront();
PrimaryForm.Instance.syncMicUpButton.Visible = true;
PrimaryForm.Instance.syncMicUpButton.BringToFront();
PrimaryForm.Instance.syncMicDownButton.Visible = true;
PrimaryForm.Instance.syncMicDownButton.BringToFront();
PrimaryForm.Instance.syncCloseButton.Visible = true;
PrimaryForm.Instance.syncCloseButton.BringToFront();
try
{
if (videoRendererPrimary == null)
{
// Console.WriteLine("VMR9 is not initialized.");
return;
}
2025-05-08 16:49:32 +08:00
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);
2025-06-20 13:12:10 +08:00
2025-05-08 16:49:32 +08:00
// trash location with trash flexible of screen size.
2025-06-20 13:12:10 +08:00
if (actualWidth == 1024)
{
2025-05-08 16:49:32 +08:00
newWidth = (int)(newWidth * 0.84f);
}
2025-06-20 13:12:10 +08:00
else if (actualWidth == 1440)
{
2025-05-08 16:49:32 +08:00
newWidth = (int)(newWidth * 0.9f);
}
2025-04-07 16:54:10 +08:00
videoWindowPrimary = (IVideoWindow)videoRendererPrimary;
2025-05-08 16:49:32 +08:00
videoWindowPrimary.put_Owner(PrimaryForm.Instance.primaryScreenPanel.Handle);
2025-04-07 16:54:10 +08:00
videoWindowPrimary.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
2025-05-08 16:49:32 +08:00
// ⬅ 左上角對齊(固定從 0,0 開始)
videoWindowPrimary.SetWindowPosition(0, 0, newWidth, newHeight);
2025-04-07 16:54:10 +08:00
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));
}
}
2025-06-20 13:12:10 +08:00
public void ClosePrimaryScreenPanel()
2025-04-07 16:54:10 +08:00
{
try
{
PrimaryForm.Instance.primaryScreenPanel.Visible = false;
PrimaryForm.Instance.syncServiceBellButton.Visible = false;
PrimaryForm.Instance.syncCutSongButton.Visible = false;
PrimaryForm.Instance.syncReplayButton.Visible = false;
PrimaryForm.Instance.syncOriginalSongButton.Visible = false;
PrimaryForm.Instance.syncMuteButton.Visible = false;
PrimaryForm.Instance.syncPauseButton.Visible = false;
PrimaryForm.Instance.syncPlayButton.Visible = false;
PrimaryForm.Instance.syncVolumeUpButton.Visible = false;
PrimaryForm.Instance.syncVolumeDownButton.Visible = false;
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)
{
Console.WriteLine(String.Format("Error closing primary screen panel: {0}", ex.Message));
MessageBox.Show(String.Format("Error closing primary screen panel: {0}", ex.Message));
}
}
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
public enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
}
private void DisplayBarrageOnOverlay(string text)
{
if (overlayForm.InvokeRequired)
overlayForm.Invoke(new System.Action(() => overlayForm.DisplayBarrage(text)));
else
overlayForm.DisplayBarrage(text);
}
public async Task PlayNextSong()
{
// 等待初始化完成(例如播放器、串口等資源尚未就緒時)
2025-04-07 16:54:10 +08:00
while (!isInitializationComplete)
{
await Task.Delay(100);
}
StopAndReleaseResources();
await Task.Delay(100);
2025-04-07 16:54:10 +08:00
Console.WriteLine("開始播放下一首歌曲...");
var songToPlay = SongList.Next();
if (!songToPlay.isPublicSong) {
// 若是使用者點播模式先送出升Key的串口指令
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
{
byte[] commandBytesIncreasePitch1 = new byte[] { 0xA2, 0x7F, 0xA4 };
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch1, 0, commandBytesIncreasePitch1.Length);
}
2025-04-07 16:54:10 +08:00
}
2025-06-20 13:12:10 +08:00
// pathToPlay 需要調整
2025-07-08 18:13:36 +08:00
var pathToPlay = songToPlay.getFile();
2025-06-20 13:12:10 +08:00
// 更新畫面上顯示的下一首歌資訊
SongList.UpdateNextSongLabel();
2025-06-20 13:12:10 +08:00
// 顯示 QRCode可能是點歌頁用
overlayForm.DisplayQRCodeOnOverlay(HttpServer.randomFolderPath);
2025-06-20 13:12:10 +08:00
// 隱藏「暫停中」標籤
overlayForm.HidePauseLabel();
2025-04-07 16:54:10 +08:00
try
{
// 確保在UI線程上執行COM對象操作
if (this.InvokeRequired)
this.Invoke(new Action(async () => { await InitializeAndPlayMedia(pathToPlay); }));
2025-04-07 16:54:10 +08:00
else
await InitializeAndPlayMedia(pathToPlay);
}
catch (Exception ex)
{
Console.WriteLine($"播放時發生錯誤: {ex.Message}");
// 嘗試重新初始化並重播
try
{
await Task.Delay(100);
2025-04-07 16:54:10 +08:00
StopAndReleaseResources();
await Task.Delay(100);
2025-06-20 13:12:10 +08:00
2025-04-07 16:54:10 +08:00
// 重新初始化 COM
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
if (hr >= 0)
{
InitializeGraphBuilderPrimary();
InitializeGraphBuilderSecondary();
await InitializeAndPlayMedia(pathToPlay);
}
}
catch (Exception retryEx)
{
Console.WriteLine($"重試播放時發生錯誤: {retryEx.Message}");
}
}
}
private async Task InitializeAndPlayMedia(string pathToPlay)
{
2025-05-08 11:45:47 +08:00
if (videoWindowPrimary != null) videoWindowPrimary.put_Visible(OABool.False);
if (videoWindowSecondary != null) videoWindowSecondary.put_Visible(OABool.False);
2025-04-07 16:54:10 +08:00
// 清理並初始化 DirectShow 圖表
RemoveAllFilters(graphBuilderPrimary);
RemoveAllFilters(graphBuilderSecondary);
2025-04-07 16:54:10 +08:00
InitializeGraphBuilderPrimary();
InitializeGraphBuilderSecondary();
// 渲染媒體文件
RenderMediaFilePrimary(pathToPlay);
RenderMediaFileSecondary(pathToPlay);
// 綁定視頻窗口到副屏幕
videoWindowSecondary = (IVideoWindow)videoRendererSecondary;
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);
await Task.Delay(100); // 給予視窗一些時間進行設置
videoWindowSecondary.put_Visible(OABool.True);
}
// 音量處理
if (isMuted)
{
SetVolume(-10000);
}
// 開始播放
if (mediaControlPrimary != null) mediaControlPrimary.Run();
if (mediaControlSecondary != null) mediaControlSecondary.Run();
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
2025-04-07 16:54:10 +08:00
}
public void ReplayCurrentSong()
{
var songToPlay = SongList.Current();
2025-07-08 18:13:36 +08:00
var pathToPlay = songToPlay.getFile();
2025-04-07 16:54:10 +08:00
// 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); // 隐藏副屏幕窗口,避免闪烁
2025-04-07 16:54:10 +08:00
// 清理并初始化 DirectShow 图表
RemoveAllFilters(graphBuilderPrimary);
RemoveAllFilters(graphBuilderSecondary);
2025-04-07 16:54:10 +08:00
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); // 静音
2025-04-07 16:54:10 +08:00
// 开始播放
mediaControlPrimary?.Run();
mediaControlSecondary?.Run();
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
2025-04-07 16:54:10 +08:00
}
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;
2025-04-07 16:54:10 +08:00
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); // 调整视频窗口大小以填满黑色区域
2025-05-08 11:45:47 +08:00
//Task.Delay(100).Wait();
2025-04-07 16:54:10 +08:00
videoWindowPrimary.put_Visible(OABool.True);
SaveGraphFile(graphBuilderPrimary, "primary_graph.grf");
Console.WriteLine("主檔案 " + ((hr == 0) ? "成功" : "失敗"));
2025-04-07 16:54:10 +08:00
} catch (Exception ex) {
2025-04-07 16:54:10 +08:00
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.");
}
}
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);
}
private void InitializeOverlayForm(Screen secondaryScreen)
{
overlayForm = new OverlayForm();
Screen secondMonitor = ScreenHelper.GetSecondMonitor();
if (secondMonitor != null)
{
overlayForm.Location = secondMonitor.WorkingArea.Location;
overlayForm.StartPosition = FormStartPosition.Manual;
overlayForm.Size = new Size(secondMonitor.WorkingArea.Width, secondMonitor.WorkingArea.Height);
}
overlayForm.ShowInTaskbar = false;
overlayForm.Owner = this;
overlayForm.Show();
this.Focus();
}
private bool isPlayingNext = false;
public void MonitorMediaEvents()
{
Console.WriteLine("開始監聽媒體事件...");
Task.Run(async () =>
{
while (true)
{
try
{
if (mediaControlSecondary == null)
2025-04-07 16:54:10 +08:00
{
await Task.Delay(500);
continue;
}
// 只檢查播放進度
IMediaSeeking mediaSeekingSecondary = graphBuilderSecondary as IMediaSeeking;
if (mediaSeekingSecondary != null && !isPlayingNext)
{
long currentPosition = 0;
long duration = 0;
2025-06-20 13:12:10 +08:00
2025-04-07 16:54:10 +08:00
if (mediaSeekingSecondary.GetCurrentPosition(out currentPosition) >= 0 &&
mediaSeekingSecondary.GetDuration(out duration) >= 0)
{
double currentSeconds = currentPosition / 10000000.0;
double durationSeconds = duration / 10000000.0;
// 添加更严格的结束条件判断
2025-06-20 13:12:10 +08:00
bool isAtEnd = durationSeconds > 0 && currentSeconds > 0 &&
2025-04-07 16:54:10 +08:00
Math.Abs(currentSeconds - durationSeconds) < 0.1 && // 确保真的到了结尾
!isPaused;
if (isAtEnd && !isPlayingNext)
{
Console.WriteLine($"檢測到歌曲結束 - 當前位置: {currentSeconds:F2}秒, 總時長: {durationSeconds:F2}秒");
2025-06-20 13:12:10 +08:00
BeginInvoke(new Action(async () =>
2025-04-07 16:54:10 +08:00
{
StopAndReleaseResources();
await Task.Delay(100); // 给予足够的时间释放资源
await PlayNextSong();
}));
2025-04-07 16:54:10 +08:00
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"監控媒體事件時發生錯誤: {ex.Message}");
isPlayingNext = false;
2025-06-20 13:12:10 +08:00
2025-04-07 16:54:10 +08:00
await Task.Delay(1000);
}
await Task.Delay(500);
}
});
}
public void BringOverlayToFront()
{
if (overlayForm != null)
{
if (!overlayForm.Visible)
{
overlayForm.Show();
}
overlayForm.BringToFront();
overlayForm.TopMost = true;
}
}
public void Play()
{
if (mediaControlPrimary != null)
mediaControlPrimary.Run();
if (mediaControlSecondary != null)
mediaControlSecondary.Run();
isPaused = false;
OverlayForm.MainForm.HidePauseLabel();
}
public void Stop()
{
if (mediaControlPrimary != null)
mediaControlPrimary.Stop();
if (mediaControlSecondary != null)
mediaControlSecondary.Stop();
}
public void Pause()
{
if (mediaControlPrimary != null)
mediaControlPrimary.Pause();
if (mediaControlSecondary != null)
mediaControlSecondary.Pause();
isPaused = true;
OverlayForm.MainForm.ShowPauseLabel();
}
public void PauseOrResumeSong()
{
if (isPaused)
{
Play();
PrimaryForm.Instance.pauseButton.Visible = true;
PrimaryForm.Instance.playButton.Visible = false;
PrimaryForm.Instance.syncPauseButton.Visible = true;
PrimaryForm.Instance.syncPlayButton.Visible = false;
}
else
{
Pause();
PrimaryForm.Instance.pauseButton.Visible = false;
PrimaryForm.Instance.playButton.Visible = true;
PrimaryForm.Instance.syncPauseButton.Visible = false;
PrimaryForm.Instance.syncPlayButton.Visible = true;
OverlayForm.MainForm.ShowPauseLabel();
}
}
private void UpdateSyncButtons()
{
if (isPaused)
{
PrimaryForm.Instance.syncPlayButton.Visible = true;
PrimaryForm.Instance.syncPauseButton.Visible = false;
}
else
{
PrimaryForm.Instance.syncPlayButton.Visible = false;
PrimaryForm.Instance.syncPauseButton.Visible = true;
}
}
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 void ToggleVocalRemoval()
2025-04-07 16:54:10 +08:00
{
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) {
2025-04-07 16:54:10 +08:00
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);
2025-04-07 16:54:10 +08:00
// 隐藏标签
//OverlayForm.MainForm.HideOriginalSongLabel();
2025-04-07 16:54:10 +08:00
}
}
}
catch (Exception ex)
{
2025-06-20 13:12:10 +08:00
Console.WriteLine(ex.Message);
2025-04-07 16:54:10 +08:00
}
}
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)
{
2025-06-20 13:12:10 +08:00
Console.WriteLine(ex.Message);
2025-04-07 16:54:10 +08:00
}
}
}
2025-06-20 13:12:10 +08:00
}