diff --git a/Services/MediaService.cs b/Services/MediaService.cs index 5991c97..24c6544 100644 --- a/Services/MediaService.cs +++ b/Services/MediaService.cs @@ -5,142 +5,67 @@ namespace DualScreenDemo.Services public class MediaService : IDisposable { private readonly LibVLC _libVLC; - private readonly MediaPlayer _mediaPlayerPrimary; - private readonly MediaPlayer _mediaPlayerSecondary; + private readonly MediaPlayer _mediaPlayer; private Media? _media; - private Media? _mediaNoAudio; private bool _disposed; - public MediaService() + public MediaService(nint handle) { Core.Initialize(); _libVLC = new LibVLC( - "--aout=directsound", - //"--avcodec-hw=dxva2", "--network-caching=300", "--file-caching=300", - "--audio-time-stretch" + "--audio-time-stretch", + "--video-filter=clone", + $"--clone-views={handle}" // 這是 clone 的目標視窗 ); - _mediaPlayerPrimary = new MediaPlayer(_libVLC); - _mediaPlayerSecondary = new MediaPlayer(_libVLC); + _mediaPlayer = new MediaPlayer(_libVLC); } #region Player Setup - public void SetVideoFormPrimary(nint handle, int width, int height) + public void SetVideoOutput(nint handle, int width, int height) { - _mediaPlayerPrimary.Hwnd = handle; - _mediaPlayerPrimary.AspectRatio = $"{width}:{height}"; - _mediaPlayerPrimary.Scale = 0; // 保持原比例 - } - - public void SetVideoFormSecondary(nint handle, int width, int height) - { - _mediaPlayerSecondary.Hwnd = handle; - _mediaPlayerSecondary.AspectRatio = $"{width}:{height}"; - _mediaPlayerSecondary.Scale = 0; + _mediaPlayer.Hwnd = handle; + _mediaPlayer.AspectRatio = $"{width}:{height}"; + _mediaPlayer.Scale = 0; } #endregion #region Playback - public MediaPlayer PrimaryPlayer => _mediaPlayerPrimary; - public MediaPlayer SecondaryPlayer => _mediaPlayerSecondary; - public bool IsPlaying => _mediaPlayerSecondary.IsPlaying; + public MediaPlayer Player => _mediaPlayer; + public bool IsPlaying => _mediaPlayer.IsPlaying; public bool IsAtEnd() { - var duration = _mediaPlayerSecondary.Media?.Duration ?? 0; - var time = _mediaPlayerSecondary.Time; + var duration = _mediaPlayer.Media?.Duration ?? 0; + var time = _mediaPlayer.Time; return duration > 0 && Math.Abs(duration - time) < 1000; } public void LoadMedia(string filePath, int audioTrackIndex = 0) { - _mediaPlayerPrimary.Pause(); - _mediaPlayerSecondary.Pause(); + _mediaPlayer.Stop(); _media?.Dispose(); - // 建立一個完整有聲音的 Media 給 secondary 播放器 _media = new Media(_libVLC, filePath, FromType.FromPath); - _media.AddOption(":audio-output=directsound"); _media.AddOption($":audio-track={audioTrackIndex}"); - - // 同時準備給 primary 播放器用的無聲音版本 Media - _mediaNoAudio?.Dispose(); - _mediaNoAudio = new Media(_libVLC, filePath, FromType.FromPath); - _mediaNoAudio.AddOption(":no-audio"); // 關閉聲音輸出 } - public async Task PlayAsync() + public void Play() { - if (_media == null || _mediaNoAudio == null) return; - - _mediaPlayerPrimary.Play(_mediaNoAudio); // 播放無聲音版本 - await Task.Delay(100); - _mediaPlayerSecondary.Play(_media); // 播放有聲音版本 - - await SyncPlayersAsync(); - } - public async Task Play() - { - _mediaPlayerPrimary.Play(); // 播放無聲音版本 - await Task.Delay(100); - _mediaPlayerSecondary.Play(); // 播放有聲音版本 - - await SyncPlayersAsync(); - } - - public void Pause() - { - _mediaPlayerPrimary.Pause(); - _mediaPlayerSecondary.Pause(); - } - - public void Stop() - { - _mediaPlayerPrimary.Stop(); - _mediaPlayerSecondary.Stop(); + if (_media == null) return; + _mediaPlayer.Play(_media); } + public void Pause() => _mediaPlayer.Pause(); + public void Stop() => _mediaPlayer.Stop(); #endregion - #region Sync - private async Task SyncPlayersAsync() - { - while (_mediaPlayerPrimary.IsPlaying && _mediaPlayerSecondary.IsPlaying) - { - var t1 = _mediaPlayerPrimary.Time; - var t2 = _mediaPlayerSecondary.Time; - - if (Math.Abs(t1 - t2) > 100) - _mediaPlayerSecondary.Time = t1; - - await Task.Delay(200); - } - } - #endregion - - #region Mute - public bool Mute(bool isMuted) - { - _mediaPlayerSecondary.Mute = isMuted; - return _mediaPlayerSecondary.Mute; - } - #endregion - - #region Volume - public void SetVolume(int volume) - { - _mediaPlayerPrimary.Volume = volume; - _mediaPlayerSecondary.Volume = volume; - } - public int GetVolume() => _mediaPlayerSecondary?.Volume ?? 0; - #endregion - - #region Audio Tracks + #region Audio public List GetAudioTracks() { var result = new List(); - var media = _mediaPlayerSecondary.Media; + var media = _mediaPlayer.Media; if (media == null) return result; @@ -161,26 +86,29 @@ namespace DualScreenDemo.Services return result; } - public void SetAudioTrackToAsync(int trackIndex) + public void SetAudioTrack(int trackIndex) { var audioTracks = GetAudioTracks(); if (trackIndex < 0 || trackIndex >= audioTracks.Count) return; - _mediaPlayerSecondary.SetAudioTrack(audioTracks[trackIndex].Id); + _mediaPlayer.SetAudioTrack(audioTracks[trackIndex].Id); } #endregion + #region Volume + public void SetVolume(int volume) => _mediaPlayer.Volume = volume; + public int GetVolume() => _mediaPlayer.Volume; + public bool Mute(bool isMuted) => _mediaPlayer.Mute = isMuted; + #endregion + #region Dispose public void Dispose() { if (_disposed) return; - _mediaPlayerPrimary?.Dispose(); - _mediaPlayerSecondary?.Dispose(); + _mediaPlayer.Dispose(); _media?.Dispose(); - _mediaNoAudio?.Dispose(); - _libVLC?.Dispose(); + _libVLC.Dispose(); - _mediaNoAudio = null; _media = null; _disposed = true; } diff --git a/VideoPlayerForm.cs b/VideoPlayerForm.cs index 5612b08..5c45eb1 100644 --- a/VideoPlayerForm.cs +++ b/VideoPlayerForm.cs @@ -67,7 +67,7 @@ namespace DualScreenDemo private const int WS_EX_TOPMOST = 0x00000008; private const uint SWP_NOZORDER = 0x0004; //private MediaServicePrimary primary = new MediaServicePrimary(); - private MediaService secondary= new MediaService(); + private MediaService secondary; public static OverlayForm overlayForm; public bool isMuted = false; public int previousVolume = 100; @@ -119,9 +119,10 @@ namespace DualScreenDemo 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); + { + secondary= new MediaService(PrimaryForm.Instance.videoPanel.Handle); + secondary.SetVideoOutput(this.Handle, secondMonitor.Bounds.Width, secondMonitor.Bounds.Height); + PlayNextSong(); } } @@ -322,7 +323,7 @@ namespace DualScreenDemo // 渲染媒體文件 secondary.LoadMedia(pathToPlay,song.isPublicSong ? 0 : 1); secondary.Mute(isMuted); - secondary.PlayAsync(); + secondary.Play(); // 音量處理 //SetVolume(isMuted ? 0 : previousVolume); @@ -459,7 +460,7 @@ namespace DualScreenDemo { isVocalRemoved=!isVocalRemoved; int trackIndex = isVocalRemoved ? 1:0; - secondary.SetAudioTrackToAsync(trackIndex); + secondary.SetAudioTrack(trackIndex); OverlayForm.MainForm.ShowTopRightLabelTime(isVocalRemoved ? "無人聲" : "有人聲"); } } diff --git a/superstar.csproj b/superstar.csproj index fb63755..9aaf71c 100644 --- a/superstar.csproj +++ b/superstar.csproj @@ -9,6 +9,7 @@ enable enable true + true app.manifest true CS8618,CS8602,CS8622,CS8625,CS8600,CS8603,CS8601,CS8604,CS4014