ラベル video の投稿を表示しています。 すべての投稿を表示
ラベル video の投稿を表示しています。 すべての投稿を表示

2009年5月22日金曜日

I wana just add VBR-header to mp3 files...

ffmpegを使って
ffmpeg -i smile.flv -acodec copy output.mp3
ってな感じでflv動画から音を抜いてるんですが、これをやったあとに付きまとう問題点。。。

元の音声がVBRだろうと何だろうと、-acodec copyでとにかく抜き出せるのは良いものの、抜き出して出来たファイルにはVBRヘッダが無いので、プレーヤによっては再生時間がとんでもないことになっちゃったりします。
例えば、実際の再生時間は4分程度の筈なのに、32分として扱われちゃったり。

なので、VBRヘッダをファイルに付加する方法を探してたんですが、linux環境向けのツールを見つけられませんでした。。。

なので、とりあえずWindows向けのVBR Header Makerをwineで使用。
うわぁ、、こんな簡単に出来るのになぁ。。。

2008年12月24日水曜日

encode video with h264+aac codecs by ffmpeg on Ubuntu8.10 Intrepid Ibex

Ubuntu8.04まではMedibuntuのリポジトリを入れていれば、h264,aacなどのコーデックに対応した版のffmpegがaptでインストール出来たのですが、8.10からやり方が変わったようです。

ffmpegでPSP用に動画をエンコードしようとして、「h264なんてエンコーダ知らないよ」とエラーメッセージを吐かれてびっくらこきました。
確かに、ffmpeg -formatsの出力結果のCodec欄を見ても、h264の行にエンコード可を示すEの字がありません。

Medibuntuの8.04Hardy用のパッケージ一覧ページを見るとちゃんとffmpegがあるのが分かりますが、8.10Intrepidのページではffmpegがそもそもffmpegがありません。
Intrepidでは、ffmpeg本体は公式リポジトリのものを使い、h264などのエンコードには外部ライブラリで対応するという形のようです。


IntrepidのMedibuntuリポジトリを入れた状態で、libavcodec-unstripped-51,libavdevice-unstripped-52,libavformat-unstripped-52,libavutil-unstripped-49,libpostproc-unstripped-51,libswscale-unstripped-0の6つのパッケージをインストールします。
$ sudo apt-get install libavcodec-unstripped-51 libavdevice-unstripped-52 \
libavformat-unstripped-52 libavutil-unstripped-49 libpostproc-unstripped-51 \
libswscale-unstripped-0


Ubuntu公式のリポジトリにはlibavformat52といったパッケージがありますが、これらのMedibutu版が〜-unstripped-〜と名前についたパッケージ達です。ffmpeg本体は公式のもののままで大丈夫のはずです。

これでffmpeg -formatsを実行すると、h264のところはそのままですが、「 EV libx264」という行が追加されている筈です。libx264というのはh264コーデックでエンコードするためのffmpeg外部のライブラリです。(libx264パッケージは勝手にインストールされます)
実際にエンコードを行う場合には、以前なら「-vcodec h264」としていた箇所を、「-vcodec libx264」とすれば良いです。aacも、libfaacとします。
この他にも、外部ライブラリとして対応するようになったものがあります(ogg,xvidなど)。ffmpeg -formatsで確認してください。

試しに、一つ動画をPSP用にエンコードしてみましたが問題なく再生出来ました。

$ ffmpeg -i smile.flv -acodec libfaac -ab 256kb -vcodec libx264 -b 256kb -ar 48000 -mbd 2 -coder 1 -cmp 2 -subcmp 2 -aspect 360:272 -s 360x272 -r 30000/1001 -f psp -flags loop -trellis 2 -level 30 -threads 2 output.mp4 -y

2008年11月28日金曜日

ffmpeg for psp

ただの覚書。
内容については保証できない。

成功例
$ ffmpeg -i input.mp4 -acodec aac -ab 128kb -vcodec h264 -b 1200kb -ar 48000 -mbd 2 -coder 1 -cmp 2 -subcmp 2 -aspect 480:272 -s 480x272 -r 30000/1001 -f psp -flags loop -trellis 2 -level 30 output.mp4
参考:http://d.hatena.ne.jp/amt/20070422/LevelOptions

ffmpeg -i input.mp4 -acodec aac -ab 128kb -vcodec h264 -b 1200kb -ar 48000 -mbd 2 -coder 1 -cmp 2 -subcmp 2 -aspect 480:272 -s 480x312 -r 30000/1001 -f psp -flags loop -trellis 2 -level 30 -croptop 20 -cropbottom 20 output.mp4


$ ffmpeg -i smile.mp4 -f psp -s 320x240 -r 29.97 -b 768k -ar 24000 -ab 128k output.mp4

$ ffmpeg -i smile.mp4 -f psp -aspect 16:9 -s 320x272 -croptop 46 -cropbottom 46 -r 29.97 -b 256k -ar 24000 -ab 128k croppedoutput.mp4

memo

cropの不思議(Ubuntuのリポジトリにはいってるffmpegで確認)
cropで指定するピクセル数は元動画の画面サイズからいくら引くか、で考える。
画面サイズを縮小する場合もそう。ただし、-s指定する画面サイズは「縮小後サイズ+元動画からcropするピクセル数」とする。そうしないと、動画のプロパティ上と実際のデータと違うサイズとなる。

となる。

サムネイル
ffmpeg -i saihateband.mp4 -f image2 -ss 7 -vframes 1 -s 160x120 -an test.jpg

2008年11月24日月曜日

正規表現が何故/〜/なのか



4:00の奇跡に刮目。

プリン吹き出しそうになった。

2008年10月27日月曜日

PSP向けに動画を変換出来ない。

PSPを買ったのだけれど、動画を思い通りに扱えない。
何度やっても、非対応データと言われる。

このページを参考に、
$ ffmpeg -i test.mpg -f psp -r 29.97 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
とやってみて、再生できたのが唯一の成功例。
けど、24kHz/32kbpsなんて糞音質で聞けるわけねーだろチキショウと、値を上げてやったりするとそれだけで、もう非対応データとなってしまう。

やはり、Ubuntuのリポのffmpegを使ってるのがいかんのだろうか。
グーグル先生に聞いても、載っている他のバージョンのffmpegの設定例なので、その通りになるわけも無いというのは分かってるんですが、悔しい。

2008年9月23日火曜日

ニコスクリプトでLispインタプリタ


こんなん見つけました。

残念ながら私はLisp知りませんが orz

2008年8月14日木曜日

端末で「初音ミクに「Ievan Polkka」を歌わせてみた」を再現してみた のソースコード


先週くらいに作ったこの動画で、ソースが欲しいという米があったので、ここに私の酷いコードを晒します。ソース自体は無駄な長さなので、この記事の最後に置いときました。
あと、端末とは言ってますが実際はLinux上の端末エミュレータってやつですね。スミマセン。

実行した環境はUbuntu 8.10訂正:8.04のgnome-terminal上です。フォントはデフォのまま。

一応、動画作成に至った流れの解説というかなんというか。

コマンドプロンプトの人のを見る→せめてちゃんとしたAAで……俺が作るか!→よしゃ出来た。あれ?なんかもう他にも色々うpされてる→もっとなにか拈りを入れないと→他の顔のバリエーションも作ろう→AAを分解したらもっと短くなるけど、全体コピペして微調整したらAA分解するの面倒くさくなった。描画する関数書き変えるのも面倒→ソースの半分をAAが占めることに→どうせなら、背景のロゴも→どうせなら動画全体を再現……→プログラムの全体構造書き変えるの面倒くせえ。メインループそのままでぶん回しながら時間計ってifで→あ、歌詞入れるタイミングとネギ振りのタイミングが同期するわけじゃないから、ネギ振りに合わせてsleepすると……→まぁ、いいや。

他の方の環境では、フォントが違ったりしてAAがうまく表示され無いかもしれません。
ちなみに、私の環境で端末のウィンドウを横73文字x縦27行になるようにリサイズしたらちょうど512x384ピクセルくらいになりました。

ソースは途中で面倒くさくなって定数を#defineしなくなったりしてます。もうちょっと丁寧に作るべきだったか……。最後の1行分だけ歌詞抜けちゃってるし。
AAは等幅フォント用に作ってますが、私の環境で全角文字が半角幅で表示されるという現象が起こったので、半角スペースを入れて微調整してあります。なので、普通に表示できる環境ではAAの一部がズレてます。

Cursesは、2バイト文字対応のncurseswというライブラリを使いました。ライブラリを導入してから、-lncursesw とオプションを付けてコンパイルしてください。

以下、「動きゃいいんだよ動きゃあ」精神のコード。動画に使った物そのままです。
テキストエディタで特殊文字をエスケープ文字に置換してからコピペしましたが、マズってるかもしれないのでお気を付けて。
#include <stdio.h>
#include <ncurses.h>
#include <locale.h>
#include <time.h>
#include <sys/time.h>

//screen size(row x col)
// 27x73

#define true 1
#define false 0

//for sleep command
#define DELAY 1000

#define UP 0
#define DOWN 1

#define MIKU_SIZE_ROW 17
#define MIKU_SIZE_COL 37

#define REAL_MIKU_SIZE_ROW 26
#define REAL_MIKU_SIZE_COL 47

//Negi ASCII Arts
char *negiUp[] = {
"くヽ l^i ",
" \\l l ",
" \\l ",
" \\ ",
" \\",
" \"
};

char *negiDown[] = {
" <\ ",
" \\ ",
"┌ ーーーーーー",
"└ ーーーーーー"
};


//Miku ASCII Arts
char *normal[] = {
" rへ ___ __ ",
" /: : : : : Y: :ヽ ",
" /: ./l: lヽ::ヽ: :ヽ ",
" //: /ノ l:ト、ヽ: :':: .', ",
" / l: / ○ 丶l ○ l: :ハ : : ',",
" _/: l: l@┌ ー┐ @l:/:l: : .',",
"/:::\ヽl \ │ │ /://l : :.i",
"{:::::::`T7└-ー┴‐< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};


char *cool[] = {
" rへ ___ __",
" /: : : : : Y: :ヽ",
" /: ./l: lヽ::ヽ: :ヽ",
" //: /\ l:ト /: :':: .',",
" / l: / ○ 丶l ○ l: :ハ : : ',",
" _/: l: l@ @l:/:l: : .',",
"/:::\ヽl \ /\ /://l : :.i",
"{:::::::`T7ヽ ̄ー-< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *tired[] = {
" rへ ___ __ ",
" /: : : : : Y: :ヽ ",
" /: ./l: lヽ::ヽ: :ヽ ",
" //: /ノ l:ト、ヽ: :':: .', ",
" / l: / ○ 丶l ○ l: :ハ : : ',",
" _/: l: l@ @l:/:l: : .',",
"/:::\ヽl \ /\ /://l : :.i",
"{:::::::`T7ヽ ̄ー-< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *sleep1[] = {
" rへ ___ __",
" /: : : : : Y: :ヽ",
" /: ./l: lヽ::ヽ: :ヽ",
" //: /ノ l:ト、ヽ: :':: .',",
" / l: / =丶l =l: :ハ : : ',",
" _/: l: l@┌ ー┐ @l:/:l: : .',",
"/:::\ヽl \ │ │ /://l : :.i",
"{:::::::`T7└--u┴‐< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *sleep2[] = {
" rへ ___ __",
" /: : : : : Y: :ヽ",
" /: ./l: lヽ::ヽ: :ヽ",
" //: /ノ l:ト、ヽ: :':: .',",
" / l: / =丶l =l: :ハ : : ',",
" _/: l: l@┌ ー┐ @l:/:l: : .',",
"/:::\ヽl\ │ │ /://l : :.i",
"{:::::::`T7└--u┴‐< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
",': : : / /:/: : l:::l. l : : :.l",
"i : : く/レへ : l::::',! : : : l",
"l : : : \:::::ヽイ::::〉l : : : l",
"l : : : : ト、:::::>tjtjノ |: : : : l",
"',: : : :/ ├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *realMiku[] = {
" ,.-.‐ . へ",
" {少'\" : : : 幻、\",
" /:/:: : : Y::i : :ヽ.",
" /:/N: iヽ: l_:_: :l: ハ",
" ,{:/:Lヾ{ 'Tヽ|::} ハ:: :i",
" /:ヽ:|心 、 ゛ .}'-:'/l: :!",
" /: : :>:ヽ ー;,イiTく l: : l",
" /: : :/ `≧ 令ーツー.、 l: : :l",
" /: : :/ r‐ f^小:::./ }l: : :l",
" /: : :/ j//./::::Y i / l: : :l",
" /: : :/ /:}/./:::::ll. 01! l: : :l",
" /: : :/ /://./:::::::! ̄: | l: : :l",
" /: : :/ /://./:::::::/|;;;;| l: : :l",
" /: : :/ /// ':::::::/ |;;;;| l: : :l",
" /: : :/ /':/ i:::::{ |;;;;| l: : :l",
" /: : ://:::/ i::::::\|;;;;;| l: : :l",
" /: : :/::::〈 i:::::::`!;;:;;;| l: : :l",
" / : ;〈:::::/ヽ/::::::::: |;;;;;;l l: : :l",
" /: :∧ ヽ:::/;;;;\::::::/ |;;;;;;| l: : :l",
" /: :/;;Y ゝ;;;;;:::;\::/;.;ィ|;;;;;;|. l: : :l",
",: : /、;lヽ√`ート::::,;r-Y\"..\"ィ|;;;;;;| |: : l",
"!: :′l>、| `ー- L_ ̄ i 斗 ´ |;;;;;;l.l: : l",
"l: :i ^|` ー - | ̄|_,.- ‐ 1ヽ;;;;;ノ l: : l",
" ',:l |;;;;;;:! l;;;;;;| ヒ!ヘハ! l: ,'",
" ゛:, |;;;;;:i l;;;;;;| } :/",
" ヾ:. |;;;;;:! l;;;:;;| /:/",
" |;;;;;! l;;;;;;| /'"
};

char *mikuLogo[] = {
" _ _",
" ∪ ∪",
" ┌─ ─┐┌ ─ ┐┌ ─ ─┐ ∩ __",
" └─ ./└_┐ |└┐┌┐┌┘│ └ ─ ┐/ |",
" // _||||┌┘└┘└┐└ ─ ─ ┘\/~||",
"//||//||||└ ─ ─┘┌ ─ ─ ┐ /|",
"|/|トゝ|||| ┌ ─ ,└ ─ ─ ┘ //",
" || |||| │ =)┌ ─ ─ ┐//",
" └┘ └┘└┘ └ ─ '└ ─ ┐//",
" HACHUNE MIKU |/",
};

//Lyrics
char *lyric0 = "あらっつぁっつぁ〜やりびだびりんらばりったんりんだんれんだんどぅ";
char *lyric1 = "まばりぱっぱーっぱりぱり ぱりりりびりびりすてんれんだんどぅ〜";
char *lyric2 = "やばりんらんてんだんでんあろ〜 わらば るぶるぶるぶるぶれいえぶ〜";
char *lyric3 = "ばりったんりんだんでんらんろ〜 たた たたたたとぅとぅでんやぶ〜";
char *lyric4 = "りんらんてんだんりんらんてんだん りんらんりんらんりんりんりりり";

//values
int nowNegiType = UP;
int sleepingType = UP;
int mikuRow;
int mikuCol;

void addLogo(int ha, int li, int vo){
int i;

if(ha){
for(i = 0; i < 10; i++){
mvaddstr(LINES - 14 + i, 0, mikuLogo[i]);
}
}

if(li){
for(i = 0; i < COLS; i++){
mvaddstr(LINES - 3, i, "-");
}
}

if(vo){
mvaddstr(LINES - 2, 2, "VOCALOID2");
}
}

void addAA(char *miku[]){
int i;
for(i = 0; i < MIKU_SIZE_ROW; i++){
mvaddstr(mikuRow + i,mikuCol,miku[i]);
}
}

void addLyric(char *lyric){
mvaddstr(LINES - 1, 5, lyric);
}

void addNegi(int type){
int i;
if(type == UP){
for(i = 0; i < 6; i++){
mvaddstr(mikuRow + i + 1, mikuCol - 11, negiUp[i]);
}
}else{ //DOWN
for(i = 0; i < 4; i++){
mvaddstr(mikuRow + i + 4, mikuCol - 14, negiDown[i]);
}
}
}

void mvAddNegi(int row, int col, int type){
int i;
if(type == UP){
for(i = 0; i < 6; i++){
mvaddstr(row + i, col, negiUp[i]);
}
}else{ //DOWN
for(i = 0; i < 4; i++){
mvaddstr(row + i, col, negiDown[i]);
}
}
}

void toggleSleepMiku(){
if(sleepingType == UP){
addAA(sleep1);
sleepingType = DOWN;
}else{
addAA(sleep2);
sleepingType = UP;
}
}

void toggleAddNegi(){
addNegi(nowNegiType);
if(nowNegiType == UP){
nowNegiType = DOWN;
}else{
nowNegiType = UP;
}
}

double getTimeOfDay_sec(){
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + (double)tv.tv_usec*1e-6;
}

double getPassedTime(double startTime){
return (getTimeOfDay_sec() - startTime);
}

main(){

int i;
double passedTime;
double startTime;
int sleepTime;
int logoHa;
int logoLi;
int logoVo;

setlocale(LC_ALL,"");
initscr();
cbreak();
noecho();

mikuRow = LINES - MIKU_SIZE_ROW - 3;
mikuCol = COLS - MIKU_SIZE_COL - 1;

logoHa = false;
logoLi = false;
logoVo = true;

startTime = getTimeOfDay_sec();

sleepTime = 0;

//ここから動画の描画

//初期状態 VOCALOID2ロゴのみ
addLogo(false, false, true);
move(LINES - 1,COLS - 1);
refresh();
usleep(5500 * DELAY);

//ボーダーライン表示
for(i = 0; i < REAL_MIKU_SIZE_ROW; i++){
mvaddstr(LINES - REAL_MIKU_SIZE_ROW + i,
COLS - REAL_MIKU_SIZE_COL - 1,
realMiku[i]);
}
addLogo(true, false, true);
move(LINES - 1,COLS - 1);
refresh();
usleep(3500 * DELAY);

//初音ミク ロゴ登場
clear();
for(i = 0; i < REAL_MIKU_SIZE_ROW - 3; i++){
mvaddstr(LINES - REAL_MIKU_SIZE_ROW + i,
COLS - REAL_MIKU_SIZE_COL - 1,
realMiku[i]);
}
addLogo(true, true, true);
move(LINES - 1,COLS - 1);
refresh();
usleep(7000 * DELAY);

while(1){
clear();
passedTime = getPassedTime(startTime);
addLogo(true, true, true);

//ミク
if(passedTime <= 16.8){
//登場 休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 32.0){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 33.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 48.5){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 49.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 64.5){
//居眠り
sleepTime = 500;
toggleSleepMiku();
addNegi(UP);
}else if(passedTime <= 65.0){
//YO!!
sleepTime = 500;
mvaddstr(mikuRow - 1, mikuCol + 10, "!!!");
addAA(cool);
addNegi(UP);
addLyric("            YO!!!");
}else if(passedTime <= 80.0){
//激しい振り
sleepTime = 125;
addAA(cool);
toggleAddNegi();
}else if(passedTime <= 81.0){
//疲れ休符
sleepTime = 100;
addAA(tired);
addNegi(UP);
}else if(passedTime <= 96.0){
//疲れ振り
sleepTime = 250;
addAA(tired);
toggleAddNegi();
}else if(passedTime <= 97.0){
//りんらんりんらんりんらんりんらん

sleepTime = 125;
addAA(tired);
toggleAddNegi();

}else if(passedTime <= 113.0){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 114.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 129.0){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 130.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 145.0){
//居眠り
sleepTime = 500;
toggleSleepMiku();
addNegi(UP);
}else if(passedTime <= 145.1){
//ネギ落下 中段
sleepTime = 50;
addAA(sleep1);
mvAddNegi(mikuRow + 8, mikuCol - 13, DOWN);
}else if(passedTime <= 146.0){
//ネギ落下 中段
sleepTime = 50;
addAA(sleep1);
mvAddNegi(mikuRow + 13, mikuCol - 14, DOWN);
}else{
usleep(5000 * DELAY);
break;
}

//歌詞
if(passedTime <= 17.0){
//前奏
}else if(passedTime <= 20.0){
addLyric(lyric0);
}else if(passedTime <= 24.0){
addLyric(lyric1);
}else if(passedTime <= 29.0){
addLyric(lyric2);
}else if(passedTime <= 32.0){
addLyric(lyric3);
}else if(passedTime <= 37.0){
addLyric(lyric0);
}else if(passedTime <= 40.0){
addLyric(lyric1);
}else if(passedTime <= 45.0){
addLyric(lyric2);
}else if(passedTime <= 48.0){
addLyric(lyric3);
}else if(passedTime <= 81.0){
//間奏
}else if(passedTime <= 85.0){
addLyric(lyric0);
}else if(passedTime <= 89.0){
addLyric(lyric1);
}else if(passedTime <= 93.0){
addLyric(lyric2);
}else if(passedTime <= 97.0){
addLyric(lyric4);
}else if(passedTime <= 101.0){
addLyric(lyric0);
}else if(passedTime <= 105.0){
addLyric(lyric1);
}else if(passedTime <= 109.0){
addLyric(lyric2);
}else if(passedTime <= 113.0){
addLyric(lyric3);
}else if(passedTime <= 117.0){
addLyric(lyric0);
}else if(passedTime <= 121.0){
addLyric(lyric1);
}else if(passedTime <= 125.0){
addLyric(lyric2);
}else{
//do nothing
}

move(LINES - 1,COLS - 1);
refresh();
usleep(sleepTime * DELAY);
}

//終了
endwin();

}

2008年8月12日火曜日

ffmpegでニコニコ動画用のmp4動画を作る

一昨日のエントリで愚痴をこぼしましたが、あとあとmp4で再エンコせずにしっかりアップ出来ました。

参考にしたのはこちら↓

エンコード設定(中画質~高画質) - ニコニコ動画まとめwiki: ffmpegを用いてmp4(H.264、AAC)で2passエンコードする
http://nicowiki.com/%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E8%A8%AD%E5%AE%9A%EF%BC%88%E4%B8%AD%E7%94%BB%E8%B3%AA%EF%BD%9E%E9%AB%98%E7%94%BB%E8%B3%AA%EF%BC%89.html#teeca4db

「まとめWikiじゃねぇか!」と怒らないで orz
わかってます。基本目を通すべきだってのはわかってます。でも、素で見逃してました('A`)

んで、本題。
上記ページに、ffmpegを用いてmp4で2passエンコードする場合のコマンドの例が書かれています。
コーデック指定にlibx264・libfaacと指定していますが、Ubuntu 8.04のaptでインストールできるffmpegのversionでは、そのままh264・aacと指定できます。("$ ffmpeg -formats"の出力結果参照)

<20081222追記>
Ubuntu8.04のリポジトリでなく、正確にはMedibuntuのUbuntu8.04向けリポジトリだったかと

(以降のコマンドラインの例は、私が実際に行ったものとはちょっと違っています。入力動画を切り分けるために-tオプションを使いましたが、一般例としては必要ないので割愛しました。また、ファイル名もわかりやすく変えてあります。)

私はとりあえず普通に1passで試してみました。
$ ffmpeg -i input.mpeg -i input.mp3 -s 512x384 -vcodec h264 -b 300k -r 25.00 -acodec aac -ab 64k -ar 44100 output.mp4
ビットレートが 動画300k + 音声64k = 364k となるかと思いきや、この結果出てきたファイルは570kbpsでした。
どうやら、実際に指定している値より高めになるようです。このために、初め動画のビットレートを500kと指定した時には700kbps台のファイルが出来てしまい、ちょうど良いbpsになるように何度も繰り返す羽目に。(そういうもんなのかな?)

それと2passの場合。
$ ffmpeg -i input.mpeg -vcodec h264 -r 25.00 -b 500k -s 512x384 -pass 1 -passlogfile "./passlog" -an "./1pass.mp4"
$ ffmpeg -i input.mpeg -vcodec h264 -r 25.00 -b 500k -s 512x384 -pass 2 -passlogfile "./passlog" -i input.mp3 -acodec aac -ar 44100 -ab 64k "./2pass.mp4"

こちらの場合は、動画のビットレートが指定したとおりにエンコードされました。

1passも2passもニコニコ動画に再エンコード無しでアップロード出来ました。
ただし、いずれも実際にうpした後のデータをダウンロードしてみると、元のファイルと少しファイルサイズやヘッダが違っているようです。bpsも少しだけ食い違っています。
ですが、大幅に画質が落ちたりしていないので、再エンコードはされていないと判断してます。ニコニコ動画の仕様で、適切なエンコードを行っていたとしても、うpされたデータは必ずエンコード処理に通されます(エンコードしています、とページに表示される)。その時に、実際には再エンコードされていなくても、ヘッダファイルの内容だけ書き変えたりしているのかも。

ちなみに、テストでうpしたその動画は短い上に元の映像も私のデスクトップをキャプチャした少し質の悪いものだったので、1passと2passの差がまったく分かりませんでした orz
再生時間がそれなりに長い動画でなければ違いは出てこないんじゃないかと思うので、よほど質を気にしなければ1passで十分です。

実際にうpした動画のスクリーンショット。

flvでエンコードしたが、再エンコされてしまった場合。


1passのmp4+aacで、再エンコ無しの場合。


やはり、後者の方が圧倒的に綺麗です。文字もくっきり見えて読めます。(当たり前か)


それと最後に注意事項。私と同じ轍を踏む方が一人でもいなくなるように。。。

エンコードする際には必ず、-s -b -r(画面サイズ・映像ビットレート・映像フレームレート)などの値を指定しましょう。オプション指定せずにffmpegのデフォルト値のままでエンコードして40MB以下/600kbps以下になるとしても、ちゃんと指定しないと何故かうpしたときにエンコード処理の時点でエラーで弾かれます。
一昨日あたりに失敗した時は、元の動画を作成する時点(デスクトップのキャプチャ)でサイズが512x384になるようにしていたので、ffmpegによるエンコード時点では-sオプションを使いませんでした。また、フレームレートもデフォルトのままで良いと思い、-rを指定しませんでした。
確証はありませんが、これが失敗した原因だと思います。デフォルト値と同じ値だったとしても、指定した場合としなかった場合では書き出されるヘッダが違っているのではないかと私は思っています。

それと、エンコードした動画を確認するのにTotemは使わない方が良いでしょう。Totemに限らずGstreamerを使っているプレイヤーではAdobeの純正のflash playerに比べ再生時の画質がかなり落ちるようです。
Linux上のブラウザでflashを再生するのにGnashなど使っている方もいると思うのですが、私はAdobe純正のプラグインを使っています。
http://www.adobe.com/go/gntray_dl_getflashplayer_jp
Gnashなど他のプレーヤーのプラグインと比べた訳ではないので、純正が1番と言うつもりはないですが。

兎に角、これで良画質でニコニコ動画にうpできるようになりました^^

<追記>
Ubuntuのバージョン間違えてました。この記事書いた時点では8.10は出てない出てない。

2008年8月11日月曜日

またニコ動にうpしてみた



我ながらひどい内容です。

毎回、映像作成してる時間の方が長いというジレンマ。
Linuxでニコ動用にエンコする方法を体得してないので、やはり再エンコ。

2008年8月10日日曜日

ffmpegはニコ動と相性が悪い?

というか、ニコニコ動画のほうが制限かけすぎな感じがする。
ffmpegで作ったflvファイルは、再エンコされたかと思ったら、ファイルヘッダがおかしいといわれ終了。
再エンコありきで考えて、画質の出来るだけ良い動画をそのままうpするのが手っ取り早いけど、それをやると負けかなと思っている。

やっと作った動画をうpようとしたらこんな風に弾かれて、時間だけ消費して悲しくなった、という記事。

<追記>
解決しました。
ffmpegでニコニコ動画用のmp4動画を作る

2008年8月8日金曜日

ニコニコ動画にアップしてみた



sm4171553:コマンドプロンプトでネギを振らせてみたを見て作り始めたはいいものの、いろいろ面倒くさかった……

初めはRubyでHachuneMikuクラスでも作ってやろうと思ったんだけど、RubyのCursesで2バイト文字が扱えないらしく、Cでガリガリ書くことに。

この記事の日付が8月8日で、動画は9日の00:24になってますが、キニシナイキニシナイ。

2008年7月28日月曜日

ffmpegで動画から音声を分離

ffmpegを使うと、動画から音声を分離したりするのは簡単にできる。

まず、音声コーデックをチェック
$ ffmpeg -i input.mp4 2>&1|grep Audio
Stream #0.1(und): Audio: aac, 44100 Hz, stereo
$

音声コーデックがaacなので、出力先の拡張子をaacとする。
$ ffmpeg -i input.mp4 -acodec copy output.aac

(<追記>aacはあくまでコーデックであり、ファイル形式(コンテナ)とは違うんですけどね)

ffmpegでは出力先に指定したファイル名の拡張子で、出力するファイルフォーマットを判断するので、先に必ず動画の音声コーデックをチェックしなければならない。音声コーデックがaacなのにmp3などの拡張子を付けるとエラーになる。

単純作業なので、動画から一発で音声を分離してくれるスクリプトでも組もうかと思ったのだが、実際は意外と面倒そう。
ffmpegは数々のフォーマットに対応しており、まずフォーマットと拡張子の対応テーブルを作らなければならないし、デコード出来てもエンコード出来ない、またはその逆のフォーマットもある。更には、出力にoutput.wmaと指定したら、WindowsMediaAudioの筈なのに動画ストリーム付きでファイルを書き出してくれたり。。。

正直、手作業の方が気楽。

<追記>
WMAとWMVってコンテナの形式は同じで、オーディオだけならWMA、動画ならWMVって違いなだけじゃね?と気付いた。
ちゃんとストリーム指定して出力すりゃいいのかな?