202508130945
更換播放器 使用VLC 加入dbChange,situation 調整全部靜音寫法
This commit is contained in:
parent
b88b39c1c8
commit
732572bda2
@ -235,18 +235,13 @@ namespace DualScreenDemo
|
|||||||
{
|
{
|
||||||
if (VideoPlayerForm.Instance.isMuted)
|
if (VideoPlayerForm.Instance.isMuted)
|
||||||
{
|
{
|
||||||
|
VideoPlayerForm.Instance.Mute(false);
|
||||||
VideoPlayerForm.Instance.SetVolume(VideoPlayerForm.Instance.previousVolume);
|
|
||||||
|
|
||||||
VideoPlayerForm.Instance.isMuted = false;
|
VideoPlayerForm.Instance.isMuted = false;
|
||||||
OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.HideMuteLabel()));
|
OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.HideMuteLabel()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
VideoPlayerForm.Instance.Mute(true);
|
||||||
VideoPlayerForm.Instance.previousVolume = VideoPlayerForm.Instance.GetVolume();
|
|
||||||
VideoPlayerForm.Instance.SetVolume(-10000);
|
|
||||||
|
|
||||||
VideoPlayerForm.Instance.isMuted = true;
|
VideoPlayerForm.Instance.isMuted = true;
|
||||||
OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.ShowMuteLabel()));
|
OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.ShowMuteLabel()));
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,9 @@ namespace DBObj
|
|||||||
db.Field<string>("artistA_simplified") ?? "",
|
db.Field<string>("artistA_simplified") ?? "",
|
||||||
db.Field<string>("artistB_simplified") ?? "",
|
db.Field<string>("artistB_simplified") ?? "",
|
||||||
db.Field<string>("song_simplified")?? "",
|
db.Field<string>("song_simplified")?? "",
|
||||||
db.Field<int>("vocal")
|
db.Field<int>("vocal"),
|
||||||
|
db.Field<int>("db_change"),
|
||||||
|
db.Field<string>("situation")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,20 +8,27 @@ namespace DBObj
|
|||||||
private string Name_Simplified;
|
private string Name_Simplified;
|
||||||
private string FileName;
|
private string FileName;
|
||||||
private int HumanVoice;
|
private int HumanVoice;
|
||||||
|
private int DbChange;
|
||||||
|
private string Situation;
|
||||||
public Song(string num,string name,string name_s,string filename,int humanVoice) {
|
|
||||||
Number=num;
|
|
||||||
Name=name;
|
public Song(string num, string name, string name_s, string filename, int humanVoice, int dbChange, string situation)
|
||||||
Name_Simplified=name_s;
|
{
|
||||||
FileName=filename;
|
Number = num;
|
||||||
HumanVoice =humanVoice;
|
Name = name;
|
||||||
|
Name_Simplified = name_s;
|
||||||
|
FileName = filename;
|
||||||
|
HumanVoice = humanVoice;
|
||||||
|
DbChange = dbChange;
|
||||||
|
Situation = situation;
|
||||||
}
|
}
|
||||||
public string getNumber() => Number;
|
public string getNumber() => Number;
|
||||||
public string getName(bool IsSimplified) => IsSimplified ? Name_Simplified : Name;
|
public string getName(bool IsSimplified) => IsSimplified ? Name_Simplified : Name;
|
||||||
public string getName() =>Name;
|
public string getName() =>Name;
|
||||||
public string getFileName() => FileName;
|
public string getFileName() => FileName;
|
||||||
public int getHumanVoice() => HumanVoice;
|
public int getHumanVoice() => HumanVoice;
|
||||||
public string setNameTest(string s) => Name = Name+s;
|
public int getDbChange() => DbChange;
|
||||||
|
public string getSituation() => Situation;
|
||||||
|
public string setNameTest(string s) => Name = Name + s;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,12 +14,12 @@ namespace DBObj
|
|||||||
public PlayState state;
|
public PlayState state;
|
||||||
public SongData(string songNumber, string song, string filename, int humanVoice, bool isPublic)
|
public SongData(string songNumber, string song, string filename, int humanVoice, bool isPublic)
|
||||||
{
|
{
|
||||||
basic=new(songNumber,song,"",filename,humanVoice);
|
basic=new(songNumber,song,"",filename,humanVoice,0,"");
|
||||||
isPublicSong = isPublic;
|
isPublicSong = isPublic;
|
||||||
}
|
}
|
||||||
public SongData(string songNumber, string song, string artistA, string artistB, string filename, string artistASimplified, string artistBSimplified, string songSimplified, int humanVoice)
|
public SongData(string songNumber, string song, string artistA, string artistB, string filename, string artistASimplified, string artistBSimplified, string songSimplified, int humanVoice,int dbChange,string situation)
|
||||||
{
|
{
|
||||||
basic=new(songNumber,song,songSimplified,filename,humanVoice);
|
basic=new(songNumber,song,songSimplified,filename,humanVoice,dbChange,situation);
|
||||||
A = new Artist(artistA, artistASimplified);
|
A = new Artist(artistA, artistASimplified);
|
||||||
B = !artistB.Equals("") ? new Artist(artistB, artistBSimplified) : null;
|
B = !artistB.Equals("") ? new Artist(artistB, artistBSimplified) : null;
|
||||||
isPublicSong = false;
|
isPublicSong = false;
|
||||||
|
@ -482,7 +482,7 @@ namespace DualScreenDemo
|
|||||||
if (PrimaryForm.Instance.videoPlayerForm.isMuted)
|
if (PrimaryForm.Instance.videoPlayerForm.isMuted)
|
||||||
{
|
{
|
||||||
// 取消静音,恢复之前的音量
|
// 取消静音,恢复之前的音量
|
||||||
PrimaryForm.Instance.videoPlayerForm.SetVolume(PrimaryForm.Instance.videoPlayerForm.previousVolume);
|
PrimaryForm.Instance.videoPlayerForm.Mute(false);
|
||||||
// muteButton.Text = "Mute";
|
// muteButton.Text = "Mute";
|
||||||
PrimaryForm.Instance.videoPlayerForm.isMuted = false;
|
PrimaryForm.Instance.videoPlayerForm.isMuted = false;
|
||||||
OverlayForm.MainForm.HideMuteLabel();
|
OverlayForm.MainForm.HideMuteLabel();
|
||||||
@ -490,8 +490,7 @@ namespace DualScreenDemo
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 静音,将音量设置为-10000
|
// 静音,将音量设置为-10000
|
||||||
PrimaryForm.Instance.videoPlayerForm.previousVolume = PrimaryForm.Instance.videoPlayerForm.GetVolume();
|
PrimaryForm.Instance.videoPlayerForm.Mute(true);
|
||||||
PrimaryForm.Instance.videoPlayerForm.SetVolume(-10000);
|
|
||||||
// muteButton.Text = "Unmute";
|
// muteButton.Text = "Unmute";
|
||||||
PrimaryForm.Instance.videoPlayerForm.isMuted = true;
|
PrimaryForm.Instance.videoPlayerForm.isMuted = true;
|
||||||
OverlayForm.MainForm.ShowMuteLabel();
|
OverlayForm.MainForm.ShowMuteLabel();
|
||||||
|
@ -46,7 +46,9 @@ namespace DualScreenDemo{
|
|||||||
reader["artistA_simplified"].ToString(),
|
reader["artistA_simplified"].ToString(),
|
||||||
reader["artistB_simplified"].ToString(),
|
reader["artistB_simplified"].ToString(),
|
||||||
reader["song_simplified"].ToString(),
|
reader["song_simplified"].ToString(),
|
||||||
Convert.ToInt32(reader["vocal"])
|
Convert.ToInt32(reader["vocal"]),
|
||||||
|
Convert.ToInt32(reader["db_change"]),
|
||||||
|
reader["situation"].ToString()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ namespace DualScreenDemo
|
|||||||
public partial class PrimaryForm : Form
|
public partial class PrimaryForm : Form
|
||||||
{
|
{
|
||||||
public Panel primaryScreenPanel;
|
public Panel primaryScreenPanel;
|
||||||
|
public Panel videoPanel;
|
||||||
public Button syncServiceBellButton;
|
public Button syncServiceBellButton;
|
||||||
public Button syncCutSongButton;
|
public Button syncCutSongButton;
|
||||||
public Button syncReplayButton;
|
public Button syncReplayButton;
|
||||||
@ -21,6 +22,7 @@ namespace DualScreenDemo
|
|||||||
private void InitializeSyncScreen()
|
private void InitializeSyncScreen()
|
||||||
{
|
{
|
||||||
this.primaryScreenPanel = new System.Windows.Forms.Panel();
|
this.primaryScreenPanel = new System.Windows.Forms.Panel();
|
||||||
|
this.videoPanel = new System.Windows.Forms.Panel();
|
||||||
this.syncServiceBellButton = new System.Windows.Forms.Button();
|
this.syncServiceBellButton = new System.Windows.Forms.Button();
|
||||||
this.syncCutSongButton = new System.Windows.Forms.Button();
|
this.syncCutSongButton = new System.Windows.Forms.Button();
|
||||||
this.syncReplayButton = new System.Windows.Forms.Button();
|
this.syncReplayButton = new System.Windows.Forms.Button();
|
||||||
@ -37,9 +39,13 @@ namespace DualScreenDemo
|
|||||||
|
|
||||||
ResizeAndPositionControl(this.primaryScreenPanel, 0, 0, 1440, 900);
|
ResizeAndPositionControl(this.primaryScreenPanel, 0, 0, 1440, 900);
|
||||||
this.primaryScreenPanel.TabIndex = 0;
|
this.primaryScreenPanel.TabIndex = 0;
|
||||||
this.primaryScreenPanel.BorderStyle = BorderStyle.FixedSingle;
|
this.primaryScreenPanel.BorderStyle = BorderStyle.None;
|
||||||
this.primaryScreenPanel.BackColor = System.Drawing.Color.Black;
|
this.primaryScreenPanel.BackColor = System.Drawing.Color.Black;
|
||||||
|
|
||||||
|
ResizeAndPositionControl(this.videoPanel, 0, 0, 1200, 900);
|
||||||
|
this.videoPanel.TabIndex = 0;
|
||||||
|
this.videoPanel.BorderStyle = BorderStyle.None;
|
||||||
|
this.videoPanel.BackColor = System.Drawing.Color.Black;
|
||||||
var data=LoadConfigData();
|
var data=LoadConfigData();
|
||||||
|
|
||||||
// 同步畫面 服務鈴
|
// 同步畫面 服務鈴
|
||||||
@ -125,6 +131,7 @@ namespace DualScreenDemo
|
|||||||
|
|
||||||
this.ClientSize = new System.Drawing.Size(1440, 900);
|
this.ClientSize = new System.Drawing.Size(1440, 900);
|
||||||
this.Controls.Add(this.primaryScreenPanel);
|
this.Controls.Add(this.primaryScreenPanel);
|
||||||
|
this.primaryScreenPanel.Controls.Add(this.videoPanel);
|
||||||
this.Controls.Add(this.syncCloseButton);
|
this.Controls.Add(this.syncCloseButton);
|
||||||
this.Name = "PrimaryForm";
|
this.Name = "PrimaryForm";
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
@ -1894,21 +1894,15 @@ namespace DualScreenDemo
|
|||||||
#region 主畫面按鍵點擊事件
|
#region 主畫面按鍵點擊事件
|
||||||
private void MuteUnmuteButton_Click(object sender, EventArgs e)
|
private void MuteUnmuteButton_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (videoPlayerForm.isMuted)
|
if (videoPlayerForm.isMuted)
|
||||||
{
|
{
|
||||||
|
videoPlayerForm.Mute(false);
|
||||||
videoPlayerForm.SetVolume(videoPlayerForm.previousVolume);
|
|
||||||
|
|
||||||
videoPlayerForm.isMuted = false;
|
videoPlayerForm.isMuted = false;
|
||||||
OverlayForm.MainForm.HideMuteLabel();
|
OverlayForm.MainForm.HideMuteLabel();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
videoPlayerForm.Mute(true);
|
||||||
videoPlayerForm.previousVolume = videoPlayerForm.GetVolume();
|
|
||||||
videoPlayerForm.SetVolume(-10000);
|
|
||||||
|
|
||||||
videoPlayerForm.isMuted = true;
|
videoPlayerForm.isMuted = true;
|
||||||
OverlayForm.MainForm.ShowMuteLabel();
|
OverlayForm.MainForm.ShowMuteLabel();
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace DualScreenDemo
|
|||||||
private static PrimaryForm primaryForm; // 儲存實例的參考
|
private static PrimaryForm primaryForm; // 儲存實例的參考
|
||||||
public static Room room = new Room();
|
public static Room room = new Room();
|
||||||
|
|
||||||
public static string verSion = "Server V2.8 202508120940";
|
public static string verSion = "Server V2.8 202508130945";
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
static void Main()
|
static void Main()
|
||||||
|
@ -1,234 +1,201 @@
|
|||||||
using DirectShowLib;
|
using LibVLCSharp.Shared;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace DualScreenDemo.Services
|
namespace DualScreenDemo.Services
|
||||||
{
|
{
|
||||||
public class MediaService : Video
|
public class MediaService : IDisposable
|
||||||
{
|
{
|
||||||
public IGraphBuilder graphBuilder;
|
private readonly LibVLC _libVLC;
|
||||||
private IMediaControl mediaControl;
|
private readonly MediaPlayer _mediaPlayerPrimary;
|
||||||
public IBaseFilter renderer;
|
private readonly MediaPlayer _mediaPlayerSecondary;
|
||||||
//private IBaseFilter audioRenderer;
|
private Media? _media;
|
||||||
private IBaseFilter lavSplitter;
|
private Media? _mediaNoAudio;
|
||||||
private IVideoWindow videoWindow;
|
private bool _disposed;
|
||||||
public int Run() => (mediaControl != null) ? mediaControl.Run() : 0;
|
|
||||||
public int Stop() => (mediaControl != null) ? mediaControl.Stop() : 0;
|
|
||||||
public int Pause() => (mediaControl != null) ? mediaControl.Pause() : 0;
|
|
||||||
|
|
||||||
public void VideoPlayerForm_FormClosing()
|
public MediaService()
|
||||||
{
|
{
|
||||||
if (videoWindow != null)
|
Core.Initialize();
|
||||||
{
|
_libVLC = new LibVLC(
|
||||||
videoWindow.put_Visible(OABool.False);
|
"--aout=directsound",
|
||||||
videoWindow.put_Owner(IntPtr.Zero);
|
//"--avcodec-hw=dxva2",
|
||||||
Marshal.ReleaseComObject(videoWindow);
|
"--network-caching=300",
|
||||||
videoWindow = null;
|
"--file-caching=300",
|
||||||
}
|
"--audio-time-stretch"
|
||||||
|
);
|
||||||
|
|
||||||
|
_mediaPlayerPrimary = new MediaPlayer(_libVLC);
|
||||||
|
_mediaPlayerSecondary = new MediaPlayer(_libVLC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Player Setup
|
||||||
|
public void SetVideoFormPrimary(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;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Playback
|
||||||
|
public MediaPlayer PrimaryPlayer => _mediaPlayerPrimary;
|
||||||
|
public MediaPlayer SecondaryPlayer => _mediaPlayerSecondary;
|
||||||
|
public bool IsPlaying => _mediaPlayerSecondary.IsPlaying;
|
||||||
|
|
||||||
public bool IsAtEnd()
|
public bool IsAtEnd()
|
||||||
{
|
{
|
||||||
bool isAtEnd = false;
|
var duration = _mediaPlayerSecondary.Media?.Duration ?? 0;
|
||||||
if (mediaControl == null) { return false; }
|
var time = _mediaPlayerSecondary.Time;
|
||||||
IMediaSeeking mediaSeeking = graphBuilder as IMediaSeeking;
|
return duration > 0 && Math.Abs(duration - time) < 1000;
|
||||||
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)
|
|
||||||
|
public void LoadMedia(string filePath, int audioTrackIndex = 0)
|
||||||
{
|
{
|
||||||
if (videoWindow != null) videoWindow.put_Visible(OABool.False);
|
_media?.Dispose();
|
||||||
Stop();
|
|
||||||
SafeRelease(ref renderer);
|
|
||||||
SafeRelease(ref lavSplitter);
|
|
||||||
SafeRelease(ref graphBuilder);
|
|
||||||
|
|
||||||
|
// 建立一個完整有聲音的 Media 給 secondary 播放器
|
||||||
int hr = 0;
|
_media = new Media(_libVLC, filePath, FromType.FromPath);
|
||||||
graphBuilder = (IGraphBuilder)new FilterGraph();
|
_media.AddOption(":audio-output=directsound");
|
||||||
try
|
_media.AddOption($":audio-track={audioTrackIndex}");
|
||||||
{
|
|
||||||
lavSplitter = AddFilterByClsid(graphBuilder, "LAV Splitter", Clsid.LAVSplitter);
|
// 同時準備給 primary 播放器用的無聲音版本 Media
|
||||||
AddFilterByClsid(graphBuilder, "LAV Video Decoder", Clsid.LAVVideoDecoder);
|
_mediaNoAudio?.Dispose();
|
||||||
renderer = AddFilterByClsid(graphBuilder, "Secondary Video Renderer", Clsid.VideoRenderer);
|
_mediaNoAudio = new Media(_libVLC, filePath, FromType.FromPath);
|
||||||
//audioRenderer = AddFilterByClsid(graphBuilder, "Default DirectSound Device", Clsid.AudioRenderer);
|
_mediaNoAudio.AddOption(":no-audio"); // 關閉聲音輸出
|
||||||
mediaControl = (IMediaControl)graphBuilder;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Error initializing graph builder with second monitor: " + ex.Message);
|
|
||||||
}
|
|
||||||
hr = graphBuilder.RenderFile(filePath, null);
|
|
||||||
if (hr == 0)
|
|
||||||
SetAudioTrackTo(1);
|
|
||||||
else
|
|
||||||
Console.WriteLine("Failed to render secondary file.");
|
|
||||||
// 绑定视频窗口到副屏幕
|
|
||||||
videoWindow = renderer 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);
|
|
||||||
}
|
|
||||||
mediaControl.Run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task PlayAsync()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
#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)
|
public void SetVolume(int volume)
|
||||||
{
|
{
|
||||||
if (graphBuilder != null)
|
_mediaPlayerPrimary.Volume = volume;
|
||||||
{
|
_mediaPlayerSecondary.Volume = volume;
|
||||||
IBasicAudio basicAudio = graphBuilder as IBasicAudio;
|
|
||||||
if (basicAudio != null)
|
|
||||||
basicAudio.put_Volume( Math.Max(-10000, Math.Min(0, volume)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public int GetVolume()
|
public int GetVolume() => _mediaPlayerSecondary?.Volume ?? 0;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Audio Tracks
|
||||||
|
public List<TrackDescription> GetAudioTracks()
|
||||||
{
|
{
|
||||||
if (graphBuilder != null)
|
var result = new List<TrackDescription>();
|
||||||
|
var media = _mediaPlayerSecondary.Media;
|
||||||
|
|
||||||
|
if (media == null) return result;
|
||||||
|
|
||||||
|
if (!media.IsParsed)
|
||||||
|
media.Parse(MediaParseOptions.ParseLocal);
|
||||||
|
|
||||||
|
var tracks = media.Tracks;
|
||||||
|
if (tracks == null) return result;
|
||||||
|
|
||||||
|
foreach (var track in tracks.Where(t => t.TrackType == TrackType.Audio))
|
||||||
{
|
{
|
||||||
IBasicAudio basicAudio = graphBuilder as IBasicAudio;
|
result.Add(new TrackDescription
|
||||||
if (basicAudio != null)
|
|
||||||
{
|
{
|
||||||
int volume;
|
Id = track.Id,
|
||||||
basicAudio.get_Volume(out volume);
|
Name = !string.IsNullOrEmpty(track.Language) ? track.Language : $"Audio Track {track.Id}"
|
||||||
return volume;
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -10000;
|
return result;
|
||||||
}
|
}
|
||||||
private bool isVocalRemoved = false;
|
|
||||||
public bool ToggleVocalRemoval()
|
public async Task SetAudioTrackToAsync(int trackIndex)
|
||||||
{
|
{
|
||||||
try
|
var audioTracks = GetAudioTracks();
|
||||||
|
if (trackIndex < 0 || trackIndex >= audioTracks.Count) return;
|
||||||
|
|
||||||
|
if (!_mediaPlayerSecondary.IsPlaying)
|
||||||
{
|
{
|
||||||
IAMStreamSelect streamSelect = lavSplitter as IAMStreamSelect;
|
_mediaPlayerSecondary.Play();
|
||||||
|
await Task.Delay(500);
|
||||||
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)
|
|
||||||
{
|
_mediaPlayerSecondary.SetAudioTrack(audioTracks[trackIndex].Id);
|
||||||
Console.WriteLine(ex.Message);
|
await Task.Delay(300);
|
||||||
}
|
|
||||||
return isVocalRemoved;
|
|
||||||
}
|
}
|
||||||
public void SetAudioTrackTo(int trackIndex)
|
#endregion
|
||||||
|
|
||||||
|
#region Dispose
|
||||||
|
public void Dispose()
|
||||||
{
|
{
|
||||||
try
|
if (_disposed) return;
|
||||||
{
|
|
||||||
IAMStreamSelect streamSelect = lavSplitter as IAMStreamSelect;
|
|
||||||
|
|
||||||
if (streamSelect != null)
|
_mediaPlayerPrimary?.Dispose();
|
||||||
{
|
_mediaPlayerSecondary?.Dispose();
|
||||||
int trackCount;
|
_media?.Dispose();
|
||||||
if (streamSelect.Count(out trackCount) == 0 && trackCount > 0)
|
_mediaNoAudio?.Dispose();
|
||||||
{
|
_libVLC?.Dispose();
|
||||||
int audioTrackIndex = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < trackCount; i++)
|
_mediaNoAudio = null;
|
||||||
{
|
_media = null;
|
||||||
AMMediaType mediaType;
|
_disposed = true;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TrackDescription
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,92 +0,0 @@
|
|||||||
using DirectShowLib;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace DualScreenDemo.Services
|
|
||||||
{
|
|
||||||
public class MediaServicePrimary : Video
|
|
||||||
{
|
|
||||||
IGraphBuilder graphBuilder;
|
|
||||||
IBaseFilter lavSplitter;
|
|
||||||
IBaseFilter lavVideoDecoder;
|
|
||||||
public IBaseFilter videoRenderer;
|
|
||||||
IMediaControl mediaControl;
|
|
||||||
IBaseFilter sourceFilter;
|
|
||||||
private IVideoWindow videoWindow;
|
|
||||||
public MediaServicePrimary() { }
|
|
||||||
|
|
||||||
public int Run() => (mediaControl != null) ? mediaControl.Run() : 0;
|
|
||||||
public int Stop() => (mediaControl != null)? mediaControl.Stop() : 0;
|
|
||||||
public int Pause() => (mediaControl != null) ? mediaControl.Pause() : 0;
|
|
||||||
|
|
||||||
public void VideoPlayerForm_FormClosing() {
|
|
||||||
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 videoRenderer);
|
|
||||||
SafeRelease(ref sourceFilter);
|
|
||||||
SafeRelease(ref lavVideoDecoder);
|
|
||||||
SafeRelease(ref lavSplitter);
|
|
||||||
SafeRelease(ref graphBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RenderMediaFile(string filePath, nint Handle, int Width, int Height)
|
|
||||||
{
|
|
||||||
if (videoWindow != null) videoWindow.put_Visible(OABool.False);
|
|
||||||
StopAndReleaseResources();
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
videoWindow = videoRenderer as IVideoWindow;
|
|
||||||
if (videoWindow != null)
|
|
||||||
{
|
|
||||||
videoWindow.put_Owner(Handle);
|
|
||||||
videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
|
|
||||||
videoWindow.SetWindowPosition(0, 0, Width, Height);
|
|
||||||
videoWindow.put_Visible(OABool.True);
|
|
||||||
}
|
|
||||||
mediaControl.Run();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Error initializing graph builder for primary monitor: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
using DirectShowLib;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace DualScreenDemo.Services
|
|
||||||
{
|
|
||||||
public class Video
|
|
||||||
{
|
|
||||||
protected IBaseFilter AddFilterByClsid(IGraphBuilder graphBuilder, string name, Guid clsid)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Type filterType = Type.GetTypeFromCLSID(clsid);
|
|
||||||
object filterObject = Activator.CreateInstance(filterType);
|
|
||||||
IBaseFilter filter = filterObject as IBaseFilter;
|
|
||||||
|
|
||||||
if (filter == null)
|
|
||||||
{
|
|
||||||
IntPtr comObjectPointer = Marshal.GetIUnknownForObject(filterObject);
|
|
||||||
filter = (IBaseFilter)Marshal.GetObjectForIUnknown(comObjectPointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加过滤器到图形构建器
|
|
||||||
int hr = graphBuilder.AddFilter(filter, name);
|
|
||||||
DsError.ThrowExceptionForHR(hr);
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Exception in AddFilterByClsid: {ex.Message}");
|
|
||||||
throw; // Rethrow the exception to handle it further up the call stack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected 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;
|
|
||||||
}
|
|
||||||
protected 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+":"+pinInfo.name);
|
|
||||||
|
|
||||||
if (pinInfo.name == pinName)
|
|
||||||
{
|
|
||||||
return pins[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
protected void SafeRelease<T>(ref T comObject) where T : class
|
|
||||||
{
|
|
||||||
if (comObject != null)
|
|
||||||
{
|
|
||||||
Marshal.ReleaseComObject(comObject);
|
|
||||||
comObject = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,4 @@
|
|||||||
using System.IO; // For StreamWriter
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Media;
|
|
||||||
using DirectShowLib;
|
|
||||||
using DBObj;
|
using DBObj;
|
||||||
using OverlayFormObj;
|
using OverlayFormObj;
|
||||||
using DualScreenDemo.Services;
|
using DualScreenDemo.Services;
|
||||||
@ -69,14 +66,11 @@ namespace DualScreenDemo
|
|||||||
private const int GWL_EXSTYLE = -20;
|
private const int GWL_EXSTYLE = -20;
|
||||||
private const int WS_EX_TOPMOST = 0x00000008;
|
private const int WS_EX_TOPMOST = 0x00000008;
|
||||||
private const uint SWP_NOZORDER = 0x0004;
|
private const uint SWP_NOZORDER = 0x0004;
|
||||||
private MediaServicePrimary primary = new MediaServicePrimary();
|
//private MediaServicePrimary primary = new MediaServicePrimary();
|
||||||
private MediaService secondary = new MediaService();
|
private MediaService secondary= new MediaService();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static OverlayForm overlayForm;
|
public static OverlayForm overlayForm;
|
||||||
public bool isMuted = false;
|
public bool isMuted = false;
|
||||||
public int previousVolume = 0;
|
public int previousVolume = 100;
|
||||||
public bool isPaused = false;
|
public bool isPaused = false;
|
||||||
private bool isSyncToPrimaryMonitor = false;
|
private bool isSyncToPrimaryMonitor = false;
|
||||||
|
|
||||||
@ -87,12 +81,9 @@ namespace DualScreenDemo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Screen secondMonitor;
|
private static Screen secondMonitor;
|
||||||
private int newWidth;
|
|
||||||
private int newHeight;
|
|
||||||
|
|
||||||
public VideoPlayerForm()
|
public VideoPlayerForm()
|
||||||
{
|
{
|
||||||
Lold();
|
|
||||||
Instance = this;
|
Instance = this;
|
||||||
// this.DoubleBuffered = true;
|
// this.DoubleBuffered = true;
|
||||||
this.Load += VideoPlayerForm_Load;
|
this.Load += VideoPlayerForm_Load;
|
||||||
@ -103,32 +94,7 @@ namespace DualScreenDemo
|
|||||||
HttpServer.OnDisplayBarrage += DisplayBarrageOnOverlay;
|
HttpServer.OnDisplayBarrage += DisplayBarrageOnOverlay;
|
||||||
MonitorMediaEvents();
|
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)
|
private void VideoPlayerForm_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@ -154,14 +120,15 @@ namespace DualScreenDemo
|
|||||||
}
|
}
|
||||||
else
|
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();
|
PlayNextSong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VideoPlayerForm_FormClosing(object sender, FormClosingEventArgs e)
|
private void VideoPlayerForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||||
{
|
{
|
||||||
primary.VideoPlayerForm_FormClosing();
|
secondary.Dispose();
|
||||||
secondary.VideoPlayerForm_FormClosing();
|
|
||||||
// 清理COM
|
// 清理COM
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
@ -353,11 +320,11 @@ namespace DualScreenDemo
|
|||||||
{
|
{
|
||||||
string pathToPlay = song.getFile();
|
string pathToPlay = song.getFile();
|
||||||
// 渲染媒體文件
|
// 渲染媒體文件
|
||||||
primary.RenderMediaFile(pathToPlay,PrimaryForm.Instance.primaryScreenPanel.Handle,newWidth,newHeight);
|
secondary.LoadMedia(pathToPlay,song.isPublicSong ? 0 : 1);
|
||||||
secondary.RenderMediaFile(pathToPlay,this.Handle,secondMonitor.Bounds.Width,secondMonitor.Bounds.Height);
|
secondary.PlayAsync();
|
||||||
|
|
||||||
// 音量處理
|
// 音量處理
|
||||||
SetVolume(isMuted ? -10000 : previousVolume);
|
SetVolume(isMuted ? 0 : previousVolume);
|
||||||
|
|
||||||
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
|
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -389,8 +356,7 @@ namespace DualScreenDemo
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool isAtEnd = secondary.IsAtEnd() ;
|
if (secondary.IsAtEnd())
|
||||||
if (isAtEnd && !isPaused)
|
|
||||||
{
|
{
|
||||||
BeginInvoke(new Action(async () =>
|
BeginInvoke(new Action(async () =>
|
||||||
{
|
{
|
||||||
@ -425,21 +391,18 @@ namespace DualScreenDemo
|
|||||||
|
|
||||||
public void Play()
|
public void Play()
|
||||||
{
|
{
|
||||||
primary.Run();
|
secondary.Play();
|
||||||
secondary.Run();
|
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
OverlayForm.MainForm.HidePauseLabel();
|
OverlayForm.MainForm.HidePauseLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
primary.Stop();
|
|
||||||
secondary.Stop();
|
secondary.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
primary.Pause();
|
|
||||||
secondary.Pause();
|
secondary.Pause();
|
||||||
isPaused = true;
|
isPaused = true;
|
||||||
OverlayForm.MainForm.ShowPauseLabel();
|
OverlayForm.MainForm.ShowPauseLabel();
|
||||||
@ -477,18 +440,26 @@ namespace DualScreenDemo
|
|||||||
PrimaryForm.Instance.syncPauseButton.Visible = true;
|
PrimaryForm.Instance.syncPauseButton.Visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public bool Mute(bool isMuted)
|
||||||
|
{
|
||||||
|
return secondary.Mute(isMuted);
|
||||||
|
}
|
||||||
public void SetVolume(int volume)
|
public void SetVolume(int volume)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine($"SetVolume: {volume}");
|
||||||
secondary.SetVolume(volume);
|
secondary.SetVolume(volume);
|
||||||
}
|
}
|
||||||
public int GetVolume()
|
public int GetVolume()
|
||||||
{
|
{
|
||||||
return secondary.GetVolume();
|
return secondary.GetVolume();
|
||||||
}
|
}
|
||||||
|
private bool isVocalRemoved = false;
|
||||||
public void ToggleVocalRemoval()
|
public void ToggleVocalRemoval()
|
||||||
{
|
{
|
||||||
OverlayForm.MainForm.ShowTopRightLabelTime(secondary.ToggleVocalRemoval() ? "無人聲" : "有人聲");
|
isVocalRemoved=!isVocalRemoved;
|
||||||
|
int trackIndex = isVocalRemoved ? 1:0;
|
||||||
|
secondary.SetAudioTrackToAsync(trackIndex);
|
||||||
|
OverlayForm.MainForm.ShowTopRightLabelTime(isVocalRemoved ? "無人聲" : "有人聲");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LibVLCSharp" Version="3.9.4" />
|
||||||
|
<PackageReference Include="LibVLCSharp.WinForms" Version="3.9.4" />
|
||||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.117" />
|
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.117" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="9.0.3" />
|
<PackageReference Include="System.IO.Ports" Version="9.0.3" />
|
||||||
|
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="AxInterop.WMPLib">
|
<Reference Include="AxInterop.WMPLib">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user