Silverlight 4 で Webカメラキャプチャ & JPEG としてローカル保存

Silverlight 4 では、DirectShow でやろうとするとそれなりに面倒な、Webカメラが対応している解像度、フレームレートの一覧が VideoCaptureDevice.SupportedFormats であっさり取れてしまいます。.NET Framework 本家にも実装してほしいですね。

Silverlight 4 の標準ライブラリでは、JPEG 画像が生成できないので、Silverlight 界隈では、ほぼデファクトスタンダードとなっている FJCoreJPEG 画像を生成しています。

利用方法は以下のような感じ。

// 保存ボタンが押されたら、保存ダイアログを開き、
// WriteableBitmap を Jpeg に変換して保存
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
    bool? dialogResult = this.saveDialog.ShowDialog();

    if (dialogResult == true)
    {
        // WriteableBitmap を JPEGに変換して保存
        byte[] fileBytes = CreateImageAsJpeg(_bmp, 80);

        // ユーザーのボタン操作によってファイル保存処理を
        // 行わないとセキュリティ例外
        using (Stream fs = (Stream)this.saveDialog.OpenFile())
        {
            // ユーザーが指定したファイル名で保存
            fs.Write(fileBytes, 0, fileBytes.Length);
            fs.Close();
        }
    }
}

CreateImageAsJpeg の中身はこのような感じ。http://blogs.msdn.com/b/davrous/archive/2009/12/18/silverlight-4-tutorial-adding-avatar-support-to-the-wcf-ria-services-business-template.aspx のコードを若干修正。

/// <summary>
/// ビットマップをJpegのバイト列へ変換します
/// </summary>
/// <param name="bitmap">変換元画像</param>
/// <param name="quality">圧縮品質(0:最高圧縮率〜100:最高品質)</param>
/// <returns>Jpegエンコードされたバイト列</returns>
private byte[] CreateImageAsJpeg(WriteableBitmap bitmap, int quality)
{
    int width = bitmap.PixelWidth;
    int height = bitmap.PixelHeight;
    int bands = 3;
    byte[][,] raster = new byte[bands][,];

    for (int i = 0; i < bands; i++)
    {
        raster[i] = new byte[width, height];
    }

    for (int row = 0; row < height; row++)
    {
        for (int column = 0; column < width; column++)
        {
            int pixel = bitmap.Pixels[width * row + column];
            raster[0][column, row] = (byte)(pixel >> 16);
            raster[1][column, row] = (byte)(pixel >> 8);
            raster[2][column, row] = (byte)pixel;
        }
    }

    ColorModel model = new ColorModel { colorspace = ColorSpace.RGB };
    FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);

    // JPEG形式でエンコード
    MemoryStream stream = new MemoryStream();
    FluxJpeg.Core.Encoder.JpegEncoder encoder
        = new FluxJpeg.Core.Encoder.JpegEncoder(img, quality, stream);
    encoder.Encode();

    // MemoryStream の先頭に移動
    stream.Seek(0, SeekOrigin.Begin);

    // MemoryStream の内容を配列にコピー
    byte[] binaryData = new Byte[stream.Length];
    long bytesRead = stream.Read(binaryData, 0, (int)stream.Length);
    return binaryData;
}

画像の生成そのものは、クライアントPC上のみで行っているので、サーバ側は静的なファイルを置くだけでOKです。
ただ、セキュリティの理由で、保存用ダイアログに、デフォルトの保存ファイル名を指定できないようです。