次の方法で共有


Windows Phone

Windows Phone ビデオ キャプチャ: ベスト オブ ブリード手法

Chris Barker

コード サンプルのダウンロード

Windows Phone 7.1 SDK は、Windows Phone 7.5 デバイスのカメラへのアクセス機能など、多くの新しい開発シナリオを可能にしました。Windows Phone 8 がリリースされると、サポートされているハードウェアで 1,080 ピクセルの高解像度ビデオをキャプチャする機能などの新機能が導入されました。Windows Phone 7.1 SDK でも新しいデバイス機能をサポートするよう拡張できましたが、Windows Phone 8 リリースでは OS のアーキテクチャがさらに変更されており、カーネルを Windows 8 と共有しています。さらに重要な変更として、多数の API が Windows 8 モデルに移行され、Windows ランタイム (WinRT) として知られる開発者サーフェスが使用されるようになりました。

Windows Phone 7.1 SDK は、開発者が Windows Phone 7.5 の機能を対象に開発できるバージョンです。ここからは、Windows Phone 7.5 を OS のバージョンと SDK の両方を含む言葉として用います (ついでながら、Windows Phone 7.5 のコード ネームは "Mango" で、Windows Phone 8 のコード ネームは "Apollo" でした。これらの名前を他の記事やドキュメントで目にされているかもしれません)。

Windows ランタイムでは、Windows Phone と Windows 8 に共通する多数の API が導入されただけではなく、より効率的なネイティブのランタイムが提供され、同じモデルを使って新しい Windows Phone 固有の API を提供できるようになりました。この記事では Windows ランタイムがもたらした新しい変更点について詳細には説明しませんが、ビデオ キャプチャを可能にするなどの API に影響を与えていることに留意してください。

この記事ではバージョン間の変更点の概要を説明しますが、本当の目的は、Windows Phone 7.5 のプロジェクトを維持しながらより豊かなエクスペリエンスを Windows Phone 8 のユーザーに提供する方法の説明です。この説明方法の大きな利点は、この記事で説明する手法がビデオ キャプチャに当てはまるだけでなく、Windows Phone 8 用に作り直されたすべての API にも当てはまることです。既存のパブリック コード サンプルを使用して、Windows Phone 7.5 プロジェクトと Windows Phone 8 プロジェクト、さらには Windows 8 プロジェクトにまたがったコードの再利用の導入に使用できる方法を紹介します。

開始する

ここで説明する主な目標の 1 つは、Windows Phone 7.5 デバイスを無視することなく Windows Phone 8 にソリューションを移植する方法を説明することです。その前に、少し話を戻して、開発者は多くの場合何も作業する必要がないことを指摘しておきましょう。予期しない動作が発生しないことを検証する必要はありますが、既存の Windows Phone 7.5 アプリは Windows Phone 8 でも引き続き実行できます。Visual Studio 2012 では、Windows Phone 7.5 を対象にする場合、サポートされている API だけを使用できます。また、いずれかの新しい API やその機能を利用する必要がある場合は、Windows Phone 8 用コードの作成を考慮するだけでよいことに注意してください。

ここでは、Windows Phone 7.5 のユーザーをサポートしたまま、Windows Phone 8 のユーザーがデバイスの充実した新機能を利用できるようにする必要があると想定して、話を進めます。

ベスト オブ ブリード ソリューションの実現方法を説明するために、出発点として Windows Phone (7.5) ビデオ レコーダー サンプル (bit.ly/16tM2c1、英語) を使用します。今回はたまたまビデオ レコーダーのコード サンプルを使用することにしましたが、ここで説明する手法は、プラットフォームの複数バージョンを対象にする必要がある、任意の数のプラットフォーム機能にすることができます。

このソリューションには、次のようにいくつか留意事項があります。

  • モデル - ビュー - ビューモデル (MVVM: Model-View-ViewModel) パターンを使用しません。
    • UI の大部分は MainPage.xaml ファイルに含まれています。
    • ロジックの大部分は MainPage.xaml.cs 分離コード ファイルで実行します。
  • カメラの操作には、Windows Phone 7.5 API、つまり System.Windows.Media.VideoCaptureDevice クラスを使用します。Windows Phone 8 では、この分野に新しい API が導入されていますが、これについては後で詳しく説明します。

このソリューションで MVVM パターンを使用しないことは、大きな問題ではありません。MVVM パターンを使用する方が将来のリファクタリング作業を多少省略できるので望ましいですが (一般に実稼働コードでは推奨されます)、本質的に、互換性の問題ではありません。

ただし、VideoCaptureDevice クラスは、Windows Phone 8 に移行する際の制限になります。このクラスは、Windows Phone 8 ランタイムに対して問題なく実行されますが、パフォーマンスが最大限に引き出されず、ハードウェアでサポートされているすべての解像度を対象に開発できません (他にも制限があります)。

以降のセクションでは、次の手法を採用します。

  • プラットフォーム間で実装方法が異なる機能に、共通の抽象化を作成します。これにより、その機能を使用するコードを共有できるようになります。
  • ビデオ レコーダー用の抽象化をポータブル クラス ライブラリ (PCL) に配置します。これにより、後で必要に応じてより多くのコードを PCL に移行しやすくなります。
  • ファイル リンクを使用して MainPage の分離コードを共有します (既存のアプリでは MVVM パターンを使用しないので、これは実用的な方法です。MVVM パターンを使用する場合は、ビューモデルを PCL に移行することをお勧めします)。

したがって、欲張ってベスト オブ ブリード手法を採用する場合は、MainPage.xaml.cs ファイルから Windows Phone 7.5 カメラ ロジックを抽象化するために何ができるかを考える必要があります。この問題の解決策は、次のセクションで紹介します。

実装を分離して抽象化する

まず、コードのファクタリング先が必要です。クラス ライブラリも理にかなっていますが、PCL を使用できます。PCL を使用すると、対象にするプラットフォームを指定でき、指定したすべてのプラットフォームで共通して提供される API だけを使用する便利な方法を利用できます。最終的には、(コンパイル時のターゲット プラットフォームに対する単なるコード/プロジェクトのリンクと再ビルドではなく) プロジェクト間のバイナリ参照を作成できます。ここでは、Windows Phone 7.5 以降と Windows ストア アプリ (つまり、Windows 8 の "最新のアプリ") を対象にする PCL プロジェクトを作成し、VideoRecorder.Shared という名前を付けることができます (図 1 参照)。


図 1 ポータブル クラス ライブラリの構成

抽象化パターン (bit.ly/YQwsVD、英語) に従って、VideoRecorderCore 抽象クラスを作成できます。このため、必要に応じてプラットフォーム固有のコードを対象にできます。この抽象クラスは、図 2 のようになります。

図 2 VideoRecorderCore 抽象クラスの作成

namespace VideoRecorder.Shared
 {
   public abstract class VideoRecorderCore
   {
     public abstract void InitializeVideoRecorder();
     public abstract void StartVideoRecording();
     public abstract void StopVideoRecording();
     public abstract void StartVideoPreview();
     public abstract void DisposeVideoPlayer();
     public abstract void DisposeVideoRecorder();
     public static VideoRecorderCore Instance { get; set; }
   }
 }

注: 図 2で示した例では、同じくらい簡単にインターフェイスを使用することもできますが、多くのシナリオでは共通の基本機能がおそらく必要になります。

sdkVideoRecorderCS プロジェクトでは、多少時間を費やして MVVM パターンを実装すると理想的ですが、後で実装することもできます。今回のリファクタリングでは、抽象クラスの具体的な実装を提供することに焦点を当てます。さいわい、既に具体的な実装があります。ただし、MainPage.xaml.cs での結合がいくぶん密接すぎます。これに対処するには、sdkVideoRecorderCS プロジェクトに WP7VideoRecorder クラスを作成して、PCL プロジェクトの VideoRecorderCore を継承します。後は、MainPage.xaml.cs から適切なオーバーライドされたメソッドに実装を移動するだけです。例を挙げると、InitializeVideoRecorder メソッドは最初は図 3のような状態になります。

図 3 InitializeVideoRecorder メソッド

public override void InitializeVideoRecorder()
 {
   if (captureSource == null)
   {
     captureSource = new CaptureSource();
     fileSink = new FileSink();
     videoCaptureDevice =
        CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
     captureSource.CaptureFailed +=
     new EventHandler(OnCaptureFailed);
     if (videoCaptureDevice != null)
     {
       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.");
     }
   }
 }

この記事では、図 3 のコードを 1 行ずつは説明しません。完全な説明については別のドキュメント (https://msdn.microsoft.com/ja-jp/library/windowsphone/develop/hh394041(v=vs.105).aspx) を参照してください。ただし、要約すると、コードでは VideoCaptureDevice のインスタンスを初期化して、UI にビデオ プレビューを設定しています。分離コードを具体的な実装にコピーして貼り付けただけなので、いくつか問題が生じています。コードでは、UI 要素や UI メソッド (たとえば、viewfinderRectangle と UpdateUI メソッド) を参照しています。このような参照は、具体的な実装に含める必要ががありません。また、既にビュー モデルを導入していればもっと簡単に取り除けます。そのため、ここでちょっとした除去作業が必要になります。

  1. UI コードを対応する UI メソッド (この場合は、MainPage.xaml.cs ファイルの InitializeVideoRecorder) に戻します。
  2. VideoRecorderBrush を初期化する新しい抽象メソッドを作成します。これは、このクラスが Windows Phone 7.5 と Windows Phone 8 に共通して必要になるためです (ただし、実装は異なる場合があります)。

この除去作業をいくらか実行すると、メソッドは次のようになります。

public override void InitializeVideoRecorder()
 {
   if (_captureSource == null)
   {
     _captureSource = new CaptureSource();
     _fileSink = new FileSink();
     _videoCaptureDevice = 
       CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
     _captureSource.CaptureFailed +=
        new EventHandler(OnCaptureFailed);
    }
 }

これで、Windows Phone 7.5 と Windows Phone 8 の両バージョンにまたがって機能する Windows Phone の分離コード (MainPage.xaml.cs) の利点を活用できるようになりました (図 4 参照)。

図 4 Windows Phone 7.5 と Windows Phone 8 で機能する分離コード

public void InitializeVideoRecorder()
 {
   _videoRecorder.InitializeVideoRecorder();
   _videoRecorder.CaptureFailed += OnCaptureFailed;
   if (_videoRecorder.VideoCaptureDevice != null)
   {
     videoRecorderBrush = new VideoBrush();
     videoRecorderBrush.SetSource(
       _videoRecorder.VideoSource as CaptureSource);
     viewfinderRectangle.Fill = videoRecorderBrush;
     _videoRecorder.StartVideoSource();
     UpdateUI(ButtonState.Initialized,
       "Tap record to start recording...");
   }
   else
   {
     UpdateUI(ButtonState.CameraNotSupported,
        "A camera is not supported on this device.");
   }
 }

後は VideoRecorder のインスタンスを初期化するだけで、新しいプラットフォーム固有の実装の使用を開始できます。

_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();

いくつか簡単に変更すれば、残りのメソッドも同様の方法でリファクタリングできます。

新しい Windows Phone 8 API を対象にする

既にまとめたように、Windows Phone 7.5 のコードを Windows Phone 8 に移植するのは非常に簡単です。必要な作業は、プロジェクトを開いて [Windows Phone 8.0 にアップグレード] を選択するだけです (図 5参照)。しかし、両方のバージョンを維持する必要がある場合はどうでしょうか。先ほど実行した手順のおかげで、コードの再利用を簡単にサポートできる方法でコードが構造化されています。それだけでなく、ビデオ レコーダーを分離したので、Windows Phone 8 でビデオカメラをキャプチャするための新しくより強力な WinRT API を使用できます。


図 5 Windows Phone 8 ランタイムを対象にするためのプロジェクトのアップグレード

次の手順では、Windows Phone 8 プロジェクトを作成し、できるだけ多くのコードを再利用して、必要な新しい機能を導入します。

この時点では、Windows Phone 8 バージョンを作成する方法は 2 つあります。つまり、既存のプロジェクトのコピーを作成してアップグレードする方法と、新しい Windows Phone 8 プロジェクトをゼロから作成して、重複するコード ファイルと参照を追加する方法です。選択基準は、プロジェクトの規模と、再利用が必要なコードの量です。この例では、プロジェクトのコピーを作成してアップグレードします。そのため、再利用する必要がある部分だけを取り出すことになり、重要なファイルを失う危険性が軽減されます。必要な作業は、次のとおりです。

  • sdkVideoRecorderCS プロジェクトをコピーし、sdkVideoRecorderCS.csproj の名前を sdkVideoRecorderCS.WP8.csproj に変更します。次に、両方のバージョンを維持できるよう、新しいプロジェクトを既存のソリューションに追加します。
  • 新しいプロジェクトを Windows Phone 8 にアップグレードします。アップグレードしたプロジェクトは、それ以上変更を加えなくても Windows Phone 8 で問題なく実行されます。これは、API の下位互換性による動作です。この動作には、コードを変更する必要がないという長所がありますが、パフォーマンスが低く、そのプラットフォームの最新かつ最高の機能を使用できないという短所もあります。
  • これで、再利用対象の要素を選択できるようになりました。たとえば、MainPage.xaml と MainPage.xaml.cs は (少なくともこのソリューションの場合) バージョンが変わっても大部分は同じままなので、Windows Phone 8 プロジェクトでこれらのファイルの既存バージョンを削除し、Windows Phone 7.5 プロジェクトのバージョンへのファイル リンクを追加します。そのためには、プロジェクトを右クリックして [既存項目の追加] を選択し、[リンクとして追加] を選択しながら MainPage.xaml ファイルを指定します (図 6 参照)。


図 6 ファイル リンクの追加

注: ここで説明したメニューを操作する代わりに、Ctrlキーと Shiftキーを押しながら、あるプロジェクトから目的のプロジェクトに MainPage.xaml ファイルをドラッグするだけでもかまいません。これによりファイル リンクが作成され、分離コード ファイルも自動的に移動されます。

関連するすべてのファイルについて同じ手順を繰り返す必要がありますが、説明のために、ここではこのファイル リンクだけを追加して再利用することにします。

少し本題から外れます。コードの再利用に関してはしばしば理想論が語られますが、実際の世界では、プラットフォーム間で同じコードを使用する必要があり、しかも 1 つか 2 つの小さな違いしかない場合があります。こうした状況では、2 つのコード セットを維持するのは現実的ではない可能性がありますが、プラットフォーム固有のコードを抽象化するのは行き過ぎです。このようなシナリオでは、多くの場合、特定のプラットフォームにおける特定のコード行を対象とするプリプロセッサ ディレクティブを使う方が適切です。この手法の例は後で示しますが、この手法を濫用するとすぐにコードが複雑になるので注意してください。

プラットフォームの抽象化を使うタイミングの良い例として、プラットフォーム固有の機能が分離している場合が挙げられます。今回の例では、これには実際のビデオ レコーダーのロジックが該当します。現在、Windows Phone 8 プロジェクトにはまったく問題なく機能する WP7VideoRecorder.cs ファイルがありますが、新しい Windows Phone 8 API を使用していないため、このファイルを変更する必要があります。Windows Phone 8 プロジェクトから WP7VideoRecorder.cs ファイルを削除し、同じく VideoRecorderCore を継承する WP8VideoRecorder.cs という名前の新しいファイルを作成します。

先ほどと同じように、各メソッドを実装します。ここでの主な違いは、System.Windows.Media 名前空間ではなく Windows.Phone.Media.Capture 名前空間を使用することです。Windows.Phone.Media.Capture 名前空間は新しい WinRT の名前空間で、AudioVideoCaptureDevice という名前のクラスが導入されます。これは前に使用した VideoCaptureDevice クラスと同様の目的を果たしますが、機能はずっと優れています。

WinRT API では、以前の API とは異なる手法をいくつか採用しています。今回の例に見られるこうした変更の 1 つは、多くの API が非同期であることです (後で詳しく説明します)。もう 1 つの違いは、なじみ深い System.IO.IsolatedFileStream 名前空間ではなく Windows.Storage 名前空間を使用してストレージを処理することです (ただし、メディア プレイバックのサポートには引き続き System.IO.IsolatedFileStream を使用します)。

ここからは、比較的大きな変更点を説明することでビデオ レコーダーのシナリオの違いを示し、コードの再利用の要素を維持しながらこのような違いを解決するためのより汎用的な手法を紹介します。

Windows Phone 7.5 バージョンのビデオ レコーダー コードでは、いくつかのプライベート変数が定義されていました。

private CaptureSource _captureSource;
 private VideoCaptureDevice _videoCaptureDevice;
 private IsolatedStorageFileStream _isoVideoFile;
 private FileSink _fileSink;
 private string _isoVideoFileName = "CameraMovie.mp4";

2 つのコードベースをできる限り類似した状態に保つことが理想的ですが、新しい API では CaptureSource や FileSink は必要ありません。CaptureSource は、ソースとして機能する実際の VideoCaptureDevice のインスタンスと置き換えられています。また、特に指定しなくても FileSink 機能を利用できるので、保存に必要なのは StorageFile だけです。

private AudioVideoCaptureDevice _videoCaptureDevice;
 private IsolatedStorageFileStream _isoVideoFile;
 private StorageFile sfVideoFile;
 private string _isoVideoFileName = "CameraMovie.mp4";

次に必要な手順は、メソッドの具体的な実装に取り組むことです。再び、InitializeVideoRecorder から説明を始めましょう。以前のバージョンでのこのメソッドの機能については既に説明したので、ここでは同様の方法で AudioVideoCaptureDevice のインスタンスを初期化する必要があるだけです。

public async override void InitializeVideoRecorder()
 {
   CameraSensorLocation location = CameraSensorLocation.Back;
   var captureResolutions =
      AudioVideoCaptureDevice.GetAvailableCaptureResolutions(location);
   _videoCaptureDevice =
      await AudioVideoCaptureDevice.OpenAsync(location, 
        captureResolutions[0]);
   _videoCaptureDevice.RecordingFailed += OnCaptureFailed;
 }

ご覧のように、構文は異なりますが、コードは同じ目的を果たします。このコードでは、キャプチャの解像度や使用するカメラ (複数のカメラを使用できる場合) を指定するなど、その他のカメラ プロパティも構成できます。携帯電話のカメラでサポートされていれば完全な 1,080 ピクセルの高解像度を利用できるので、このコードで解像度を制御できることだけでも、Windows Phone 7.5 API よりも優れています。多くの WinRT API と同様に、もう 1 つの歓迎すべき違いはメソッドが非同期なことです。管理しやすいよう、C# 5 の async キーワードと await キーワードを導入できます。これらの機能についてはこの記事以外の資料で取り上げているのでここでは詳細に説明しませんが、これらの機能は複雑さを軽減しつつ非同期コードを活用するのに役立ちます。

次に説明するメソッドは InitializeFileSink メソッドです。FileSink 自体はもう存在しませんが、ビデオ キャプチャを格納しているファイルを初期化する必要があります。このメソッドは、StorageFolder や StorageFile などのクラスが含まれる Windows.Storage 名前空間の使用を開始する場所でもあります。

public async override void InitializeFileSink()
 {
   StorageFolder isoStore = ApplicationData.Current.LocalFolder;
   sfVideoFile = await isoStore.CreateFileAsync(
     _isoVideoFileName, 
     CreationCollisionOption.ReplaceExisting);
 }

説明が必要な特に大きい違いはこれで全部なので、すべてのメソッドを説明する必要はありません。両方のバージョンでメソッドが同じままの場合は、そのメソッドを VideoRecorderCore 基本クラスに移動してサブクラス間で共通の動作を保証できます。

残りの変更を加えると、次の行に関するビルド エラーが発生することがわかります。

_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();

この原因は非常に明白で、もはや存在しない WP7VideoRecorder クラスを Windows Phone 8 コードで参照しているためです。当然、新しい WP8VideoRecorder クラスを参照する必要がありますが、MainPage.xaml.cs ファイルにこの変更を加えると、Windows Phone 7.5 のコードも同じファイルにリンクしているので機能しなくなります。

ここでいくつかの選択肢があります。たとえば、プリプロセッサ ディレクティブを次のように使用できます。

#if WP7
   _videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();
 #else
   _videoRecorder = VideoRecorderCore.Instance = new WP8VideoRecorder();
 #endif

プリプロセッサ ディレクティブを使用する場合は、プリプロセッサ ディレクティブをラッパー クラスの内部に配置すると簡潔になります。たとえば、CreateVideoRecorder メソッドを VideoRecorderCore 抽象基本クラスに導入し、上記の #if 宣言をこのクラス内に配置します。

この記事では、プリプロセッサ ディレクティブを使用せず、具体的なクラス参照を別のファクトリ クラスに抽象化する方法を採用することにしました。つまり、Windows Phone 7.5 と Windows Phone 8 の間で、レコーダーを使用するコードを統一しています。

_videoRecorder =
   VideoRecorderCore.Instance = VideoRecorderFactory.CreateVideoRecorder();

別の方法としては、依存関係の挿入 (DI) パターンまたはサービス ロケーター パターンを使用し、引き続きインスタンスをそれぞれコンテナーまたはサービスで処理することもできます。

よりわかりにくい問題として、カメラ プレビューの VideoBrush を設定する問題があります。この場合も、実装の細部を変更次のようなプリプロセッサ #if ディレクティブの手法を検討できます。

#if WP7
   videoRecorderBrush.SetSource(_videoRecorder.VideoSource as CaptureSource);
 #else
   videoRecorderBrush.SetSource(_videoRecorder.VideoSource);
 #endif

この手法が必要なのは、Windows Phone 8 では、SetSource メソッドをオーバーロードすることでオブジェクトを Source オブジェクトとして指定できるようにしているためです。これが、Windows Phone 8 の VideoSource プロパティで AudioVideoCaptureDevice のインスタンスを返せる理由です。この手法をサポートするには、"WP7" という新しい条件付きコンパイル シンボルを [プロジェクトのプロパティ] の [ビルド] で Windows Phone 7.5 プロジェクトに追加する必要があります (図 7 参照)。


図 7 Windows Phone 7.5 プロジェクトへのカスタムの条件付きコンパイル シンボルの追加

既に述べたように、Windows ランタイムはアプリの応答性を向上するために非同期メソッドに大きく依存していますが、プラットフォームのバージョン間でコードを共有するとどのような影響があるでしょうか。Windows Phone 7.5 を対象にするプロジェクトでは C# 5 コードを作成できますが、async と await のキーワードを使用する場合は一部の実装の詳細を利用できません。次のメソッドを考えてみます。

public async override void InitializeVideoRecorder()
 {
   CameraSensorLocation location = CameraSensorLocation.Back;
   var captureResolutions =
      AudioVideoCaptureDevice.GetAvailableCaptureResolutions(location);
   _videoCaptureDevice =
      await AudioVideoCaptureDevice.OpenAsync(location, 
        captureResolutions[0]);
   _videoCaptureDevice.RecordingFailed += OnCaptureFailed;
 }

このメソッドでは VideoCaptureDevice プロパティを効果的に設定しますが、このメソッドを使用するコードは次のようになります。

_videoRecorder.InitializeVideoRecorder();
 if (_videoRecorder.VideoCaptureDevice != null)
 {
   ...
 }
 else
 {
   UpdateUI(ButtonState.CameraNotSupported,
      "A camera is not supported on this device.");
 }

わかりにくいかもしれませんが、AudioVideoCaptureDevice.OpenAsync メソッドの呼び出しを待機する間に、_videoRecorder.VideoCaptureDevice のチェックが既に発生している可能性があります。言い換えると、VideoCaptureDevice チェックは、1 回も初期化されていないために null に評価される可能性があります。

ここでも、いくつかの選択肢があります。使用するコードを非同期にするとコードが少し改善しますが、ソリューションによっては多大な労力が必要になる場合があります。これを回避する場合は、単に非同期コードを同期ラッパー メソッドでラップします。また、初期化が完了したときにコンシューマーに通知するための、イベントベースのメカニズムを設定する方法もあります。

この機会を利用して新しい非同期モデルに移行した結果 InitializeVideoRecorder の呼び出しを待機する場合も考えられますが、Windows Phone 7.5 SDK が必要なコンストラクトをサポートしていない場合は、どうすれば実現できるでしょうか。解決策の 1 つは、Visual Studio 内で "Async for .NET Framework 4, Silverlight 4 and 5, and Windows Phone 7.5" NuGet パッケージをダウンロードすることです (図 8 参照)。


図 8 「microsoft.bcl.async」を検索して見つかった Windows Phone 7.5 用の Async NuGet パッケージ

この記事の執筆時点では、このパッケージはまだプレリリース版でしたが、近日中に安定したリリースが公開される見込みです。もっと実績がある方法を採用する場合は、先ほど説明したようにコードに手を加えて、イベント パターンか同期ラッパーを導入する必要があります。

注: NuGet パッケージ マネージャーのバージョン 2.1 以上がインストールされていることを確認してください。インストールされていない場合、このパッケージを使用しようとすると予期しないエラー メッセージが表示されます。最新のバージョンは、bit.ly/dUeqlu(英語) からダウンロードできます。

このパッケージをダウンロードしたら、各プロジェクトに合わせてコードを編集できます。たとえば、void ではなく Task 型を返すと、抽象基本クラスに非同期サポートを組み込むことができます。この結果、現在の "呼び出し以外の処理が不要な" メカニズムを使用する代わりに、結果を待機できるようになります。また、"Async" を非同期メソッド名に追加することもお勧めします。このようにすると、開発者にとってメソッドの使用がより直感的になります。たとえば、現在のところ一部のメソッドに次のようなシグネチャがあるとします。

public abstract Task InitializeVideoRecorderAsync();
 public abstract Task InitializeFileSinkAsync();

当然ながら、コードをサブクラスにリファクタリングする必要があります。次に例を示します。

public override async Task InitializeVideoRecorderAsync()
 {

最後に、MainPage.xaml.cs コードを更新して、非同期操作を待機する機能をサポートできます。たとえば、次のようになります。

public async void InitializeVideoRecorder()
 {
   await _videoRecorder.InitializeVideoRecorderAsync();

これらの最後の変更を加えると、Windows Phone 7.5 と Windows Phone 8 の両方のプロジェクトを正常に実行できるようになります。また、これらのプロジェクトにはコード再利用の強力な要素を備えており、サポートされている場合は最新の機能を使用するようになりす。これで、ベスト オブ ブリード ソリューションの完成です。

Windows 8 について

私が協力しているパートナーの多くは、XAML と C# を使用して Windows Phone アプリを開発したことがあります。Windows 8 用の Windows ストア アプリの開発を検討する際、最初にパートナーが抱く技術的な問いの 1 つは、HTML5/JavaScript 方式と XAML/C# 方式のどちらを使用すればよいかという問いです。Windows Phone ソリューションが適切に設計されていれば、大量のコードを Windows 8 で再利用できるので、これが Windows ストア アプリ開発に使用する言語を選択する際の決定的要因になります。

Windows Phone 7.5 と Windows 8 の間ではコードを再利用できますが、それぞれに異なるコードを用意する必要があるシナリオも多数あります。ビデオ レコーダーもそうしたシナリオの 1 つですが、さいわい、この記事で説明してきた手法を実施すれば時間と労力を大幅に節約できます。

このコードの Windows 8 バージョンの実装については読者の皆さんに課題として残しておきますが、必要に応じて、説明したすべてのコードと Windows 8 ソリューションが含まれている付属のサンプルをダウンロードすることもできます。

注: Windows 8 の実装には、Windows.Media.Capture.MediaCapture クラスを使用する必要があります。

MVVM で強化する

以上の実装の結果、2 つのバージョンの Windows Phone 用の MainPage.xaml でコードを完全に再利用できるようになりました。抽象化作業とリファクタリング作業を実行すれば、付属の分離コード ファイルでもほぼ完全に再利用できるようになります。MVVM パターンを導入すると、これらの手法をさらに強化できます。しかも、Windows Phone のバージョンだけでなく Windows 8 コードベースでも、大幅な再利用を実現できます。

これらの手法を使用すると再利用を促進できると同時に、この記事で説明したビデオ キャプチャだけでなく、アプリケーション ライフサイクル管理 (ALM)、ファイル ストレージなど、多数のプラットフォーム固有の機能を抽象化できます。

Chris Barker は、英国のダービシャーに拠点を置くマイクロソフトのテクニカル エバンジェリストです。彼は長年にわたって、独立系ソフトウェア ベンダーとエンタープライズ組織の両方のためにコードを開発し、パフォーマンスとスケーラビリティに関するラボを運営し、低レベル デバッグ手法に熱心に取り組んできました。近年は XAML と C# を使用したクライアント開発に重点を当てており、現在は世界中のコンシューマー パートナーと共に Windows 8 および Windows Phone プラットフォームの開発に取り組んでいます。

この記事のレビューに協力してくれた技術スタッフの Daniel Plaisted (マイクロソフト) に心より感謝いたします。
Daniel Plaisted は、2008 年にマイクロソフトに入社して以来、Managed Extensibility Framework (MEF)、ポータブル クラス ライブラリ (PCL)、および Windows ストア アプリ用 Microsoft .NET Framework に取り組んできました。彼は、MS TechEd、BUILD など、さまざまなローカル グループ、コード キャンプ、および会議で発表してきました。余暇には、コンピューター ゲーム、読書、ハイキング、ジャグリング、およびフットバッグ (ハッキーサック) を楽しんでいます。彼のブログは、(blogs.msdn.com/b/dsplaisted/、英語) です。