引っ越しました 1

Posted by Gosuke Miyashita Sat, 27 Oct 2007 08:17:00 GMT

FLV::Info で ustream の FLV ファイル情報をとろうとするとエラーになる件

Posted by Gosuke Miyashita Mon, 08 Oct 2007 05:07:00 GMT

ustream の FLV をダウンロードして音声変換する方法は、typesterさんのとこに詳しく書いてある わけですが、sox に渡すときにサンプルレートを指定する必要があったりします。

で、これを Plagger プラグインで実現するためには、何らかの方法で元ファイルのサンプルレートを取得しないといけないわけで、FLV::Info でできそうだな、と思ったところ

Failed to read FLV file: Tag size is too small (0) at byte 193 (0xc1) at /usr/local/lib/perl5/site_perl/5.8.7/FLV/Tag.pm line 81.

といったエラーが出ます。色々調べてみたところ、ustream の FLV ファイル中に、ボディが 0 のオーディオタグが存在するから、ということがわかったので、パッチを書いて RT に投げておきました。

パッチ自体は数行コメントアウトして1行追加しただけ、という簡単なものなのですが、このパッチを書くために、FLVのファイルフォーマット を調べて、音声フォーマットを取得する Perl スクリプトを自分で書いてみたりと、えらく回り道しました。せっかくなんでスクリプトを載せておきます。スクリプト作成にあたって、nelly2pcm のソースも参考にしています。

#!/usr/bin/perl

use strict;
use warnings;
use Readonly;

Readonly my $FLV_TAG_TYPE_AUDIO                => 0x08;
Readonly my $AUDIO_MASK_STEREO                 => 0x01;
Readonly my $AUDIO_MASK_16bit                  => 0x02;
Readonly my $AUDIO_MASK_RATE                   => 0x0c;
Readonly my $AUDIO_RATE_5point5KHZ             => 0x00;
Readonly my $AUDIO_RATE_11KHZ                  => 0x04;
Readonly my $AUDIO_RATE_22KHZ                  => 0x08;
Readonly my $AUDIO_RATE_44KHZ                  => 0x0c;
Readonly my $AUDIO_MASK_FORMAT                 => 0xf0;
Readonly my $AUDIO_FORMAT_UNCOMPRESSED         => 0x00;
Readonly my $AUDIO_FORMAT_ADPCM                => 0x10;
Readonly my $AUDIO_FORMAT_MP3                  => 0x20;
Readonly my $AUDIO_FORMAT_NELLYMOSER_8KHZ_MONO => 0x50;
Readonly my $AUDIO_FORMAT_NELLYMOSER           => 0x60;

Readonly my %AUDIO_RATE => (
    $AUDIO_RATE_5point5KHZ => 5.5,
    $AUDIO_RATE_11KHZ      => 11,
    $AUDIO_RATE_22KHZ      => 22,
    $AUDIO_RATE_44KHZ      => 44,
);

Readonly my %AUDIO_FORMAT => (
    $AUDIO_FORMAT_UNCOMPRESSED         => 'Uncompressed',
    $AUDIO_FORMAT_ADPCM                => 'ADPCM',
    $AUDIO_FORMAT_MP3                  => 'MP3',
    $AUDIO_FORMAT_NELLYMOSER_8KHZ_MONO => 'Nellymoser 8kHz mono',
    $AUDIO_FORMAT_NELLYMOSER           => 'Nellymoser',
);

my $file = shift;

open my $fh, '<', $file;

my $header = read_header($fh);

while ( ! eof $fh ) {
    my $tag = read_tag($fh);
    if ( $tag->{type} == $FLV_TAG_TYPE_AUDIO and $tag->{length} ) {
        my $first_byte = $tag->{first_byte};

    my $stereo       = $first_byte & $AUDIO_MASK_STEREO;
    my $audio_size  = $first_byte & $AUDIO_MASK_16bit ? 16 : 8;
        my $audio_rate   = $AUDIO_RATE{ $first_byte & $AUDIO_MASK_RATE } || 'unknown';
        my $audio_format = $AUDIO_FORMAT{ $first_byte & $AUDIO_MASK_FORMAT } || 'unknown';

        print 'audio type: ' . ( $stereo ? 'stereo' : 'mono' ) . "\n";
        print "audio size: $audio_size bit\n";
        print "audio rate: $audio_rate Hz\n";
        print "audio format: $audio_format\n";
        last;
    }
}

sub read_header {
    my $fh = shift;
    read $fh, my $sig,       3;
    read $fh, my $version,   1;
    read $fh, my $flag,      1;
    read $fh, my $offset,    4;
    read $fh, my $prev_tag,  4;

    return {
        sig => $sig,
        version => unpack('H*', $version),
        flag    => unpack('H*', $flag),
        offset  => unpack('H*', $offset),
    };
}

sub read_tag {
    my $fh = shift;
    read $fh, my $type,               1;
    read $fh, my $length,             3;
    read $fh, my $timestamp,          3;
    read $fh, my $timestamp_extended, 1;
    read $fh, my $stream_id,          3;
    read $fh, my $first_byte,         1;

    my $pos = hex unpack 'H*', $length;
    seek $fh, $pos - 1, 1;

    read $fh, my $prev_tag,  4;

    return {
        type       => unpack('C', $type),
        length     => $pos,
        first_byte => unpack('C', $first_byte),
    };
}

こんな感じで FLV ファイルの音声情報を表示します。

$ ./get_flv_info.pl broadcast_35957_1191232400660.flv
audio type: mono
audio size: 16 bit
audio rate: 11 Hz
audio format: Nellymoser  

あとは Plagger プラグインを書くだけ、という感じなのですが、リビドーが沸いてこないのでちょっと保留。FFmpeg で Nellymoser に対応する予定がある(既に SVN HEAD には Nellymoser という文字列がコード中にある)ようなので、それまで待ってもいいかなー、と。

音声変換するプラグイン Filter::Audio は coderepos にあげてありますので、俺が対応してやるぜ、という方はお好きなようにいじってください。

Shibuya.pm テクニカルトーク #8 でしゃべってきました

Posted by Gosuke Miyashita Mon, 01 Oct 2007 14:18:00 GMT

プレゼン途中にバッテリーが切れるという失態をやらかしたため、yusukebeさん言うところの「お口で」のみのプレゼンとなってしまいました。ごめんなさい。会場でお見せできなかった幻の資料はこちらにアップしておきます

Thinkpad X60sはバッテリー残量表示が1時間になっていても、プレゼンやると2,3分でバッテリー切れますので、みなさんご注意下さい。

puppet-mode.el のパッチ

Posted by Gosuke Miyashita Wed, 29 Aug 2007 15:30:00 GMT

Puppet で同じタイプのリソースを大量に記述する時には、

package { sudo:
  ensure => latest
}

package { ntp:
  ensure => latest
}

package { ssh:
  ensure => latest
}

なんて書くのはだるいので、

package {
  sudo:
    ensure => latest;
  ntp:
    ensure => latest;
  ssh:
    ensure => latest;
}

と書きたいのですが、Emacs + puppet-mode.el では、後者みたいな書き方だと、インデントが意図した通りにならなくて、イラっときてパッチ書いた。

=== puppet-mode.el
==================================================================
--- puppet-mode.el      (revision 1800)
+++ puppet-mode.el      (local)
@@ -106,7 +106,9 @@
                  (progn
                     (setq cur-indent (- (current-indentation) 2))
                    (setq not-indented nil))
-               (setq cur-indent (- (current-indentation) 2))))
+               (setq cur-indent (- (current-indentation) 2)))
+              (if (looking-at "^.*;")
+                  (setq cur-indent (- cur-indent 2))))
            (if (< cur-indent 0)     ; We can't indent past the left margin
                (setq cur-indent 0)))
        (save-excursion
@@ -125,6 +127,18 @@
                    (setq cur-indent (+ (current-indentation) 2))
                    (setq not-indented nil))
                (if (bobp)
+                   (setq not-indented nil)))
+             (if (looking-at "^.*:\s*$")
+                 (progn
+                   (setq cur-indent (+ (current-indentation) 2))
+                   (setq not-indented nil))
+               (if (bobp)
+                   (setq not-indented nil)))
+             (if (looking-at "^.*;")
+                 (progn
+                    (setq cur-indent (- (current-indentation) 2))
+                   (setq not-indented nil))
+               (if (bobp)
                    (setq not-indented nil)))))))
       (if cur-indent
          (indent-line-to cur-indent) 

他にもインデントまわりの動作で微妙なところはあるけど、ちょっとはましになった。インデントまわりの処理で参考になる emacs-lisp って何かないかな?

Trac の SearchHyperEstraierPlugin を Trac 0.10.x で動かすパッチ

Posted by Gosuke Miyashita Wed, 08 Aug 2007 18:03:00 GMT

SearchHyperEstraierPlugin が、Trac 0.10.x というか、うちの環境で動かないので、パッチ書いた。初 Python プログラミング。(というほどのものじゃないけど。)


=== searchhyperestraier/searchhyperestraier.py
==================================================================
--- searchhyperestraier/searchhyperestraier.py    (revision 892)
+++ searchhyperestraier/searchhyperestraier.py    (local)
@@ -2,8 +2,9 @@
 # SearchRepositoryWithNamazu plugin

 from trac.core import *
-from trac.Search import ISearchSource, query_to_sql, shorten_result
+from trac.Search import ISearchSource, shorten_result
 from trac.util import NaivePopen
+from trac.util.text import to_unicode
 from StringIO import StringIO
 import urllib
 import time
@@ -44,9 +45,9 @@
         browse_trac = self.env.config.get('searchhyperestraier', 'browse_trac','enabled')

-        cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,index_path,unicode(query,'utf-8').encode(estcmd_encode))
+        cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,index_path,' '.join(query))
         self.log.debug('SearchHyperEstraier:%s' % cmdline)
-        np = NaivePopen(cmdline)
+        np = NaivePopen( cmdline.encode(estcmd_encode) )
         #self.log.debug('Result:%s' % unicode(np.out,'utf-8').encode('mbcs'))
         #self.log.debug('Result:%s' % np.out)
         if np.errorlevel or np.err:
@@ -74,7 +75,7 @@
             attrelem_array = element.getElementsByTagName("attribute")
             for attrelem in attrelem_array:
                 attr_name = attrelem.getAttribute("name")
-                attr_value = unicode(attrelem.getAttribute("value")).encode('utf-8')
+                attr_value = to_unicode(attrelem.getAttribute("value"))
                 #URLとタイトルを生成
                 if attr_name == "_lreal":
                     attr_value=attr_value[len(replace_left):].replace("\\","/")

エラーが出るたびに、適当にいじってたら動いた、という感じ。なので、こんなんでいいのかはよく分からない。

Pushmi つかってます & 技術者募集中 at 福岡 3

Posted by Gosuke Miyashita Thu, 02 Aug 2007 16:52:00 GMT

弊社 は東京と福岡にオフィスがあり、それぞれの拠点に Subversion + Trac 環境を構築し、OpenVPN により VPN 接続してお互いの開発状況を公開しています。サービス絡みの開発は基本的に、東京は東京、福岡は福岡で完結しているので、この方式で問題はないのですが、サーバ管理関連のスクリプトなんかは、東京と福岡で共通するものが多いため、別々の SVN リポジトリで管理されてると不便なんですよね。かといって、どちらかの拠点だけしかリポジトリがないと、VPN の障害発生時に、リポジトリのない拠点からはまったくアクセスができない、という困ったことになってしまいます。

そこで、SVN リポジトリレプリケーションツール Pushmi を導入してみました。詳細は YAPC::Asia での 作者 Cl Kao によるスピーチ動画 を見て頂くとして、ここでは行った設定についてメモを残しておきます。ほとんど perldoc Pushmi の内容と同じですが。

まず当然 Pushmi のインストールが必要です。これの手順は省略。また、より良いアトミックロック実現のために memcached を利用している、ということなのでインストールしておきます。これも手順は省略。

memcached を起動します。

$ sudo /usr/bin/memcached -p 8123 -dP /var/run/memcached.pid -u nobody

/etc/pushmi.conf を設定します。弊社の環境では memcached のポートのみ指定してます。

authproxy_port: 8123

ミラーリポジトリを作成します。

$ pushmi mirror /var/db/my-local-mirror http://master.repository/svn
Mirror initialized.

ミラーリポジトリとマスターリポジトリを同期します。

$ pushmi sync /var/db/my-local-mirror
Retrieving log information from 1 to 62

ミラーリポジトリは、svnadmin create で作成するのと同様なディレクトリ、ファイル構造になってますが、Pushmi 用の pre-commit スクリプトと post-commit スクリプトが置かれている、というところが異なります。

pre-commit では以下の様なコマンドが実行され、ミラーリポジトリにコミットされた内容を、マスターリポジトリにコミットしに行きます。

/usr/bin/pushmi runhook $1 --txnname $2

post-commit では以下の様なコマンドが実行され、ミラーとマスターの整合性を確認しています。

/usr/bin/pushmi verify $1 --revision $2

Apache + WebDAV で SVN リポジトリにアクセスするために、以下の様な設定を Apache で行います。

<Location /svn/server>
  PerlSetVar SVNPath /var/db/my-local-mirror
  PerlSetVar Pushmi /usr/bin/pushmi
  PerlSetVar PushmiConfig /etc/pushmi.conf
  <LimitExcept GET PROPFIND OPTIONS REPORT>
    AuthName "mirrored private area" 
    AuthType Basic
    Require valid-user
    AuthLDAPURL ldap://localhost:389/ou=people,o=paperboy?uid?sub?(objectclass=*)
    PerlAuthenHandler Pushmi::Apache::AuthCommit
  </LimitExcept>
</Location>

設定見て分かると思いますが、mod_perl を利用していて、Apche2 + mod_perl の環境が必要です。Apache は 2.0 系でも 2.2 系でも大丈夫なようです。弊社では 2.0 系を利用しています。

特にポイントとなるのは、

PerlAuthenHandler Pushmi::Apache::AuthCommit

の部分で、認証で渡されたユーザ名、パスワードをこのモジュールで memcached にキャッシュしておき、マスタリポジトリへのコミット時の認証に利用します。Apache 2.2 系の場合には、以下の様に設定します。

AuthBasicProvider Pushmi::Apache::RelayProvider

この状態では、ミラーへのコミットは即マスターに反映されますが、マスターへのコミットはミラーに反映されませんので、以下の様な cron 設定を行い、5分おきにマスターとミラーを同期するようにします。

*/5 * * * * /usr/bin/pushmi sync --nowait /var/db/my-local-mirror

今のところこれで問題なく動いています。

話変わりまして、弊社福岡支社では、プログラマサーバエンジニア を募集しています。Pushmi を実戦で使ってみたい!という方はぜひ。メガネ女子プログラマもいますよ。(東京本社にもいます。人妻ですが。)

mod_dosdetector を Apache 2.0 系で動かすパッチ 1

Posted by Gosuke Miyashita Thu, 26 Jul 2007 13:32:00 GMT

mod_dosdetecter 0.2 を Apache 2.0 系で動かすパッチを書いてみた。修正ポイントは、

  • /usr/local/apache/include/pcreposix.h:41: error: redeclaration of enumerator `REG_BADBR’ といったエラーが大量に出てくるが、これは /usr/include/regex.h と宣言が重複しているため。なので、#include <regex.h> は削除。
  • ap_regmatch_t 構造体と ap_regex_t 構造体は 2.2 系にのみ存在し、2.0 系には存在しない。なのでそれぞれ、regmatch_t, regex_t に置き換える。
  • apr_shm_remove は 2.2系 でしか使えないので、そのための修正を mod_fcgid のソース からパクる。
  • set_ignore_contenttype_config() がセグフォるので修正
=== mod_dosdetector.c
==================================================================
--- mod_dosdetector.c    (revision 804)
+++ mod_dosdetector.c    (local)
@@ -28,7 +28,6 @@
 #include <arpa/inet.h>
 //#include <netinet/in.h>
 #include <time.h>
-#include <regex.h>
 #include "httpd.h" 
 #include "http_config.h" 
 #include "http_request.h" 
@@ -41,6 +40,7 @@
 #include "apr_strings.h" 
 #include "apr_shm.h" 
 #include "apr_thread_mutex.h" 
+#include "apr_version.h" 

 //#define _DEBUG

@@ -102,7 +102,90 @@
 static apr_global_mutex_t *lock = NULL;
 static apr_shm_t *shm = NULL;

+/* apr version 0.x not support apr_shm_remove, I have to copy it from apr version 1.x */
+#if (APR_MAJOR_VERSION < 1)
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_IPC_H
+#include <sys/ipc.h>
+#endif
+#ifdef HAVE_SYS_MUTEX_H
+#include <sys/mutex.h>
+#endif
+#ifdef HAVE_SYS_SHM_H
+#include <sys/shm.h>
+#endif
+#if !defined(SHM_R)
+#define SHM_R 0400
+#endif
+#if !defined(SHM_W)
+#define SHM_W 0200
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif

+static apr_status_t apr_shm_remove(const char *filename, apr_pool_t * pool)
+{
+#if APR_USE_SHMEM_SHMGET
+    apr_status_t status;
+    apr_file_t *file;
+    key_t shmkey;
+    int shmid;
+#endif
+
+#if APR_USE_SHMEM_MMAP_TMP
+    return apr_file_remove(filename, pool);
+#endif
+#if APR_USE_SHMEM_MMAP_SHM
+    if (shm_unlink(filename) == -1) {
+        return errno;
+    }
+    return APR_SUCCESS;
+#endif
+#if APR_USE_SHMEM_SHMGET
+    /* Presume that the file already exists; just open for writing */
+    status = apr_file_open(&file, filename, APR_WRITE,
+                           APR_OS_DEFAULT, pool);
+    if (status) {
+        return status;
+    }
+
+    /* ftok() (on solaris at least) requires that the file actually
+     * exist before calling ftok(). */
+    shmkey = ftok(filename, 1);
+    if (shmkey == (key_t) - 1) {
+        goto shm_remove_failed;
+    }
+
+    apr_file_close(file);
+
+    if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) {
+        goto shm_remove_failed;
+    }
+
+    /* Indicate that the segment is to be destroyed as soon
+     * as all processes have detached. This also disallows any
+     * new attachments to the segment. */
+    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
+        goto shm_remove_failed;
+    }
+    return apr_file_remove(filename, pool);
+
+  shm_remove_failed:
+    status = errno;
+    /* ensure the file has been removed anyway. */
+    apr_file_remove(filename, pool);
+    return status;
+#endif
+
+    /* No support for anonymous shm */
+    return APR_ENOTIMPL;
+}
+#endif                            /* APR_MAJOR_VERSION<1 */
+
+
 static apr_status_t cleanup_shm(void *not_used)
 {
     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Notice: cleaning up shared memory");
@@ -261,8 +344,8 @@

     address = r->connection->remote_ip;

-    ap_regmatch_t regmatch[AP_MAX_REG_MATCH];
-    ap_regex_t **contenttype_regexp = (ap_regex_t **) cfg->contenttype_regexp->elts;
+    regmatch_t regmatch[AP_MAX_REG_MATCH];
+    regex_t **contenttype_regexp = (regex_t **) cfg->contenttype_regexp->elts;
     for (i = 0; i < cfg->contenttype_regexp->nelts; i++) {
         if(!ap_regexec(contenttype_regexp[i], content_type, AP_MAX_REG_MATCH, regmatch, 0)){
             //ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, 0, "ignoring content-type: %s", content_type);
@@ -390,15 +473,13 @@
                      const char *arg)
 {
     dosdetector_dir_config *cfg = (dosdetector_dir_config *) mconfig;
-    char **ignore_contenttype = (char **) cfg->ignore_contenttype->elts;
+    regex_t *regexp;
+    char *type;

-    *(char **) apr_array_push(cfg->ignore_contenttype) = apr_pstrdup(parms->pool, arg);
-
-    int i;
-    regex_t *regexp;
-    for (i = 0; i < cfg->ignore_contenttype->nelts; i++) {
-        regexp = (regex_t *)ap_pregcomp(parms->pool, (char *)ignore_contenttype[i], REG_EXTENDED|REG_ICASE);
-        *(regex_t **)apr_array_push(cfg->contenttype_regexp) = regexp;
+    while (*arg) {
+      type = ap_getword_conf(parms->pool, &arg);
+      regexp = ap_pregcomp(parms->pool, type, REG_EXTENDED|REG_ICASE);
+      *(regex_t **)apr_array_push(cfg->contenttype_regexp) = regexp;
     }

     return NULL;

2.0 系でも 2.2 系でもどっちでも動くように修正した方がいいんだろうけど、それはまた今度。

Puppet の連載はじめました

Posted by Gosuke Miyashita Sat, 30 Jun 2007 06:10:00 GMT

gihyo.jp で Puppet に関する連載をはじめました。

第1回目は、システム管理の自動化がなぜ必要なのか、ということと、Puppet がどういったツールなのか、その概要について書いています。

この連載を一通り読めば、Puppet をシステム管理の現場で実践するための十分な知識が得られる、といったものを目指しています。

分かりにくいなどご意見ございましたら、ぜひお知らせください。

3週間毎に更新予定です。

紫色の何かを口に押し付けている… 2

Posted by Gosuke Miyashita Thu, 14 Jun 2007 04:02:00 GMT

会社に紫色のブツがあったので。

紫

この写真は DPL(Dr.Pepper License) により保護されています。ドクターペッパー1本おごってくれれば、自由に使ってもらって構いません。(ライセンス発案者は id:tokuhirom さんと id:hirose31 さん)

Puppet 勉強会資料

Posted by Gosuke Miyashita Fri, 25 May 2007 13:16:00 GMT

ペパボ社内で実施した Puppet 勉強会の資料をパペウィキにアップしました

内容的には、Puppetをまったく知らない人が、とりあえず動かしてみるために最低限知っておく必要のある情報をまとめてみた、という感じです。既に触っている方や、どんなものか一通りご存知の方には当たり前の内容ばかりだと思います。

Older posts: 1 2 3 ... 27