Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions docs/architecture/playback-protocol-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -994,11 +994,15 @@ fallback to another media version must remap the selected subtitle. It
prefers a version with an equivalent track; when none has one, playback
continues with subtitles off and the plan carries `subtitle_track_unavailable`
rather than ending in a terminal. A selection the effective file cannot honor
on a direct start degrades the same way. A replan that would have to drop the
on a direct start degrades the same way. When the file being planned was
refused with `subtitle_conversion_unsupported` and the only other versions that
play drop the subtitle, the server plays that file with subtitles off instead
of moving to one of them, provided that plan can run; when no other version
plays, the terminal stands. A replan that would have to drop the
subtitle to return to the requested version stays on the version already
playing, unless quality `original` pins the requested version; the requested
version remains a fallback without the subtitle if no version that keeps it can
play. Malformed
version remains a fallback without the subtitle, ahead of any other version
without it, if no version that keeps it can play. Malformed
selections (a track identity that does not parse or names another file, or a
negative index) are still rejected.

Expand Down Expand Up @@ -1240,7 +1244,7 @@ The plan will play, but something the user might notice was given up.
| `bandwidth_cap_applied` | `bandwidth_cap_kbps` limited the selection |
| `evidence_insufficient_for_direct` | Evidence tier blocked a direct route |
| `audio_track_unavailable` | The selected audio track is not on the effective file; its default track plays |
| `subtitle_track_unavailable` | The selected subtitle has no equivalent on the effective file; playback starts with subtitles off |
| `subtitle_track_unavailable` | The selected subtitle has no equivalent on the effective file, or is on it but cannot be shown there; playback continues with subtitles off. `message` says which |

### 7.3 Terminal reasons

Expand Down
69 changes: 65 additions & 4 deletions internal/api/handlers/playback_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -1807,13 +1807,28 @@ func (h *PlaybackHandler) startPlaybackApplicationV3(r *http.Request, body []byt
firstFailureAudioIndex = candidateAudioIndex
}
}
subtitleNotShown := false
if result.Terminal != nil && droppedSubtitle != nil && subtitleRefusalV3(result.Terminal) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// The planner pinned the requested file's refusal on the
// subtitle, and every sibling that plays drops the subtitle
// too. Playing the requested file without it keeps its
// picture, when that plan can run.
if candidate := h.planWithoutSubtitleV3(r.Context(), playback.PlannerInputV3{Request: baseReq, RequestedFile: requestedFile, EffectiveFile: effectiveFile, LowerVersion: lowerVersionForFileV3(lowerVersion, effectiveFile), ServerBitrateCapKbps: serverBitrateCapV3(r.Context()), AudioTrackIndex: baseAudioIndex, Settings: settings, Registry: h.transformationRegistryV3(r.Context()), DVRPUStrippable: h.lazyDVRPUStrippableV3(r.Context(), effectiveFile), Now: time.Now(), AdditionalSubtitles: h.downloadedSubtitleInventoryV3(r.Context(), effectiveFile)}); candidate != nil {
droppedSubtitle = candidate
subtitleNotShown = true
}
}
if result.Terminal != nil && droppedSubtitle != nil {
req = droppedSubtitle.request
effectiveFile = droppedSubtitle.file
audioIndex = droppedSubtitle.audioIndex
result = droppedSubtitle.result
toneMapCapabilityErr = droppedSubtitle.toneMapErr
warnings = append(warnings, playback.SubtitleTrackUnavailableWarningV3())
if subtitleNotShown {
warnings = append(warnings, playback.SubtitleNotShownWarningV3())
} else {
warnings = append(warnings, playback.SubtitleTrackUnavailableWarningV3())
}
} else if result.Terminal != nil && firstFailureFile != nil {
req = firstFailureReq
effectiveFile = firstFailureFile
Expand Down Expand Up @@ -4842,6 +4857,9 @@ func (h *PlaybackHandler) executeReplanV3(r *http.Request, record *playback.Atte
r = r.WithContext(withServerBitrateCapV3(r.Context(), record.ServerBitrateCapKbps))
audioDegraded := false
subtitleDegraded := false
// subtitleNotShown marks a subtitle the effective file has but cannot
// show, as opposed to one the file lacks.
subtitleNotShown := false
reservationHeld := false
reservationHandedOff := false
cancelReservation := func() {
Expand Down Expand Up @@ -5196,8 +5214,10 @@ func (h *PlaybackHandler) executeReplanV3(r *http.Request, record *playback.Atte
}
if candidateResult.Terminal == nil && subtitleDropped {
// Prefer a version that keeps the viewer's subtitle; hold
// this one back in case none does.
if droppedSubtitle == nil {
// this one back in case none does. The requested edition,
// listed last when it was passed over only for the
// subtitle, outranks any other version held back.
if droppedSubtitle == nil || candidateFile.ID == requestedFile.ID {
droppedSubtitle = &alternateCandidateV3{file: candidateFile, request: candidateStart, audioIndex: candidateAudioIndex, result: candidateResult, toneMapErr: candidateToneMapErr}
Comment on lines +5220 to 5221

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

Candidate overwrite replaces an executable sibling before verifying that the requested subtitle-dropped candidate's route is executable. When the requested edition plans as a progressive remux rejected by the header-authenticated worker-only policy, this causes the later droppedSubtitle.file.ID != requestedFile.ID guard to skip planWithoutSubtitleV3, returning local_transcode_disabled instead of the playable direct sibling; retain the executable sibling until the requested route passes the same progressive-remux escalation check, or defer the requested-edition preference until validation completes.

// Do not replace an executable held-back sibling with the requested edition until the requested route has been validated by the same escalation logic used before committing the plan.
if droppedSubtitle == nil {
	droppedSubtitle = &alternateCandidateV3{file: candidateFile, request: candidateStart, audioIndex: candidateAudioIndex, result: candidateResult, toneMapErr: candidateToneMapErr}
}
Prompt for LLM

File internal/api/handlers/playback_v3.go:

Line 5149 to 5150:

Candidate overwrite replaces an executable sibling before verifying that the requested subtitle-dropped candidate's route is executable. When the requested edition plans as a progressive remux rejected by the header-authenticated worker-only policy, this causes the later `droppedSubtitle.file.ID != requestedFile.ID` guard to skip `planWithoutSubtitleV3`, returning `local_transcode_disabled` instead of the playable direct sibling; retain the executable sibling until the requested route passes the same progressive-remux escalation check, or defer the requested-edition preference until validation completes.

Suggested Code:

// Do not replace an executable held-back sibling with the requested edition until the requested route has been validated by the same escalation logic used before committing the plan.
if droppedSubtitle == nil {
	droppedSubtitle = &alternateCandidateV3{file: candidateFile, request: candidateStart, audioIndex: candidateAudioIndex, result: candidateResult, toneMapErr: candidateToneMapErr}
}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

}
continue
Expand All @@ -5218,6 +5238,18 @@ func (h *PlaybackHandler) executeReplanV3(r *http.Request, record *playback.Atte
firstFailureAudioIndex = candidateAudioIndex
}
}
// A held-back requested edition is already the picture the
// viewer asked for, so it keeps its place.
if result.Terminal != nil && droppedSubtitle != nil && droppedSubtitle.file.ID != requestedFile.ID && subtitleRefusalV3(result.Terminal) {
Comment thread
Quick104 marked this conversation as resolved.
// The planner pinned the effective file's refusal on the
// subtitle, and every sibling that plays drops the subtitle
// too. Playing the effective file without it keeps its
// picture, when that plan can run.
if candidate := h.planWithoutSubtitleV3(r.Context(), playback.PlannerInputV3{Request: baseStart, RequestedFile: plannerRequestedFile, EffectiveFile: baseEffectiveFile, LowerVersion: lowerVersionForFileV3(lowerVersion, baseEffectiveFile), ServerBitrateCapKbps: serverBitrateCapV3(r.Context()), AudioTrackIndex: baseAudioIndex, Settings: plannerSettings, Registry: h.transformationRegistryV3(r.Context()), DVRPUStrippable: h.lazyDVRPUStrippableV3(r.Context(), baseEffectiveFile), Now: time.Now(), AttemptedKeys: attemptedKeys, AdditionalSubtitles: h.downloadedSubtitleInventoryV3(r.Context(), baseEffectiveFile)}); candidate != nil {
droppedSubtitle = candidate
subtitleNotShown = true
}
}
if result.Terminal != nil && droppedSubtitle != nil {
start = droppedSubtitle.request
effectiveFile = droppedSubtitle.file
Expand Down Expand Up @@ -5267,7 +5299,11 @@ func (h *PlaybackHandler) executeReplanV3(r *http.Request, record *playback.Atte
result.Plan.DegradationWarnings = append(result.Plan.DegradationWarnings, playback.AudioTrackUnavailableWarningV3())
}
if subtitleDegraded {
result.Plan.DegradationWarnings = appendWarningOnceV3(result.Plan.DegradationWarnings, playback.SubtitleTrackUnavailableWarningV3())
warning := playback.SubtitleTrackUnavailableWarningV3()
if subtitleNotShown {
warning = playback.SubtitleNotShownWarningV3()
}
result.Plan.DegradationWarnings = appendWarningOnceV3(result.Plan.DegradationWarnings, warning)
}
session, err := h.sessionMgr.GetSession(record.SessionID)
if err != nil {
Expand Down Expand Up @@ -6040,6 +6076,31 @@ func terminalAllowsAlternateFileV3(terminal *playback.TerminalV3) bool {
}
}

// subtitleRefusalV3 reports a refusal the planner pinned on the selected
// subtitle: a burn-in that was the only reason for an adaptation the file
// cannot take, or a subtitle the fidelity policy cannot serve. The file may
// then play with the subtitle cleared; callers re-plan it to find out.
func subtitleRefusalV3(terminal *playback.TerminalV3) bool {
return terminal != nil && terminal.Reason == terminalSubtitleConversionUnsupportedV3
}

// planWithoutSubtitleV3 plans input's file again with the selected subtitle
// cleared. It returns nil unless that plan can run: a progressive remux this
// attempt cannot reach must escalate here, because the caller drops a sibling
// that already plays in favor of the result.
func (h *PlaybackHandler) planWithoutSubtitleV3(ctx context.Context, input playback.PlannerInputV3) *alternateCandidateV3 {
input.Request.SubtitleTrackID, input.Request.SubtitleTrackIndex = "", nil
result, toneMapErr := h.planPlaybackWithCapabilitiesV3(ctx, input)
if result.Terminal != nil {
return nil
}
result, escalateErr := h.escalateRefusedProgressiveRemuxV3(ctx, headerAuthenticatedMediaV3(input.Request.ClientFeatures), func() playback.PlannerInputV3 { return input }, result)
if escalateErr != nil {
return nil
}
return &alternateCandidateV3{file: input.EffectiveFile, request: input.Request, audioIndex: input.AudioTrackIndex, result: result, toneMapErr: toneMapErr}
}

// alternateCandidateV3 is a planned alternate version held back while the
// fallback loop looks for a better one.
type alternateCandidateV3 struct {
Expand Down
Loading
Loading