preload
9月 25

GV-MVP/RX2 を入手して以来、ubuntu desktop でのテレビ視聴設定に苦心してきたのだが、ivtv-1.0.2 を試してみたら、あっさりうまくいったという話。

基本的には、

  1. ivtvdriver.org で ivtv のソースと firmware を拾ってくる
  2. ぱ研さんとこ(というか macmil_co_jp さんとこ)のパッチを当てる
  3. ivtv 入れる
  4. firmware 入れる
  5. saa7115 やら ivtv やらを modprobe する設定を modprobe.conf へ

という流れなのだが、ivtv-0.9.8 くらい以降 ivtv-0.10.3 くらいまででいろいろ試したが、映像は出るものの、どうも音が出ない(かすかに出ているのだが、どうもデコードと同期に失敗してて聞き取れない)。mplayer で 10 秒くらい視聴していると

**** YOUR SYSTEM IS TOO SLOW TO PLAY THIS! ****

とか怒られて、だんだんカクカクになる。-cache しても -framedrop しても同じ。ライブが不要であれば $ cat /dev/video0 > hoge.mpg とかで録画すれば録画したモノは滑らかに再生できるので問題ないとしても、音が出ないのは致命的だ。

ちなみにマシンスペックは、GA-945G-S3 / Intel Core 2 Duo E6300 @1.86GHz / 2048KB cache / 1.5GB SDRAM / GeForce 7300 GS GPU で、そんなに低いとも思えなかった。

ふと、ivtvdriver.org を見に行ってみたら、ivtv-1.0.2 が出ていた。1.0 系が出ているのを知らなかったので、アーカイブを拾ってみたら README の supported cards に I/O Data GV-MVP/RX2E と、それっぽい記述が(もっとも 0.10 系でもこの記述はあった)。ということで、こいつをビルドしてみる。

ivtv-1.0.2 は kernels 2.6.22 以上対象なので、そのままだとビルドできない。まずはカーネルのバージョンアップから。Ubuntu Geekの記事を参考に、apt の sources.list に Gusty のリポジトリを追記。まさかこんな形で Gusty を試すことになるとは思わなかった。2.6.22-12 の各種ディストリビューションパッケージを $ sudo apt-get install => 再起動。NVIDIA カードの初期化に失敗したとかいって X 起動に失敗したので、nvidia 関連のパッケージ(nvidia-glx-new, nvidia-glx-new-dev とか)もインストールして、$ sudo modprobe -r nvidia && sudo modprobe nvidia-new した。

カーネルのバージョンアップが終わったら、ivtv-1.0.2 を $ make && sudo make install で OK。firmware をインストールしてから再起動。で、

$ ivtv-tune -d /dev/video0 -t japan-bcast -c1
$ mplayer /dev/video0 -cache 8192 -framedrop

で視聴開始。む、またも音が出ない。やはりパッチが必要か。

というわけで macmil_co_jp さんとこからパッチを取得。macmil_co_jp さんとこは相当数のパッチを公開されてて、目的のモノを捜し出すのがちと大変。僕が拾ってきたのは、

  • http://www.macmil.co.jp/macmil/dat/s7115f8p.tar
  • http://www.macmil.co.jp/macmil/dat/tvau-f8p.tar
  • http://www.macmil.co.jp/macmil/dat/kb10-f8.tar

この3つ。それぞれ、saa7115.c, tvaudio.c, Kbuild にリネームして ivtv-1.0.2 を再ビルド。ついでに と /etc/modprobe.conf に saa7115 やら tvaudio の設定を追加。

ivtv screenshotこれで再度 mplayer 起動。おお、音が出た!カクカクも起こらない!

あとは適当に tv コマンド(テレビ起動) やら ch コマンド(チャンネル変更) やらを整えて完了。Gusty 標準(?) の Compiz を有効にしてウィンドウふにふに 3D デスクトップしても、テレビ視聴問題なし。うーん、すばらしい。

ちなみに、S 端子入力にプレステ2の映像を突っ込んで、

$ v4l2-ctl -d /dev/video0 –set-input=1

とかすれば、S 端子外部入力に切り替えできる。–set-input=2 ならコンポジット(RCA)端子入力。でも、外部入力からの映像表示は遅延が大きく、コントローラ入力から映像への応答を見るに 2 秒弱遅延しているようだ。これはゲームには無理かなぁ。。

2007/10/01 追記。
-cache 8192 してたから遅延は当たり前でした。でも、-cache しなくても 1 秒弱は遅れるので、やぱしゲームでの利用は厳しいな。

Tagged with:
8月 20

lighttpd-1.4.15 + FastCGI な環境で、HTTP リクエストヘッダ中の任意のレコードを参照して、その内容によって環境変数を設定しておきたい、という状況。apache であれば SetEnvIf ディレクティブを使うことで、

SetEnvIf X-Hoge ".*" bar=yes

とかで設定できるが、lighttpdではこういう事ができないらしい。lighttpdの設定でSetEnvIfのような条件分岐処理を書こうとすると、マニュアルのConditional Configurationによれば、

<field> <operator> <value> {

}

てな記述ができるのだが、<field> に指定できるのは $HTTP や $SERVER で参照できる、固定の変数だけのようだ。$HTTP["X-Hoge"] とか試しに指定してみたけど、syntax error で無視されてしまう。

デフォルト提供されている mod_xx や設定方法のマニュアルを当たってみたり、あと、騙し騙し使えそうな設定方法を試行してみたが、どれもダメだった。mod_setenv を使えば環境変数の設定まではできるんだが、その手前の条件記法を拡張する事ができないみたいだ。

ということで、lighttpd の拡張プラグインを書いてみようと思い立った。条件記法の拡張まではできなさそう(うまくやればできるかも??)だが、デフォルト指定できない特定のHTTPリクエストヘッダを参照し、環境変数を設定する、というだけであれば、プラグインで書けそうだ。ということで、プラグイン作成の作業メモ。

Writing Plugins のページに簡単なチュートリアルがあるので、これに従ってプラグインを書いてみた。プラグインの作成ステップは、大きく以下のような感じ。

  1. mod_skeleton.c からスケルトンコードのコピー
  2. スケルトンコードを書き換える形でプラグインを記述
  3. プラグインの Makefile.am の修正
  4. プラグインの make .. Makefile.in の生成と作成したプラグインのプリコンパイル
  5. lighttpd の configure .. –enable-maintainer-mode で Makefile を書き換える
  6. lighttpd の make && make install

本家のチュートリアルには上記 (4) のステップが抜けているので注意((6) も抜けてるけど)。あと、プラグインの作成には lighttpd の動作モデルと、プラグインがフックできるインタフェース(エントリポイント)の概要くらいは見ておく必要があると思う。

プラグインソースコードの構成(レイアウト)は、上記のチュートリアルを参照。意訳すると、

  1. プラグイン処理中で使用する構造体の定義
  2. 構造体の初期化処理
  3. configuration file の parse 処理 .. set_defaults
  4. プラグインが条件分岐中で適用された場合の処理 .. patch-function
  5. プラグインのメイン処理
  6. プラグインの初期化処理(プラグイン中で定義した関数と、プラグインインタフェースとの対応はここで定義する) .. plugin_init

こんなかんじの構成になっている。

あとは、既存のプラグインソースなどを参考に、プラグインを書いていく。今回書いたプラグイン mod_addenv.c は、こんなかんじ。

#include 
#include 
#include "base.h"
#include "log.h"
#include "buffer.h"
#include "plugin.h"
/* plugin config for all request/connections */
typedef struct {
int handled;
} handler_ctx;
typedef struct {
array *match;
} plugin_config;
typedef struct {
PLUGIN_DATA;
plugin_config **config_storage;
plugin_config conf;
} plugin_data;
static handler_ctx * handler_ctx_init() {
handler_ctx * hctx;
hctx = calloc(1, sizeof(*hctx));
hctx->handled = 0;
return hctx;
}
static void handler_ctx_free(handler_ctx *hctx) {
free(hctx);
}
/* init the plugin data */
INIT_FUNC(mod_addenv_init) {
plugin_data *p;
p = calloc(1, sizeof(*p));
return p;
}
/* detroy the plugin data */
FREE_FUNC(mod_addenv_free) {
plugin_data *p = p_d;
UNUSED(srv);
if (!p) return HANDLER_GO_ON;
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
array_free(s->match);
free(s);
}
free(p->config_storage);
}
free(p);
return HANDLER_GO_ON;
}
/* handle plugin config and check values */
SETDEFAULTS_FUNC(mod_addenv_set_defaults) {
plugin_data *p = p_d;
size_t i = 0;
config_values_t cv[] = {
{ "addenv.env", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
};
if (!p) return HANDLER_ERROR;
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
s->match    = array_init();
cv[0].destination = s->match;
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
return HANDLER_ERROR;
}
}
return HANDLER_GO_ON;
}
#define PATCH(x) 
p->conf.x = s->x;
static int mod_addenv_patch_connection(server *srv, connection *con, plugin_data *p) {
size_t i, j;
plugin_config *s = p->config_storage[0];
PATCH(match);
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
data_config *dc = (data_config *)srv->config_context->data[i];
s = p->config_storage[i];
/* condition didn't match */
if (!config_check_cond(srv, con, dc)) continue;
/* merge config */
for (j = 0; j < dc->value->used; j++) {
data_unset *du = dc->value->data[j];
if (buffer_is_equal_string(du->key, CONST_STR_LEN("addenv.env"))) {
PATCH(match);
}
}
}
return 0;
}
#undef PATCH
URIHANDLER_FUNC(mod_addenv_uri_handler) {
plugin_data *p = p_d;
size_t i, j;
handler_ctx *hctx;
if (con->plugin_ctx[p->id]) {
hctx = con->plugin_ctx[p->id];
} else {
hctx = handler_ctx_init();
con->plugin_ctx[p->id] = hctx;
}
if (hctx->handled) {
return HANDLER_GO_ON;
}
hctx->handled = 1;
mod_addenv_patch_connection(srv, con, p);
for (i = 0; i < con->request.headers->used; i++) {
data_string *dsh;
dsh = (data_string *)con->request.headers->data[i];
if(dsh->key->used && dsh->value->used && (0 == strcasecmp(dsh->key->ptr, "X-HOGE"))) {
for (j = 0; j < p->conf.match->used; j++) {
data_string *ds = (data_string *)p->conf.match->data[j];
data_string *ds_dst;
if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
ds_dst = data_string_init();
}
buffer_copy_string_buffer(ds_dst->key, ds->key);
buffer_copy_string_buffer(ds_dst->value, ds->value);
array_insert_unique(con->environment, (data_unset *)ds_dst);
}
}
}
/* not found */
return HANDLER_GO_ON;
}
REQUESTDONE_FUNC(mod_addenv_reset) {
plugin_data *p = p_d;
UNUSED(srv);
if (con->plugin_ctx[p->id]) {
handler_ctx_free(con->plugin_ctx[p->id]);
con->plugin_ctx[p->id] = NULL;
}
return HANDLER_GO_ON;
}
/* this function is called at dlopen() time and inits the callbacks */
int mod_addenv_plugin_init(plugin *p) {
p->version     = LIGHTTPD_VERSION_ID;
p->name        = buffer_init_string("addenv");
p->init        = mod_addenv_init;
p->handle_uri_clean  = mod_addenv_uri_handler;
p->set_defaults  = mod_addenv_set_defaults;
p->cleanup     = mod_addenv_free;
p->handle_request_done = mod_addenv_reset;
p->data        = NULL;
return 0;
}

このソースを mod_addenv.c として保存し、lighttpd-1.4.15/src 以下で make したあと、./configure –enable-maintainer-mode && make && make install する。既存の環境がある場合は、テスト用に –prefix 指定して違うところに入れた方がいいかも。あとは lighttpd.conf で、

server.modules = ( "mod_addenv" )
addenv.env = (
 "bar" => "yes"
)

とか設定しておけば、lighttpd でも上述の SetEnvIf のような効果が得られる。X-HOGE をソース中に書いているのがダサいな。なんとかしたい。

ところで、プラグインの記述方法は、ちょっと癖があるようにも思えたが、こういうモノなのだろうか。まあ、一度やってみておいて損は無いかな、とはおもった。他にもいろいろ使い道ありそうだし。

Tagged with: