之前的项目需要镜像翻转摄像头视频,使用Aforge.Net来处理视频。
一开始考虑直接从Aforge.Net读取没一帧视频图片,然后复制给WPF的Image控件,这种方法基本很卡,所以放弃了。
考虑到Aforge.net 返回的图片是Bitmap的,所以打算直接在WPF中嵌入Winform的picturebox来实现视频。
【注意】如果在WPF中嵌入Winform窗口是不可透明的。
【Xaml】
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" WindowState="Normal" Background="Black"
WindowStyle="None" BorderThickness="0"
Left="0" Top="0" WindowStartupLocation="Manual"
xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
xmlns:winform="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
>
<wfi:WindowsFormsHost x:Name="video" >
<winform:PictureBox x:Name="pb" SizeMode="StretchImage" BackColor="Black" ForeColor="Black"/>
</wfi:WindowsFormsHost>
</Window>
【c#代码】
#region << Field >>
private VideoCaptureDevice VCD;
private static double VideoResolutionWidth = 1280;
#endregion
[DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
private static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);
[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern long GetWindowLong(IntPtr handle, int style);
public const int GWL_STYLE = -16;
public const int GWL_EXSTYLE = -20;
public const long WS_CAPTION = 0x00C00000L;
public const long WS_CAPTION_2 = 0X00C0000L;
private static int VideoClipXPos = 0;
private static int VideoClipWidth = 0;
public static System.Drawing.Bitmap CurrentPhoto = null;
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.Closing += MainWindow_Closing;
}
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
StopCamera(true);
}
private void StopCamera(bool flag)
{
if (flag)
VCD.Stop();
else
VCD.Start();
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.Width = 1080;
this.Height = 1920;
this.grid.Width = 809;
this.grid.Height = 1082;
SetPhotoView();
//this.pb.Width = 3413;
//this.pb.Height = 1920;
// this.video.Margin = new Thickness(-666, 0, 0, 0);
// this.Left = -(3413 - 1080) / 2;
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (videoDevices.Count != 0)
{
VCD = new VideoCaptureDevice(videoDevices[0].MonikerString);
foreach (var item in VCD.VideoCapabilities)
{
if (item.FrameSize.Width == VideoResolutionWidth)
{
VCD.VideoResolution = item;
VCD.NewFrame += new AForge.Video.NewFrameEventHandler(videoSource1_NewFrame);
VCD.VideoSourceError += new AForge.Video.VideoSourceErrorEventHandler(videoSource1_VideoSourceError);
VCD.Start();
break;
}
}
}
///设置窗口无边框
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
long oldstyle = GetWindowLong(windowHandle, GWL_STYLE);
SetWindowLong(windowHandle, GWL_STYLE, (int)(oldstyle & (~(WS_CAPTION | WS_CAPTION_2))));
}
private void SetPhotoView()
{
VideoClipWidth = 536;
VideoClipXPos = (1280 - VideoClipWidth) / 2;
}
void videoSource1_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
{
bool isGood = true;
System.Drawing.Bitmap img = (System.Drawing.Bitmap)eventArgs.Frame.Clone(new System.Drawing.Rectangle(VideoClipXPos, 0, VideoClipWidth,
eventArgs.Frame.Height),
System.Drawing.Imaging.PixelFormat.Undefined);
this.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
img.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX);
}
catch (Exception e)
{
isGood = false;
}
if (isGood)
{
if (this.pb.Image != null)
{
this.pb.Image.Dispose();
this.pb.Image = null;
}
this.pb.Image = img;
CurrentPhoto = img;
}
else
{
img.Dispose();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}));
}
void videoSource1_VideoSourceError(object sender, AForge.Video.VideoSourceErrorEventArgs eventArgs)
{
}
}
上诉代码是实现特定区域的视频截取,这种方法在1080p,和1920*2 1080 的分辨率下不卡,(测试机器是i5,4G内存的)
为了不卡,肯定会丢帧,但是还是能接受的。
其实最好的办法是将窗体的handle,传给c++来进行视频绘制。 但一般情况下,我觉得这种方法够用了。