superstar_v2/VideoPlayerForm.cs
jasonchenwork d12759b1c2 202508130945
img.ini 移動至本地
VideoPlayerForm檔案音量控制與靜音
2025-08-13 13:07:16 +08:00

469 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Runtime.InteropServices;
using DBObj;
using OverlayFormObj;
using DualScreenDemo.Services;
namespace DualScreenDemo
{
public class VideoPlayerForm : Form
{
#region
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
#endregion
// 单例实例
public static VideoPlayerForm Instance { get; private set; }
// 导入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 MediaServicePrimary primary = new MediaServicePrimary();
private MediaService secondary= new MediaService();
public static OverlayForm overlayForm;
public bool isMuted = false;
public int previousVolume = 100;
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;
}
Screen screen = Screen.FromHandle(this.Handle);
}
private void VideoPlayerForm_Shown(object sender, EventArgs e)
{
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
if (hr < 0)
{
Console.WriteLine("Failed to initialize COM library.");
return;
}
else
{
secondary.SetVideoFormPrimary(PrimaryForm.Instance.videoPanel.Handle,PrimaryForm.Instance.videoPanel.Width,PrimaryForm.Instance.videoPanel.Height);
secondary.SetVideoFormSecondary(this.Handle,secondMonitor.Bounds.Width,secondMonitor.Bounds.Height);
PlayNextSong();
}
}
private void VideoPlayerForm_FormClosing(object sender, FormClosingEventArgs e)
{
secondary.Dispose();
// 清理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
}
// 同步畫面事件
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();
}
public void ClosePrimaryScreenPanel()
{
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;
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()
{
// 等待初始化完成(例如播放器、串口等資源尚未就緒時)
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);
}
}
// 更新畫面上顯示的下一首歌資訊
SongList.UpdateNextSongLabel();
// 顯示 QRCode可能是點歌頁用
overlayForm.DisplayQRCodeOnOverlay(HttpServer.randomFolderPath);
// 隱藏「暫停中」標籤
overlayForm.HidePauseLabel();
await _InitializeAndPlayMedia(songToPlay);
}
public async Task ReplayCurrentSong() =>await _InitializeAndPlayMedia(SongList.Current());
private async Task _InitializeAndPlayMedia(SongData song)
{
try
{
if (InvokeRequired)
{
await InvokeAsync(() => InitializeAndPlayMedia(song));
}
else
{
await InitializeAndPlayMedia(song);
}
}
catch (Exception ex)
{
Console.WriteLine($"播放時發生錯誤: {ex.Message}");
await RetryInitializeAndPlayMedia(song);
}
}
private async Task RetryInitializeAndPlayMedia(SongData song)
{
try
{
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
if (hr >= 0)
{
await InitializeAndPlayMedia(song);
}
else
{
Console.WriteLine("CoInitializeEx 失敗,無法重新初始化 COM。");
}
}
catch (Exception retryEx)
{
Console.WriteLine($"重試播放時發生錯誤: {retryEx.Message}");
}
}
// 通用的 async invoke 方法(避免重複寫)
private Task InvokeAsync(Func<Task> func)
{
var tcs = new TaskCompletionSource<bool>();
BeginInvoke(async () =>
{
try
{
await func();
tcs.SetResult(true);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
return tcs.Task;
}
private Task InitializeAndPlayMedia(SongData song)
{
string pathToPlay = song.getFile();
// 渲染媒體文件
secondary.LoadMedia(pathToPlay,song.isPublicSong ? 0 : 1);
secondary.Mute(isMuted);
secondary.PlayAsync();
// 音量處理
//SetVolume(isMuted ? 0 : previousVolume);
SetVolume(100+song.getBasic().getDbChange());
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
return Task.CompletedTask;
}
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();
}
public void MonitorMediaEvents()
{
Task.Run(async () =>
{
await Task.Delay(10000);
Console.WriteLine("開始監聽媒體事件...");
while (true)
{
try
{
if (secondary.IsAtEnd())
{
BeginInvoke(new Action(async () =>
{
await PlayNextSong();
}));
}
}
catch (Exception ex)
{
Console.WriteLine($"監控媒體事件時發生錯誤: {ex.Message}");
await Task.Delay(1000);
}
await Task.Delay(1000);
}
});
}
public void BringOverlayToFront()
{
if (overlayForm != null)
{
if (!overlayForm.Visible)
{
overlayForm.Show();
}
overlayForm.BringToFront();
overlayForm.TopMost = true;
}
}
public void Play()
{
secondary.Play();
isPaused = false;
OverlayForm.MainForm.HidePauseLabel();
}
public void Stop()
{
secondary.Stop();
}
public void Pause()
{
secondary.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 bool Mute(bool isMuted)
{
return secondary.Mute(isMuted);
}
public void SetVolume(int volume)
{
Console.WriteLine($"SetVolume: {volume}");
secondary.SetVolume(volume);
}
public int GetVolume()
{
return secondary.GetVolume();
}
private bool isVocalRemoved = false;
public void ToggleVocalRemoval()
{
isVocalRemoved=!isVocalRemoved;
int trackIndex = isVocalRemoved ? 1:0;
secondary.SetAudioTrackToAsync(trackIndex);
OverlayForm.MainForm.ShowTopRightLabelTime(isVocalRemoved ? "無人聲" : "有人聲");
}
}
}