最近做了一个项目,其中功能有二维、三维地图展示,视频、音频播放,视频解码同步音频等,项目过程中也遇到一些技术难题,但最终还是解决,我个人感觉最应该记录下来的就是视频局放大的功能。先上两张效果图:
根据项目需求,需要对视频进行局部放大,便于对细节地方进行查看,视频播放控件采用的是xZune.Vlc.Wpf - .Net 4.5 的VlcMediaPlayer网上有开源的代码,因为需要对视频的音频输出端口进行设置,而不是通过默认的扬声器输出,设置音频设置端口为:
this.videoPlayer.Initialize(libVlcPath, options);
libVlcPath为libvlc库文件夹路径;options为参数,包括音频输出设备(如:"-I", "dummy", "--avi-index=1", "--ignore-config", "--no-video-title", "--aout=directsound")
视频局部放大主要参考网上有位朋友发的wpf图片局部放大的原理来实现,主要实现步骤如下:
一、布局
二、截图框的移动
private void MoveRect_MouseMove(object sender, MouseEventArgs e)
{
// 鼠标按下时才移动
if (e.LeftButton == MouseButtonState.Released) return;
FrameworkElement element = sender as FrameworkElement;
//计算鼠标在X轴的移动距离
double deltaV = e.GetPosition(MoveRect).Y - MoveRect.Height / 2;
//计算鼠标在Y轴的移动距离
double deltaH = e.GetPosition(MoveRect).X - MoveRect.Width / 2; ;
//得到图片Top新位置
double newTop = deltaV + (double)MoveRect.GetValue(Canvas.TopProperty);
//得到图片Left新位置
double newLeft = deltaH + (double)MoveRect.GetValue(Canvas.LeftProperty);
//边界的判断
if (newLeft <= 0)
{
newLeft = 0;
}
//左侧图片框宽度 - 半透明矩形框宽度
if (newLeft >= (this.SmallBox.Width - this.MoveRect.Width))
{
newLeft = this.SmallBox.Width - this.MoveRect.Width;
}
if (newTop <= 0)
{
newTop = 0;
}
//左侧图片框高度度 - 半透明矩形框高度度
if (newTop >= this.SmallBox.Height - this.MoveRect.Height)
{
newTop = this.SmallBox.Height - this.MoveRect.Height;
}
MoveRect.SetValue(Canvas.TopProperty, newTop);
MoveRect.SetValue(Canvas.LeftProperty, newLeft);
}
三、图片放大
Timer事件刷新图片,30次/秒,肉眼看上去就是视频的效果啦
private void ImageTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.Dispatcher.Invoke(new Action(delegate
{
if (videoPlayer.VideoSource != null)
{
bigImg.Source = videoPlayer.VideoSource;
imageBrush.ImageSource = videoPlayer.VideoSource;
#region
//获取右侧大图框与透明矩形框的尺寸比率
double n = this.BigBox.Width / this.MoveRect.Width;
//获取半透明矩形框在左侧小图中的位置
double left = (double)this.MoveRect.GetValue(Canvas.LeftProperty);
double top = (double)this.MoveRect.GetValue(Canvas.TopProperty);
//计算和设置原图在右侧大图框中的Canvas.Left 和 Canvas.Top
bigImg.SetValue(Canvas.LeftProperty, -left * n);
bigImg.SetValue(Canvas.TopProperty, -top * n);
#endregion
}
}));
}
完整的代码:
(布局)
(功能代码)
using DevExpress.XtraEditors;
using NAudio.CoreAudioApi;
using QACDR.Common;
using QACDR.Common.Model;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using xZune.Vlc.Wpf;
namespace QACDR.UI
{
///
/// VlcWpf.xaml 的交互逻辑
///
public partial class VlcWpf : UserControl
{
///
/// VLC参数
///
private List baseVlcSettings;
///
/// vlclib路径
///
private string libVlcPath = "libvlc";
///音频设备
private MMDevice audioDevice;
///
/// 快进等级 1/2/3
///
private int rateLevel = 1;
///
/// 当前音量
///
private int currVolume = 0;
///
/// 是否静音
///
private bool bNoVoice = false;
///
/// 视频文件列表
///
private List videoList = new List();
///
/// 当前播放序号
///
private int currIndex = 0;
///
/// 图片质量
///
private int quality = 10;
private System.Timers.Timer imageTimer = null;
private ImageBrush imageBrush = new ImageBrush();
public VlcWpf()
{
InitializeComponent();
InitVlc(); // 初始化VLC
EventPublisher.ChangeVolChEvent += EventPublisher_ChangeVolChEvent;
EventPublisher.SearchDataEvent += EventPublisher_SearchDataEvent;
videoPlayer.StateChanged += VideoPlayer_StateChanged;
EventPublisher.ZoomVideoEvent += EventPublisher_ZoomVideoEvent;
int interValue = 100;
string str = ConfigurationManager.AppSettings["FreamCount"];
if (!string.IsNullOrEmpty(str))
{
interValue = Convert.ToInt32(str);
}
string strQuality = ConfigurationManager.AppSettings["FreamCount"];
if (!string.IsNullOrEmpty(strQuality))
quality = Convert.ToInt32(strQuality);
imageTimer = new System.Timers.Timer();
imageTimer.Enabled = true;
imageTimer.Interval = 30;
imageTimer.Elapsed += ImageTimer_Elapsed;
// 初始化时复制,避免内存增加
SmallBox.Background = imageBrush;
SmallBox.Visibility = Visibility.Hidden;
MoveRect.Visibility = Visibility.Hidden;
videoPlayer.Visibility = Visibility.Visible;
bigImg.Visibility = Visibility.Hidden;
}
private void EventPublisher_ZoomVideoEvent(object sender, ZoomVideoEventArgs e)
{
if (e.IsZoom)
{
imageTimer.Start();
SmallBox.Visibility = Visibility.Visible;
MoveRect.Visibility = Visibility.Visible;
BigBox.Visibility = Visibility.Visible;
videoPlayer.Visibility = Visibility.Hidden;
bigImg.Visibility = Visibility.Visible;
}
else
{
imageTimer.Stop();
SmallBox.Visibility = Visibility.Hidden;
MoveRect.Visibility = Visibility.Hidden;
BigBox.Visibility = Visibility.Hidden;
videoPlayer.Visibility = Visibility.Visible;
bigImg.Visibility = Visibility.Hidden;
}
}
private void ImageTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.Dispatcher.Invoke(new Action(delegate
{
if (videoPlayer.VideoSource != null)
{
bigImg.Source = videoPlayer.VideoSource;
imageBrush.ImageSource = videoPlayer.VideoSource;
#region
//获取右侧大图框与透明矩形框的尺寸比率
double n = this.BigBox.Width / this.MoveRect.Width;
//获取半透明矩形框在左侧小图中的位置
double left = (double)this.MoveRect.GetValue(Canvas.LeftProperty);
double top = (double)this.MoveRect.GetValue(Canvas.TopProperty);
//计算和设置原图在右侧大图框中的Canvas.Left 和 Canvas.Top
bigImg.SetValue(Canvas.LeftProperty, -left * n);
bigImg.SetValue(Canvas.TopProperty, -top * n);
#endregion
}
}));
}
///
/// 初始化VLC
///
private void InitVlc()
{
try
{
baseVlcSettings = new List { "-I", "dummy", "--avi-index=1", "--ignore-config", "--no-video-title", "--aout=directsound" };
// 初始化:设置音频输出的默认通道
bool bIsVlcInit = false;
MMDeviceEnumerator dve = new MMDeviceEnumerator();
MMDeviceCollection devices = dve.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
foreach (var device in devices)
{
if (device.ID == Properties.Settings.Default.AudioDeviceID)
{
audioDevice = device;
List vlcSettings = new List(baseVlcSettings);
string[] temp_params = audioDevice.ID.Split('.');
vlcSettings.Add(string.Format("--directx-audio-device={0}", temp_params[temp_params.Length - 1]));
this.videoPlayer.Initialize(libVlcPath, vlcSettings.ToArray());
bIsVlcInit = true;
break;
}
}
if (bIsVlcInit == false)
{
this.videoPlayer.Initialize(libVlcPath, baseVlcSettings.ToArray());
}
currVolume = 100;
videoPlayer.Volume = 100;
voiceSlider.Value = 100;
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
///
/// 播放视频
///
///
public void PlayVideo(VideoFileInfo videoInfo)
{
try
{
if (!File.Exists(videoInfo.path))
{
MessageBox.Show("视频文件不存在!");
return;
}
videoPlayer.BeginStop(new Action(delegate
{
while (videoPlayer.VlcMediaPlayer.Media != null)
{
videoPlayer.VlcMediaPlayer.Media = null;
Thread.Sleep(200);
} // 添加此段代码,修复连续播放视频时会死掉的现象
if (videoPlayer.State == xZune.Vlc.Interop.Media.MediaState.Stopped || videoPlayer.State == xZune.Vlc.Interop.Media.MediaState.NothingSpecial)
{
videoPlayer.LoadMedia(videoInfo.path);
videoPlayer.Play();
}
}));
// 获取当前播放的序号
for (int i = 0; i < videoList.Count; i++)
{
if (videoList[i].path == videoInfo.path && videoList[i].id == videoInfo.id)
{
currIndex = i;
break;
}
}
FileInfo fi = new FileInfo(videoInfo.path);
this.Dispatcher.Invoke(new Action(delegate
{
//lbTitle.Content = fi.Name;
}));
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
///
/// 设置VLC音频输出端口
///
///
public void SelectAudioDevice(string[] options)
{
try
{
videoPlayer.BeginStop(new Action(delegate
{
this.videoPlayer.Initialize(libVlcPath, options);
}));
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
///
/// 视频截图
///
public void Snapshot()
{
try
{
string path = AppDomain.CurrentDomain.BaseDirectory + "snapshot";
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
string name = path + "\\" + DateTime.Now.ToFileTime() + ".png";
videoPlayer.TakeSnapshot(name, SnapshotFormat.JPG, 30);
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
#region 控件事件
private void VideoPlayer_StateChanged(object sender, xZune.Vlc.ObjectEventArgs e)
{
if (e.Value == xZune.Vlc.Interop.Media.MediaState.Ended)
{
if (currIndex + 1 < videoList.Count)
{
// 播放下一视频
currIndex++;
PlayVideo(videoList[currIndex]);
}
else
{
this.Dispatcher.Invoke(new Action(delegate
{
//lbTitle.Content = "播放完成。";
}));
}
//EventPublisher.PictureShowEvent(this, new PictureShowEventArgs() { StateShow = ShowEnum.Stop });
}
else if (e.Value == xZune.Vlc.Interop.Media.MediaState.Paused)
{
}
}
// 声道切换事件
private void EventPublisher_ChangeVolChEvent(object sender, ChangeVolChEventArgs e)
{
if (e.CType != 2) return; // 如果不是音频所发起的,则不执行
//cbvChanel.Text = e.VolChStr;
}
// 查询数据
private void EventPublisher_SearchDataEvent(object sender, SearchDataEventArgs e)
{
string cmdText = "select * from QACDR_VIDEO where operation_id > 0 " + e.Condition + " order by start_time asc";
DataSet ds = SQLiteHelper.ExecuteDataSet(cmdText);
if (ds == null || ds.Tables.Count <= 0 || ds.Tables[0].Rows.Count <= 0) return;
videoList.Clear();
foreach (DataRow row in ds.Tables[0].Rows)
{
VideoFileInfo video = new VideoFileInfo();
video.id = Convert.ToInt32(row["id"]);
video.operation_id = Convert.ToInt32(row["operation_id"]);
video.machine_id = Convert.ToInt32(row["machine_id"]);
video.path = row["path"].ToString();
video.start_time = Convert.ToDateTime(row["start_time"]);
video.import_time = Convert.ToDateTime(row["import_time"]);
videoList.Add(video);
}
}
// 声道
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
//string str = cbvChanel.Text;
//EventPublisher.PublishChangeVolChEvent(this, str, 1);
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
// 静音/取消静音
private void Label_MouseDown(object sender, MouseButtonEventArgs e)
{
try
{
if (bNoVoice == true)
{
currVolume = videoPlayer.Volume;
videoPlayer.Volume = 0;
System.Windows.Controls.Image img = new Image();
img.Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + @"Image\NoVoice.png"));
lbVoice.Content = img;
voiceSlider.IsEnabled = false;
bNoVoice = false;
}
else
{
videoPlayer.Volume = currVolume;
System.Windows.Controls.Image img = new Image();
img.Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + @"Image\Voice.png"));
lbVoice.Content = img;
voiceSlider.IsEnabled = true;
bNoVoice = true;
}
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
// 停止
private void btnStop_Click(object sender, RoutedEventArgs e)
{
try
{
videoPlayer.BeginStop(new Action(delegate
{
this.Dispatcher.Invoke(new Action(delegate
{
//lbTitle.Content = "播放完成。";
}));
}));
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
// 快退
private void VideoBack_Click(object sender, RoutedEventArgs e)
{
try
{
float tp = videoPlayer.Position - (float)0.05;
if (tp > 0)
{
videoPlayer.Position = tp;
}
else
{
videoPlayer.Position = 0;
}
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
// 播放/暂停
private void VideoPlay_Click(object sender, RoutedEventArgs e)
{
try
{
if (videoPlayer.State == xZune.Vlc.Interop.Media.MediaState.Playing)
{
System.Windows.Controls.Border boder = (System.Windows.Controls.Border)VideoPlay.Template.FindName("btnBoder", VideoPlay);
System.Windows.Controls.Image img = (System.Windows.Controls.Image)boder.FindName("img");
img.Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + @"Image\Play.png"));
}
else if (videoPlayer.State == xZune.Vlc.Interop.Media.MediaState.Paused)
{
System.Windows.Controls.Border boder = (System.Windows.Controls.Border)VideoPlay.Template.FindName("btnBoder", VideoPlay);
System.Windows.Controls.Image img = (System.Windows.Controls.Image)boder.FindName("img");
img.Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + @"Image\Pause.png"));
}
videoPlayer.PauseOrResume();
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
// 快进
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
if (rateLevel == 1)
{
rateLevel = 2;
videoPlayer.Rate = 3.0f;
//lpSpeed.Content = "播放速度:快进x1";
}
else if (rateLevel == 2)
{
rateLevel = 3;
videoPlayer.Rate = 5.0f;
//lpSpeed.Content = "播放速度:快进x2";
}
else if (rateLevel == 3)
{
rateLevel = 4;
videoPlayer.Rate = 7.0f;
//lpSpeed.Content = "播放速度:快进x3";
}
else if (rateLevel == 4)
{
rateLevel = 1;
videoPlayer.Rate = 1;
//lpSpeed.Content = "播放速度:正常";
}
}
catch (Exception ex)
{
Log4Allen.WriteLog(typeof(VlcWpf), ex);
}
}
// 同步音频
private void cbSync_Checked(object sender, RoutedEventArgs e)
{
if (Utils.Mode == SyncModeEnum.DDS)
{
XtraMessageBox.Show("正在进行DDS同步,视频同步音频。");
return;
}
EventPublisher.PublishSetAudioSyncEvent(this, new SetAudioSyncEventArgs() { IsSync = true });
Utils.Mode = SyncModeEnum.VIDEO;
}
private void cbSync_Unchecked(object sender, RoutedEventArgs e)
{
EventPublisher.PublishSetAudioSyncEvent(this, new SetAudioSyncEventArgs() { IsSync = false });
Utils.Mode = SyncModeEnum.NONE;
}
#endregion
private void MoveRect_MouseMove(object sender, MouseEventArgs e)
{
// 鼠标按下时才移动
if (e.LeftButton == MouseButtonState.Released) return;
FrameworkElement element = sender as FrameworkElement;
//计算鼠标在X轴的移动距离
double deltaV = e.GetPosition(MoveRect).Y - MoveRect.Height / 2;
//计算鼠标在Y轴的移动距离
double deltaH = e.GetPosition(MoveRect).X - MoveRect.Width / 2; ;
//得到图片Top新位置
double newTop = deltaV + (double)MoveRect.GetValue(Canvas.TopProperty);
//得到图片Left新位置
double newLeft = deltaH + (double)MoveRect.GetValue(Canvas.LeftProperty);
//边界的判断
if (newLeft <= 0)
{
newLeft = 0;
}
//左侧图片框宽度 - 半透明矩形框宽度
if (newLeft >= (this.SmallBox.Width - this.MoveRect.Width))
{
newLeft = this.SmallBox.Width - this.MoveRect.Width;
}
if (newTop <= 0)
{
newTop = 0;
}
//左侧图片框高度度 - 半透明矩形框高度度
if (newTop >= this.SmallBox.Height - this.MoveRect.Height)
{
newTop = this.SmallBox.Height - this.MoveRect.Height;
}
MoveRect.SetValue(Canvas.TopProperty, newTop);
MoveRect.SetValue(Canvas.LeftProperty, newLeft);
}
public void ResizeCtrl()
{
}
}
}