[讀書心得] ffmpeg tutorial (一) Making Screencaps
Media files 種類分成 music, image, video.
1.1 LOOK AT HEADER
每個 file 自身又稱為 container, 各自的格式如下:
-music container : AIFF(mac), WAV(windows), XMF(extensible music format), etc.
-image container : TIFF, FITS, etc.
-video container : 3GP, AVI, ASF, Matroska, Quick Time, MPEG, MP4, RM, etc.
container type 決定了 儲存於 file 的資訊(這不廢話...)
container 由 streams (audio/video) 所構成, 每個 stream又可以分成數個 frames.
其中 streams 被各種 codec 以 encode 方式儲存於 container.
codec 定義了 stream 如何被 enCOde 跟 DECode, 如 H.264, Xvid, MP3.
不過在傳送時, streams 是以數個 decoded 的 raw frames 為單位, 稱作 packet.
一個簡單的處理程序如下:
10 OPEN video_stream FROM video.avi 20 READ packet FROM video_stream INTO frame 30 IF frame NOT COMPLETE GOTO 20 40 DO SOMETHING WITH frame 50 GOTO 20
不過這裡有幾個疑問(Q1):
(1) packet 是固定大小的 frames?
(2) 一個 image 由固定大小的 frames 組成? audio 又是怎麼傳輸?
恩, 直覺上最簡單的做法是每次收 data frames 時先檢查大小.
25 READ list FOR X frames transfer
(先記著, 回頭再來check)
這裡的example 是"開一個 file, 將它的 stream 取出, 在 40 "DO SOMETHING" 中將 frame存到 PPM file 中"。
1. OPEN THE FILE
#include#include ... int main(int argc, char *argv[]){ av_register_all();
一開始 call av_register_all() 註冊所要用的 format 跟 codec.
ps. 也可以選擇性註冊, 若懂它們是什麼的話... (Q2)
1.1 LOOK AT HEADER
AVFormatContext *fmt_ctx; int ret, i; if ((ret = av_open_input_file(&fmt_ctx, filename, NULL, 0, NULL))) return ret;
av_open_input_file() 作用是將 file 的 header information 取出, 並依照 AVFormatContext structure 存入變數 fmt_ctx. 後面3個參數則是分別指定 file format, buffer size 跟 format options. 將其設為 NULL 因為稍後 libavformat 會自行找出.
1.2 CHECK OUT STREAM INFORMATIONif ((err = av_find_stream_info(fmt_ctx)) <0) return err;
dump_format(fmt_ctx, 0, filename, 0);
av_find_stream_info() 會將找到的 INFO 存於 fmt_ctx->streams 中.
fmt_ctx->streams 是 array of pointers, 可以可以透過 fmt_ctx->nb_streams 得知長度.
1.3 BIND A DECODER TO EACH INPUT STREAM
avcodec_open() 將該 codec 記錄下來.
1.4 CREATE STORING SPACE
因為目標是將開啟的 file 輸出成 PPM file, 所以這裡需要產生兩個 frame space 跟
一個 raw data space:
avpicture_get size(): buffer 則是 儲存 raw data.
另外 AVFrame 是 AVPicture 的 superset, 所以這裡用 casting.
NOTE: 這裡又定義一個 Picture 指 decoded 後的 frame, 即 raw data.
1.5 READING DATA
Read 主要分成3個步驟: Read, Convert , and Save it.
READ:
av_read_frame() 是自fmt_ctx依序讀一個packet, 寫到AVPacket struct (所以內部的memory allocation 也是透過它而不用我們自己初始化).(所以一次只讀一個Packet? 一個packet有幾個stream是否固定? )(Q4)
CONVERT:
avcodec_decode_video() 依據先前找到的 codec 將 packet 轉為 frame. 但要確保有足夠的
資訊正確 decode packet, 需要一直作 READ 直到 frameFinished 被 set.
img_convert() 將 native format (pCodecCtx->fmt) 轉為 RGB.
SAVE:
saveFrame() 是將 FrameRGB 寫到 DISK(存成.PPM). (但為什麼要寫五次?)(Q5)
fmt_ctx->streams 是 array of pointers, 可以可以透過 fmt_ctx->nb_streams 得知長度.
1.3 BIND A DECODER TO EACH INPUT STREAM
for (i=0 ; iavcode_find_decoder() 會找出 AVcodec 中第一個 match 的 codec.nb_streams ; i++) { AVStream *stream = fmt_ctx->streams[i]; AVCodec *codec; if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) { fprintf (stderr, "Unsupported codec (id=%d) for input stream %d\n", stream->codec->codec_id, stream->index); }else if (avcodec_open(stream->codec, codec) < 0) { fprintf (stderr, "Error while opening codec for input stream %d\n", stream->index); }
avcodec_open() 將該 codec 記錄下來.
1.4 CREATE STORING SPACE
因為目標是將開啟的 file 輸出成 PPM file, 所以這裡需要產生兩個 frame space 跟
一個 raw data space:
pFrame = avcodec_alloc_frame() pFrameRGB = avcodec_alloc_frame() nBytes = avpicture_get_size(PIX_FMT_RGB24, pCoecCtx->width, pCodecCtx->height) buffer = av_malloc(nBytes * sizeof(uint8_u))avcodec_alloc_frame(): pFrame 儲存自file 接收的 frame, pFrameRBG 則是儲存轉換後的 frame.
avpicture_get size(): buffer 則是 儲存 raw data.
avpicture_fill( (AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCoecCtx->width, pCodecCtx->height)avpicture_fill() 這裡是說將 buffer 與 pFrameRGB 作 association (不懂?!)(Q3);
另外 AVFrame 是 AVPicture 的 superset, 所以這裡用 casting.
NOTE: 這裡又定義一個 Picture 指 decoded 後的 frame, 即 raw data.
1.5 READING DATA
Read 主要分成3個步驟: Read, Convert , and Save it.
int frameFinished; AVPacket packet; i=0; while (av_read_frame(fmt_ctx, &packet)>0) { if (packet.stream_index==videoStream) { avcodec_decode_video (codec_ctx, pFrame, &frameFinished, packet.data, packet.size); if (frameFinished) { img_convert ((AVPicture*)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); if (++i<=5) saveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); } } av_free_packet(&packet); }這段程式碼中,
READ:
av_read_frame() 是自fmt_ctx依序讀一個packet, 寫到AVPacket struct (所以內部的memory allocation 也是透過它而不用我們自己初始化).(所以一次只讀一個Packet? 一個packet有幾個stream是否固定? )(Q4)
CONVERT:
avcodec_decode_video() 依據先前找到的 codec 將 packet 轉為 frame. 但要確保有足夠的
資訊正確 decode packet, 需要一直作 READ 直到 frameFinished 被 set.
img_convert() 將 native format (pCodecCtx->fmt) 轉為 RGB.
SAVE:
saveFrame() 是將 FrameRGB 寫到 DISK(存成.PPM). (但為什麼要寫五次?)(Q5)
(Q1)
Comments
Post a Comment