The viewmodel contains the following three commands:
TakePhotoCommand
ToggleVideoCaptureCommand
PlayVideoCommand
TakePhotoCommand
uses the CaptureSource
to save a still image to isolated storage. ToggleVideoCaptureCommand
starts and stops the recording of video and initially causes the CaptureSource
to be attached to a FileSink
object so that a video file is written to isolated storage.
The commands are instantiated in the viewmodel constructor like so:
public CaptureSourceViewModel() : base("CaptureSource")
{
takePhotoCommand
= new DelegateCommand(obj => TakePhoto(), obj => started);
toggleVideoCaptureCommand
= new DelegateCommand(obj => ToggleCapturingVideo());
playVideoCommand
= new DelegateCommand(obj => PlayVideo(),
obj => !capturingVideo && IsVideoCaptured());
}
Whenever something takes place that may alter the executable state of a command, the viewmodel’s UpdateCommands
method is called, which is shown in the following excerpt:
void UpdateCommands()
{
takePhotoCommand.RaiseCanExecuteChanged();
playVideoCommand.RaiseCanExecuteChanged();
}
The viewmodel’s TakePhoto
method calls the CaptureImageAsync
method of the CaptureSource
object, as shown:
void TakePhoto()
{
if (captureSource != null && captureSource.State == CaptureState.Started)
{
captureSource.CaptureImageAsync();
}
}
CaptureImageAsync
attempts to use the device camera to capture an image.
Note
If CaptureImageAsync
is called without first calling the CaptureSource.Start
method, an InvalidOperationException
is raised.
The CaptureSource.CaptureImageCompleted
event is raised if the call completes asynchronously, which does not necessarily mean that an image has been successfully acquired. If an error occurs during image capture, the CaptureSource.CaptureFailed
event is raised.
Note
Although the resulting CaptureImageCompletedEventArgs
has an Error
property (inherited from its AsyncCompletedEventArgs
), this property is never set; therefore, it cannot be used for error detection.
The resulting image is saved to the phone’s photo hub using the MediaLibrary
class (see Listing 21.12). The dimensions of the image are supplied by the event arguments. Just as the PhotoCamera
example maintained a collection of captured images, so too does the CaptureSourceViewModel
.
LISTING 21.12. CaptureSourceViewModel.HandleCaptureImageCompleted
Method
void HandleCaptureImageCompleted(object sender, CaptureImageCompletedEventArgs e)
{
if (e.Result != null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MediaLibrary mediaLibrary = new MediaLibrary();
string imageName = string.Format(
"Unleashed_{0:yyyy-MM-dd-HH-mm-ss}.jpg", DateTime.Now);
using (MemoryStream stream = new MemoryStream())
{
e.Result.SaveJpeg(
stream, e.Result.PixelWidth, e.Result.PixelHeight,
0, 100);
stream.Position = 0;
mediaLibrary.SavePicture(imageName, stream);
}
CapturedImages.Add(e.Result);
});
}
}
The CaptureSource.State
property, which is an enum value of type CaptureState
, indicates the state of the CaptureSource
object and can be either Failed, Started, or Stopped.
Recording the audio and video of a CaptureSource
instance requires the attachment of a FileSink
(see Listing 21.13). The FileSink
object is assigned the location of a file in isolated storage, to which it writes the video file in MP4 format.
The CaptureSource
object must be in a stopped state to attach or detach a FileSink
. Attempting to set the FileSink.CaptureSource
property while the CaptureSource
object is in a Started state raises an InvalidOperationException
.
Stopping the capture of video involves detaching the FileSink
from the CaptureSource
object. Again, the FileSink
can be removed only if the CaptureSource
is not started; CaptureSource.State
must not equal CaptureState.Started
.
LISTING 21.13. CaptureSourceViewModel.ToggleCapturingVideo
Method
FileSink fileSink;
void ToggleCapturingVideo()
{
if (!capturingVideo)
{
fileSink = new FileSink {IsolatedStorageFileName = videoFileName};
captureSource.Stop();
fileSink.CaptureSource = captureSource;
captureSource.Start();
}
else
{
captureSource.Stop();
fileSink.CaptureSource = null;
captureSource.Start();
}
CapturingVideo = !capturingVideo;
UpdateCommands();
}
After a video has been recorded to isolated storage, it can be played back using either a MediaPlayer
control within the app or via the MediaPlayerLauncher
, as shown in the following excerpt:
void PlayVideo()
{
MediaPlayerLauncher mediaPlayerLauncher = new MediaPlayerLauncher
{
Media = new Uri(videoFileName, UriKind.Relative),
Location = MediaLocationType.Data,
};
mediaPlayerLauncher.Show();
}
Note
Although AppBar
buttons were used to demonstrate the CaptureSource
class, the Microsoft.Devices.CameraButtons
class (presented earlier in this chapter) could be used instead. CameraButtons
can be used in conjunction with the CaptureSource
class to allow the hardware shutter button to start and stop video capture.
18.116.21.232