Картинкой можно выразить гораздо больше, чем словами. Вы же знаете пословицу «лучше один раз увидеть, чем сто раз услышать»; вообразите, какие типы задач вы могли бы решать, будь у вашего приложения Windows Phone прямой доступ к камере. Что ж, с появлением Windows Phone 7.5 вы можете приступить к решению таких задач, используя встроенные камеры.
В этой статье я расскажу о передней и задней камерах, API-средствах управления ими и сопоставленном манифесте, а также о нескольких способах использования камеры в вашем следующем приложении для Windows Phone 7.5. Мы обсудим:
- получение снимков — я создам очень простое приложение для работы с фотографиями;
- доступ к буферу предварительного просмотра в камере (camera preview buffer) — я покажу Camera Grayscale Sample;
- запись видео — я дам обзор Video Recorder Sample.
Для создания приложения Windows Phone 7.5 вам потребуется Windows Phone SDK 7.1. Этот SDK включает примеры кода, которые детально демонстрируют каждый из упомянутых выше сценариев. Более подробную информацию см. в разделах Basic Camera Sample, Camera Grayscale Sample и Video Recorder Sample на странице Code Samples в SDK по ссылке wpdev.ms/officialsamples.
Заметьте, что в этой статье не рассматривается задача захвата камеры (camera capture task), которая была доступна еще в Windows Phone 7. Хотя эта задача позволяет легко получать фотоснимки для вашего приложения, она не дает возможности программного захвата снимков и не обеспечивает доступа к буферу предварительного просмотра в камере.
Устройство под управлением Windows Phone 7.5 может включать до двух камер, обозначаемых как основная (primary) и на лицевой стороне (front-facing). Основная камера находится на задней стороне устройства и обычно имеет более высокое разрешение и более широкие возможности, чем камера, находящаяся на лицевой стороне. Ни одна из этих камер не является обязательной на устройстве с Windows Phone 7.5, поэтому не забывайте сначала проверять в своем коде их наличие, прежде чем пытаться создавать объекты камеры. Позднее я продемонстрирую, как использовать для этой цели статический метод IsCameraTypeSupported.
Многие устройства с Windows Phone, доступные в США, оборудованы основной камерой с разрешением от 5 мегапикселей и выше, с автофокусировкой и вспышкой. Камера на лицевой стороне является новой возможностью, поддерживаемой Windows Phone 7.5.
Подробнее о спецификациях таких устройств см. вкладку Buy на странице windowsphone.com.
Получение снимков
Для доступа к основной и передней камере можно использовать одни и те же классы. Как вы увидите, выбор типа камеры сводится к указанию одного параметра в конструкторе объекта PhotoCamera. Однако с точки зрения проектирования, вы, вероятно, захотите по-другому обрабатывать взаимодействие с камерой на лицевой панели. Например, вы могли бы переворачивать по вертикали изображение с передней камеры, чтобы у пользователя возникала иллюзия того, что он смотрит в зеркало.
При захвате снимков в приложении Windows Phone 7.5 вы будете работать в основном с классом PhotoCamera из пространства имен Microsoft.Devices. Этот класс обеспечивает широкие возможности в управлении параметрами камеры и ее поведением. Например, вы можете:
- активизировать затвор камеры вызовом метода PhotoCamera.CaptureImage;
- инициировать автофокусирование методом PhotoCamera.Focus;
- указать разрешение снимка через свойство PhotoCamera.Resolution;
- задать параметры вспышки настройкой свойства PhotoCamera.FlashMode;
- связать аппаратную кнопку срабатывания затвора с событиями из статического класса CameraButtons;
- реализовать автофокусировку на определенной точке с помощью метода PhotoCamera.Focus¬AtPoint.
В этой статье я продемонстрирую только первый пункт. Пример, в котором показывается, как делать все из перечисленного, см. в Basic Camera Sample на странице образцов кода Windows Phone SDK.
Заметьте: даже при наличии камеры может оказаться так, что она поддерживает не все перечисленные API. Следующие подходы помогут определить, что именно вам доступно:
- камера — используйте статический метод PhotoCamera.IsCameraTypeSupported;
- автофокусировка — используйте метод PhotoCamera.IsFocus¬¬Supported;
- настройка разрешения снимков — проверяйте набор PhotoCamera.AvailableResolutions;
- настройка вспышки — используйте метод PhotoCamera.IsFlashMode¬Supported;
- автофокусировка по определенной точке (point-specific focus) — используйте метод PhotoCamera.IsFocus¬AtPointSupported.
Чтобы вы получили представление о том, как получать снимки в своем приложении, давайте разберем простую программу, которая захватывает снимок, когда вы касаетесь видоискателя, а затем сохраняет его в папке Camera Roll в Pictures Hub.
Начнем со стандартного проекта Windows Phone, используя шаблон Windows Phone Application. Вы можете писать приложения Windows Phone 7.5 на C# или Visual Basic. В этом примере используется C#.
Я упрощу этот пример, ограничив приложение только ландшафтной ориентацией и применением только основной камеры. Управление ориентацией для устройства и двух камер, каждая из которых смотрит в разных направлениях, может довольно быстро запутать; советую всегда тестировать свои программы на реальном физическом устройстве, чтобы убедиться в правильности их поведения. Об ориентации мы поговорим подробнее позже.
В MainPage.xaml измените атрибуты PhoneApplicationPage следующим образом:
SupportedOrientations="Landscape" Orientation="LandscapeLeft"
Потом замените содержимое сетки LayoutRoot на Canvas и TextBlock, как показано на рис. 1..
Рис. 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."/>
XAML на рис. 1 использует VideoBrush в Canvas для отображения видоискателя и предоставляет TextBlock для взаимодействия с пользователем. Матрица камеры дает аспект (соотношение сторон) 4:3, а аспект экрана равен 15:9. Если вы не укажете размер холста (canvas) с тем же аспектом 4:3 (640×480), изображение будет растянуто на весь экран.
В элементе Canvas атрибут Tap указывает метод viewfinder_Tapped, который должен вызываться при касании экрана. Чтобы отобразить поток изображения их буфера предварительного просмотра в камере, Video¬Brush с именем viewfinderBrush указывается в качестве фона холста. Как и видоискатель от однообъективной зеркальной камеры (single-lens reflex, SLR), viewfinderBrush позволяет видеть кадры от камеры в режиме предварительного просмотра. Преобразование в viewfinderBrush, по сути, «закрепляет» видоискатель по центру холста при его вращении. Отделенный код для этого XAML мы обсудим в следующем разделе. Нарис. 2 показан Simple Photo App UI.
Рис. 2. Simple Photo App UI
Инициализация и освобождение камеры Для получения снимков и их сохранения в папке Camera Roll в Pictures Hub вам потребуются классы 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, они все же не являются обязательными компонентами, поэтому важно проверять их наличие перед созданием объекта камеры. Если основной камеры нет, данный метод выдает сообщение пользователю.
Добавьте в класс MainPage методы, показанные на рис. 3.
Рис. 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, захват каждого изображения происходит в два этапа. Сначала матрица камеры захватывает снимок, а затем создаются изображения на основе ее данных.
Рис. 5. Последовательность событий захвата изображения в классе PhotoCamera
Сохранение снимка После того как матрица захватывает снимок, создаются сразу два файла: полноразмерного изображения и эскиза (thumbnail). Вы не обязаны использовать их оба. Каждый из них доступен как поток изображения в формате JPG в свойстве e.ImageStream в аргументах соответствующих событий.
Медиа-библиотека автоматически создает собственные эскизы для отображения в Pictures Hub устройства, поэтому в данном примере эскизная версия изображения не требуется. Однако, если вы хотите отображать эскиз в своем приложении, используйте e.ImageStream в обработчике события Capture¬ThumbnailAvailable.
Когда поток данных доступен, вы можете использовать его для сохранения изображения в нескольких местах, например:
- в папке Camera Roll — вызовите метод MediaLibrary.SavePictureToCameraRoll;
- в папке Saved Pictures — вызовите метод MediaLibary.Save¬Picture;
- в изолированном хранилище (Isolated Storage) — вызовите метод IsolatedStorageFile¬Stream.Write.
В этом примере мы сохраним изображение в папке Camera Roll. Пример того, как сохранить изображение в изолированном хранилище, см. в Basic Camera Sample в Windows Phone SDK. Добавьте в класс MainPage код, приведенный на рис. 6.
Рис. 6. Сохранение изображения в папке Camera Roll
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.";
});
}
UI в коде на рис. 6 посылаются сообщения до и после сохранения изображения в папке Camera Roll. Эти сообщения просто помогают понять, что происходит; использовать их не обязательно. Для передачи сообщения в UI-поток нужен метод BeginInvoke. Если вы не использовали BeginInvoke, будет сгенерировано исключение попытки передачи данных между потоками (cross-threading exception). Для краткости в этом методе нет кода обработки ошибок.
Обработка поворачивания Когда вы сохраняете снимок в медиа-библиотеку, правильная ориентация изображения будет отмечена в EXIF-информации файла. Основная задача вашего приложения — определить, как ориентировать в UI изображение предварительного просмотра, поступающее от камеры. Чтобы поддерживать правильность ориентации изображения предварительного просмотра, поворачивайте видоискатель (VideoBrush), если это возможно. Вращение осуществляется переопределенным виртуальным методом OnOrientationChanged. Добавьте в класс MainPage код, приведенный на рис. 7.
Рис. 7. Переопределение виртуального метода OnOrientationChanged
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 градусов, чтобы он правильно отображался в UI. Свойство Orientation объекта PhotoCamera используется здесь на случай нетипичной физической ориентации основной камеры.
Объявление возможностей приложения Наконец, когда ваше приложение использует камеру, вы должны объявить об этом в его файле манифеста, 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 и отладьте это простое приложение на своем устройстве. Вы можете вести отладку в эмуляторе, но в таком случае вы увидите лишь черный прямоугольник, перемещаемый по экрану, так как в эмуляторе нет физической камеры. При отладке на устройстве учитывайте, что вы не сможете просматривать новые изображения в Picture Hub, пока не отключите устройство от ПК.
Если вас интересуют подробности, изучите Basic Camera Sample в Windows Phone SDK. Этот пример иллюстрирует весь API для захвата снимков: от настройки вспышки и разрешения до автофокусировки по определенной точке и аппаратной кнопки затвора.
Доступ к буферу предварительного просмотра в камере
В предыдущем примере кадры из буфера предварительного просмотра передавались потоком в видоискатель. Класс PhotoCamera также поддерживает манипуляции над пикселями текущего кадра из буфера предварительного просмотра. Рассмотрим пример из Windows Phone SDK, чтобы понять, как можно манипулировать кадрами из этого буфера и отображать их на записываемой битовой карте в UI.
Класс PhotoCamera предоставляет текущий кадр из буфера предварительного просмотра с помощью следующих GetPreview-методов:
GetPreviewBufferArgb32 — оперирует целочисленным массивом текущего кадра в формате ARGB;
GetPreviewBufferYCbCr — оперирует байтовым массивом текущего кадра в формате YCbCr;
GetPreviewBufferY — оперирует байтовым массивом только яркостной части в аналогичном формате.
ARGB — формат, применяемый для описания цвета в приложениях Silverlight for Windows Phone. YCbCr обеспечивает эффективную обработку изображений, но Silverlight не может использовать YCbCr. Если вы хотите манипулировать кадром в формате YCbCr в своем приложении, то должны преобразовать этот кадр в формат ARGB до его отображения. Подробнее об этих форматах и преобразовании цветовых пространств см. на странице MSDN Library «Camera Color Conversion (YCbCr to ARGB) for Windows Phone» по ссылке wpdev.ms/colorconversion.
Camera Grayscale Sample из Windows Phone SDK (рис. 8) демонстрирует, как манипулировать ARGB-кадрами из буфера предварительного просмотра и записывать их в битовую карту почти в реальном времени. В этом примере каждый кадр преобразуется из цветного в оттенки серого. Заметьте, что цель этого примера — демонстрация манипуляций над ARGB-изображением; если в вашем приложении нужны черно-белые кадры с оттенками серого, подумайте о переходе на метод GetPreviewBufferY.
Рис. 8. Camera Grayscale Sample UI
В XAML-файле используется тег image для размещения соответствующей записываемой битовой карты (черно-белого изображения в левом нижнем углу UI):
<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
Как только буфер скопирован в массив, поток перебирает каждый пиксель и преобразует его цвет в соответствующий оттенок серого (подробнее о том, как именно это делается, см. пример в SDK):
for (int i = 0; i < ARGBPx.Length; i++)
{
ARGBPx[i] = ColorToGray(ARGBPx[i]);
}
Наконец, перед обработкой следующего кадра поток вызывает метод BeginInvoke для обновления WriteableBitmap в UI. Метод CopyTo перезаписывает пиксели WriteableBitmap содержимым массива ARGBPx, а вызов метода Invalidate приводит к перерисовке WriteableBitmap:
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
// Копируем в WriteableBitmap
ARGBPx.CopyTo(wb.Pixels, 0);
wb.Invalidate();
pauseFramesEvent.Set();
});
Класс WriteableBitmap открывает широкий простор для творчества. Теперь вы можете включить буфер предварительного просмотра в набор своих визуальных элементов для UI.
Запись видео
Хотя вы можете использовать класс PhotoCamera для потоковой передачи содержимого буфера предварительного просмотра в UI, он не годится для записи видео. Для этой цели вам понадобятся некоторые классы из пространства имен System.Windows.Media. В этой последней части статьи мы рассмотрим пример Video Recorder Sample из Windows Phone SDK (рис. 9), чтобы понять, как записывать видео в MP4-файл в изолированное хранилище. Вы найдете этот пример на странице образцов кода в SDK.
Рис. 9. Video Recorder Sample UI
Основные классы для записи видео:
- CaptureDeviceConfiguration — позволяет проверить доступность устройства захвата видео;
- CaptureSource — используется для старта/остановки записи видео или предварительного просмотра;
- VideoBrush — позволяет заполнить UI-элементы Silverlight объектом CaptureSource или PhotoCamera;
- FileSink — применяется для записи видео в изолированное хранилище при работе объекта CaptureSource.
В XAML-файле для отображения видоискателя камеры используется элемент управления Rectangle:
<Rectangle
x:Name="viewfinderRectangle"
Width="640"
Height="480"
HorizontalAlignment="Left"
Canvas.Left="80"/>
Однако элемент управления Rectangle не обязателен для отображения видео. Вы могли бы задействовать элемент управления Canvas, как было показано в первом примере. Элемент Rectangle применяется просто для демонстрации другого способа отображения видео.
На уровне страницы объявляются следующие переменные:
// Видоискатель для захвата видео
private VideoBrush videoRecorderBrush;
// Источник и устройство для захвата видео
private CaptureSource captureSource;
private VideoCaptureDevice videoCaptureDevice;
// Данные о файле для хранения записи
private IsolatedStorageFileStream isoVideoFile;
private FileSink fileSink;
private string isoVideoFileName = "CameraMovie.mp4";
Когда пользователь переходит на страницу, метод InitializeVideoRecorder запускает камеру и посылает содержимое буфера предварительного просмотра в прямоугольник (rectangle). После создания объектов captureSource и fileSink метод InitializeVideoRecorder использует статический объект CaptureDeviceConfiguration для поиска видеоустройства. Если камера недоступна, videoCaptureDevice будет равен null:
videoCaptureDevice =CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
В Windows Phone 7.5 камеры опциональны. Хотя они распространены в современных устройствах, лучше всего проверять их наличие в коде. Как показано на рис. 10, videoCaptureDevice используется для проверки наличия камеры. Если таковая есть, captureSource настраивается как источник для VideoBrush с именем videoRecorderBrush, и этот videoRecorderBrush заполняет элемент управления Rectangle с именем viewfinderRectangle. При вызове метода Start объекта captureSource камера начинает посылать видеопоток в этот прямоугольник.
Рис. 10. Вывод видео в режиме предварительного просмотра
// Инициализируем камеру, если она есть на устройстве
if (videoCaptureDevice != null)
{
// Создаем VideoBrush для видоискателя
videoRecorderBrush = new VideoBrush();
videoRecorderBrush.SetSource(captureSource);
// Показываем изображение видоискателя в прямоугольнике
viewfinderRectangle.Fill = videoRecorderBrush;
// Начинаем захват видео и отображаем его в видоискателе
captureSource.Start();
// Задаем состояние кнопки и сообщение
UpdateUI(ButtonState.Initialized,
"Tap record to start recording...");
}
else
{
// Отключаем кнопки, если камера
// не поддерживается устройством
UpdateUI(ButtonState.CameraNotSupported,
"A camera is not supported on this device.");
}
В этом примере вспомогательный метод UpdateUI управляет состоянием кнопок и выводит сообщения пользователю. Детали см. в примере Video Recorder Sample.
Хотя объект fileSink создан, в этот момент видео не записывается. Такое состояние приложения называют предварительным просмотром видео. Для записи видео fileSink нужно подключить к captureSource до его запуска. Иначе говоря, прежде чем вы сможете записывать видео, вы должны остановить captureSource.
Когда пользователь касается кнопки записи в примере, метод StartVideoRecorder инициирует переход из предварительного просмотра к записи. Первый этап этого перехода заключается в остановке captureSource и перенастройке fileSink:
// Подключаем fileSink к captureSource
if (captureSource.VideoCaptureDevice != null
&& captureSource.State == CaptureState.Started)
{
captureSource.Stop();
// Соединяем ввод и вывод fileSink
fileSink.CaptureSource = captureSource;
fileSink.IsolatedStorageFileName = isoVideoFileName;
}
Хотя классы CaptureSource и VideoBrush могут показаться знакомыми, если вы уже разрабатывали приложения для плагинов Silverlight, класс FileSink является совершенно новым. Это эксклюзивный класс для приложений Windows Phone, которому известно все, что необходимо для записи в изолированное хранилище; от вас требуется лишь предоставить имя файла.
После перенастройки fileSink метод StartVideoRecorder перезапускает captureSource и обновляет UI:
captureSource.Start();
// Задаем состояние кнопки и сообщение
UpdateUI(ButtonState.Ready, "Ready to record.");
Когда пользователь останавливает запись, начинается переход от записи к предварительному просмотру и captureSource нужно снова остановить до перенастройки fileSink, как показано на рис. 11.
Рис. 11. Переход от записи к предварительному просмотру
// Останавливаем запись
if (captureSource.VideoCaptureDevice != null
&& captureSource.State == CaptureState.Started)
{
captureSource.Stop();
// Отключаем fileSink
fileSink.CaptureSource = null;
fileSink.IsolatedStorageFileName = null;
// Задаем состояние кнопки и сообщение
UpdateUI(ButtonState.NoChange, "Preparing viewfinder...");
StartVideoPreview();
}
Логика запуска видео/предварительного просмотра была изолирована в другом методе, который обеспечивает переход в предварительный просмотр из состояния воспроизведения видео (в этой статье эта тема не рассматривается). Хотя я ничего не говорю здесь о воспроизведении видео, важно отметить, что в Windows Phone единовременно поддерживается только один видеопоток.
В Video Recorder Sample присутствуют два раздельных видеопотока:
{Для верстки: сохранить стрелки, как в оригинале}
- captureSource → videoRecorderBrush → viewfinderRectangle (элемент управления Rectangle).
- isoVideoFile → VideoPlayer (элемент управления MediaElement).
Поскольку единовременно может работать только один видеопоток, в этом примере предусмотрен Dispose-метод для каждого потока, который можно вызывать перед запуском другого потока. В методах DisposeVideoPlayer и Dispose¬VideoRecorder видеопоток останавливается вызовом метода Stop из соответствующего объекта (и присваиванием источнику для MediaElement значения null). Объекты CaptureSource и MediaElement на самом деле не реализуют интерфейс IDisposable.
До этого момента вы, возможно, думали, что в примере Camera Grayscale Sample было два видеопотока одновременно. В реальности в том приложении присутствовал только один видеопоток: от объекта PhotoCamera к элементу управления VideoBrush. Черно-белое «видео» на самом деле было просто битовой картой, которая очень быстро перерисовывалась.
Заключение
API камеры — новинка Windows Phone 7.5 — открывает возможности для появления новой категории приложений, решающих такие задачи и развлекающих такими способами, которые были невозможны в более ранних версиях этой операционной системы. В данной статье были затронуты лишь некоторые аспекты этого API. Полное описание см. в разделе Camera and Photos документации Windows Phone SDK на странице wpdev.ms/cameraandphotos.