Windows Phone 7
El uso de las cámaras en las aplicaciones para Windows Phone
Las imágenes permiten comunicar ideas con una eficiencia y elegancia que no es posible al usar solamente palabras. Ya habrá oído que “una imagen vale más que mil palabras”, imagínese entonces la clase de problemas que podría resolver si su aplicación de Windows Phone tuviera acceso directo a una cámara. Bueno, a partir de Windows Phone 7.5, podrá comenzar a resolver esos “problemas de mil palabras” con las cámaras integradas en los dispositivos.
En este artículo, presentaré las cámaras frontales y trasera, las API para las cámaras y sus capacidades manifiestas, además de examinar algunas formas en que puede usar una cámara en su siguiente aplicación para Windows Phone 7.5. Analizaré:
- Captura de fotografías: crearé una aplicación fotográfica muy sencilla.
- Obtención de acceso al búfer de vista previa de la cámara: presentaré el Ejemplo de escala de grises de la cámara.
- Grabación de vídeo: examinaré el Ejemplo de la grabadora de vídeo.
Es necesario contar con el Windows Phone SDK 7.1 para crear una aplicación de Windows Phone 7.5. El SDK incluye ejemplos de código que demuestran todos estos escenarios en forma detallada. Para obtener más información, revise el Ejemplo básico de la cámara, el Ejemplo de la cámara con escala de grises y el Ejemplo de la grabadora de vídeo de la página Ejemplos de código en el SDK en wpdev.ms/officialsamples.
Tenga en cuenta que en este artículo no abordaré la tarea de captura de cámara, que está disponible a partir de Windows Phone 7. Aunque con esta tarea se pueden adquirir fotografías fácilmente desde la aplicación, no permite capturar fotografías de manera programática u obtener acceso al búfer de vista previa de la cámara.
Un dispositivo Windows Phone 7.5 puede incluir hasta dos cámaras, designadas como cámara primaria y cámara frontal. La cámara primaria está ubicada en la parte posterior del dispositivo y generalmente ofrece una mayor resolución y más características que la cámara frontal. Como los dispositivos con Windows Phone 7.5 no cuentan obligatoriamente con ninguna de estas cámaras, en el código debe asegurarse de comprobar su presencia antes de crear los objetos de cámara. Más adelante ilustraré el uso del método estático IsCameraTypeSupported para este fin.
Muchos de los dispositivos con Windows Phone disponibles en Estados Unidos incluyen una cámara primaria con un sensor de 5 MP o más, enfoque automático y un flash. La cámara frontal es una característica nueva de Windows Phone 7.5.
Para obtener más información acerca de las especificaciones del dispositivo, revise la pestaña Comprar en windowsphone.com.
Captura de fotografías
Las mismas clases sirven para obtener acceso tanto a la cámara primaria como a la frontal. Como verá, basta sencillamente con especificar un solo parámetro en el constructor del objeto PhotoCamera para seleccionar el tipo de cámara. Sin embargo, desde la perspectiva del diseño, es posible que queramos administrar la interacción con la cámara frontal en forma diferente. Por ejemplo, quizás queramos invertir las imágenes de la cámara frontal para entregarle al usuario una experiencia análoga a un espejo, que resulte más natural.
Al capturar fotografías en una aplicación Windows Phone 7.5 tendremos que trabajar principalmente con la clase PhotoCamera del espacio de nombres Microsoft.Devices. Esta clase nos ofrece un buen grado de control sobre la configuración y el comportamiento de la cámara. Nos permite, por ejemplo:
- Activar el obturador de la cámara con el método PhotoCamera.CaptureImage.
- Desencadenar el enfoque automático con el método PhotoCamera.Focus.
- Especificar la resolución de la imagen mediante la configuración de la propiedad Photo-Camera.Resolution.
- Especificar la configuración del flash al configurar la propiedad Photo-Camera.FlashMode.
- Incorporar el botón de hardware del obturador con eventos de la clase estática CameraButtons.
- Implementar un enfoque de contacto con el método PhotoCamera.FocusAtPoint.
En este artículo solo ilustraré el primer punto. Para ver cómo usar todos los puntos anteriores, revise el Ejemplo básico de la cámara en la página de código de muestra de Windows Phone SDK.
Fíjese que, incluso cuando haya una cámara disponible, es posible que no sea compatible con todas esas API. Los siguientes métodos pueden servirle para determinar cuáles están disponibles:
- Cámara: Use el método estático PhotoCamera.IsCameraTypeSupported.
- Enfoque automático: Use el método PhotoCamera.IsFocus-Supported.
- Configuración de resolución de imagen: Compruebe la colección Photo-Camera.AvailableResolutions.
- Configuración del flash: Use el método PhotoCamera.IsFlashModeSupported.
- Enfoque en un punto específico: Use el método PhotoCamera.IsFocusAtPointSupported.
Para hacernos una idea de cómo capturar fotografías en nuestra aplicación, recorramos una aplicación simple que captura una foto al tocar el visor y después la guarda en la carpeta Álbum de cámara en el Concentrador de imágenes.
Comience con un proyecto de Windows Phone estándar, con la plantilla Aplicación para Windows Phone. Puede escribir las aplicaciones para Windows Phone 7.5 en C# o en Visual Basic. En este ejemplo usaremos C#.
Para simplificar este ejemplo limitaré la aplicación a la orientación horizontal y con la cámara primaria solamente. Administrar la orientación del dispositivo y dos cámaras que apuntan en direcciones distintas, rápidamente puede llevar a enredar las cosas; mi recomendación es ponerlo a prueba en un dispositivo físico para asegurarse de que haya alcanzado el comportamiento deseado. Más adelante me referiré más detenidamente en la orientación.
En MainPage.xaml, actualice los atributos de PhoneApplicationPage, tal como se indica a continuación:
SupportedOrientations="Landscape" Orientation="LandscapeLeft"
Después, reemplace los contenidos de la cuadrícula LayoutRoot y TextBlock como se indica en la
Figura 1.
Figura 1 Adición de un Canvas y un 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."/>
El XAML de la Figura 1 usa un VideoBrush en un elemento Canvas para mostrar el visor, y proporciona un TextBlock para comunicarse con el usuario. La relación de aspecto del sensor de la cámara es 4:3 y el de la pantalla es 15:9. Si no especificamos un tamaño de lienzo con la misma relación 4:3 (640x480), la imagen se verá estirada en la pantalla.
En el elemento Canvas, el atributo Tap especifica el método que se debe llamar cuando el usuario toca la pantalla, el método viewfinder_Tapped. Para mostrar la secuencia de imágenes del búfer de vista previa de la cámara, especificamos un VideoBrush llamado viewfinderBrush como fondo del lienzo. Al igual que el visor de una cámara reflex de un objetivo (SLR), viewfinderBrush permite ver los fotogramas de la vista previa de la cámara. La transformación de viewfinderBrush básicamente “fija” al visor en el centro del lienzo mientras se le hace rotar. Analizaré el código que se encuentra detrás de este XAML en las siguientes secciones. La Figura 2 muestra la UI de Simple Photo App.
Figura 2 La UI de Simple Photo App
Para inicializar y liberar la cámara para capturar fotografías y guardarlas en la carpeta Álbum de cámara en el Concentrador de imágenes vamos a necesitar las clases PhotoCamera y MediaLibrary, respectivamente. Comience por agregar una referencia al ensamblado Microsoft.Xna.Framework. No hace falta saber de programación de XNA para este ejemplo, aunque sí va a necesitar tipos en este ensamblado para obtener acceso a la biblioteca de medios.
En la parte superior del archivo MainPage.xaml.cs, agregue las directivas para la biblioteca de cámara y medios:
using Microsoft.Devices;
using Microsoft.Xna.Framework.Media;
En la clase MainPage, agregue las siguientes variables de nivel de clase:
private int photoCounter = 0;
PhotoCamera cam;
MediaLibrary library = new MediaLibrary();
Es posible que la cámara tarde algunos segundos en inicializarse. Al declarar el objeto PhotoCamera en el nivel de clase, podemos crearlo al navegar a la página y eliminarlo de la memoria cuando salgamos de la página. Para esto usaremos los métodos OnNavigatedTo y OnNavigatingFrom.
En el método OnNavigatedTo, cree el objeto de cámara, regístrelo para los eventos de cámara en los que lo usará y establezca la vista previa de la cámara en viewfinderBrush, como la fuente del visor. Aunque muy comunes, las cámaras son opcionales en Windows Phone 7.5, y por lo tanto es importante comprobar su presencia antes de crear el objeto de cámara. Si la cámara primaria no está disponible el método muestra un mensaje al usuario.
Agregue los métodos que aparecen en la Figura 3 a la clase MainPage.
Figura 3 Los métodos OnNavigatedTo y 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();
}
}
Al navegar fuera de la página, usaremos el método OnNavigatingFrom para eliminar el objeto de cámara y para que elimine del registro cualquier evento de la cámara. Esto permite reducir el consumo de energía, hacer más expedito el apagado y liberar memoria.
Captura de una fotografía Tal como se indica en el XAML, cuando el usuario toca el visor, se llama al método viewfinder_Tapped. Este método inicia la captura de imagen en cuanto la cámara está lista. Si la cámara no se ha inicializado o si en ese momento está capturando otra imagen, se genera una excepción. Para mitigar las excepciones, plantéese la posibilidad de desactivar los mecanismos que gatillan la captura fotográfica hasta que se activa el evento Initializated. Omitiremos este paso con el fin de simplificar el ejemplo.
En la Figura 4 se muestra el código que debemos agregar a la clase MainPage.
Figura 4 El método 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;
});
}
}
}
La captura y el guardado de las fotografías son tareas asincrónicas. Cuando se llama al método CaptureImage, se inicia una cadena de eventos y el control se devuelve a la UI. Tal como se indica en el diagrama de eventos en la Figura 5, cada captura de imagen tiene dos etapas. Primero, el sensor de la cámara captura la fotografía y después se crea la imagen con los datos del sensor.
Figura 5 La secuencia de eventos de captura de imágenes de la clase PhotoCamera
Guardado de una fotografía Una vez que el sensor captura la foto, se crean dos archivos de imagen en paralelo, un archivo en tamaño completo y una miniatura. No estamos obligados a usar ambos. Cada uno está disponible como una secuencia de imágenes JPG en la propiedad e.ImageStream de los argumentos de los eventos correspondientes.
Como la biblioteca de medios crea automáticamente sus propias miniaturas para mostrarlas en el Concentrador de imágenes del dispositivo, este ejemplo no requiere de la versión en miniatura de la imagen. Sin embargo, cuando deseemos mostrar una miniatura en su aplicación, la propiedad e.ImageStream del controlador de eventos CaptureThumbnailAvailable podría ser una excelente opción.
En cuanto está disponible la secuencia, podemos usarla para guardar la imagen en varias ubicaciones. Por ejemplo:
- Carpeta Álbum de cámara: use el método MediaLibrary.SavePictureToCameraRoll.
- Carpeta Imágenes guardadas: use el método MediaLibrary.Save-Picture.
- Almacenamiento aislado: use el método IsolatedStorageFile-Stream.Write.
En este ejemplo guardaremos la imagen en la carpeta Álbumes de cámara. Para ver cómo guardar una imagen en el Almacenamiento aislado, consulte el Ejemplo básico de la cámara en Windows Phone SDK. Agregue el código de la Figura 6 a la clase MainPage.
Figura 6 Guardado de una imagen en la carpeta Álbumes de cámara
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.";
});
}
Con el código de la Figura 6, los mensajes se envían a la UI antes y después de que se guarda la imagen en la carpeta Álbumes de cámara. Estos mensajes sirven únicamente para que usted pueda entender mejor lo que está pasando, no son necesarios. El método BeginInvoke es requerido para pasar el mensaje al subproceso de la UI. Si no usamos BeginInvoke, aparecerá una excepción por acceso entre subprocesos. Para ser breves, este método carece del código para el control de errores.
Al controlar RotationWhen en el momento de guardar una imagen en la biblioteca de medios se registra la orientación correcta de la imagen en la información EXIF del archivo. La preocupación principal de nuestra aplicación es la orientación de la vista previa de la cámara en la UI. Para que la vista previa siga apareciendo siempre con la orientación correcta, debemos rotar el visor (VideoBrush) en la medida que sea apropiado. Para lograr la rotación debemos reemplazar el método virtual OnOrientationChanged. Agregue el código de la Figura 7 a la clase MainPage.
Figura 7 Reemplazo del método virtual 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);
}
En el caso que no se realicen ajustes sobre su orientación, el visor de una cámara principal típica solo estará orientado correctamente cuando el botón del obturador esté apuntando hacia arriba (LandscapeLeft). Si hacemos rotar el dispositivo de manera que el obturador del hardware apunte hacia abajo (LandscapeRight), se rotará el visor en 180 grados para mostrarlo correctamente en la UI. La orientación de la propiedad PhotoCamera se usa aquí para aquellos casos en los que la orientación física de la cámara primaria es atípica.
Declaración de las capacidades de la aplicación Por último, siempre cuando nuestra aplicación use una cámara, debemos declararlo en el manifiesto de aplicación, el archivo WMAppManifest.xml. Sin importar la cámara que usemos, necesitaremos la capacidad ID_CAP_ISV_CAMERA. De forma opcional, podemos usar también ID_HW_FRONTCAMERA para indicar que la aplicación requiere de una cámara frontal:
<Capability Name="ID_CAP_ISV_CAMERA"/>
<Capability Name="ID_HW_FRONTCAMERA"/>
Nuestra aplicación para la cámara no se ejecutará sin la capacidad ID_CAP_ISV_CAMERA. Si hasta ahora no nos habíamos topado con problemas al ejecutar la aplicación, se debe a que esta capacidad se agrega automáticamente a los proyectos nuevos para Windows Phone. Sin embargo, cuando actualicemos la aplicación, deberemos hacerlo manualmente. ID_HW_FRONTCAMERA siempre hay que agregarlo manualmente, pero su ausencia no impide que se ejecute esta aplicación.
Estas capacidades sirven como advertencia para los usuarios con dispositivos que no cuentan con una cámara, pero nada les impide descargar y comprar la aplicación. Por esta razón, le recomiendo que siempre cree una versión de evaluación de su aplicación. De este modo, si los usuarios no ven las advertencias, ellos no gastarán dinero para encontrarse con que la aplicación no funciona como esperado en su dispositivo. Esto favorecerá la calificación de su aplicación.
Si no lo ha hecho aún, presione F5 y depure esta aplicación sencilla para la cámara en su dispositivo. Puede depurar la aplicación en el emulador, pero solo verá una caja negra que se desplaza por la pantalla, puesto que el emulador no tiene una cámara física. Al depurar en un dispositivo, tenga en cuenta que no podrá ver las imágenes nuevas en el Concentrador de imágenes hasta que desconecte el dispositivo de su PC.
Para profundizar más, eche un vistazo al Ejemplo básico de la cámara de Windows Phone SDK. Ese ejemplo ilustra toda la API para capturar fotografías: desde ajustar la configuración del flash y la resolución a incorporar el enfoque táctil y el botón de hardware del obturador.
Obtención de acceso al búfer de vista previa de la cámara
En el ejemplo anterior, los cuadros del búfer de vista previa de la cámara se transmitieron al visor. La clase PhotoCamera también expone el fotograma actual del búfer de vista previa para permitir una manipulación píxel por píxel de cada fotograma. Demos ahora una mirada a un ejemplo de Windows Phone SDK para ver cómo podemos manipular los fotogramas del búfer de vista previa y mostrarlos en un mapa de bits de escritura en la UI.
La clase PhotoCamera expone el cuadro actual del búfer de vista previa con los siguientes métodos de obtención de vista previa:
- GetPreviewBufferArgb32: matriz de enteros con el fotograma actual en formato ARGB.
- GetPreviewBufferYCbCr: matriz de bytes con el fotograma actual en formato YCbCr.
- GetPreviewBufferY: matriz de bytes con el plano de la luminancia solamente, en un formato similar.
El formato ARGB es el que se usa para describir los colores en Silverlight para las aplicaciones de Windows Phone. YCbCr permite un procesamiento eficaz de las imágenes, pero Silverlight no puede usar YCbCr. Si desea manipular un cuadro YCbCr en su aplicación, debe convertir el fotograma a ARGB antes de mostrarlo. Para obtener más información acerca de estos formatos y de la conversión de colores, consulte la página de la biblioteca de MSDN “Conversión de color en la cámara (YCbCr a ARGB) para Windows Phone”, en wpdev.ms/colorconversion.
El Ejemplo de la cámara con escala de grises de Windows Phone SDK (ver la Figura 8) enseña como manipular los fotogramas en ARGB del búfer de vista previa y escribirlos prácticamente en tiempo real en una imagen de mapa de bits de escritura. En este ejemplo convertimos cada fotograma de color a escala de grises. Observe que la finalidad de este ejemplo es demostrar la manipulación de ARGB; si su aplicación solo necesita escala de grises, plantéese la posibilidad de usar el método GetPreviewBufferY en su lugar.
Figura 8 La UI del Ejemplo de la cámara con escala de grises
En el archivo XAML, se usa una etiqueta image para hospedar el mapa de bits de escritura correspondiente (la imagen en blanco y negro que se encuentra en la esquina inferior izquierda de la UI), de esta manera:
<Image x:Name="MainImage"
Width="320" Height="240"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
Margin="16,0,0,16"
Stretch="Uniform"/>
Cuando se presiona un botón para permitir la conversión a escala de grises, se crea un subproceso nuevo para realizar el procesamiento; el mapa de bits de escritura (que tiene las mismas dimensiones del búfer de vista previa) se crea y asigna como fuente del control Imagen:
wb = new WriteableBitmap(
(int)cam.PreviewResolution.Width,
(int)cam.PreviewResolution.Height);
this.MainImage.Source = wb;
El subproceso realiza su trabajo en el método PumpARGBFrames. Allí, se usa una matriz entera llamada ARGBPx para conservar una instantánea del búfer de vista previa actual. Cada entero de la matriz representa un píxel del fotograma en formato ARGB. Esta matriz también se crea con las mismas dimensiones del búfer de vista previa:
int[] ARGBPx = new int[
(int)cam.PreviewResolution.Width *
(int)cam.PreviewResolution.Height];
Mientras está habilitada la característica “escala de grises” del ejemplo, el subproceso copia el cuadro actual del búfer de vista previa en la matriz ARGBPx. Aquí, phCam es el objeto de la cámara:
phCam.GetPreviewBufferArgb32(ARGBPx);
Una vez que el búfer se copió a la matriz, el subproceso itera por cada píxel y lo convierte a escala de grises (para obtener más detalles sobre cómo se lleva a cabo esto, consulte el ejemplo):
for (int i = 0; i < ARGBPx.Length; i++)
{
ARGBPx[i] = ColorToGray(ARGBPx[i]);
}
Finalmente, antes de procesar el siguiente fotograma, el subproceso usa el método BeginInvoke para actualizar WriteableBitmap en la UI. El método CopyTo anula los píxeles de WriteableBitmap con la matriz ARGBPx y el método Invalidate fuerza la actualización de WriteableBitmap, del siguiente modo:
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
// Copy to WriteableBitmap.
ARGBPx.CopyTo(wb.Pixels, 0);
wb.Invalidate();
pauseFramesEvent.Set();
});
La clase WriteableBitmap ofrece una gran variedad de posibilidades creativas. Ahora podrá incorporar el búfer de vista previa de la cámara en su repertorio de elementos visuales para la UI.
Grabación de vídeo
Aunque la clase PhotoCamera sirve para transmitir el búfer de vista previa a la UI, no podemos usarla para grabar vídeo. Para eso, necesitaremos algunas clases del espacio de nombres System.Windows.Media. En la parte final de este artículo, daremos un vistazo al Ejemplo de la grabadora de vídeo de Windows Phone SDK (consulte la Figura 9) para ver cómo grabar vídeo en un archivo MP4 en el Almacenamiento aislado. Podrá encontrar este ejemplo en la página de muestras de código de SDK.
Figura 9 UI del Ejemplo de la grabadora de vídeo
Las clases más importantes para la grabación de vídeo son:
- CaptureDeviceConfiguration: úsela para comprobar la disponibilidad de un dispositivo de captura de vídeo.
- CaptureSource: úsela para iniciar y detener la grabación/vista previa de vídeo.
- VideoBrush: úsela para llenar los controles de UI de Silverlight con un objeto CaptureSource o PhotoCamera.
- FileSink: úsela para grabar un vídeo al Almacenamiento aislado cuando se esté ejecutando un objeto CaptureSource.
En el archivo XAML usamos un control Rectangle para mostrar el visor de la cámara:
<Rectangle
x:Name="viewfinderRectangle"
Width="640"
Height="480"
HorizontalAlignment="Left"
Canvas.Left="80"/>
Vale decir, sin embargo, que el control Rectangle no es necesario para mostrar vídeo. También podemos usar el control Canvas, como se muestra en el primer ejemplo. Usé el control Rectangle simplemente para ejemplificar otra manera de mostrar vídeo.
En el nivel de página, declaramos las siguientes variables:
// 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";
Cuando un usuario navega a la página, el método InitializeVideoRecorder inicia la cámara y envía una vista previa de la cámara al rectángulo. Después de crear los objetos captureSource y fileSink, el método InitializeVideoRecorder usa el objeto estático CaptureDeviceConfiguration para encontrar un dispositivo de vídeo. Si no hay ninguna cámara disponible, la variable videoCaptureDevice será nula:
videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
En Windows Phone 7.5, las cámaras son opcionales. Aunque son muy comunes en los dispositivos de hoy en día, se recomienda confirmar su presencia en el código. Tal como puede ver en la Figura 10, usamos la variable videoCaptureDevice para confirmar la presencia de una cámara. Si hay una disponible, se configura captureSource como la fuente de un VideoBrush denominado videoRecorderBrush, el cual se usa para rellenar el control Rectangle denominado viewfinderRectangle. En cuanto se llama al método Start del objeto captureSource, la cámara comienza a enviar el vídeo al rectángulo.
Figura 10 Visualización de la vista previa de vídeo
// 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.");
}
En este ejemplo, un método auxiliar llamado UpdateUI administra los estados del botón y escribe los mensajes para el usuario. Para obtener más información, consulte el Ejemplo de la grabadora de vídeo.
Si bien creamos el objeto fileSink, todavía no se graba ningún vídeo. Este estado de la aplicación se denomina “vista previa” del vídeo. Para grabar vídeo, debemos conectar fileSink a captureSource antes de iniciarse. En otras palabras, antes de que pueda registrar vídeo, tenemos que detener el objeto captureSource.
Cuando el usuario presiona el botón de grabación del Ejemplo de la grabadora de vídeo, el método StartVideoRecorder inicia la transición de la vista previa a la grabación. El primer paso de la transición es detener al objeto captureSource y reconfigurar el objeto 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;
}
Si bien las clases CaptureSource y VideoBrush pueden sonarle familiares si anteriormente ha desarrollado aplicaciones para el complemento de Silverlight, la clase FileSink es completamente nueva. La clase FileSink, disponible solo en las aplicaciones para Windows Phone, lo sabe todo acerca de la escritura en el Almacenamiento aislado; lo único que tenemos que hacer es entregarle el nombre del archivo.
Una vez que se haya reconfigurado fileSink, el método StartVideoRecorder volverá a iniciar captureSource y actualizará la UI:
captureSource.Start();
// Set the button states and the message.
UpdateUI(ButtonState.Ready, "Ready to record.");
Cuando el usuario deje de grabar, para cambiar de la grabación a la vista previa, hay que detener captureSource nuevamente antes de que se vuelva a configurar el objeto fileSink, tal como podemos observar en la Figura 11.
Figura 11 Transición de la grabación a la vista previa
// 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();
}
Aislamos la lógica del “inicio de la vista previa del vídeo” en otro método para permitir la transición de la vista previa a partir del estado de reproducción de vídeo (lo cual no se abordará en este artículo). Aunque no me referiré a la reproducción, es importante observar que en Windows Phone solo se puede realizar una transmisión de vídeo a la vez.
El Ejemplo del reproductor de vídeo tiene dos transmisiones de vídeo independientes:
- captureSource g videoRecorderBrush g viewfinderRectangle (control de Rectangle).
- isoVideoFile g VideoPlayer (control de MediaElement).
Puesto que solo se puede realizar una transmisión a la vez, en este ejemplo cada transmisión cuenta con un método de “eliminación” que podemos llamar antes de ejecutar la otra transmisión. En los métodos DisposeVideoPlayer y DisposeVideoRecorder, para detener la transmisión llamamos al método Stop en el objeto pertinente (y establecemos la fuente del control MediaElement en nulo). Los objetos CaptureSource y MediaElement realmente no implementan la interfaz IDisposable.
En este punto, puede dar la impresión que en el Ejemplo de la cámara con escala de grises se ejecutan dos vídeos al mismo tiempo. En realidad, esa aplicación solo cuenta con una transmisión de vídeo: la transmisión del objeto PhotoCamera al control VideoBrush. De hecho, el “vídeo” en escala de grises no era más que un mapa de bits que se actualizaba a gran velocidad, donde cada cuadro se manipulaba individualmente a partir de búfer de vista previa de la cámara.
En resumen
La nueva API para las cámaras de Windows Phone 7.5 abre la puerta a una nueva generación de aplicaciones para solucionar problemas y entretener de formas que habrían sido imposibles con las versiones anteriores del sistema operativo. En este artículo traté superficialmente unos pocos aspectos de la API solamente. Para ver la referencia completa, puede consultar la sección Cámara y fotografías de la documentación de Windows Phone SDK en wpdev.ms/cameraandphotos.
Matt Stroshane escribe documentación para desarrolladores para el equipo de Windows Phone. Sus otras contribuciones a la biblioteca de MSDN tratan acerca de productos tales como SQL Server, SQL Azure y Visual Studio. Cuando no está escribiendo, puede toparse con él en las calles de Seattle, donde entrena para la siguiente maratón. Puede seguirlo por Twitter en twitter.com/mattstroshane.
Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Eric Bennett, Nikhil Deore, Adam Lydick y Jon Sheller