東京てらこではなしてきました。

ということで、2012.1.14 東京てらこに行ってきたので、やったことをまとめてみましたよっと。
(みんな発表慣れしててちゃんと自分も今年は意識的に経験していかないとなーと反省もできた。。)

今回はまずつくりたいコンテンツがあって、それを実現する為にやる必要のあることとしては、
flashでごにょごにょ(まだ果てしなく遠い)→ flashでつくった画像をtwitter側にアップしたい!(まず今日ココ)
というわけでこの部分を作っててらこで話してみようかなと。:-)

※ ちょっとここからすごく長い&少しマニアックな話なので、実際に作ってみたのはこれ。

Google Chrome 11 で音声認識できるので(さすがGoogleの音声認識精度!)、それを受け取ってflashで合成してそのまま画像tweet!!
※ Google Chrome 11(最新版?)とかじゃないと動かないと思います。
※ マイク入力 & Webカム入力 がないと体験できないと思います。。スイマセン
※ connectでtwitterにOAuth認証した後、上のFormのマイクアイコンを押すと音声入力できます。日本語で音声認識できると画像が自動tweetされます!注意!

Chrome最新版ならこんなんが出るはず。

そしてtweetされるとこんな感じ。なんだこれw

で、やってることとして順番に纏めるとこんな感じ。
・phpでTwitterのOAuth認証。
・FlashでWebカメラから映像取得。
・HTML5+GoogleChrome11で x-webkit-speech を使って日本語音声認識。
・JavaScript経由でFlash側に文言を渡す。
・Flash側でカメラの映像と文言を1枚の画像として合成。
・phpに画像、文言を渡してTwitterの公式画像アップローダーAPIで画像付きtweet。

という感じ。

今回はFlash表現としてたまたまWebカメラとマイク入力という再生可能環境の少ないものを選んだんですが、主な内容は(php twitter 画像 post)とか(Twitterに画像を投稿、アップロード)って検索の人に見てもらえればいいなって内容で書きたいと思います。:-)

でわでわ昨年8月頃に出たtwitterのapiを探ってみる。これ。
https://dev.twitter.com/docs/api/1/post/statuses/update_with_media

実はお仕事でこれの一覧取得の方のapiは触ったんだけど、(GETするだけ。むしろOAuth認証通してPOSTでの取得apiとかまだ用意されてないぽい。。ずっと。。orz)画像をPOST(アップロード)する方は触ってなかった。

ちなみに画像の一覧取得はTwitter OAuth認証なしで取得できるデータのめも。 の、
■自分のtweetした画像(media_timeline) ってとこに書いておきました。

まーこの中は見れば分かるんですが、公式アップローダ使ってアップされたものの配列の中だけに
data[i][“entities”][“media”] ってオブジェクトがあって、
その中の配列の1個目に[“media_url”]ってのが入ってますので、
それが画像への直リンクです。あとは取得してごにょごにょどうぞ。

で、本題の画像アップロード。

phpでOAuth用のライブラリはtmhOAuthっての使えばそれ用の関数も用意されてて一発だったんだけど、
これのためだけに今まで使ってたtwitterOAuthとtmhOAuthの2つのlibrary共存させるの気持ち悪いなぁと思ってなんとかtwitterOAuthのままPOSTできるようにいろいろ勉強してみよう!と思ったわけです。
 → 素直にtmhOAuthを使えばハマらずに済んだのにね。。

で、まずはじめにそもそも画像のアップロードってどうやるん?ってとこから。

PHP Lesson11-1 ~画像のアップロード~
http://plog.pya.jp/program/php/lesson11/sample01.html
http://plog.pya.jp/program/php/lesson11/sample02.html
この2つ読めば簡単なフォームから画像アップロード→受け取ってサーバに保存までの流れが一通りできる。
わー簡単。(・∀・)

ここでphpを使った、簡易ファイルリスト(ただの画像パスまでの羅列txt)の作成と、
任意の場所への画像溜め込みができた。
(ここDBへの保存とか登録できるようになると後々素敵なので別言語でいつかやる)
(幸いtweetreeってサイト作った時のヒゲさんのソースがサーバに残ってるので見る。盗む。)

で、twitteroauthでもpost()関数が用意されてるので、まず単純にこれをいじってみた。

通常、OAuth認証した後、POSTしてtweetする時とかだと

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
$connection->format = 'xmlとかjsonとか';
$entry = $_POST['message'];
$connection->post('apiのURL', array('status' => $entry));

なんで、最後のarray(‘status’ => $entry)を、$paramsに変えて、

$entry = $_POST['message'];
$image = './nuko.jpg';
$params = array('status' => $entry, 'media[]' => "@{$image}");
$connection->post('apiのURL', $params);

とかで行けるだろうと考えてたんですよ。

はい全然行けません。

で、ここで色々調べまくってたらなんだか
Request Headers の Content-type を multipart/form-data にする必要があると。。

はい multipart/form-data って何ですか?

一般的なPOST方式は送信データの大きさに制限はないがファイルの実際のデータをサーバーに送ることができない。
ファイルデータを送るためには特別な形式の POST 送信を使わなければならない。

ほう。どいうこと?

<form method="post" action="cgiとかphpとか" enctype="multipart/form-data">
	<input type="file" name="upfile" size="50">
	<input type="submit" value="送信ボタン">
</form>

あー、formに使われるenctype(エンコードタイプ)のことか!

今までPOSTする時とかってkeyword的な短い単語とかちょっとした文章くらいしかやったことなかったから全然知らなかったんだけど、
画像とかのファイルをPOSTしようとするとエンコードの種類を multipart/form-data にしないとダメなんね。
<input type=”file” name=”upfile” size=”50″>こいうの使うformの時とかね。
この辺りはマークアップとかでformを作る機会がある人とかなら当然のとこなんでしょね。:-)

で、TwitterOAuthのライブラリではPOST時に使うRequest HeadersのContent-typeが、
通常oAuthが使用する、
application/x-www-form-urlencoded になってるので、
    ↓
multipart/form-data に変更しないとだめ。だと。そいうことか!

で、実際にTwitterOAuthのライブラリとかではこのPOST用のheaderを別途用意してるので(どっちのライブラリもcURL使ってた)、ここのheader作成時に Content-type を multipart/form-data にしてやれば行けるはず。。!!

ということでtwitteroauth.phpの改造。 → 自分で2,3時間やってみたけど全然上手くいかず。。
そもそもcURLでのRequestHeaderの生成がよく分からねーよ!! → google先生に聞いてみたら5分で解決。。orz
twitteroauth.phpの改造

ここでやられてるのは画像POSTして背景画像変えたり、プロフィール画像変えたりするあれやね。
あぁ、これであの人が作ってたあんなサービスやこの人が作ってたこんなサービスも作り方が分かった。なるほど。:-)

で、この中身をほんのちょこっっっっっっとだけ変えて、以下。

twitteroauth.php

  /**
   * POST wrapper for oAuthRequest.		// コレ元のPost用メソッド。
   */
  function post($url, $parameters = array()) {
    $response = $this->oAuthRequest($url, 'POST', $parameters);
    if ($this->format === 'json' && $this->decode_json) {
      return json_decode($response);
    }
    return $response;
  }

  /**
   * POST IMAGE wrapper for oAuthRequest.		// 今回の画像tweetのPost用メソッド。新規。
   */
  function postImg($url, $parameters = array()) {
    $response = $this->oAuthRequestImage($url, 'POST', $parameters); // ここだけoAuthRequestImage
    if ($this->format === 'json' && $this->decode_json) {
      return json_decode($response);
    }
    return $response;
  }


  /**
   *  Image Post		// ここからは全部上の人のまま このoAuthRequestImageは新規。
   */
  function oAuthRequestImage($url, $method = NULL ,$args = array())
  {
    $req = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, array());
    $req->sign_request($this->sha1_method, $this->consumer, $this->token);
    return $this->http($req->get_normalized_http_url(), $method, $args, $req->to_header());
  }

  /**
   * Make an HTTP request
   *
   * @return API results		// ここ!ここで http header 作成時にごにょる!
   */
  function http($url, $method, $postfields = NULL, $header = '') {	// $header追加。
    $this->http_info = array();
    $ci = curl_init();
    /* Curl settings */
    curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
    curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
    curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
    curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);

//    curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:'));		// こうなってるのを、
    curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:',$header));	// 画像POST用にContent-typeとか変更!!

で、さっきやってた

$connection->post('apiのURL', $params);

を、

$connection->postImg('apiのURL', $params);

に変えてやれば、、

いけた!!!!!(´;ω;`) ブワッ
ここ通っちまえばこっちのもんよ。ってことで後は楽しいflashライフを!(・∀・)

はい、てらこはflash勉強会なのでflashやります。すいません。

こっからまずは適当に画像つくってpostしてみますかね。

以下as3の image post用関数。

/**
 * Photo POST
* postPhoto( Img:BitmapData, TweetText:String = '' ) */ public function postPhoto( $bmd:BitmapData, $str:String='' ):void { if($bmd) { var byte:ByteArray = PNGEncoder.encode( $bmd ); var base64:String = Base64.encodeByteArray( byte ); var va:URLVariables = new URLVariables(); va.access_token = _access_token; va.access_token_secret = _access_token_secret; va.message = $str; va.base64 = base64; var url:URLRequest = new URLRequest('./twitter/photo.php'); url.method = URLRequestMethod.POST; url.data = va; var loader:URLLoader = new URLLoader(); loader.dataFormat = URLLoaderDataFormat.TEXT; loader.load(url); loader.addEventListener(Event.COMPLETE, photoComp); }else{ MonsterDebugger.trace('error','not photo bitmapdata...'); } } private function photoComp(e:Event):void { MonsterDebugger.trace('post photo', e ); // data取得完了 dispatchEvent( new Event( Twitter.PHOTO_POSTCOMP )); }

BitmapDataとtweetテキストを投げるとポストしてくれて完了したら
Twitter.PHOTO_POSTCOMPってEventが返されて、
そのタイミングでアップされた写真へのURLにアクセスできるようになるお!(・∀・)

PHPがわ。

<?php

// photo と txtdata 格納場所 本番では www非公開層に置くのがいいよね。
$upload_photo_dir = "./img/";
$upload_data_dir  = "./data/";

// photo 格納場所と命名規則
$photopath = $upload_photo_dir."contentsname_".date("Ymdhis").".png";



// as3 → バイナリ(のみ) 受け取る場合
//$bin = file_get_contents("php://input");

// as3 → base64EncodeData 受け取る場合
$base64 = $_POST['base64'];
$bin = base64_decode( $base64 );

// 上記2種の場合はfile書き込み必要。
$fp = fopen( $photopath,"w");
fwrite($fp,$bin);
fclose($fp);


/*

//POSTで受け取る場合
$tmp  = $_POST_FILES['***']['tmp_name'];	// 一時保管場所
copy($tmp, $photopath);						// 移動
$name = $_POST_FILES['***']['name'];		// ファイル名
$size = $_POST_FILES['***']['size'];		// ファイルサイズ

//formとかからアップロードで受け取る場合
//<input name="***" type="file" size="40">
$tmp  = $_FILES['***']['tmp_name'];			// 一時保管場所
move_uploaded_file( $tmp, $photopath );		// 移動
$name = $_FILES['***']['name'];				// ファイル名
$size = $_FILES['***']['size'];				// ファイルサイズ

*/


// data を残す。
$fp = fopen( $upload_data_dir."data.txt","a");
fputs($fp,$photopath."\n");
fclose($fp);


// fileを画像データで残すのに抵抗あったり、容量のこと考えたら
// DBに残すのがスマートかなってことで、以下、DBに保存の場合。
/* ----- DB 保存処理 ------ */

/* ----- /DB 保存処理 ------ */


// ここからOAuth認証のtokenとか取得してtwitterにPOST!!
require_once('twitteroauth/twitteroauth.php');
require_once('config.php');

$access_token = $_POST['access_token'];
$access_token_secret = $_POST['access_token_secret'];

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
$connection->format = 'json';

$entry = $_POST['message'];
//$image = './nuko.jpg';
$uri = 'https://upload.twitter.com/1/statuses/update_with_media.json';
$params = array('status'  => $entry,
                'media[]' => "@{$photopath}");

//$content = $connection->post($uri, $params);
$content = $connection->postImg($uri, $params);		// 今回画像tweet用に作成した postImg();
print_r($content);

?>

今回はas3からのPOSTだったので、bytearrayのみ送ろうと考えてたんですけど、
よくよく考えたら一緒にtweet文言送ったり一緒にいろいろしないといけないので、
一緒に送れる形としてbase64Encodeかけた文字列としてPOSTの一部にして送ることにしました。
ここ、POSTの一部としてバイナリ(bytearray)送って、PHP側でエンコードかけるときとかどうすればいんだろ??
(まー別にbase64エンコード&デコードで何も問題はないと思うんですが。。)

あとここ、きんくまさんのように、multipart/form-data の Requestheader つくって送れるようにしておけば
受け取り側でいろいろごにょごにょ変えたりしなくて済みますね!素敵!

きんくまさんのところ。
[AS3] PHPとAS3の連携 as3から直接 multipart/form-data でデータのアップロード!

Flashからアップロードする方法いろいろはこちら。バイナリのままアップロードする方法が分かる!

さてここからflashぽく、flash使った何かをやってみる。
それで最終的に絵を作ってそれを投稿できたら一旦、モックとしてのゴールとしますかね。:-)

で、てらこのお題として新年あけましておめでとうって書いてあったので、まー投稿する画像はあけましておめでとうがいいよね。ってことで、あけましておめでとうって言ったら勝手に画像にされて、それがtweetされるとかにしてみようかな。単純ー。

さてじゃあ音声認識かー。ようやくここでAlchemyってやつに初挑戦ですね。(`・ω・´)キリッ

思い起こせば表参道での奥田さんとか、blogでのsaqooshaさんとか、F-siteでの山田さんとか、いろんな人の発表やらblogを読んでネイティブの言語との連携にときめきつつ、ほんとやりたいやりたいと思いつつ、全くそこまで行けてなくて(結局今も全然分からんままやけど。。)、、でもまず挑戦!

まず Alchemy Toolkit for Mac OSX (ZIP, 40.5 MB) だうんろーど

このタイミングで Flash Builder 4.6 と Flex 4.6 SDK がDLできることに気付いてさっそくダウンロード。(これくっそ時間かかった。そしてめんどくさすぎる。。

ここからのあたりはamachangさんの見ながらやると何も問題ないよ!素敵!

flex_sdk 4.6 入れる。(FlashBuilder4.6入れたらそれのSDKへのpath追加しとけばおk) → コンパイルできるようにする。
Alchemy Toolkit Package 入れる。 → セットアップ(./config実行して、alchemy-setup )する。
(あれ?これ毎回、$ source alchemy-setup でセットアップから始めないとダメなん?)
おぉ、alc-on, alc-off で環境変数に alchemy へのパスが追加できたり消したりできるわけね。

で、問題なく読み進めて行くと HelloFlash! で gcc HelloFlash.c すると
-rwxr-xr-x 1 *** staff 258119 1 13 12:13 a.exe
-rwxr-xr-x 1 *** staff 8672 1 13 12:12 a.out
みたいな感じで a.exeファイル ができるのでそれを実行するとー。

$ ./a.exe
/Applications/Adobe\ Flash\ Builder\ 4.6/sdks/4.6.0/bin/adl _sb_29715/app.xml 2> /tmp/adl.trace & echo $!
Hello Flash!

できたー(・∀・)

2個目の stringecho ではtraceを吐くswfを作るんだけども、ここでswcへのコンパイルの仕方と、それを使ってのswfへの書き出しが分かる。:-)

で、
$ mxmlc -library-path+=stringecho.swc –target-player=10.0.0 as3/EchoTest.as
の部分で、FlashBuilder4.6にはデフォルトではplayerが11.1からしか入ってないので
$ mxmlc -library-path+=stringecho.swc –target-player=11.1 as3/EchoTest.as で

/as3/EchoTest.swf (76878 バイト)

書き出せたー(・∀・)

はい次、自分でつくる段階。

■hello.c

#include "AS3.h"
static AS3_Val hello(void* self, AS3_Val args)
{
    return AS3_String("Hello, world.");
}
int main()
{
    AS3_Val method = AS3_Function( NULL, hello );
    AS3_Val object = AS3_Object( "hello: AS3ValType", method );
    AS3_Release( method );
    AS3_LibInit( object );
    return 0;
}

■Hello.as

package {
    import flash.display.Sprite;
    import cmodule.hello.CLibInit;

    public class Hello extends Sprite {
        public function Hello() {
            throw Error(new CLibInit().init().hello());
        }
    }
}

をつくって、例のごとくplayer11.1で指定して
$ gcc hello.c -swc -o hello.swc && mxmlc -library-path+=hello.swc –target-player=11.1 Hello.as
えい。

無事に、”Hello, world.”がErrorとして吐き出されたー(・∀・)
てかこやってswcつくってasコンパイルまで同時にとかもできるのね。

はいそれでは Alchemy でのswc作成できるよになったのでJuliusをflashでできるよにやってみる!
さー、間に合うか。(14:30てらこスタート。現13:07!オワタ)

はいそして断念。(14:20w)

方針変更。いやねいてぃぶとか全然分からんかったしー。

おぉ、HTML5+GoogleChromeで音声認識できる。みたい??(・∀・)

これで勝つる!

<input type="text" x-webkit-speech onwebkitspeechchange="speechInput();" />
function speechInput(){
	console.log(document.getElementsByTagName("input")[0].value);
}

さーまにあうか。

で、なんとかできたもの。

Google Chrome 11 で音声認識できるので(さすがGoogleの音声認識精度!)、それを受け取ってflashで合成してそのまま画像tweet!!
※ Google Chrome 11(最新版?)とかじゃないと動かないと思います。
※ マイク入力 & Webカム入力 がないと体験できないと思います。。スイマセン
※ connectでtwitterにOAuth認証した後、上のFormのマイクアイコンを押すと音声入力できます。日本語で音声認識できると画像が自動tweetされます!注意!

タイトル通り音声認識はGoogleAPIに任せたかったので、ChromeのHTML5の機能を使って音声入力&認識を行って、
それをそのまま写真に載せてtweet!ていうモックです。Chromeでしか動きませんw Chrome最新版ならこんなんが出るはず。

まー作りたいものはまだまだ先なので技術検証モックということでご勘弁くださいw
やー、夢広がる発表が聞けていろいろおもしろい東京てらこでした。
どうもありがとうございました。

you