サービス

livedoor Wiki記法モジュールを公開しました

カテゴリ
ブックマーク数
このエントリーを含むはてなブックマーク はてなブックマーク - livedoor Wiki記法モジュールを公開しました
このエントリーをはてなブックマークに追加

こんにちは。 livedoor Wiki担当のぽろきぃです。先週、livedoor Wiki記法モジュールを公開しましたのでその報告です。

livedoor Wiki記法モジュールとは

Perlで書かれたlivedoor Wiki記法をHTMLに変換することが出来るモジュールです。Text::Livedoor::Wiki という名前でモジュール化されており、CPANからダウンロード & インストールできます。

sudo cpan install Text::Livedoor::Wiki

サンプル

使い方はとても簡単です。__DATA__セクションにWiki文を記述すると、HTMLとして出力するCGIも簡単に作れます。

#!/usr/bin/perl

use warnings;
use strict;
use Text::Livedoor::Wiki;
use CGI; print CGI->header;

print qq|
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link href="http://static.wiki.livedoor.jp/formatter-storage/css/cpan-like.css" type="text/css" rel="stylesheet" />
</head>
<body>
|;

my $wiki_text = do { local $/;  <DATA>; };

my $wiki 
    = Text::Livedoor::Wiki->new({
        opts => { storage => 'http://static.wiki.livedoor.jp/formatter-storage' } 
    });
my $html = $wiki->parse( $wiki_text );
print $html;

print "</body></html>";
__DATA__
* livedoorウィキ
&fukidashi(polocky){livedoor Wiki記法へようこそ!}

** 最新情報

=||
livedoor wikiでは最近、投稿フォームを
作り直したみたいですね
||=

** おすすめリンク

- [[livedoor Wiki>http://wiki.livedoor.com]]
- [[livedoor Wiki記法モジュールウィキ>http://wiki.livedoor.jp/wiki_text/]]

実行内容

上記サンプルを実行すると、このように表示されます。

ピクチャ 8


工夫したポイント 〜 シングルトン化対応

Text::Livedoor::Wikiでは、記法をプラガブルに選択でき、便利に拡張することができます。ただこのプラガブル手法を実装する場合、プラグインロード時間のコストを解決する必要があります。

シングルトン化し、インスタンスを使い回す手法により解決する道を選びました。ただし、この手法は$selfを使い回すので、メモリの肥大化などを防ぐために、$selfを汚染しない実装が必要になります。その答えとして、 local 変数を活用することにしました。

    local $Text::Livedoor::Wiki::scratchpad = {};
    local $Text::Livedoor::Wiki::opts 
      = { id_keeper => $id_keeper , 
          catalog_keeper => $catalog_keeper , 
         %$opts 
      };

この実装により、プラグイン設計時にメモリの肥大化を意識する必要がなくなりました。少し力技ですが、問題なく動作しています。

まとめ

Text::Livedoor::Wikiはこのように簡単に利用できます。利用方法の詳しくは、Text::Livedoor::Wiki ウィキにまとめてます。是非ご活用ください。

自宅で個人でラジオ放送を楽しみましょう!

カテゴリ
ブックマーク数
このエントリーを含むはてなブックマーク はてなブックマーク - 自宅で個人でラジオ放送を楽しみましょう!
このエントリーをはてなブックマークに追加
お久しぶりの洲崎です。
前回は技術ネタではなかったので今回は開発ネタです。

livedoorにはねとらじとゆー
いわゆるネットラジオのコンテンツがあるのをご存知でしょうか。
(トップページから飛ぶにはコンテンツ一覧→楽しむ→ネットラジオ)

今年2008年の1月頃に
同コンテンツの担当スタッフが一新されまして私もその中の一人です。
概ね3名で運営しています。(デザイナーさんなどを除けば)
一人はディレクターさん
一人はサーバー側のエンジニアさん
一人はクライアントアプリのエンジニア(私)です。

簡単ではありますがタイトルの通り
インターネットラジオ放送の簡単な仕組みの説明と
クライアントアプリ(放送する方)の開発に触れてみます。


■ネットラジオの仕組み



インターネットのラジオなので
実際のラジオのように音声が電波で飛んでいくわけではありません。
IPでのデータの送受信になります。

では通信するための設備や構成はとゆーと
放送する側も、それを聴取する側も、パソコンさえあればOKです。
放送する方(DJと呼びます)はサウンドカードとマイクがあればOK
聴取される方はスピーカーから音が出せるパソコンがあればOK
音声配信用のストリーミングサーバはlivedoorが無償で接続用に公開しています。

続いて放送から聴取までの流れですが
まずDJさんが放送用アプリ用意します。
公式アプリのBelugaでも構いませんし
WinAmpのプラグインでもその他のフリーソフト
Windows,Mac用それぞれ何でも構いません。
ストリーミングサーバに対してTCPでコネクションが可能で
そのコネクション上で音声をエンコードして送信できるものであればOK

視聴側はRAZIEに代表されるような
いわゆるヘッドラインソフト(公式アプリのDolphinもあります)にて
まずは番組の一覧を見ることができます。
そこから聞いてみたい放送を選び
実際の放送はWindowsMediaPlayerやWinAmpなど
拡張子m3uを再生可能な外部プレイヤーでデコードし聴取します。
なので音声データの流れとしては

DJアプリ → ストリーミングサーバ → 聴取中の複数ユーザへ配信

となります。
本日記事で取り上げるのは一番左のDJアプリ(公式アプリBeluga)です。
プラットフォームはWindowsを想定していますが基本は同じです。



■DJアプリを作る



それではDJアプリ開発に必要な要素を順に。

まず第一段階としてストリーミングサーバとの接続、コネクションは?
聞きなれないサーバかもしれませんがTCPでのコネクションです。
サーバのIP,Portを指定して接続するだけです。暗号化などは無し。
接続後に所定のフォーマットのテキストデータを送信し、
簡単な認証を行います。
(後ほどご紹介するWikiやBelugaのソースコードが参考になります)

この時、ヘッダ情報に誤りがあったり認証に失敗すると
400番台のエラーコードが返信されてきて切断されます。
また、接続に成功しても
接続後10秒以内に何の音声データも送信されない場合も切断されます。
(間隔はサーバの設定に依存)
ちなみにWindowsであればWinSockのようなAPIを利用します。
WinInetやMFC、他.NETなどが用意するものでも勿論OK

第二段階は音声データの取得です。
マイクデバイスからの生音声データは
低レベルマルチメディアAPIと呼ばれる関数群で簡単に取得できます。
デバイスの数を取得し、各デバイスの能力を取得し、デバイスを選択し、
デバイスをオープンし、音声取得のためのバッファを渡し・・・
のような流れです。
下記に挙げるAPIで検索するとサンプルや関連情報がかなり入手できますし
Belugaのソースも参考になります。

waveInOpen()
waveInPrepareHeader()
waveInAddBuffer()
waveInStart()

各APIの利用方法などは
ヘルプを読んで頂ければPGさんはすぐ理解できると思いますが
コツといいますか注意点としましては
リアルタイムに音声を拾ってそれをエンコードし、送信するので
録音用のバッファが溢れたらすぐ次のバッファを用意しなければ
その間の音声が欠落してしまいます。
この時、マルチバッファリングと呼ばれる手法を採ります。
複数のバッファを用意し、片方が溢れたらすぐもう片方を利用します。
その間に溢れた方をエンコード、送信する仕組みです。

ここまで、音声データの取得ができたところで
次は第三段階の音声エンコード(変換)です。
生の音声データそのままではサイズが大きいので
お馴染みのmp3データに変換します。
広く普及し、プレイヤーを余り選ばないコーデックを選択しましたが
Ogg Vorbis や AACでもストリーミングサーバは対応しています。

自力でエンコーダを作り変換することも可能でしょうが
Lame(レイムと読みます)と呼ばれる
軽くてソースコードも公開されてフリーのライブラリがありますので
Belugaもこれを利用しました。
なので難しいことは何も考えず(考えてもいいです)これを使います。
APIの数も少ないのでBelugaを参考に使ってみてください。

補足としてはLameエンコーダはLGPLライセンスですので
これを使ったソフトウェアもそれに沿い同じくLGPLライセンスとなります

さて音声のエンコードができましたので第四段階です。
といっても、最初のTCPコネクション上で
エンコードされたmp3データを送信するだけです。
分割サイズにも特に縛りはないようなので
適当なサイズで送信すれば後はサーバ側で
キャッシュなどは上手に処理してくれるそうです。

どうです?簡単そうでしょう?DJアプリ側はこれだけでOK。
後はサーバとヘッドラインソフトやプレイヤーが処理してくれます。

あとはGUIを凝ってみたり
波形やレベルメーターのようなものを表示してみたり
録音機能を付けてみたり色々楽しめると思います。
Belugaもバージョンアップしていきます。

最後にリンクを。
ねとらじWiki
こちらからサーバに関する情報や
ご紹介したBelugaアプリのZIPファイル(ソース込み)
今回紹介できなかったヘッドラインソフトのDolphinが取得できます。

プロに負けないほど面白い番組もありますので
ねとらじをぜひ聴取してみてください。
お気に入りのDJさんに出会えるといいですね。
それでは今後ともlivedoorネットラジオねとらじをどうぞ宜しく

洲崎でした。


追記:

通常、インターネットの世界で通話とか音声とかゆーと
いわゆるVoIPでは音声はUDPです。
何故ならリアルタイム性が高い情報なので
再送してもらってもしょうがないし(遅れて届いた音声を利用できない)
投げ捨て受け捨て、とまでは言いませんが通常はUDPを利用します。
その分信頼性はTCPよりも落ちますが高速でコネクションが不要です。
YahooメッセンジャーなどはHTTPですが。
(以前はそうでした。今はどうでしょう。NAT越え対策と思われます)
コーデックもG729とかG711とかそんなのですね。
(既存の電話やゲートウェイとの相互接続性のため)
Belugaは最初の5秒をバッファリングしてサーバへデータ送信します。
そうしないと聞く側が激しくバッファリングしようとして
音が途切れるので。以上追記でした。

livedoor Blog のニューススクロールが重い件

カテゴリ
ブックマーク数
このエントリーを含むはてなブックマーク はてなブックマーク - livedoor Blog のニューススクロールが重い件
このエントリーをはてなブックマークに追加
こんにちは。ブログ担当の nabokov7 です。

今日は、このブログの右上 ↑ にも表示されている、ニュースをスクロールさせる js の処理が重かったのを軽くしました、という話です。
livedoor Blog 開発日誌の方に書いても良かったのですが、多少技術的な話もあったのでこちらに。

問題は、Firefox で、特に長いページ (例: http://blog.livedoor.jp/ld_directors/ ) を閲覧している際に CPU使用率が異常に高くなる、というものでした。
前からある程度問題は認識していたのですが、Firefox 使用、長いページ、など、ある程度条件が重ならないと再現しなかったこともあり、btsの優先度はあまり高く設定されていませんでした。

が、気付くとこちらで問題に挙げられていたり、ついには二列向こうの同僚のところでも実害が発生している様子

ちょ、エントリよりもパッチを書いてくれればよかったのに !
うそです。ご迷惑をおかけしました。firefox が壊れてしまった件につきましては、なんといいますか、ご愁傷様です。


あわてて昨日修正しましたので、以下にその概要を説明します。
  • livedoor Blog の上部ヘッダにあるニュースのスクロールは、position=relative な div の中にヘッドラインを書きこみ、そのスタイルの top / left を逐次書き換えることでスクロールを表現しています。

  • しかし、position=relative な div の中身が書き変わるということは、それより後の要素の position も連鎖的に変わる可能性があるということです。

  • そのため、長いページを表示した際に、スクロールのたびに大量の後続要素のpositionの再計算が(もしかしたら再レンダリングも?)必要になり、CPU使用率が異常に上がってしまっていたようです。

つまり、スクロールする部分を囲っている div を position=absolute にしてやれば、後続要素の書き換えの必要がなくなり、CPU使用率の問題も解決するはずです。

が、表示部分の html はすでに全てのブログに書き出されてしまっているので html 側での対策は非現実的です。また、広告がらみの表示は、見た目をちょっといじるだけでも大人の事情が大量に発生するため、変更は最小限にとどめなければなりません。

表示部分の html はそのままにして、外部ファイルになっている css と js の置き換えだけで済む方法はないか ?

...ということで今回採用したのが、prototype.js にある Element.absolutize() を使う方法です。

これは、ある要素の表示位置はかえずに position を relative から absolute に変換したいといった時に便利な機能です。(例えば scriptaculous の dragdrop.js 内などで、 position=relative な div を任意の位置にドラッグできるように変換するために使われています。逆の動作をする relativize() というのもあります。)

prototype.js が使える環境なら

Element.absolutize('エレメントid');

と書きくわえるだけで済みます。今回の場合は prototype.js は読み込まれていないので必要な function だけを、該当するスクロール用 function の内部にほぼコピペで埋め込みました。

ひとつだけ面倒だったのが absolutize をかけるタイミングです。

このスクロールエリアはもともと <div align="right"> と指定されたdivの中にあり、右寄せで表示されていました。が、この外側のdivの右端が描画の段階では定まっていない(リキッドレイアウトっていうんですか) ため、スクロールエリア書き出しと同時に absolutize をかけると右端がうまく計算されず、ニュースが画面の遥か右にすっ飛んでいってしまうことがありました。

全体のレイアウトが定まってから absolutize するという方法もあったのですが、今回は別ファイルになっている共通CSSで該当するdivに

{ text-align: left!important; }

を指定し、強引に align="right" を無効化するという手で切り抜けました。


他にも、position=relative な div の中身をぐりぐり書き換える処理がある場合は、この辺りに注意しておいた方が良いと思いました。


モブログに潜んでいる不具合

カテゴリ
ブックマーク数
このエントリーを含むはてなブックマーク はてなブックマーク - モブログに潜んでいる不具合
このエントリーをはてなブックマークに追加
今回はモブログに潜んでいる不具合を紹介してみたいと思います。
モブログと言ってもブログサービスに限った話ではなく、SNS の日記などを携帯から投稿したり、送信するメールに写真を添付してフォトストレージサービスにアップロードしたり、その仕組みは様々なサービスに応用されています。

では、その様々なサービスに潜んでいる不具合の内容からご説明しましょう。

・件名を「サークルKサンクス」など「全角半角英数全角」としてモブログ。
・投稿された記事のタイトルが「サークルK サンクス」となる。
 「サークルK」と「サンクス」の間に半角スペースが入る。
・AU、SoftBank の端末ではこの不具合は起こらない。

こんな感じ。

なぜこの不具合が起こるか、を説明する前に RFC2822(822) の Section 2 に目を通しておいた方がよいかもしれません。
とは言うものの、英文で量も少なくないので大事な部分を抜粋します。

2.1.1. Line Length Limits

There are two limits that this standard places on the number of
characters in a line. Each line of characters MUST be no more than
998 characters, and SHOULD be no more than 78 characters, excluding
the CRLF.


2.2.3. Long Header Fields

Each header field is logically a single line of characters comprising
the field name, the colon, and the field body. For convenience
however, and to deal with the 998/78 character limitations per line,
the field body portion of a header field can be split into a multiple
line representation; this is called "folding". The general rule is
that wherever this standard allows for folding white space (not
simply WSP characters), a CRLF may be inserted before any WSP. For
example, the header field:

(p8)
Subject: This is a test

can be represented as:

Subject: This
is a test

「2.2.3. Long Header Fields」はちょっと理解しずらいですが大事なので補足的な説明を。
ヘッダフィールド(「Subject: This is a test」や「To: precure@example.com」などのこと)は

「フィールド名」「:」「フィールドボディ」「CRLF」

という構造をしていなくてはいけません。
ヘッダにおいて「CRLF」は1つヘッダフィールドの終わりを示す特別なものです。
しかし、「2.1.1. Line Length Limits」に「SHOULD be no more than 78 characters, excluding the CRLF.」とあるように、1つのヘッダフィールドは 78文字以内にするのが推奨されます。
でも、78文字を越える場合は「CRLF」で folding することができます。
その場合は、「CRLF」に続く次の行はホワイトスペースではじめましょう。
ということです。

それでは実際に DoCoMo、AU、SoftBank のそれぞれの端末でメールを送ってみて Subject: がどうなっているか見てみましょう。

まずは、DoCoMo。

Subject: =?iso-2022-jp?B?GyRCJFUkPyRqJE8bKEI=?=PrettyCure
=?iso-2022-jp?B?GyRCJEAkaCRNISohKhsoQg==?=

1行目の「=?iso-2022-jp?B?GyRCJFUkPyRqJE8bKEI=?=PrettyCure」で 57文字。78文字の制限まで多少余裕はあるけれど、次の「=?iso-2022-jp?B?GyRCJEAkaCRNISohKhsoQg==?=」を続けられるほど余裕はないので改行します。2行目はホワイトスペースを1つ入れて「 =?iso-2022-jp?B?GyRCJEAkaCRNISohKhsoQg==?=」。
RFC2822(822) の推奨通りですね。行儀が良いです。

続いて、AU。

Subject: =?ISO-2022-JP?B?GyRCJFUkPyRqJE8bKEJQcmV0dHlDdXJlGyRCJEAkaCRNISohKhsoQg==?=

AU は改行することなく1行。ちなみに、75文字。行儀よくないです。

最後に、SoftBank。

Subject: =?ISO-2022-JP?B?GyRCJFUkPyRqJE8bKEJQcmV0dHlDdXJlGyRCJEAkaBsoQg==?=
=?ISO-2022-JP?B?GyRCJE0hKiEqGyhC?=

SoftBankも改行しています。

おまけとして、Thunderbird。

Subject: =?ISO-2022-JP?B?GyRCJFUkPyRqJE8bKEJQcmV0dHlDdXJlGyRCJEAkaCRNGyhC?=
=?ISO-2022-JP?B?GyRCISohKhsoQg==?=

きれいですね。

さて、3キャリアから送信したメールのヘッダが揃いましたが、これらをデコードしてみましょう。Perl でモブログの機能を提供しているところではおそらく、MIME::Parser を使用しているのではないでしょうか。そして、肝心な Subject: のデコードは、MIME::Words::decode_mimewords で実装されていると思います。
ということで、今回は MIME::Wrods::decode_mimewords でデコードした結果を掲載します。
DoCoMo の場合。

ふたりはPrettyCure(CRLF)
だよね!!(CRLF)

AU の場合。

ふたりはPrettyCureだよね!!(CRLF)

SoftBank の場合。

ふたりはPrettyCureだよね!!(CRLF)

ヘッダの最後には必ず CRLF が含まれます。デコードしてもその CRLF が取れることはありません。なのでアプリ側で行末の CRLF を取り除いたりしていると思います。CRLF を取り除いてみましょう。このとき、DoCoMo の途中にある変な CRLF も一緒に取れてしまいます。

DoCoMo の場合。

ふたりはPrettyCure だよね!!

AU の場合。

ふたりはPrettyCureだよね!!

SoftBank の場合。

ふたりはPrettyCureだよね!!

やっぱり、DoCoMo の端末のみ「PrettyCure」の後にホワイトスーペースが入っています。AU はそもそもホワイトスペースを挿入していないのでホワイトスペースが入るわけもありませんが、SoftBank は改行してホワイトスペース(1行目は TAB)を入れているにも関わらず、デコードしてもホワイトスーペースが入っていることはありません。なぜでしょうか?
それは、DoCoMo のエンコードの仕方に問題があるからです。
メールのヘッダは ASCII を使うようにしなくてはならないので、日本語は Base64 にエンコードしますね。3キャリアすべて Base64 にエンコードされてはいますが、DoCoMo 茸もともと ASCII である「PrettyCure」をエンコードしていないのです。
(おまけとして挙げた Thunderbird は AU、SoftBank 同様すべてを Base64 でエンコードしています。)
この状態で MIME::Words::decode_mimewords を通ると、2行目以降の行頭の半角スペースが取れません。
それはなぜか?
MIME::Words::decode_mimewords をちょっと覗いてみましょう。手元のバージョンは 5.420 です。

sub decode_mimewords {
my $encstr = shift;
my %params = @_;
my @tokens;
$@ = ''; ### error-return

### Collapse boundaries between adjacent encoded words:
$encstr =~ s{(\?\=)\s*(\=\?)}{$1$2}gs;
pos($encstr) = 0;
### print STDOUT "ENC = [", $encstr, "]\n";

### Decode:
my ($charset, $encoding, $enc, $dec);
## 以下デコード処理

鍵は真ん中あたりの「$encstr =~ s{(\?\=)\s*(\=\?)}{$1$2}gs;」。
「?=」はエンコードの終了マーク、「=?」はエンコードの開始マークになります。
なので、この行で行っている処理は、

エンコードの終了とエンコードの開始マークの間にあるホワイトスペースを取り除く

ということになります。s オプションがついているので改行は無視されて単一行として扱われていますね。前述したように、DoCoMo はそもそも ASCII である「PrettyCure」をエンコードしていません。「?=」と「=?」の間にホワイトスペース以外の「PrettyCure」があるので上記の正規表現に引っ掛からないんですね。だから、CRLF の後の行頭のホワイトスペースがとれないんです。デコード後に「変な CRLF」が残っているのも上記の正規表現に引っ掛からないからです。
結果として DoCoMo の端末で件名を「ふたりはPrettyCureだよね!!」としてモブログすると、投稿された記事のタイトルが「ふたりはPrettyCure だよね!!」となるわけです。

ここまでの説明だと
「『PrettyCure』をエンコードしない DoCoMo の端末がダメなんじゃない?」
なんて思われる方もいるかもしれませんが、そうでもないみたいです。
と言うのも、RFC2047
MIME の Non-ASCII Text についての取り決めです。
RFC2047 で重要な部分を「Perlメモ - Base64エンコード・デコードする」から引用します。

1. encoded-word は 75バイト以内でなければならない.
2. encoded-word を含む行は 76バイト以内でなければならない.
3. encoded-word はそれぞれ独立してデコード可能でなければならない.
4. encoded-text をデコードした文字列の文字コードは,最後に ASCII が指定された状態でなければならない.
5. encoded-word が現れる出現位置に関する決まり.
* Subject や Comment のヘッダフィールドなどの, 'text' 内に出現.
* "(" と ")" で区切られた 'comment' 内に出現.
* From や To,CC ヘッダなどで,'phrase' 内に出現.
* 'addr-spec' 内で出現してはならない.
* 'quoted-string' 内で出現してはならない.などなど.
6. 隣り合う encoded-word の間の 'linear-white-space' は無視する.

RFC2822(822) から「ヘッダフィールドは CRLF を抜いて 78文字以内にしましょうね」と紹介しましたが、日本語 (Non-ASCII Text) を使用するときは Base64 でエンコードしますので上記の 1 と 2 から 1つのヘッダフィールドは76文字以内にしなくてはいけません。
6 は MIME::Words::decode_mimewords の
「エンコードの終了とエンコードの開始マークの間にあるホワイトスペースを取り除く」
というロジックの根拠ですね。
1 〜 6 はどれも大事なことなのでメール関係のスクリプトを書くときはおさえておきたい項目です。

そして、今回の不具合において大事なのは次の引用


RFC 2047 には本来 encoded-word に変換する必要のないもの,つまり,ASCII だけから成る単語まで変換するのは推奨できないと書かれています.ですから,実行例のように is や test. までいっしょに encoded-word に変換するのはあまりいい例とは言えません.

おそらく、上記の「推奨」を重んじて DoCoMo の端末は「PrettyCure」をエンコードしないのだと思います。

今回紹介したモブログに潜む不具合がアプリのバグなのか、RFC2047 の推奨を頑なに守る DoCoMo の端末がおかしいのかはわかりませんが、とにかく提供しているサービスに不具合があることは確かです。
担当者のみなさん、頑張って直してくださいね。

開発部「ちわ」でした。

続きを読む