KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

2010年08月

せっかくだから俺はAndroidソースコードを読むぜ(1)

どうもこんにちは、コンバット○前です。 嘘です。nakazawa-kです。この数日少しずつ涼しくなってきていますね。 KLab若手エンジニアブログでiPhoneやiPadばかり書かれていてAndroidが 全く書かれていないことに気付いたので、少しずつ勉強した内容などを書いてみます。 現在KLabの社内ではMacユーザ率の上昇に合わせてiPhone開発者増加の 兆しが見えているのですが、開発の取っつきやすさではAndroidだって負けちゃいない。 なんたってMacが無くても不自由なく開発出来る(←ここ重要!)のですから。 というわけで(どういうわけか)Androidなお話です。 今回はカメラからの映像に適当なオーバーレイ要素を追加してリアルタイム エンコードすることがAndroidとそれを走らせているハードウェア上で 実現出来るのか?を調べるためにMediaRecorder関連のソースを追いかけてみました。 ひとつひとつ丹念に見ていけばOSのソースって案外辿って行けて面白いよ!というのをなんとなく感じて頂けると幸いです。
  • Androidは最高の学習環境!?
Androidはご存じの通りオープンソースなモバイル向けOSで、その比較的 新しいコードがAOSP(Android Open Source Project)として公開されています。
ちなみにそのリリース方法はLinuxのカーネル本体とは異なり、Google社が各OS世代の早期パートナー企業(端末メーカ)との間で最新のコードベースでの開発を行い、端末リリース前後のタイミングでAOSPでのコード公開を行う、という流れになっているようです。開発に協力するパートナー企業としては最新OSを搭載した端末を他社に先駆けて市場へ投入出来、またOSに対して自社の望む仕様を公式仕様として追加し易いというメリットが考えられます。
閑話休題、ソースコードが公開されているので、「ここはどういう風に 実装されているんだろう?」という疑問を持った際に、必要であれば OSのコードまでさかのぼって調査することが出来ます。 とはいえ、片っ端からコードの山をひっくり返していけば目的のコードへ 辿り着ける、というわけではありません。なんといってもコード量が莫大です。 gitからソースコードを取得することが出来ますが、AOSPのドキュメントに 従い全て取得すると4.1GB(10/08/30時点)となります。 これ位のコード量になってくると全体に対してgrepをかけるのも一苦労ですし、 検索結果に物凄い数のファイルが見つかって途方に暮れたりするものです。 このような大量のファイルから必要な情報を効率的に得るためには、システムの アーキテクチャ認識を持ち、『必要なものがありそうな場所の勘が働くようにする』 ことが結構重要なことだと思います。 システムの中での呼び出しの流れとデータの流れ(デバイスからの入出力を含め)が 掴めてくると、割と膨大なソースとも付き合いやすくなるのではないでしょうか!? 目の前で動作している端末がどのようなアーキテクチャ、システムで動作しているのかを 学ぶことは非常に良い勉強になるものと思います。せっかくソースコードが 公開されているのでどんどん読みましょう。 ということでだいぶ前置きが長くなってしまいました。nakazawa-kがソースを読み始めたというのが今回の話です。
  • 今回のターゲット:MediaRecorder
先にお断りしておきます。今回の話は、ブログ記事1本分では決着しませんでした。 目的の達成は次回までお預けとなってしまっています。
発端は「Androidのカメラから入力された映像データに対してリアルタイムに オーバーレイをかけ、H.264等で出力することは出来ないものか」と思ったところでした。 ビデオカメラ(CamCoder)といえば通常、MediaRecorder http://developer.android.com/reference/android/media/MediaRecorder.html クラスを用いてソースと出力フォーマット、出力圧縮形式、 出力先を指定した上でstart()を呼んでやれば後は適当にやってくれるという感じです。 逆に、この映像に対して加工を行うことが出来ればARアプリなどで自分の見ていた 世界を動画に記録してそのままYouTubeなどにアップロードする、というような 使い方が出来そうです(ARアプリ内で完結するなら、当然それらをメタデータとして 別途格納しておき、再生時にオーバーレイする方法が考えられますが)。 これを実現するためには
+------------+               +----------------+
| 映像ソース | --フィルタ--> |動画出力エンジン|
+------------+               +----------------+
としてやる必要があります。しかし、残念ながらMediaRecorderで提供されているのは 前述のような入出力設定のみで、実際の動画出力などがどのような仕組みにて 提供されているかは見えなくなっています。
  • 前準備
まずは今回やりたいことがどのようにして実現出来るか、あたりを付けるためにざっと情報を整理してみます。 一般にH.264等のリアルタイムエンコードを行うには携帯端末向けのCPUでは力不足です。そのため、ほとんどの携帯端末ではH.264等の圧縮を丸ごと行う、あるいは部分的にサポートするDSPを用いたエンコードを行っていると考えられます。この入力データに任意のストリームを渡せるのか、それともカメラからの入力を統合チップ内部でバイパスして渡すような方法を採らざるを得ないのか、によって今回の目的の実現可能性は大きく変わってきます(場合によっては一部WindowsMobile機のようにWMVエンコードのソフトウェア実装を持ち込んで力業でエンコードするような方針になります)。 ソースを読み始める前に、少しだけデータシート(というかチップのパンフ的な資料)を見てみましょう。 手元に転がっていたHT-03Aに搭載されている統合チップはMSM7200Aで、どうやら http://www.datasheetpro.com/268119_download_MSM7200A_datasheet.html で見ることが出来ます。 性能的には30fpsでWVGAの動画をキャプチャ出来るほどのチップであることは分かりますが、残念ながら動画エンコード機能に対して任意のストリームを投入出来るか否かについて判断は出来ませんでした。ひょっとすると入力(カメラ)と出力(動画圧縮エンジン)のやりとりをチップ内で完結させなければスループットを出せない(=フレームレートが不足する)かもしれません。 まあ多少フレームサイズやフレームレートが落ちても、加工済みの動画がリアルタイム出力出来れば良いのです。仮にカメラからのJPEG出力系を流用してMJEPGになったとしても、大失敗ではないのです。
  • 本題
それでは、AndroidのMediaRecorderがどのような実装になっているのかを見ていきましょう。 Androidのアーキテクチャは http://developer.android.com/guide/basics/what-is-android.html#os_architecture に記載されているような形になっていています。 今回の場合、Javaで書かれたカメラアプリがAndroid独自のJava系VMである Dalvik Virtual Machineにて実行され、内部からMedia Frameworkが呼び出され、 そこからLinux KernelのCamera DriverやFlash Memory Driverなどを呼び出していると 考えられます。 とりあえず、コードを取得しましょう。
curl http://android.git.kernel.org/repo > repo chmod +x repo sudo mv repo /usr/local/bin repo init -u git://android.git.kernel.org/platform/manifest.git (AOSPのドキュメントではinit-uとなっていますが、正しくは上記の通りです)
まず、おさらいの部分ですがカメラアプリのコードから追いましょう。 アプリケーションのソースはpackages/apps/Camera以下にあります。 MediaRecorderの初期化を行っているのはpackages/apps/Camera/src/com/android/camera/VideoCamera.java VideoCameraクラスのinitializeRecorderメソッドです。

 895         mMediaRecorder = new MediaRecorder();
 896
 897         mMediaRecorder.setCamera(mCameraDevice);
 898         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 899         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
 900         mMediaRecorder.setProfile(mProfile);
 901         mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
...
以下、基本的に録画開始を指示するstart()メソッドの流れを追いかけていきます。 次は当然、android.media.MediaRecorderの定義を調べます。 早速すこしずつややこしくなってきます。ほとんどがプロキシのリレーです。 MediaRecorderの実装はほとんどJavaで行われておらず、ネイティブ実装になっています。 Java側のMediaRecorder定義はframeworks/base/media以下にあります。 frameworks/base/media/java/android/media/MediaRecorder.javaでは
 58 public class MediaRecorder
 59 {
 60     static {
 61         System.loadLibrary("media_jni");
 62         native_init();
 63     }
...
513     public native void start() throws IllegalStateException;
との定義がされています。JNIで処理をC系のライブラリに委ねている形です。 これに対応するコードがframeworks/base/media/jni/android_media_MediaRecorder.cppにあります。
339 static void
340 android_media_MediaRecorder_start(JNIEnv *env, jobject thiz)
341 {
342     LOGV("start");
343     sp mr = getMediaRecorder(env, thiz);
344     process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed.");
345 }
...
466     {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
C++側でのMediaRecorderクラスは
421 static void
422 android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
423 {
424     LOGV("setup");
425     sp mr = new MediaRecorder();
で作られます。このあたりは frameworks/base/include/media/mediarecorder.hに定義があります。 各種定数もこのファイルに定義があるので参考になります。 実装はこちらです。frameworks/base/media/libmedia/mediarecorder.cpp このファイルではインスタンスが存在する場合、そのメソッドを呼び出したり 例外処理を行っているだけなので、重要なのはインスタンスの生成部分です。
586 MediaRecorder::MediaRecorder()
587 {
588     LOGV("constructor");
589
590     const sp& service(getMediaPlayerService());
591     if (service != NULL) {
592         mMediaRecorder = service->createMediaRecorder(getpid());
593     }
594     if (mMediaRecorder != NULL) {
595         mCurrentState = MEDIA_RECORDER_IDLE;
596     }
597     doCleanUp();
598 }
この呼び出しはframeworks/base/media/libmediaplayerservice/MediaPlayerService.cppの
 229 sp MediaPlayerService::createMediaRecorder(pid_t pid)
 230 {
 231 #ifndef NO_OPENCORE
 232     sp recorder = new MediaRecorderClient(this, pid);
 233     wp w = recorder;
 234     Mutex::Autolock lock(mLock);
 235     mMediaRecorderClients.add(w);
 236 #else
 237     sp recorder = NULL;
 238 #endif
 239     LOGV("Create new media recorder client from pid %d", pid);
 240     return recorder;
 241 }
に対応しています。ここで、OpenCoreライブラリを利用しない設定でのビルド時には レコーダを無効化しています。MediaRecorderClientクラスを追いかけましょう。 frameworks/base/media/libmediaplayerservice/MediaRecorderClient.cppです。
292 MediaRecorderClient::MediaRecorderClient(const sp& service, pid_t pid)
293 {
294     LOGV("Client constructor");
295     mPid = pid;
296
297 #if BUILD_WITH_FULL_STAGEFRIGHT
298     char value[PROPERTY_VALUE_MAX];
299     if (property_get("media.stagefright.enable-record", value, NULL)
300         && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
301         mRecorder = new StagefrightRecorder;
302     } else
303 #endif
304 #ifndef NO_OPENCORE
305     {
306         mRecorder = new PVMediaRecorder();
307     }
308 #else
309     {
310         mRecorder = NULL;
311     }
312 #endif
313
314     mMediaPlayerService = service;
315 }
OpenCoreが利用出来る際にはPVMediaRecorderを生成していますね。 PVでOpenCoreと来ればpacket-videoのOpenCoreです。OpenCoreはAndroid内で 利用されているマルチメディア処理ライブラリで、公式サイトは http://www.opencore.net/ です。repoコマンドで取得したツリーでは、 external/opencore/doc/にドキュメントも取得されているはずです。 実はここまで読み進めた後でOpenCoreのドキュメントを読まずに external/opencore/engines/author/src/pvauthorengine.cppあたりまで 追いかけてみたのですが、細かな個別実装ばかりが見えてきて全体が 掴みにくくなってきたのでまずはOpenCoreのドキュメントを読み、ざっと 構造を把握してから続きを読むように方針転換しました。 かなり長くなったので今回はこのあたりにして、次回OpenCoreの構造あたりから 続けていきたいと思います。

PHP Xdebug のProfile の手軽な共有ツールを作ったよ

お疲れ様です。sasaki-kです。毎日暑いですね。 KLabではPHPで作られたフレームワークの高速化が熱いです。 高速化にあたっては、計測が重要です。 KLabではphpのxdebug extensionを使用し、プロファイル結果をcachegrindファイル形式で取得しWinCacheGrind で解析しています。 また皆でレビューするために Webgrind で解析結果を共有したり、言葉で説明しています。しかし、 Webgrindは一見してボトルネックが分かりにくかったり、言葉では説明がもどかしかったりしていました。 一方、KLabではCodepaste の社内版があり、 フォーマット済みのコードをフォームに貼りつけ、ブラウザのアドレスバーからURLをコピーしてIRC貼りつけてレビュー対象のコードを手軽に共有しています。 「Codepasteみたいにプロファイル結果も共有できたらいいのに・・・」と思い、もっと良いものを作りたいなと思い探してみました。 結果、xdebugtoolkit という cachegrind ファイルをコールグラフを表現したdot ファイルへ変換してくれるスクリプトを発見しました。 ボトルネックとなる関数が以下のように色で表示されていて一目で分かりやすく、コールパスもまとめられていてすっきり見やすいです。
  • 呼び出し回数が多い関数はより濃い黄色
  • 自身の実行時間がかかっている関数はより濃い紫
  • 両方の特徴をもつ関数はより濃い赤 (=黄色+紫)
「こりゃいい!」ということで cachegrindファイルをuploadすると、ユニークな短いURLで xdebugtoolkit の結果画像を共有できる社内向けWebサービスを作りました。 またcachegrindファイルを保持しておく必要があるWebGrindを共有サービスにするにあたっての問題は、同ファイルは一般に巨大で、disk fullを起こさないようすぐに削除しなくてはならなかった点にありました。 ご紹介した方法だとdotファイルに変換された段階で小さくなり、かつcachegrindファイルを消してしまっても構わないので都度dotファイルから画像生成すればファイル削除を気にする必要がなさそうです。 このサービスを使ってみんなお手軽にProfileできたらいいなと思っています。

PHP で Gmail の OAuth+SMTP を使う

はじめまして。
KLabにインターンでお邪魔させていただいている nakatani-s です。

インターンで製作しているアプリの中で、GmailのSMTPサーバにOAuth認証で入る部分があります。 こちらにあるように、GmailはIMAP・SMTPともにOAuth認証に対応しているのですが、OAuth+IMAPのサンプルコードはあってもOAuth+SMTPのサンプルって中々見つからないんです。
そこで、OAuth+SMTP in Gmail のシンプルなPHPコードを公開して、少しでも世のお役に立てたらなと思うわけです。
いえ、僕が探した範囲なので本当はあるかもしれませんよ?でも恥ずかしいので知っている方も生温かい目でスルーしてください。

開発中にたくさんの助言・アイディアを下さり、インターンの身にこのような機会を与えてくださったKLabの方々には頭が上がらない思いです。

目次

  1. 必要な環境
  2. 下準備
  3. 動作確認
  4. コードと解説
  5. まとめ
  6. 参考

必要な環境

PHPが動くサーバが必要です。サーバのルートに自分でファイルが置ける必要もあります。

下準備

まずはGoogleに、サーバをOAuthのConsumerとして利用するための申請をし ましょう。これはこちらのページを参考にしました。
OAuth Consumer Key と OAuth Consumer Secret は後で使うので、忘れないようにしておいて下さい。

次に、GoogleオフィシャルのOAuth+IMAPのプログラムを動かしてみます。
Gmail IMAP and SMTP using OAuth - Libraries and SamplesのPHP Sampleをダウンロード・解凍し、 xoauth-php-samples/common.php を開きます。
先ほど仕入れた OAuth Consumer Key と OAuth Consumer Secret の情報を、以下の部分に加えて下さい。

$THREE_LEGGED_CONSUMER_KEY = 'your_consumer_key';  // ここと
$THREE_LEGGED_SIGNATURE_METHOD = 'HMAC-SHA1';
$THREE_LEGGED_CONSUMER_SECRET_HMAC = 'your_consumer_secret';  // ここ

それが終わったら xoauth-php-samples/ ディレクトリ以下のファイルを全て自分のサーバにアップロードし、ブラウザ上から three-legged.php にアクセスしてみて下さい。
表示されたページでエディットボックスにご自分のGmailアドレスを入力し、実行ボタンを押すと、GoogleのOAuth認証画面にリダイレクトされます。

これで、"Total messages: X" と表示されたら、ここまではOKです。

ちなみに、僕はここで入力するアドレスをtypoしたせいで、社員の方を巻き込み6時間は悩みましたw

動作確認

こちらのPHPコードをダウンロードしていただき、エディタで開いて下さい。

190-195行目を、ご自分の環境に合わせて編集します。

  $from = 'your_account@gmail.com'; /* OAuth認証で使ったアカウントを指定 */
  $namefrom = 'your_name';          /* ご自由に */
  $to = 'someone@someserver.foo';       /* 送り先 */
  $nameto = 'someone';              /* 送り相手の名前 */
  $subject = 'test mail';           /* 件名 */
  $message = 'OAuth+SMTP test';     /* 本文 */

これが終わったら、サーバ上の common.php や three-legged.php と同じディレクトリ上に置いて下さい。ブラウザから three-legged-smtp.php にアクセスすると、指定した送信先にメールが届くはずです。

コードと解説

three-legged-smtp.php の解説をさせていただきます。
お気づきな方はお気づきでしょうが、ほぼ three-legged.php と同内容です。
内部で行われている処理の流れを簡単に説明すると、

  1. OAuth認証をし、Googleから認証情報をもらう(1-113行目)
  2. もらった認証情報を使ってssl通信を開始し、SMTPプロトコルで通信する権利を得る(114-187行目)
  3. SMTPプロトコルでメールを送る(190-212行目)
  4. Googleとのコネクションをクローズする(124-126行目)
という感じです。
この「OAuth認証をし、Googleから認証情報をもらう(1-113行目)」と「もらった認証情報を使ってssl通信を開始し、SMTPプロトコルで通信する権利を得る(114-187行目)」の部分が、OAuth+IMAP通信を行っていた three-legged.phpとほぼ共通なので、ほぼ同内容となっています。

さて、元のコードから追加・変更・削除した部分を説明します。

22-23行目: インクルードファイルの変更

require_once 'Zend/Mail/Protocol/Smtp.php'; /* Imap.php -> Smtp.php */
// require_once 'Zend/Mail/Storage/Imap.php';  /* 必要無し */

121-123行目: ssl通信時に送るURLを、SMTP用のものに変更

  $url = 'https://mail.google.com/mail/b/' .
       $email_address . 
       '/smtp/';                /* imap -> smtp */

167-216行目: コメント参照。
要は、Googleのドキュメントに従ってssl通信をしているだけです。

  /* GmailのSMTPサーバと通信するためのsocketを開く */
  $smtpServer = "tls://smtp.gmail.com";
  $port = "465";                /* 465番ポートでうまくいかなかったら587番も試してみてください */
  $timeout = "45";
  $localhost = $_SERVER['REMOTE_ADDR'];
  $newLine = "\r\n";

  $smtpConnect = fsockopen($smtpServer, $port, $errno, $errstr, $timeout);

  $smtpResponse = fgets($smtpConnect, 4096);

  /* SMTP+OAuthの認証手続き */
  /* http://code.google.com/intl/ja/apis/gmail/oauth/protocol.html に規格があります */
  fputs($smtpConnect, "HELO $localhost". $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  
  fputs($smtpConnect,"AUTH XOAUTH" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  fputs($smtpConnect, $initClientRequestEncoded . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  
  /* 認証終了後にメールを送る */
  $from = 'your_account@gmail.com'; /* OAuth認証で使ったアカウントを指定 */
  $namefrom = 'your_name';          /* ご自由に */
  $to = 'someone@someserver.foo';       /* 送り先 */
  $nameto = 'someone';              /* 送り相手の名前 */
  $subject = 'test mail';           /* 件名 */
  $message = 'OAuth+SMTP test';     /* 本文 */

  fputs($smtpConnect, "MAIL FROM: <$from>" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  fputs($smtpConnect, "RCPT TO: <$to>" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  
  fputs($smtpConnect, "DATA" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  $headers = "MIME-Version: 1.0" . $newLine;
  $headers .= "Content-type: text/html; charset=iso-8859-1" . $newLine; /* charsetはお好みで */
  $headers .= "To: $nameto $to1" . $newLine;
  $headers .= "From: $namefrom <$from>" . $newLine;
	 
  fputs($smtpConnect, "To: <$to>\r\nFrom: $from\r\nSubject: $subject\r\n$headers\r\n\r\n$message\r\n.\r\n");
  $smtpResponse = fgets($smtpConnect, 4096);
	 
  fputs($smtpConnect,"QUIT" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  fclose($smtpConnect);

218-245行目: OAuth+IMAPでのメール受信に固有の部分をコメントアウト

  /* IMAP用の認証なので必要無し */
  /**
   * Make the IMAP connection and send the auth request
   */
  /* $imap = new Zend_Mail_Protocol_Imap('imap.gmail.com', '993', true); */
  /* $authenticateParams = array('XOAUTH', $initClientRequestEncoded); */
  /* $imap->requestAndResponse('AUTHENTICATE', $authenticateParams); */
  
  /* 以下、GMailからメールを受信して表示する部分なので必要無し */
  /**
   * Print the INBOX message count and the subject of all messages
   * in the INBOX
   */
  /* $storage = new Zend_Mail_Storage_Imap($imap); */
 
  /* include 'header.php';  */
  /* echo '<h1>Total messages: ' . $storage->countMessages() . "</h1>\n"; */

  /**
   * Retrieve first 5 messages.  If retrieving more, you'll want
   * to directly use Zend_Mail_Protocol_Imap and do a batch retrieval,
   * plus retrieve only the headers
   */
  /* echo 'First five messages: 
    '; */ /* for ($i = 1; $i <= $storage->countMessages() && $i <= 5; $i++ ){ */ /* echo '
  • ' . htmlentities($storage->getMessage($i)->subject) . "
  • \n"; */ /* } */ /* echo '
'; */

まとめ

いかがでしたか? Google Code に置いてあるコードをベースに、OAuth+SMTP認証ができました。
しかし、このコードでは Google からOAuth認証情報を得る部分が PHP のZend Framework の内部に隠されてしまっています。
インターン中は時間がないのですが、なるべくいろいろな言語に流用できるように認証情報取得部分もホワイトボックス化したいと思います。
もしできたら、僕の個人ブログにでもコードを書きたいと思っています。

最後までお読み下さりありがとうございました。

ソースコード

http://lab.klab.org/young/wp-content/uploads/data/code/three-legged-smtp.php.txt

参考

第1回チキチキIRCボットファイターズ! 祭りの後

ご無沙汰してました! sasaki-k です。 遅れてしまい申し訳ございません。先日ご案内した表題の「チキチキIRCボットファイターズ!」社内投票と読者の皆様のはてなブックマークと、いいね!を合計した結果をご報告いたします。 以前 takada-atさんが開発したAIが社内でも話題になっていたのですが、そのIRC版が好評だったようです。またはてなブックマーク、「いいね!」ボタンも1票づついただけて文句なしの1位でした!ちょっとした笑いを誘う粗品を贈呈させていただきましたー。 2位のosuga-hさんは締切り過ぎてしまったのですが、JavaScriptでボットを作るということが新鮮で、かつはてなブックマークも1票いただいて見事2位となりました。 3位のnakazawa-kさんのボットもご本人の熱い思いとともに、社内に増殖しつつある(?)pythonista達に火をつけたようです(?)で3位となりました。 エントリーいただいた皆様、ブックマークやいいねボタンを押していただいた皆様どうもありがとうございました!! 最後となりましたが、私の拙い案内文のためKLab社外からのエントリーも可能と誤解を受けてしまった方もいらっしゃったようです。実際には社内イベントでした。この場をお借りしてお詫び申し上げます。

Twitterで140文字の制限を超えてみせる???

umjammer です、 先日 Twitter にて 140 文字以上投稿されるという事件が起きました。もうそのバグは塞がれたみたいですが、人間制限を設けられるとやはり超えてみたいと思うものです。じゃあ超えてみせましょう(笑) 140文字以上tweetできるTwitter利用サイト、その名も「Too言ったー」。
toowitter

http://umjammer09.appspot.com/ まず Twitter のアカウントは取得しておいてください。初めて使用する場合には「signin」のリンクをクリックしてください。OAuth を用いた例の認証が行われるので、許可してあげてください。 signin 後、サイトのテキストボックスに長文を入れて「Too言っとく」ボタンを押せば140文字の制限を超えて投稿できます。投稿後しばらくすると「成功しました」のダイアログが出ます。「Preview」ボタンは投稿する前にイメージを把握することができます。投稿後は中程の「View Your Post」リンクをクリックすれば今投稿したものを見ることができます。 Twitter ユーザなら何をしているか一目瞭然ですね。 改行とかエラー処理を真面目に行っていないので何か問題があってもご容赦ください。 まとめ 冗談みたいなサービスですが、結構新しめの技術を使っています。 HTML5 の canvas を使用しています。canvas にテキストエリアに入力した文字をレンダリングして toDataURL メソッドで画像にして GAE に送ってそこから API を使って投稿しています。ブラウザによっては動かない可能性があります。 JavaScript は苦手なので GWT と gwt-g2d を使用しました。本家の canvas を使用せずに gwt-g2d を使用したのは本家には文字を描くメソッドがなかった(ハズ)からです。GWT は楽ですね。もう少し言うなら canvas 周りのグラフィックス API を早く統一して欲しいです、出来れば Java 2D 準拠で。Google さん主導なんだから最低でも android API 準拠にしてくれればと思います。 ソースはこちら参考サイト 補足 私も Twitter ユーザですし、140 文字だからこそ Twitter の文化があるということは十分理解しています。あくまで技術検証を目的とした洒落のサービスだということをご理解願います。
 KLab若手エンジニアブログのフッター