Windows Phone 7

Einsatz von Kameras in Ihrer Windows Phone-Anwendung

Matt Stroshane

Bilder ermöglichen eine effiziente und zugleich elegante Kommunikation, wie sie durch Worte alleine nicht realisiert werden kann. Wir alle kennen das Sprichwort „Ein Bild sagt mehr als tausend Worte“. Stellen Sie sich nur einmal vor, welche Probleme Sie alle lösen könnten, wenn Ihre Windows Phone-Anwendung direkten Zugriff auf eine Kamera hätte. Von der Version Windows Phone 7.5 an können Sie nun das Problem mit den tausend Worten mithilfe der integrierten Kameras lösen.

In diesem Artikel erhalten Sie eine Einführung in die Gerätekameras auf der Vorder- und Rückseite, in die Kamera-APIs und in die damit verbundenen Manifestfunktionen. Außerdem werden die verschiedenen Möglichkeiten vorgestellt, wie Sie in Ihrer nächsten Windows Phone 7.5-Anwendung Kameras einsetzen können. Folgende Themen werden behandelt:

  • Aufnahme von Fotos: Eine einfache Foto-App wird erstellt.
  • Zugriff auf den Kameravorschaupuffer: Das Beispiel Camera Grayscale wird vorgestellt.
  • Aufnahme von Videos: Das Beispiel Video Recorder wird besprochen.

Zum Erstellen einer Windows Phone 7.5-Anwendung benötigen Sie das Windows Phone SDK 7.1. Mit den Codebeispielen in dem SDK werden die einzelnen Szenarien sehr detailliert veranschaulicht. Weitere Informationen erhalten Sie durch die Codebeispiele Basic Camera, Camera Grayscale und Video Recorder auf der Seite mit den Codebeispielen im SDK unter wpdev.ms/officialsamples.

Beachten Sie, dass die seit Windows Phone 7 verfügbare Aufgabe der Kameraaufnahme nicht in diesem Artikel behandelt wird. Mit dieser Aufgabe erhalten Sie zwar ohne Schwierigkeiten Fotos für die Anwendung, doch sie ermöglicht weder eine programmatische Fotoaufnahme noch den Zugriff auf den Kameravorschaupuffer.

Ein Gerät mit Windows Phone 7.5 kann bis zu zwei Kameras haben: eine Hauptkamera und die Kamera auf der Vorderseite, die sogenannte Frontkamera. Die Hauptkamera befindet sich auf der Rückseite des Geräts und bietet für gewöhnlich eine höhere Auflösung sowie mehr Funktionen als die Frontkamera. Nicht alle Geräte mit Windows Phone 7.5 besitzen eine Kamera. Sehen Sie daher vor der Erstellung der Kameraobjekte im Code nach, ob überhaupt eine Kamera vorhanden ist. Später in diesem Artikel wird gezeigt, wie Sie zu diesem Zweck die statische Methode IsCameraTypeSupported einsetzen.

Viele der in den USA erhältlichen Windows Phone-Geräte haben eine Hauptkamera mit einem Sensor von mindestens fünf Megapixeln, Autofokus und Blitz. Die Frontkamera wird erst seit Windows Phone 7.5 unterstützt.

Weitere Informationen zu Gerätespezifikationen finden Sie unter windowsphone.com auf der Registerkarte Kaufen.

Aufnahme von Fotos

Für den Zugriff auf die Hauptkamera und auf die Frontkamera können Sie dieselben Klassen verwenden. Sie werden sehen, dass die Auswahl des Kameratyps einfach durch Angabe eines einzelnen Parameters im Konstruktor des Objekts PhotoCamera erfolgt. Aus Designersicht kann es jedoch auch erwünscht sein, die Interaktion mit der Frontkamera anders zu gestalten. So ist es zum Beispiel möglich, die Bilder der Frontkamera zu spiegeln, sodass die Benutzer einen natürlichen Spiegeleffekt erleben.

Bei der Aufnahme von Fotos in einer Windows Phone 7.5-App arbeiten Sie hauptsächlich mit der Klasse PhotoCamera- aus dem Namespace Microsoft.Devices. Diese Klasse bietet gute Steuerungsmöglichkeiten für die Einstellungen und Verhaltensweisen der Kamera. Sie können z. B. Folgendes tun:

  • Aktivieren der Kamerablende mit der Methode PhotoCamera.CaptureImage
  • Auslösen des Autofokus mit der Methode PhotoCamera.Focus
  • Angeben der Bildauflösung durch Festlegen der Eigenschaft Photo-Camera.Resolution
  • Angeben der Blitzeinstellungen durch Festlegen der Eigenschaft Photo-Camera.FlashMode
  • Einbinden der Hardwaretaste für die Blende mit Ereignissen aus der statischen Klasse CameraButtons
  • Integrieren des Berührungsfokus mit der Methode PhotoCamera.Focus­AtPoint

In diesem Artikel wird nur auf den ersten Punkt eingegangen. Einzelne Beispiele zu allen genannten Punkten finden Sie im Beispiel Basic Camera auf der Seite mit den Codebeispielen zum Windows Phone SDK.

Beachten Sie, dass gegebenenfalls nicht alle diese APIs unterstützt werden, selbst wenn eine Kamera verfügbar ist. Folgendermaßen können Sie bestimmen, ob eine Funktion verfügbar ist:

  • Kamera: Verwenden Sie die statische Methode PhotoCamera.IsCameraTypeSupported.
  • Autofokus: Verwenden Sie die Methode PhotoCamera.IsFocus­­-Supported.
  • Bildauflösungseinstellungen: Überprüfen Sie die Sammlung Photo­-Camera.AvailableResolutions.
  • Blitzeinstellungen: Verwenden Sie die Methode PhotoCamera.IsFlashMode­Supported.
  • Punktueller Fokus: Verwenden Sie die Methode PhotoCamera.IsFocus­AtPointSupported.

Damit Sie eine Vorstellung davon erhalten, wie Fotos mit Ihrer App aufgenommen werden, gehen wir nun Schritt für Schritt eine einfache App durch. Mit dieser App wird beim Berühren des Suchers ein Foto aufgenommen. Das Foto wird dann im Ordner Eigene Aufnahmen im Bilder-Hub gespeichert.

Beginnen Sie mit einem Standardprojekt für Windows Phone, und nutzen Sie die Vorlage für Windows Phone-Anwendungen. Windows Phone 7.5-Apps können in C# oder Visual Basic geschrieben werden. In diesem Beispiel wird C# verwendet.

Das Beispiel wird zu Demonstrationszwecken vereinfacht, indem die App als Ausrichtung nur das Querformat verwendet und nur die Hauptkamera genutzt wird. Der Umgang mit den verschiedenen Ausrichtungen des Geräts und mit zwei Kameras, die in unterschiedliche Richtungen zeigen, kann sehr schnell zu Verwirrung führen. Es empfiehlt sich daher, die gewünschten Verhaltensweisen anhand eines richtigen Geräts auszuprobieren. Die Ausrichtung wird weiter unten noch genauer behandelt.

Aktualisieren Sie im MainPage.xaml die PhoneApplicationPage-Attribute wie folgt:

SupportedOrientations="Landscape" Orientation="LandscapeLeft"

Ersetzen Sie dann den Inhalt des Rasters LayoutRoot durch Canvas und TextBlock (siehe Abbildung 1).

Abbildung 1 – Hinzufügen von „Canvas“ und „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."/>

Im XAML in Abbildung 1 wird zur Anzeige des Suchers ein VideoBrush-Element in einer Canvas verwendet. Das TextBlock-Element dient der Kommunikation mit dem Benutzer. Das Seitenverhältnis des Kamerasensors beträgt 4:3 und das des Bildschirms 15:9. Wird die Größe der Canvas nicht mit dem gleichen Verhältnis von 4:3 (640 x 480) angegeben, erscheint das Bild verzerrt.

Im Canvas-Element gibt das Tap-Attribut die Methode an, die beim Antippen des Bildschirms aufgerufen werden soll: die Methode viewfinder_Tapped. Damit der Bildstream aus dem Kameravorschaupuffer angezeigt werden kann, wird ein Video­Brush-Element namens viewfinderBrush als Hintergrund der Canvas angegeben. Wie beim Sucher einer einäugigen Spiegelreflexkamera ermöglicht das Element viewfinderBrush die Anzeige der Kameravorschaurahmen. Durch die Transformation im Element viewfinderBrush wird der Sucher im Prinzip in die Mitte der gedrehten Canvas „geheftet“. Der hinter diesem XAML stehende Code wird in den folgenden Abschnitten erläutert. Abbildung 2 zeigt die Benutzeroberfläche der einfachen Foto-App.

The Simple Photo App UI
Abbildung 2 – Benutzeroberfläche der einfachen Foto-App

Initialisieren und Freigeben der Kamera – Zum Aufnehmen von Fotos und zum Speichern im Ordner Eigene Aufnahmen im Bilder-Hub benötigen Sie entsprechend die Klassen PhotoCamera und MediaLibrary. Beginnen Sie mit dem Hinzufügen einer Referenz zur Assembly Microsoft.Xna.Framework. Für dieses Beispiel sind keine XNA-Programmierkenntnisse erforderlich. Sie benötigen allerdings Typen in dieser Assembly, um auf die Medienbibliothek zugreifen zu können.

Fügen Sie zu Beginn der Datei MainPage.xaml.cs Anweisungen für Kamera und Medienbibliothek ein:

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

Fügen Sie in der Klasse MainPage die folgenden Variablen auf Klassenebene hinzu:

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

Es kann ein paar Minuten dauern, bis die Kamera initialisiert ist. Dadurch, dass das Objekt PhotoCamera auf Klassenebene deklariert wurde, kann es erstellt werden, wenn zu der Seite navigiert wird, und aus dem Arbeitsspeicher entfernt werden, wenn woanders hinnavigiert wird. Zu diesem Zweck werden die Methoden OnNavigatedTo und OnNavigatingFrom eingesetzt.

Erstellen Sie in der Methode OnNavigatedTo das Kameraobjekt, registrieren Sie die zu verwendenden Kameraereignisse, und legen Sie die Kameravorschau als Quelle für den Sucher viewfinderBrush fest. Kameras sind zwar weit verbreitet, aber nicht jedes Gerät mit Windows Phone 7.5 verfügt über eine Kamera. Daher vergessen Sie nicht, vor dem Erstellen des Kameraobjekts das Vorhandensein von Kameras zu überprüfen. Wenn die Hauptkamera nicht verfügbar ist, gibt die Methode eine Meldung an den Benutzer aus.

Fügen Sie die in Abbildung 3 gezeigten Methoden der Klasse MainPage hinzu.

Abbildung 3 – Methoden „OnNavigatedTo“ und „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();
  }
}

Nutzen Sie für den Fall, dass von der Seite weg navigiert wird, die Methode OnNavigatingFrom, um die Kameraobjekte zu entfernen und die Registrierung der Kameraereignisse aufzuheben. Dies senkt den Energieverbrauch, beschleunigt das Herunterfahren und gibt Arbeitsspeicher frei.

Aufnahme von Fotos – Wie im XAML zu sehen, wird durch Antippen des Suchers durch den Benutzer die Methode viewfinder_Tapped aufgerufen. Diese Methode initiiert die Bildaufnahme, wenn die Kamera bereit ist. Ist die Kamera noch nicht initialisiert oder nimmt sie gerade ein anderes Bild auf, wird eine Ausnahme ausgegeben. Um die Häufigkeit von Ausnahmen zu verringern, erwägen Sie, den Mechanismus zum Auslösen der Fotoaufnahme zu deaktivieren, bis das Initialized-Ereignis ausgegeben wurde. Damit dieses Beispiel nicht zu komplex wird, wird dieser Schritt hier übersprungen.

In Abbildung 4 ist der Code zu sehen, den Sie der Klasse MainPage hinzufügen müssen.

Abbildung 4 – Methode „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;
      });
    }
  }
}

Aufnahme und Speicherung eines Fotos verlaufen asynchron. Wenn die Methode CaptureImage aufgerufen wird, wird eine Ereigniskette initiiert, und die Kontrolle wird an die Benutzeroberfläche zurückgegeben. Wie im Ereignisablaufdiagramm in Abbildung 5 zu sehen, verläuft jede Bildaufnahme in zwei Stufen. Als Erstes erfasst der Kamerasensor das Foto, und anschließend werden die Bilder anhand der Sensordaten erstellt.

The Image-Capture Event Sequence of the PhotoCamera Class
Abbildung 5 – Ereignisablauf bei der Bildaufnahme der PhotoCamera-Klasse

Speichern von Fotos – Nachdem der Sensor ein Foto aufgenommen hat, werden parallel zwei Bilddateien erstellt: eine Bilddatei in Originalgröße sowie ein Miniaturbild. Es müssen nicht beide Bilder verwendet werden. Die Bilder sind einzeln als JPG-Bildstream des Ereignisses e.ImageStream in den Argumenten der entsprechenden Ereignisse verfügbar.

Da die Medienbibliothek automatisch eigene Miniaturbilder zur Anzeige im Bilder-Hub des Geräts erstellt, werden die Miniaturversionen der Bilder in diesem Beispiel nicht benötigt. Wenn Sie jedoch in Ihrer eigenen App ein Miniaturbild anzeigen möchten, ist hierfür das Element e.ImageStream des Ereignishandlers Capture­ThumbnailAvailable ein effizientes Mittel der Wahl.

Ist der Stream verfügbar, können Sie mit ihm das Bild an mehreren Orten speichern, wie zum Beispiel:

  • Ordner Eigene Aufnahmen: Verwenden Sie die Methode MediaLibrary.SavePictureToCameraRoll.
  • Ordner Gespeicherte Bilder: Verwenden Sie die Methode MediaLibary.Save­-Picture.
  • Isolierter Speicher: Verwenden Sie die Methode IsolatedStorageFile­-Stream.Write.

In diesem Beispiel wird das Bild im Ordner Eigene Aufnahmen gespeichert. Ein Beispiel zum Speichern eines Bilds im isolierten Speicher finden Sie im Beispiel Basic Camera des Windows Phone SDK. Fügen Sie den Code in Abbildung 6 zur Klasse MainPage hinzu.

Abbildung 6 – Speichern eines Bilds im Ordner „Eigene Aufnahmen“

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.";
  });
}

Im Code in Abbildung 6 werden Meldungen auf der Benutzeroberfläche ausgegeben, bevor und nachdem ein Bild im Ordner Eigene Aufnahmen gespeichert wird. Mit diesen Meldungen werden nur die Vorgänge auf dem Gerät verdeutlicht. Sie sind nicht erforderlich. Die Methode BeginInvoke wird benötigt, um die Meldung an den Benutzeroberflächenthread weiterzugeben. Ohne die Methode BeginInvoke würde eine threadübergreifende Ausnahme ausgegeben werden. Der Kürze halber wurde der Fehlerbehandlungscode in dieser Methode ausgelassen.

Umgang mit der Drehung – Beim Speichern eines Bilds in der Medienbibliothek wird die korrekte Ausrichtung des Bilds in den EXIF-Informationen der Datei festgehalten. Für die App ist dabei hauptsächlich von Bedeutung, wie die Vorschau der Kamera in der Benutzeroberfläche ausgerichtet wird. Damit die Vorschau stets in der korrekten Ausrichtung angezeigt wird, drehen Sie den Sucher (VideoBrush) entsprechend. Die Drehung wird durch Überschreiben der virtuellen Methode OnOrientationChanged erzielt. Fügen Sie den Code in Abbildung 7 zur Klasse MainPage hinzu.

Abbildung 7 – Überschreiben der virtuellen Methode „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);
}

Ohne Anpassung der Sucherausrichtung wird der Sucher einer durchschnittlichen Hauptkamera nur dann richtig ausgerichtet, wenn die Hardwaretaste für die Blende nach oben weist (LandscapeLeft). Wird das Gerät so gedreht, dass die Hardwaretaste für die Blende nach unten weist (LandscapeRight), muss der Sucher um 180 Grad gedreht werden, damit er korrekt in der Benutzeroberfläche angezeigt wird. Für den Fall, dass die tatsächliche Ausrichtung der Hauptkamera untypisch ist, wird hier die Eigenschaft PhotoCamera Orientation verwendet.

Deklarieren von Anwendungsfunktionen – Wenn Ihre Anwendung eine Kamera verwendet, müssen Sie dies zum Schluss noch in der Anwendungsmanifestdatei WMAppManifest.xml deklarieren. Unabhängig von der verwendeten Kamera benötigen Sie die Funktion ID_CAP_ISV_CAMERA. Alternativ dazu können Sie die Funktion ID_HW_FRONTCAMERA verwenden, um anzugeben, dass Ihre App eine Frontkamera benötigt:

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

Die Kamera-App kann ohne die Funktion ID_CAP_ISV_CAMERA nicht ausgeführt werden. Wenn Sie bislang noch keine Probleme mit der Ausführung hatten, ist das darauf zurückzuführen, dass die Funktion neuen Windows Phone-Projekten automatisch hinzugefügt wird. Wenn Sie jedoch Ihre App aktualisieren, müssen Sie die Funktion manuell hinzufügen. ID_HW_FRONTCAMERA hingegen muss stets manuell hinzugefügt werden. Fehlt diese Funktion jedoch, hat dies keine Auswirkungen auf die Lauffähigkeit der App.

Mit diesen Funktionen werden Benutzer gewarnt, wenn ihre Geräte nicht mit Kameras ausgestattet sind. Sie werden jedoch nicht daran gehindert, die App herunterzuladen und zu kaufen. Aus diesem Grund empfiehlt es sich, eine Testversion der App zur Verfügung zu stellen. So kann verhindert werden, dass Benutzer, die die Warnmeldung übersehen haben, Geld für die App ausgeben, nur um hinterher festzustellen, dass sie nicht wie erwartet mit dem Gerät genutzt werden kann. Dies würde sich negativ auf die Bewertungen der App auswirken.

Sofern noch nicht geschehen, drücken Sie F5, und debuggen Sie diese einfache Kamera-App auf Ihrem Gerät. Sie können die App auch auf dem Emulator debuggen, jedoch wird dann nur ein schwarzes Kästchen angezeigt, das sich auf dem Bildschirm hin und her bewegt. Dieses Verhalten ist darauf zurückzuführen, dass der Emulator keine Kamera hat. Bedenken Sie beim Debuggen mit einem Gerät, dass Sie die neuen Bilder im Bilder-Hub erst dann ansehen können, wenn Sie das Gerät vom PC trennen.

Für den Fall, dass Sie sich eingehender mit diesem Thema beschäftigen möchten, sehen Sie sich das Beispiel Basic Camera im Windows Phone SDK an. In diesem Beispiel wird die gesamte API für die Aufnahme von Fotos vorgestellt: von der Anpassung der Einstellungen für Blitz und Auflösung bis hin zur Integration des Berührungsfokus und der Hardwaretaste für die Blende.

Zugriff auf den Kameravorschaupuffer

Im vorherigen Beispiel wurden die Rahmen aus dem Kameravorschaupuffer an den Sucher gestreamt. Auch die Klasse PhotoCamera macht den aktuellen Rahmen des Vorschaupuffers verfügbar und ermöglicht so eine pixelweise Manipulation der einzelnen Rahmen. Im Folgenden werfen wir einen Blick auf ein Beispiel aus dem Windows Phone SDK, das veranschaulicht, wie Rahmen aus dem Vorschaupuffer manipuliert und auf einer beschreibbaren Bitmap in der Benutzeroberfläche angezeigt werden.

Die Klasse PhotoCamera macht den aktuellen Rahmen aus dem Vorschaupuffer mit den folgenden get preview-Methoden verfügbar:

  • GetPreviewBufferArgb32: Ganzzahlenarray des aktuellen Rahmens im ARGB-Format
  • GetPreviewBufferYCbCr: Bytearray des aktuellen Rahmens im YCbCr-Format
  • GetPreviewBufferY: Bytearray nur der Leuchtdichtenebene, gleiches Format

Mit dem Format ARGB wird in Silverlight die Farbe für Windows Phone-Anwendungen beschrieben. YCbCr ermöglicht zwar eine effiziente Bildbearbeitung, doch Silverlight kann YCbCr nicht verarbeiten. Wenn Sie in Ihrer Anwendung einen YCbCr-Rahmen manipulieren möchten, müssen Sie ihn zunächst in ARGB konvertieren, damit er angezeigt werden kann.

Das Beispiel Camera Grayscale aus dem Windows Phone SDK (siehe Abbildung 8) veranschaulicht, wie ARGB-Rahmen aus dem Vorschaupuffer manipuliert und nahezu in Echtzeit auf eine beschreibbare Bitmap geschrieben werden. In diesem Beispiel werden alle Rahmen von Farbe in Graustufen konvertiert. Beachten Sie, dass der Zweck dieses Beispiels in der Demonstration der ARGB-Manipulation liegt. Wenn Sie für Ihre App nur Graustufen benötigen, erwägen Sie stattdessen den Einsatz der Methode GetPreviewBufferY.

The Camera Grayscale Sample UIAbbildung 8 – Beispiel „Camera Grayscale“ in der Benutzeroberfläche

In der XAML-Datei wird ein Bildtag verwendet, um die entsprechende beschreibbare Bitmap (das Schwarzweißbild in der unteren linken Ecke der Benutzeroberfläche) zu hosten. Das sieht dann wie folgt aus:

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

Wird eine Schaltfläche betätigt, um die Graustufenkonvertierung zu aktivieren, wird ein neuer Thread für die Ausführung dieses Vorgangs erstellt. Dafür wird eine beschreibbare Bitmap mit den Maßen des Vorschaupuffers erstellt und als Quelle des Steuerelements Image zugewiesen:

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

Der Thread führt seine Arbeit in der Methode PumpARGBFrames aus. Dort kommt ein Ganzzahlenarray namens ARGBPx zum Einsatz, um eine Momentaufnahme des aktuellen Vorschaupuffers aufzunehmen. Jede Ganzzahl in dem Array steht für einen Pixel des Rahmens im ARGB-Format. Dieses Array wird ebenfalls mit den Maßen des Vorschaupuffers erstellt:

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

Wenn die Funktion grayscale des Beispiels aktiviert ist, kopiert der Thread den aktuellen Rahmen im Vorschaupuffer in das ARGBPx-Array. Das Kameraobjekt ist dann phCam:

phCam.GetPreviewBufferArgb32(ARGBPx);

Sobald der Puffer in das Array kopiert wurde, führt der Thread eine Schleife durch die einzelnen Pixel durch und konvertiert sie in Graustufen. Weitere Details dazu können Sie dem Beispiel entnehmen:

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

Bevor der nächste Rahmen verarbeitet wird, nutzt nun der Thread die Methode BeginInvoke, um das Element WriteableBitmap in der Benutzeroberfläche zu aktualisieren. Die Methode CopyTo überschreibt die WriteableBitmap-Pixel mit dem ARGBPx-Array, und die Methode Invalidate erzwingt eine Neuzeichnung von WriteableBitmap. Das sieht dann wie folgt aus:

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

Die Klasse WriteableBitmap ermöglicht viele verschiedene kreative Ansätze. Jetzt können Sie den Kameravorschaupuffer in die Sammlung der visuellen Objekte der Benutzeroberfläche aufnehmen.

Aufnahme von Videos

Sie können mit der Klasse PhotoCamera zwar den Vorschaupuffer auf die Benutzeroberfläche streamen, die Aufnahme von Videos ist mit ihr jedoch nicht möglich. Dafür benötigen Sie einige Klassen aus dem Namespace System.Windows.Media. In diesem letzten Teil dieses Artikels werfen wir einen Blick auf das Beispiel Video Recorder aus dem Windows Phone SDK (siehe Abbildung 9) und erfahren, wie ein Video als MP4-Datei im isolierten Speicher aufgenommen wird. Das Beispiel ist auf der Seite mit den Codebeispielen des SDK verfügbar.

The Video Recorder Sample UI
Abbildung 9 – Beispiel „Video Recorder“ in der Benutzeroberfläche

Es gibt folgende Hauptklassen für die Videoaufnahme:

  • CaptureDeviceConfiguration: zur Überprüfung der Verfügbarkeit eines Videoaufnahmegeräts
  • CaptureSource: zum Beginnen und Beenden der Videoaufnahme/Videovorschau
  • VideoBrush: zum Befüllen von Silverlight-Benutzeroberflächen-Steuerelementen mit einem CaptureSource-Objekt oder einem PhotoCamera-Objekt
  • FileSink: zur Videoaufnahme in den isolierten Speicher, wenn ein CaptureSource-Objekt ausgeführt wird

In der XAML-Datei wird ein Rectangle-Steuerelement zur Anzeige des Kamerasuchers verwendet:

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

Für die Anzeige eines Videos wird ein Rectangle-Steuerelement jedoch nicht benötigt. Dafür können Sie das Canvas-Steuerelement verwenden, wie im ersten Beispiel gezeigt. Das Rectangle-Steuerelement wird lediglich verwendet, um eine andere Art der Videoanzeige zu demonstrieren.

Auf Seitenebene werden folgende Variablen deklariert:

// 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";

Wenn ein Benutzer zu dieser Seite navigiert, startet die Methode InitializeVideoRecorder die Kamera und sendet die Kameravorschau an das Rectangle-Steuerelement. Nachdem die Objekte captureSource und fileSink erstellt wurden, nutzt die Methode InitializeVideoRecorder das statische Objekt Capture­DeviceConfiguration für die Suche nach einem Videogerät. Wenn keine Kamera verfügbar ist, erhält videoCaptureDevice den Wert null:

videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();

Nicht jedes Gerät mit Windows Phone 7.5 verfügt über eine Kamera. Moderne Geräte haben zwar üblicherweise eine Kamera, doch es empfiehlt sich, das Vorhandensein im Code zu überprüfen. Wie in Abbildung 10 zu sehen, wird zur Überprüfung, ob eine Kamera vorhanden ist, videoCaptureDevice verwendet. Wenn eine Kamera vorhanden ist, wird captureSource als Quelle eines VideoBrush-Elements namens videoRecorderBrush festgelegt. videoRecorderBrush wird als Füllung für das Rectangle-Steuerelement namens viewfinderRectangle eingesetzt. Wenn die Start-Methode von captureSource aufgerufen wird, beginnt die Kamera mit der Übertragung des Videos an das Rectangle-Steuerelement.

Abbildung 10 – Anzeige der Videovorschau

// 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.");
}

In diesem Beispiel verwaltet eine Helpermethode namens UpdateUI die Zustände der Schaltflächen und gibt Meldungen an den Benutzer aus. Weitere Informationen dazu finden Sie im Beispiel Video Recorder.

Zwar wurde das Objekt fileSink bereits erstellt, doch bislang wurde noch kein Video aufgenommen. Dieser Anwendungszustand wird als Videovorschau bezeichnet. Damit ein Video aufgenommen werden kann, muss fileSink vor dem Start mit captureSource verbunden werden. Anders ausgedrückt, captureSource muss angehalten werden, bevor ein Video aufgenommen werden kann.

Wenn der Benutzer die Aufnahmeschaltfläche im Beispiel Video Recorder antippt, geht die Methode StartVideoRecorder von der Vorschau zur Aufnahme über. Während dieses Übergangs besteht der erste Schritt darin, captureSource anzuhalten und fileSink neu zu konfigurieren:

// 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;
}

Möglicherweise sind Ihnen die Klassen CaptureSource und VideoBrush bereits aus der Entwicklung von Anwendungen für das Silverlight-Plug-In bekannt. Die Klasse FileSink ist jedoch ganz neu. Die Klasse FileSink gibt es exklusiv nur bei Windows Phone-Anwendungen und enthält alle Angaben zum Schreiben in den isolierten Speicher. Sie müssen lediglich den Namen der Datei angeben.

Nach der Neukonfiguration von fileSink startet die Methode StartVideoRecorder das captureSource-Element neu und aktualisiert die Benutzeroberfläche:

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

Wenn der Benutzer die Aufnahme beendet, muss für diesen Übergang von der Aufnahme zur Vorschau captureSource erneut angehalten werden, bevor fileSink neu konfiguriert wird (siehe Abbildung 11).

Abbildung 11 – Übergang von Aufnahme zu Vorschau

// 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();
}

Die Logik Beginnen-Video-Vorschau wurde in eine andere Methode isoliert, um den Übergang vom Zustand der Videowiedergabe in die Vorschau zu ermöglichen, was nicht Gegenstand dieses Artikels ist. Auch wenn an dieser Stelle nicht weiter auf die Wiedergabe eingegangen wird, sollte doch erwähnt werden, dass unter Windows Phone nicht mehrere Videostreams gleichzeitig ausgeführt werden können.

Das Beispiel Video Recorder unterstützt zwei separate Videostreams:

  1. captureSource g videoRecorderBrush g viewfinderRectangle (Rectangle-Steuerelement)
  2. isoVideoFile g VideoPlayer (MediaElement-Steuerelement)

Da nur jeweils ein Stream ausgeführt werden kann, enthält dieses Beispiel für jeden Stream eine dispose-Methode, die aufgerufen werden kann, bevor der nächste Stream ausgeführt wird. Bei den Methoden DisposeVideoPlayer und Dispose­VideoRecorder wird der Stream durch Aufrufen der Stop-Methode – und Setzen der Quelle von MediaElement auf null – für das entsprechende Objekt angehalten. Die Objekte CaptureSource und MediaElement haben keine direkten Auswirkungen auf die Schnittstelle IDisposable.

Möglicherweise haben Sie an dieser Stelle den Eindruck, dass das Beispiel Camera Grayscale zwei gleichzeitig ablaufende Videos enthält. Tatsächlich enthält diese Anwendung aber nur einen Videostream: den Stream vom Objekt PhotoCamera zum Steuerelement VideoBrush. Bei dem Graustufen-„Video“ handelt es sich in Wirklichkeit um eine Bitmap, die auf Grundlage von einzeln manipulierten Rahmen aus dem Kameravorschaupuffer mit hoher Geschwindigkeit neu gezeichnet wurde.

Zusammenfassung

Die neu für Windows Phone 7.5 eingeführte Kamera-API eröffnet neue Möglichkeiten für Anwendungen, mit denen bestimmte Probleme gelöst werden und die Benutzer unterhaltsame Funktionen erhalten können, die in früheren Versionen des Betriebssystems so nicht möglich waren. In diesem Artikel wurden nur einige Aspekte dieser API behandelt. Eine vollständige Beschreibung finden Sie im Abschnitt „Kamera und Fotos“ in der Dokumentation des Windows Phone SDK unter wpdev.ms/cameraandphotos.

Matt Stroshane schreibt Entwicklerdokumentation für das Windows Phone-Team. In anderen Beiträgen für die MSDN Library hat er Produkte wie SQL Server, SQL Azure und Visual Studio besprochen. Wenn er nicht am Schreibtisch sitzt, läuft er durch die Straßen von Seattle und trainiert für den nächsten Marathon. Sie können ihm auf Twitter unter twitter.com/mattstroshane folgen.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Eric BennettNikhil DeoreAdam Lydick und Jon Sheller