mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
* feat(engine): encoders cap peak bitrate and place keyframes on a set interval * style(engine): shorten the rate-cap comment * style(engine): drop the doc lines the comment ratchet flagged * feat(producer): mp4 renders honour a keyframe interval and a peak bitrate cap on every encoder path * style(producer): shorten the delivery encoder file headers * style(producer): drop a comment line from the segment manifest * style(producer): shorten the integration test header * test: delivery encoder tests follow the ffprobe argv contract and run in the integration lane * feat: the render request, server and Lambda plan carry the keyframe interval and peak bitrate cap * fix(studio): the Studio render route passes the keyframe interval and bitrate cap to the encoder * chore(studio-server): drop a comment the helper's name already says * feat(cli): render and lambda render take --keyframe-interval, --max-bitrate and --buffer-size
89 lines
3.4 KiB
TypeScript
89 lines
3.4 KiB
TypeScript
/**
|
||
* Boundary tests for the wire config emitted by `hyperframes lambda render`.
|
||
* Pins that the aspect-agnostic resolution flag survives all the way into
|
||
* `SerializableDistributedRenderConfig`, which is what the Lambda worker's
|
||
* compile stage reads before remapping `landscape` → `portrait` for an
|
||
* aspect-agnostic alias like `--output-resolution 1080p`.
|
||
*
|
||
* See `../cloudrun.test.ts` for the parseOutputResolution counterpart and
|
||
* `../render.test.ts` for the local-CLI preflight guardrails.
|
||
*/
|
||
|
||
import { describe, expect, it } from "vitest";
|
||
import lambdaCommand from "../lambda.js";
|
||
import { buildLambdaRenderConfig, type RenderArgs } from "./render.js";
|
||
|
||
const BASE_ARGS: RenderArgs = {
|
||
projectDir: "/tmp/hf-project",
|
||
stackName: "hf-test",
|
||
fps: 30,
|
||
width: 1080,
|
||
height: 1920,
|
||
format: "mp4",
|
||
json: false,
|
||
wait: false,
|
||
waitIntervalMs: 5000,
|
||
};
|
||
|
||
describe("buildLambdaRenderConfig — aspect-agnostic wire threading", () => {
|
||
it("threads outputResolutionAspectAgnostic=true through for portrait 1080p", () => {
|
||
// The exact bug shape: portrait 1080×1920 comp + `--output-resolution 1080p`.
|
||
// Before the fix, the aspect-agnostic flag never reached the wire, so
|
||
// the Lambda worker saw the explicit `landscape` preset and rejected
|
||
// the portrait comp — reproducing the local-CLI regression on the
|
||
// distributed path.
|
||
const config = buildLambdaRenderConfig(
|
||
{ ...BASE_ARGS, outputResolution: "landscape", outputResolutionAspectAgnostic: true },
|
||
undefined,
|
||
);
|
||
expect(config.outputResolution).toBe("landscape");
|
||
expect(config.outputResolutionAspectAgnostic).toBe(true);
|
||
});
|
||
|
||
it("keeps the aspect-agnostic key absent when the flag is a canonical preset", () => {
|
||
// Sparse-wire invariant: only forward `true`; canonical presets stay
|
||
// strict and don't need the compile-stage remap.
|
||
const config = buildLambdaRenderConfig(
|
||
{ ...BASE_ARGS, outputResolution: "portrait-4k" },
|
||
undefined,
|
||
);
|
||
expect(config.outputResolution).toBe("portrait-4k");
|
||
expect(config.outputResolutionAspectAgnostic).toBeUndefined();
|
||
});
|
||
|
||
it("sends the delivery options on the wire and nothing when they are unset", () => {
|
||
const config = buildLambdaRenderConfig(
|
||
{ ...BASE_ARGS, keyframeIntervalSeconds: 2, maxBitrate: "8M", bufferSize: "12M" },
|
||
undefined,
|
||
);
|
||
expect(config).toMatchObject({
|
||
keyframeIntervalSeconds: 2,
|
||
maxBitrate: "8M",
|
||
bufferSize: "12M",
|
||
});
|
||
expect(
|
||
JSON.parse(JSON.stringify(buildLambdaRenderConfig(BASE_ARGS, undefined))),
|
||
).not.toHaveProperty("maxBitrate");
|
||
});
|
||
|
||
it("carries variables verbatim through the wire config", () => {
|
||
const config = buildLambdaRenderConfig(BASE_ARGS, { alice: "hello" });
|
||
expect(config.variables).toEqual({ alice: "hello" });
|
||
});
|
||
});
|
||
|
||
describe("lambda render chunk sizing", () => {
|
||
it("hands the deploy's reserved concurrency to the planner", () => {
|
||
expect(buildLambdaRenderConfig(BASE_ARGS, undefined, 64).workerConcurrency).toBe(64);
|
||
expect(buildLambdaRenderConfig(BASE_ARGS, undefined).workerConcurrency).toBeUndefined();
|
||
});
|
||
|
||
it("help no longer claims --chunk-size defaults to 240", () => {
|
||
const chunkSizeArg = lambdaCommand.args
|
||
? Reflect.get(lambdaCommand.args, "chunk-size")
|
||
: undefined;
|
||
expect(chunkSizeArg?.description).not.toContain("240");
|
||
expect(chunkSizeArg?.description).toContain("timeout");
|
||
});
|
||
});
|