Subscribed unsubscribe Subscribe Subscribe

「ユニコード」で予期せぬ目に遭った話

などというツイートをしたところ、思ったより反響があったのでまとめておく。

上記ではあいまいに「バリエーション」などと書いたが、Unicodeとそれを扱う環境においては、バリエーションと一口に言っても次のような状況がある。

  1. 意味論的に等価な異なる字形の集合
  2. 同じ字形で異なるコードポイントの集合

aは結構なじみ深いと思う。

a-1. 異なるコードポイントにそれぞれ異なる字形が割り当てられているもの

例:

  • 「東」(U+6771) ⇔「东」(U+4E1C)
  • 「斉」(U+6589) ⇔「齊」(U+9F4A)
  • 「高」(U+9AD8) ⇔「髙」(U+9AD9)

a-2. 同じコードポイントで、フォントによって字形が異なるもの

例:

  • MS MinchoのU+9AA8」(MS MinchoのU+9AA8) ⇔「SimSunのU+9AA8」 (SimSunのU+9AA8)
  • EmojiOneのU+1F600」(EmojiOneのU+1F600) ⇔「NotoのU+1F600」 (Noto Color EmojiのU+1F600)

a-3. 同じコードポイントで、異体字セレクタなどによって字形が特定されるもの

例:

  • EmojiOneのU+260E」(U+260E + VS16)⇔「MS MinchoのU+260e」(U+260E + VS15)
  • 「辻󠄁」(二点しんにょう U+8FBB + VS18) ⇔「辻󠄀」(一点しんにょう U+8FBB + VS17)

bの状況としては、

b-1. 意味論的にも同じだが政治的・歴史的経緯で重複して収録されているもの

例:

  • 「A」(全角アルファベットのA、FULLWIDTH LATIN CAPITAL LETTER A U+FF21)⇔「A」(アルファベットのA、LATIN CAPITAL LETTER A U+0041)
  • 「א」(ヘブライ文字アレフ U+05D0)⇔「ℵ」(アレフシンボル U+2135) *1

b-2. 意味論的に異なっているため包摂されず別個に収録されているもの

例:

  • 「西」 (WEST U+897F) ⇔「⻄」(CJK RADICAL WEST TWO U+2EC4)

さて、ツイートしたのは、b-2 の状況である。CJK Radicalsとは「部首」を表す文字のことで、意味論的には単一の部首のみで構成されるCJK Ideographとは区別される。

これの何が恐ろしかったかというと、現実に (とあるインターネットサービス上で) ユーザーが住所の一部に入力した文字としてこのCJK Radicalsが出現し、CP932に変換できないという事案が発生したからだ。

JISX0208とJISX0213で例示字形が異なることからWindowsのバージョン間でもフォントの差異があり、しばしばやり玉に上げられる「辻」などでも、最近のOS標準のIMEがIVSにも対応していることから、同様の問題が起こりそうな予感がする。やれやれ。


*1 両者が意味論的に等価かどうか、というのは議論の余地がある、たとえば「R」と「ℝ」は文脈の区別をしたいからこそ字形が異なっているわけなので。しかし、ここではdecomposed formがあるかないかを基準とすることにした。

mod_wozozo

(この記事は、Pyspaアドベントカレンダー 2016の12/4のコミットを補足する形で書かれました。)

はじめに

皆様ご存知のことと思いますが、OSSコミュニティーにおいて、開発者が結婚する際には、新郎と新婦に

オリジナル○○○○○○○○○○○

を贈呈するという慣習があります。

はい、もちろん

オリジナルApacheモジュール

ですね。

タイムライン

10/3

ハンドルwozozoだしやっぱりOzネタで行くしかないだろうということで、OzのApacheモジュールを実装することにする。

12/4

11:00

起床。全日の京都での披露宴参加からの帰宅で思いのほか疲れており、8時起床予定が大幅にずれる。

11:30

鬼平犯科帳スペシャルを見始める

13:40

鬼平犯科帳スペシャルを見終わる。そろそろアドベントカレンダーのネタ仕込みするかと思い、github.com:mozart/mozart2 をチェックアウト

13:45

ビルドインストラクションを見る。とりま cmake などを入れてみて、 cmake . などする。

13:50

cmake が通らない。自動的に clang とか LLVM とか落としてくると書いてあるのだが... 仕方なく OS (Ubuntu 15.04) のパッケージから LLVM を入れてビルドを試みることに。

14:00

OS のパッケージの clang や LLVM では、なぜかインストールされるファイルの欠損がありビルドができない。Ubuntu 16.10 では直っているようなので、do-release-upgrade を試みる。

14:20

OS のアップグレードが完了したので、ビルドを開始する。

14:30

ビルドに失敗するので、いろいろ調べてみると、ライブラリ付属の cmake ファイルの甘さがいくらかあることが判明。どのオプションが必須なのかを調査する。

14:40

オプションの指定を解明できた。ビルドが走り始める。時間がかかりそうなので、食事に出かける。

15:00

食事から戻る。ビルドは無事完了していた。ランタイムのテストをするため、Oz の言語仕様などを調べ始める。

15:15

古いバージョン (Mozart1) の資料からコピペしてサンプルコードを動かすことに成功。

15:30

標準出力に出力する方法 (System.show) などを学ぶ。

16:00

ソースコードの構成を学びはじめる。

17:00

VMのフロントエンドとしてBoostにプラットフォームの差異を吸収させたboosthost と、それの対になる boostenv というコンポーネントがあることがわかる。この部分で切り出すことでApacheモジュールに組み込めそうだという見込みを立てる。

17:10

Apacheモジュールのひな形を書き始める。apxsはそのまま使えないのでapxsから変数だけクエリしてあとはMakefileでやることにする。

18:30

催促される。

19:00

libtool に -module オプションを渡しても .so が生成できない問題の謎が解ける。 (-rpath オプションが必要だった)

19:30

ひな形にMozart2のコードの一部を組み込んだ形でコンパイルを通すことに成功する。

だが、Mozart2のビルドシステムでビルドされるコンビニエンスライブラリ (最終的なバイナリ生成物である ozemulator を生成するためにビルドの途中で便宜的に作られるライブラリ) は、PIC コードではなかったのでリンクで失敗してしまう。

仕方ないので CMAKE_CXX_FLAGS-fPIC を追加した形で Mozart2 をビルドしなおすことに。ビルドに時間がかかるので食事に出かける。

20:00

食事から戻り、作業再開。すぐにビルドには成功する。

Apacheのconfigから得られた内容をVMの起動パラメータに変換する簡単なお仕事の開始。

21:00

I/Oスレッドの扱いに悩み始める。I/Oスレッドをひとまずpost_configフェーズで起動する形で実装。

22:00

handlerVMを起動させるところまで来る、が、VMが起動しない。デバッグを続ける。

23:40

ドはまりしたが、余計なパラメータを渡していることが原因と判明、修正し、リクエストURIで指定されたドキュメントルート配下のMozart2のバイナリを読み込んで実行することに成功する。しかし、この時点では、まだレスポンスを書き出すことはできていないので、標準エラー出力への実行結果の出力で確かめるのみ。

24:00

Apacheとのつなぎこみを始める。最初の難関である、VMの起動時にVMにrequest_recを渡すという部分への対応を開始する。

25:20

VMのパラメータとして渡しているのは、値渡しのVMIdentifier VirtualMachineOptions型と、 std::unique_ptr<std::string> 渡しのアプリケーションURLのみ。

std::string を継承したクラスを作り、そこに request_rec を突っ込むという強引なやり方で成功する。

25:30

Mozart2 のビルトインプロシージャの作り方を調べる。C++のクラスの実装と、それをビルドシステムのブートストラップでclangのAPIを使ってパースしてスタブを生成するコードから成ることを解明して、スタブを自前で書くことで対応を開始する。

26:00

レスポンスを書き出すビルトインプロシージャを作るには、VM生成時に渡されたrequest_recをプロシージャの文脈で取得できなければならないが、しかし、エントリポイントは、スタティックメソッドである。

VMからビルトインモジュール名を指定してモジュールを取得できそうなことが分かったので、その方法を調べる。

27:10

vm/vm/main 配下のVM内部の実装を読み解き、ようやくモジュールを取得してもコンテキストは取れないので、代わりにビルトインプロシージャのスタブそのものへのポインタを取得する方法しかないことが判明する。

27:40

Apache.Rputs の実装ができ、ブラウザに文字を出力することに成功する。疲弊しきったため風呂に入る。

28:00

1時間ほどの仮眠を試みるが、4時間寝てしまう。

32:00

Ozで何をするかを決めようとする。I/Oスレッドが独立していることから、それが正しく動いていることを確認するためのコードを書きたく、それをそのままネタにすることにする。

32:10

TCPでの通信の方法を調べはじめる。とりあえずApacheの文脈から外れて、Ozでスタンドアロンで通信をするコードを書く。サーバー側はGoで実装した。

33:10

Goで実装したサーバーとの通信に成功する。

33:20

通信した内容をApacheを通じてレスポンスとして返すことに成功する。 ネタを仕込むための実装を始めるため、Ozの標準モジュールについていろいろ調べ始める。

33:40

ちょっとした遊びを実装するため、Apache.Rflush の実装をする。

34:10

通して動くようになる。レポジトリを作成して、push。

34:54

最後に

めでたい。

Remove all ads

FFmpegで動画編集をするガイド

こんにちは。年末になり、ますますコマンドラインで動画編集をする機会が増えてきているかと思いますが、皆様いかがお過ごしでしょうか。普段触れていないとついつい忘れてしまうffmpegのオプション群。そんなあなたのためのチートシートです。

基礎編

トランスコーディング

ffmpeg -i input.mp4 -c:v libx264 -preset medium -c:a libfdk_aac output.mp4

または

ffmpeg -i input.mp4 -vcodec libx264 -preset medium -acodec libfdk_aac output.mp4

解説

-c:v オプションは映像コーデックを、-c:a は音声コーデックを指定する。古くはそれぞれ -vcodec -acodec というオプションであった。両者は等価である。

-preset は libx264 の設定プリセット名を与える。

AAC エンコーダは ffmpeg ネイティブのものもあるが (aac) 2015年12月現在、未だ実験的 (experimental) な扱いとなっているため、libfdk_aac を代わりに使っている。なお、デコーダは experimental ではない。

(もしネイティブの aac コーデックを利用する場合には -strict experimental オプションが追加で必要となるので注意)

コンテナフォーマットの変更

ffmpeg -i input.flv -c:v copy -c:a copy output.mp4

または

ffmpeg -i input.flv -c:v copy -c:a copy -f mp4 output.mp4

解説

出力ファイルフォーマットは拡張子から自動的に決定される。-f オプションにより明示することもできる。コーデック copy を指定すると、入力ストリームはエンコード・デコードを経ずにそのまま出力に流される。

映像から音声を抜き出す

ffmpeg -i input.mp4 -c:a copy -vn output.mp4

または

ffmpeg -i input.mp4 -c:a copy -map 0:a output.mp4

解説

コンテナフォーマットに依存するが、基本的には動画ファイルは1つ以上の映像ストリームと音声ストリームから構成されている。何も指示がない場合、ffmpeg は入力のストリームの構成から自動的に出力のストリームの構成を決定する。入力ストリームと出力ストリームの対応を明示的に行うには -map オプションを用いる。-map オプションを指定した場合、対応づけが行われていないストリームは破棄される。

-map オプションに指定できるのは次のような識別子である。

識別子 説明
0:0
1:1
0番目の入力ファイルの0番目のストリーム
1番目の入力ファイルの1番目のストリーム
0:v
1:a
0番目の入力ファイルの全ての映像ストリーム (出現順)
1番目の入力ファイルの全ての音声ストリーム (出現順)
[out] libavfilter (lavf)のストリーム名 (後述)

動画に別録りした音声をつける

ffmpeg -i input.mp4 -i audio.mp3 -c:v copy -c:a aac -strict experimental -map 0:v -map 1:a output.mp4

解説

ffmpeg では -i オプションを繰り返し指定することで、複数の入力ファイルを指定できる。最初の -i オプションで指定したファイルは 0 番目のファイル、次の -i オプションで指定したファイルは 1 番目のファイルとなる。

この例では、前述の -map オプションを用いて、input.mp4 ファイルの動画ストリームと audio.mp3 の音声ストリームを合わせて output.mp4 として出力している。

動画のリサイズ

ffmpeg -i input.mp4 -s hd480 output.mp4

または

ffmpeg -i input.mp4 -s 852x400 output.mp4

または

ffmpeg -i input.mp4 -vf scale=hd480 output.mp4

または

ffmpeg -i input.mp4 -vf scale=852:400 output.mp4

解説

-s オプションは出力される動画ストリームのフレームサイズを指定する。一方、-vf オプションは動画ストリームに適用するフィルタを設定する。

明示的な指定がなくとも、常に暗黙にスケーリングフィルタが適用されるので、-s オプションの指定で大体は十分ではあるが、スケーリングフィルタを明示的に指定することで、細かな設定が可能になる。

(実は上の例で-vfオプションを指定した場合には明示的に指定されたフィルタの上に等倍のスケーリングフィルタが適用されることになり、二重にスケーリングフィルタが適用されることになるが、問題になることはない)

動画から連番のPNGファイルを生成

ffmpeg -i movie.mp4 -c:v png -f image2 output/%06d.png

解説

出力フォーマット image2 では %XXd のようなプレイスホルダーをファイル名に含めることで、フレーム番号をファイル名に含めることが可能である。

動画からアニメーションGIFを生成 (thanks to Yoshiori)

ffmpeg -i movie.mp4 -c:v gif -f gif -r 8 -s 320x180 output.gif

解説

出力フォーマット gif を使うとアニメーションGIFを生成することができる。元動画のフレームレートが高い場合、出力ファイルサイズが肥大してしまうかもしれない。そのような場合は -r オプションを指定することで、出力ファイルのフレームレートを指定して、調整を図る。

動画の特定の範囲を切り出す

ffmpeg -i movie.mp4 -ss 10 -t 20 -c:v copy -c:a copy output.mp4

解説

-ss オプションを指定すると、続く引数に指定された秒数にシークして変換を開始する。-t オプションは、続く引数に指定された秒数、変換を行う。

応用編

動画に画像を重ねる

ffmpeg -i movie.mp4 -i picture.png -filter_complex '[0:0] [1:0] overlay=x=0:y=0 [out]' -map '[out]' -map 0:a output.mp4

解説

overlayフィルタを利用すると、フィルタの0番目の入力を1番目の入力に重ねることができる。

この例では、最初の -i オプションで指定された映像のストリーム ([0:0]) をフィルタの0番目の入力に、次の -i オプションで指定された映像のストリーム ([1:0]) をフィルタの1番目の入力として、1番目の入力を0番目の入力のフレーム上の座標 (x, y) = (0, 0) に重ねている。

指定したタイミングで映像に画像を重ねる

ffmpeg -i movie.mp4 -i picture.png -filter_complex '[0:0] [1:0] overlay=x=if(gt(t,5),if(lt(t,10),0,-10000),-10000),y=0 [out]' -map '[out]' -map 0:a output.mp4

解説

overlayフィルタのパラメータにはexpressionを使うことができる。この例では現フレームの時間であるtという変数を使ってオーバーレイする座標を非連続に設定している。

5秒未満: x=-10000 10秒未満: x=0 10秒以上: x=-10000

重ねる対象を x=-10000 のように画面外に持ってくることによって特定のタイミングで重ね合わせを行うことを実現している。

まとめ

とくにまとめるような内容ではないのですが、よいお年をお迎えください。

Many thanks to pyspa community.

この記事はPyspa Advent Calendar 2015のエントリとして書かれました。次の記事はこちらです。

識別子に絵文字などを使えるようにする

とある言語で識別子に絵文字などのシンボルが使えるそうなのですが、一応GoだってUnicodeクリーンなんだ。

f:id:moriyoshi:20140603121552p:plain

これをコンパイラに食わせてみる。

$ go run sushi.go

どうだ?

# command-line-arguments
./sushi.go:3: invalid identifier character U+1f363
./sushi.go:8: invalid identifier character U+1f363

なのでちょっと悔しい。ということでやりました。


$ go run sushi.go
sushi~

「:」を引数の名前と型の間に入れられるようしたりにする魔改造

とある言語が

func halfOpenRangeLength(start: Int, end: Int) -> Int {
    return end - start
}
println(halfOpenRangeLength(1, 10))
// prints "9"

のような文法だったのを見てウッと思ったのでやってみた

Go のソースを落としてきて

を当てて

$ (cd src/cmd/gc && rm y.tab.h && make)

とする。

package main

type Int int

func halfOpenRangeLength(start: Int, end: Int) -> Int {
	return end - start
}

func main() {
	println(halfOpenRangeLength(1, 10))
	// prints "9"
}
$ go run test.go
9

どーん

MacPro に似た形状のアレを1年使ってみての感想

当時話題沸騰だったアレを、2013年6月11日に購入した。

f:id:moriyoshi:20140602194259j:plain

こんな感じでアップル製品と一緒に並べてみて一通り満足してから、Garbage Collectorとしての利用を開始。

f:id:moriyoshi:20140602200520p:plain

中側の筒 (写真左) の上から外側の筒 (写真右) をかぶせる形で使用する。このような構造になっているのは、ビニール袋を中側の筒にかぶせてあっても、外側の筒で覆い隠せるようにし、美観を損ねないようにするための配慮だ。

近影
f:id:moriyoshi:20140602191928j:plain

  • ビニール袋を中に入れても外側の筒で覆い隠せるので美観が損なわれないのは良い
  • 中の筒は外側の筒よりも半径が3cmほど小さいので、見た目よりかなり容量が小さく感じられる
  • 性能上は競合製品と際立った差は見られない

ロシアでは

別れの時にNginxのモジュールを贈るのが習わしとなっていますので、作りました.

https://github.com/moriyoshi/ngx_ymsr_module

nginxをビルドするときに、

$ ./configure --add-module={ngx_ymsr_moduleのパス}

を指定してあげればOKです。

id:Yamashiro0217 を偲ぶパスを心に決めてください。

あとは、

location / {
    ymsr on;
}

そのURLにアクセスしてください。

moriyoshi@chicwab ~% curl -vv http://localhost/
* About to connect() to localhost port 80 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5
> Host: localhost
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: ngx_openresty/1.4.3.6
< Date: Sat, 15 Feb 2014 12:45:51 GMT
< Content-Type: text/html
< Content-Length: 5
< Last-Modified: Sat, 15 Feb 2014 11:09:21 GMT
< Connection: keep-alive
< ETag: "52ff4ae1-5"
< X-Yamashiro: エンジニアにメモリを与えないと転職する。あると思います。
< Accept-Ranges: bytes
<
test
* Connection #0 to host localhost left intact
* Closing connection #0

おおっ、 リクエストのたびに名言が出力される X-Yamashiro ヘッダが追加されましたね!

まとめ

R.I.P.

Remove all ads