環境設定は終わったので、ここからは実際に動かしてみようのコーナー
とりあえずtutorial01.cを実行してみたよ
このページではいくつか修正点が挙げられてるけど、自分がダウンロードしたときはこれらの点は修正されていたよ
ただ、コード中のPIX_FMT_RGB24
という定数名をAV_PIX_FMT_RGB24
に修正する必要がありました
結果、ppmという拡張子のファイルが5枚生成されました というか、他にないの?Windowsにもとから入ってるソフトで、.ppmファイルを表示できるソフト
最近1つのことに没頭しがちだけど、それあんまりいい効果もたらさないからね?
saveFrame.c
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
fclose(pFile);
}
これファイル名生成するところとか関数切り出せそうだけどね
writeHeader.txt
P6
1280 720
255
あー、AVFrame.dataには二次元配列(二重ポインタ)が格納されてるのか
data[0]
で二次元配列の要素を取れる、実際にはdata[0][0]
が格納されたメモリへのポインタとして取得する
で、data[0]
配列の要素番号y * pFrame->linesize[0]
の要素を見に行っているのか
data[0][y * pFrame->linesize[0]]
というイメージだな
メモリ上の要素にアクセスしたあと、先頭から1バイト分取ってきてファイルに width * 3 回分書き込む
書き込まれる内容がイメージできなかったので、y = 0の処理が終わったときにループを打ち切るようにしてみた
データ部分は3840バイトあって、これを3で割ったら1280だった
つまり、一度の fwrite で1バイト * width * 3という大きさのバイト列が書き込まれたことになる
今回の sizeは1バイトのはずで、普通に考えたら同じ値の1バイトが width * 3 回書き込まれそうだけど・・・
1バイトでないデータを書き込む場合エンディアンの問題もあるから、データのサイズが分かってないといけない
2バイト以上になると書き込むバイトの順番も考慮してくれるのかな
で、fwriteは今メモリのどの位置を読んでるのかを覚えてるのかな
なので、width * 3 回分、1バイト読んでは次の位置に移動していく感じ
ここでは、data[0][y * pFrame->linesize[0]]
を最初の位置として、1バイトずつ読んで書き込むのをwidth * 3回分やっている
生成されたppmファイルの内容は、ほとんどuint8_t配列data[0]
の内容がそのまま書き込まれているといっていいかな
なぜdataもlinesizeも、添字は0なのか?
printf("%s\n", pFrame->data[1] == NULL ? "NULL" : "not NULL");
とやってみたらNULLだった
pFrame->linesize[0]
とwidth * 3
は同じ値でした
少なくともpRGBFrameにおいて、各行のサイズ(linesize)はRGB(=3)× widthに等しいということかな
(1行目1列目R)(1行目1列目G)(1行目1列目B)(1行目2列目R)...(1行目width列目B)(2行目1列目R)...(height行目width列目B)
行順、列順にRGBの順で1バイトずつ値が並んでいる一次元配列
ppmはちょうど都合の良い構造をしているなあ・・・
Wikipediaの例はテキスト形式で、バイナリ形式はちょうどdata[0]
の構造をそのまま書き込めば使える形式になっている
各関数の実装を読んでみる必要はありそうだけど、特に理解は難しくない
Note that we must not use the AVCodecContext from the video stream directly! So we have to use avcodec_copy_context() to copy the context to a new location (after allocating memory for it, of course).
(意訳)video streamのAVCodecContextを直接使うべきではない!avcodec_copy_context()
を使って、AVCodecContextを別の場所にコピーしましょう(もちろんその前にメモリを確保しておきます)。
2. コピーに使った関数のインターフェースはどうなっている?
Copy contextでもそうだけど、ffmpegの提供する構造体のほとんどは、allocateするための関数を自身の中に持っているね
対応する関数を呼び出してallocateし、取得用の関数の戻り値をallocateされたメモリに格納する、という流れを繰り返す。
avcodec_allocate_context3(AVCodec *codec)
3. AVCodecを用いてAVCodecContextを初期化
avcodec_open2(AVCodecContext *avctx, AVcodec *codec, AVDictionary **options)
ああ、ここで書き換える必要があるから、video streamに含まれるAVCodecContext*を使っちゃいけなかったんだね
Color conversion and scaling library.
ここコメントアウトしてsaveFileにpFrame直接渡してもいけたわ・・・
YUVとRGB、そんなに見た目変わらないってこと??
直接的には、av_read_frameが成功した回数が変わっているということになるが
使い方が決まってるものだから、それぞれのインターフェースのつながりを覚えるだけなんだよね
今日と明日、2回写経してこのページは終わりにしよう