Last Updates


環境設定は終わったので、ここからは実際に動かしてみようのコーナー
NKT・・・


Part 1
最初のコードは誤字があると思う
X charg *argv[]
O char *argv[]

誤字だよね・・・?

とりあえずtutorial01.cを実行してみたよ
このページではいくつか修正点が挙げられてるけど、自分がダウンロードしたときはこれらの点は修正されていたよ
ただ、コード中のPIX_FMT_RGB24という定数名をAV_PIX_FMT_RGB24に修正する必要がありました

結果、ppmという拡張子のファイルが5枚生成されました
ナニコレ?

Kritaで開いたら読めた
というか、他にないの?Windowsにもとから入ってるソフトで、.ppmファイルを表示できるソフト

いいんじゃないかあ!?

tutorial01.cを読もう
2023/4/4 ごめん、今日はここまで
最近1つのことに没頭しがちだけど、それあんまりいい効果もたらさないからね?

読むぞー!!

saveFrame関数
saveFrame.c
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
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->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}

これファイル名生成するところとか関数切り出せそうだけどね
肝心なのは
Write header
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の処理が終わったときにループを打ち切るようにしてみた
Stirlingでppmファイルを読む
データ部分は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]の構造をそのまま書き込めば使える形式になっている
ppmファイルの構造が大事

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回写経してこのページは終わりにしよう



ドキュメント(正しい情報はここを読んで)

以下メモ
example.c
/*
pFrame: pointer of AVFrame
*/
void displayFrame(AVFrame* pFrame, int height, int width) {
uint8_t* data;
data = pFrame->data;
}

AVFrameのプロパティ
data(uint8_t *)
イメージとしては uint8_t の配列として考えていい
1バイトの整数といえば?
1つのチャンネルにおける1つのピクセルの色ですよね



These can be useful for instance if you don't have access to a command line to run ffmpeg as an executable, or if you want to use just a small "part" of FFmpeg inside your own program, or if you want access to raw video frames in your program, etc. Also note that if you just need access to raw video frames, you could also write an audio or video filter and compile it along with FFmpeg and distribute that.

今の目的には合致してるんじゃないかな
まあ、ffmpegを使うこと、ないかもしれないけど・・・


いや、そんな内容なかったw
ffmpeg tutorial を読む 続きはコチラで