* perf(aws-lambda): small-chunk cloud renders stitch their output in parallel
When every chunk's payload is under S3's 5 MiB part minimum (typical for short 4K chunks), the assemble step could not copy any part server-side and pulled the whole output through one Lambda's network link. Those range-only parts now go to parallel invocations of the warm function that just rendered the chunks, about 32 MiB each and never more at once than the stack's reserved concurrency allows. The output bytes are unchanged.
* fix(aws-lambda): a throttled stitch helper builds its parts in the assemble instead
Another render can hold the stack's reserved concurrency while this one assembles, so a helper invocation may be refused. The assemble now builds that batch itself, logs it, and counts it in assemble_stages; the helper's error type is kept as the error name. Comments trimmed back to each file's share at the base.
* chore(aws-lambda): raise the package size budget to the measured size plus headroom
The parallel stitch helpers add about 1.7 KB packed and 9.9 KB unpacked; the new budget is the measured size times the checker's own 1.05 headroom.
* perf(aws-lambda): each chunk logs where its seconds went
The chunk_capture event now carries the handler's setup time and the renderer's own stage timings (plan check, browser boot, capture, encode, total), which the renderer already measured but the logs dropped. Lets a fleet run be split into start-up, capture and tail per chunk.
* perf(aws-lambda): chunk logs also time the Chrome path resolution
On the container image the compressed Chrome may be unpacked on every cold chunk; chromeMs separates that from the plan download in setupMs.
* fix(producer): chunk capture round-robins only for callers that bill wall time
* fix(engine): worker, extraction and encoder sizing read the CPU quota, not the host core count
* Revert "fix(engine): worker, extraction and encoder sizing read the CPU quota, not the host core count"
This reverts commit cdd4d858c8.
* chore(producer): drop the doc comments the comment ratchet rejects
* perf(engine): a remote source's tail decodes run many at a time, not one per half-core
* perf(producer): the plan logs where its extract stage spends its time
* perf(engine): a window that ends inside its source takes its frame count without a decode
* test(engine): pin the tail-decode slot test to the windows that decode
* style(engine): format the tail-probe window check
* test(engine): deferred counts match extraction on a B-frame NTSC source; VFR is not deferred
* style(engine): format the tail-probe tests
* perf(aws-lambda): a site stores each video's ffprobe JSON, and the plan reads it instead of probing
* test(aws-lambda): a site stores and hands out each video's probe
* style(aws-lambda): format the probe-facts upload and tests
* perf(engine): a site's stored probe also holds the final-frame timestamp the plan would probe for
* test(engine): drop an unused import
* test(aws-lambda): the stored probe holds the ffprobe JSON and the final frame
* test(aws-lambda): the stored probe holds the ffprobe JSON and the final frame
* perf(producer): a distributed chunk encodes while it captures
* perf(producer): the audio task logs where its mix spends its time
One line per audio stage: prepare, groups, mix, master, encode and the peak check, in ms.
* perf(engine): locked warmup ticks stop sleeping once the page is ready
* perf(engine): a window reaching a stored video's end takes its frame count from stored facts
* fix(producer): the cloud audio task logs its phases
* perf(engine): stored facts keep the last packet length so late-start windows skip the tail decode
* fix(engine): a probe spawns without waiting when no stored facts are declared
* chore(aws-lambda): shorten comments over the line limit
* fix(engine): warmup keeps pacing after a failed tick; stored final frames check ffprobe major
* test(engine): the recorded ffprobe major matches the build's own banner
* test(engine): the version-banner probes go through a named binary like the other spawns
* perf(aws-lambda): a render reads a video's audio from a small file stored beside it
A video's audio is interleaved with its pictures, so mixing it from the cloud read the whole video (2.5 GB for a 20-minute 4K film) at the Lambda's network speed. lambda sites create now stores each large video's audio packets as a separate object keyed by the video's hash; the audio task reads that when it exists.
* chore(producer): shorten the audio sidecar header comment
* fix(aws-lambda): no audio sidecar for a video whose audio starts late or that has several tracks
* test(producer): format the audio sidecar tests and end their ffprobe options with --
* perf(aws-lambda): warm the audio function while the plan runs
The audio task runs on the x86 render function, which is cold when the plan ran on the arm64 video function. A fire-and-forget invoke at the start of a render now wakes it, so the audio no longer outlasts the chunks.
* perf(aws-lambda): warm the render function only on a stack with a video function
An x86-only stack has no cold function to warm, so it takes the old path with no extra step. The CDK stack has no video function and is unchanged.
* perf(aws-lambda): wrap a template comment
* style(aws-lambda): format the warm-up template test
* fix(aws-lambda): keep the arm64 image staging script out of the published package
stage-arm64-image.ts is run from a checkout by the image workflow and the publish job, and nothing in dist or the CLI imports it. With the release's prebuilt/ files added, aws-lambda measured 73484 bytes packed and 267013 unpacked in the publish job, over its budgets of 73369 and 266805, so v0.8.187 did not publish.
* ci: the packed-manifest check pins stand-in Lambda assets, like the publish job
The publish job adds prebuilt/handler-zip.json and prebuilt/video-image.json before verify:packed-manifests; the PR check did not, so a package could pass on the PR and fail at publish. The verifier now runs the same pin script on sparse stand-ins of release size when no pins exist, and removes them afterwards. It lives in the verifier, not a Build step, so the edit-accuracy input closure stays unchanged.
* feat(aws-lambda): price arm64 chunk invocations at the arm64 rate
getRenderProgress takes chunkArchitecture, the architecture of the function
that renders chunks. RenderChunk invocations are priced at its per GB-second
rate, both inline and from a Distributed Map's output; plan, audio and
assemble stay at the x86 rate.
* feat(cli): render chunks on an arm64 function with lambda deploy --video-arch
--video-arch arm64 pushes the arm64 video function image to the stack's own
ECR repository and deploys the template with VideoImageUri set to its digest.
Outside a checkout the image is the one the installed adapter pins; inside a
checkout it must come from HYPERFRAMES_LAMBDA_VIDEO_IMAGE, built from that
checkout. The default stays x86_64, which always sends an empty VideoImageUri
so a redeploy drops a video function rather than keeping it.
destroy deletes the repository, render and progress price chunks at the arm64
rate, and policies lists the ECR actions.
* fix(cli): push the video image only to an ECR repository this stack tagged
* fix(cli): price the plan at the video function's rate now that it runs there
* feat(cli): lambda deploy renders on arm64 by default; --video-arch x86_64 keeps x86
* refactor(cli): drop two doc comments the names already carry
* feat(aws-lambda): push a prebuilt image to ECR without Docker
Adds pushImageToEcr and ensureEcrRepository to the SDK subpath. The push
reads a single-platform OCI image layout, verifies every blob against its
descriptor before any ECR call, uploads only the blobs ECR lacks through
the layer-upload API, and puts the manifest under a tag, returning the
digest-pinned image URI.
* fix(aws-lambda): keep the ECR client out of the SDK bundle
Like the other AWS SDK clients, @aws-sdk/client-ecr is a dependency the consumer
resolves. Bundled into the ESM SDK it hits a dynamic require of node:https and
the packed package fails to import.
* feat(aws-lambda): build the ECR client from a region like the other SDK calls
* build(aws-lambda): add only the ECR client to the lockfile
* fix(aws-lambda): adopt an existing ECR repository only when it carries the caller's tags
* chore(aws-lambda): raise the size budget for pushing an image to ECR
pushImageToEcr ships in dist (unminified chunk plus its .d.ts); no source maps, tests or release-only files are involved. Built dist, npm pack --dry-run, main versus main plus this branch: +2486 B packed, +8846 B unpacked. The merge-group run measured 73369 packed and 266805 unpacked, which are the new budgets (were 70874 and 258166).
* feat(aws-lambda): attach the arm64 video image to releases and pin it
The release builds the arm64 video function image from the handler ZIP it
attaches, attaches it as an OCI layout tarball with its sha256, and pins it
in prebuilt/video-image.json, read through prebuiltVideoImage(). A retried
release reuses the image already attached.
* fix(aws-lambda): keep the release pin script out of the published package
pin-handler-zip.ts is run from a checkout by publish.yml; nothing in dist or the CLI imports it. Leaving it out keeps aws-lambda under its size budget with the video image reader added.
* feat(aws-lambda): arm64 video function image from the handler ZIP
The image is the release handler ZIP with its x86 ffmpeg and Chrome swapped for
the native ffmpeg tree and the sha256-pinned Sparticuz arm64 Chrome pack, so the
x86 and arm64 functions run the same handler code. The image stage only copies,
so any host builds it. CI builds it and runs ffmpeg, a real Chrome render and the
Lambda runtime on an ARM runner.
* fix(aws-lambda): launch Chrome in the image smoke the way Lambda does
@sparticuz/chromium inflates its AL2023 libraries only when AWS_EXECUTION_ENV
names a Node runtime; Lambda sets it, a bare docker run does not. The workflow
also runs on @sparticuz/chromium bumps and handler ZIP layout changes, which
re-stage the image.
* fix(aws-lambda): smoke the arm64 image through its own Lambda runtime
The smoke is now a handler invoked through the image's entrypoint, whose
bootstrap sets AWS_EXECUTION_ENV the way Lambda does, so @sparticuz/chromium
takes its Lambda branch with nothing set by hand. It returns the value it saw.
* fix(aws-lambda): agree the move of the ffmpeg build workflow into the image workflow
* fix(aws-lambda): build the Chrome pack URL from the pin and hand the checked pack to tar in memory
* fix(aws-lambda): make webm and mov audio in the separate audio task like mp4
A WebM or MOV plan mixed and encoded its own audio, so on a stack whose plan runs on an arm64 function the audio came out of a different ffmpeg build than an x86 render's. The plan now leaves that audio to the separate audio task, as mp4 already does, and the assembler joins the chunks with that file. Only mp4 chunks stitch; cfr and png-sequence keep their audio in the plan.
* test(aws-lambda): join mov chunks with the audio task's track too
* refactor(producer): keep the separate-audio gate's one-line doc comment
* feat(aws-lambda): run chunks on an optional arm64 image function
A new VideoImageUri parameter adds an arm64 container-image function that only
renders chunks. Plan, audio and assemble stay on the x86 ZIP function, so audio
encodes are unchanged. Empty (the default) deploys exactly as before. The
reserved concurrency moves to whichever function renders the chunks.
* docs(aws-lambda): the template tags both render functions
* fix(aws-lambda): nest the arm64 video function so sam deploy needs no image repository
sam deploy refuses a template with any top-level image function unless it is given an image
repository, even when that function's Condition is false, so every deploy of the template failed.
The video function now lives in video-function.yaml, a nested stack created only when
VideoImageUri is set. The release ships it beside template.yaml and the CLI stages both.
A new workflow runs the CLI's sam deploy argv against the template with fake credentials and
requires it to reach the first upload.
* fix(aws-lambda): run the plan on the arm64 video function with the chunks it plans
* refactor(cli): name the staged template directory instead of commenting it
* docs(aws-lambda): say the arm64 image runs the plan as well as the chunks
* build(aws-lambda): pinned native ffmpeg build for an arm64 Lambda image
One recipe, run per architecture on the Lambda Node 22 base it ships on. Codec
libraries link statically; glibc and GnuTLS stay dynamic, with the two libraries
the bare base lacks bundled beside the binaries. A check stage on the bare base
proves every encoder, decoder, filter and protocol the engine calls, and the
artifact cannot be produced without it. CI builds it on two ARM runners and
requires byte-identical tarballs.
* fix(aws-lambda): link GnuTLS dynamically in the arm64 ffmpeg build
With --static pkg-config flags, gnutls.pc names a libgmp.a from its own RPM build
tree and configure rejected GnuTLS. The codec libraries exist only as static
archives, so they still link statically. The recipe moves to image/, which will
also hold the video function image stage.
* fix(aws-lambda): build x265 for arm64 with the base image compiler
x265 builds its SVE2 kernels with -march=armv9-a, which gcc 11 rejects. Turn
them off, and stop a failed x265 build from reaching ffmpeg configure as a
misleading "x265 not found".
* fix(aws-lambda): pin x264 to the revision the x86 ffmpeg uses
The x86 ffmpeg-static 7.0.2 encodes with x264 r3191 (4613ac3); match it.
* fix(aws-lambda): 10-bit x265 in the arm64 ffmpeg, checked by output format
x265 now builds the 10-bit library and links it into the 8-bit one, so HDR
renders get yuv420p10le instead of a silent 8-bit fallback. The check asserts
the encoded pixel format rather than a successful command, and its ffprobe calls
terminate options with -- like every other caller.
* chore: release v0.8.182
* chore: list #5953 in the v0.8.182 notes
* chore: add #5843, #5932 and #5945 to the v0.8.182 notes
* chore: add #5923, #5943, #5956, #5960, #5961, #5963, #5964 and #5966 to the v0.8.182 notes
* chore: add #5972, #5977, #5981 and #5982 to the v0.8.182 notes
* chore: add the 13 PRs merged since the last refresh to the v0.8.182 notes
* chore: add #6003, #6005 and #5579 to the v0.8.182 notes
* chore: add #6006 to the v0.8.182 notes
* feat(aws-lambda): a cloud render mixes its audio beside its chunks
* fix(aws-lambda): the audio task keeps a worker free and runs only when the plan left audio out
* test(aws-lambda): the audio task's free worker holds for a reserved pool of 1 and 2
* refactor(aws-lambda): one helper points every handler at the bundled Chrome