65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF:网络摄像头控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (145投票s)

2011年11月18日

CPOL

3分钟阅读

viewsIcon

1536767

downloadIcon

90544

一个用于显示和录制网络摄像头视频的WPF控件

WPF_WebcamControl/Screenshot_1.png

引言

WebcamControl 是一个 WPF 控件,用于使用 Expression Encoder SDK 显示和录制摄像头视频。它还支持拍摄摄像头视频快照。

特点

该控件具有以下功能:

  • 显示摄像头视频;
  • 支持保存摄像头视频(格式为 .wmv);
  • 支持保存视频快照。

要求

注意:要使用该控件,请确保您的项目生成平台设置为 x86。

网络摄像头

您可以通过在 NuGet 包管理器控制台中运行以下命令来通过 NuGet 安装该控件:

Install-Package WpfWebcamControl -Version 3.3.1

属性

 名称描述
WPF_WebcamControl/Property.pngVideoFileFormat获取保存摄像头视频的格式。这是一个类型为 String 的依赖属性。
WPF_WebcamControl/Property.pngSnapshotFormat获取或设置保存摄像头预览快照的格式。这是一个类型为 ImageFormat 的依赖属性。
WPF_WebcamControl/Property.pngVideoDevice获取或设置要使用的摄像头。这是一个类型为 Microsoft.Expression.Encoder.Devices.EncoderDevice 的依赖属性。
WPF_WebcamControl/Property.pngAudioDevice获取或设置要使用的麦克风。这是一个类型为 Microsoft.Expression.Encoder.Devices.EncoderDevice 的依赖属性。
WPF_WebcamControl/Property.pngVideoName获取或设置视频文件的名称 – 不应包含文件扩展名。这是一个类型为 String 的依赖属性。
WPF_WebcamControl/Property.pngVideoDirectory获取或设置保存摄像头视频的文件夹。这是一个类型为 String 的依赖属性。
WPF_WebcamControl/Property.pngImageDirectory获取或设置保存视频快照的文件夹。这是一个类型为 String 的依赖属性。
WPF_WebcamControl/Property.png比特率获取或设置比特率。这是一个类型为 Integer 的依赖属性。(默认值为 2000)。
WPF_WebcamControl/Property.pngFrameRate获取或设置帧率,单位为每秒帧数。这是一个类型为 Integer 的依赖属性。(默认值为 15)。
WPF_WebcamControl/Property.pngFrameSize获取或设置视频配置文件的尺寸。这是一个类型为 System.Drawing.Size 的依赖属性。(默认值为 320x240)。
WPF_WebcamControl/Property.pngIsRecording获取一个值,指示是否正在进行视频录制。这是一个类型为 Boolean 的依赖属性。

方法

 名称描述
WPF_WebcamControl/Method.pngStartPreview开始显示摄像头预览。(如果指定的设备已被另一个应用程序使用,则会引发 Microsoft.Expression.Encoder.SystemErrorException)。
WPF_WebcamControl/Method.pngStopPreview停止显示摄像头预览,并停止任何正在进行的录制。
WPF_WebcamControl/Method.pngStartRecording开始录制摄像头视频,并返回视频文件的完整路径。
WPF_WebcamControl/Method.pngStopRecording停止录制摄像头视频。
WPF_WebcamControl/Method.pngTakeSnapshot保存摄像头预览的快照,并返回图像文件的完整路径。

示例

以下示例展示了如何使用该控件。该示例包含一个 Webcam 控件、两个用于列出视频和音频设备的组合框,以及用于调用各种控件功能的按钮。

<Window x:Class="WPF_Webcam_CS.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_Webcam_CS"
        xmlns:cam="clr-namespace:WebcamControl;assembly=WebcamControl"
        Title="WPF Webcam" Height="495" Width="353">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="75"/>
            <RowDefinition Height="132"/>
        </Grid.RowDefinitions>

        <cam:Webcam x:Name="WebcamViewer" Margin="10"
                    FrameRate="30"
                    FrameSize="640, 480"                    
                    ImageDirectory="C:\WebcamSnapshots"
                    VideoDirectory="C:\VideoClips"
                    VideoDevice="{Binding SelectedItem, ElementName=VidDevices}"
                    AudioDevice="{Binding SelectedItem, ElementName=AudDevices}"/>

        <Grid Grid.Row="1" HorizontalAlignment="Center">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <TextBlock Text="Video Device" VerticalAlignment="Center"/>
            <ComboBox x:Name="VidDevices" Grid.Column="1" Margin="10,0,0,0"
                      Width="210" Height="24"
                      ItemsSource="{Binding VideoDevices}"
                      DisplayMemberPath="Name"
                      SelectedIndex="0"/>
            
            <TextBlock Text="Audio Device" Grid.Row="1" VerticalAlignment="Center"/>
            <ComboBox x:Name="AudDevices" Grid.Row="1" Grid.Column="1"
                      Width="210" Height="24" Margin="10,0,0,0"
                      ItemsSource="{Binding AudioDevices}"
                      DisplayMemberPath="Name"
                      SelectedIndex="0"/>            
        </Grid>

        <Grid Grid.Row="2" HorizontalAlignment="Center" Margin="0,10">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Button Content="Start Capture" 
                    Height="24" Width="112" HorizontalAlignment="Right" Margin="0,0,10,0"
                    Click="StartCaptureButton_Click"/>
            
            <Button Grid.Column="1" Content="Stop Capture"
                    Height="24" Width="112" HorizontalAlignment="Left" Margin="10,0,0,0"  
                    Click="StopCaptureButton_Click"/>

            <Button Grid.Row="1" Content="Start Recording"
                    Height="24" Width="112" HorizontalAlignment="Right" Margin="0,0,10,0"
                    Click="StartRecordingButton_Click"/>

            <Button Grid.Row="1" Grid.Column="1" Content="Stop Recording" 
                    Height="24" Width="115" HorizontalAlignment="Left" Margin="10,0,0,0"
                    Click="StopRecordingButton_Click"/>

            <Button Grid.Row="2" Grid.ColumnSpan="2" Content="Take Snapshot"
                    Height="24" Width="120" HorizontalAlignment="Center" 
                    Click="TakeSnapshotButton_Click"/>
        </Grid>       
    </Grid>
</Window>
using System.Windows;
using Microsoft.Expression.Encoder.Devices;
using System.Collections.ObjectModel;

namespace WPF_Webcam_CS
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {       
        public Collection<EncoderDevice> VideoDevices { get; set; }
        public Collection<EncoderDevice> AudioDevices { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;

            VideoDevices = EncoderDevices.FindDevices(EncoderDeviceType.Video);
            AudioDevices = EncoderDevices.FindDevices(EncoderDeviceType.Audio);
        }

        private void StartCaptureButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Display webcam video
                WebcamViewer.StartPreview();
            }
            catch (Microsoft.Expression.Encoder.SystemErrorException ex)
            {
                MessageBox.Show("Device is in use by another application");
            }
        }

        private void StopCaptureButton_Click(object sender, RoutedEventArgs e)
        {
            // Stop the display of webcam video.
            WebcamViewer.StopPreview();
        }

        private void StartRecordingButton_Click(object sender, RoutedEventArgs e)
        {
            // Start recording of webcam video to harddisk.
            WebcamViewer.StartRecording();
        }

        private void StopRecordingButton_Click(object sender, RoutedEventArgs e)
        {
            // Stop recording of webcam video to harddisk.
            WebcamViewer.StopRecording();
        }

        private void TakeSnapshotButton_Click(object sender, RoutedEventArgs e)
        {
            // Take snapshot of webcam video.
            WebcamViewer.TakeSnapshot();
        }
    }
}
Imports Microsoft.Expression.Encoder.Devices
Imports System.Collections.ObjectModel

Public Class MainWindow

    Public Property VideoDevices As Collection(Of EncoderDevice)
    Public Property AudioDevices As Collection(Of EncoderDevice)

    Public Sub New()
        InitializeComponent()

        DataContext = Me

        VideoDevices = EncoderDevices.FindDevices(EncoderDeviceType.Video)
        AudioDevices = EncoderDevices.FindDevices(EncoderDeviceType.Audio)
    End Sub

    Private Sub StartCaptureButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        ' Display webcam video
        Try
            WebcamViewer.StartPreview()
        Catch ex As Microsoft.Expression.Encoder.SystemErrorException
            MessageBox.Show("Device is in use by another application")
        End Try
    End Sub

    Private Sub StopCaptureButton_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
        ' Stop the display of webcam video
        WebcamViewer.StopPreview()
    End Sub

    Private Sub StartRecordingButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        ' Start recording of webcam video
        WebcamViewer.StartRecording()
    End Sub

    Private Sub StopRecordingButton_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
        ' Stop recording of webcam video
        WebcamViewer.StopRecording()
    End Sub

    Private Sub TakeSnapshotButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        ' Take snapshot of webcam video
        WebcamViewer.TakeSnapshot()
    End Sub
End Class

MainWindow 的代码隐藏中,VideoDevicesAudioDevices 集合属性已设置为可用的音频和视频设备。其余代码不言自明,按钮单击事件的处理程序会调用各种控件功能。

网络摄像头

以下是用户控件的 XAML 标记:

<UserControl x:Class="Webcam" 
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
             MinHeight="100" MinWidth="100"
             mc:Ignorable="d">
    <Grid>
        <WindowsFormsHost Name="WinFormsHost" Margin="0" Background="{x:Null}">
            <wf:Panel x:Name="WebcamPanel"/>
        </WindowsFormsHost>
    </Grid>
</UserControl>

要显示来自摄像头的视频,用户控件使用了 LiveJob 类,该类位于 Microsoft.Expression.Encoder.Live 命名空间下。LiveJob 提供了从摄像头等实时源编码视频和音频的例程。摄像头视频显示在 WinForms 的 Panel 中,该 Panel 托管在 WindowsFormsHost 中。

''' <summary>
''' Displays webcam video.
''' </summary>
Public Sub StartPreview()
    Try
        If isPreviewing Then StopPreview()

        Job = New LiveJob
        Dim frameDuration As Long = CLng(FrameRate * Math.Pow(10, 7))

        deviceSource = Job.AddDeviceSource(_videoDevice, _audioDevice)
        deviceSource.PickBestVideoFormat(FrameSize, frameDuration)
        deviceSource.PreviewWindow = New PreviewWindow(New HandleRef(WebcamPanel, WebcamPanel.Handle))

        Job.OutputFormat.VideoProfile = New AdvancedVC1VideoProfile With {.Size = FrameSize,
                .FrameRate = FrameRate, .Bitrate = New ConstantBitrate(Bitrate)}

        Job.ActivateSource(deviceSource)

        isPreviewing = True
    Catch ex As SystemErrorException
        Throw New SystemErrorException
    End Try
End Sub

LiveJob 对象用于使用 LiveJob.StartEncoding() 方法保存摄像头视频。

''' <summary>
''' Starts the recording of webcam images to a video file.
''' </summary>
Public Function StartRecording() As String
    If Not isPreviewing Then Throw New PreviewNotStartedException("Recording can't be done without previewing.")
    If String.IsNullOrEmpty(VideoDirectory) Then Throw New DirectoryNotSpecifiedException("Video directory has not been specified.")
    If Not Directory.Exists(VideoDirectory) Then Directory.CreateDirectory(VideoDirectory)
    If IsRecording Then StopRecording()

    Dim filePath As String
    If String.IsNullOrEmpty(VideoName) Then
        filePath = Path.Combine(VideoDirectory, "Webcam " & TimeStamp() & VideoFileFormat)
    Else
        filePath = Path.Combine(VideoDirectory, VideoName & VideoFileFormat)
    End If

    Dim archiveFormat As New FileArchivePublishFormat(filePath)

    If Job.PublishFormats.Count > 0 Then Job.PublishFormats.Clear()

    Job.PublishFormats.Add(archiveFormat)
    Job.StartEncoding()

    SetValue(IsRecordingPropertyKey, True)
    Return filePath
End Function

摄像头视频的快照实际上只是 WinForms Panel 的快照。

''' <summary>
''' Takes a snapshot of an webcam image.
''' The size of the image will be equal to the size of the control.
''' </summary>
Public Function TakeSnapshot() As String
    If Not isPreviewing Then Throw New PreviewNotStartedException("Recording can't be done before previewing.")
    If String.IsNullOrEmpty(ImageDirectory) Then Throw New DirectoryNotSpecifiedException("Image directory has not been specified")
    If Not Directory.Exists(ImageDirectory) Then Directory.CreateDirectory(ImageDirectory)

    Dim panelWidth As Integer = WebcamPanel.Width
    Dim panelHeight As Integer = WebcamPanel.Height
    Dim filePath As String = Path.Combine(ImageDirectory, "Snapshot " & TimeStamp() & "." & SnapshotFormat.ToString())
    Dim pnt As Point = WebcamPanel.PointToScreen(New Point(WebcamPanel.ClientRectangle.X, WebcamPanel.ClientRectangle.Y))

    Using bmp As New Bitmap(panelWidth, panelHeight)
        Using grx As Graphics = Graphics.FromImage(bmp)
            grx.CopyFromScreen(pnt, Point.Empty, New Size(panelWidth, panelHeight))
        End Using
        bmp.Save(filePath, SnapshotFormat)
    End Using

    Return filePath
End Function

Public Function TakeSnapshot(ByVal name As String) As String
    If String.IsNullOrEmpty(name) Then Throw New ArgumentNullException()
    If Not isPreviewing Then Throw New PreviewNotStartedException("Recording can't be done before previewing.")
    If String.IsNullOrEmpty(ImageDirectory) Then Throw New DirectoryNotSpecifiedException("Image directory has not been specified")
    If Not Directory.Exists(ImageDirectory) Then Directory.CreateDirectory(ImageDirectory)

    Dim panelWidth As Integer = WebcamPanel.Width
    Dim panelHeight As Integer = WebcamPanel.Height
    Dim filePath As String = Path.Combine(ImageDirectory, name & "." & SnapshotFormat.ToString())
    Dim pnt As Point = WebcamPanel.PointToScreen(New Point(WebcamPanel.ClientRectangle.X, WebcamPanel.ClientRectangle.Y))

    Using bmp As New Bitmap(panelWidth, panelHeight)
        Using grx As Graphics = Graphics.FromImage(bmp)
            grx.CopyFromScreen(pnt, Point.Empty, New Size(panelWidth, panelHeight))
        End Using
        bmp.Save(filePath, SnapshotFormat)
    End Using

    Return filePath
End Function

如果您想查看用户控件的其余代码,请查看源代码下载中的用户控件库项目。

结论

希望您觉得本文很有用。如有任何疑问,您可以留下评论,我会尽力回答。

历史

  • 2011年11月18日:初次发布
  • 2011年11月19日:更新代码
  • 2012年3月31日:添加了快照功能。
  • 2012年11月17日:添加了 FrameRateFrameSize 属性。
  • 2013年10月30日:v3.0
  • 2014年7月24日:v3.1,
    • 摄像头预览现在会随控件一起缩放;
    • StartCapture() 重命名为 StartPreview()
    • StopCapture() 重命名为 StopPreview()
  • 2016年5月6日:添加了 BitrateVideoName 属性。
  • 2017年9月24日:更新代码 – StartRecording()TakeSnapshot() 返回完整文件路径。
© . All rights reserved.