次の方法で共有


この記事は機械翻訳されたものです。

UI の最前線

Windows Phone 7 のビデオ フィード (機械翻訳)

Charles Petzold

コード サンプルをダウンロードします。

Charles Petzold現代のスマート フォンは、外の世界についての情報を取得できます電子の感覚器官に満載されています。 これらには、携帯ラジオ自体、Wi-Fi、GPS、タッチ スクリーン、モーション検知器などが含まれます。

アプリケーション プログラマには、これらの感覚器官は Api に関連付けられていると便利です。 Api が不十分な場合は、ハードウェア機能ほど貴重な価値もないなったりします。

アプリケーション プログラマの観点からは、Windows Phone の最初のリリースから行方不明の特徴の 1 つは良い目玉だった。 カメラは常に Windows Phone にされているが、唯一の API、元のリリースで利用可能なカメラだった­CaptureTask。 このクラスは本質的に写真を撮り、ユーザーことができますし、画像をアプリケーションに返します、子プロセスを生成します。 それはあります。 アプリケーションこのプロセスの任意の部分を制御することはできませんも、レンズを通して、ライブ ビデオ フィードを取得することができます。

その欠乏今プログラミング インターフェイスの 2 つのセットで修正されています。

1 つの Api セット、カメラおよび PhotoCamera クラスを懸念します。 これらのクラスは、フラッシュのオプションを含む、写真撮影の UI 全体をアセンブルすると、アプリケーションできます; フィードのライブ プレビュー ビデオ; キーを押すと半分機シャッター; 検出の焦点を当てます。 このインターフェイスは今後のコラムで説明したいです。

このコラムで私は論議する Api は、Silverlight 4 ウェブカメラ インターフェイスから継承されました。 彼らは携帯電話のカメラおよびマイクロ フォンからライブ ビデオとオーディオのフィードを取得、アプリケーションことができます。 これらのフィードをファイルに保存、ユーザーに提示することができますまたは — とここで、もっとおもしろく — 操作またはいくつかの方法で解釈します。

デバイスとソース

Silverlight 4 の webcam インターフェイスは少し Windows Phone 7 を強化され、System.Windows.Media 名前空間で定義された約 1 ダースのクラスで構成されています。 常に、静的な CaptureDeviceConfiguration クラスを開始します。 複数のカメラやマイク、携帯電話をサポートする場合、これらを得るからご利用いただけます­AvailableVideoCaptureDevices と GetAvailableAudioCapture­デバイスのメソッド。 選択リストでこれらのユーザーが存在する可能性があります。 また、単に、GetDefaultVideoCaptureDevice および GetDefaultAudioCaptureDevice メソッドを呼び出すことができます。

ドキュメントの言及、これらのメソッドは null を返します可能性がありますおそらく、携帯電話を示すカメラが含まれていません。 これはそうではありませんが、とにかくの null をチェックすることをお勧めします。

これらの CaptureDeviceConfiguration メソッドは、VideoCaptureDevice、AudioCaptureDevice、またはこれら 2 つのクラスのインスタンスのコレクションのインスタンスを返します。 これらのクラスは、フレンドリ名、デバイス、SupportedFormats コレクションおよび DesiredFormat プロパティを提供します。 ビデオの形式は各フレームのビデオ、色の形式、1 秒あたりのフレームのピクセル寸法を含みます。 オーディオの形式チャネル、サンプルとパルス コード変調 (PCM) は常に、wave 形式のビットの数を指定します。

Silverlight 4 アプリケーション、CaptureDevice を呼び出す必要があります­ウェブカムにアクセスするには、ユーザーからアクセス許可を取得するには、Configuration.RequestDeviceAccess メソッド。 ボタンをクリックするなど、ユーザー入力への応答でこの呼び出しが必要があります。 場合は、CaptureDevice­configur 含んでいる­­アクション。AllowedDeviceAccess プロパティただし、true の場合、ユーザーは既にこのアクセス許可を与えているし、プログラムが RequestDeviceAccess を再度呼び出す必要はありません。

明らかに、RequestDeviceAccess メソッドは、ユーザーのプライバシーを保護するために提供しています。 しかし、Web ベースの Silverlight および Silverlight の Windows Phone 7 この点では少し異なるようです。 ひそかにあなたのウェブカメラをアクセスする Web サイトのアイデアは明らかに、不気味なものが、はるかに少ないため、電話プログラム。 それは Windows Phone のアプリケーションでは、AllowedDeviceAccess は常に true を返すこと私の経験です。 それにもかかわらず、このコラムで説明したすべてのプログラムでは、私は RequestDeviceAccess をコールするための UI を定義しました。

アプリケーションは、ビデオ デバイス、オーディオ デバイスはライブ ビデオとオーディオの単一のストリームに結合、CaptureSource オブジェクトを作成する必要もあります。 CaptureSource VideoCaptureDevice、AudioCaptureDevice、VideoCaptureDevice のインスタンスを設定し、CaptureDeviceConfiguration から AudioCaptureDevice を取得、2 つのプロパティがあります。 のみビデオまたはオーディオに興味があるなら両方のプロパティを設定必要はありません。 このコラムのサンプル プログラムでは、私は完全ビデオに焦点を当ててきました。

CaptureSource オブジェクトを作成した後、オブジェクトを呼び出すことができますを開始および停止方法。 ビデオまたはオーディオのフィードを取得するには、プログラムで、おそらく、OnNavigated で Start を呼び出してするよ­をオーバーライドし、OnNavigatedFrom のオーバーライドで停止します。

また、WriteableBitmap オブジェクトの形での個別のビデオ フレームを取得するには、CaptureSource の CaptureImageAsync メソッドを使用できます。 その機能を示すことはありません。

CaptureSource オブジェクトとは、2 つの方向のいずれかでを行くことができます: ライブ ビデオ フィードを表示するには、VideoBrush を作成するか、「シンク」のオブジェクト raw データへのアクセスを取得するまたは分離ストレージ内ファイルを保存するに CaptureSource を接続することができます。

VideoBrush

間違いなく、最も簡単な CaptureSource オプション、VideoBrush です。 MediaElement ソース、VideoBrush の Silverlight 3 の導入し、Silverlight 4 CaptureSource 代替 VideoBrush を追加します。 として任意のブラシでは、色要素の背景または前面に使用することができます。

ダウンロード可能なこのコラムのコードでは、プログラム VideoCaptureDevice、CaptureSource、VideoBrush を使用して、デフォルトのカメラ レンズを通して、ライブ ビデオ フィードを表示するには StraightVideo と呼ばれます。 図 1 MainPage.xaml ファイルの良い塊を示しています。 (これは、ビデオのフィードを必要があります) 風景モード、VideoBrush コンテンツ グリッドの背景プロパティの定義およびボタンを使用して、カメラにアクセスするユーザーのアクセス許可を取得するに注意してください。

図 1 は、MainPage.xaml ファイル StraightVideo から

<phone:PhoneApplicationPage
  x:Class="StraightVideo.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
  ...
SupportedOrientations="Landscape" Orientation="LandscapeLeft"
  shell:SystemTray.IsVisible="True">
  <Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
      <TextBlock x:Name="ApplicationTitle" Text="STRAIGHT VIDEO"
        Style="{StaticResource PhoneTextNormalStyle}"/>
    </StackPanel>
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
      <Grid.Background>
        <VideoBrush x:Name="videoBrush" />
      </Grid.Background>
      <Button Name="startButton"
        Content="start"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Click="OnStartButtonClick" />
    </Grid>
  </Grid>
</phone:PhoneApplicationPage>

図 2 多くの分離コード ファイルを示しています。 ページのコンス トラクターでは、CaptureSource オブジェクトが作成されますが、それが起動し、ナビゲーションのオーバーライドで停止します。 また、SetSource OnNavigatedTo で VideoBrush を呼び出す必要見つけた; それ以外の場合、イメージは前の停止呼び出しの後は失われました。

図 2 MainPage.xaml.cs ファイル StraightVideo から

public partial class MainPage : PhoneApplicationPage
{
  CaptureSource captureSource;
  public MainPage()
  {
    InitializeComponent();
    captureSource = new CaptureSource
    {
      VideoCaptureDevice =
        CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice()
    };
  }
  protected override void OnNavigatedTo(NavigationEventArgs args)
  {
    if (captureSource !=
      null && CaptureDeviceConfiguration.AllowedDeviceAccess)
    {
      videoBrush.SetSource(captureSource);
      captureSource.Start();
      startButton.Visibility = Visibility.Collapsed;
    }
    base.OnNavigatedTo(args);
  }
  protected override void OnNavigatedFrom(NavigationEventArgs args)
  {
    if (captureSource != null && captureSource.State == CaptureState.Started)
    {
      captureSource.Stop();
      startButton.Visibility = Visibility.Visible;
    }
    base.OnNavigatedFrom(args);
  }
  void OnStartButtonClick(object sender, RoutedEventArgs args)
  {
    if (captureSource != null &&
        (CaptureDeviceConfiguration.AllowedDeviceAccess ||
        CaptureDeviceConfiguration.RequestDeviceAccess())
    {
      videoBrush.SetSource(captureSource);
      captureSource.Start();
      startButton.Visibility = Visibility.Collapsed;
    }
  }
}

Windows Phone エミュレーターでは、このプログラムを実行することができますが、実際のデバイスではるかに興味深い。 ビデオのフィードのレンダリングが非常に敏感であることがわかります。 明らかに、ビデオのフィード、ビデオ ハードウェアに直接予定です。 (この仮定のためのより多くの証拠は WriteableBitmap に PhoneApplicationFrame オブジェクトをレンダリングすることで、携帯電話からのスクリーン ショットを取得私の習慣の方法ではこのプログラム didn't 仕事です。ビデオ経由で、このブラシをレンダリングするので、ブラシはコンテンツのグリッドの大きさに拡大され、イメージがゆがむまた気づくでしょう。

ブラシの素晴らしい機能の 1 つは複数の要素間で共有できることです。 それは FlipXYVideo の背後にある考え方です。 このプログラムは、グリッドにタイル張りの四角形オブジェクトの束を動的に作成します。 同じ VideoBrush すべて他の四角形の垂直方向または水平方向に反転を除いてまたは両方のように使用される図 3。 ApplicationBar ボタンから行と列の数を増減することができます。

図 3 FlipXYVideo VideoBrush オブジェクトの共有

void CreateRowsAndColumns()
{
  videoPanel.Children.Clear();
  videoPanel.RowDefinitions.Clear();
  videoPanel.ColumnDefinitions.Clear();
  for (int row = 0; row < numRowsCols; row++)
    videoPanel.RowDefinitions.Add(new RowDefinition
    {
      Height = new GridLength(1, GridUnitType.Star)
    });
  for (int col = 0; col < numRowsCols; col++)
    videoPanel.ColumnDefinitions.Add(new ColumnDefinition
  {
    Width = new GridLength(1, GridUnitType.Star)
  });
  for (int row = 0; row < numRowsCols; row++)
    for (int col = 0; col < numRowsCols; col++)
    {
      Rectangle rect = new Rectangle
      {
        Fill = videoBrush,
        RenderTransformOrigin = new Point(0.5, 0.5),
        RenderTransform = new ScaleTransform
        {
          ScaleX = 1 - 2 * (col % 2),
          ScaleY = 1 - 2 * (row % 2),
        },
      };
      Grid.SetRow(rect, row);
      Grid.SetColumn(rect, col);
      videoPanel.Children.Add(rect);
    }
  fewerButton.IsEnabled = numRowsCols > 1;
}

これで遊ぶの楽しいがない多くの楽しみとしては説明します、万華鏡プログラムとして、です。

ソースとシンク

VideoBrush を使用する代わり、CaptureSource オブジェクトを AudioSink、VideoSink または FileSink オブジェクトに接続しています。 これらのクラスの名前で「シンク」単語の使用「コンセント」の意味であり、電子機器やネットワークの理論で、単語の使用に似ています。 (「熱源」と「ヒートシンク。」と思う)

FileSink クラスは、アプリケーションの分離ストレージの介入なしにあなたの部分のビデオまたはオーディオ ストリームを保存するための推奨される方法です。 実際のビデオまたはオーディオ情報をリアルタイムでへのアクセスが必要な場合は、VideoSink と AudioSink を使用します。 これら 2 つのクラスは抽象です。 1 つまたは両方のこれらの抽象クラスからクラスを派生、OnCaptureStarted、OnCaptureStopped、OnFormatChange、OnSample メソッドをオーバーライドします。

VideoSink または AudioSink から派生したクラスは常に、最初に OnSample を呼び出す前に OnFormatChange への呼び出しを取得します。 OnFormatChange で指定した情報は、サンプル データがどのように解釈されるかを示します。 両方の VideoSink と AudioSink については、OnSample の呼び出しタイミング情報と、バイトの配列を提供します。 AudioSink は、これらのバイトは PCM データを表します。 VideoSink は、これらのバイトはビデオの各フレームのピクセルの行と列です。 このデータは、常に生と圧縮されていないです。

ディスパッチャー オブジェクトを UI スレッドで実行する必要がありますこれらのメソッド内のタスクに使用する必要がありますので、OnFormatChange と OnSample の両方での実行、セカンダリ スレッドと呼ばれます。

VideoSink からの派生クラスを通じてビデオのデータを付属 StraightVideoSink プログラム StraightVideo に似ています。 これは、派生クラス (表示図 4) それだけ OnSample バイト配列を取得し、WriteableBitmap に転送するために SimpleVideoSink という名前です。

図 4 StraightVideoSink で SimpleVideoSink クラスを使用

public class SimpleVideoSink : VideoSink
{
  VideoFormat videoFormat;
  WriteableBitmap writeableBitmap;
  Action<WriteableBitmap> action;
  public SimpleVideoSink(Action<WriteableBitmap> action)
  {
    this.action = action;
  }
  protected override void OnCaptureStarted() { }
  protected override void OnCaptureStopped() { }
  protected override void OnFormatChange(VideoFormat videoFormat)
  {
    this.videoFormat = videoFormat;
    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
    {
      writeableBitmap = new WriteableBitmap(videoFormat.PixelWidth,
        videoFormat.PixelHeight);
      action(writeableBitmap);
    });
  }
  protected override void OnSample(long sampleTimeInHundredNanoseconds,
    long frameDurationInHundredNanoseconds, byte[] sampleData)
  {
    if (writeableBitmap == null)
      return;
    int baseIndex = 0;
    for (int row = 0; row < writeableBitmap.PixelHeight; row++)
    {
      for (int col = 0; col < writeableBitmap.PixelWidth; col++)
      {
        int pixel = 0;
        if (videoFormat.PixelFormat == PixelFormatType.Format8bppGrayscale)
        {
          byte grayShade = sampleData[baseIndex + col];
          pixel = (int)grayShade | (grayShade << 8) |
            (grayShade << 16) | (0xFF << 24);
        }
        else
        {
          int index = baseIndex + 4 * col;
          pixel = (int)sampleData[index + 0] | (sampleData[index + 1] << 8) |
            (sampleData[index + 2] << 16) | (sampleData[index + 3] << 24);
        }
        writeableBitmap.Pixels[row * writeableBitmap.PixelWidth + col] = pixel;
      }
      baseIndex += videoFormat.Stride;
    }
    writeableBitmap.Dispatcher.BeginInvoke(() =>
      {
        writeableBitmap.Invalidate();
      });
  }
}

MainPage その WriteableBitmap イメージ要素を結果の動画のフィードを表示します。 (または、それが、ImageBrush を作成し、を背景または前景要素に設定。

ここでは、キャッチ: は WriteableBitmap 作成できません VideoSink 派生では、OnFormatChange メソッドが呼び出されるまで、呼び出し、ビデオのフレームのサイズを示すので。 (それは通常私の携帯電話上の 640 x 480 ピクセルがおそらく何か他のことがあります。)VideoSink 誘導体の WriteableBitmap 作成しますが、MainPage にアクセスする必要があります。 だからこそ私は、WriteableBitmap が作成されたときに呼び出すアクションの引数が含まれている SimpleVideoSink のコンス トラクターを定義します。

SimpleVideoSink、ディスパッチャー オブジェクトを使用して、作成 UI スレッドのキューに入れるので、WriteableBitmap プログラムの UI スレッドでは、作成する必要がありますに注意してください。 これは、最初の OnSample を呼び出す前に、WriteableBitmap を作成可能性があることを意味します。 注意 ! OnSample メソッドのセカンダリ スレッドで WriteableBitmap ピクセル配列にアクセスすることができますが、その呼び出しは、最終的に、イメージ要素で、ビットマップの表示に影響するので Invalidate ビットマップへの呼び出しが UI スレッドで必要があります。

Page クラス StraightVideoSink のアプリケーションが­色と動画のフィードを淡色表示を切り替えるには、ボタン バー。 これらはのみ 2 つのオプションと、VideoCaptureDevice オブジェクトの DesiredFormat プロパティを設定する 1 つまたは他を切り替えることができます。 色フィード ピクセルあたり 4 バイト (これは常には 255 になります)、緑、青、赤、アルファあります。 1 ピクセルあたり 1 バイトのみされているグレーの陰影フィード。 どちらの場合では、WriteableBitmap 常に 1 ピクセルあたり 4 バイト、すべてのピクセルは 32 ビットの整数、最高 8 ビット、アルファ チャネルは、赤、緑、青が続くので表されます。 CaptureSource オブジェクトを停止して形式を切り替えたときに再起動する必要があります。

StraightVideo と StraightVideoSink の両方に、ライブ ビデオ フィードを表示するが、StraightVideoSink がかなりプログラムに WriteableBitmap ビデオのフレームを転送するには、作業の結果として低迷であることに気づくでしょう。

万華鏡を作る

あなただけ時折フレーム キャプチャとリアルタイムのビデオの食事が必要な場合は、キャプチャの CaptureImageAsync メソッドを使用することができます­ソース。 パフォーマンスのオーバーヘッドのために、ピクセル ビットの操作を含むより専門的なアプリケーションに VideoSink の使用を制限することおそらくされます。

万華鏡のようなパターンにビデオフィードを配置このような「特殊な」プログラムを書いてみましょう。 概念的には、これは非常に簡単です:、VideoSink 誘導体取得ビデオ フィードおそらく 640 x 480 ピクセル サイズまたはおそらくの各フレームが他の何か。 示すように、フレームから正三角形の画像データを参照する図 5

三角形の上部と下部の最盛期より良い顔をキャプチャする決めました。

The Source Triangle for a Kaleidoscope
図 5 ソース三角形の万華鏡

その三角形の画像 [、WriteableBitmap 複数回でいくつかの回転に複製され、これを反転画像タイル張りに六角形、不連続なしで示すように分け図 6。 きれいな花のように、見える六角形を知っているが、実際には多くのイメージ私の顔の (おそらくあまりにも多くのイメージ私の顔の) です。

The Destination Bitmap for a Kaleidoscope
図 6、転送先ビットマップの万華鏡

繰り返しのパターンで示すように、個々 の三角形が区切られているときは、明白になる図 7。 電話でレンダリングされるときに、ターゲット WriteableBitmap の高さと同じ携帯電話の小さいディメンション、または 480 ピクセルになります。 各正三角形したがって 120 ピクセルの側にいます。 つまり、三角形の高さ 120 回 0.75、または約 104 ピクセルの平方根です。 プログラムで、私は 104、数学がビットマップのサイズは 105、ループを簡単にするため使用します。 全体の結果のイメージは、630 ピクセルです。

The Destination Bitmap Showing the Source Triangles
図 7 ソースの三角形を表示、コピー先のビットマップ

私はそれを 3 つ同一垂直バンド 210 ピクセルとしての全体のイメージを治療する最も便利な発見。 それらの垂直方向のバンドを垂直軸に回転対称がそれぞれは減少するので、ビットマップ イメージを 1 つの 105 x 480 ピクセル 6、半分のこれらの反射繰り返される。 バンドには、わずか 7 における三角形の 2 つの部分の三角形で構成されます。

たとえそうでも、私はこのイメージを組み立てるために必要な計算について非常に神経質だった。 その後、これらの計算は 1 秒あたり 30 回のビデオ リフレッシュ レートで実行する必要はないことを実現しました。 とき、ビデオ イメージのサイズは、OnFormatChange のオーバーライドで利用可能になると、のみ実行する必要があります。

プログラムは、Kaleidovideo と呼ばれます。 (名前の語源の醜態伝統によって「カレイド」「美しいフォーム、」を意味するギリシャ語ルートからなるが、「ビデオ」、ラテンのルートの新しい単語のコイニングとき、2 つのミックスになっていることため見なされます。)

KaleidoscopeVideoSink クラスは、VideoSink をオーバーライドします。 OnFormatChange メソッドは、indexMap と呼ばれる配列のメンバーを計算するための責任です。 配列として多くのメンバー WriteableBitmap (105 480、または 50,400 による乗算) のピクセル数としておりインデックス、ビデオ イメージの四角形の領域に格納されます。 この indexMap の配列を使用して、ピクセルのビデオ画像からへの転送、WriteableBitmap OnSample メソッドで、おそらく高速限りかなりです。

このプログラムは、たくさんの楽しみです。 世界のすべては Kaleidovideo ではなくより美しく見えます。 図 8 私の本棚のいずれかの例を示しています。 それを介してテレビをも見ることができます。

A Typical Kaleidovideo Screen
図 8 典型的な Kaleidovideo 画面

何かを保存したいのですが、画面に表示、場合だけで、「取り込み」ボタン ApplicationBar を追加。 携帯電話のフォト ライブラリの保存画像フォルダーの画像を見つけること。

Charles Petzold は長年の貢献エディターに MSDN マガジン。彼の最近の本は、プログラミング Windows Phone 7 (Microsoft Press、2010年) は無料ダウンロードとして利用可能な bit.ly/cpebookpdf

この記事のレビュー、次の技術的な専門家に感謝: Mark HopkinsMatt Stroshane