Repository navigation
feat(video,audio,publish)!: matching capture Control handles, with a keyframe request #4184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b8269cb
5d49dfe
431fdd6
b9ef931
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,6 +147,9 @@ export class Encoder { | |
| // doesn't re-probe the hardware. | ||
| #codecFilter: Computed<string>; | ||
|
|
||
| // A keyframe asked for by {@link cut} and not yet encoded. | ||
| #cut = false; | ||
|
|
||
| #signals = new Effect(); | ||
| #stalled = new Catalog.Stalled.Detector(); | ||
| #firstCaptured?: Time.Micro; | ||
|
|
@@ -326,11 +329,17 @@ export class Encoder { | |
|
|
||
| const interval = config?.keyframeInterval ?? Time.Milli.fromSecond(2 as Time.Second); | ||
|
|
||
| // Force a keyframe if this is the first frame (no group yet), or GOP elapsed. | ||
| // Force a keyframe if this is the first frame (no group yet), the GOP elapsed, or | ||
| // the caller asked for one and the last is old enough. | ||
| const since = lastKeyframe === undefined ? undefined : frame.timestamp - lastKeyframe; | ||
| const keyFrame = | ||
| !lastKeyframe || lastKeyframe + Time.Micro.fromMilli(interval) <= frame.timestamp; | ||
| since === undefined || | ||
| since >= Time.Micro.fromMilli(interval) || | ||
| (this.#cut && since >= Time.Micro.fromMilli(MIN_CUT_INTERVAL)); | ||
| if (keyFrame) { | ||
| lastKeyframe = frame.timestamp as Time.Micro; | ||
| // Any keyframe serves an outstanding request. | ||
| this.#cut = false; | ||
| } | ||
|
|
||
| encoder.encode(frame, { keyFrame }); | ||
|
|
@@ -664,11 +673,27 @@ export class Encoder { | |
| throw new Error("no supported codec"); | ||
| } | ||
|
|
||
| /** | ||
| * Request a keyframe, opening a new group at a frame no earlier than this call. | ||
| * | ||
| * For a resume, a recording cut, or a known tune-in moment; {@link Config.keyframeInterval} is the | ||
| * cadence. Requests coalesce into the next keyframe, and forced keyframes land at least 500ms | ||
| * apart so a caller in a loop cannot pin the encoder at all-keyframe. A request while not encoding | ||
| * is served by the keyframe every encode starts with. | ||
| */ | ||
| cut(): void { | ||
| this.#cut = true; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When encoding falls behind and Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| close() { | ||
| this.#signals.close(); | ||
| } | ||
| } | ||
|
|
||
| // The closest two requested keyframes may land. A keyframe costs several times a predicted frame, | ||
| // and this stays well under the default two-second GOP so a request still beats the cadence. | ||
| const MIN_CUT_INTERVAL = 500 as Time.Milli; | ||
|
|
||
| // The source's nominal frame rate: what the capture device settled on, or what a frame stream | ||
| // declared. Undefined when nothing reports one. | ||
| function sourceFrameRate(source: Source): number | undefined { | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Serve a cut only with a frame captured after the request.
If a frame is enqueued before
cut()but read afterward,#cutcan select that older frame and then clear the request. The next frame will not receive the requested keyframe. This breaks thecut()contract that the new group opens at a frame no earlier than the call. Record the request time and retain the request until an eligible frame produces a keyframe.Also applies to: 342-342
🤖 Prompt for AI Agents