This commit is contained in:
parent
ee2a75ba8d
commit
4e7b6a2f92
|
|
@ -0,0 +1,28 @@
|
||||||
|
using Agora.Rtc;
|
||||||
|
using Prism.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Demo.Common.Events
|
||||||
|
{
|
||||||
|
public class AudioMuteStateChangedEvent : PubSubEvent<AudioMuteInfo>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AudioMuteInfo
|
||||||
|
{
|
||||||
|
public AudioMuteInfo(long uid, bool isMute, string channel)
|
||||||
|
{
|
||||||
|
Channel = channel;
|
||||||
|
UId = uid;
|
||||||
|
IsMute = isMute;
|
||||||
|
}
|
||||||
|
public string Channel { get; set; }
|
||||||
|
public long UId { get; set; }
|
||||||
|
public bool IsMute { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Prism.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Demo.Common.Events
|
||||||
|
{
|
||||||
|
public class VideoMuteStateChangedEvent:PubSubEvent<VideoMuteInfo>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VideoMuteInfo
|
||||||
|
{
|
||||||
|
public VideoMuteInfo(long uid, bool isMute, string channel)
|
||||||
|
{
|
||||||
|
Channel = channel;
|
||||||
|
UId = uid;
|
||||||
|
IsMute = isMute;
|
||||||
|
}
|
||||||
|
public string Channel { get; set; }
|
||||||
|
public long UId { get; set; }
|
||||||
|
public bool IsMute { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
using Demo.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Demo.Common.Extensions
|
||||||
|
{
|
||||||
|
public static class ObservableCollectionExtension
|
||||||
|
{
|
||||||
|
public static void InitUserList<T>(this ObservableCollection<T> list, int count) where T : User
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
list.Add((T)User.GetInit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int AddUser<T>(this ObservableCollection<T> list, T user) where T : User
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
if (list[i].Id == 0)
|
||||||
|
{
|
||||||
|
list[i] = user;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ClearUserById<T>(this ObservableCollection<T> list, long Id) where T : User
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
if (list[i].Id == Id)
|
||||||
|
{
|
||||||
|
list[i] = (T)User.GetInit();
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearAllUser<T>(this ObservableCollection<T> list) where T : User
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
list[i] = (T)User.GetInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,12 +9,16 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Demo.Common.Helpers
|
namespace Demo.Common.Helpers
|
||||||
{
|
{
|
||||||
public class AgoraHelper : IRtcEngineEventHandler
|
public class AgoraHelper
|
||||||
{
|
{
|
||||||
public static uint _localUId;
|
public static uint _localUId;
|
||||||
public static IRtcEngine _RtcEngineInstance;
|
public static IRtcEngine _RtcEngineInstance;
|
||||||
public static int Init(string appId, uint localUid = 0)
|
public static int Init(string appId, uint localUid = 0)
|
||||||
{
|
{
|
||||||
|
if (_RtcEngineInstance != null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (null == _RtcEngineInstance)
|
if (null == _RtcEngineInstance)
|
||||||
{
|
{
|
||||||
_RtcEngineInstance = RtcEngine.CreateAgoraRtcEngine();
|
_RtcEngineInstance = RtcEngine.CreateAgoraRtcEngine();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Demo.Common.Helpers
|
||||||
|
{
|
||||||
|
public class AvatatHelper
|
||||||
|
{
|
||||||
|
private static Image cameraCloseImage = null;
|
||||||
|
public static Image CameraCloseImage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (cameraCloseImage != null)
|
||||||
|
{
|
||||||
|
return cameraCloseImage;
|
||||||
|
}
|
||||||
|
using (var fileStream = new FileStream($@"Assets/camera_close.png", FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
using var _imageStream = new MemoryStream();
|
||||||
|
fileStream.CopyTo(_imageStream);
|
||||||
|
cameraCloseImage = Image.FromStream(_imageStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cameraCloseImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取姓名对应的圆形图片
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="width"></param>
|
||||||
|
/// <param name="height"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Bitmap GetNickNameImage(string name, int width = 132, int height = 132)
|
||||||
|
{
|
||||||
|
string color = "#2B6EC0";
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
string firstName = GetFirstTwoChars(name);
|
||||||
|
Bitmap img = new Bitmap(width, height);
|
||||||
|
Graphics g = Graphics.FromImage(img);
|
||||||
|
|
||||||
|
// 启用抗锯齿
|
||||||
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
|
|
||||||
|
// 创建圆形路径
|
||||||
|
GraphicsPath path = new GraphicsPath();
|
||||||
|
path.AddEllipse(0, 0, width, height);
|
||||||
|
|
||||||
|
// 设置裁剪区域为圆形
|
||||||
|
g.SetClip(path);
|
||||||
|
|
||||||
|
// 填充背景色
|
||||||
|
System.Drawing.Brush brush = new SolidBrush(ColorTranslator.FromHtml(color));
|
||||||
|
g.FillEllipse(brush, 0, 0, width, height);
|
||||||
|
|
||||||
|
// 填充文字
|
||||||
|
Font font = new Font("微软雅黑", 50);
|
||||||
|
SizeF firstSize = g.MeasureString(firstName, font);
|
||||||
|
g.DrawString(firstName, font, System.Drawing.Brushes.White,
|
||||||
|
new PointF((img.Width - firstSize.Width) / 2, (img.Height - firstSize.Height) / 2));
|
||||||
|
|
||||||
|
g.Dispose();
|
||||||
|
path.Dispose();
|
||||||
|
brush.Dispose();
|
||||||
|
font.Dispose();
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static string GetFirstTwoChars(string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
return string.Empty;
|
||||||
|
input = input.Trim();
|
||||||
|
return input[..Math.Min(2, input.Length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存图片到磁盘
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="targetFile"></param>
|
||||||
|
/// <param name="width"></param>
|
||||||
|
/// <param name="height"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Bitmap SaveNameImage(string name, string targetFile, int width = 132, int height = 132)
|
||||||
|
{
|
||||||
|
Bitmap img = GetNickNameImage(name, width, height);
|
||||||
|
img.Save(targetFile + ".png", ImageFormat.Png);
|
||||||
|
img.Dispose();
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ using Prism.Ioc;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
using Demo.Common.Events;
|
using Demo.Common.Events;
|
||||||
using Masuit.Tools;
|
using Masuit.Tools;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Demo.Common.Helpers
|
namespace Demo.Common.Helpers
|
||||||
{
|
{
|
||||||
|
|
@ -19,10 +21,16 @@ namespace Demo.Common.Helpers
|
||||||
aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户加入频道回调
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <param name="remoteUid"></param>
|
||||||
|
/// <param name="elapsed"></param>
|
||||||
public override void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed)
|
public override void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed)
|
||||||
{
|
{
|
||||||
//base.OnUserJoined(connection, remoteUid, elapsed);
|
//base.OnUserJoined(connection, remoteUid, elapsed);
|
||||||
Console.WriteLine($@"新用户加入: channelId:{connection.channelId} localuid: {connection.localUid} remoteuid:{remoteUid} elapsed: {elapsed}");
|
Console.WriteLine($@" {DateTime.Now.ToString("HH:mm:ss")}新用户加入: channelId:{connection.channelId} localuid: {connection.localUid} remoteuid:{remoteUid} elapsed: {elapsed}");
|
||||||
|
|
||||||
|
|
||||||
//var aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
//var aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||||
|
|
@ -36,6 +44,12 @@ namespace Demo.Common.Helpers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户离开频道回调
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <param name="remoteUid"></param>
|
||||||
|
/// <param name="reason"></param>
|
||||||
public override void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason)
|
public override void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason)
|
||||||
{
|
{
|
||||||
//var aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
//var aggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||||||
|
|
@ -48,6 +62,13 @@ namespace Demo.Common.Helpers
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 音频音量提示回调
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <param name="speakers"></param>
|
||||||
|
/// <param name="speakerNumber"></param>
|
||||||
|
/// <param name="totalVolume"></param>
|
||||||
public override void OnAudioVolumeIndication(RtcConnection connection, AudioVolumeInfo[] speakers, uint speakerNumber, int totalVolume)
|
public override void OnAudioVolumeIndication(RtcConnection connection, AudioVolumeInfo[] speakers, uint speakerNumber, int totalVolume)
|
||||||
{
|
{
|
||||||
if (speakers.Length <= 0)
|
if (speakers.Length <= 0)
|
||||||
|
|
@ -57,12 +78,134 @@ namespace Demo.Common.Helpers
|
||||||
speakers.ForEach(sp =>
|
speakers.ForEach(sp =>
|
||||||
{
|
{
|
||||||
sp.uid = sp.uid == 0 ? AgoraHelper._localUId : sp.uid;
|
sp.uid = sp.uid == 0 ? AgoraHelper._localUId : sp.uid;
|
||||||
|
|
||||||
|
|
||||||
aggregator.GetEvent<AudioVolumeIndicationEvent>().Publish(sp);
|
aggregator.GetEvent<AudioVolumeIndicationEvent>().Publish(sp);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地用户音频发布状态改变回调。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="oldState"></param>
|
||||||
|
/// <param name="newState"></param>
|
||||||
|
/// <param name="elapseSinceLastState"></param>
|
||||||
|
public override void OnAudioPublishStateChanged(string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState)
|
||||||
|
{
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_IDLE:
|
||||||
|
break;
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_NO_PUBLISHED:
|
||||||
|
aggregator.GetEvent<AudioMuteStateChangedEvent>().Publish(new AudioMuteInfo(AgoraHelper._localUId, true, channel));
|
||||||
|
break;
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_PUBLISHING:
|
||||||
|
break;
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_PUBLISHED:
|
||||||
|
aggregator.GetEvent<AudioMuteStateChangedEvent>().Publish(new AudioMuteInfo(AgoraHelper._localUId, false, channel));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 远端用户音频订阅状态发生改变回调。
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="channel"></param>
|
||||||
|
///// <param name="uid"></param>
|
||||||
|
///// <param name="oldState"></param>
|
||||||
|
///// <param name="newState"></param>
|
||||||
|
///// <param name="elapseSinceLastState"></param>
|
||||||
|
//public override void OnAudioSubscribeStateChanged(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState)
|
||||||
|
//{
|
||||||
|
// //Console.WriteLine($@"OnAudioSubscribeStateChanged====================== channel {channel} uid {uid} {newState}");
|
||||||
|
|
||||||
|
// switch (newState)
|
||||||
|
// {
|
||||||
|
// case STREAM_SUBSCRIBE_STATE.SUB_STATE_IDLE:
|
||||||
|
// break;
|
||||||
|
// case STREAM_SUBSCRIBE_STATE.SUB_STATE_NO_SUBSCRIBED:
|
||||||
|
// aggregator.GetEvent<AudioMuteStateChangedEvent>().Publish(new AudioMuteInfo(uid, true, channel));
|
||||||
|
// break;
|
||||||
|
// case STREAM_SUBSCRIBE_STATE.SUB_STATE_SUBSCRIBING:
|
||||||
|
// break;
|
||||||
|
// case STREAM_SUBSCRIBE_STATE.SUB_STATE_SUBSCRIBED:
|
||||||
|
// aggregator.GetEvent<AudioMuteStateChangedEvent>().Publish(new AudioMuteInfo(uid, false, channel));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远端用户音频订阅状态发生改变回调。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <param name="remoteUid"></param>
|
||||||
|
/// <param name="muted"></param>
|
||||||
|
public override void OnUserMuteAudio(RtcConnection connection, uint remoteUid, bool muted)
|
||||||
|
{
|
||||||
|
aggregator.GetEvent<AudioMuteStateChangedEvent>().Publish(new AudioMuteInfo(remoteUid, muted, connection.channelId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远端用户视频订阅状态发生改变回调。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
/// <param name="remoteUid"></param>
|
||||||
|
/// <param name="muted"></param>
|
||||||
|
public override void OnUserMuteVideo(RtcConnection connection, uint remoteUid, bool muted)
|
||||||
|
{
|
||||||
|
aggregator.GetEvent<VideoMuteStateChangedEvent>().Publish(new VideoMuteInfo(remoteUid, muted, connection.channelId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地用户视频发布状态改变回调。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="oldState"></param>
|
||||||
|
/// <param name="newState"></param>
|
||||||
|
/// <param name="elapseSinceLastState"></param>
|
||||||
|
public override void OnVideoPublishStateChanged(VIDEO_SOURCE_TYPE source, string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState)
|
||||||
|
{
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_IDLE:
|
||||||
|
break;
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_NO_PUBLISHED:
|
||||||
|
aggregator.GetEvent<VideoMuteStateChangedEvent>().Publish(new VideoMuteInfo(AgoraHelper._localUId, true, channel));
|
||||||
|
break;
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_PUBLISHING:
|
||||||
|
break;
|
||||||
|
case STREAM_PUBLISH_STATE.PUB_STATE_PUBLISHED:
|
||||||
|
aggregator.GetEvent<VideoMuteStateChangedEvent>().Publish(new VideoMuteInfo(AgoraHelper._localUId, false, channel));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远端用户视频订阅状态发生改变回调。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel"></param>
|
||||||
|
/// <param name="uid"></param>
|
||||||
|
/// <param name="oldState"></param>
|
||||||
|
/// <param name="newState"></param>
|
||||||
|
/// <param name="elapseSinceLastState"></param>
|
||||||
|
public override void OnVideoSubscribeStateChanged(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState)
|
||||||
|
{
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case STREAM_SUBSCRIBE_STATE.SUB_STATE_IDLE:
|
||||||
|
break;
|
||||||
|
case STREAM_SUBSCRIBE_STATE.SUB_STATE_NO_SUBSCRIBED:
|
||||||
|
aggregator.GetEvent<VideoMuteStateChangedEvent>().Publish(new VideoMuteInfo(uid, true, channel));
|
||||||
|
break;
|
||||||
|
case STREAM_SUBSCRIBE_STATE.SUB_STATE_SUBSCRIBING:
|
||||||
|
break;
|
||||||
|
case STREAM_SUBSCRIBE_STATE.SUB_STATE_SUBSCRIBED:
|
||||||
|
aggregator.GetEvent<VideoMuteStateChangedEvent>().Publish(new VideoMuteInfo(uid, false, channel));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using Prism.Mvvm;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Demo.Common.Models
|
||||||
|
{
|
||||||
|
public class AgoraDeviceInfo : BindableBase
|
||||||
|
{
|
||||||
|
public AgoraDeviceInfo(string deviceId, string deviceName, bool isSelected = false)
|
||||||
|
{
|
||||||
|
DeviceId = deviceId;
|
||||||
|
DeviceName = deviceName;
|
||||||
|
IsSelected = isSelected;
|
||||||
|
}
|
||||||
|
public string DeviceId { get; set; }
|
||||||
|
|
||||||
|
public string DeviceName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
private bool isSelected;
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get { return isSelected; }
|
||||||
|
set { SetProperty(ref isSelected, value); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
using Prism.Mvvm;
|
using Demo.Common.Helpers;
|
||||||
|
using Prism.Mvvm;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Demo.Common.Models
|
namespace Demo.Common.Models
|
||||||
{
|
{
|
||||||
|
|
@ -21,8 +24,17 @@ namespace Demo.Common.Models
|
||||||
public string UserName
|
public string UserName
|
||||||
{
|
{
|
||||||
get { return userName; }
|
get { return userName; }
|
||||||
set { SetProperty(ref userName, value); }
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref userName, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 用户头像
|
||||||
|
///// </summary>
|
||||||
|
//public Bitmap Avatar => AvatatHelper.GetNickNameImage(userName);
|
||||||
|
|
||||||
private bool isManager;
|
private bool isManager;
|
||||||
public bool IsManager
|
public bool IsManager
|
||||||
{
|
{
|
||||||
|
|
@ -38,11 +50,73 @@ namespace Demo.Common.Models
|
||||||
set { SetProperty(ref isLocal, value); }
|
set { SetProperty(ref isLocal, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
//private bool isMain;
|
private bool isMuteAudio;
|
||||||
//public bool IsMain
|
public bool IsMuteAudio
|
||||||
|
{
|
||||||
|
get { return isMuteAudio; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref isMuteAudio, value);
|
||||||
|
RaisePropertyChanged(nameof(MicoSlashLineVisibility));
|
||||||
|
RaisePropertyChanged(nameof(MicStatusText));
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
// 静音,音量置为0
|
||||||
|
MicoVolume = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isMuteVideo;
|
||||||
|
public bool IsMuteVideo
|
||||||
|
{
|
||||||
|
get { return isMuteVideo; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref isMuteVideo, value);
|
||||||
|
RaisePropertyChanged(nameof(VideoSlashLineVisibility));
|
||||||
|
RaisePropertyChanged(nameof(VideoStatusText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 麦克风音量
|
||||||
|
/// </summary>
|
||||||
|
private uint micoVolume;
|
||||||
|
public uint MicoVolume
|
||||||
|
{
|
||||||
|
get { return micoVolume; }
|
||||||
|
set { SetProperty(ref micoVolume, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于控制斜线显示的属性
|
||||||
|
public Visibility MicoSlashLineVisibility => (IsMuteAudio ? Visibility.Visible : Visibility.Collapsed);
|
||||||
|
// 用于控制斜线显示的属性
|
||||||
|
public Visibility VideoSlashLineVisibility => (IsMuteVideo ? Visibility.Visible : Visibility.Collapsed);
|
||||||
|
|
||||||
|
//用于控制麦克风文本显示的属性
|
||||||
|
public string MicStatusText => (IsMuteAudio ? "解除静音" : "静音");
|
||||||
|
|
||||||
|
//用于控制摄像头文本显示的属性
|
||||||
|
public string VideoStatusText => (IsMuteVideo ? "开启视频" : "停止视频");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static User GetInit()
|
||||||
|
{
|
||||||
|
return new User
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
UserName = "待初始化"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//void IDisposable.Dispose()
|
||||||
//{
|
//{
|
||||||
// get { return isMain; }
|
// if (Avatar != null)
|
||||||
// set { SetProperty(ref isMain, value); }
|
// {
|
||||||
|
// Avatar.Dispose();
|
||||||
|
// }
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<!--<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml" />-->
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
||||||
|
<ResourceDictionary Source="/Assets/Resource/MeetingRoomDictionary.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
<ResourceDictionary
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converter="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol">
|
||||||
|
|
||||||
|
<converter:BooleanInverseConverter x:Key="BoolInverseConverter" />
|
||||||
|
<converter:AspectRatioConverter x:Key="AspectRatioConverter" />
|
||||||
|
<!--<converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />-->
|
||||||
|
<converter:VolumeToScaleConverter x:Key="VolumeToScaleConverter" />
|
||||||
|
|
||||||
|
<Style
|
||||||
|
x:Key="WGS_ButtonDock"
|
||||||
|
BasedOn="{StaticResource ButtonBaseStyle}"
|
||||||
|
TargetType="{x:Type Button}">
|
||||||
|
<Setter Property="Width" Value="50" />
|
||||||
|
<Setter Property="Height" Value="45" />
|
||||||
|
<Setter Property="FontSize" Value="11" />
|
||||||
|
<Setter Property="Background" Value="#FFFFFF" />
|
||||||
|
<Setter Property="BorderBrush" Value="#FFFFFF" />
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#E6E8EB" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter Property="Background" Value="#c7c7c7" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style
|
||||||
|
x:Key="WGS_SplitButtonDock"
|
||||||
|
BasedOn="{StaticResource SplitButtonBaseStyle}"
|
||||||
|
TargetType="hc:SplitButton">
|
||||||
|
<Setter Property="Width" Value="10" />
|
||||||
|
<Setter Property="Height" Value="45" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="hc:SplitButton">
|
||||||
|
<hc:SimplePanel x:Name="templateRoot">
|
||||||
|
<Border Background="{TemplateBinding Background}" CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}" />
|
||||||
|
<Border
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel
|
||||||
|
Margin="{TemplateBinding Padding}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Path
|
||||||
|
x:Name="PathMain"
|
||||||
|
Width="{TemplateBinding hc:IconElement.Width}"
|
||||||
|
Height="{TemplateBinding hc:IconElement.Height}"
|
||||||
|
Data="{TemplateBinding hc:IconElement.Geometry}"
|
||||||
|
Fill="{TemplateBinding Foreground}"
|
||||||
|
SnapsToDevicePixels="True"
|
||||||
|
Stretch="Uniform" />
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="ContentPresenterMain"
|
||||||
|
Margin="6,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
RecognizesAccessKey="True"
|
||||||
|
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleButton
|
||||||
|
x:Name="PART_Arrow"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="Auto"
|
||||||
|
Height="Auto"
|
||||||
|
Padding="{Binding Padding, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ThicknessSplitConverter}, ConverterParameter='0,1,1,1'}"
|
||||||
|
hc:IconElement.Geometry="{StaticResource UpGeometry}"
|
||||||
|
hc:IconElement.Width="9"
|
||||||
|
hc:IconSwitchElement.GeometrySelected="{StaticResource DownGeometry}"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
|
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
Style="{StaticResource ToggleButtonIconTransparent}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<Popup
|
||||||
|
Margin="1"
|
||||||
|
AllowsTransparency="true"
|
||||||
|
IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
Placement="Top"
|
||||||
|
PopupAnimation="Fade"
|
||||||
|
StaysOpen="False">
|
||||||
|
<Border
|
||||||
|
x:Name="dropDownBorder"
|
||||||
|
MinWidth="{Binding ActualWidth, ElementName=templateRoot}"
|
||||||
|
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
||||||
|
Background="{DynamicResource RegionBrush}"
|
||||||
|
BorderBrush="{DynamicResource BorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}">
|
||||||
|
<ScrollViewer x:Name="DropDownScrollViewer" Margin="0,4">
|
||||||
|
<hc:SimplePanel RenderOptions.ClearTypeHint="Enabled">
|
||||||
|
<Canvas
|
||||||
|
Width="0"
|
||||||
|
Height="0"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Top">
|
||||||
|
<Rectangle
|
||||||
|
x:Name="opaqueRect"
|
||||||
|
Width="{Binding ActualWidth, ElementName=dropDownBorder}"
|
||||||
|
Height="{Binding ActualHeight, ElementName=dropDownBorder}"
|
||||||
|
Fill="{Binding Background, ElementName=dropDownBorder}" />
|
||||||
|
</Canvas>
|
||||||
|
<ContentPresenter ContentSource="DropDownContent" />
|
||||||
|
</hc:SimplePanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</hc:SimplePanel>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="Content" Value="{x:Null}">
|
||||||
|
<Setter TargetName="ContentPresenterMain" Property="Visibility" Value="Collapsed" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="hc:IconElement.Geometry" Value="{x:Null}">
|
||||||
|
<Setter TargetName="PathMain" Property="Visibility" Value="Collapsed" />
|
||||||
|
<Setter TargetName="ContentPresenterMain" Property="Margin" Value="0" />
|
||||||
|
</Trigger>
|
||||||
|
<MultiTrigger>
|
||||||
|
<MultiTrigger.Conditions>
|
||||||
|
<Condition Property="HitMode" Value="Click" />
|
||||||
|
<Condition SourceName="PART_Arrow" Property="IsChecked" Value="True" />
|
||||||
|
</MultiTrigger.Conditions>
|
||||||
|
<Setter Property="IsDropDownOpen" Value="True" />
|
||||||
|
</MultiTrigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SecondaryRegionBrush}" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource BorderBrush}" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734572431893" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4287" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M318.513292 1001.73913a40.70559 40.70559 0 0 1-17.172671-4.452173 35.617391 35.617391 0 0 1-16.536646-21.624845 36.889441 36.889441 0 0 1 3.816149-26.077019l94.767702-164.730435a34.981366 34.981366 0 0 1 30.529193-17.17267 33.073292 33.073292 0 0 1 17.17267 4.452173 34.981366 34.981366 0 0 1 12.720497 47.701864l-94.767702 164.730435a34.981366 34.981366 0 0 1-30.529192 17.17267zM685.499627 1001.73913a33.709317 33.709317 0 0 1-29.893167-17.17267l-95.403727-164.730435a34.981366 34.981366 0 0 1 12.720497-47.701864 34.981366 34.981366 0 0 1 17.808695-4.452173 34.981366 34.981366 0 0 1 29.893168 17.17267l95.403727 164.730435a33.709317 33.709317 0 0 1 3.180124 26.077019 35.617391 35.617391 0 0 1-16.536646 21.624845 38.161491 38.161491 0 0 1-17.172671 4.452173z" fill="#8a8a8a" p-id="4288"></path><path d="M216.749317 1024a34.981366 34.981366 0 0 1-34.345342-34.981366 34.981366 34.981366 0 0 1 34.345342-34.981367h572.42236a34.981366 34.981366 0 0 1 34.981366 34.981367 34.981366 34.981366 0 0 1-34.981366 34.981366zM175.407702 669.734161a410.236025 410.236025 0 0 1-88.407454-254.409937A415.324224 415.324224 0 0 1 502.324472 0.636025a407.691925 407.691925 0 0 1 254.409938 87.771428l-45.157764 50.245963A365.714286 365.714286 0 0 0 502.324472 69.962733 345.997516 345.997516 0 0 0 156.962981 415.324224a338.365217 338.365217 0 0 0 71.870808 204.8l-48.337888 52.154037zM502.324472 829.376398a415.324224 415.324224 0 0 1-284.303106-112.576398l45.157764-49.609938a363.170186 363.170186 0 0 0 239.145342 92.859627 345.361491 345.361491 0 0 0 344.725466-345.36149 342.817391 342.817391 0 0 0-92.223603-234.693168l44.52174-48.973913a395.607453 395.607453 0 0 1 117.664596 283.667081 415.324224 415.324224 0 0 1-414.688199 414.688199z" fill="#8a8a8a" p-id="4289"></path><path d="M334.413913 509.455901a190.807453 190.807453 0 0 1-25.440994-94.767702 190.807453 190.807453 0 0 1 190.807454-190.807454 190.807453 190.807453 0 0 1 94.131677 26.077019l-45.793789 52.154037a183.175155 183.175155 0 0 0-48.337888-8.268323 127.204969 127.204969 0 0 0-124.024845 123.38882 127.204969 127.204969 0 0 0 8.904348 45.157764l-50.245963 53.426087zM502.324472 608.039752a190.807453 190.807453 0 0 1-127.204969-48.337889l44.521739-49.609938a141.83354 141.83354 0 0 0 82.047205 27.985094 123.38882 123.38882 0 0 0 123.38882-123.38882 123.38882 123.38882 0 0 0-28.621118-77.595031l45.157764-48.973913a177.450932 177.450932 0 0 1 52.790062 127.204969 190.807453 190.807453 0 0 1-192.079503 192.715528z" fill="#8a8a8a" p-id="4290"></path><path d="M108.625093 905.699379a34.981366 34.981366 0 0 1-24.168944-10.176398 33.709317 33.709317 0 0 1 0-48.973913L890.299627 40.069565a36.253416 36.253416 0 0 1 24.804969-10.176397 36.253416 36.253416 0 0 1 24.804969 10.176397 34.981366 34.981366 0 0 1 0 48.973913L133.430062 895.522981a34.981366 34.981366 0 0 1-24.804969 10.176398z" fill="#8a8a8a" p-id="4291"></path></svg>
|
||||||
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733909306083" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13182" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M504.391111 734.862222h14.933333a200.675556 200.675556 0 0 0 200.533334-200.675555V200.675556A200.675556 200.675556 0 0 0 519.466667 0h-15.075556A200.675556 200.675556 0 0 0 304 200.675556v333.511111a200.675556 200.675556 0 0 0 200.391111 200.675555z" fill="#4E5461" p-id="13183"></path><path d="M843.022222 533.191111a34.986667 34.986667 0 0 0-69.973333 0 261.12 261.12 0 1 1-522.24 0 34.986667 34.986667 0 0 0-69.973333 0 331.662222 331.662222 0 0 0 296.106666 329.244445v91.448888h-121.457778a35.057778 35.057778 0 0 0 0 70.115556h312.888889a34.986667 34.986667 0 1 0 0-69.973333h-120.888889v-91.591111a331.662222 331.662222 0 0 0 295.537778-329.244445z" fill="#4E5461" p-id="13184"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734420317677" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9974" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M860.16 322.56h-15.36L716.8 419.84v168.96c0 5.12 0 10.24 5.12 10.24l128 97.28c5.12 0 5.12 5.12 10.24 5.12h5.12c5.12 0 5.12-5.12 5.12-15.36V337.92c0-5.12-5.12-10.24-10.24-15.36zM599.04 291.84H215.04S153.6 291.84 153.6 353.28V665.6c0 61.44 61.44 61.44 61.44 61.44h384s61.44 0 61.44-61.44V358.4c5.12-66.56-61.44-66.56-61.44-66.56z" fill="#666666" p-id="9975"></path></svg>
|
||||||
|
After Width: | Height: | Size: 702 B |
|
|
@ -1,6 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net6.0-windows</TargetFramework>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<UseWindowsForms>True</UseWindowsForms>
|
<UseWindowsForms>True</UseWindowsForms>
|
||||||
|
|
@ -8,8 +8,11 @@
|
||||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Assets\manager.svg" />
|
<None Remove="Assets\camera_close.svg" />
|
||||||
|
<None Remove="Assets\huatong.png" />
|
||||||
|
<None Remove="Assets\mico_btn.svg" />
|
||||||
<None Remove="Assets\more.svg" />
|
<None Remove="Assets\more.svg" />
|
||||||
|
<None Remove="Assets\video_btn.svg" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="agora_rtc_sdk" Version="4.2.1" />
|
<PackageReference Include="agora_rtc_sdk" Version="4.2.1" />
|
||||||
|
|
@ -24,10 +27,20 @@
|
||||||
<ProjectReference Include="..\..\Demo.Services\Demo.Services.csproj" />
|
<ProjectReference Include="..\..\Demo.Services\Demo.Services.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Assets\manager.svg" />
|
<Resource Include="Assets\camera_close.svg" />
|
||||||
|
<Resource Include="Assets\mico_btn.svg" />
|
||||||
<Resource Include="Assets\more.svg">
|
<Resource Include="Assets\more.svg">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Resource>
|
</Resource>
|
||||||
<Resource Include="Assets\mico.svg" />
|
<Resource Include="Assets\mico.svg" />
|
||||||
|
<Resource Include="Assets\video_btn.svg" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Assets\manager.svg">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Assets\camera_close1.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Agora.Rtc;
|
using Agora.Rtc;
|
||||||
|
using AngleSharp.Dom;
|
||||||
using Demo.Common.Events;
|
using Demo.Common.Events;
|
||||||
using Demo.Common.Helpers;
|
using Demo.Common.Helpers;
|
||||||
using Demo.Common.Models;
|
using Demo.Common.Models;
|
||||||
|
|
@ -6,10 +7,12 @@ using Demo.Services.Interfaces;
|
||||||
using HandyControl.Controls;
|
using HandyControl.Controls;
|
||||||
using HandyControl.Data;
|
using HandyControl.Data;
|
||||||
using Masuit.Tools;
|
using Masuit.Tools;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
using Prism.Commands;
|
using Prism.Commands;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
using Prism.Mvvm;
|
using Prism.Mvvm;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
namespace Meeting.V2.Demo.ViewModels
|
namespace Meeting.V2.Demo.ViewModels
|
||||||
|
|
@ -20,6 +23,15 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
{
|
{
|
||||||
this._aggregator = aggregator;
|
this._aggregator = aggregator;
|
||||||
this._agoraConfigService = agoraConfigService;
|
this._agoraConfigService = agoraConfigService;
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init()
|
||||||
|
{
|
||||||
|
AgoraHelper.Init("4a4f7be64fa1404ebda74784fe9ac381", (uint)UId);
|
||||||
|
InitMicoDevices();
|
||||||
|
InitPlaybackDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _title = "Prism Application";
|
private string _title = "Prism Application";
|
||||||
|
|
@ -46,17 +58,46 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
set { SetProperty(ref isJoin, value); }
|
set { SetProperty(ref isJoin, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ObservableCollection<AgoraDeviceInfo> audioDevices = new();
|
||||||
|
public ObservableCollection<AgoraDeviceInfo> AudioDevices
|
||||||
|
{
|
||||||
|
get { return audioDevices; }
|
||||||
|
set { SetProperty(ref audioDevices, value); }
|
||||||
|
}
|
||||||
|
private ObservableCollection<AgoraDeviceInfo> playbackDevices = new();
|
||||||
|
public ObservableCollection<AgoraDeviceInfo> PlaybackDevices
|
||||||
|
{
|
||||||
|
get { return playbackDevices; }
|
||||||
|
set { SetProperty(ref playbackDevices, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private User localUser;
|
||||||
|
public User LocalUser
|
||||||
|
{
|
||||||
|
get { return localUser; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref localUser, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public DelegateCommand JoinChannelCommand => new DelegateCommand(JoinChannel);
|
public DelegateCommand JoinChannelCommand => new DelegateCommand(JoinChannel);
|
||||||
async void JoinChannel()
|
async void JoinChannel()
|
||||||
{
|
{
|
||||||
AgoraHelper.Init("4a4f7be64fa1404ebda74784fe9ac381", (uint)UId);
|
//var rrr = AgoraHelper._RtcEngineInstance.RegisterLocalUserAccount("4a4f7be64fa1404ebda74784fe9ac381", UId.ToString());
|
||||||
|
|
||||||
var rtcToken = await _agoraConfigService.GetRtcTokenAsync();
|
var rtcToken = await _agoraConfigService.GetRtcTokenAsync();
|
||||||
if (!string.IsNullOrWhiteSpace(rtcToken))
|
if (!string.IsNullOrWhiteSpace(rtcToken))
|
||||||
Application.Current.Properties["rtctoken"] = rtcToken;
|
Application.Current.Properties["rtctoken"] = rtcToken;
|
||||||
|
|
||||||
|
//var result = AgoraHelper._RtcEngineInstance.EnableAudio();
|
||||||
|
//Console.WriteLine($@"{nameof(JoinChannel)}-EnableAudio : {result}");
|
||||||
|
|
||||||
var result = AgoraHelper._RtcEngineInstance.EnableVideo();
|
var result = AgoraHelper._RtcEngineInstance.EnableVideo();
|
||||||
Console.WriteLine($@"{nameof(JoinChannel)}-EnableVideo : {result}");
|
Console.WriteLine($@"{nameof(JoinChannel)}-EnableVideo : {result}");
|
||||||
|
|
||||||
|
|
@ -76,28 +117,75 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
options.autoSubscribeVideo.SetValue(true);
|
options.autoSubscribeVideo.SetValue(true);
|
||||||
|
|
||||||
AgoraHelper._RtcEngineInstance.JoinChannel(System.Windows.Application.Current.Properties["rtctoken"].ToString(), "999", (uint)UId, options);
|
AgoraHelper._RtcEngineInstance.JoinChannel(System.Windows.Application.Current.Properties["rtctoken"].ToString(), "999", (uint)UId, options);
|
||||||
|
//AgoraHelper._RtcEngineInstance.JoinChannelWithUserAccount(System.Windows.Application.Current.Properties["rtctoken"].ToString(), "999", UId.ToString(), options);
|
||||||
|
|
||||||
///启用用户音量提示。
|
///启用用户音量提示。
|
||||||
AgoraHelper._RtcEngineInstance.EnableAudioVolumeIndication(200, 5, false);
|
AgoraHelper._RtcEngineInstance.EnableAudioVolumeIndication(200, 3, false);
|
||||||
|
|
||||||
_aggregator.GetEvent<UserJoinEvent>().Publish(new User
|
LocalUser = new User
|
||||||
{
|
{
|
||||||
Id = UId,
|
Id = UId,
|
||||||
UserName = UId.ToString(),
|
UserName = UId.ToString(),
|
||||||
IsManager = true,
|
IsManager = true,
|
||||||
IsLocal = true
|
IsLocal = true,
|
||||||
});
|
IsMuteVideo = false,
|
||||||
|
IsMuteAudio = false
|
||||||
|
};
|
||||||
|
_aggregator.GetEvent<UserJoinEvent>().Publish(LocalUser);
|
||||||
|
|
||||||
IsJoin = !IsJoin;
|
IsJoin = !IsJoin;
|
||||||
Growl.Success("加入频道成功");
|
Growl.Success("加入频道成功");
|
||||||
|
UserInfo userInfo = new UserInfo();
|
||||||
|
var rrra = AgoraHelper._RtcEngineInstance.GetUserInfoByUserAccount(UId.ToString(), ref userInfo);
|
||||||
|
|
||||||
|
|
||||||
|
//GetDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelegateCommand JoinChannel4AudienceCommand => new DelegateCommand(JoinChannelForAudience);
|
||||||
|
private async void JoinChannelForAudience()
|
||||||
|
{
|
||||||
|
AgoraHelper.Init("4a4f7be64fa1404ebda74784fe9ac381", (uint)UId);
|
||||||
|
|
||||||
|
var rtcToken = await _agoraConfigService.GetRtcTokenAsync();
|
||||||
|
if (!string.IsNullOrWhiteSpace(rtcToken))
|
||||||
|
Application.Current.Properties["rtctoken"] = rtcToken;
|
||||||
|
|
||||||
|
var result = AgoraHelper._RtcEngineInstance.EnableVideo();
|
||||||
|
AgoraHelper._RtcEngineInstance.EnableLocalAudio(false);
|
||||||
|
AgoraHelper._RtcEngineInstance.MuteLocalVideoStream(false);
|
||||||
|
AgoraHelper._RtcEngineInstance.MuteAllRemoteVideoStreams(true);
|
||||||
|
AgoraHelper._RtcEngineInstance.MuteAllRemoteAudioStreams(true);
|
||||||
|
|
||||||
|
|
||||||
|
ChannelMediaOptions options = new ChannelMediaOptions();
|
||||||
|
options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
|
||||||
|
options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_AUDIENCE);
|
||||||
|
options.audienceLatencyLevel.SetValue(AUDIENCE_LATENCY_LEVEL_TYPE.AUDIENCE_LATENCY_LEVEL_LOW_LATENCY);
|
||||||
|
//// 发布麦克风采集的音频流
|
||||||
|
//options.publishMicrophoneTrack.SetValue(true);
|
||||||
|
//// 发布摄像头采集的视频流
|
||||||
|
//options.publishCameraTrack.SetValue(true);
|
||||||
|
// 自动订阅所有音频流
|
||||||
|
options.autoSubscribeAudio.SetValue(true);
|
||||||
|
// 自动订阅所有视频流
|
||||||
|
options.autoSubscribeVideo.SetValue(true);
|
||||||
|
|
||||||
|
AgoraHelper._RtcEngineInstance.JoinChannel(System.Windows.Application.Current.Properties["rtctoken"].ToString(), "999", (uint)UId, options);
|
||||||
|
IsJoin = !IsJoin;
|
||||||
|
Growl.Success("加入频道成功");
|
||||||
|
|
||||||
|
|
||||||
|
//GetDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DelegateCommand LeavelChannelCommand => new DelegateCommand(LeavelChannel);
|
public DelegateCommand LeavelChannelCommand => new DelegateCommand(LeavelChannel);
|
||||||
void LeavelChannel()
|
void LeavelChannel()
|
||||||
{
|
{
|
||||||
|
//AgoraHelper._RtcEngineInstance.DisableAudio();
|
||||||
AgoraHelper._RtcEngineInstance.LeaveChannel();
|
AgoraHelper._RtcEngineInstance.LeaveChannel();
|
||||||
AgoraHelper._RtcEngineInstance.StopPreview();
|
|
||||||
AgoraHelper._RtcEngineInstance.DisableVideo();
|
AgoraHelper._RtcEngineInstance.DisableVideo();
|
||||||
|
AgoraHelper._RtcEngineInstance.StopPreview();
|
||||||
|
|
||||||
|
|
||||||
_aggregator.GetEvent<UserLeaveEvent>().Publish(new User
|
_aggregator.GetEvent<UserLeaveEvent>().Publish(new User
|
||||||
|
|
@ -113,6 +201,115 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
Growl.Success("离开频道成功");
|
Growl.Success("离开频道成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DelegateCommand OperMicoCommand => new DelegateCommand(ExecuteOperMicoCommand);
|
||||||
|
|
||||||
|
void ExecuteOperMicoCommand()
|
||||||
|
{
|
||||||
|
if (LocalUser == null)
|
||||||
|
{
|
||||||
|
Growl.Error("请先加入频道!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 静音
|
||||||
|
var result = AgoraHelper._RtcEngineInstance.MuteLocalAudioStream(!LocalUser.IsMuteAudio);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
Growl.Error($@"开启音频错误code:{result}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelegateCommand OperVideoCommand => new DelegateCommand(ExecuteOperVideoCommand);
|
||||||
|
|
||||||
|
void ExecuteOperVideoCommand()
|
||||||
|
{
|
||||||
|
if (LocalUser == null)
|
||||||
|
{
|
||||||
|
Growl.Error("请先加入频道!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!LocalUser.IsMuteVideo)
|
||||||
|
{
|
||||||
|
// 停止本地预览
|
||||||
|
AgoraHelper._RtcEngineInstance.StopPreview();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AgoraHelper._RtcEngineInstance.StartPreview();
|
||||||
|
}
|
||||||
|
// 关闭摄像头
|
||||||
|
var result = AgoraHelper._RtcEngineInstance.MuteLocalVideoStream(!LocalUser.IsMuteVideo);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
Growl.Error($@"开启视频错误code:{result}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换麦克风设备
|
||||||
|
/// </summary>
|
||||||
|
public DelegateCommand<string> ChangeAudioDeviceCommand => new DelegateCommand<string>(ExecuteChangeAudioDeviceCommand);
|
||||||
|
void ExecuteChangeAudioDeviceCommand(string audioDeviceId)
|
||||||
|
{
|
||||||
|
var audioDeviceManager = AgoraHelper._RtcEngineInstance.GetAudioDeviceManager();
|
||||||
|
audioDeviceManager.SetRecordingDevice(audioDeviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换扬声器设备
|
||||||
|
/// </summary>
|
||||||
|
public DelegateCommand<string> ChangePlaybackDeviceCommand => new DelegateCommand<string>(ExecuteChangePlaybackDeviceCommand);
|
||||||
|
void ExecuteChangePlaybackDeviceCommand(string playbackDeviceId)
|
||||||
|
{
|
||||||
|
var audioDeviceManager = AgoraHelper._RtcEngineInstance.GetAudioDeviceManager();
|
||||||
|
audioDeviceManager.SetPlaybackDevice(playbackDeviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void InitMicoDevices()
|
||||||
|
{
|
||||||
|
AudioDevices.Clear();
|
||||||
|
var audioDeviceManager = AgoraHelper._RtcEngineInstance.GetAudioDeviceManager();
|
||||||
|
var defaultAudioDeviceId = string.Empty;
|
||||||
|
var defaultAudioDeviceName = string.Empty;
|
||||||
|
audioDeviceManager.GetRecordingDefaultDevice(ref defaultAudioDeviceId, ref defaultAudioDeviceName);
|
||||||
|
defaultAudioDeviceName = $@"系统默认({defaultAudioDeviceName})";
|
||||||
|
|
||||||
|
AudioDevices.Add(new AgoraDeviceInfo(defaultAudioDeviceId, defaultAudioDeviceName, true));
|
||||||
|
// 获取所有麦克风设备
|
||||||
|
var deviceInfos = audioDeviceManager.EnumerateRecordingDevices();
|
||||||
|
|
||||||
|
foreach (var deviceInfo in deviceInfos)
|
||||||
|
{
|
||||||
|
AudioDevices.Add(new AgoraDeviceInfo(deviceInfo.deviceId, deviceInfo.deviceName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitPlaybackDevices()
|
||||||
|
{
|
||||||
|
PlaybackDevices.Clear();
|
||||||
|
var audioDeviceManager = AgoraHelper._RtcEngineInstance.GetAudioDeviceManager();
|
||||||
|
var defaultPlaybackDeviceId = string.Empty;
|
||||||
|
var defaultPlaybackDeviceName = string.Empty;
|
||||||
|
audioDeviceManager.GetPlaybackDefaultDevice(ref defaultPlaybackDeviceId, ref defaultPlaybackDeviceName);
|
||||||
|
defaultPlaybackDeviceName = $@"系统默认({defaultPlaybackDeviceName})";
|
||||||
|
|
||||||
|
PlaybackDevices.Add(new AgoraDeviceInfo(defaultPlaybackDeviceId, defaultPlaybackDeviceName, true));
|
||||||
|
// 获取所有扬声器
|
||||||
|
var deviceInfos = audioDeviceManager.EnumeratePlaybackDevices();
|
||||||
|
|
||||||
|
foreach (var deviceInfo in deviceInfos)
|
||||||
|
{
|
||||||
|
PlaybackDevices.Add(new AgoraDeviceInfo(deviceInfo.deviceId, deviceInfo.deviceName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
using Demo.Common.Events;
|
using Agora.Rtc;
|
||||||
|
using Demo.Common.Events;
|
||||||
|
using Demo.Common.Extensions;
|
||||||
|
using Masuit.Tools;
|
||||||
using Meeting.V2.Demo.Core.Mvvm;
|
using Meeting.V2.Demo.Core.Mvvm;
|
||||||
using Prism.Commands;
|
using Prism.Commands;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
using Prism.Regions;
|
using Prism.Regions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
|
@ -12,6 +16,7 @@ using User = Demo.Common.Models.User;
|
||||||
|
|
||||||
namespace Meeting.V2.Demo.ViewModels
|
namespace Meeting.V2.Demo.ViewModels
|
||||||
{
|
{
|
||||||
|
|
||||||
public class VideoAreaViewModel : RegionViewModelBase
|
public class VideoAreaViewModel : RegionViewModelBase
|
||||||
{
|
{
|
||||||
public VideoAreaViewModel(IRegionManager regionManager,
|
public VideoAreaViewModel(IRegionManager regionManager,
|
||||||
|
|
@ -21,8 +26,16 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
|
|
||||||
aggregator.GetEvent<UserJoinEvent>().Subscribe((a) => Application.Current.Dispatcher.BeginInvoke(() =>
|
aggregator.GetEvent<UserJoinEvent>().Subscribe((a) => Application.Current.Dispatcher.BeginInvoke(() =>
|
||||||
{
|
{
|
||||||
|
// 不新增,避免重复创建销毁控件
|
||||||
UserInfos.Add(a);
|
UserInfos.AddUser(a);
|
||||||
|
//for (var i = 0; i < UserInfos.Count; i++)
|
||||||
|
//{
|
||||||
|
// if (UserInfos[i].Id == 0)
|
||||||
|
// {
|
||||||
|
// UserInfos[i] = a;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
}));
|
}));
|
||||||
aggregator.GetEvent<UserLeaveEvent>().Subscribe((a) =>
|
aggregator.GetEvent<UserLeaveEvent>().Subscribe((a) =>
|
||||||
{
|
{
|
||||||
|
|
@ -30,27 +43,30 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
{
|
{
|
||||||
if (a.IsLocal)
|
if (a.IsLocal)
|
||||||
{
|
{
|
||||||
UserInfos.Clear();
|
//for (var i = 0; i < UserInfos.Count; i++)
|
||||||
|
//{
|
||||||
|
// UserInfos[i] = new User() { Id = 0, UserName = "待初始化" };
|
||||||
|
//}
|
||||||
|
UserInfos.ClearAllUser();
|
||||||
FocusUser = null;
|
FocusUser = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < UserInfos.Count; i++)
|
|
||||||
{
|
UserInfos.ClearUserById(a.Id);
|
||||||
if (UserInfos[i].Id == a.Id)
|
//for (int i = 0; i < UserInfos.Count; i++)
|
||||||
{
|
//{
|
||||||
UserInfos.RemoveAt(i);
|
// if (UserInfos[i].Id == a.Id)
|
||||||
return;
|
// {
|
||||||
}
|
// UserInfos[i] = User.GetInit();
|
||||||
}
|
// //UserInfos.RemoveAt(i);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
//UserInfos.Add(new UserInfo() { Id = 1, UserName = "张三", IsManager = true });
|
|
||||||
//UserInfos.Add(new UserInfo() { Id = 2, UserName = "李四", IsManager = false });
|
UserInfos.InitUserList(6);
|
||||||
//UserInfos.Add(new UserInfo() { Id = 3, UserName = "李四", IsManager = false });
|
|
||||||
//UserInfos.Add(new UserInfo() { Id = 4, UserName = "李四", IsManager = false });
|
|
||||||
//UserInfos.Add(new UserInfo() { Id = 4, UserName = "李四", IsManager = false });
|
|
||||||
//UserInfos.Add(new UserInfo() { Id = 4, UserName = "李四", IsManager = false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObservableCollection<User> userInfos = new();
|
private ObservableCollection<User> userInfos = new();
|
||||||
|
|
|
||||||
|
|
@ -2,37 +2,62 @@
|
||||||
using Demo.Common.Events;
|
using Demo.Common.Events;
|
||||||
using Demo.Common.Helpers;
|
using Demo.Common.Helpers;
|
||||||
using Demo.Common.Models;
|
using Demo.Common.Models;
|
||||||
|
using DryIoc;
|
||||||
|
using HandyControl.Controls;
|
||||||
using Masuit.Tools;
|
using Masuit.Tools;
|
||||||
using Meeting.V2.Demo.Core.Mvvm;
|
using Meeting.V2.Demo.Core.Mvvm;
|
||||||
using Meeting.V2.Demo.Views;
|
using Meeting.V2.Demo.Views;
|
||||||
using Prism.Commands;
|
using Prism.Commands;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace Meeting.V2.Demo.ViewModels
|
namespace Meeting.V2.Demo.ViewModels
|
||||||
{
|
{
|
||||||
public class VideoViewModel : ViewModelBase
|
public class VideoViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
private VideoView _view;
|
private VideoView _this;
|
||||||
|
|
||||||
public VideoViewModel(IEventAggregator aggregator)
|
public VideoViewModel(IEventAggregator aggregator)
|
||||||
{
|
{
|
||||||
aggregator.GetEvent<AudioVolumeIndicationEvent>().Subscribe(SetUserVolume,
|
// 音频音量监听
|
||||||
|
aggregator.GetEvent<AudioVolumeIndicationEvent>().Subscribe((arg => UserInfo.MicoVolume = arg.volume),
|
||||||
arg =>
|
arg =>
|
||||||
{
|
{
|
||||||
return UserInfo != null && UserInfo.Id > 0 && arg.uid > 0 && arg.uid == UserInfo.Id;
|
return UserInfo != null && UserInfo.Id > 0 && arg.uid > 0 && arg.uid == UserInfo.Id && !UserInfo.IsMuteAudio;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 语音状态监听
|
||||||
|
aggregator.GetEvent<AudioMuteStateChangedEvent>().Subscribe((arg) =>
|
||||||
|
{
|
||||||
|
UserInfo.IsMuteAudio = arg.IsMute;
|
||||||
|
}, arg => UserInfo != null && UserInfo.Id > 0 && arg.UId > 0 && arg.UId == UserInfo.Id);
|
||||||
|
|
||||||
|
// 视频状态监听
|
||||||
|
aggregator.GetEvent<VideoMuteStateChangedEvent>().Subscribe((arg) =>
|
||||||
|
{
|
||||||
|
UserInfo.IsMuteVideo = arg.IsMute;
|
||||||
|
if (arg.IsMute)
|
||||||
|
{
|
||||||
|
RefreshPictureBox();
|
||||||
|
//DisplayAvatar();
|
||||||
|
}
|
||||||
|
}, arg => UserInfo != null && UserInfo.Id > 0 && arg.UId > 0 && arg.UId == UserInfo.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化控件时,将控件传入到ViewModel中
|
||||||
public DelegateCommand<VideoView> LoadedCommand => new DelegateCommand<VideoView>((u) =>
|
public DelegateCommand<VideoView> LoadedCommand => new DelegateCommand<VideoView>((u) =>
|
||||||
{
|
{
|
||||||
if (u == null)
|
if (u == null)
|
||||||
return;
|
return;
|
||||||
Console.WriteLine($@"LoadedCoommand 被执行了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
Console.WriteLine($@"LoadedCoommand 被执行了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||||
this._view = u;
|
this._this = u;
|
||||||
RenderVideoView();
|
RenderVideoView();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,61 +80,67 @@ namespace Meeting.V2.Demo.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint microphoneVolume;
|
|
||||||
/// <summary>
|
|
||||||
/// 用户麦克风音量
|
|
||||||
/// </summary>
|
|
||||||
public uint MicrophoneVolume
|
|
||||||
{
|
|
||||||
get { return microphoneVolume; }
|
|
||||||
set { SetProperty(ref microphoneVolume, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetUserVolume(AudioVolumeInfo volumeInfo)
|
|
||||||
{
|
|
||||||
MicrophoneVolume = volumeInfo.volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public DelegateCommand OperClickCommand => new DelegateCommand(() =>
|
public DelegateCommand OperClickCommand => new DelegateCommand(() =>
|
||||||
{
|
{
|
||||||
Console.WriteLine($@"id:{UserInfo.Id} userName:{UserInfo.UserName} isManager:{UserInfo.IsManager}");
|
Console.WriteLine($@"id:{UserInfo.Id} userName:{UserInfo.UserName} isManager:{UserInfo.IsManager}");
|
||||||
UserInfo.IsManager = !UserInfo.IsManager;
|
//UserInfo.IsMuteAudio = !UserInfo.IsMuteAudio;
|
||||||
userInfo.UserName = DateTime.Now.ToString();
|
//if (UserInfo.IsLocal)
|
||||||
Console.WriteLine($@"id:{UserInfo.Id} userName:{UserInfo.UserName} isManager:{UserInfo.IsManager}");
|
//{
|
||||||
//MessageBox.Show($@"id:{Id} userName:{UserName} isManager:{IsManager}");
|
// // 静音
|
||||||
|
// var result = AgoraHelper._RtcEngineInstance.MuteLocalAudioStream(!UserInfo.IsMuteAudio);
|
||||||
|
// if (result != 0)
|
||||||
|
// {
|
||||||
|
// Growl.Error($@"开启音频错误code:{result}");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (UserInfo.Id > 0)
|
||||||
|
//{
|
||||||
|
// UserInfo.Id = 0;
|
||||||
|
//}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渲染视频画面
|
||||||
|
/// </summary>
|
||||||
public void RenderVideoView()
|
public void RenderVideoView()
|
||||||
{
|
{
|
||||||
if (_view == null || UserInfo == null || UserInfo.Id <= 0)
|
if (_this == null || UserInfo == null)
|
||||||
{
|
{
|
||||||
if (_view != null)
|
|
||||||
{
|
|
||||||
//_view.pic_frame.Invoke(() =>
|
|
||||||
//{
|
|
||||||
// _view.pic_frame.Refresh();
|
|
||||||
//});
|
|
||||||
|
|
||||||
if (oldUserInfo != null)
|
|
||||||
{
|
|
||||||
AgoraHelper.ClearUserVideo((uint)(oldUserInfo.Id), (long)_view.pic_frame.Handle);
|
|
||||||
_view.pic_frame.Invoke(() =>
|
|
||||||
{
|
|
||||||
_view.pic_frame.Refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AgoraHelper.SetupUserVideo((uint)UserInfo.Id, (long)_view.pic_frame.Handle, (uint)(oldUserInfo == null ? 0 : oldUserInfo.Id));
|
if (_this != null && oldUserInfo != null && (UserInfo == null || UserInfo.Id == 0))
|
||||||
|
{
|
||||||
|
AgoraHelper.ClearUserVideo((uint)(oldUserInfo.Id), (long)_this.pic_frame.Handle);
|
||||||
|
RefreshPictureBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AgoraHelper.SetupUserVideo((uint)UserInfo.Id, (long)_this.pic_frame.Handle, (uint)(oldUserInfo == null ? 0 : oldUserInfo.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新PictureBox视图,解决最后一帧不刷新的问题
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshPictureBox()
|
||||||
|
{
|
||||||
|
_this.pic_frame.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
_this.pic_frame.Refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisplayAvatar()
|
||||||
|
{
|
||||||
|
_this.pic_frame.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
//_this.pic_frame.Image = AvatatHelper.CameraCloseImage;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@
|
||||||
xmlns:cmn="clr-namespace:Demo.Common;assembly=Demo.Common"
|
xmlns:cmn="clr-namespace:Demo.Common;assembly=Demo.Common"
|
||||||
xmlns:cnt="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
|
xmlns:cnt="clr-namespace:Demo.Common.Converters;assembly=Demo.Common"
|
||||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
|
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
xmlns:intertop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=MahApps.Microsoft.DwayneNeed"
|
xmlns:intertop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=MahApps.Microsoft.DwayneNeed"
|
||||||
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
|
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
|
||||||
xmlns:prism="http://prismlibrary.com/"
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||||
xmlns:wfc="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
|
xmlns:wfc="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
|
||||||
xmlns:wfh="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
|
xmlns:wfh="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
|
||||||
Width="962"
|
Width="962"
|
||||||
|
|
@ -15,16 +17,11 @@
|
||||||
MinWidth="1050"
|
MinWidth="1050"
|
||||||
MinHeight="642"
|
MinHeight="642"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
Background="#252525"
|
Background="#FFFFFF"
|
||||||
CloseButtonForeground="Red"
|
CloseButtonForeground="Red"
|
||||||
NonClientAreaBackground="Black"
|
NonClientAreaForeground="#7B818F"
|
||||||
NonClientAreaForeground="White"
|
OtherButtonForeground="#7B818F"
|
||||||
OtherButtonForeground="White"
|
|
||||||
WindowStartupLocation="CenterScreen">
|
WindowStartupLocation="CenterScreen">
|
||||||
<Window.Resources>
|
|
||||||
<cnt:BooleanInverseConverter x:Key="BoolInverseConverter" />
|
|
||||||
</Window.Resources>
|
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
|
|
@ -32,44 +29,193 @@
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ContentControl Grid.Row="0" prism:RegionManager.RegionName="{x:Static cmn:RegionNames.VideoRegion}" />
|
<ContentControl Grid.Row="0" prism:RegionManager.RegionName="{x:Static cmn:RegionNames.VideoRegion}" />
|
||||||
|
<!--<ScrollViewer
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalScrollBarVisibility="Hidden">
|
||||||
|
<StackPanel Margin="0,10,10,10" VerticalAlignment="Top" />
|
||||||
|
</ScrollViewer>-->
|
||||||
|
|
||||||
<ScrollViewer
|
<DockPanel Grid.Row="1">
|
||||||
|
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
|
||||||
|
<hc:TextBox
|
||||||
|
x:Name="uid"
|
||||||
|
Margin="5"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
hc:InfoElement.Placeholder="输入任意UID"
|
||||||
|
IsEnabled="False"
|
||||||
|
Text="{Binding UId}" />
|
||||||
|
<Button
|
||||||
|
Margin="5"
|
||||||
|
Command="{Binding JoinChannelCommand}"
|
||||||
|
IsEnabled="{Binding IsJoin, Converter={StaticResource BoolInverseConverter}}">
|
||||||
|
主播加入频道
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding JoinChannel4AudienceCommand}" IsEnabled="{Binding IsJoin, Converter={StaticResource BoolInverseConverter}}">
|
||||||
|
观众加入频道
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Margin="5"
|
||||||
|
Command="{Binding LeavelChannelCommand}"
|
||||||
|
IsEnabled="{Binding IsJoin}"
|
||||||
|
Style="{StaticResource ButtonDanger}">
|
||||||
|
离开频道
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock>仅主播可用</TextBlock>
|
||||||
|
<!--#region 静音开关按钮-->
|
||||||
|
<Button Command="{Binding OperMicoCommand}" Style="{StaticResource WGS_ButtonDock}">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid Width="50">
|
||||||
|
<svgc:SvgViewbox Height="19" Source="Assets/mico_btn.svg" />
|
||||||
|
<Rectangle
|
||||||
|
Width="8"
|
||||||
|
Height="14"
|
||||||
|
Margin="0,-5,0,1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Fill="#36B282"
|
||||||
|
RadiusX="3"
|
||||||
|
RadiusY="3"
|
||||||
|
RenderTransformOrigin="0.5,1">
|
||||||
|
<Rectangle.RenderTransform>
|
||||||
|
<ScaleTransform>
|
||||||
|
<ScaleTransform.ScaleY>
|
||||||
|
<Binding
|
||||||
|
Converter="{StaticResource VolumeToScaleConverter}"
|
||||||
|
Mode="OneWay"
|
||||||
|
NotifyOnTargetUpdated="True"
|
||||||
|
Path="LocalUser.MicoVolume" />
|
||||||
|
</ScaleTransform.ScaleY>
|
||||||
|
</ScaleTransform>
|
||||||
|
</Rectangle.RenderTransform>
|
||||||
|
</Rectangle>
|
||||||
|
<Line
|
||||||
|
x:Name="SlashLine"
|
||||||
|
Stroke="#FA4E32"
|
||||||
|
StrokeThickness="3"
|
||||||
|
Visibility="{Binding LocalUser.MicoSlashLineVisibility}"
|
||||||
|
X1="35"
|
||||||
|
X2="15"
|
||||||
|
Y1="0"
|
||||||
|
Y2="17" />
|
||||||
|
</Grid>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,5,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="Black"
|
||||||
|
Text="{Binding LocalUser.MicStatusText}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- 扬声器、麦克风切换按钮 -->
|
||||||
|
<hc:SplitButton
|
||||||
|
x:Name="AudioSelector"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="#4e5461"
|
||||||
|
Style="{StaticResource WGS_SplitButtonDock}">
|
||||||
|
<hc:SplitButton.DropDownContent>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Margin="10,10,10,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Style="{StaticResource TextBlockDefaultSecLight}"
|
||||||
|
Text="选择扬声器" />
|
||||||
|
<ItemsControl Margin="10,5,10,0" ItemsSource="{Binding PlaybackDevices}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<RadioButton
|
||||||
|
Margin="5,4,5,2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Content="{Binding DeviceName}"
|
||||||
|
GroupName="PlaybackDeviceGroup"
|
||||||
|
IsChecked="{Binding IsSelected}">
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="Checked">
|
||||||
|
<i:InvokeCommandAction Command="{Binding DataContext.ChangePlaybackDeviceCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding DeviceId}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
|
</RadioButton>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
<hc:Divider Margin="10,6,10,0" />
|
||||||
|
<TextBlock
|
||||||
|
Margin="10,10,10,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Style="{StaticResource TextBlockDefaultSecLight}"
|
||||||
|
Text="选择麦克风" />
|
||||||
|
<ItemsControl Margin="10,5,10,10" ItemsSource="{Binding AudioDevices}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<RadioButton
|
||||||
|
Margin="5,4,5,2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Content="{Binding DeviceName}"
|
||||||
|
GroupName="AudioDeviceGroup"
|
||||||
|
IsChecked="{Binding IsSelected}">
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="Checked">
|
||||||
|
<i:InvokeCommandAction Command="{Binding DataContext.ChangeAudioDeviceCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding DeviceId}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
|
</RadioButton>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</hc:SplitButton.DropDownContent>
|
||||||
|
</hc:SplitButton>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
Command="{Binding OperVideoCommand}"
|
||||||
|
Style="{StaticResource WGS_ButtonDock}">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid Width="50" Margin="0,4,0,2">
|
||||||
|
<svgc:SvgViewbox Height="15" Source="Assets/video_btn.svg" />
|
||||||
|
<Line
|
||||||
|
Stroke="#FA4E32"
|
||||||
|
StrokeThickness="3"
|
||||||
|
Visibility="{Binding LocalUser.VideoSlashLineVisibility}"
|
||||||
|
X1="35"
|
||||||
|
X2="10"
|
||||||
|
Y1="0"
|
||||||
|
Y2="18" />
|
||||||
|
</Grid>
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,5,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="Black"
|
||||||
|
Text="{Binding LocalUser.VideoStatusText}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
<!--<ScrollViewer
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalScrollBarVisibility="Hidden">
|
VerticalScrollBarVisibility="Hidden">
|
||||||
<StackPanel Margin="0,10,10,10" VerticalAlignment="Top" />
|
<StackPanel Margin="0,10,10,10" VerticalAlignment="Top" />
|
||||||
</ScrollViewer>
|
</ScrollViewer>-->
|
||||||
<StackPanel
|
|
||||||
Grid.Row="1"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Background="#242424"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<hc:TextBox
|
|
||||||
x:Name="uid"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
hc:InfoElement.Placeholder="输入任意UID"
|
|
||||||
IsEnabled="{Binding IsJoin, Converter={StaticResource BoolInverseConverter}}"
|
|
||||||
Text="{Binding UId}" />
|
|
||||||
<Button
|
|
||||||
Height="45"
|
|
||||||
Margin="10"
|
|
||||||
Command="{Binding JoinChannelCommand}"
|
|
||||||
IsEnabled="{Binding IsJoin, Converter={StaticResource BoolInverseConverter}}">
|
|
||||||
加入频道
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
Height="45"
|
|
||||||
Margin="10"
|
|
||||||
Command="{Binding LeavelChannelCommand}"
|
|
||||||
IsEnabled="{Binding IsJoin}">
|
|
||||||
离开频道
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
|
|
||||||
</StackPanel>
|
|
||||||
<!--<local:VideoView x:Name="videoVIew" />-->
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</hc:Window>
|
</hc:Window>
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,10 @@ namespace Meeting.V2.Demo.Views
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SplitButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,16 @@
|
||||||
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
|
xmlns:local="clr-namespace:Meeting.V2.Demo.Views"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:prism="http://prismlibrary.com/"
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||||
x:Name="root"
|
x:Name="root"
|
||||||
d:DesignHeight="530"
|
d:DesignHeight="530"
|
||||||
d:DesignWidth="960"
|
d:DesignWidth="960"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
Background="#434549"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<!--<UserControl.Resources>
|
||||||
<cnt:AspectRatioConverter x:Key="AspectRatioConverter" />
|
<cnt:AspectRatioConverter x:Key="AspectRatioConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>-->
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*" MaxHeight="155" />
|
<RowDefinition Height="*" MaxHeight="155" />
|
||||||
|
|
@ -28,9 +30,16 @@
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Padding="3"
|
Padding="3"
|
||||||
ItemsSource="{Binding UserInfos}">
|
ItemsSource="{Binding UserInfos}">
|
||||||
|
<ItemsControl.Background>
|
||||||
|
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
|
||||||
|
<GradientStop Offset="0" Color="#222426" />
|
||||||
|
<GradientStop Offset="1" Color="#525559" />
|
||||||
|
</LinearGradientBrush>
|
||||||
|
</ItemsControl.Background>
|
||||||
|
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<StackPanel
|
<VirtualizingStackPanel
|
||||||
x:Name="stack_users"
|
x:Name="stack_users"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Background="#2D3033"
|
Background="#2D3033"
|
||||||
|
|
@ -40,13 +49,13 @@
|
||||||
<i:InvokeCommandAction Command="{Binding UserStackPanelSizeChangeCommand}" CommandParameter="{Binding ElementName=stack_users}" />
|
<i:InvokeCommandAction Command="{Binding UserStackPanelSizeChangeCommand}" CommandParameter="{Binding ElementName=stack_users}" />
|
||||||
</i:EventTrigger>
|
</i:EventTrigger>
|
||||||
</i:Interaction.Triggers>-->
|
</i:Interaction.Triggers>-->
|
||||||
</StackPanel>
|
</VirtualizingStackPanel>
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<local:VideoView
|
<local:VideoView
|
||||||
Margin="3"
|
CacheMode="BitmapCache"
|
||||||
Command="{Binding DataContext.SelectedUserCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}}"
|
Command="{Binding DataContext.SelectedUserCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}}"
|
||||||
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"
|
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"
|
||||||
UserInfo="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}">
|
UserInfo="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}">
|
||||||
|
|
@ -56,11 +65,18 @@
|
||||||
<Binding ElementName="user_vid" Path="ActualWidth" />
|
<Binding ElementName="user_vid" Path="ActualWidth" />
|
||||||
<!--<Binding Path="DataContext.UserPageSize" RelativeSource="{RelativeSource AncestorType=UserControl}" />
|
<!--<Binding Path="DataContext.UserPageSize" RelativeSource="{RelativeSource AncestorType=UserControl}" />
|
||||||
<Binding Path="Margin" RelativeSource="{RelativeSource Mode=Self}" />-->
|
<Binding Path="Margin" RelativeSource="{RelativeSource Mode=Self}" />-->
|
||||||
|
|
||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</local:VideoView.Width>
|
</local:VideoView.Width>
|
||||||
|
<!-- 添加数据触发器 -->
|
||||||
|
<local:VideoView.Style>
|
||||||
|
<Style TargetType="local:VideoView">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding DataContext.Id, RelativeSource={RelativeSource AncestorType=ContentPresenter}}" Value="0">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</local:VideoView.Style>
|
||||||
</local:VideoView>
|
</local:VideoView>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
|
|
@ -70,16 +86,7 @@
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
Background="#2D3033">
|
Background="#2D3033">
|
||||||
<local:VideoView x:Name="main_video" UserInfo="{Binding DataContext.FocusUser, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}}">
|
<local:VideoView x:Name="main_video" UserInfo="{Binding DataContext.FocusUser, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}}" />
|
||||||
|
|
||||||
<!--<local:VideoView.Width>
|
|
||||||
<MultiBinding Converter="{StaticResource AspectRatioConverter}">
|
|
||||||
<Binding ElementName="border_main" Path="ActualHeight" />
|
|
||||||
<Binding ElementName="border_main" Path="ActualWidth" />
|
|
||||||
</MultiBinding>
|
|
||||||
</local:VideoView.Width>-->
|
|
||||||
|
|
||||||
</local:VideoView>
|
|
||||||
|
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
x:Name="root"
|
x:Name="root"
|
||||||
MinWidth="135"
|
MinWidth="135"
|
||||||
MinHeight="75"
|
MinHeight="75"
|
||||||
d:DesignHeight="1080"
|
d:DesignHeight="180"
|
||||||
d:DesignWidth="1920"
|
d:DesignWidth="320"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<i:Interaction.Triggers>
|
<i:Interaction.Triggers>
|
||||||
|
|
@ -26,13 +26,31 @@
|
||||||
</i:EventTrigger>
|
</i:EventTrigger>
|
||||||
</i:Interaction.Triggers>
|
</i:Interaction.Triggers>
|
||||||
|
|
||||||
<UserControl.Resources>
|
|
||||||
<cnt:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
|
||||||
<cnt:AspectRatioConverter x:Key="AspectRatioConverter" />
|
|
||||||
<cnt:VolumeToScaleConverter x:Key="VolumeToScaleConverter" />
|
|
||||||
</UserControl.Resources>
|
|
||||||
|
|
||||||
|
<Border Padding="2" Background="#5E6166">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<!-- 添加蒙层 -->
|
||||||
|
<Grid
|
||||||
|
x:Name="MaskLayer"
|
||||||
|
Panel.ZIndex="1"
|
||||||
|
Visibility="{Binding UserInfo.IsMuteVideo, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Padding="6"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="#2B6EC0"
|
||||||
|
CornerRadius="20">
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="25"
|
||||||
|
Foreground="White"
|
||||||
|
Text="{Binding UserInfo.UserName}" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<!--#region 视频区域 + 右上角按钮-->
|
<!--#region 视频区域 + 右上角按钮-->
|
||||||
<intertop:AirspaceDecorator
|
<intertop:AirspaceDecorator
|
||||||
AirspaceMode="Redirect"
|
AirspaceMode="Redirect"
|
||||||
|
|
@ -41,20 +59,22 @@
|
||||||
<WindowsFormsHost x:Name="gridHost">
|
<WindowsFormsHost x:Name="gridHost">
|
||||||
<wfc:PictureBox
|
<wfc:PictureBox
|
||||||
x:Name="pic_frame"
|
x:Name="pic_frame"
|
||||||
BackColor="#000000"
|
BackColor="#2D3033"
|
||||||
Dock="Fill"
|
Dock="Fill"
|
||||||
MouseDoubleClick="pic_frame_MouseDoubleClick"
|
MouseDoubleClick="pic_frame_MouseDoubleClick"
|
||||||
MouseEnter="pic_frame_MouseEnter"
|
MouseEnter="pic_frame_MouseEnter"
|
||||||
MouseLeave="pic_frame_MouseLeave"
|
MouseLeave="pic_frame_MouseLeave"
|
||||||
SizeMode="StretchImage" />
|
SizeMode="Zoom" />
|
||||||
</WindowsFormsHost>
|
</WindowsFormsHost>
|
||||||
</intertop:AirspaceDecorator>
|
</intertop:AirspaceDecorator>
|
||||||
|
<!-- 右上角操作按钮 -->
|
||||||
<Button
|
<Button
|
||||||
x:Name="btn_oper"
|
x:Name="btn_oper"
|
||||||
Margin="0,8,8,0"
|
Margin="0,8,8,0"
|
||||||
Padding="5"
|
Padding="5"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
|
Panel.ZIndex="2"
|
||||||
hc:BorderElement.CornerRadius="4"
|
hc:BorderElement.CornerRadius="4"
|
||||||
Background="#99727169"
|
Background="#99727169"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
|
|
@ -71,8 +91,10 @@
|
||||||
Height="22"
|
Height="22"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
|
Panel.ZIndex="2"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Border Background="#ff801a" Visibility="{Binding UserInfo.IsManager, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
<!-- 管理员图标 -->
|
||||||
|
<Border Background="#ff801a" Visibility="{Binding UserInfo.IsManager, Mode=TwoWay, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||||
<svgc:SvgViewbox
|
<svgc:SvgViewbox
|
||||||
x:Name="icon_manager"
|
x:Name="icon_manager"
|
||||||
Margin="3"
|
Margin="3"
|
||||||
|
|
@ -80,18 +102,15 @@
|
||||||
</Border>
|
</Border>
|
||||||
<Border Background="#8C000000" CornerRadius="0,8,0,0">
|
<Border Background="#8C000000" CornerRadius="0,8,0,0">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<Grid>
|
<Grid Width="18" Height="16">
|
||||||
<!-- 原始的SVG图标 -->
|
<!-- 麦克风图标 -->
|
||||||
<svgc:SvgViewbox
|
<svgc:SvgViewbox x:Name="icon_mic" Source="Assets/mico.svg" />
|
||||||
x:Name="icon_mic"
|
|
||||||
Margin="3"
|
|
||||||
Source="Assets/mico.svg" />
|
|
||||||
|
|
||||||
<!-- 音量进度条 -->
|
<!-- 音量进度条 -->
|
||||||
<Rectangle
|
<Rectangle
|
||||||
Width="7"
|
Width="7"
|
||||||
Height="12"
|
Height="12"
|
||||||
Margin="0,-4,0,0"
|
Margin="-1,-4,0,0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Fill="#36B282"
|
Fill="#36B282"
|
||||||
|
|
@ -105,14 +124,24 @@
|
||||||
Converter="{StaticResource VolumeToScaleConverter}"
|
Converter="{StaticResource VolumeToScaleConverter}"
|
||||||
Mode="OneWay"
|
Mode="OneWay"
|
||||||
NotifyOnTargetUpdated="True"
|
NotifyOnTargetUpdated="True"
|
||||||
Path="MicrophoneVolume" />
|
Path="UserInfo.MicoVolume" />
|
||||||
</ScaleTransform.ScaleY>
|
</ScaleTransform.ScaleY>
|
||||||
</ScaleTransform>
|
</ScaleTransform>
|
||||||
</Rectangle.RenderTransform>
|
</Rectangle.RenderTransform>
|
||||||
</Rectangle>
|
</Rectangle>
|
||||||
|
|
||||||
</Grid>
|
<Line
|
||||||
|
x:Name="SlashLine"
|
||||||
|
Stroke="#FA4E32"
|
||||||
|
StrokeThickness="2.5"
|
||||||
|
Visibility="{Binding UserInfo.MicoSlashLineVisibility}"
|
||||||
|
X1="15"
|
||||||
|
X2="1"
|
||||||
|
Y1="1"
|
||||||
|
Y2="16" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
<!-- 用户名 -->
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="txt_uname"
|
x:Name="txt_uname"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
|
|
@ -126,4 +155,5 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!--#endregion-->
|
<!--#endregion-->
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Border>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
||||||
|
|
@ -44,47 +44,41 @@ namespace Meeting.V2.Demo.Views
|
||||||
|
|
||||||
|
|
||||||
#region 控制右上角操作按钮显示与消失
|
#region 控制右上角操作按钮显示与消失
|
||||||
/// <summary>
|
private bool isMouseInPicFrame = false;
|
||||||
/// 控制操作按钮显示
|
private bool isMouseInBtnOper = false;
|
||||||
/// </summary>
|
|
||||||
private bool IsOperVisible = false;
|
|
||||||
|
|
||||||
private void pic_frame_MouseEnter(object sender, EventArgs e)
|
private void pic_frame_MouseEnter(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
isMouseInPicFrame = true;
|
||||||
btn_oper.Visibility = Visibility.Visible;
|
btn_oper.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void pic_frame_MouseLeave(object sender, EventArgs e)
|
private void pic_frame_MouseLeave(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 使用ConfigureAwait(false)可以避免不必要的线程上下文切换
|
isMouseInPicFrame = false;
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
// 如果鼠标既不在大控件也不在小控件内,则隐藏小控件
|
||||||
|
if (!isMouseInBtnOper)
|
||||||
// 如果需要操作UI,使用Dispatcher
|
|
||||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
// UI相关操作
|
|
||||||
if (!IsOperVisible)
|
|
||||||
{
|
{
|
||||||
btn_oper.Visibility = Visibility.Hidden;
|
btn_oper.Visibility = Visibility.Hidden;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btn_oper_MouseEnter(object sender, MouseEventArgs e)
|
private void btn_oper_MouseEnter(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
IsOperVisible = true;
|
isMouseInBtnOper = true;
|
||||||
|
|
||||||
|
//IsOperVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void btn_oper_MouseLeave(object sender, MouseEventArgs e)
|
private void btn_oper_MouseLeave(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
//await Task.Delay(50).ConfigureAwait(false);
|
isMouseInBtnOper = false;
|
||||||
|
// 如果鼠标既不在大控件也不在小控件内,则隐藏小控件
|
||||||
|
if (!isMouseInPicFrame)
|
||||||
IsOperVisible = false;
|
{
|
||||||
|
btn_oper.Visibility = Visibility.Hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue