ffmpeg tutorial を読む
環境設定は終わったので、ここからは実際に動かしてみようのコーナー
NKT・・・
Part 1
最初のコードは誤字があると思う
X charg *argv[]
O char *argv[]
誤字だよね・・・?
とりあえずtutorial01.cを実行してみたよ
このページではいくつか修正点が挙げられてるけど、自分がダウンロードしたときはこれらの点は修正されていたよ
ただ、コード中のPIX_FMT_RGB24という定数名をAV_PIX_FMT_RGB24に修正する必要がありました
結果、ppmという拡張子のファイルが5枚生成されました ナニコレ?
というか、他にないの?Windowsにもとから入ってるソフトで、.ppmファイルを表示できるソフト
https://scrapbox.io/files/642af600a76053001b784e45.png
いいんじゃないかあ!?
tutorial01.cを読もう
2023/4/4 ごめん、今日はここまで
最近1つのことに没頭しがちだけど、それあんまりいい効果もたらさないからね?
読むぞー!!
saveFrame関数
code: saveFrame.c
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data0+y*pFrame->linesize0, 1, width*3, pFile); // Close file
fclose(pFile);
}
これファイル名生成するところとか関数切り出せそうだけどね
肝心なのは
Write header
code: writeHeader.txt
P6
1280 720
255
例えばこういう感じになるね
Write pixel data
どういうこと・・・??
書き込まれるデータを見てみよう
あー、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という大きさのバイト列が書き込まれたことになる
わからないのは、fwriteの n 引数
今回の sizeは1バイトのはずで、普通に考えたら同じ値の1バイトが width * 3 回書き込まれそうだけど・・・
あー、なるほど
1バイトでないデータを書き込む場合エンディアンの問題もあるから、データのサイズが分かってないといけない
今回はuint8なので1バイトでいいんだけども
2バイト以上になると書き込むバイトの順番も考慮してくれるのかな
で、fwriteは今メモリのどの位置を読んでるのかを覚えてるのかな
なので、width * 3 回分、1バイト読んでは次の位置に移動していく感じ
ここでは、data[0][y * pFrame->linesize[0]]を最初の位置として、1バイトずつ読んで書き込むのをwidth * 3回分やっている
なので
生成されたppmファイルの内容は、ほとんどuint8_t配列data[0]の内容がそのまま書き込まれているといっていいかな
疑問
linesizeはなぜ配列なのか?
なぜdataもlinesizeも、添字は0なのか?
printf("%s\n", pFrame->data[1] == NULL ? "NULL" : "not NULL");とやってみたらNULLだった
つまり0から先は存在しない
基本は0でいいと思う
メモ
pFrame->linesize[0]とwidth * 3は同じ値でした
少なくともpRGBFrameにおいて、各行のサイズ(linesize)はRGB(=3)× widthに等しいということかな
配列data[0]は、次のような配列なのだろう
(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]の構造をそのまま書き込めば使える形式になっている
main関数
各関数の実装を読んでみる必要はありそうだけど、特に理解は難しくない
引っかかった部分
Copy context
1. なぜコピーする?
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. コピーに使った関数のインターフェースはどうなっている?
3. コピーはどのような処理になっている?
Allocate video frame
Copy contextでもそうだけど、ffmpegの提供する構造体のほとんどは、allocateするための関数を自身の中に持っているね
対応する関数を呼び出してallocateし、取得用の関数の戻り値をallocateされたメモリに格納する、という流れを繰り返す。
例
AVCodecContext
手順
1. Allocate
avcodec_allocate_context3(AVCodec *codec)
2. (コピー)
avcodec_copy_context()
3. AVCodecを用いてAVCodecContextを初期化
avcodec_open2(AVCodecContext *avctx, AVcodec *codec, AVDictionary **options)
ああ、ここで書き換える必要があるから、video streamに含まれるAVCodecContext*を使っちゃいけなかったんだね
AVFrame
手順
1. Allocate
av_frame_alloc()
2. 初期化
av_image_fill_array()
Deprecated
avpicture_fill()
Initialize SWS
SWSとはなにか?
Color conversion and scaling library.
ほーん、で?
Sws scale
ここコメントアウトしてsaveFileにpFrame直接渡してもいけたわ・・・
何?
何だと?
pFrameはYUV画像ですね
YUVとRGB、そんなに見た目変わらないってこと??
問題
iの最後の値が毎回変わる・・・・
直接的には、av_read_frameが成功した回数が変わっているということになるが
そんなことはなかった
これ以上読んでも仕方なさそう
使い方が決まってるものだから、それぞれのインターフェースのつながりを覚えるだけなんだよね
今日と明日、2回写経してこのページは終わりにしよう