2008年3月23日日曜日

UTF-8フラグにハマる

前回の続き。UTF-8の文字列をUTF-16LE with BOMに変換する際に、入力文字列によってはEncode::from_toが死ぬ。どうやら原因は入力文字列にUTF8フラグが立っているか否かのようだ(立っているとダメ)。


sub utf16bom {
        my $s = shift;
        $s = encode("utf8", "$s") if (utf8::is_utf8($s));
        Encode::from_to($s, 'utf-8', 'UTF-16LE');
        if($@)
        {
          die 'encoding error';        }
        $s= "\xff\xfe" . $s . "\x0\x0";
        return $s;
}


2008年3月21日金曜日

iTunes対応のUTF-16LE ID3v2タグを付加する

PerlスクリプトからMP3ファイルにID3タグを付けようとしてハマる。


MP3::Tag を使い iTunes で読めるようなID3タグを付けたかったのだが、何パターンか試したもののうまくいかなかった。原因がわからないので iTunes が生成した ID3 タグを MP3::Tag の出力結果と比べ、同じような結果が得られるようなコードを書いたのだがそれでもなお、うまくいかない。


寝たり他ごとしたりしつつ 12 時間ぐらいたってしまったが、解決しないので、 ID3v2 フォーマットの仕様書を見てみた。


ID3 tag version 2.4.0 - Main Frames の 4. ID3v2 frame overview には、以下の通り記述がある。


   Frames that allow different types of text encoding contains a text
   encoding description byte. Possible encodings:

     $00   ISO-8859-1 [ISO-8859-1]. Terminated with $00.
     $01   UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
           strings in the same frame SHALL have the same byteorder.
           Terminated with $00 00.
     $02   UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
           Terminated with $00 00.
     $03   UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.

   Strings dependent on encoding are represented in frame descriptions
   as , or  if newlines are allowed. Any empty strings of
   type $01 which are NULL-terminated may have the Unicode BOM followed
   by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).


つまり iTunes 互換の UTF-16 Little Endian でエンコードする場合は


  • BOM (リトルエンディアンの場合は 0xFF 0xFE) ではじまり、 0x00 0x00 で終わること


  • encoding description byte が 1 であること


上記 2 点を満たす必要があるようだ。これを MP3::Tag で素直に書くと


use MP3::Tag;
use Encode qw/encode/;
(省略)
$id3v2->add_frame("TPE1",
1, #encoding description byte
sprintf("\xFF\xFE%s\x00\x00", encode('UTF-16LE', '初音ミク'));


という感じになるのだが、手元のマシンにインストールされていた sarge の MP3::Tag 0.40 だと ID3v2.pm に以下の Encoding=0 を強制するコードがあって encoding=1 を受け入れてくれない。まったく余計なことしてくれる。


MP3/Tag/ID3v2.pm
    450         if ($fs->{name} eq "_encoding") {
    451             $encoding = shift @data unless $defenc;
    452             warn "Encoding of text not supported yet\n" if $encoding;
    453             $encoding = 0; # other values are not used yet, so let's not
         write them in a tag
    454             $datastring .= chr($encoding);
    455             next;
    456         }


仕方ないので 453 行目をコメントアウトしてしまい、とりあえず目的は達成できるようになった。だるー。さっさとソースや仕様にあたれってことですかね...


2008年3月19日水曜日

仮想ネットワークインターフェイスと MAC アドレス



Xenで仮想マシンをネットワークに接続する場合は、仮想マシンの定義ファイルに vif= 行を記述します。


vif=[ '' ]


上記設定を行うと、ランダムな MAC アドレスを持つ仮想ネットワークデバイスがひとつ作成されます。しかし、仮想マシンの起動時に毎回 MAC アドレスを乱数か生成するため、起動ごとに MAC アドレスが変化するという問題があります。


起動するたびに変わるMACアドレスの例
00:16:3e:eb:55:45
00:16:3e:03:af:32
00:16:3e:9a:54:7e
00:16:3e:db:87:62
...


仮想ネットワークインターフェイスの MAC アドレスが変化すると、Domain-U上のudevが…


  『前回使っていたのとは違うNICが挿された』


…と解釈し、 eth0, eth1, eth2 .... と、使われていない別のデバイス名を割り当ててしまいます。


この状況を解決する一番単純な方法は、仮想ネットワークインターフェイスにあらかじめ固定の MAC アドレスを割り当てることです。Xenの開発の中心であるXenSourceがベンダコード 00:16:3e を取得していますので、このアドレスレンジ中で適当な MAC アドレスを捏造します。


[root@localhost original-php]# perl
print sprintf("00:16:3e:%2.2x:%2.2x:%2.2x\n", rand()*255, rand()*255, rand()*255, );
(Control-Dを押下)
00:16:3e:05:ed:3f
#


はい、できました。この MAC アドレスを持つ仮想ネットワークインターフェイスを定義するには仮想マシン定義ファイルに以下の要領で記述します。


vif=[ 'mac=00:16:3e:05:ed:3f' ]


今回は perl を使って MAC アドレスをランダムに生成しましたが、ネットワーク上に同じ MAC アドレスを持つノードがあると困ったことになります。24ビット空間の中でランダムに生成していますが、多数の仮想ネットワークインターフェイスを生成する場合は衝突していないか確認したり、 MAC アドレスをランダムに生成するのではなく連番で振るなどの運用も検討しなければなりません。