WindowsPhone8.1でカメラ操作(MediaCapture)と銘打ちましたが、普通にMediaCaptureを使用したサンプルなどは良くあります。
にもかかわらず、なぜに?となりますが、よくサンプルが出ているのは「WindowsRuntime」ベースのMediaCaptureです。
そこで「WindowsPhone SilverLight 8.1(以下WPSL8.1)」ベースでの解説など行うことにしました。
なぜWPSLで「MediaCapture」なのかですが、「PhotoCaptureDevice」API群がWindowsPhone8.1では非推奨(VS上では使用不可と出る)なためです。
さて、大きな違いですが、WPSL8.1ではUIElementの「CaptureElement」が存在しません。
WPSL8.1では今までの方法同様「VideoBrush」にアタッチする形をとります。
その為のAPIとして「Windows.Phone.Media.Capture.MediaCapturePreviewSink」があります。
<Rectangle x:Name="PreviewRectangle" Width="450" Height="450" VerticalAlignment="Top" Margin="0,40,0,12" Tap="PreviewRectangle_Tap" >
<Rectangle.Fill>
<VideoBrush x:Name="PreviewBrush">
<VideoBrush.RelativeTransform>
<CompositeTransform x:Name="previewTransform" CenterX=".5" CenterY=".5" Rotation="90" />
</VideoBrush.RelativeTransform>
</VideoBrush>
</Rectangle.Fill>
</Rectangle>
定番の方法で「Rectangle」に「VideoBrush」を設定します。
「Rotation=”90″」は入力が90度回転した状態で送られてくるためです。
次にコードの部分です。
基本構成としては「初期化」「プレビューの開始」「プレビューの停止」「解放」となっています。
また、基本以外のところでは「フォーカス」「カメラIDの取得」を行っています。
public class CameraDevice : IDisposable
{
// キャプチャー本体
private MediaCapture _captureManager;
// プレビュー用
private MediaCapturePreviewSink _previewSink;
// プレビュー判定
private bool _bPreview = false;
/// <summary>
/// Camera Device Initialize
/// </summary>
/// <returns></returns>
public async Task CameraDeviceInitialize()
{
try
{
// Get DeviceID rear camera
var devId = await GetCameraId(Panel.Back);
// 初期化
_captureManager = new MediaCapture();
await _captureManager.InitializeAsync(new MediaCaptureInitializationSettings
{
StreamingCaptureMode = StreamingCaptureMode.Video,
PhotoCaptureSource = PhotoCaptureSource.Photo,
VideoDeviceId = devId.Id
});
}
catch (Exception ex)
{
throw new Exception("CameraDeviceInitialize : " + ex.Message);
}
}
/// <summary>
/// Start PreView
/// </summary>
/// <param name="capturePreview">Preview area VideoBrush</param>
/// <returns></returns>
public async Task StartPreView(VideoBrush capturePreview)
{
try
{
if (_captureManager != null)
{
// Preview Sink Initialize
_previewSink = new MediaCapturePreviewSink();
// Photo Primary
_captureManager.VideoDeviceController.PrimaryUse = CaptureUse.Photo;
// List of supported video preview formats to be used by the default preview format selector.
var supportedVideoFormats = new List<string> { "nv12", "rgb32" };
// Find the supported preview format
var availableMediaStreamProperties =
_captureManager.VideoDeviceController.GetAvailableMediaStreamProperties(
Windows.Media.Capture.MediaStreamType.VideoPreview)
.OfType<Windows.Media.MediaProperties.VideoEncodingProperties>()
.Where(p => p != null
&& !String.IsNullOrEmpty(p.Subtype)
&& supportedVideoFormats.Contains(p.Subtype.ToLower()))
.ToList();
var previewFormat = availableMediaStreamProperties.FirstOrDefault();
// Start Preview stream
await
_captureManager.VideoDeviceController.SetMediaStreamPropertiesAsync(
Windows.Media.Capture.MediaStreamType.VideoPreview, previewFormat);
await
_captureManager.StartPreviewToCustomSinkAsync(
new Windows.Media.MediaProperties.MediaEncodingProfile { Video = previewFormat }, _previewSink);
// Set the source of the VideoBrush used for your preview
Microsoft.Devices.CameraVideoBrushExtensions.SetSource(capturePreview, _previewSink);
_bPreview = true;
}
}
catch (Exception ex)
{
Debug.WriteLine("StartPreView : " + ex.Message);
}
}
/// <summary>
/// Stop Preview
/// </summary>
/// <returns></returns>
public async Task StopPreView()
{
try
{
if (_captureManager != null && _bPreview)
{
await _captureManager.StopPreviewAsync();
_bPreview = false;
}
}
catch (Exception ex)
{
Debug.WriteLine("StopPreView" + ex.Message);
}
}
/// <summary>
/// I explicitly free
/// </summary>
public async void Dispose()
{
if (_captureManager != null)
{
await StopPreView();
_captureManager.Dispose();
}
}
/// <summary>
/// I will confirm the presence or absence rear camera, front camera
/// </summary>
/// <param name="desired"></param>
/// <returns></returns>
private static async Task<DeviceInformation> GetCameraId(Panel desired)
{
//var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
DeviceInformation deviceId = (await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture))
.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == desired);
if (deviceId != null)
{
return deviceId;
}
else
{
throw new Exception(string.Format("Camera of type {0} doesn't exist.", desired));
}
}
/// <summary>
/// Execution of focus
/// </summary>
/// <returns></returns>
public async Task SetFocusTask()
{
try
{
if (_captureManager == null)
{
return;
}
foreach (var variable in _captureManager.VideoDeviceController.FocusControl.SupportedFocusModes)
{
Debug.WriteLine(variable.ToString());
}
if (_captureManager.VideoDeviceController.FocusControl.Supported)
{
_captureManager.VideoDeviceController.FocusControl.Configure(new FocusSettings { Mode = FocusMode.Auto, DisableDriverFallback = true });
await _captureManager.VideoDeviceController.FocusControl.FocusAsync();
Debug.WriteLine("focus supported");
}
else
{
Debug.WriteLine("focus Not supported");
}
}
catch (Exception ex)
{
Debug.WriteLine("SetFocusTask" + ex.Message);
}
}
}
使用の際は「CameraDeviceInitialize」行って「StartPreView(PreviewBrush)」すると開始されます。
「PreviewBrush」が表示されていない場合は例外が発生しますので注意してください。
「OnNavigatingFrom」でプレビューの停止と解放を行いましょう。
「OnNavigatedTo」に初期化を書いておくことで、復帰の際にも対処可能です。
フォーカスやその他の機能に関しては必ずサポートの有無を確認したうえで、操作を開始しないと未サポートの場合、例外が起きて停止します。
駆け足ではありますが、これでWPSL8.1環境下でもカメラが使用可能です。
ただ使用して思うのは、このAPIでは機能が制限される端末が存在しうることです。
「Lumia 1320」ではフォーカスが可能なのですが「Samsung ATIV S」では未サポートとなります。(旧APIでは使用可能)
これは、使用しているWindowsPhone8.1がDeveloperPreviewであることが原因の可能性もあります。
それでも、明らかに以前より機能が落ちるAPIを使用しなければいけないのは残念です。(「入力画素数」が指定できない等)
もっとも、自分がまだAPIを把握し切れていないために、使いきれていないということもあり得ます。
今後WPSL8.1はOSのバージョンアップなどに伴って、使用可能なAPIが制限されるのではないかと思いますので、アプリを作成する際は、ストアへの登録のしやすさなども考え見ると「universal Apps」を前提として「WindowsRuntime」ベースへ移行していったほうがよいと思います。
もっとも、WinRTでは使用できないAPIが多くあるのも事実ですので、そういったAPIを生かしたアプリを作るのであれば「WindowsPhone SilverLight 8.1」ベースで作りこむのがよいと思います。