Windows Phone 7

使用 Windows Phone 应用程序中的相机功能

Matt Stroshane

通过图片进行沟通交流是一种高效且优雅的方式,而只借助文字进行沟通是无法与之媲美的。您听说过“一图抵千言”吧;请想象一下,当您的 Windows Phone 应用程序能够直接访问相机功能时您可以解决的各种问题。从 Windows Phone 7.5 开始,您可以开始使用设备中的相机功能解决这些难于用语言描述的问题。

在本文中,我将介绍前置和后置相机、相机 API 及相关的清单功能,我还将讨论您可以在下一版本的 Windows Phone 7.5 应用程序中使用相机功能的几种不同方式。我将讨论以下内容:

  • 拍摄照片: 我将创建一个非常简单的照片应用程序。
  • 访问相机预览缓冲区: 我将介绍“相机灰度示例”。
  • 录制视频: 我将介绍“录像机示例”。

您将需要使用 Windows Phone SDK 7.1 创建一个 Windows Phone 7.5 应用程序。该 SDK 包括一些极为详尽地演示其中每个方案的代码示例。有关详细信息,请参阅 SDK 中“代码示例”页(网址为 wpdev.ms/officialsamples)上的“基本相机示例”、“相机灰度示例”和“录像机示例”。

请注意,本文不介绍自 Windows Phone 7 以来提供的相机捕捉任务。尽管该任务是一种为应用程序获取照片的简单方式,但它不允许您以编程方式拍摄照片或访问相机预览缓冲区。

Windows Phone 7.5 设备最多可以包括两个相机,即一个主相机和一个前置相机。主相机位于设备的背面,与前置相机相比,它通常可提供较高的分辨率和更多功能。这两个相机在 Windows Phone 7.5 设备中都不是必需的,因此,请确保在创建相机对象之前,在您的代码中检查这些相机的状态。稍后,我将演示如何使用静态 IsCameraTypeSupported 方法实现此目的。

在美国地区销售的许多 Windows Phone 设备均包括一个主相机,该相机具有 5MP 或更高像素的传感器、闪光灯和自动聚焦功能。前置相机是 Windows Phone 7.5 中的新增功能。

有关设备规格的详细信息,请参阅 windowsphone.com 上的“购买”选项卡。

拍摄照片

您可以使用相同的类同时访问主相机和前置相机。您将看到,选择相机类型就是在 PhotoCamera 对象的构造函数中指定一个参数。但是,从设计角度而言,您可能希望采用不同的方式来处理与前置相机的交互。例如,您可能希望从前置相机翻转图像,以便为用户提供更自然的镜像体验。

在使用 Windows Phone 7.5 应用程序拍摄照片时,您将主要使用 Microsoft.Devices 命名空间中的 PhotoCamera 类。该类对相机设置和行为提供大量控制。例如,您可以:

  • 使用 PhotoCamera.CaptureImage 方法激活相机快门
  • 使用 PhotoCamera.Focus 方法触发自动聚焦
  • 通过设置 Photo-Camera.Resolution 属性指定图片分辨率
  • 通过设置 Photo-Camera.FlashMode 属性指定闪光灯设置
  • 使用静态 CameraButtons 类中的事件合并硬件快门按钮
  • 使用 PhotoCamera.Focus­AtPoint 方法实现触控聚焦

在本文中,我将只演示第一点。有关演示如何执行所有这些操作的示例,请参阅 Windows Phone SDK 代码示例页中的“基本相机示例”。

值得注意的是,即使相机可供使用,它也可能并不支持所有这些 API。以下几种方法可以帮助确定可供使用的功能:

  • 相机: 使用 PhotoCamera.IsCameraTypeSupported 静态方法。
  • 自动聚焦: 使用 PhotoCamera.IsFocus­­-Supported 方法。
  • 图片分辨率设置: 检查 Photo­-Camera.AvailableResolutions 集合。
  • 闪光灯设置: 使用 PhotoCamera.IsFlashMode­Supported 方法。
  • 特定点聚焦: 使用 PhotoCamera.IsFocus­AtPointSupported 方法。

为使您了解如何使用应用程序拍摄照片,我将指导您创建一个简单的应用程序,该应用程序会在您触摸取景器时拍摄照片,然后将照片保存到图片中心的“本机照片”文件夹中。

可使用 Windows Phone 应用程序模板从标准 Windows Phone 项目开始。您可以在 C# 或 Visual Basic 中编写 Windows Phone 7.5 应用程序。本示例将使用 C#。

我会通过将应用程序限制为仅横向方向以及仅使用主相机来简化本示例。管理设备和两个相机的方向,如果每个都指向不同的方向,很快会令人困惑;建议使用物理设备进行测试,以确保获得所需的行为。我将在稍后更详细地介绍方向。

在 MainPage.xaml 上,更新 PhoneApplicationPage 属性,如下所示:

SupportedOrientations="Landscape" Orientation="LandscapeLeft"

然后,使用图 1 中所示的 Canvas 和 TextBlock 替换 LayoutRoot Grid 的内容。

图 1 添加 Canvas 和 TextBlock

<Canvas x:Name="viewfinderCanvas" Width="640" Height="480" Tap="viewfinder_Tapped">
  <Canvas.Background>
    <VideoBrush x:Name="viewfinderBrush">
      <VideoBrush.RelativeTransform>
        <CompositeTransform
          x:Name="viewfinderTransform"
          CenterX="0.5"
          CenterY="0.5"/>
      </VideoBrush.RelativeTransform>
    </VideoBrush>
  </Canvas.Background>
</Canvas>
<TextBlock Width="626" Height="40"
           HorizontalAlignment="Left"
           Margin="8,428,0,0"
           Name="txtMessage"
           VerticalAlignment="Top"
           FontSize="24"
           FontWeight="ExtraBold"
           Text="Tap the screen to capture a photo."/>

图 1 中的 XAML 在 Canvas 中使用 VideoBrush 来显示取景器,并提供用于和用户进行沟通的文本块。 相机传感器的长宽比为 4:3,而屏幕的长宽比为 15:9。 如果您不使用同一 4:3 比率指定画布尺寸 (640x480),图像将在屏幕上拉伸显示。

在 Canvas 元素中,Tap 属性指定当用户点击屏幕时调用的方法,即 viewfinder_Tapped 方法。 为从相机预览缓冲区中显示图像流,已将名为 viewfinderBrush 的 Video­Brush 指定为画布的背景。 与单反 (SLR) 相机中的取景器一样,viewfinderBrush 使您能够查看相机预览帧。 ViewfinderBrush 中的转换实际上会在取景器旋转时将其“固定”到画布的中心。 我将在以下部分中讨论此 XAML 后面的代码。 图 2 显示该简单照片应用程序的用户界面。

The Simple Photo App UI
图 2 简单照片应用程序的用户界面

初始化和释放相机:要拍摄照片并将照片保存到图片中心的“本机照片”文件夹中,您将分别需要 PhotoCamera 和 MediaLibrary 类。 首先添加对 Microsoft.Xna.Framework 程序集的引用。 对于本示例,您无需了解 XNA 编程;但是,您确实需要此程序集中的类型,以便访问媒体库。

在 MainPage.xaml.cs 文件的顶部,为相机和媒体库添加指令:

using Microsoft.Devices;
using Microsoft.Xna.Framework.Media;

在 MainPage 类中,添加以下类级别的变量:

private int photoCounter = 0;
PhotoCamera cam;
MediaLibrary library = new MediaLibrary();

相机可能需要几秒钟时间来进行初始化。 通过在类级别声明 PhotoCamera 对象,您可在导航到页面时创建该对象,并在离开该页面时将其从内存中删除。 我们将使用 OnNavigatedTo 和 OnNavigatingFrom 方法实现此目的。

在 OnNavigatedTo 方法中,创建相机对象,注册将要使用的相机事件,并将相机预览设置为取景器、viewfinderBrush 的源。 虽然相机通常在 Windows Phone 7.5 中是可选功能;但在创建相机对象之前检查相机十分重要。 如果主相机不可用,则该方法会向用户写入一条消息。

图 3 中所示的方法添加到 MainPage 类。

图 3 OnNavigatedTo 和 OnNavigatingFrom 方法

protected override void OnNavigatedTo
  (System.Windows.Navigation.NavigationEventArgs e)
{
  if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true)
  {
    cam = new PhotoCamera(CameraType.Primary);
    cam.CaptureImageAvailable +=
      new EventHandler<Microsoft.Devices.ContentReadyEventArgs>
        (cam_CaptureImageAvailable);
    viewfinderBrush.SetSource(cam);
  }
  else
  {
    txtMessage.Text = "A Camera is not available on this device.";
  }
}
protected override void OnNavigatingFrom
  (System.Windows.Navigation.NavigatingCancelEventArgs e)
{
  if (cam != null)
  {
    cam.Dispose();
  }
}

当离开页面时,可以使用 OnNavigatingFrom 方法处理相机对象并取消注册任何相机事件。 这有助于将电能消耗降至最低、加快关机速度和释放内存。

拍摄照片:如 XAML 中所示,在用户点击取景器之后,会调用 viewfinder_Tapped 方法。 此方法会在相机准备就绪时启动图像捕获。 如果相机尚未初始化或者当前正在捕获另一个图像,则会引发异常。 为帮助减少异常情况,请考虑禁用触发照片拍摄的机制,直到触发 Initialized 事件。 为简便起见,在本示例中,我们将跳过这一步骤。

图 4 显示需要添加到 MainPage 类的代码。

图 4 viewfinder_Tapped 方法

void viewfinder_Tapped(object sender, GestureEventArgs e)
{
  if (cam != null)
  {
    try
    {
      cam.CaptureImage();
    }
    catch (Exception ex)
    {
      this.Dispatcher.BeginInvoke(delegate()
      {
        txtMessage.Text = ex.Message;
      });
    }
  }
}

拍摄照片和保存照片是异步任务。 调用 CaptureImage 方法后,会启动一系列事件并且控制权将传递回 UI。 如图 5 中的事件序列图所示,每个图像捕获过程都包含两个阶段。 首先,相机传感器拍摄照片,然后根据传感器数据创建图像。

The Image-Capture Event Sequence of the PhotoCamera Class
图 5 PhotoCamera 类的图像捕获事件序列

保存照片:在传感器拍摄照片后,会并行创建两个图像文件:一个完整尺寸的图像文件和一个缩略图。 您没有必要使用这两个文件。 每个文件均作为相应事件参数中 e.ImageStream 属性中的 JPG 图像流提供。

媒体库会自动创建其自己的缩略图,以便在设备的图片中心中显示,因此本示例不需要图像的缩略图版本。 但是,如果您希望在自己的应用程序中显示缩略图,Capture­ThumbnailAvailable 事件处理程序中的 e.ImageStream 将是一个有效的选择。

当该图像流可用时,您可以使用它将图像保存到多个位置。 例如:

  • “本机照片”文件夹: 使用 MediaLibrary.SavePictureToCameraRoll 方法。
  • “已保存图片”文件夹: 使用 MediaLibary.Save­-Picture 方法。
  • 独立存储: 使用 IsolatedStorageFile­-Stream.Write 方法。

在本示例中,我们会将图像保存到“本机照片”文件夹。 有关如何将图像保存到独立存储的示例,请参阅 Windows Phone SDK 中的“基本相机示例”。 将图 6 中的代码添加到 MainPage 类。

图 6 将图像保存到“本机照片”文件夹

void cam_CaptureImageAvailable(object sender,
  Microsoft.Devices.ContentReadyEventArgs e)
{
  photoCounter++;
  string fileName = photoCounter + ".jpg";
  Deployment.Current.Dispatcher.BeginInvoke(delegate()
  {
    txtMessage.Text = "Captured image available, saving picture.";
  });
  library.SavePictureToCameraRoll(fileName, e.ImageStream);
  Deployment.Current.Dispatcher.BeginInvoke(delegate()
  {
    txtMessage.Text = "Picture has been saved to camera roll.";
  });
}

图 6 中的代码中,消息将在图像保存到“本机照片”文件夹前后发送到 UI。 这些消息只用于帮助您了解发生的情况;它们不是必需的。 将消息传递到 UI 线程需要使用 BeginInvoke 方法。 如果您未使用 BeginInvoke,则会引发跨线程异常。 为简便起见,此方法不包含错误处理代码。

处理旋转:在将图片保存到媒体库时,将在文件的 EXIF 信息中注明图像的正确方向。 应用程序最关注的是如何在用户界面中调整相机的预览方向。 若要始终以正确的方向显示预览,可在适当的时候旋转取景器 (VideoBrush)。 可以通过重写 OnOrientationChanged 虚拟方法来实现旋转。 将图 7 中的代码添加到 MainPage 类。

图 7 重写 OnOrientationChanged 虚拟方法

void cam_CaptureImageAvailable(object sender,
  Microsoft.Devices.ContentReadyEventArgs e)
{
  photoCounter++;
  string fileName = photoCounter + ".jpg";
  Deployment.Current.Dispatcher.BeginInvoke(delegate()
  {
    txtMessage.Text = "Captured image available, saving picture.";
  });
  library.SavePictureToCameraRoll(fileName, e.ImageStream);
  Deployment.Current.Dispatcher.BeginInvoke(delegate()
  {
    txtMessage.Text = "Picture has been saved to camera roll.";
  });
}
protected override void OnOrientationChanged
  (OrientationChangedEventArgs e)
{
  if (cam != null)
  {
    Dispatcher.BeginInvoke(() =>
    {
      double rotation = cam.Orientation;
      switch (this.Orientation)
      {
        case PageOrientation.LandscapeLeft:
          rotation = cam.Orientation - 90;
          break;
        case PageOrientation.LandscapeRight:
          rotation = cam.Orientation + 90;
          break;
      }
        viewfinderTransform.Rotation = rotation;
    });
  }
  base.OnOrientationChanged(e);
}

无需对取景器方向进行任何调整,只要硬件快门按钮朝上 (LandscapeLeft),常用主相机的取景器就会以正确方向显示。 如果您旋转设备使得硬件快门按钮朝下 (LandscapeRight),则取景器必须旋转 180 度才能在用户界面中正确显示。 如果主相机的物理方向是非常规的,则此处会使用 PhotoCamera Orientation 属性。

声明应用程序功能:最后,当您的应用程序使用相机时,您必须在应用程序清单文件 (WMAppManifest.xml) 中声明这一点。 无论使用哪个相机,您都会需要 ID_CAP_ISV_CAMERA 功能。 您还可以选择使用 ID_HW_FRONTCAMERA 指定您的应用程序需要前置相机:

<Capability Name="ID_CAP_ISV_CAMERA"/>
<Capability Name="ID_HW_FRONTCAMERA"/>

如果没有 ID_CAP_ISV_CAMERA 功能,您的相机应用程序将无法运行。如果到目前为止一直都能正常运行程序,则是因为此功能已自动添加到新的 Windows Phone 项目中。但是,如果您要升级您的应用程序,您将需要手动添加它。必须始终手动添加 ID_HW_FRONTCAMERA,但缺少它不会阻止应用程序运行。

这些功能有助于对其设备上不具有相机功能的用户加以警告,但不会阻止他们下载和购买您的应用程序。因此,最好提供您的应用程序的试用版。其次,如果用户错过警告,他们不会花了钱却得知您的应用程序无法在其设备上按预期运行。您将获得好的应用程序评级。

如果您尚未做到这一点,请按 F5 并在您的设备上调试这一简单的相机应用程序。您可以在仿真器上调试该应用程序,但是,您只会看到一个黑色方框在屏幕上移动,因为仿真器不具有物理相机。对设备进行调试时,请记住,在将设备从您的 PC 移除之前,您无法在图片中心查看您的新图像。

为深入了解,我们来看一看 Windows Phone SDK 中的“基本相机示例”。该示例演示用于拍摄照片的完整 API: 从调整闪光灯和分辨率设置到合并触控聚焦和硬件快门按钮。

访问相机预览缓冲区

在前面的示例中,相机预览缓冲区中的帧传送到了取景器。PhotoCamera 类还会公开预览缓冲区的当前帧以允许对每个帧进行逐像素操控。让我们看一个 Windows Phone SDK 中的示例,以便了解如何操控预览缓冲区中的帧并将其显示在用户界面中的可写位图上。

PhotoCamera 类使用以下“get preview”方法公开预览缓冲区的当前帧:

  • GetPreviewBufferArgb32: ARGB 格式的当前帧的整数数组
  • GetPreviewBufferYCbCr: YCbCr 格式的当前帧的字节数组
  • GetPreviewBufferY: 仅亮度平面的类似格式的字节数组

ARGB 是用于描述 Silverlight for Windows Phone 应用程序中的颜色的格式。YCbCr 支持有效的图像处理功能,但 Silverlight 无法使用 YCbCr。如果您希望在应用程序中操控 YCbCr 帧,则必须将该帧转换为 ARGB 格式,之后才能显示它。有关这些格式和颜色转换的详细信息,请参阅 MSDN 库页面的“Windows Phone 的相机颜色转换(YCbCr 到 ARGB)”,网址为 wpdev.ms/colorconversion。

Windows Phone SDK 中的“相机灰度示例”(参见图 8)演示如何操控预览缓冲区中的 ARGB 帧以及如何将这些帧几乎实时地写入可写位图图像。在此示例中,每个帧均由彩色转换为灰度。请注意,此示例旨在演示 ARGB 操控;如果您的应用程序只需要灰度,请考虑改用 GetPreviewBufferY 方法。

The Camera Grayscale Sample UI图 8 相机灰度示例的用户界面

在 XAML 文件中,图像标记用于承载相应的可写位图(用户界面左下角的黑白图像),如下所示:

<Image x:Name="MainImage"
       Width="320" Height="240"
       HorizontalAlignment="Left" VerticalAlignment="Bottom" 
       Margin="16,0,0,16"
       Stretch="Uniform"/>

当按下按钮以启用灰度转换时,会创建一个新的线程来执行该过程;还会创建一个具有与预览缓冲区相同尺寸的可写位图,并将此位图指定为 Image 控件的源:

wb = new WriteableBitmap(
        (int)cam.PreviewResolution.Width,
        (int)cam.PreviewResolution.Height);
this.MainImage.Source = wb;

该线程使用 PumpARGBFrames 方法执行其任务。 此处会使用一个名为 ARGBPx 的整数数组来承载当前预览缓冲区的快照。 数组中的每个整数代表帧的一个像素(ARGB 格式)。 创建的此数组同样具有与预览缓冲区相同的尺寸:

int[] ARGBPx = new int[
    (int)cam.PreviewResolution.Width *
    (int)cam.PreviewResolution.Height];

当启用示例的“灰度”功能时,该线程会将预览缓冲区中的当前帧复制到 ARGBPx 数组。 此处,phCam 是相机对象:

phCam.GetPreviewBufferArgb32(ARGBPx);

将缓冲区复制到数组后,该线程将遍历每个像素并将其转换为灰度(有关如何完成此操作的更多详细信息,请参见示例):

for (int i = 0; i < ARGBPx.Length; i++)
{
  ARGBPx[i] = ColorToGray(ARGBPx[i]);
}

最后,在处理下个帧之前,该线程将使用 BeginInvoke 方法更新用户界面中的 WriteableBitmap。 CopyTo 方法使用 ARGBPx 数组覆盖 WriteableBitmap 像素,而 Invalidate 方法强制 WriteableBitmap 重绘,如下所示:

Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
  // Copy to WriteableBitmap.
ARGBPx.CopyTo(wb.Pixels, 0);
  wb.Invalidate();
  pauseFramesEvent.Set();
});

WriteableBitmap 类支持多种创意目标。 现在,您可以将相机预览缓冲区合并到您的用户界面的视觉效果库中。

录制视频

虽然您可以使用 PhotoCamera 类将预览缓冲区流式传输到用户界面,但您无法使用它来录制视频。 因此,您将需要 System.Windows.Media 命名空间中的一些类。 在本文的最后一部分中,我们来看看 Windows Phone SDK 中的“录像机示例”(参见图 9),了解如何在独立存储中将视频录制为 MP4 文件。 您可以在 SDK 代码示例页上找到此示例。

The Video Recorder Sample UI
图 9 录像机示例用户界面

视频录制的主类有:

  • CaptureDeviceConfiguration: 用于检查视频捕获设备的可用性
  • CaptureSource: 用于开始和停止视频录制/预览
  • VideoBrush: 用于使用 CaptureSource 或 PhotoCamera 对象填充 Silverlight UI 控件
  • FileSink: 用于在 CaptureSource 对象运行时将视频录制到独立存储

在 XAML 文件中,Rectangle 控件用于显示相机取景器:

<Rectangle
  x:Name="viewfinderRectangle"
  Width="640"
  Height="480"
  HorizontalAlignment="Left"
  Canvas.Left="80"/>

但是,Rectangle 控件并非显示视频所必需的。 您可以使用 Canvas 控件,如第一个示例所示。 使用 Rectangle 控件只是为了介绍显示视频的另一种方法。

在页面级别,声明了以下变量:

// Viewfinder for capturing video.
private VideoBrush videoRecorderBrush;
// Source and device for capturing video.
private CaptureSource captureSource;
private VideoCaptureDevice videoCaptureDevice;
// File details for storing the recording.       
private IsolatedStorageFileStream isoVideoFile;
private FileSink fileSink;
private string isoVideoFileName = "CameraMovie.mp4";

当用户导航到页面时,InitializeVideoRecorder 方法将启动相机并将相机预览发送到 Rectangle。 创建 captureSource 和 fileSink 对象后,InitializeVideoRecorder 方法将使用静态 Capture­DeviceConfiguration 对象查找视频设备。 如果没有相机可用,videoCaptureDevice 将为 null:

videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();

在 Windows Phone 7.5 中,相机是可选功能。 虽然相机在现在的设备中很常见,但最好还是在您的代码中对其进行检查。 如图 10 所示,videoCaptureDevice 用于检查是否存在相机。 如果有可用的相机,captureSource 将设置为名为 videoRecorderBrush 的 VideoBrush 的源,且 videoRecorderBrush 将用作名为 viewfinderRectangle 的 Rectangle 控件的填充。 调用 captureSource 的 Start 方法后,相机开始向 Rectangle 发送视频。

图 10 显示视频预览

// Initialize the camera if it exists on the device.
if (videoCaptureDevice != null)
{
  // Create the VideoBrush for the viewfinder.
videoRecorderBrush = new VideoBrush();
  videoRecorderBrush.SetSource(captureSource);
  // Display the viewfinder image on the rectangle.
viewfinderRectangle.Fill = videoRecorderBrush;
  // Start video capture and display it on the viewfinder.
captureSource.Start();
  // Set the button state and the message.
UpdateUI(ButtonState.Initialized, "Tap record to start recording...");
}
else
{
  // Disable buttons when the camera is not supported by the device.
UpdateUI(ButtonState.CameraNotSupported, "A camera is not supported on this device.");
}

在本示例中,名为 UpdateUI 的帮助程序方法管理按钮状态并向用户写入消息。 有关更多详细信息,请参阅“录像机示例”。

虽然 fileSink 对象已创建,但此时没有录制任何视频。 应用程序的这种状态称为视频“预览”。若要录制视频,则需要在开始之前将 fileSink 连接到 captureSource。 换言之,您需要停止 captureSource,之后才能录制视频。

当用户点击“录像机示例”中的录制按钮时,StartVideoRecorder 方法将启动从预览到录制的转换。 转换的第一歩是停止 captureSource 并重新配置 fileSink:

// Connect fileSink to captureSource.
if (captureSource.VideoCaptureDevice != null
    && captureSource.State == CaptureState.Started)
{
  captureSource.Stop();
  // Connect the input and output of fileSink.
fileSink.CaptureSource = captureSource;
  fileSink.IsolatedStorageFileName = isoVideoFileName;
}

在您为 Silverlight 插件开发了应用程序之后,虽然 CaptureSource 和 VideoBrush 类可能有些耳熟,但 FileSink 类是全新的。 FileSink 类为 Windows Phone 应用程序所独有,它了解关于写入独立存储的全部信息;您只需提供文件名称即可。

重新配置 fileSink 后,StartVideoRecorder 方法将重新启动 captureSource 并更新用户界面:

captureSource.Start();
// Set the button states and the message.
UpdateUI(ButtonState.Ready, "Ready to record.");

当用户停止录制,从录制转换为预览时,需要在重新配置 fileSink 之前再次停止 captureSource,如图 11 所示。

图 11 从录制转换为预览

// Stop recording.
if (captureSource.VideoCaptureDevice != null
&& captureSource.State == CaptureState.Started)
{
  captureSource.Stop();
  // Disconnect fileSink.
fileSink.CaptureSource = null;
  fileSink.IsolatedStorageFileName = null;
  // Set the button states and the message.
UpdateUI(ButtonState.NoChange, "Preparing viewfinder...");
  StartVideoPreview();
}

启动视频预览逻辑隔离在另一方法中,以便启用从视频播放状态(本文不对此进行介绍)到预览的转换。尽管在此不对播放进行介绍,但请务必注意,在 Windows Phone 中一次只能运行一段视频流。

“录像机示例”具有两个独立的视频流:

  1. captureSource g videoRecorderBrush g viewfinderRectangle(Rectangle 控件)
  2. isoVideoFile g VideoPlayer(MediaElement 控件)

因为一次只能运行一段视频流,所以此示例为每段流提供了可在其他流运行之前调用的“dispose”方法。在 DisposeVideoPlayer 和 Dispose­VideoRecorder 方法中,可以通过对各自的对象调用 Stop 方法(以及将 MediaElement 的源设置为 null)来停止流。CaptureSource 和 MediaElement 对象实际上并不实现 IDisposable 接口。

此时,您可能在想“相机灰度示例”似乎同时运行了两个视频。事实上,该应用程序中只有一段视频流: 从 PhotoCamera 对象到 VideoBrush 控件的流。灰度“视频”实际上只是基于相机预览缓冲区中单独操控的帧以高速率重绘的位图。

总结

相机 API 是 Windows Phone 7.5 的新增功能,它为新型应用程序开启了希望之门,从而能够以早期版本的操作系统无法实现的方式解决问题并进行娱乐。本文介绍的只是 API 的几个方面。有关完整参考,请参阅 Windows Phone SDK 文档中的“相机与照片”一节,网址为 wpdev.ms/cameraandphotos。

Matt Stroshane 负责为 Windows Phone 团队编写开发人员文档。 他在 MSDN 库中发表的其他文章主要涉及 SQL Server、SQL Azure 和 Visual Studio 等产品。 在他不工作的时候,您可能会在西雅图的大街上看见他正在为下一次马拉松比赛进行训练。 请在 Twitter 上关注他:twitter.com/mattstroshane

衷心感谢以下技术专家对本文的审阅:Eric BennettNikhil DeoreAdam LydickJon Sheller