DEV Community

The duration your video API accepts is not the duration it renders

A sequence I cut to a music bed was three frames out at the first transition, nine at the second, and by the sixth segment nothing lined up with anything. I had asked every generation for ten seconds. Every generation had returned a file that was not ten seconds. Nothing in the API said so. The request took duration: 10 , returned 200 , and produced an MP4 whose container duration was 8.708 . No warning field, no note in the response body, and - the part that actually cost me the afternoon - no mention of it on the docs page I had read three times. This is a general property of latent video models rather than a bug in one provider, and once you know the shape of it you can handle it in about twenty lines. Here is the shape. Seconds are the wrong unit A video diffusion model does not work on frames. It works on a compressed latent tensor, and the compression is temporal as well as spatial: a causal 3D autoencoder folds a run of input frames into a single latent frame. Because the encoder is causal, the first frame is kept whole and everything after it is compressed in groups. With a temporal stride of s , a clip of F frames becomes latent_frames = (F - 1) / s + 1 which only divides evenly when F โ‰ก 1 (mod s) . Frame counts that miss that condition get padded or truncated, so implementations pick the nearest legal count and render that instead. Stack a second constraint on top - many of these models generate in fixed blocks of latent frames rather than one at a time - and the set of renderable lengths collapses into a short arithmetic progression: F = head + block ยท n n โˆˆ โ„• Every legal duration is one of those F values divided by the frame rate. Nothing between them is reachable. duration: 10 is not a request. It is a hint that gets snapped to a grid you were never shown. What the snapping does to users Three separate problems, and only the first is obvious. The output is not the length you promised. Your UI said 10s, the file is 8.708s, so your UI lied. Not by much, and not in a way anyone notices on one clip. The error compounds. Nobody makes one clip. They make six and cut them together. Six segments each 1.3 seconds short is eight seconds of drift, which is the difference between "cuts on the beat" and "re-render the sequence." Your cost estimate is wrong in the direction that generates tickets. These APIs bill per second of output. Estimate from the requested duration, let the model render a longer legal block, and you have quoted one number and charged another. Users find that one on their own. Quantise on the way in, not on the way out The fix is to stop treating duration as a free variable at the edge of your system. Resolve it to a legal value before anything is displayed, priced or persisted. type Grid = { fps: number; block: number; head: number; min: number; max: number }; /** Legal frame counts are head + blockยทn, clamped to the provider's range. */ export function legalFrames(g: Grid): number[] { const out: number[] = []; for (let n = 0; ; n++) { const f = g.head + g.block * n; if (f > g.max) break; if (f >= g.min) out.push(f); } return out; } export function quantise(seconds: number, g: Grid) { const target = Math.round(seconds * g.fps); const frames = legalFrames(g).reduce((best, f) => Math.abs(f - target) { for (const requested of [4, 5, 6, 7, 8, 9, 10]) { const job = await client.create({ prompt: "a still grey card", duration: requested }); const meta = await ffprobe(await client.download(job.id)); expect(meta.duration).toBeCloseTo(job.resolvedDuration, 2); } }); Note what it asserts. Not that the file matches the request - that is a test you cannot pass and should not want to - but that it matches what the API told you it resolved to. That is a contract you can hold a provider to, and if the provider returns no resolved value at all, the absence is itself the finding. Running the loop is also the cheapest way to discover the grid empirically. Seven requests, ffprobe on each, and you have the progression whether or not anyone documented it. Get the grid before you design around a number For anyone about to build on one of these APIs: - Find the frame rate and the block size. They are often in the model card even when they are missing from the API reference. - Enumerate the legal durations once, at build time, and ship the list. - Check whether any legal duration is a whole number of seconds. There may be exactly one. If your product ever cuts to music, that value is the only sensible default you have. That last point is not hypothetical. On the model I work with daily, the block arithmetic is tight enough that across its entire published 4-to-15 second range precisely one setting comes out even - and it is not a number anyone would think to type. Someone worked the progression out frame by frame and showed why only one lands on a whole second, which is ten minutes well spent if you are about to pick a default your users will inherit. The general lesson is smaller than the arithmetic: when a generative API accepts a continuous parameter the model can only satisfy discretely, the rounding is part of the contract. Ask where the grid is before you let a number reach your UI. I help run minimax-h3ai.video, an independent third-party interface for MiniMax H3. Not affiliated with MiniMax. Everything above is checked against the published docs and linked where it isn't. Top comments (0)

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.