Skip to content

DVR Chapters

DVR chapters turn one continuous recording into smaller playback windows. Each chapter is finalized as a canonical .mkv VOD artifact with a matching .dtsh seek index and fresh poster/sprite thumbnails for the chapter timeline. Chapters use the standard VOD playback path, and their processing pass uses the lifecycle-specific dvr_finalize process snapshot resolved by Commodore.

Historical chapter mode is a property of the source Stream, snapshotted onto each recording at StartDVR. Changes apply to the next recording, not in-flight ones.

NONE only disables historical chapter artifacts. It does not disable DVR recording. The active recording still has its rolling DVR playback surface (dvr+<dvr_internal_name>) for live time-shift within the configured DVR window. Without chapters, there is no finalized replay artifact after media rolls out of that window.

mutation EnableChapters {
updateStream(
id: "stream-id"
input: { record: true, dvrChapterMode: FIXED_INTERVAL, dvrChapterIntervalSeconds: 3600 }
) {
... on Stream {
id
dvrChapterMode
dvrChapterIntervalSeconds
}
}
}
ModeBest for
WINDOW_SIZEDDefault historical replay buckets sized to the stream’s DVR window.
FIXED_INTERVALRegular historical replay buckets such as hourly, 6-hour, or 24-hour chapters.
NONERolling DVR only: record and serve the live DVR window, but do not create chapter artifacts.

FIXED_INTERVAL requires dvrChapterIntervalSeconds ≥ 3600 (1 hour).

All chapter boundaries use UTC epoch milliseconds. Civil-time bucketing belongs in your app: convert “yesterday in Europe/Amsterdam” to a UTC range there, then read or play the chapter that covers it.

OPEN → CLOSED → FINALIZING → FINALIZED → FROZEN → RECLAIMED
└─→ FAILED_SOURCE_MISSING | FAILED_PERMANENT

A chapter becomes playable at FINALIZED (canonical .mkv produced on the recording origin). FROZEN means the artifact + .dtsh are durably on S3 and source TS segments are safe to delete. RECLAIMED is the steady state — the row remains as range metadata; the chapter artifact is the only thing serving playback.

playableNow is true when state ∈ {FINALIZED, FROZEN, RECLAIMED}.

query DVRChapter {
dvrChapter(dvrId: "dvr-hash", startMs: 1715126400000, endMs: 1715212800000) {
chapterId
state
playbackId
isCurrent
hasGaps
segmentCount
wallClockStartUnixMs
wallClockEndUnixMs
playableNow
lastFailureReason
}
}

playbackId is the Commodore-minted public playback key for the chapter’s VOD artifact. Resolve playback through resolveViewerEndpoint against that ID; the resulting endpoint is a standard VOD edge URL. There is no dvr+chapter_id token — chapter playback uses the same path as any other VOD.

wallClockStartUnixMs and wallClockEndUnixMs are absolute Unix epoch milliseconds. Use them to render the chapter’s calendar time and to map player timeline position (videoCurrentTime * 1000 + wallClockStartUnixMs) back to absolute time. Mist preserves DateUTC end-to-end through the remux, so the artifact itself also carries unixoffset natively — but the API metadata is the product contract.

query DVRChapters {
dvrChapters(dvrId: "dvr-hash") {
chapters {
chapterId
state
playbackId
startMs
endMs
segmentCount
hasGaps
lastFailureReason
}
nextPageToken
}
}

Paginate with pageSize (default 200, max 1000) + nextPageToken. Use this for chapter pickers, timeline markers, or quick links into a long archive.

If a segment was lost from both local disk AND the recovery-bridge S3 (severe storage pressure during recording), chapter finalization marks the chapter FAILED_SOURCE_MISSING. lastFailureReason carries the operator-facing explanation. The chapter row remains addressable as range metadata; the artifact does not exist.

hasGaps=true on a successful chapter means the output media has missing timeline coverage. Recovery from the temporary S3 segment object produces the original bytes and does not count as a gap.