ActionScript-C#間でデータのやり取りをおこなってみた 後編

chakemiです。今日から3連休でnoto師匠のもと、webアプリケーション開発の手解きを 受けております。 本日は前回の続きで、いよいよC#側から外部APIを叩いてみます。

ExternalAPIを使用したActiveXコンテナとの通信について調べてみますと、 FlashPlayerは、メッセージをあるXMLフォーマットでアプリケーションに送信し、 コンテナアプリケーションからの関数呼び出しと戻り値に同じXMLフォーマットを使用する必要があるとのこと。

例 : ActiveX コンテナに対する External API の使用

ちょっと試しに触れてみようと思って手を出しましたが、この時点で面倒になってしまって どうしたもんかと考えたところ、上記のページのサンプルをお借りすることにしました。

上記のページ内のサンプルファイルへのリンクは現在、リンク切れとなっていましたので http://www.adobe.com/devnet/flash/samples.htmlのProgramming_ActionScript3.0_samples.zipから入手しました。

サンプルソースを見たところ、DEBUGフォルダの中の「ExternalInterfaceProxy.dll」を そのまま使っちゃおうかと。

ソリューションエクスプローラーの「参照設定」から「参照の追加」を選択し 参照タブを開き、「ExternalInterfaceProxy.dll」を追加しました。

サンプルのソースを参考にExternalInterfaceProxyクラスライブラリを利用する為、名前空間を追加します。

1
2
//追加
using Flash.External;

ExternalInterfaceProxyインスタンスを宣言し、参照をユーザーインターフェイスの Shockwave Flash ActiveX コントロールに渡します

1
2
3
4
5
6
7
8
9
10
11
12
13
public partial class Form1 : Form
{
    private ExternalInterfaceProxy proxy;

    public Form1()
    {
        InitializeComponent();

        //Flashの読み込み
        String swfPath = Directory.GetCurrentDirectory() + .DirectorySeparatorChar + "Cam.swf";
        axShockwaveFlash1.LoadMovie(0, swfPath);
        proxy = new ExternalInterfaceProxy(axShockwaveFlash1);
    }

あとは適当にボタンを配置し、クリック時にExternalInterfaceProxyクラスのCallメソッドから ActionScriptの関数を呼び出します。

Callメソッドの第一引数は、呼び出すActionScript側の関数名 以降は、ActionScript関数へ渡す引数となります。

1
2
3
4
5
6
7
8
9
10
11
12
13
private void button1_Click(object sender, EventArgs e)
{
~省略~

    double[,] byteArray = new double[320, 240];

    for (int x = 0; x < 320; x++)
        {
            for (int y = 0; y < 240; y++)
            {

        byteArray[x,y] = (double)proxy.Call("capture", x, y);
~省略~

で、ぐるぐるとカメラのサイズ分のカラー情報を取ってきます。

ExternalInterfaceSerializer.csファイルの中身を確認し、 Callメソッドから返される値の型がどうなっているのか確認したところ、 ActionScriptへnumber型で渡し、返された値をdouble型としてC#へ返しているようだったので、 キャストして受け取ります。

ここで先日も書きましたが、ActionScript側でgetPixels()メソッドを使ってバイト配列で 取得したかったのですが、ActionScriptの多次元配列がAllayListクラス型で返されてきて スキルのない自分には、どうにも扱えなくて、というかメンドクサクなって断念してしまいました。。。 だからbyteArrayってのはその名残です。。。

気を取り直して拾ってきたカラー情報をビットシフトしてRGBの値を取り出し BitmapクラスのSetPixelメソッドでピクセルデータを並べ、bitmapファイルに保存します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int[,] r= new int[320, 240];
int[,] g = new int[320, 240];
int[,] b = new int[320, 240];
~省略~
    }
}
for (int x = 0; x < 320; x++)
{
    for (int y = 0; y < 240; y++)
  {
    r[x, y]=((int)byteArray[x, y]>>16&0xff);
        g[x, y]=((int)byteArray[x, y]>>8&0xff);
        b[x, y]=((int)byteArray[x, y]&0xff);
        bitmap.SetPixel(x, y,
        Color.FromArgb(
        r[x, y],
        g[x, y],
        b[x, y]
        ));
    }
}
bitmap.Save("outbmp.bmp");

これで見事、画像キャプチャに成功しました!!

ソース全体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//追加
using Flash.External;
using System.Drawing.Imaging;
namespace FlashVideo
{
    public partial class Form1 : Form
    {
        private ExternalInterfaceProxy proxy;
        public Form1()
        {
            InitializeComponent();
            //Flashの読み込み
            String swfPath = Directory.GetCurrentDirectory() + .DirectorySeparatorChar + "Cam.swf";
            axShockwaveFlash1.LoadMovie(0, swfPath);
            proxy = new ExternalInterfaceProxy(axShockwaveFlash1);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Bitmap bitmap = new Bitmap(320, 240, PixelFormat.Format24bppRgb);
            double[,] byteArray = new double[320, 240];
            int[,] r= new int[320, 240];
            int[,] g = new int[320, 240];
            int[,] b = new int[320, 240];
            for (int x = 0; x < 320; x++)
            {
                for (int y = 0; y < 240; y++)
                {


                byteArray[x,y] = (double)proxy.Call("capture", x, y);

                }
            }
            for (int x = 0; x < 320; x++)
            {
                for (int y = 0; y < 240; y++)
        {
          r[x, y]=((int)byteArray[x, y]>>16&0xff);
              g[x, y]=((int)byteArray[x, y]>>8&0xff);
              b[x, y]=((int)byteArray[x, y]&0xff);
                    bitmap.SetPixel(x, y,
                    Color.FromArgb(
                    r[x, y],
                    g[x, y],
                    b[x, y]
                    ));
                }
            }
      //↓とりあえず処理が終わったらわかるように
      textBox1.Text = "Complete";
            bitmap.Save("outbmp.bmp");
        }
    }
}

ついでにFormデザインはこんな感じで

もともとSetPixelメソッドは処理が遅いとか聞きましたが、 ActionScript側から1ピクセルづつ取ってきてるんで取得するのに30秒位かかって thread処理もしてないし、とてもじゃないがこのままでは使い物になりませんが、 とりあえずActionScript-C#間でデータを扱う足掛かりは出来たかなというとこで 一旦終了としようと思います。

時間があったらもっとちゃんとしたものにしたいんだけど 画像取得ならDirectshowとか利用したほうが自然なんで これはもういいかな。。。

参考

RGBのビットシフト(ここはC++ですが) http://atocmoc.com/wordpress/?p=10

Comments