superstar_v2/VideoPlayerForm.cs

548 lines
19 KiB
C#
Raw Normal View History

2025-04-07 16:54:10 +08:00
using System.Runtime.InteropServices;
using DBObj;
using OverlayFormObj;
using DualScreenDemo.Services;
using LibVLCSharp;
2025-04-07 16:54:10 +08:00
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;
2025-08-18 10:29:00 +08:00
private MediaService _mediaService0 = new MediaService();
private MediaService _mediaService1 = new MediaService();
2025-08-15 12:15:33 +08:00
2025-04-07 16:54:10 +08:00
public static OverlayForm overlayForm;
public bool isMuted = false;
public int previousVolume = 100;
2025-04-07 16:54:10 +08:00
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();
}
2025-08-18 10:29:00 +08:00
2025-04-07 16:54:10 +08:00
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;
2025-04-07 16:54:10 +08:00
}
Screen screen = Screen.FromHandle(this.Handle);
2025-04-07 16:54:10 +08:00
}
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;
}
else
{
this.Controls.Add(PrimaryForm.Instance.videoView1);
PrimaryForm.Instance.videoView1.MediaPlayer = _mediaService1.Player;
PrimaryForm.Instance.videoView0.MediaPlayer = _mediaService0.Player;
2025-09-03 17:34:33 +08:00
//同步畫面比例
_mediaService0.Player.AspectRatio = "8:5";
_mediaService0._zero = true;
PlayNextSong();
}
2025-04-07 16:54:10 +08:00
}
private void VideoPlayerForm_FormClosing(object sender, FormClosingEventArgs e)
{
2025-08-15 17:53:55 +08:00
_mediaService0.Dispose();
_mediaService1.Dispose();
2025-04-07 16:54:10 +08:00
// 清理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
}
2025-08-18 10:29:00 +08:00
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();
2025-05-08 16:49:32 +08:00
2025-04-07 16:54:10 +08:00
}
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;
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()
{
try
{
// 等待初始化完成(例如播放器、串口等資源尚未就緒時)
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);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
2025-08-18 10:29:00 +08:00
2025-04-07 16:54:10 +08:00
}
2025-08-15 12:15:33 +08:00
public async Task ReplayCurrentSong()
{
if (Program.room.IsClose())
{
return;
}
else
2025-08-18 10:29:00 +08:00
{
2025-08-15 12:15:33 +08:00
await _InitializeAndPlayMedia(SongList.Current());
}
}
2025-08-18 10:29:00 +08:00
private async Task _InitializeAndPlayMedia(SongData song)
{
2025-04-07 16:54:10 +08:00
try
{
if (InvokeRequired)
2025-04-07 16:54:10 +08:00
{
await InvokeAsync(() => InitializeAndPlayMedia(song));
}
else
{
await InitializeAndPlayMedia(song);
2025-04-07 16:54:10 +08:00
}
}
catch (Exception ex)
{
Console.WriteLine($"播放時發生錯誤: {ex.Message}");
await RetryInitializeAndPlayMedia(song);
2025-04-07 16:54:10 +08:00
}
}
private async Task RetryInitializeAndPlayMedia(SongData song)
2025-04-07 16:54:10 +08:00
{
try
{
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
if (hr >= 0)
2025-04-07 16:54:10 +08:00
{
await InitializeAndPlayMedia(song);
2025-04-07 16:54:10 +08:00
}
else
2025-04-07 16:54:10 +08:00
{
Console.WriteLine("CoInitializeEx 失敗,無法重新初始化 COM。");
2025-04-07 16:54:10 +08:00
}
}
catch (Exception retryEx)
2025-04-07 16:54:10 +08:00
{
Console.WriteLine($"重試播放時發生錯誤: {retryEx.Message}");
2025-04-07 16:54:10 +08:00
}
}
// 通用的 async invoke 方法(避免重複寫)
private Task InvokeAsync(Func<Task> func)
2025-04-07 16:54:10 +08:00
{
var tcs = new TaskCompletionSource<bool>();
BeginInvoke(async () =>
2025-04-07 16:54:10 +08:00
{
try
{
await func();
tcs.SetResult(true);
}
catch (Exception ex)
{
2025-08-25 09:27:39 +08:00
Program.WriteLog(ex.ToString());
tcs.SetException(ex);
}
});
return tcs.Task;
2025-04-07 16:54:10 +08:00
}
private Task InitializeAndPlayMedia(SongData song)
2025-04-07 16:54:10 +08:00
{
2025-08-25 09:27:39 +08:00
try
{
string pathToPlay = song.getFile();
// Uri url = song.getFileUrl();
//影片畫面播放器載入media設置聲道
// if(url !=null)
// {
// _mediaService0.LoadMedia(url, 0);
// _mediaService1.LoadMedia(url, song.isPublicSong ? 0 : 1);
// }
if(pathToPlay != null)
2025-09-03 17:34:33 +08:00
{
_mediaService0.LoadMedia(pathToPlay, 0);
_mediaService1.LoadMedia(pathToPlay, song.isPublicSong ? 0 : 1);
}
// _mediaService0.Player.Media.AddOption(":no-audio");
// _mediaService0.Player.AspectRatio = "8:5";
isVocalRemoved = true;
if (isMuted) { Mute(true); }
// 音量處理
2025-08-25 09:27:39 +08:00
SetVolume(100 + song.getBasic().getDbChange());
Task.Run(() => Thread.Sleep(100));
2025-08-25 09:27:39 +08:00
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
PrimaryForm.Instance.standardKeyButton.PerformClick();
2025-08-25 09:27:39 +08:00
return Task.CompletedTask;
}
catch (Exception ex)
2025-08-25 09:27:39 +08:00
{
Console.WriteLine(ex.Message);
Program.WriteLog(ex.ToString());
return Task.CompletedTask;
}
2025-08-18 10:29:00 +08:00
2025-04-07 16:54:10 +08:00
}
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("開始監聽媒體事件...");
2025-04-07 16:54:10 +08:00
while (true)
{
try
{
if (_mediaService1.IsAtEndTime())
2025-04-07 16:54:10 +08:00
{
BeginInvoke(new Action(async () =>
2025-04-07 16:54:10 +08:00
{
await PlayNextSong();
if (PrimaryForm.Instance.isOnOrderedSongsPage) PrimaryForm.Instance.orderedSongsButton.PerformClick();
}));
2025-04-07 16:54:10 +08:00
}
}
catch (Exception ex)
{
Console.WriteLine($"監控媒體事件時發生錯誤: {ex.Message}");
}
await Task.Delay(1000);
2025-04-07 16:54:10 +08:00
}
});
}
public void BringOverlayToFront()
{
if (overlayForm != null)
{
if (!overlayForm.Visible)
{
overlayForm.Show();
}
overlayForm.BringToFront();
overlayForm.TopMost = true;
}
}
public void Play()
{
2025-08-15 17:53:55 +08:00
_mediaService0.Play();
_mediaService1.Play();
2025-04-07 16:54:10 +08:00
isPaused = false;
OverlayForm.MainForm.HidePauseLabel();
}
public void Stop()
{
2025-08-15 17:53:55 +08:00
_mediaService0.Stop();
_mediaService1.Stop();
2025-04-07 16:54:10 +08:00
}
public void Pause()
{
2025-08-15 17:53:55 +08:00
_mediaService0.Pause();
_mediaService1.Pause();
2025-04-07 16:54:10 +08:00
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)
{
2025-08-18 10:29:00 +08:00
if (isMuted)
{
2025-08-18 10:58:58 +08:00
_mediaService1.Mute(true);
2025-08-18 10:29:00 +08:00
}
else
{
_mediaService1.Mute(false);
2025-08-15 17:53:55 +08:00
}
return isMuted;
}
2025-04-07 16:54:10 +08:00
public void SetVolume(int volume)
{
Console.WriteLine($"SetVolume: {volume}");
2025-08-15 17:53:55 +08:00
_mediaService0.SetVolume(volume);
_mediaService1.SetVolume(volume);
2025-04-07 16:54:10 +08:00
}
public int GetVolume()
{
2025-08-15 17:53:55 +08:00
return _mediaService1.GetVolume();
2025-04-07 16:54:10 +08:00
}
2025-08-15 17:53:55 +08:00
private bool isVocalRemoved = true;
public void ToggleVocalRemoval()
2025-04-07 16:54:10 +08:00
{
2025-09-02 11:46:50 +08:00
if (SongList.Current().isPublicSong == false)
{
2025-09-02 11:46:50 +08:00
if (isVocalRemoved)
{
_mediaService1.Player.Select(TrackType.Audio, "audio/2");
2025-09-02 11:46:50 +08:00
isVocalRemoved = false;
}
else
{
_mediaService1.Player.Select(TrackType.Audio, "audio/1,audio/3");
// _mediaService1.Player.Select(TrackType.Audio, "audio/3");
isVocalRemoved = true;
}
OverlayForm.MainForm.ShowTopRightLabelTime(isVocalRemoved ? "無人聲" : "有人聲");
}
2025-09-02 11:46:50 +08:00
2025-04-07 16:54:10 +08:00
}
2025-04-07 16:54:10 +08:00
}
2025-06-20 13:12:10 +08:00
}