NetStreamとVideoを組み合わせたmp4の再生

学会を間近に控え、発表用のデモを作成中です。

そのデモの中でローカルにあるflvやmp4の動画をFlex上のコンポーネントで指定の秒数から再生する必要が出てきました。
単純にFlexのVideoDisplayコンポーネントを使えば再生出来ると思ってたのですが、いざやってみるとローカルのファイルがなぜか再生できない(セキュリティ上??)

で、色々と調べているとNetStreamとVideo(flash.media.Video)を組み合わせることでお手軽にローカルの動画をFlex上で再生できるらしい。
で、色んな所参考にして、

var nc:NetConnection;
var ns:NetStream;
var player:Video;
var uiC:UIComponent = new UIComponent();
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
			
player = new Video(512, 384);
player.attachNetStream(ns);
uiC.addChild(player);
canvas.addChild(uiC);
ns.play("動画ファイルパス");
ns.seek(120);

と書いてみたのですが、flvは再生出来るものの、mp4はonMetaDataがどうのと怒られる。

で、また色々なところを参考にした結果、NetStreamのclientプロパティを使うと上手くいくらしい。で、

var nc:NetConnection;
var ns:NetStream;
var player:Video;
var uiC:UIComponent = new UIComponent();
var cObj:Object = new Object();
cObj.onMetaData = function():void{
	_seek();
};
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = cObj;
			
player = new Video(512, 384);
player.attachNetStream(ns);
uiC.addChild(player);
canvas.addChild(uiC);
ns.play("動画ファイルパス");

function _seek():void{
	ns.seek(120);
}

としたら、怒られることなく映りました。
が、シークをしてくれません。
正確には、シークはしてくれるのですがシーク後になぜか再生がストップしてしまうのです。
resumeメソッドも試してみましたが解消しません。
もっと正確に言うと、数回に一回はきちんと再生してくれます。
これでは使い物にならないので、また色々と探したのですが、僕の検索能力では有用な情報が見つからない。
で、悩んで色々と試した結果、上のソースで再生と同時にシークをせず、再生中にボタンを押してシークをすることは可能だったので、再生とシークの処理時間に差がないと上手く行かないのではという結論にいたり、タイマー使って

var nc:NetConnection;
var ns:NetStream;
var player:Video;
var uiC:UIComponent = new UIComponent();
var cObj:Object = new Object();
cObj.onMetaData = function():void{
	var timer:Timer = new Timer(100,1);
	timer.addEventListener(TimerEvent.TIMER, _seek);
	timer.start();
};
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = cObj;
			
player = new Video(512, 384);
player.attachNetStream(ns);
uiC.addChild(player);
canvas.addChild(uiC);
ns.play("動画ファイルパス");

function _seek(e:TimerEvent):void{
	ns.seek(120);
}

としてやると無事上手くいきました。
(正確には、一瞬、動画の頭の部分の映像と音声が入る。まあ、無視できるレベル)
flvではこんな面倒なことをしなくても良かったのですが、なぜかmp4は再生中でないとシークがうまく出来ないようです。
(ニコ動で動画ページのURLの最後に?from=120などと入れるとその秒数から再生してくれるのですが、これもなぜかmp4の動画に対しては正しい挙動をしません。もしかすると同じようなことが原因か…)

もちろん、明らかにこれは完全な解決方法ではないでしょうし、もっと他にスマートな方法はあるのかもしれないですので、指摘ある方お願いしますmm

ちなみに、半日この問題の解決で時間を使いました…

※参考にしたいろんな所のいくつか
http://dev.convexstyle.net/2007/12/videodisplay_netstreamseek.html
http://dev.convexstyle.net/2008/04/h264_progressive.html
http://blog.gu-raphix.com/2009/02/as3-mp4flv.html

ニコタググラフWeb版

2つ前のエントリ*1に書いたニコタググラフのWeb版を作ってみました。
http://atago.me/product/nicotag/graph.html


前回のスタンドアロン版との違いは、ニコ動側とのやりとりはサーバ側のphpで行い、クライアント側では主にグラフの描画のみを行います。また、ログインが不要になりました。

使い方はスタンドアロン版とほぼ同じですが、追加機能としてノード展開数を調整できたり、ノードを右クリックするとそのノードのページにアクセスできるコンテクストメニューが出たりします。

多人数同時アクセスなどにさくらさんのサーバがどの程度耐えられるのか少し心配です。

ニコニコ動画を対象にした学術研究

ニコニコ動画の有料会員数がそろそろ50万人になるようです。
黒字化に向けてがんばっていただきたいものです。

さて、そんなニコニコ動画ですが、最近では若干ですが学術研究の対象にもなってきています。
コメントをアノテーションとみなしてその量からサビが検出できないか*1とか、ニコ動上での初音ミクの素材の再利用ネットワークの解析をしてみたり*2、非負行列因子分解(NMF:Non-negative Matrix Factorization)をコメントに適用することでコメントから動画の分類をしてみたり*3、等々。
(この辺で調べると他にもあります*4 )
ちなみにニコ動のアイディアはそもそも学術研究から来ているという話もあったりします。*5
あとは、ニコニコ動画データ分析研究会*6なども行われていたりしますね。

かく言う僕自身も10月の下旬に開催されるJAWS2009で、恐れ多くもニコ動を題材に発表してまいります。せっかくなので簡単に手法と結果を下記に書いちゃいます。
詳しいのはそのうち公開するかもしれない論文を読んでいただければと。



「コメントをアノテーションとみなしシーンの検索を行う」
技術的には難しいことをしておらず、基本的に動画をユニットに区切って、ユニット内のコメントをスコアリングして閾値以上のユニットを検索したシーンとするといった具合です。
スコアリングするに当たって、検索したいシーンに関するキーワードには重みを付けてあげます。
また、動画長やコメント数の違いにある程度対応できるようスコアの正規化も行ってあげます。
で、実際にこの方法でどんな感じの精度で検索できるのか実験してみました。
実験は、
1.サッカーのゴールシーン
2.サッカーの動画で複数の被験者が印象に残ると感じたシーン
3.アイマスのやよいが登場するシーン
4.麻生首相が発言しているシーン
をそれぞれ複数の動画について、重みαと閾値δを替えながらシーンの検査精度を測定してみました。
実際に各動画を閲覧し検索したいシーンの時間を書き出しておいて、システムが結果として返したシーンに書き出した時間が含まれていれば正解とします。
検索精度の指標は、情報検索の精度表現でよく用いられる、再現率と適合率の調和平均であるF値を用いています。当然値が高い方がよい結果です。
図1.ゴールシーンの検索精度

図2.サッカーの印象に残るシーンの検索精度

図3.やよいの登場シーンの検索精度

図4.麻生氏の発言シーンの検索精度

ん〜、なんとも微妙な結果ですw
ある程度の精度と言えば精度ですが、良いとは言えないでしょうね(ぇ
でも、シーン検索や動画要約でよく用いられる既存手法である、映像解析などに比べるとマシンパワー、金銭的コスト、汎用性では有用かなと思います。
ただ、検索したいシーンを表すキーワード群を用意してあげないといけない、ジャンルなどによって適切な重みが違うなどのデメリットもあります。
その辺をどうにか解決できればもう少し使えるものになるかもしれないですねぇ。
(キーワード群に関しては論文中ではオントロジーのように集合知を用いることで解決できるとはしてますw

まあ、自分のことを棚に上げて言いますと、ニコ動を題材にした研究はどれも現段階ではある程度の結果で、とても良い結果だよ!というのはない状態です。
ただ、始めにも書きましたが有料会員数も50万人に近づきつつあり、ミクやニコマスなど独特な文化形成に多大なる(色んな意味の)パワーがこれだけつぎ込まれているのは中々珍しいと思います。
これから先、何か発想の転換的な感じでとても役立つニコ動を題材にした学術的研究をしてくれる人が現れるのを期待してます。

*1:青木秀憲, 宮下芳明, ニコニコ動画における映像要約とサビ検出の試み, 第75回音楽情報科学研究会 / 第128回ヒューマンコンピュータインタラクション研究会(2008)

*2:M. Hamasaki, H. Takeda and T. Nishimura, Network Analysis of Massively Collaborative Creation of Multimedia Contents Case Study of Hatsune Miku videos on Nico Nico Douga, in First International Conference on Designing Interactive User Experiences for TV and Video(2008)

*3:澤田敬治, 手塚太郎, 木村文則, 前田亮, 動画共有サイトにおけるコメントを用いた動画分類精度の向上, FIT2009(2009)

*4:http://scholar.google.com/scholar?&q=nicovideo

*5:http://synvie.net/

*6:http://nicovideo.g.hatena.ne.jp/Yoshikawa/20081219/1229678324

ニコ動のタグや動画の繋がりを可視化するツール

前回の日記に書いたサーベイ会が先週終了し、今日は久しぶりのゆっくりできる水曜。
本当は論文書かなくちゃいけなかったりと暇ではないのですが、あまりにも研究のやる気が出ないため、タイトルのツール作って遊んでました。
[追記]AdobeAIR環境にて、SpringGraphを用いて作成しています。

スクリーンショットはこちら

使い方としては、任意のキーワード入れて検索。
検索をするとキーワードが含まれるタグを探しに行きます。
で、その結果を表示し、それをダブルクリックするとそのタグを元に動画を検索しサムネイルを表示します。
サムネイルをダブルクリックすると登録されているタグを拾い、それを表示します。
これを永遠に繰り返すだけですw
そのウチ動画再生機能とかつけてもいいのかもなぁ。
ちなみに「自動的に動画のタグを展開する」をチェックするとサムネイル表示と同時に登録タグも表示されてカオスになります。
ただ、その分非常に動作がもっさりします。
とりあえず公開しておきますので興味があればどうぞ。
http://atago.yokochou.com/

※最近、ニコ動側のスタイル変更が多いためスタンドアロン版は正常に動作しません。
Web版で逐一対応を行っていますので、ひとまずこちらをお試し下さい。
http://nicotag.sakura.ne.jp/graph.html

英語論文を簡単(?)に翻訳するためのツールを作ってみた

6月の終わりから毎週木曜日にWWW2009のサーベイをしています。
サーベイなのでそんなに深く読まないのですが、毎週6本論文に目を通さないといけないのです。
で、英語力のない僕はスペースアルクヤフー翻訳などの翻訳サイトをよく利用するのですが、PDF形式の論文ってコピペすると文末で単語がハイフンで分割されていたり(appli-cationとか)、efficiencyのffiが特殊フォントのffiが使われていてefficiencyになっていたりと、そのまま翻訳サイトやアルクの検索窓に突っ込んでも意味不明な結果しか返さないのです。
で、さすがに分量が多いとそれを手作業で直すのも面倒なわけで、その変のことをやってくれるツールを作ってみました。

開発はAdobeAIRで行いました。
最初に入力エリアに論文の文章をそのままコピペし、翻訳ボタンを押すと上記の様な問題部分をがりがり置換してからGoogleの翻訳API(Google AJAX Language API)に渡して翻訳してくれます。
動作の様子はこんな感じ


で、どうせならと置換されて整形された文中の英単語をダブルクリックするとポップアップでアルクの英和辞典を参照するようにしました。

中身は基本的にごり押しですw
Google AJAX Language APIから返ってくる値もJSONフォーマットなので本来は解析のAPIとか使えばいいのでしょうが、なぜか上手く行かなかったのでごりごりスクレイピングしています。
ということで技術的に特に書くことはないのかなぁ。

ああ、Google AJAX Language APIFlex等のJS以外の環境で使う場合、
http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&langpair=○○%7C××
に「q=翻訳したい文章」をPOSTしてあげればOKです。
○○は元の文章の言語、××は翻訳後の言語を指定します。
注意点として日本語はjpでなくjaです。(これに1時間くらいはまってた…)
結構色んな言語に翻訳できて有用性は高そうです。詳しくはデベロッパーガイドをご覧下さい。
http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/#SupportedPairs


あと、アルクから単語の情報をアルクのページ以外から取ってくるのが規約的にどうか分からないのがちょっと怖いです。
ちなみに、アルクから取ってきた単語情報は最初スクレイピングをして表の形なんかで出すことを目指したのですが、思いのほか上手く行かなかったので、対象部分だけ上手くHTMLとして切り出してmx:HTMLコンポーネントに表示させることで決着を図りました。

まあ、自分のために作ったものだから見た目はどうでもいいのですがw


自分で使うにしても、まだ単語情報を上手く取って来れない単語があったりとバグがあるのをつぶしていかないとなぁ・・・

BlazeDSを導入したTomcatを利用したWebアプリのルートディレクトリ絶対パスの取得方法

さて、また約一日悩んだことについて書くとでもします。

見出しの環境ではweb.xmlの内容が自分で書いたサーブレットを直接指さず、「MessageBrokerServlet」等になっているためJava側でgetServletContextを用いての絶対パス取得が出来ない。
そこで、getServletContextを使わずにルートディレクトリの絶対パスを得る方法として色々見て回って見つかったのがこちら

  1. あらかじめルートディレクトリ直下にtest.txtなど適当なファイルを作っておき、
  2. this.getClass().getResource(../.../test.txt)*1.toStringでtest.txtまでの絶対パスを取得
  3. 後は先頭の「file:」と末尾の「test.txt」を消してあげればOK

参考サイト:http://shiro9211.jugem.jp/?eid=442

まあ、でもスマートな方法ではないなとも思います。もしかすると他にもっと簡単な方法があるかも。
ただ、僕の場合、そもそもDBを導入するほどでない小規模なファイルの読み書きをサーバ上で行いたくて調べてたのですが、とりあえずはこれで望み通りの動きをするので、参考サイト様も言ってますが「一応」はこれを使えばOKということでw
べ、別にMySQLが使えないからこうした訳じゃないんだからねっ!//


SQL等も勉強せねばなぁ・・・
オススメの参考書や参考サイトをご存じでしたら教えて下さい。


[追記]
コメント欄でcon_mame氏(通称こんにぃ)が教えてくれたようにflex.messaging.FlexContextをインポートしてあげることでgetRealPath使えました。
ただ、このflex.messaging.FlexContextがtomcat下のlibディレクトリ中に見あたらず、サンプルのlibディレクトリから持ってくることに。
いったい大元はどこにあるの〜?

*1:クラスファイルからtest.txtまでの相対パス

サーバサイドでのインスタンスの保持とAIRのViewStack

相変わらず初心者的なつまずきばかりだけども、同じような境遇の人に役立つかもしれないから書いておく!!(プラス思考)


・サーバサイドのインスタンスの保持
AIRというか、Flex開発したものからTomcatへアクセスする場合、WEB-INFのflexに入ってるremoting-config.xmlにおいて


  
    ××
  

と書いてあげればサーバサイドのjavaのメソッドにアクセス出来ると書いたけども、このままだと処理を行うたびにクラスをインスタンス化し直してしまう。
インスタンスの生存期間が1リクエストの間だけ)
これを防ぐためにはの中に

○○

を追加してあげることで、生存期間を調整できる。
○○をsessionにしてあげればセッション保持中はインスタンスが生存する。
他にもapplicationとかあるっぽい。
サーバをよくいじる人やネットワーク使うソフト組む人にとっては当たり前な、こんなしょうもないところでつまずきます…orz


・ViewStackの注意事項
AIRFlex開発)のViewStackを使う上で気をつけること。
ViewStackは便利なコンポーネントだけども、デフォルトではスタック上に配置した子コンポーネントはそのスタックがvisibleになった瞬間に生成される。
(要は1番上のスタック上のコンポーネントは起動時に生成されるけども、2番目、3番目のスタック上のコンポーネントはスタックが見えるまで生成されない)
なので、これを知らずにソースを書いてるといざ実行した時にスタック上のコンポーネントがnullでエラーになることがある。
これを防ぐにはViewStackの「creationPolicy」を「all」にしてあげる。
こうすれば、ViewStackが生成されると同時に全てのスタックとその上のコンポーネントが生成されるようになる。


まあ、どっちも超初心者向け&自分の覚え書きだなww