2012/12/23

XBMC for Android カスタマイズ

ここでは普通の設定じゃ出来ないけどソース側をちょっと変更することで自分都合ですがいい感じに変更したことを書きたいと思います。こういうわがままもソースがあるからできることなので、オープンソースってやっぱりいいなぁと思います。


「映画」を「録画」変える

以前の記事で.nfoファイルを用意してビデオライブラリをいい感じにするのを行いましたが、地デジ録画ファイルを「映画」として扱っているわけです。それはいいとしても「映画」って違うよねってことで、表示だけでも変更して違和感がないようにしたいと。
ついでに、nfoでstudioにチャンネル、directorに録画日時を入れているので、これらも変更します。

マルチランゲージのファイルはAPKのassetsに入るので、advancedsettings.xmlみたいに外から手出しできません。apkをばらして変更して再構築っていう手もありますが、ビルド環境があるんで普通にやります。

xbmc-android/language/Japanese/string.po っていうファイルが日本語文字列が入っているファイルです。
この中の「映画」ってなっているところを全部「番組」に変更します。
「スタジオ」、「監督」もそれぞれ「チャンネル」、「録画日時」に変更します。

そしたらxbmc-androidのところで
$ make apk
で、apkの構築だけをやれば出来上がり。


ホームアプリ化

http://melea1000.blogspot.jp/2012/09/xbmc-as-launcher.html にも書かれていますが、XBMCをホームアプリ(Lancher)にしてしまおうということです。
XBMCの「プログラム」から別アプリを起動することも出来るんでランチャーとしてXBMCは使えます。
それよりも、リビングで家族でビデオを楽しむSetTopBoxのような環境にしたいわけです。(XBMCが不用意に終わらないようにしたい)

xbmc-android/tools/android/packaging/xbmc/AndroidManifest.xml を変更

<category android:name="android.intent.category.LAUNCHER">
と書かれているところを
<category android:name="android.intent.category.HOME">
<category android:name="android.intent.category.DEFAULT">
に変更します。

常駐するためには、
<application android:hascode="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:persistent="true">
のように、persistentを追加します。が、これやってもやらなくても体感は変わらなかったですね。
すごくレスポンスがよくなるならいいですが、変わらない感じなのでいらないかなと。

これもソースはいじってないので
$ make apk
で、apkの構築だけをやればOKです。

ホームアプリ(ランチャー)を気軽に変更するホームスイッチャー系のアプリを入れておくと便利です。
上記の変更をすると普通のアプリとしては見えなくなる(アプリ管理のAllで見える)ので。


デバッグビルドじゃなくする

ビデオ再生部分とかベースライブラリ群はデバッグビルドじゃないんでパフォーマンスにはほとんど関係ないと思いますが、デバッグログとかXBMC本体のデバッグコードとかちょっとでも処理を減らせたらいいかなと思い、やったことです。

xbmc-android/tools/android/depends/xbmc/Makefile を変更
# configuration settings
CONFIGURE=cp -f $(CONFIG_SUB) $(CONFIG_GUESS) build-aux/ ;\
./configure --prefix=$(PREFIX) --host=$(HOST) \
--enable-neon --enable-gles --enable-debug \
--disable-sdl --disable-x11 --disable-xrandr \
--disable-optical-drive --disable-joystick \
--enable-shared-lib --disable-alsa

って書いてありますが、この中の"--enable-debug"を"--disable-debug"に変更します。

xbmc-android/tools/android/packaging/xbmc/AndroidManifest.xml を変更

ホームアプリ化のところに書いちゃいましたが、
<application android:dabuggable="true" android:hascode="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name">
から、android:debuggable="true"を削除します。
おまじないみたいなものですが、ログ出力とかちょっとでも余計な処理を省けるかなと。

そしてビルド
$ make -C tools/android/depends/xbmc

出来上がったapkのサイズがちょっと減ったので、少しは効果あるかな?と思いましたが体感は全然変わりませんでした。まあ気分ですね。


アップデート

変更じゃないですが、上のような変更をするとなると、もう他からのAPKダウンロードじゃだめです。自分でアップデートしていかないと。
ということで、アップデート手順も簡単に書き留めておきます。

$ cd xbmc-android
$ make -C tools/android/depends/xbmc clean (やらないで済むといいな)
$ git pull
$ make apk
$ adb install -r xbmcapp-armeabi-v7a-debug.apk

通常はこんな感じでしょうかね。makeが通らなかったり、ライブラリ側が更新された時はもっと面倒でしょう。
ライブラリのconfigure当たりからやり直しでしょうか。



とにかくこれで、かな~りしっくりくる環境になりました。これならかみさんにも使ってもらえそうです。

2012/12/14

XBMC for Android 設定とMySQL対応

更に快適なXBMC環境にするためにってことで、普通に設定で出来そうなことを色々やってみました。結果、どれも問題なく、いい感じです。以下、自分が行った設定です。


システム設定

外観(Appearance)

日本語化(誰もがやることですね。順番が大事かも)
  1. Skin>Font=>Arial Base
  2. International>Character Set=>Japanese(Shift-JIS)
  3. International>Language=>Japanese
天気予報 (役に立つような立たないような・・)

一般>設定の"Location setup"からLocation 1を選んで、"tokyo"って入れて出てくるメニューから適当に選んで設定。Location 1に"Tokyo,Japan"とか表示されたらOKする。

サーバー

ネットワーク経由のリモートアプリから制御できるように
  • Webサーバー>HTTP経由でのXBMCの制御を有効に=>オン
  • リモートコントロール>システム上のプログラムによるXBMCの制御を許可=>オン
システム
  • オーディオハードウエア>オーディオ出力=>HDMI
  • 入力デバイス>リモコンによるキーボード入力=>オフ
  • 入力デバイス>マウス・タッチスクリーンの有効化=>オフ(これやると外部キーボードだけの操作になる。潔し!)
MK808なので一応HDMIってやりましたが、効果はよくわかりません。パススルーのオーディオ装置をつなげている場合は、パススルー設定にすると処理が軽くなってパフォーマンスがあがるらしいですが、Android端末には関係ない話か。

フルスクリーン化(ナビゲーションバーを消す)

これはXBMCの設定じゃないんですが、XBMCのためにってことで。

Android4.0から(だったかな?)物理キーを持たないPad/Tab系Androidにはシステムナビゲーションバーが画面下に入るようになって、アプリからHide操作しても、’・・・’ってなるだけで完全には消えないんですね。ビデオ再生時もずっといるので目障りで仕方ありません。

HideBarというアプリを使うといいんじゃない?と、http://wiki.xbmc.org/index.php?title=XBMC_for_Android_specific_FAQ で説明されています。ストアには有料版がアップされていますが、無料(デモ)版がいいのでhttp://ppareit.github.com/HideBar/ から、デモバージョン入手してインストールします。たまにバナーが表示されたりしますが気にしません。

それより、これを動かすにはOSがルート化されている必要があるようです。自分は以前の記事の際にやったので大丈夫でしたが、もっと簡単な方法ないですかね。

HideBarとXBMCが連動しているわけではないので、きれいにフルスクリーン化するには手順がちょっとあります。
  1. XBMCが起動していないこと。リジューム状態ではだめです。
  2. HideBarでナビゲーションバーを消す。
  3. XBMCを起動する
すると画面全体をXBMCが使うようになりますので、ビデオ再生でTV画面全部使ってくれます。
逆に、この状態でナビゲーションバーを表示させると、XBMCが上にずれてしまいますが、またバーを消せば元に戻るんで大丈夫。この辺はOS仕様ともに改善されるといいんですが。


advancedsettings.xmlで設定

このファイルにデフォルトを変更するユーザ設定を色々書いてuserdataフォルダに置いておけば反映します。詳細はhttp://wiki.xbmc.org/index.php?title=Advancedsettings.xml

用意したxmlファイルをAndroidの所定の場所にコピーしてXBMCを再起動すれば反映されます。

adbでコピーするなら、コネクトして
$ adb push advancedsettings.xml /sdcard/android/data/org.xbmc.xbmc/files/.xbmc/userdata
ストレージマウントしてコピーしても同じです。

自分は以下の設定を行いました。

<splash>

splashをfalseにすると起動時にスプラッシュロゴ表示がなくなります。

<video>

シーク時間設定
これが出来ないと自分にとって何の意味もないんですが、XBMCはちゃんとできます。

左右キーでのシーク時間変更 timeseekforward と timeseekbackward
上下キーでのシーク時間変更 timeseekforwardbig と timeseekbackwardbig

これ以外にもvideoタグ内では色んな設定が出来ますね。

<videodatabase> と <musicdatabase>

ビデオデータベース変更

ライブラリとか視聴記録とかの管理データを、内臓データベース(SQLite3)から外部データベースへ変更することが出来ます。
メリットは、データベースを外部に持つことで複数のXBMC端末からライブラリを共有できるようになることかなと思いますが、自分が考えているのは、地デジ録画システムから視聴した場合でも情報が連動するように出来たらいいなぁと思ってます。


実際にXBMCに食わせたadvancedsettings.xmlがこちら
<advancedsettings>

<splash>false</splash>

<video>
  <timeseekforward>30</timeseekforward>
  <timeseekbackward>-30</timeseekbackward>
  <timeseekforwardbig>60</timeseekforwardbig>
  <timeseekbackwardbig>-60</timeseekbackwardbig>
</video>

<videodatabase>
  <type>mysql</type>
  <host>{MYSQLサーバーIPアドレス}</host>
  <name>xbmc_video</name>
  <user>xbmc</user>
  <pass>xbmc</pass>
</videodatabase>

<musicdatabase>
  <type>mysql</type>
  <host>{MYSQLサーバーIPアドレス}</host>
  <name>xbmc_music</name>
  <user>xbmc</user>
  <pass>xbmc</pass>
</musicdatabase>

</advancedsettings>


MySQLサーバー側の設定

$ mysql -uroot -p

# ローカルホストとLAN上のホストからアクセス可能なユーザ'xbmc'を作成
create user 'xbmc'@'localhost' identified by 'xbmc';
create user 'xbmc'@'192.168.1.%' identified by 'xbmc';

# 'xbmc'ユーザにデータベース作成権を与える
grant all on *.* to 'xbmc'@'localhost';
grant all on *.* to 'xbmc'@'192.168.1.%';

flush privileges;
exit;

別ホストからアクセスできるように設定変更

$ sudo emacs /etc/mysql/my.conf
bind-addressの行をコメントアウト
$ sudo service mysql restart
リスタート

ポイントは、'xbmc_video'とか、自分でデータベースを作ってはいけないということ。
XBMCがデータベース自体を作成します。最初これを理解していませんでした。

デバッグログ出力を有効にして、/data/data/org.xbmc.xbmc/cache/temp/xbmc.log を読んでエラー箇所を見つけてやっと理解しました。自ら作ろうとしてるぞ?!ってね。

作成できるように強い権限を与える必要があるため、上のように、localhostとLAN内からのアクセスに限定しています。

古いバージョンデータベースを見つけたらアップデートは勝手にやってくれるようですね。空っぽのxbmc_videoDBを最初作ってしまったものだから権限与えてもエラーになっていて、ちょっとハマりました。

advancedsettings.xmlでデータベース名を'xbmc_video'って書いてありますが、データベース名のprefixを指定しているにすぎません。実際のデータベース名はXBMCのライブラリバージョンごとに変わるようです。
実際には'xbmc_video75'と'xbmc_music32'というのがXBMCによって作られました。
最後の数字はライブラリバージョンらしいです。

これで地デジ録画サーバー上のMySQLにXBMCのライブラリが共存するように出来たので、視聴記録の連動とかがしやすいかな。

次はある意味、設定変更なんですが、再ビルドしないとならない自分勝手な変更を行う内容を書く予定。

2012/12/07

XBMC用.nfoファイルを用意する

XMBCがAndroid端末(MK808)で動きましたので、次の段階です。

再生時の操作については別途書きたいなと思ってますが、今回のテーマは録画ビデオファイルのリスト表示を、ファイル名だけとかサムネイルだけとかじゃなくてちゃんと番組名、内容説明を表示するようにしたいということへの取り組みです。

詳しくはないんですが、XBMCのImport-export library を読むと、nfoファイルというものを用意すれば、無機質なファイル名ではなくて、タイトル名やらなんやら、オンデマンドコンテンツのように表示させることが出来るとありますね。

ビデオファイル名.nfoファイルを作ってあげればよいと

初めは、XBMCの方でそのファイルをインポートするなんて操作をしなければならないのかな?面倒だな。なんて勘違いしてましたが、全然大丈夫でした。
ビデオファイルが入っている同じフォルダ内に拡張子を"nfo"にして置いておけば勝手に読んでライブラリ化してくれます。これなら自動化も楽ちんです。

nfoファイルの中身

XBMCがライブラリ化できるのは大きく分けて、映画(movie)、TV番組(tvshow)、TVエピソード(episodedetails)及び音楽ビデオ(musicvideo)のようです。

メジャーコンテンツ扱う場合なら、オンラインデータベースからの情報とリンクさせてより詳しいデータとかを取得出来たりするようですね。そういうのが一切関係ないローカルで汎用的なビデオ(video)とかあればいいなと思いましたがね。

どのライブラリタイプを使うか

どれが一番適しているか色々試してみました。

本来はtvshow(TV番組)としてライブラリ化するのが自然だろうなぁと思いましたが、tvshowは"tvshow.nfo"と名前が決まっていて、1ファイル1ディレクトリ管理じゃないとだめっぽい。
TV_Show_files_naming_conventions
この辺を読むと、さらにその下にepisodedetailsを置いて、ファイル名とか色々構成を整えないといい感じにならないらしい。

movieとmusicvideoは、ファイル単位のnfoを作るだけで読まれたので、どっちでもよさそうでしたが、musicvideoだと、並べ替えで「日付」を選ぶことが出来なくて、リストがいまいち使いにくい。どちらもpodcastのような公開日とかがないので、実ファイルの作成日が使えることが重要だったりします。

消去法で、内容はTV番組ですが、映画ライブラリとして構成するのが一番使い勝手が良いようです。結論を先に書きましたが、作成と読み込み手順は以下のような感じで進めました。

nfoファイル作成

録画サーバーから提供できる番組情報は、元々地デジEPG情報なので、番組名・内容・日時・長さ(分)・ジャンル・チャンネル位なので、それをどうにか対応させます。
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<movie>
 <id>番組ID</id>
 <title>番組名</title>
 <outline>内容</outline>
 <plot>内容</plot>
 <runtime>長さ</runtime>
 <genre>ジャンル</genre>
 <studio>チャンネル</studio>
 <director>録画日時</director>
 <set>番組名のサブセット</set>
</movie>
outline,plotどっちで内容が出てくれるのかよく分からないので両方に。
チャンネル項目がないので、studioに適当に入れました。
録画日時を表示させたいので、directorに適当に入れました。
studioとdirectorがとにかく目立つところに表示されるのでね。意味が全然違いますけど・・・
idってのがあったのでチケットID入れてみましたが、どこにも表示されませんな。

このnfoファイルを作成する録画システム上のスクリプトがこちら
xbmc-movie.rb

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
#
require 'rubygems'
require 'active_record'
require 'rexml/document'
require 'digest/md5'

@media_path = '/opt/videos/'

ActiveRecord::Base.establish_connection(
        :adapter => 'mysql2',
        :host => 'localhost',
        :username => 'redmine',
        :password => 'redmine',
        :database => 'redmine'
)

class Issue < ActiveRecord::Base; end
class IssueCategories < ActiveRecord::Base; end
class Attachments < ActiveRecord::Base; end
class CustomValues < ActiveRecord::Base; end

def add_element_with_text (parent, element, text)
  e = parent.add_element element
  e.text = text
end

# create a nfo file
def create_nfo (issue)

  video = Attachments.first(:conditions => {
                              :container_id => issue.id,
                              :content_type => "video/mp4"})
  channel = CustomValues.first(:conditions => {
                                 :customized_id => issue.id,
                                 :custom_field_id => 2})
  time_tmp = format("%04d", CustomValues.first( :conditions => {
                                                  :customized_id => issue.id,
                                                  :custom_field_id => 1}).value.to_i)
  time = "#{time_tmp[0..1]}:#{time_tmp[2..3]}"

  # 試行錯誤の末こんな感じに
  set = issue.subject.sub(/[  ”’#(「・◇].*/,'')

  xml = REXML::Document.new
  info = xml.add_element 'movie'
  add_element_with_text info, 'id', issue.id
  add_element_with_text info, 'title', issue.subject
  add_element_with_text info, 'year', issue.start_date
  add_element_with_text info, 'runtime', issue.estimated_hours
  add_element_with_text info, 'outline', issue.description
  add_element_with_text info, 'plot', issue.description
  add_element_with_text info, 'genre', IssueCategories.find(issue.category_id).name
  add_element_with_text info, 'director', "#{issue.start_date} / #{time}"
  add_element_with_text info, 'studio', channel.value
  add_element_with_text info, 'set', set

  nfo_file = video.filename.to_s.sub(/.mp4/,".nfo")
  puts "#{nfo_file}:#{set}"

  nfo = File.open(@media_path + nfo_file,"w")
  nfo.puts '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'
  xml.write nfo
  nfo.puts "\n"
  nfo.close

  # 本件に関係ないので省略
  #attach "nfoファイル", issue.id, nfo_file, "xbmc/nfo"
end

# 公開ステータスの録画チケット
Issue.find( :all, :conditions => {:tracker_id => 3,:status_id => 4}).each {|issue|
  create_nfo issue
#  break
}

今までのビデオファイル全てにnfoファイルを作成するものになってますが、録画システムとしては番組録画後にそれ用のnfoファイルを作成するフローにしています。以前podcastがらみでやったこととほとんど同じ要領なので、ちゃちゃと出来ました。

XBMC側で読んでもらうには

ビデオソースを追加して、HTTPとかSMBとかで、ビデオフォルダを見れるようにします。
この辺の手順は他のHOWTO記事を参考にされた方が親切でいいです。

コンテンツの種類として”(映画)”を選んで設定ボタンを押して、有効になっているやつを全部外します。外さなくてもいいけれど勝手にDBへアクセスしに行ったりするようなので。
設定を全部OKしてダイアログを全部閉じるとライブラリ化はその瞬間から自動で開始されます。

終了するまで数分時間がかかりました。その最中でも大丈夫そうでしたが、落ち着いたかなぁという頃合いにXBMCのホームに戻るとビデオの下にはにはライブラリ、横に「映画」という項目が増えています。この辺をたどっていくとnfoファイルで設定した内容でビデオファイルが表示されました。

新しい順にリストさせるには、表示オプションで、並べ替えを「日付」にソートを「降順」にすれば新しい順に出ます。後は表示の種類を自分の好みにしてみる。うーん。いい感じです。

setがいい

setタグのところで、番組名のサブセットと書きましたが、XBMC上で同じセット名は仮想的なサブディレクトリとして扱われます。
毎週録画している番組とか、本来はエピソード番号とかで管理されるといいと思いますが、地デジ情報からは得られないので、番組名の共通項をセット名として定義することでシリーズ番組とかを同じセットグループとして分類が出来て便利です。

日本語での検索

表示オプションから検索が出来ますが、残念ながら日本語入力が出来ません。これはがっかりですが、xbmcユーザからすると周知の事実らしいですね。バージョンが上がると解決するんでしょうか。パッチとかあるんでしょうか?

別のAndroid/iOS/その他デバイスでXBMCリモートアプリを使って検索すれば日本語検索できるんで、さほど問題ではないですけど、出来れば普通に本体だけで済ませたいですね。

何だか思っていた以上の環境が整いそうな予感です。
もっといい感じにできるのか調べてみましょう。

2012/11/30

XBMC for Androidをビルドする

ため込んだ録画ファイルやNAS上のホームビデオをただ見れればよいではなく快適な操作で快適に視聴する環境であって欲しい。そんな環境が作れないかと日々模索しているところですが、そんな中、
例のXBMCがAndroid単体で動くらしいと知りました。よくあるリモートアプリではなくて本体です。
別にメディアサーバーを稼働させることなく、Android側で動くなら試したいことが沢山あるんです。

XBMC for Androidはどこ?
今はベータバージョンらしく、ソース一式はGITHUBにありますが、GooglePlayにはまだないようです。
なのでソースを入手してビルドするっていうのが正当な手段ということになります。

ビルド済みAPKをダウンロードする

XBMC_for_Android_specific_FAQ によると、オフィシャルではないけど、以下が紹介されています。
実際、code.google.comから入手するのが一番楽ちんだと思います。かなり頻繁にビルドアップデートされてます。
full-neonバージョンの方をmk808にいれて、問題なく動作しました。

使い勝手についてはまた今度
今回は自分でビルドすることがテーマです
ソースからビルドする

数か月前まではAndroid用リポジトリとして分かれていたようですが、現在はメインリポジトリにマージされています。
ビルド手順も丁寧だったので、やってみました。今回はこのビルド手順についてです。
ビルド環境はUbuntuが想定されています。ちょうど地デジ録画システムはUbuntuなので、そこでやってみましたが、あまり考えずにやったため、苦労してしまいました。

結論から言うと、ubuntuはubuntuでも64bit版ではなく、32bit版でビルドする事をお勧めします。


まずは、README.Androidに記載されている手順をつらつらと書いておきます。

ビルドツールインストール
$ sudo apt-get install build-essential default-jdk git curl autoconf unzip zip zlib1g-dev gawk gperf cmake

Android SDK・NDKダウンロード
$ wget http://dl.google.com/android/android-sdk_r21-linux.tgz
$ wget http://www.crystax.net/en/download/android-ndk-r7-crystax-5.beta2-linux-x86.tar.bz2

展開
$ tar zxf android-sdk_r21-linux.tgz
$ tar jxf android-ndk-r7-crystax-5.beta2-linux-x86.tar.bz2

アップデート
$ cd android-sdk-linux/tools
$ ./android update sdk -u -t platform,platform-tool
本家のNDKではなくcrystaxの方を使用します。文字コードの問題のようです。なので現状、NDKとしてのプラットフォームはAPI-14までのようです。

toolchain作成
$ cd android-ndk-r7-crystax-5.beta2/build/tools
$ ./make-standalone-toolchain.sh --ndk-dir=../../ --install-dir=$HOME/android-toolchain/android-14 --platform=android-14

Android 4.0(api14)以上を推奨しているようです。2.3(api9)でも動くようですけど。

鍵(証明書)ファイル作成
keytool -genkey -keystore ~/.android/debug.keystore -v -alias androiddebugkey -dname "CN=Android Debug,O=Android,C=US" -keypass android -storepass android -keyalg RSA -keysize 2048 -validity 10000
最後のビルドでこのファイルを使うようになっているので、この通りにやります。

XBMCソースダウンロード
git clone git://github.com/xbmc/xbmc.git xbmc-android
$ cd xbmc-android
$ git submodule update --init addons/skin.touched

XBMCコンフィグ
$ cd xbmc-android/tools/android/depends/
$ ./bootstrap
$ ./configure --with-sdk=$HOME/android-sdk-linux --with-ndk=$HOME/android-ndk-r7-crystax-5.beta2 --with-tarballs=$HOME/android-tarballs --with-toolchain=$HOME/android-toolchain/android-14

通ったらAndroid用ライブラリとかビルド
$ make -j20
数字はどんだけ並列処理するかという設定。READMEには「make -j20ってやっちゃえよ。平気だよ」と書いてありますが、非力なCPU/小メモリの場合は8以下位にした方が無難かも。一番無難なのはただmakeってやることでしょう。
最後に「Dependencies built successfully.」って出れば正常終了です。

XBMC for Androidのビルド
$ cd xbmc-android
$ make -C tools/android/depends/xbmc
問題なくこのビルドが終了すると、めでたく「 xbmc-android/xbmcapp-armeabi-v7a-debug.apk 」
というAndroidへインストール可能なapkファイルが出来上がります。


ビルド専用VMを作る

お勧めは、Oracle Virtual BoxとかでVMを作って、Ubuntu server 32bit版を入れて、ビルド環境を作るのがいいでしょう。後は、xbmc-android/docs/Android.README通りに実行することで難なくビルド出来るでしょう。
Oracle VirtualBox Download
Ubuntu Server Download (12.10は64bitしかないので、12.04 LTSの32bit版になりますね) 

VMスペックとしては、メモリ1GB、HDD16GB程度あればビルド環境としては大丈夫でしょう。


Ubuntu 64bitでビルドする(むりやり)

私が最初にやったことです。失敗も混ぜながら手順をつらつらと書いておきます。

32ビットライブラリインストール
64bitOSならばia32-libsをインストールせよとなってますが、Ubuntu-11.04の頃の話なのでしょう。
12.04だと
以下のパッケージには満たせない依存関係があります:
ia32-libs : 依存: ia32-libs-multiarch
E: 問題を解決することができません。壊れた変更禁止パッケージがあります。
というメッセージが出てインストールできませんでした。

ia32-libsの代わりに32bitライブラリを直接インストールしました。
$ sudo apt-get install libc6:i386 libstdc++6:i386
そして
$ make -j20

makeしたときに、32ビット版ライブラリが揃ってないと意味分からないエラーが色々出る
checking for -gcc... no
configure: WARNING: host was not specified. guessing.
checking for gcc... /home/nishiura/android/toolchain/android-14/arm-linux-androideabi/bin/gcc
configure: error: unsupported host ()
とか。gccあるのに何で動かないんだ?なんて、長い時間悩んでしまいました。

エラーが起きている箇所の一つ
xbmc-android/tools/android/depends/ncurses/armeabi-v7a/config.log
を見てみると
checking whether the C++ compiler works... no
configure: error: in `$HOME/xbmc-android':
configure: error: C++ compiler cannot create executables
See `config.log' for more details
make: *** [../../../..//libxbmc.so] エラー 7
というエラーが。

結局最初に入れたzlib1g-devの32bit版が必要だったようで
$ sudo apt-get install zlib1g-dev:i386
で入れました。が、入れる時に以下のような大惨事に。

@パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
libboost-filesystem1.46.1 libtorque2 libboost-date-time1.48-dev libboost-filesystem1.48.0
〜中省略〜
mpi-default-dev openmpi-common libboost-regex1.48.0 libboost-random1.48.0
libstdc++6-4.4-dev
これらを削除するには 'apt-get autoremove' を利用してください。
以下の特別パッケージがインストールされます:
libc6-dev:i386 linux-libc-dev:i386 zlib1g:i386
提案パッケージ:
glibc-doc:i386 manpages-dev:i386
推奨パッケージ:
gcc:i386 c-compiler:i386
以下のパッケージは「削除」されます:
build-essential cpp dpkg-dev g++ gcc gccxml grive libboost-python1.48-dev
libboost1.48-all-dev libtool pentium-builder
以下のパッケージが新たにインストールされます:
libc6-dev:i386 linux-libc-dev:i386 zlib1g:i386 zlib1g-dev:i386
アップグレード: 0 個、新規インストール: 4 個、削除: 11 個、保留: 0 個。
6,180 kB のアーカイブを取得する必要があります。
この操作後に追加で 6,524 kB のディスク容量が消費されます。
続行しますか [Y/n]?@
だって!パッケージマネージャの不具合なんじゃないだろうか・・・入れました!
そしたら、64ビット版のビルドツールが予告通り全部吹っ飛んでしまった!

何もできなくなったので、
$ sudo apt-get install build-essential
で再度インストール。zlib1g-dev:i386は削除されず残るみたい。pentium-builderだとだめみたい。
libncursesもないとだめらしい
$ sudo apt-get install libncurses5
更にこけたときに追加したもの
$ sudo apt-get install libboost-dev

原因を解消して再実行する際は、
$ make distclean
とかできれいにしてから再実行した方がいいようです。

途中こけたときに追加したもの(VM上でのUbuntu(32bit)上では不要だったんですが・・)
$ sudo apt-get install swig

$ make -C tools/android/depends/xbmc のところで
configure: error: C++ compiler cannot create executables
こんなエラーが出たとすると、たぶんxbmc-android/tools/android/dependsでのmakeも失敗していると思われます。
もう一度コンフィグあたりから見直してみましょう。

そんなこんなで何とか64bit環境でもビルドしてAPKファイルを作ることが出来ました。
もう2度とやりませんけど。


おまけ話
今後XBMC for Androidの開発が進むにつれて毎度ビルドに苦労しないためにビルド専用VMを作っておくのはいい方法ではないかと思います。自分はそうしようと思います。
また、XBMCを動かすAndroid端末自体もサーバーになりうるのでネットワークを固定IPにしました。

VMのコンソールで作業するのは辛いです。VMのネットワークをブリッジにして通常のLANの仲間入りをさせます。
ubuntuにはsshを入れて、sshdを動かしておきます。そして愛用のemacsもインストールします。
後は外部のsshターミナルからアクセスして作業する方が快適環境になります。
日本語がちゃんと出る。シェルウインドウサイズを自由に出来る(有料版だと出来そうだけど)。
これだけでもそうやる価値あると思います。

Androidにインストールして起動
起動させると、最初ちょっと待ってからXBMCのロゴが表示されて立ち上がりました。
MK802で動かしたら、パフォーマンスが25fps程度でもたつき感がありましたが、MK808では、60fps出てます。
XBMCから様々な方法(プロトコル)でメディアサーバーへアクセスが出来るので何でもありな感じです。操作感もサクサクでいい感じです。もっともっさりするかと思いました。

とにかく、外部キーボードで完全コントロールできるのがいいです。


課題のビデオリストですが、素の状態だとビデオファイル名がつらつらと並ぶだけなので他のメディアアプリ同様つまらないですが、ここからがXBMCのいいところで、nfoファイルっていうのを用意してあげると読んでくれてタイトルや概要、ジャンルとか設定できるらしいのです。

XBMC使う理由が出来たので、これから勉強です。
このあたりの機能に期待しています。希望通りに出来るかなぁ。

2012/11/23

MediaサーバーとMK808と外部キーボード(R600)


え~すでに価値のある情報ではありませんが、次のステップへの過程という意味で少し。

今MK808でビデオを視聴する環境整備に取り組んでおりますが、まずはUPnP/DLNAとかを考えるのが普通かなと思うので、一般に利用されているメジャーどころをやってみました。

自作地デジ録画サーバー上のビデオファイルをhtml5-videoとしてAndroidブラウザで視聴することはできますが、前記事のように思った結果にはなりませんでした。
今度はメディアサーバー(UPnP/DLNA)を稼働させて、Android(mk808)でメディアクライアントアプリで視聴する手段でやってみます。(結果は見えていたんですが・・・それは後で)

録画サーバー(Ubuntu)で稼働させるメディアサーバー
メジャーどころは、こんなところでしょうか。この中でUbuntuServerに入れられそうなのは、
MediaTomb、TVMOBiLi、uShare ですかね。これ以外は、デスクトップ(GNOME)環境が必要だったりでだめです。XBMCが動けば一番いいなぁと思ってますが、クライアント機能もあり、ディスプレイが必要なので断念。

やはり、お手軽なのは、MediaTomb ですかね。
セットアップについてはこちらで十分なので割愛します。
ビデオファイル保存フォルダをMediaTombにコンテンツ登録してサーバー側終了。

MK808用外部キーボード「Rii mini R600」購入

前記事で購入したキーボード(iPazzPort Mini)は個体差かもしれませんが、無線の有効距離がかなり短かったため、似たような別製品を購入しました。妻には「なぜ同じものを・・?」と呆れぎみ。
しかしその甲斐あってか、かなり離れても安定したマウス操作とキー入力が出来ました。

これもAirMouseなので傾けてマウス操作しますが、片手持ち(縦)のみです。iPazzPortは両手持ち(横)と切り替えが出来るので機能的には劣りますが、その代りクリックボタンだけが大きくなっていてマウス操作だけ評価すれば使いやすいと言えます。
キー入力はチャタリングもほとんどなく無線も安定しているので安心して打てました。キーの大きさも微妙ですが大きめなのでタイプミスも減るかと思われます。

リビングでソファーにねっころがって操作したいという要望にはこのコントローラで良さそうです。

視聴アプリ(UPnP/DLNAクライアント)

TVに繋いだMK808上で動かす視聴アプリを探すと沢山ありますね。
試したのは、BubbleUPnP、UPnPlay、および勝手に入っていたeHomeMediaCenterです。
どれでも普通にMediaTombからの再生は出来ました。しかし、出来ましたってレベルです。
結局だたのファイルリストから選んで再生ってだけなので、どれがどの番組なのかさっぱりです。

しかも、キーボード操作ではほとんど何もできない。AirMouseでの操作は結構つらいです。

Redmineで構築した視聴システムへブラウザからアクセスしてタイトルや内容、日時とかが分かって、番組検索が出来て、クリックしたら再生っていう流れとは大違いです。分かってはいますがね。

Androidがリブートする

これは大問題なんですが、ファームアップデートしたためなのか原因は不明ですがビデオ再生してからホームボタンクリックとかでアプリを終わらせるとAndroidがリブートしてしまうんです。
どの視聴アプリを使っても同じ現象でした。

視聴環境として選択肢がこれしかないなら、またOS周りの調査から取り組まないとならないでしょうけど、別手段を考えているので、今回の取り組みはこういう結果になったというところまでで手じまいです。

以下、個人的将来展望

やはり、ちゃんと日本語で番組名・内容・録画時間とかがちゃんとサムネイルとともにリスト表示されるようになって、再生はマウス操作なしでキーボード操作だけでコントロールできる環境が欲しいです。

タッチ操作で使いやすくという方向のアプリが主流な中でキーボードで使いやすくという方向のものは要望としては逆行しているんでしょう。でもいずれ来るであろうスマートTV時代でもタッチコントローラより従来通りのキーコントローラの方が受け入れられると思うんですよね。

それが既存ソフトの組み合わせで出来るならそれが一番です。まずはそういった模索をして、なさそうなら視聴アプリ作りますかね。

2012/11/17

MK808でadbが出来るまでの長い道のり

前回記事で書いた通り、MK808で色々遊んでみようと思い、いつも通りデスクトップにUSB接続して、自作アプリをインストールテストしようと思っていたんです。ところがどっこい、長い道のりでした。

まず、adbとは、Android Debug Bridgeという開発用便利ツールでして、Androidに色々アプリを入れなくてもPCからリモートで色んな操作が出来ます。adbはUSBまたはネットワーク越しでリモート操作が出来ます。
このUSBで接続ってところの話です。Android端末は数種類扱ってますが、今回は苦労しました。

ドライバセットアップ

Macだとこんなことしないでいいんですが、Windowsだと普通にUSB接続してもドライバがないと言われるんで、ハイハイってことで色々やります。まずは、Android SDKをダウンロードします。その中に、google-usb_driverというフォルダがあります。これを入れてやれば通常動きます。

が、このドライバのinfファイルにはNexus用の記述しか書かれていません。なので自分のMK808でインスール出来るように、android_winusb.infというドライバ記述ファイルに、以下のように書き加えます。
[Google.NTamd64]
;MK808
%SingleAdbInterface%     = USB_Install, USB\VID_2207&PID_0006&REV_0222
%CompositeAdbInterface%  = USB_Install, USB\VID_2207&PID_0006
USB_Install, 以降のコードは、デバイスマネージャで不明なデバイスとして認識されているデバイスのプロパティの詳細タブからプロパティ=デバイスIDを選ぶと表示される文字列です。
自分のWindowsは64Bitなのでamd64側にだけ書き加えました。

で、ドライバの更新でこのドライバフォルダを指定してやればインストールされます。
コンソールから
$ adb kill-server
$ adb usb
$ adb devices
ってやれば大体どんなAndroidでも接続できるもんです。もんです。あれ?
端末の方では、「USBデバッグが接続されました」と「USB接続」がちゃんと出ている。USBストレージをONにしてみる。ふむ。ちゃんとデスクトップでストレージマウント出来る。

adbだけが出来ない

マックでやってみた。同じ状態・・・
セルフパワーUSBハブかましてみた。駄目だった・・・
OTG側と電源用USBのさす順番変えたり、どっちかだけにしたり。変わらず・・・
ネット上探しても同じ状況の書き込みは見当たらない。微妙な不良品をゲットしてしまったか。

apk入れるだけなら別手段は色々ある。がっつり開発用ではないし。

microSD経由で入れる。
AirDroidとかサーバークライアント系のアプリ使って入れる。
Webサーバー上に置いておいてダウンロードして入れる。

とまあどんな方法でも入れることは出来るんで割り切ってもよかったんですが、心残りが出来るのも事実。次の手を考えます。

adb tcpip でいいか

冷静に考えれば、この端末はリビングTVに繋いで使うつもりなので、そもそもUSBでPCに接続するシチュエーションはめったにない。Wifi通して操作する方が合っています。

普通、Wifi経由のadbは以下の手順でできます。
  1. 一度USB接続して認識させる
  2. $ adb tcpip 5555 ってやってadbをusbからtcpipに切り替える。ポートも指定する。
  3. USBを抜く
  4. $ adb connect {device-ip-addr} ってやる。接続される。
Android側にadb-wifi切り替えアプリとか入れずとも、こんな感じでできますね。接続が切れてたら4だけやれば再接続されます。駄目だったら1からやり直します。

よく、adb connect {ip-addr}:5555ってやれって書いてあるけど、本当ですかね。
:5555:5555ってなって、失敗しますよね。:5555はいらないんじゃないかな。

普通はこれでいけるんですが、今回は最初のUSB接続で認識が出来ません。そのために、やはりAndroid側にWifiでADB出来るようにするアプリ使う必要があります。その手のものは沢山ありますね。でも大体どれもがルート権限が必要なのです!

自分のMK808はルート化されていなかったので

この辺でもう何だかなぁという気分ですが、始めてしまったので最後までやります。
ルート化されているFirmイメージを探しました。見つけました。
http://tabletrepublic.com/forum/downloads.php?do=file&id=164
他にもいくつか見つけましたが、どれもダウンロードするためにアカウント登録が必要だったり有料ダウンロードだったり・・・この手のものはそういう世界なので安全手軽なものを見つけるのは一苦労です。このファイルも突然別のところに移動するかもしれません。

ここからダウンロードしたrarパッケージはとてもいいですね。RKBatchToolと必要なドライバとフラッシュイメージがセットで入っていました。

沢山のネット記事で説明されている通り、リセットボタン押しながらUSB接続! 無反応・・・
はぁ。これもだめですか。普通はデバイスが認識されてドライバを入れる手順に進むのに。

microSDでアップデート

仕方がないので別手段です。USBストレージマウントはできるので、rarパッケージに入っているフラッシュイメージファイルを”update.img"という名前でmicroSDのルートにコピーします。(このパッケージのイメージファイルは最初からupdate.imgになってました)
Android側のストレージマウントをOffにします。するとその瞬間に、ファームイメージ見つけたけどインストールする?ってAndroidが聞いてきくるので、インストールします。

この方法は外部ツールいらずでお手軽&安全です。アプリや設定は保存されたままシステムだけアップデートされます。逆に言えばクリーンインストールは出来ません。

再度リセット押しながらUSB接続してみたら、今度はちゃんとPCが認識したので、改めてRKBachTool使ってクリーンインストールしました。単に気持ちの問題です。

これでルート化されたので、やっとルート権限が必要なアプリが実行できます。
だいぶ遠回りした気分ですが、これでtcpip経由でのadbが無事機能するようになりました。

eHomeMediaCenter・・・(余談です)

ファームアップデートしたらこんなアプリが入っていました。試しに動かしたらWindowsのメディアセンターやらマックにためしに入れていたTVMOBiLiとかがスパッと表示されてびっくり。
ビデオプレーヤーアプリ作る前に、ちょっとこっち方面をやってみようかな。

2012/11/10

MINITV/android(MK808) でビデオ視聴

以前の記事で購入したMK808がやっと到着した。AliExpressで購入。安く購入できたのがいいが、台湾を出るまでがめちゃめちゃ日数が掛かるのが難点だ。
MINITV/android Dual Core A9 Processorと表記されている。黒つや消しパッケージ。
なかなか、かっこいい。といっても、隙間に入れちゃうんで見えないんだけど。

本体をリビングのTVに繋いで、一緒に注文したキーボード「iPazz Port」を接続!

これでやっとソファーに寝そべって録画番組が見れると思いきや・・・

iPazz Portのワイヤレス電波範囲が思いのほか狭くて、若干前のめりに座って操作しないとマウス操作がまともに出来ない。更に、キーボード入力をその状態で打つと同じキーが何回もリピートして入ってしまう。1m以内くらいに近づけばだいぶ改善されるが、それじゃあねぇ。個体差かな。

MK802で使ってみたときはシチュエーションを考慮していなかったので気づかなかった。

キーボードを、K700の方を使えば上の問題は解決と言えば解決だけど、リビングではビデオリモコンのように片手操作したいのだ。

とにかくビデオ視聴(HTML5 videoをAndroidのブラウザで)

気を取り直して、とにかく我が家の地デジ録画サーバーをブラウザでアクセス。録画番組を観てみる。やはり以前のMK802より若干レスポンスがいい気がする。メモリ倍だしCPUもA9だしね。でも思ってたほどじゃないかな。GPUパワーが低いんだろうか。
画質は問題なしだが、PCやMacで観てるときは感じないカクつきがたまにある。ビデオのビットレートをもう少し落とした方がいいかもしれないと感じた。

MediaElements.jsも問題なくAndroidで動作しているようだが、問題はキー操作

これは残念な結果となった。The State Of HTML5 VideoのAccessibilityによると、キーボード操作はiOS/AndroidともN/Aとなっている。これは不可能という意味だけど、外部キーボードを接続すれば大丈夫じゃないの?って思ってたが・・

  • フルスクリーン状態ではどのキーも反応しなかった。
  • 一度ビデオ部分をマウスクリックしてスペースキーを2回押すとポーズがかかった。
  • 音声ボリューム操作の上下キーもたまに反応した。
  • 一番やりたかった左右カーソルキーでの送りと戻しが全然反応しなかった。

キーコードが違うんじゃない?と思って、アンドロイドのキーコード表のコードをMediaElements.jsのキー操作に加えてみたけれど、無反応だった。

ビデオ視聴中に、アルファベットキーを打つと、IMEウインドウが出て来た。
むむ。ようするにビデオ再生部分にキーボード入力イベントがフォーカスしていない感じだ。だから一度マウスクリックした後のキー操作がたまに動くんだろう。

自分なりの結論

HTML5+Video+Android+Keyboard は無理っぽい。
じゃあアプリ使うか。MX Playerで視聴してみる。いい感じ。キー操作も出来る。
だけどいちいちURL打つのは現実的じゃない。ブラウザから起動するか?
プレーヤーアプリがブラウザ起動を許す仕様になっているか?スキーマは?

ブラウザ起動アプリにする方法はたくさん情報があるけど、GooglePlayからのアプリがそうなっているっていう情報はどこにもないんで手出しできない。

・・・
自分で作るかな
Flashのswfだとブラウザに依存してしまうんで、独立したアプリにする必要があるだろう。
幸いなことに今はAndroidのビデオプレーヤーアプリを作るのはそう難しくない。
Adobe AIRを使えばいいだろう。

・・・
XBMCサーバーとか?
XMBCサーバーいれて、AndroidにはXMBC Remoteで観るとかかな。
UPnPとかDLNAとかにして、Android側でアクセスできるようにするか。

やった事無いけど多分出来そうだ。でも、番組名とかがちゃんと出ないよねきっと。

次回から、この辺の話に移っていく感じでしょうかね。

2012/11/08

自作redmineプラグインをjquery+ajax対応にする

select_tagでのajax処理をjqueryに対応させる

redmine-2.1.xでやっとjquery対応になった。自作のredmine_videoプラグインも若干ながら'prototype.js'の機能を使用しており、そのままだとエラーになってしまう部分があるため、修正した。

edit/index.html.erb
ビデオ簡易編集のビュー内でremote_functionを使っていた部分を、jquery + ajaxに変更

編集モード切替select_tagのonChangeでフォーム内容を部分的に切り替える処理をremote_functionでやっていた以前のコード

<%= select_tag :mode,
    options_for_select([["Delay Time",0],["Clip Time",1]],@mode),
    :onchange => remote_function(:url => {:action => :select_mode}, :with => "'mode='+this.value+"+@remote_params) %>

これを、以下のように変更


<%= select_tag :mode, options_for_select([["Delay Time",0],["Clip Time",1]],@mode), :params => @remote_params %>

このままでは何もしてくれないので



<!--%= javascript_include_tag 'edit-ajax.js', :plugin => 'redmine_video' %-->
<%= javascript_tag do %>
$(function(){
    $("#mode")
        .change(function(){
            var params = "mode=" + $(this).val() + $(this).attr('params');
            $.ajax({
                type: 'POST',
                url:"<%=url_for :action => :select_mode %>",
                data: params,
                dateType: "html",
                success:function(html){
                    //alert("result=" + html);
                    $('#attr').html(html);
                }
            }); // ajax
        }) // change.function
}); // jquery.function
<% end %>

というjquery+ajaxコードを追加。コメントにしているように外部ファイルにしてインクルードしたかったけど
そうするとなぜか動作してくれなかった。なんでかな?


':disable_with'もちゃんと動くようになった。
<%= submit_tag "実行", :name => "submit_execute", :data => {:disable_with=>"実行中"} %>

:confirmも動くようになった。
<%= submit_tag "保存", :name => "submit_save", :confirm => "Are you sure?" %>
<%= submit_tag "消去", :name => "submit_clear", :confirm => "Are you sure?" %>

対応版 edit/index.html.erb

<h2>ビデオ簡易編集</h2>

<%= form_tag :action =>'index',:id=>@issue.id, :result=>@result, :build=>@build do %>
<%= select_tag :mode, options_for_select([["Delay Time",0],["Clip Time",1]],@mode), :params => @remote_params %>

<div id='attr'>
<%= render :partial => 'attr' %>
</div>

<table>
  <tr><td>
    <%= submit_tag "実行", :name => "submit_execute", :data => {:disable_with=>"実行中"} %>
    <%= submit_tag "アップデート", :name => "submit_update" %>
    <br>
    <%= video_tag get_video_filename, :controls => true, :size =>"480x270" %>

    <% if @build %>
    <% if @build=='ok' && @result %>
    <td>
      <%= submit_tag "保存", :name => "submit_save", :confirm => "Are you sure?" %>
      <%= submit_tag "消去", :name => "submit_clear", :confirm => "Are you sure?" %>
      <br>
      <%= video_tag @result, :controls => true, :size =>"480x270" %>
      <% end %>
    <td><%= link_to @build, "/jenkins/job/edit_#{@issue.id}" %>
      <% end %>
</table>

<%= javascript_tag do %>
$(function(){
    $("#mode")
        .change(function(){
            var params = "mode=" + $(this).val() + $(this).attr('params');
            $.ajax({
                type: 'POST',
                url:"<%=url_for :action => :select_mode %>",
                data: params,
                dateType: "html",
                success:function(html){
                    //alert("result=" + html);
                    $('#attr').html(html);
                }
            }); // ajax
        }) // change.function
}); // jquery.function
<% end %>

コントローラの方も若干直した
controller: "edit", action: "select_mode"

# ajax partial render
  def select_mode
    get_params
    init_params
    remote_params
    render :partial => "attr" 
  end

前はよくわからないまま、renderの部分を


render :update do |page| page.replace_html "attr", :partial => "attr" 
end

ってやっていた。このままでも動くようだけれど、すっきりしてる方がいい。


引っかかったのは、jquery.ajaxを理解するところ。実際理解できたのかも分からないけど、自分なりの解釈はこうだ。

$("#mode").change(function({・・・
select_tagには、form_tagのように:remoteは効かない。よってjqueryでトリガーさせる必要がある

$.ajax({・・・
で、select_modeアクションへポストする

success:function(html){
で、結果を取得する

$('#attr').html(html);
で、部分的な更新を行う

多少納得いかないけれど、大体こんな感じなのでしょうか。
これで、ページ更新を必要としない非同期通知も出来そうなので、ビデオ再生制御情報をサーバーに送ることが出来るかな。

普通のフォームコミットのajaxとかだったらもっとすっきりするんだろうけど、今回のようなselect_tagでってなると、いきなり面倒なことになるね。
でもテンプレートとイベント処理を完全に分離できるところが実際の仕事では実用的な気がする。

とにかく、これで自作ビデオプラグインがjquery対応に出来たので、redmineを2.1.2に切り替えた。

2012/11/05

Android Mini PCとGoogleTVキーボードで視聴する

地デジ録画システムで録画したビデオをリビングで快適に視聴するために

ということで、久しぶりにいくつかアイテムを購入しました。
まだ肝心なものが届いていなかったりするんですが、思惑と、途中経過+α情報です。

前置き
AppleTV(3rd)をリビングTVに繋いでますが、ほとんど使っていない状況。
なぜならブラウザが載ってないから。JBも今だ出来ないようだから。見切りをつけました。

別手段として、Android Mini PCを使うことにしました。
ソファーに寝そべって操作したいので、USBワイヤレスキーボードも用意します。

キーボード
  手のひらサイズのメディアコントローラキー付きミニキーボード
  ジャイロ内蔵でマウス操作は本体を傾けて行う。

  普通サイズのキーボードにマウスパッドとメディアコントロールキーが付いているやつ
  K700というモデルです。K400というフルキーボードのみもありますが、K700の方が安いんです。

どちらも英語キーボード。自分は元々英語キーボードしか使えない体質なのです。
K700の方は日本では販売されていないようです。
逆輸入業者を通すととっても高いので、私は以下で購入しました。


中国のオンラインショップでございますが、amazonに比べて一割程度安いです。
日本への発送も無料です。ただし届くまですごい日数がかかります。
スピードアップするにはDHLを選択することもできますが、千円以上の送料が係ります。
2,3千円の商品にそれはいやなんで。15日くらいかかりましたが無事到着しました。


USB無線モジュール付いてないんじゃ?って不安でしたが両方ともちゃんと付いてました^^;

Android Mini PC
これも流れでAliExpressからの購入です。自分が購入したときは$72.00でしたが、今見ると買えなくなっていました。品薄なのか、$82.00に値段が上がっているページへのリンクになってました。
上のモデルは「MK808」と言われているやつです。色んなメーカーから出てますね。

残念ながら、これがまだ到着していません・・・
が、MK802は所有しているので、そっちに接続してテストしてみました。

キーボードはどちらか使いやすい方をと思って2種類買いましたが、どちらも甲乙つけがたいくらい使えます。iPazzPortのジャイロマウスは慣れるまでちょっと・・・ですが30分くらいで慣れました。
スマホくらいの大きさでデザインもいいのでリビングに置いてあっても違和感がないです。

MK802あるのになぜまたMK808を買ったか?なぜならMK802は遅いんです。AndroidOS2.3くらいがちょうどよいハードに無理やり4.0乗せちゃった感じです。なので期待を込めてMK808を購入しました。巷の評判は良いようですし。

ついでに日本語入力について

ネットでは日本語キーボードを接続してキーマップ変更して・・っていう話がよくありますが
素直に英語キーボードを使うのがいいと思いますね。キーの数も少ないので1つのキーが大きいしシルクも英数字だけなのですっきり。日本語入力も特に苦労しません。ただIMEはちょっと選んだ方がいいでしょう。

無料のメジャーIMEは、Shimeji、Google日本語入力、OpenWnn、AltIMEってところでしょうか。その他いろいろありますがフリック操作とかソフトキーボードスキンとかが違うだけかな。
これらの中で使いやすいのは、OpenWnn系だと思います。

なぜかというと、全角・半角(英数字)切り替えを、Shift+スペースキーで行えるからです。
Google日本語入力はキー操作での切り替えができないようなので却下です。

日本語入力状態から完全に抜けるにはESCキーを押すっていう手もありますね。
再度日本語入力にするにはテキスト入力するフィールドでクリック(タップ)します。
この方法ならGoogle日本語入力でもいいでしょうけど、日本語入力モードじゃないのに間違えてESCキーを押すとアプリが閉じちゃうんで危険ですね。クリック(タップ)するのも面倒です。

ということで自分はOpenWnn Plusを使用しています。Plusとしての機能は物理キーボードには不要なものなのですが、今気軽に選べるのがこれなので。
AltIMEもベースはOpenWnnなので、同じ操作が可能ですが、全角半角モードが視覚的によくわかりません。OpenWnnPlusはIMEパネルに常時表示してくれるので分かりやすいです。

ここに書いても仕方ないけれど・・・
日本の量販店のみなさん。普通に英語キーボードを陳列してください。
マニアックな店に行かなくても、海外サイトを利用しないでも済むようにしてください。

MK808が届いたら(届くかなぁ・・)ビデオ視聴環境のテスト再開予定。
MediaElements.jsとの相性も気になります。

2012/10/27

redmineを2.0.3から2.1.2へ切り替える

独自地デジ録画システムの根幹として使用しているRedmineを2.0.3から2.1.2へ切り替えることにしました。2.0.3でも特に不便はなかったんですが、jquery+ajaxな処理を色々やりたくなったので。

えーと、本家に説明されているような手順とは若干異なります。
自分の場合、自作プラグインの対応もあるので、2.0.3と2.1.2を暫く共存させて、プラグインの対応が終わったら切り替えるという手順です。

普通の場合でも、この方が無難なのでは?と思います。
会社でもいくつかのプラグインを入れていて動作確認してからじゃないと公開したくない。
同じサーバーマシン上で同居させてテスト運用してから切り替える方が気が楽というものです。

自分のredmine入れ場所は、/opt です。
いうまでもなく、OSは、ubuntuです。
テキストファイル編集は、emacsを使用してます。(ところどころ出てきます)
ということを踏まえたうえで参考にしてください。

redmine-2.1.2 インストール

$ wget http://rubyforge.org/frs/download.php/76495/redmine-2.1.2.tar.gz
$ cd /opt
$ sudo tar zxf ~/redmine-2.1.2.tar.gz
$ sudo chown -R www-data.www-data redmine-2.1.2
$ cd redmine-2.1.2
$ sudo bundle install --without development test postgresql sqlite rmagick

データベースコピー

$ cd (どこでもいいですね)
$ mysqldump -uredmine -predmine redmine > redmine2.sql
$ mysql -uredmine -predmine
create database redmine2
exit
$ mysql -uredmine -predmine redmine2 < redmine2.sql
$ rm redmine2.sql


バックアップというよりは、2.1.2用のDBを作成して2.0.3のDB内容をコピーするって感じです。


設定

$ cd /opt/redmine-2.1.2
$ sudo cp config/database.yml.example config/database.yml
$ sudo emacs config/database.yml
production:
adapter: mysql2
database: redmine2
host: localhost
username: redmine
password: redmine
encoding: utf8
データベースを'redmine2'を使うようにしておきます。

適応

$ sudo rake generate_secret_token
$ sudo rake db:migrate RAILS_ENV=production

apache2/passenger

$ sudo emacs /etc/apache2/conf.d/redmine.conf
RailsBaseURI /redmine2
$ sudo ln -s /opt/redmine-2.1.2/public /var/www/redmine2
$ sudo service apache2 reload

この辺は人によって色々でしょう。とにかく新旧両方へアクセスできるようにしておきます。

オプション

htmlタグ解禁
lib/redcloth3.rbのALLOWED_TAGSに解禁したいHTMLタグを羅列するだけ。

$ sudo emacs lib/redcloth3.rb

#    ALLOWED_TAGS = %w(redpre pre code notextile)
    ALLOWED_TAGS = %w(redpre pre code notextile html head body title isindex base meta link script h1 h2 h3 h4 h5 h6 hr br p center spacer div blockquote listing xmp address noscript span font basefont i tt s b u strike big small sub sup em strong code samp kbd var dfn q cite ul ol li dl dt dd table tr th td caption a img map area form input select option textarea applet param frameset frame noframes &quote &amp &lt &gt &nbsp &copy)

自分はこんな感じにしています。

farend_basicテーマ

自分は2.0.3で使用していたので、単純にコピーしました。
$ sudo cp -r /opt/redmine-2.0.3/public/themes/farend_basic /opt/redmine-2.1.2/public/themes/farend_basic
新規に取得する場合は、https://github.com/farend/redmine_theme_farend_basic/downloads から。

redmine_video プラグイン インストール (自作なので自分だけですが)

$ cd /opt/redmine-2.1.2
$ sudo cp -r /opt/redmine-2.0.3/plugins/redmine_video plugins
$ sudo ln -s /opt/videos public/videos
$ sudo rm -rf files
$ sudo ln -s /opt/videos files

redmine-2.1.2/Gemfile.log がrootになってしまい、パーミッションエラーがでたので・・・
$ sudo chown www-data.www-data Gemfile.lock

プラグインのマイグレーションはデータベースを完全コピーしているので不要です。


一応これで、入るには入って、Redmine自体は正常起動しましたが、prototype.jsなメソッドを使用しているビューでエラーになりました。これからこの辺を色々jqueryなものに書き換えていく作業です。


プラグインとか、ちゃんと動くようになったら

config/database.yml のdatabase:を元々の’redmine'に変更
$ sudo rake db:migrate RAILS_ENV=production
$ sudo ln -s /opt/redmine-2.1.2/public /var/www/redmine

こんな感じの手順を踏むだけで、切り替え完了です。

2012/10/04

MediaElement.jsのキー操作でCMスキップ

ここしばらく、録画されたビデオを視聴するってことに取り組んでいる。前回記事ではMediaElement.jsを組み込んで動くところまでは確認した。

今回は、やりたかったことの一つ、CMスキップについて

その前に、その他のビデオプレーヤーフレームワークはどんなもんだろうか?と思って HTML5 Video Player Comparison を参考にいろいろ試してみた。
JWPlayer、LeanBackPlayer を入れて動かしてみたけれど、MediaElement.jsみたいにFirefoxで簡単に見れるようにしてくれなかった。その時点でパスって感じ。メンドクサイのはいやなのです。
でも、LeanBackPlayerは普通の使い方としてはインプリメントも簡単で悪くない感じだった。

キーアクションにコマンドを割り振る

よくカスタムコントローラを作るっていう記事やサンプルがあるけれど、HTMLページに操作ボタンを付けたところで、フルスクリーンで視聴すると操作できないじゃん。
カスタムコントローラにカスタムボタンを追加することが出来ればいいんだけどね。これはこれで可能なのか?も含めて調べ中。
今はとにかく一番簡単確実な組み込み方法として、キーボードに機能を割り振ることでやってみる。

で、どうやるの?
MediaElement.jsのサイトでもサンプルでもキーアクションについての記述がどこにもない。
Player Options で、keyActions: [] って書いてあるだけ。仕方がないので(っていうかそうすべきなんだろうけど)JavaScriptソースを読んで、理解する。

標準キー操作
  • [SPC]  プレイ・ポーズ切り替え
  • [UP]   サウンドボリュームアップ
  • [DOWN] サウンドボリュームダウン
  • [LEFT]  5%先送り
  • [RIGHT] 5%後送り
  • [’F’]   フルスクリーン切り替え
こんな操作ができるようになっていた。おおっ。既にシークが自分がアサインしたいキーに振られているじゃないですか。一瞬困ったけど、素晴らしいことにプレイヤーオプションの方で、どのくらいシークさせるかを定義する関数が独立しておりました。
デフォルト設定は、

                // default amount to move back when back key is pressed
                defaultSeekBackwardInterval: function(media) {
                        return (media.duration * 0.05);
                },
                // default amount to move forward when forward key is pressed
                defaultSeekForwardInterval: function(media) {
                        return (media.duration * 0.05);
                },
となっている。media.duration(全体の長さ)の5%っていう簡単な関数。これを上書きしてあげれば良さそう。

ビューの最後を以下のように変更

前回記事
<%= javascript_tag "$('video').mediaelementplayer()" %>
としていたところを、
<%= javascript_include_tag "mediaelement_conf.js", :plugin => 'redmine_video' %>
に変更。HTMLにJavaScriptコードを長々と書きたくないから。

assets/javascripts/mediaelement_conf.js
$('video').mediaelementplayer({

    iPadUseNativeControls: true,

    defaultSeekBackwardInterval: function(media) {
        return 15;
    },
    defaultSeekForwardInterval: function(media) {
        return 15;
    },
});

プレイヤーオプションの中にその関数を書いてオーバーライドする。これでLEFT、RIGHTキーで15秒スキップが出来るようになった。もちろんフルスクリーン状態でも大丈夫。
ただし、HTML5ビデオネイティブの時だけ。すなわちFirefoxでSilverlightプラグインで視聴しているときは効かないようだ。しょうがないけどちょっと残念。

GoogleTVコントローラもいけそう

ソースを読む限りだと、これらのキー操作がGoogleTVコントローラコードからも機能するように書かれている。ということは、Android端末でGoogleTVコントローラ使って自分の録画ビデオの再生制御が出来そうってことか。

カスタムキーアクション追加

keyActions:
[
 { keys: [キーコード,キーコード,・・・], action: function(player, media) {処理色々・・} },
 { keys: [キーコード,キーコード,・・・], action: function(player, media) {処理色々・・} }
]
って感じでmediaplayer()オプションのkeyActionsへ追加して行けば、どんどん自分のカスタム処理をキーに割り振っていける。

ブラウザのコントローラカスタマイズに拘らずにキーボードアクションの方が使い勝手がいいかもね。


今の悩みどころ

MediaElement.jsは使い勝手が良くてとってもいいんだけれど、jquery.jsを必要とする。他のフレームワークもほとんどがjquery依存のものが多い。
だけれど、redmine2.0.3はまだjqueryベースじゃないのだ。
幸いなことにprotptype.jsと混在していてもこのフレームワークの動作には問題ないらしい。

一番困るのがajaxだ。従来通りの記述で表現してみても、jqueryな書き方してみても、どちらもajaxによる非同期処理が機能しなくなってしまうのだ。

本来、rails3 + jqueryでもajaxは問題ないはず。だけれどもredmine2.0.3はrails3だけれどjqueryベースじゃないためか動作がわけわからない感じになる。

試しにjquery-railsをgemインストールして、プラグインのGemfileに'jquery-rails'を追加してみたけれど、希望通りに動いてはくれなかった。jquery.jsをインクルードしなければ昔通りのremote_functionとかはちゃんと動く。インクルードしなければね。MediaElement.jsは動かないけどね。本末転倒。

ここら辺でやりたいことは、再生イベントからフォームコミットをしてリモート(ajax)でサーバーへ送信したいのだ。出来ればページ更新は一切せずに非同期で行ったきりにさせたい。

自分はビデオ操作情報をサーバーへ送りたいだけなんだけど。なぜにそんな簡単そうなことがこんなに難しい・・・素人にはお手上げ気分。

この辺の実装をするには、最近リリースされたredmine2.1.2なら大丈夫なのだろうか?
話によると、redmine2.1.xはやっとjqueryベースになったということらしいけど。
もうしばらく今の状態を楽しんだら、やってしまおうかな。


2012/09/27

MediaElement.jsでビデオ視聴

MediaElement.jsをredmine上のビデオ視聴プラグインに組み込んでみた

初めに思ったのは、まだ見てないやつとか見たやつとか分かるように出来ないかな?ってことでした。ビデオの再生ボタンを押したことを検出して、データベースに視聴済みと記録できないかな?って。
更には、どこまで観たかを記録できれば途中再生もできるよねとか、ビデオコントローラをカスタマイズして15秒スキップとかできればCMスキップも楽だよねとか、視聴する部分の拡張をしてみたいと。

上記のようなことは、まだまだ出来そうにないんですが、まずはそういったことがやりやすそうなフレームワークを探してみたところ、有名どころで Video.jsとMediaElement.js がありました。
どちらもコントローラのカスタマイズとかイベントAPIとかを持っていて色々出来そう。

これらのドキュメントを読んでみると、どんなブラウザでも再生できるようにすることが目的になっているようです。そういえば自分のシステムでは素のHTML5のvideoタグでソースはH264だけなのでFirefoxとかでは観れない。
このフレームワークを使用するとH264だけでもFirefoxで見れるようになると。
うーん素晴らしい。
どうして見れるんだ?ってことですがH264を再生できないブラウザでは自動的にプレーヤープラグインを通して再生するってことらしい。そのためのプラグインが内包されています。

Video.js

簡単そうだったので、初めはこっちを使ってみましたが、ドキュメント通りにやってみて、CDNホストバージョンだろうがセルフホストだろうが、Firefoxで視聴できるように出来ませんでした。
セルフホスト用をダウンロードすると、video-js.swfというのが入ってるんで、勝手に使ってくれるんだよね?って思っていましたが、甘かったようです。よくよく読んでみるとH264だけで大丈夫とは書いてない・・・
ということで、さようならです。

MediaElement.js

結果から言うと、特に色々やることなくFirefoxでは勝手にブラウザプラグインが使われて、ソースがH264だけでも再生してくれました。更にビデオイベント取得やビデオオブジェクト操作APIや、何やらカスタムコントローラにボタン追加したり出来てしまうらしい。冒頭に書いたようなことが出来そうな可能性を感じます。

ダウンロード

こちらは、フリーCDNホストはないので、パッケージをダウンロードして使用します。
$ wget http://github.com/johndyer/mediaelement/zipball/master
ってやるとダウンロードされますが、ファイル名が'master'になっちゃいます。でも気にせず
$ unzip master
で解凍して'master'はポイです。

中身を覗いてみると、/buildにフレームワーク一式。デモサンプルのHTMLとメディアファイル、Silverlight用とFlash用のプラグインのビルドファイルとソースコード一式がどっさり入っておりました。


視聴システムへの組み込み

視聴部分はredmineプラグインで構築しているので、言うなればRailsアプリへの組み込みってことになります。ビデオ視聴プラグインの一部として組み込みたいので、必要なファイルをそっちへ移動させます。
ここでは、ビデオ視聴プラグインのフォルダを、/redmine_videoとして記述しておきます。

JavaScriptとプラグインをassets/javascriptsへコピー
MediaElement/build/*.js => /redmine_video/assets/javascripts
MediaElement/build/flashmediaelement.swf => /redmine_video/assets/javascripts
MediaElement/build/silverlightmediaelement.xap => /redmine_video/assets/javascripts

cssとイメージファイルをassets/stylesheetsへコピー
MediaElement/build/*.css => /redmine_video/assets/stylesheets
MediaElement/build/*.png => /redmine_video/assets/stylesheets
MediaElement/build/*.gif => /redmine_video/assets/stylesheets

Railsではassetsフォルダの中をjavascripts/stylesheets/imagesに分けることになってますが、MediaElementはフラットなので、このような分け方にしました。全部assets/javascriptsに入れてしまっても動くでしょうけどrailsでシンプルに書きたいんで。

redmineを再起動すると、redmine_video/assetsが、redmine/public/plugin_assets/redmine_video/へ自動コピーされて使用できる状態になります。

最初なぜかredmine/public/plugin_assets/redmine_video/がルートパーミッションになってしまっていてコピーでエラーになってしまい、ちょっと悩みました。なんで?誰がやった?自分??原因不明。

ビュー作成

視聴ビューとしていた以前のplay.html.erbは以下のようなものでした。
<h2><%= @issue.subject %></h2>

<% video = @issue.attachments.detect {|a| a.content_type == "video/mp4"} %>
<% thumb = @issue.attachments.detect {|a| a.content_type == "image/jpg"} %>

<%= video_tag video.disk_filename, :id => "video",
    :poster => image_path("/videos/"+thumb.disk_filename),
:autoplay => true, :autobuffer => true, :controls => true, :size => "960x540" %&gt\
;

これを、MediaElement.jsのデモサンプルやらサイトやら色々物色して、不要なものをすべてそぎ落として、Railsで書き直したら以下のようになりました。

<% content_for :header_tags do %>
  <%= javascript_include_tag "jquery.js", :plugin => 'redmine_video' %>
  <%= javascript_include_tag "mediaelement-and-player.min.js", :plugin => 'redmine_video\
' %>
  <%= stylesheet_link_tag "mediaelementplayer.min.css", :plugin => 'redmine_video' %>
  <%= stylesheet_link_tag "mejs-skins.css", :plugin => 'redmine_video' %>
<% end %>

<% video = @issue.attachments.detect {|a| a.content_type == "video/mp4"} %>
<% thumb = @issue.attachments.detect {|a| a.content_type == "image/jpg"} %>

<%= video_tag video.disk_filename, :poster => image_path("/videos/"+thumb.disk_filename)\
,
:type => "video/mp4", :size => "960x540" %>

<%= javascript_tag "$('video').mediaelementplayer()" %>

最後のmediaelementplayer()が全て色々よろしくやってくれるようです。
この関数はイベントリスナーやら数あるオプション設定やらいろいろカスタマイズできます。

ここまで到達するのに悩んだことがいくつかありました。
  • content_for :head じゃなくて、content_for :header_tagsだった。
    railsのドキュメントには:headって書いてあるんだけどなぁ。かなり悩んだ。
  • javascript_include_tagやstylesheet_link_tagに、:pluginオプションが必要だった。
    railsドキュメントではなく、redmineプラグインドキュメントを読むべきだった。
    これにより、プラグインのassets以下を指定したことになるらしい。
  • video_tagには、:type => "video/mp4"が必要だった。
    これがないとMediaElement.jsが全く機能しなかった。
    MediaElement.jsのセットアップで、サイトにAddTypeでMIMEを追加せよと書いてあったけど、やっていない。もしかしたらそのため? でもこれで動いたんで大丈夫。
これで、やりたかったことの一つ、Firefoxで視聴できるようになりました。
実際にやってみると、付属のSilverlightプラグインを通して再生されているようです。

次回は、もう少し突っ込んだ使い方を書いてみたいと思ってますが、実はなかなかうまくいっていません。よくわからない状態継続中です。また暫く間が開くかもです・・・

2012/09/12

Jenkins新規ジョブに所要時間を設定する

Jenkinsのジョブは繰り返しビルドされることが前提。2回目からどれくらいで終了するかプログレス表示される。これが精神衛生上とってもいいわけだけど、前回の結果が分からないと表示されない。

録画システムで使用しているJenkinsのビルド所要時間を最初に設定できないかな?

録画ジョブは性質上1回実行したらお役御免。同じ日時の2回目はない。だけど、どれくらいで終了するかを表示させたい。どうにか出来ないかな?ということでやってみた。

思惑
録画時間は分かっている。エンコード時間も予測できる。時間予測は可能だ。
Jenkinsが過去にビルドした情報として扱ってくれれば出るはず。

所要時間予測
今までの実績から算出すると録画時間に対しての所要時間は2.2倍程度かな。
番組時間が30分程度の場合は1.7〜2.2位、2、3時間の場合は2.2〜2.4位。
単純に、ビルド予想時間=番組時間 * 2.2 ってことでいいかな。
沢山のサンプリングデータから統計を取って非線形計算が出来れば精度は上がるだろうね。

ビルドデータ
#{JENKINS_HOME}/jobs/#{ジョブ名}/builds/#{ビルド日時}/build.xml
に、このビルドについての情報が記述されている。この中で重要なのが、durationタグだ。
Jenkinsは前回のビルドのこの情報をもとにメーター表示しているようだ。
その一例
<?xml version='1.0' encoding='UTF-8'?>
<build>
  <actions>
    <hudson.model.CauseAction>
      <causes>
        <hudson.triggers.TimerTrigger_-TimerTriggerCause/>
      </causes>
    </hudson.model.CauseAction>
  </actions>
  <number>1</number>
  <result>SUCCESS</result>
  <duration>3977984</duration>
  <charset>US-ASCII</charset>
  <keepLog>false</keepLog>
  <builtOn></builtOn>
  <workspace>/var/lib/jenkins/jobs/record_14147/workspace</workspace>
  <hudsonVersion>1.424.6</hudsonVersion>
  <scm class="hudson.scm.NullChangeLogParser"/>
  <culprits/>
</build>

適当なジョブを作って、適当なビルド結果をコピーしてどうなるか試してみた。
Jenkinsのシステム設定からリロードをしたら、情報を拾ってくれて反映した。
色々やって、最低限必要な情報のみにしたbuild.xmlは以下のようになった。
<build>
 <number>0</number>
 <result>SUCCESS</result>
 <duration>#{duration}</duration>
</build>

ビルド番号は0番でいけた。最初のビルドが1番っていう約束事が崩れないで済みそう。

APIじゃ出来なそう
ビルドディレクトリとファイルをあらかじめ作成してしまえばいいわけだけれど、REST-APIじゃ出来ないみたい。Jenkinsプラグインなら?ってちょっと調べたけど、ジョブ作成フックみたいな切り口がないようで断念。Redmineのチケット編集フックみたいに出来ればよかったんだけれど。

パーミッションをどうするかなぁ
Jenkins側を拡張する方法がだめっぽいので外部タスクで実行することになる。そうすると、その辺のディレクトリパスはJenkinsユーザだぜっていうパーミッションが絡んでくる。選択肢はいくつか考えられる。

1. Jenkins側をを実行タスクと同じユーザにする
この場合、redmine(rails)ユーザと同じwww-dataにするってことか。

Jenkinsが扱うパス/ファイルオーナー’をwww-dataにする。
/etc/default/jenkinsファイルのJENKINS_USERをwww-dataにする。

2. 実行タスク側をJenkinsと同じjenkinsにする
Redmine(rails)実行ユーザをJenkinsにするってことか。

redmineが扱うパス/ファイルオーナーをjenkinsにする。
結果、passengerを通してredmine(rails)が、redmine/config/environment.rbのオーナーで実行されることになる。

もしくは、redmine/filesとredmine/public/videosオーナーをjenkinsにして
apacheのpassengerオプションで
PassengerUserSwitching off
PassengerDefaultUser jenkins
を追加記述する。

3. Jenkinsをソースからいじって、誰でも書き込めるようにする
これはだめでしょ。

4. ビルド#0を作成する処理をJenkinsジョブにしてリモート実行する
これかなぁ。

1と2は簡単だし普通はこれかなぁと思うけど、システム依存だし保守性(オリジナルやデフォルトを極力変更しない)が落ちてしまうなぁ。3は可能だけど論外。最後の4の方法でやることにした。

hudson-remote-apiを使ってjob作成&実行するrubyスクリプト
/opt/task/prebuild.rb
#!/usr/bin/ruby                                                                                     
# -*- coding: utf-8 -*-                                                                             

require 'hudson-remote-api'

# Jenkins                                                                                           
Hudson.settings = {:url => 'http://localhost/jenkins', :crumb => false }

def create_estimated_build record_job, record_sec

  # estimated duration                                                                              
  duration = (1000 * record_sec * 2.2).round

  # build datetime                                                                                  
  build_name = Time.now.strftime("%Y-%m-%d_%H-%M-00")

  puts "create_estimated_build: #{record_job} #{record_sec} #{duration} #{build_name}"

  build_dir = "/var/lib/jenkins/jobs/#{record_job}/builds/#{build_name}"
  build_xml = "<build><number>0</number><result>SUCCESS</result><duration>#{duration}</duration></build>"

  # create estimated build                                                                          
  Dir::mkdir(build_dir)
  xml = File.open(build_dir+"/build.xml",'w')
  xml.puts build_xml
  xml.close

  # reload jenkins                                                                                  
  system("curl %s/reload" % Hudson[:url])
end

# MAIN START                                                                                        

abort "no arguments" if ARGV.size<2
job_name = ARGV[0]
rec_time = ARGV[1]
create_estimated_build job_name, rec_time.to_i
build#0として必要最低限の情報を記述したbuild.xmlを今の時間に実行したっていうビルドディレクトリに作成する処理。最後にJenkinsのリロードをcurlを使って実行する。
hudson-remote-apiではreload-APIがなかったので、こんななった。

以前の記事で作成した
/opt/task/video-jobs.rbに以下のメソッドを追加
def remote_estimated_build record_job, record_sec
  puts "remote_estimated_build"

  job_name = "prebuild_#{record_job}"
  job = Hudson::Job.create job_name

  command = "/opt/task/prebuild.rb #{record_job} #{record_sec}"

  config = REXML::Document.new job.config
  element = config.elements['/project/builders'].add_element 'hudson.tasks.Shell'
  element.add_element('command').add_text command
  job.update config.to_s
  job.build
#  job.wait_for_build_to_finish                                                                     
#  job.delete                                                                                       
end

create_jobメソッドの最後に以下の1行を追加
remote_estimated_build jobname, info[:duration]

このcreate_jobはJenkinsジョブとして動いている自動予約スクリプトから呼ばれたり、Redmineフックスクリプトから呼ばれたりする。

出来てしまえば簡単なのだけれど、最初のアプローチで、どうやってJenkinsシステムに分かってもらえるのかをあれこれ調べるのが手間がかかったかな。

実際に活用してみると、録画処理が後どれくらいで終わるのかが視覚的に分かるようになったので、「なんか走ってるなぁ」じゃなくて「後60分くらいかぁ」ってなるので気分がすっきりする。

2012/09/06

簡易ビデオ編集機能(Web開発編)

ビデオ編集(加工)するツールはMP4Boxで取り敢えずOKなので、次はブラウザ上で編集作業が出来るようにする。

視聴するために作っていたRedmineプラグインに編集機能を追加することにした。

プランとしては以下の様な感じ
  • 簡単な入力フォームに適当な設定用フィールドとサブミットボタンからポストするビューとコントローラ
  • フォームはディレイとクリップの2タイプ。メニューで切り替え。どうせなら、ajaxで部分更新する。
  • 実行タスクは非同期のJenkinsジョブで実行する。
  • Jenkinsジョブ状態を見て、実行中、成功、失敗がビューに反映する。
  • 編集ビュー上でオリジナルと編集結果のビデオ再生が出来る。
  • 編集が完了したら、録画チケットのビデオアタッチファイルを更新する。
  • 編集キャンセルして、Jenkinsジョブをビューから削除できる。

コントローラ作成
$ sudo RAILS_ENV=production ruby script/rails generate redmine_plugin_controller redmine_video edit index
'edit'コントローラを作る。アクションは'index'だけ作らせる。
この辺の手順は、video_tagで視聴するredmineプラグインを作るでやった手順と同じ。

コントローラ
app/controllers/edit_controller.rb
class EditController < ApplicationController
  unloadable

  include EditHelper

  def index
    get_params
    init_params unless request.post?
    update if params[:submit_update]
    execute if params[:submit_execute]
    remote_params
    save if params[:submit_save]
    clear if params[:submit_clear]
  end
end

全部の処理をヘルパーに移している。そのためコントローラはごくシンプルになっている。
ここで扱っているアクションはindexだけで、GETもPOSTも複数あるボタンからのサブミットも全部
ここで受け取って処理している。

ヘルパーはコントローラでもビューでも使用するんで、include を使っている。
因みに、ビューだけで使用するなら、helper :edit って簡素に書けるのだそうだ。

POSTだったら、init_paramsを実行
アップデートボタン押されたなら、updateを実行
実行ボタン押されたら、execute実行
保存ボタン押されたら、save実行
クリアボタン押されたら、clear実行

って感じに全部の処理をひとまとめにしている。
必要な処理は全部ヘルパーに書いているんで、開発中コントローラファイルは更新しないで済む。

matchを使ってルート定義
config/routes.rb
match 'edit/index/:id',:to=>'edit#index'
match 'edit/select_mode',:to=>'edit#select_mode'

前回までは、メソッドがGET限定だったけれど、今回は、GETもPOSTもあるんで、matchを使う。
select_modeというアクションがいきなり出てきたけど、これは後から追加したやつ。
select_modeアクションはヘルパーの方に記述している。

ビュー
app/views/edit/index.html.erb

<h2>ビデオ簡易編集</h2>

<%= form_tag :action =>'index',:id=>@issue.id, :result=>@result, :build=>@build do %>

<%= select_tag :mode,
    options_for_select([["Delay Time",0],["Clip Time",1]],@mode),
    :onchange => remote_function(:url => {:action => :select_mode}, :with => "'mode='+this.value+"+@remote_params) %>

<div id='attr'>
<%= render :partial => 'attr' %>
</div>

<table>
  <tr><td>
<!--
    <%= submit_tag "実行", :name => "submit_execute", data:{disable_with:"実行中"} %>
    <%= submit_tag "実行", :name => "submit_execute", :data => {:disable_with=>"実行中"} %>
    data-disable-with属性があるとなぜかparamsにnameが入らない。そのため処理されない
-->
    <%= submit_tag "実行", :name => "submit_execute" %>
    <%= submit_tag "アップデート", :name => "submit_update" %>
    <br>
    <%= video_tag get_video_filename, :controls => true, :size =>"480x270" %>

    <% if @build %>
    <% if @build=='ok' && @result %>
    <td>
      <%= submit_tag "保存", :name => "submit_save", :onclick => "return confirm('Are you sure?')" %>
      <%= submit_tag "消去", :name => "submit_clear", :onclick => "return confirm('Are you sure?')" %>
      <br>
      <%= video_tag @result, :controls => true, :size =>"480x270" %>
      <% end %>
    <td><%= link_to @build, "/jenkins/job/edit_#{@issue.id}" %>
      <% end %>
</table>
<% end %>

<%= form_tag :action =>'index',:id=>@issue.id, :result=>@result, :build=>@build do %>
@resultには、処理実行後のビデオファイル名が入る。@buildには、Jenkinsジョブの状態が入る。どっちも一番最初はNullが入っている。
id、result、buildの設定を追記しているのは、フォーム内の入力フィールド以外のパラメータを渡して保存するため。


フォームのサブミットでコントローラに渡るparams[]に自動的に入るのは、その時の入力フォームだけなのね。
なので、それ以外に常にparams[]に入って欲しいパラメータは自分で追加記述しないとならない。


<%= render :partial => 'attr' %> で、_attr.html.erbをインクルードする
それをajax更新するために、div id='attr'タグで囲っておく。
更新は、ヘルパーに書いてあるselect_mode内で
render :update do |page| page.replace_html "attr", :partial => "attr"
って書いておくと、その部分だけの更新になるそうな。興味本位でやってみたけど、面倒くさい。
勉強にはなったか。


セレクトメニューで選択するとフォームが部分更新されるようにしたけど、色々面倒。


select_tagでajaxな効果を得ようとするには、:onchangeでremote_functionでアクションを指定するのがセオリーらしい。ただ、そうすると、サブミットみたいに勝手にparams[]にパラメーがが渡らない。JavaScriptの処理だから?
仕方なく、:withで全部のパラメータがコントローラに渡るように書かねばならなかった。
そのための変数が@remote_paramsってやつ。


パーシャルレンダリング
app/views/edit/_attr.html.erb

<% if @mode==0 %>
<%= number_field_tag :delay_time, @delay_t %>(msec)
<%= radio_button_tag :track, 1, @track==1? true:false %>映像
<%= radio_button_tag :track, 2, @track==2? true:false %>音声
<%= check_box_tag :preview, true, @preview? true:false, {} %>プレビュー
<% else %>
開始:<%= text_field_tag :start_time, @start_t %>
終了:<%= text_field_tag :end_time, @end_t %>
<% end %>

ディレイだったらクリップだったらにフォーム内容を分けている部分。
特に分ける必要はないんだけど、こんなふうに分けられるんだねって言うためのもの。
普通はビューっぽくない手続き的な処理が多いところを分けておくと見通しが良くなるっていう目的で行うらしいが。
大規模なページデザインなら活用価値があるのだろう。複数人で構築するとかだろうね。

disable_withが活用できない

<%= submit_tag "実行", :name => "submit_execute", data:{disable_with:"実行中"} %>
<%= submit_tag "実行", :name => "submit_execute", :data => {:disable_with=>"実行中"} %>
どっちの書き方をしても出力されるフォームタグには data-disable-with属性が追加される。これをしておくと、実行ボタンの2重押しを防止できるのでそうしたかったが、この記述があるとなぜかparams[:name]がアクションに渡らないのだ。仕方なく
<%= submit_tag "実行", :name => "submit_execute" %>
で我慢である。submit_tagで使った場合の問題のようなので、submitを使わずに普通のボタンだったら大丈夫だと思うが、その場合はフォーム内パラメータを受け渡す処理も自前で書かないとならないだろう。それはそれで大変だ。簡単な解決方法が欲しいぞ。

confirmが使えない(redmine2.0.3のGemfileはjquery標準じゃない)

<%= submit_tag "保存", :name => "submit_save", :onclick => "return confirm('Are you sure?')" %>
<%= submit_tag "消去", :name => "submit_clear", :onclick => "return confirm('Are you sure?')" %>

ここも困っている。保存、消去は取り返しがつかないんで、確認ダイアログを出したい。Railsな書き方を調べると:confirm => "Are you sue?"でいいらしいことが沢山書いてある。やってみた。が、全然機能しない。

更に調べると、:confirmを機能させるには、jquery-railsが必要らしい。Rails3はjqueryが標準らしい。?ん?redmineのGemfileにはjquery-railsが無いぞ?prototypeだぞ? Redmine2.0.3はRails3だけどjquery-railsは標準じゃないってことか。

jquery-railsを入れることも検討したけど、jqueryを入れるとprototypeが勝手に消えるっていう話が何処かのブログに書いてあった。Redmine自体が動かなくなってしまいそうで怖いのでやっていない。いずれ、バックアップを取ってからテストしてみよう。

なので、仕方なくprototypeのconfirmを動かす。こっちは:onclickでconfirmを呼ぶのだそうだ。ちょっとかっこ悪いコンフォームダイアログが出た。ほんとにかっこ悪いんだなこれが。

ジョブステータス表示とジョブへのリンクを兼用
<%= link_to @build, "/jenkins/job/edit_#{@issue.id}" %>
単に@build内容を表示するだけじゃ、勿体無いのでJenkinsジョブページヘのリンクを貼る。
ジョブ説明にも編集ページヘのリンクが張ってあるんで、これで行ったり来たりが出来る。


編集タスク本体
app/helpers/edit_helper.rb

# -*- coding: utf-8 -*-
module EditHelper

#  require 'hudson-remote-api'
  Hudson.settings = {:url => 'http://localhost/jenkins', :crumb => false }

  # create a jenkins job and build the job
  def jenkins command
    jobname = "edit_#{@issue.id}"
    job = Hudson::Job.get jobname
    job.delete if job
    job = Hudson::Job.create jobname

    job.description = command + " <a href=/edit/index/#{@issue.id}>編集</a>";

    config = REXML::Document.new job.config
    element = config.elements['/project/builders'].add_element 'hudson.tasks.Shell'
    element.add_element('command').add_text command
    job.update config.to_s
    job.build
  end

  # execute edit
  def execute
    video = get_video_filename
    system "rm /opt/videos/#{@issue.id}_*"
    if @preview
      @result = video.sub('.mp4','_0_60.mp4')
      system "MP4Box -split-chunk 0:60 /opt/videos/#{video}"
      jenkins "MP4Box -delay #{@track}=#{@delay_t} /opt/videos/#{@result}"
    else
      # execute!
      if @mode == 0 # delay
        @result = video.sub('.mp4',"_#{@track}_#{@delay_t}.mp4")
        jenkins "MP4Box -delay #{@track}=#{@delay_t} -out /opt/videos/#{@result} /opt/videos/#{video}"
      else # clip
        @result = video.sub('.mp4',"_*_*.mp4")
        t_start = str_to_sec @start_t
        t_end = str_to_sec @end_t
        jenkins "MP4Box -split-chunk #{t_start}:#{t_end} /opt/videos/#{video}"
      end
    end
  end

  # just update views
  def update
    @start_t = sec_to_str str_to_sec @start_t unless @start_t.nil?
    @end_t = sec_to_str str_to_sec @end_t unless @end_t.nil?
  end

  # ajax partial render
  def select_mode
    get_params
    init_params
    remote_params
    render :update do |page| page.replace_html "attr", :partial => "attr"
    end
  end

  def init_params
    @mode = 0 if @mode.nil?
    @delay_t = 0 if @delay_t.nil?
    @start_t = "00:00:00" if @start_t.nil?
    @end_t = "00:00:00" if @end_t.nil?
    @track = 1 if @track.nil?
    @preview = 'true' if @preview.nil?
  end

  def get_params
    @issue = Issue.find(params[:id])
    @mode = params[:mode].to_i
    @delay_t = params[:delay_time].to_i
    @start_t = params[:start_time]
    @end_t = params[:end_time]
    @track = params[:track].to_i
    @preview = params[:preview]
    @result = params[:result]
    @build = params[:build]
  end

  def remote_params
    job = Hudson::Job.get "edit_#{@issue.id}"
    if job
      case job.color
      when 'blue'
        @build = 'ok'
        if @result
          @result = get_result_filename @result
        else
          parse_command job.description
        end
      when 'red'
        @build = 'failed'
      else
        @build = 'running'
      end
    end
    @remote_params = "'"
    @remote_params << "&id=#{@issue.id}"
    @remote_params << "&delay_time=#{@delay_t}"
    @remote_params << "&start_time=#{@start_t}"
    @remote_params << "&end_time=#{@end_t}"
    @remote_params << "&track=#{@track}"
    @remote_params << "&preview=#{@preview}"
    @remote_params << "&result=#{@result}"
    @remote_params << "&build=#{@build}"
    @remote_params << "'"
  end

  def clear
    job = Hudson::Job.get "edit_#{@issue.id}"
    job.delete if job
    @result = nil
    @build = nil
    system("rm /opt/videos/#{@issue.id}_*")
  end

  def save
    org_file = "/opt/videos/" + get_video_filename
    dst_file = "/opt/videos/" + @result
    if File.exists?(dst_file) && system("mv -f #{dst_file} #{org_file}")
      clear
      @build = "done"
    else
      @build = "Error, file not found:#{dst_file}"
    end
  end

  # utilities

  def parse_command desc
    cmds = desc.split(' ')
    if cmds[1].include?('split')
      # split-chunk
      @mode = 1
      times = cmds[2].split(':')
      @start_t = sec_to_str times[0].to_i
      @end_t = sec_to_str times[1].to_i
      @result = get_result_filename get_video_filename.sub('.mp4',"_*_*.mp4")
    else
      # delay
      @mode = 0
      delays = cmds[2].split('=')
      @track = delays[0].to_i
      @delay_t = delays[1].to_i
      if cmds[3]=='-out'
        @result = get_result_filename cmds[4]
        @preview = 'false'
      else
        @result = get_result_filename cmds[3]
        @preview = 'true'
      end
    end
  end

  def get_video_filename
    video = @issue.attachments.detect {|a| a.content_type == "video/mp4"}
    video.filename
  end

  def get_result_filename str
    result = str
    result = `ls -1 /opt/videos/#{str}` if str.include?('*')
    result.chomp!
    result.sub('/opt/videos/','')
  end

  def str_to_sec str
    if str
      t = str.split(':').reverse
      s = t[0].to_i if t.size>0
      s += t[1].to_i * 60 if t.size>1
      s += t[2].to_i * 60 * 60 if t.size>2
      s
    else
      0
    end
  end

  def sec_to_str sec
    if sec
      [ "%02d"% [sec / (60*60)], "%02d"% [(sec / 60) % 60], "%02d"% [sec % 60] ].join(':')
    else
      "00:00:00"
    end
  end
end

require 'hudson-remote-api'は行わない
以前の記事でredmineからJenkinsジョブを操作するところで既にやっているので、require 'hudson-remote-api'は記述しない。というか記述してはいけない。

def jenkins command
Jenkinsにジョブを登録して実行する部分

job.description = command + " <a href=/edit/index/#{@issue.id}>編集</a>";
何を実行したか(しているか)を記録しておく場所としてジョブdescriptionを利用した。
ついでに、Jenkins側からRedmineの編集ページへのリンクを作る。行ったり来たりができて便利。そもそもジョブの説明のところに普通にHTMLタグを書いて機能するのがいい。

ジョブのconfig設定もう少し簡素にかけないだろうか。Shellコマンド入れたいだけなのに

config = REXML::Document.new job.config
element = config.elements['/project/builders'].add_element 'hudson.tasks.Shell'
element.add_element('command').add_text command
job.update config.to_s
job.build

録画ジョブとかは色々複雑だったのでテンプレートを作っておいたけれど、今回のジョブはシェルコマンド1つ登録して実行するだけなので、テンプレートを用いずに、直接XMLに追加する方法にした。そして即実行する。もちろんタスクは裏で動くのでビュー側が止まってしまうことはない。

def execute
実行ボタンを押すと呼ばれるところ。
この中で実際の編集コマンドを作ってジョブに登録している。
@preview='true'ならば、最初の60秒間だけの処理になる。60秒分のビデオファイルを作るのは一瞬で終わるので、その場で作成している。
クリップ処理はプレビュービデオじゃ出来ないんでこの処理はない。

def select_mode
編集がディレイなのかクリップなのかのセレクトメニュー操作をすると呼ばれる。
ajaxなことをしてみたくて色々試行錯誤した。

def get_params
アクションが呼ばれたら必ず実行する部分。
ビューとコントローラ間でパラメータを受け渡しするには、ビューからはparams[]、コントローラからはグローバル変数でやり取りするのがルールなのだね。

def remote_params
Jenkinsジョブ状態から、@resultと@buildを設定する部分。
ただ、セレクトメニューでフォームを変更した時でもパラメータが保存されるようにするために@remote_paramsに全部のパラメータ情報を保存する処理も含んでいる。ここは色々調べたけど納得行く方法に至らなかった。

def clear
ジョブを削除して、@result,@buildをリセットする。でもって、編集結果のビデオファイルも削除する。

def save
編集後のビデオファイルをアタッチビデオファイルとして更新する。
更新するといっても単にリネームしているだけ。やり直しは出来ない。

def parse_command desc
ジョブ説明文から、ジョブ内容をパースしてパラメータに反映させる部分。
もっと解析しやすいフォーマットとかJSONとか使ったほうが?とも思ったけれど、説明部分は目に見える部分でもあるので、コマンド文字列そのものにした。やってることは簡単なのでパースも簡単だった。

def get_video_filename
チケットにアタッチされているビデオファイル名を取得する。
ビューからもコントローラからも使用している。例のMP4Boxの-split-chunkでクリップした際に出来上がるファイルを特定する処理もここで行なっている。
簡単にls -1で取得できるように、クリップ実行時に予め @result = video.sub('.mp4',"_*_*.mp4") って書いておいてワイルドカード検索するようにお膳立てしておく。

MP4Boxの--split-chunkで出力されるファイル名を指定も決め打ちも出来ないための苦肉の策。

def str_to_sec str
def sec_to_str sec
"00:00:00"とかの時間フォーマット文字列と秒を相互変換するユーティリティ関数
Timeクラスとかは、こういう処理には不向きなので、簡単なやつを用意した。

~使用感~

我ながら意外と使えるなって思った。
編集中の情報はJenkinsジョブに保存されているので、実行中別のことをしてもいいし、編集再開とかが出来るし複数のビデオ編集を一度に実行することも出来る。編集ジョブを全部走らせて(数時間分の処理だとさすがに遅いのだ)翌日結果を確認するなんてことも出きるかな。
パフォーマンスが低いマシンで、尚且つ、細切れにちょっとずつしかやれる時間がない者にとって都合が良い。

欲を言えば

時間設定を、ビデオ再生のタイムスライダコントローラからドラッグ設定出きたらなぁ。

設定フォームデザインがもっとかっこよく出来たらなぁ。この辺はこつこつ暇な時にやれば出きるか。

実行ジョブが後どれくらいで終わるとかが見えるとか。これは録画ジョブの方も見れるようになるといいな。

Jenkinsジョブは2回目のビルドならどれくらいで終わるかわかるけど、最初はわからない。
こっちで概ねの時間計算は出きるだろうから、ダミーのビルド結果を作ってやって、Jenkinsがビルド2回目だと思ってくれればいい感じになりそうだね。

記事を2回くらいに分けて書こうかと思ったけど、全部書いてしまった。
とにかく、見よう見まねの、簡易ビデオ編集機能は構築できた。Rails初心者にとっては良い題材になりましたとさ。

改良点は死ぬほどあるけど、やりたかったことは出来たと思う。
ということで、録画システム構築は一区切りしようかな。