Decoded picture types are forced onto the encoder, so an image sequence encodes as all keyframes
The engine passes the decoder's pict_type straight through to the encoder, and
libx264 honours it: a frame arriving as AV_PICTURE_TYPE_I is forced to an IDR.
Nothing in src/ ever resets it.
For an image-sequence input every decoded frame is an I-picture by construction, so every output frame becomes a keyframe and the encoder never gets to choose a P or a B.
Measured
100 frames, 128×96, a white block sliding one pixel per frame across a static
gradient — about as compressible as video gets. Same encoder, same options
(bf=3, g=25):
bytes picture types
engine 34727 99 I, 1 I
ffmpeg CLI 7287 57 B, 39 P, 3 I, 1 I4.8× the size, from identical input frames.
It is invisible on a normal video input because the source's own types are reproduced: transcoding a file with 65 B / 31 P / 3 I through the engine yields exactly 65 B / 31 P / 3 I, while the CLI re-decides and yields 64 B / 32 P. The output looks plausible, so nothing has ever flagged it.
Who this bites
keryx renders reels from PNG stills, which is precisely the image-sequence case. Every reel it has produced is roughly five times larger than it needs to be, and carries no B-frames at all.
The fix
fftools/ffmpeg_enc.c clears it before encoding, and the engine should do the
same: set pict_type to AV_PICTURE_TYPE_NONE on the frame handed to
avcodec_send_frame. The decoder's picture type describes how the INPUT was
coded and says nothing about how the output should be.
Whether a caller should be able to force keyframes deliberately is a separate question — today they cannot ask for it and get it anyway.
Why this is filed rather than fixed inside #55 (closed)
It blocks #55 (closed)'s regression test. That test needs a fixture with real B-frames,
and the engine cannot currently produce one from any fixture this suite can
build: internal/fixture writes PNGs, PNGs decode as all-I, and the forced types
mean the encoder emits all-I no matter what x264 options are set. Every attempt
reports frame I:100.
So #55 (closed) cannot be tested red until this is fixed, and the two want separate commits: this one is a quality defect with its own measurement, and #55 (closed) is a correctness defect about which axis a cutoff uses.