Open Video Menu manifest format — v1.0 (preview)
Status: 1.0 preview. The compatibility charter applies: nothing here is ever removed or redefined, and later versions only add optional fields. 1.0 grew from the 0.1 draft, so 1.0 readers read 0.1 manifests too.
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are used as described in RFC 2119.
1. Purpose
An Open Video Menu manifest adds a menu to media that already exists: a film and its extras, a season of episodes, an imported DVD. It holds backgrounds, buttons, highlight art, remote-control navigation and actions. Every action points to existing media files or streams. The manifest never contains video.
2. Files
- A manifest is a UTF-8 JSON file named
<Title>.openvideomenu.json, placed next to the main media file, for exampleSintel (2010)/Sintel (2010).openvideomenu.json. - Relative paths resolve against the manifest's own URL or location. They use forward slashes, and they MAY contain spaces and other characters (readers percent-encode as needed).
- Absolute URLs are allowed anywhere a path is.
- Readers MUST accept JSON nested at least 256 levels deep (actions inside actions). Writers with deeper logic SHOULD share it with named actions (§6.4), which keeps manifests shallow.
- Keep menu art away from media scanners. Put menu assets in their own folder (e.g.
menus/). If it holds video or audio, include a.plexignorecontaining*and an empty.ignore(Jellyfin), or media servers will list the clips as titles or as extra versions of the film.openvideomenu validatewarns when these are missing.
2.1 Bundles
A menu set can ship as one file instead of a manifest plus a folder of art: a bundle named
<Title>.ovmz, placed where the manifest would be (next to the main media file).
- Publishers SHOULD bundle manifests that have local assets (menu images, clips, audio), so a title's
folder holds one menu file. A plain
<Title>.openvideomenu.jsonwith loose assets remains valid. Manifests without local assets need no bundle. - A bundle is a ZIP archive holding
manifest.jsonat its root and the manifest's assets at the paths the manifest names (e.g.menus/main.jpg). - Every entry MUST be stored uncompressed (ZIP method 0), and entries MUST NOT be encrypted. Menu art is already compressed (JPEG, PNG, video, audio), so compression would save almost nothing, while stored entries let a server serve any member, including byte ranges of a video clip, without unpacking. ZIP64 MAY be used for bundles over 4 GB.
- Entry names use forward slashes and MUST NOT be absolute or contain
..segments. - Paths resolve as if the bundle were a folder named
<Title>.ovmzholdingmanifest.json: asset paths resolve inside the bundle.mediarefs resolve against the folder that contains the bundle, exactly as for a plain manifest, so the same manifest works in either form. - If a folder holds both
<Title>.ovmzand<Title>.openvideomenu.json, readers SHOULD use the bundle. - Readers MUST support both forms.
openvideomenu packmakes a bundle from a manifest and its assets;openvideomenu unpackdoes the reverse.
The machine-readable schema is schema/manifest.schema.json. The schema checks structure only. Cross-references (§7) are checked by validators such as openvideomenu validate.
3. Top level
| Field | Required | Meaning |
|---|---|---|
format | yes | Always "openvideomenu". |
version | yes | "MAJOR.MINOR". This document is 1.0. Readers of 1.x MUST also read 0.x manifests, the draft this version grew from, as 1.0. |
title | yes | { name, year?, ids?, season?, volume? }. ids holds strings such as imdb, tmdb or tvdb. See §3.1. |
source | no | { kind, generator? }. kind is e.g. "dvd", "authored" or "generated". |
media | yes | Map of media id → media. At least one entry. |
menus | no | Map of menu id → menu. |
rootMenu | if menus | The menu opened at start and after top-level playback ends. |
firstPlay | no | Action run at start instead of opening rootMenu, e.g. an intro. |
level | no | The lowest conformance level at which the menus work as authored, 0–3. Players below it show the fallback list. |
vars | no | (Level 3) Named variables with starting values, e.g. {"audio": 1, "subs": 0} (§6.1). |
actions | no | (Level 3) Named actions, run from anywhere with run. |
resumePoint | no | (Level 3) How long the resume point lasts: "untilEnd" (default) or "untilUsed". |
keys | no | (Level 2) What the remote's menu keys do. |
cuePlayback | no | (Level 2) What playback started during playback does to the title playing: "interrupt" (default) or "replace". |
counters | no | (Level 3) Variables that count seconds by themselves. |
fallback | yes | Fallback list for players without menu support. |
x-* | no | Vendor extensions (§9). |
3.1 Season and volume
A series or a long film often comes as several menus with the same title.name: one per season, or
several per season. Two optional integers in title tell them apart:
season(0 or more): the season of a series the menu belongs to;0is specials.volume(1 or more): which one of a set of menus for the same title and season it is, e.g. the second disc of a season, or the second part of a film.
"title": { "name": "Example Show", "year": 1971, "season": 5, "volume": 2 }
Players SHOULD show them with the name wherever a viewer chooses or opens a menu (e.g.
"Season 5 · Disc 2") and SHOULD order menus with the same name by season, then volume. A player MAY
word volume to suit source.kind, e.g. "Disc" for "dvd" or "bluray", otherwise "Volume".
4. Media
"feature": {
"ref": "Sintel (2010).mp4",
"kind": "feature",
"label": "Sintel",
"duration": 888,
"chapters": [{ "time": 0, "label": "Chapter 1", "thumbnail": "menus/chapter-1.jpg" }]
}
ref(required, unless the media is made ofsegments, §4.2) is either:- a relative path or URL, or
{ "scheme": "...", "id": "..." }, an opaque reference the host player resolves. For example, a media server can use its own item ids. A player that doesn't know a scheme treats that media as unavailable.
kindsuggests how to group the media:feature,episode,trailer,featurette,behindthescenes,deleted,interview,scene,shortorother. Other values are allowed.chaptersare start times in seconds and MUST increase. Chapter numbers in actions start at 1.numberis an optional integer for logic to read (Level 3, §6.1): a disc's title number, an episode number. Players ignore it otherwise.
4.1 Angles (Level 2)
The same scene filmed or drawn more than one way: a DVD's multi-angle title, a sing-along with and without lyrics, a storyboard beside the finished shot. A media entry says where its angles are in one of two ways, the author's choice (not both):
"feature": { "ref": "Roots (angle 1).mkv", "angles": ["Roots (angle 2).mkv"] }
"storyboard": { "ref": "Storyboard.mkv", "angleTracks": 3 }
angles: the other angles as separate files, each aref(path, URL or{scheme, id}). The media's ownrefis angle 1,angles[0]angle 2, and so on. The files MUST line up in time, so changing angle keeps the position. This is how rips of multi-angle titles come out: one whole file per angle.angleTracks: N (at least 2): the file's first N video tracks are angles 1 to N.
Which angle plays: the play item's angle (a number or {"var": name}, like audio), otherwise
@angle (§6.1). A number outside the media's angles plays angle 1, like a DVD player. A player SHOULD
let the viewer change angle during playback (a DVD remote's Angle key) and report it as @angle. A
player that can't switch video tracks plays angle 1 of angleTracks media. Players without Level 2
play the ref (angle 1).
4.2 Segments (Level 2)
A title can be made of pieces of other media and still play as one title: an extended cut that
splices scenes into the film, a disc's "play all" over separately ripped episodes, a playlist that
plays a concert's songs in another order. Such a media entry has segments instead of ref:
"extended": {
"label": "Extended cut",
"segments": [
{ "media": "film", "end": 3120 },
{ "media": "extra-scenes", "end": 95 },
{ "media": "film", "start": 3120 }
],
"chapters": [{ "time": 0 }, { "time": 3120 }, { "time": 3215 }]
}
- Each segment is
{ media, start?, end? }: a range, in seconds, of a media entry that has aref(segments don't nest).startdefaults to 0.endis required on every segment but the last, which may run to the end of its media. - The media plays its segments in order as one title with its own timeline, starting at 0 with the
first segment: its
chapters,cues(§6.3),duration, the viewer's seek bar, a play'sstart, and where it stops (forresume) are all on that timeline. The segments' own media keep their chapters and cues for when they play by themselves; inside the composite they don't apply. - Players SHOULD join segments without a gap. A player that can't MAY show a brief pause between them.
- A composite media has no
anglesorangleTracksof its own. A play'sangle(or@angle) applies to each segment whose media has angles (§4.1); a segment whose media lacks that angle plays angle 1. - Players without Level 2 can't play segments: they treat the media as unavailable.
5. Menus
"main": {
"canvas": { "width": 720, "height": 480, "aspect": "16:9" },
"background": { "image": "menus/main.png", "color": "#000" },
"defaultButton": "play",
"autoNav": false,
"buttons": [ ... ]
}
canvassets the coordinate space for every rect in the menu, in storage pixels.aspectis the display aspect ratio ("16:9","4:3","2.39:1"or a number). It defaults towidth/height, meaning square pixels.- Players MUST display the canvas at
aspect, scaled to fit the screen and letterboxed or pillarboxed as needed. This is how anamorphic DVD menus (720×480 shown at 16:9) keep correct geometry.
background.imageis stretched to the whole canvas.background.colorfills anything not covered.buttonsAt: see §5.1.defaultButtongets focus when the menu opens. It defaults to the first button.buttonsMAY be empty: a menu that only shows its background, a clip or a still, like a disc's menu cell without buttons. Itstimeout(§5.2) decides what follows, as a step up, so a sequence of clips, or one that loops by going to itself, adds nothing to Back's history. Any key or click but Back skips it: its timeout runs at once, as an intro is skipped (§5.1), unless the player honours askipIntroprohibition (§6.6). Without a timeout it waits: there is nothing to press, and Back and the menu keys (§6.6) work as on any menu.back: what the remote's Back key does on this menu (§6.2).timeout: what happens when the menu is left alone (§5.2).buttonSets: buttons that change while the menu plays (§5.3).autoNav: truemeans that pressing a direction with nonaventry moves to the nearest button that way. The reference algorithm uses button centres: a candidate must lie in the direction of travel, and the score is the distance along the axis plus twice the sideways offset. WithautoNavfalse or absent, a missing direction does nothing, which matches DVD behaviour.
Buttons
| Field | Required | Meaning |
|---|---|---|
id | yes | Unique within the menu. |
rect | yes | [x, y, width, height] in canvas pixels. |
action | yes | What activating the button does. |
label | no | Human name. Used for accessibility and tools, and drawn only when showLabel is true. |
a11yLabel | no | Screen-reader text when it differs from label. |
showLabel | no | If true, the player draws label itself. Otherwise the text is part of the background art, as on DVDs. |
states | no | normal, selected and activated, each { image?, rect?, textColor?, fill? }. |
nav | no | { up?, down?, left?, right? }: button ids in the same menu. |
autoAction | no | If true, the button activates as soon as it is selected (DVD auto-action). |
altRects | no | (Level 2, information) { letterbox?, panscan? }: where the button sits when the source shows the canvas letterboxed or pan-scanned (a disc's other button groups for a widescreen menu on a 4:3 TV). Players show the whole canvas at its aspect (§5), so rect is what they use; these keep the source's layouts. |
How states are drawn:
- The state's
imageis drawn overrect, or over the state's ownrectif it has one. PNG with alpha is recommended. - Players show exactly one state per button:
activatedbriefly when the button is activated,selectedwhile it has focus, otherwisenormal. - If a button has no
selectedimage and no drawn label, players SHOULD draw their own focus indicator, so focus is never invisible.
5.1 Motion menus
"background": {
"image": "menus/main.jpg",
"video": { "src": "menus/main-loop.mp4", "loopStart": 2.5 }
},
"buttonsAt": 2.5
background.videois a motion background stretched to the canvas likeimage. It plays once from 0, which is the menu's intro, then loops fromloopStart(default 0) for as long as the menu is shown. It MAY carry its own audio.background.audiois menu music. It loops the same way and plays alongside the video.- (Level 2)
loop: falseonbackground.videoorbackground.audioplays it once and leaves it on its last frame (the video) or silent (the audio), as a disc's clip followed by its still time. Atimeoutafter the clip's length plus the still time then moves on. - Music that plays with a motion background SHOULD be in the video itself, not in
background.audio. Some players can play only one stream at a time (Roku is one), so they would drop a separate audio track. Usebackground.audiofor menus with a still image, or for music that deliberately loops independently of the video. A player that can't play both plays the video. imageremains the poster: shown while the video loads, and by players without motion support. Those players still work, because they simply show the still.buttonsAtis the number of seconds into the intro before buttons appear and respond. It's measured on the background video's clock when there is one, otherwise from when the menu opened. Until then, buttons and their highlights are hidden.- Intros play only on a fresh entry: at start, or via
goto. Returning to a menu withbackor at the end of playback starts the background atloopStartwith buttons ready, so the intro is not replayed. - Any key or click during an intro skips it. Players jump the background to
buttonsAtand show the buttons. That input does nothing else. Pointer hover does not skip. Players MUST NOT make intros unskippable, except a player that honours askipIntroprohibition (§6.6). - Keep menu assets small (a few MB per menu) so menus open instantly on TVs over Wi-Fi. H.264 MP4 or VP9 WebM for video; AAC (in MP4/M4A) or Opus for audio. MP4/M4A files SHOULD be written with the index at the start ("faststart"), because some players, such as Android WebView, stall on files that must be read from the end first.
5.2 Menu timeouts (Level 2)
A menu can move on by itself when it's left alone, as DVD menus do when their video or still time runs out: an intro gives way to the main menu, or the film starts after a minute.
"timeout": { "after": 30, "action": { "type": "play", "media": "feature" } }
afteris in seconds, counted from when the menu appears (intro included). It starts over every time the menu appears, whether fresh or returned to.restartOnInput(defaulttrue): a key press or click restarts the countdown, so nothing starts while the viewer is choosing. Importers set it tofalseto match a disc whose timer runs regardless.actionruns when the time is up, as a step up, like a menu'sback(§6.2): no history is added, and agototo a menu already in history unwinds it. Nobody chose it, so Back never returns to an intro that moved on by itself. It MAYactivateone of the menu's own buttons.- A menu's own video looping is not a timeout. An intro that runs into its loop with the same buttons
is one menu with
background.video.loopStart(§5.1). - Navigation model: whenever a menu with a timeout appears, and after input when
restartOnInputapplies (unless the viewer is leaving), the navigator emits{ "type": "startTimer", "menu", "after" }. The player keeps the clock and reports{ "type": "timeout", "menu" }when it runs out. A timeout for a menu that isn't showing any more is ignored. A newstartTimerreplaces the previous countdown.
5.3 Button sets (Level 2)
A motion menu's buttons can change while it plays: a disc's highlight information can make buttons appear, move and go during a menu cell (a "skip" button over the intro, a row of choices that slides in).
"buttons": [ ... ],
"buttonSets": [
{ "from": 0, "to": 6, "buttons": [{ "id": "skip", "rect": [560, 400, 120, 40], "action": { "type": "goto", "menu": "main" } }] },
{ "from": 12, "defaultButton": "s2", "buttons": [ ... ] }
]
- Each set shows its
buttonsfromfromtotoseconds (to the end of the loop withoutto) on the menu's clock: the background video's, as forbuttonsAt(§5.1), so the windows come round with each loop; without a background video, the time since the menu appeared. Windows MUST NOT overlap. - Outside every window, the menu's own
buttonsshow. Players without Level 2 show only those, so importers SHOULD give the menu the buttons it shows longest. - A set's buttons have everything a menu's have; their
navstays within the set.defaultButtonandautoNavmay be given per set. Button ids are unique within a set, and the same id in two sets (or in a set and the menu) is the same button for focus. - When the buttons change, the highlight stays: on the same id when the new buttons have it,
otherwise on the button in the same place in the list (a disc keeps the selected button number),
otherwise on the set's
defaultButton. - A
gotoMAY name a button that only a set has: it is highlighted when that set shows. A menu'sback,timeoutandkeysMAYactivateany of its buttons; only buttons showing can be pressed. - Navigation model: players report
{ "type": "buttonSet", "index": i }when the menu's clock enters seti's window, and{ "type": "buttonSet", "index": null }when it leaves them all.
6. Actions
Every action is an object with a type:
type | Fields | Behaviour |
|---|---|---|
play | media, chapter?, start?, end?, after?, cues? | Play one media item. chapter (1-based) wins over start (seconds). (Level 2) end stops there as if the item ended; cues (§6.3). |
playAll | items[], after?, order?, count? | Play { media, chapter?, start? } items in order. (Level 2) order: "shuffle" plays every item once in a random order; "random" plays count items (default: as many as there are), each picked at random (a disc's random and shuffle programs). Both draw from the seeded generator (§6.1). |
goto | menu, button?, transition?, history? | Open a menu, focusing button or its defaultButton. transition is a clip (path) played first, full screen. history: "replace" doesn't add the current menu to Back's history, for page turns (§6.2). |
back | — | Return to the previous menu in the history. At the top of the history, the player MAY exit. |
activate | button | Press a button of the current menu: its activated state shows and its action runs. Only allowed in a menu's back (§6.2). |
set | var, value, op?, then? | (Level 3) Change a variable or player state, then optionally run another action (§6.1). |
if | test, then, else? | (Level 3) Run then or else depending on a condition (§6.1). |
do | steps | (Level 3) Run actions in order until one navigates (§6.1). |
run | action | (Level 3) Run a named action from actions, as if it were written here (§6.4). |
saveResume | media? or item?, at?, chapter?, queue?, after? | (Level 3) Save the resume point (§6.5). |
clearResume | — | (Level 3) Forget the resume point (§6.5). |
seek | at or chapter | (Level 2) During playback, continue the title elsewhere on its timeline (§6.3). |
timer | after, action | (Level 3) Start the navigation timer (§6.7). |
cancelTimer | — | (Level 3) Stop the navigation timer (§6.7). |
requestParental | level, then, else? | (Level 3) Ask the player to allow a parental level for now (§6.8). |
exit | — | (Level 2) Leave the title, from a menu or during playback, as the player's own Stop would (a disc's Exit). The player closes the title; one that can't starts it again. |
resume | otherwise? | Continue the last playback the viewer stopped early, from where they stopped, then whatever was to follow it (the rest of a playAll, its after). When there's nothing to resume, run otherwise instead, as if it were the action itself (same history rules); without otherwise, nothing happens. otherwise is any action allowed where the resume is, but not another resume. |
Navigation semantics (normative; the conformance suite checks them):
- Start: if
firstPlayexists, run it. Otherwise openrootMenu. A manifest without menus shows the fallback list. gotofrom a button pushes the current menu and focused button onto the history (unless it sayshistory: "replace").backpops it.- When playback completes on its own, the player returns to the menu and button it was started from, then runs
afterif present. Playback a menu without buttons started (a clip's timeout) returns where Back from that menu goes: the last menu in Back's history, which it takes off the history, since clips add nothing to it. Playback started with no menu, for example fromfirstPlay, returns torootMenu. - When the viewer stops playback early (the Back key), the player returns to the same place and does not run
after. It remembers the playback forresume: the media, the position, and what was to follow. That is the resume point (§6.5). Completing that media's playback forgets it. - During playback, direction keys and Enter belong to the video player. Only Back (and, at Level 2, the menu keys of §6.6) is handled by the menu logic, except while an overlay's buttons show (§6.3).
afterMUST NOT beplayorplayAll, and neither may theotherwiseof aresumeused asafter. UseplayAllfor sequences.- Transitions: a
gotowithtransitionpushes history as usual, plays the clip, then opens the target menu as a fresh entry (with its intro). Any key or click skips the transition straight to the menu (unless the player honours askipIntroprohibition, §6.6). Hover is ignored.
6.1 Variables and logic (Level 3)
DVDs keep state in registers and decide with small command programs: which soundtrack was chosen, whether the film was started, the viewer's preferred language, which button to highlight. Open Video Menu writes the same logic as JSON data: variables, player state, conditions and a few actions, which any player can read and any tool can inspect, with no scripting language. With named actions (§6.4) that logic can loop, as a disc's programs do, so it is a small program all the same: the step budget there keeps it from running without end. It works on any media, not only discs, and the fallback list still works everywhere.
This section and §6.4–§6.8 are Level 3: the program state a player keeps. Everything else marked Level 2 a player shows or plays from the manifest as written.
"vars": { "audio": 1, "played": 0, "lang": "en" },
...
{ "type": "if",
"test": { "var": "@audioLanguage", "eq": "fr" },
"then": { "type": "do", "steps": [
{ "type": "set", "var": "@audio", "value": 2 },
{ "type": "goto", "menu": "main-fr" } ] },
"else": { "type": "goto", "menu": "main" } }
Variables. vars declares the manifest's own variables and their starting values: an integer or
text. A variable keeps that type. Variables start at their vars values when the player starts and keep
their values across menus and playback until it starts again. Names MUST NOT start with @.
Player state is read as @ variables. Players report what they know when they start (and when it
changes, e.g. the viewer picks another soundtrack during playback); anything unknown reads as its default.
Players SHOULD let the viewer choose the preferred languages in their settings, as a DVD player's setup
does, and report them as @audioLanguage, @subtitleLanguage and @menuLanguage. A change reaches
the logic as soon as it is reported; menus chosen once, at firstPlay, follow it the next time the
title starts.
| Variable | Type | Default | Meaning |
|---|---|---|---|
@audio | integer | 0 (unknown) | Current audio track, 1-based. Writable. |
@subtitle | integer | 0 (off) | Current subtitle track, 1-based; 0 = off. Writable. |
@angle | integer | 1 | Current angle, 1-based. Writable. |
@focus | text | — | The highlighted button's id: of the menu; during playback, of the overlay showing (§6.3), or "" when none shows. Writable: setting it highlights that button of the menu or the overlay. |
@audioLanguage, @subtitleLanguage, @menuLanguage | text | "" | The viewer's preferred languages, ISO 639-1 ("fr"). |
@lastMedia | text | "" | Media id of the playback in progress, or of the last one. |
@lastNumber | integer | 0 | That media's number (§4); 0 when it has none. |
@lastChapter | integer | 0 | Chapter (1-based) playback is in, or was in when it last stopped; after playback completes, the chapter it ended in (the one its end closes, or the media's last); after a resume, the chapter the resume point was saved in until playback reaches the next chapter (§6.5). Logic run during playback reads it live: for a cue or an overlay's button, the chapter playback is in just before the cue's time (so a cue at a chapter's start reads the chapter it ends, as a disc's cell command does); for a key or the timer, the chapter at the position reported. |
@region | integer | 0 (unknown) | Player region, 1–8. |
@parentalLevel | integer | 8 (unrestricted) | Parental level, 1–8. A granted requestParental raises it (§6.8). |
@country | text | "" | ISO 3166-1 alpha-2. |
@aspect | text | "16:9" | The TV's shape: "16:9" or "4:3". |
@resumeMedia | text | "" | Media id of the resume point (§6.5); "" when there is none. |
@navTimer | integer | 0 | Whole seconds left on the navigation timer (§6.7); 0 when none runs. |
@karaoke | integer | 0 | Karaoke channels to mix in, as bits (a disc's SPRM 11). Writable. Players with karaoke audio MAY apply it; others keep it as a value. |
Values are an integer, text, or {"var": name} (the current value of a variable or player state).
Conditions (test) compare one variable with a value, or combine conditions:
{ "var": v, "eq" | "ne": value }for any type;"lt" | "le" | "gt" | "ge"for integers;"has": true when any bit of the value is set in the variable (integers).{ "all": [tests] },{ "any": [tests] },{ "not": test }.- Exactly one comparison per test. Comparing an integer with text is an error.
Actions:
set{ var, value, op?, then? }changes a variable, then runsthen.opisset(default),add,sub,mul,div(towards zero),mod,and,or,xor, orrandom(a whole number from 1 tovalue). Division or modulo by zero leaves the variable unchanged. Onlysetworks on text. Writable player state: setting@audio/@subtitle/@anglechooses the track for the next playback (a DVD's SetSTN), and during playback (in a cue, an overlay's button or a key) also changes it now (§6.3); setting@focushighlights a button (SetHL_BTN).if{ test, then, else? }runsthenwhen the test holds, otherwiseelse(or nothing).do{ steps }runs actions in order and stops after the first one that navigates: opens a menu, starts playback or a transition, or leaves. Like a DVD program, which ends at its link.if,do,set,run,saveResume,clearResume,timer,cancelTimerandrequestParentalMUST NOT be used in the fallback list.afterMAY be anifordothat plays (a disc's conditional play-all), but not a bareplay/playAll.
Focus from a variable. A menu's defaultButton and a goto's button MAY be {"var": name}
holding a button id. When it doesn't name a button of that menu, the usual default applies.
Choosing tracks (Level 2; from a variable, Level 3). play and playAll items MAY have audio
and subtitle, each a number or {"var": name}:
audio: Nplays the Nth audio track of the media (1-based);subtitle: Nshows the Nth subtitle track;subtitle: 0turns subtitles off.- Without them, playback uses the current
@audioand@subtitlewhen known, otherwise the player's default. A number outside the media's tracks is ignored. angle: Nplays angle N of media that has angles (§4.1); without it,@angledecides.
Randomness. random draws from a generator the player seeds at start. The reference navigator uses
seed = (seed × 1103515245 + 12345) mod 2³¹, result 1 + seed mod value, so conformance cases can
check it; players seed it unpredictably.
Players without Level 3 ignore these fields: menus open with their plain defaultButton, media play
with their own or default tracks, and set/if/do do nothing. Menus that need logic to work say so
with the manifest's level (§10), and players below it show the fallback list instead.
6.2 The remote's Back key
On a DVD, the remote's Return key doesn't retrace every menu you visited: each menu has a "go up" target. Open Video Menu menus say the same with back:
"neo-bio-2": {
"back": { "type": "activate", "button": "cast" },
"buttons": [
{ "id": "cast", "rect": [300, 400, 100, 40], "action": { "type": "goto", "menu": "cast", "button": "neo" } },
{ "id": "next", "rect": [500, 400, 100, 40], "action": { "type": "goto", "menu": "neo-bio-3", "history": "replace" } }
]
}
- Remote Back on a menu with
backruns that action instead of popping history. It MAY be any action;activatepresses one of the menu's own buttons (typically its on-screen Back or "Cast" button), showing its activated state. - It is a step up, not a new visit. A
gotorun bybackdoesn't add to history, and if its target is already in the history, the history is unwound to it (that entry and everything after it are dropped). So ten bio pages still take one press to leave. - Page turns (next/previous page of the same thing) SHOULD use
history: "replace", so they add no Back steps even in menus withoutback. - Without
back, Back returns through history as in §6 (at the top, the player MAY exit). - Back on
rootMenuleaves the title (the player exits), unless that menu has its ownback. This holds however the viewer got there: a "return to the main menu" button is agotoand adds history, but the main menu is still the top, and retracing into a submenu from it surprises viewers. - Back never traps the viewer. If a menu's
backleaves the viewer on the same menu, players MUST return through history instead (or leave, onrootMenu). (A DVD may leave Return inactive; a TV app must not, unless it honours abackprohibition, §6.6.) - Importers choose per menu to match the source as closely as possible: a DVD's Go Up target, the menu's own on-screen Back button, or history (see the DVD profile).
6.3 During playback (Level 2)
Menus can reach into playback: stop a title at a point (a DVD's "play this song, then back to the menu"), run an action when playback reaches a moment, or show buttons over the video, like the icon some discs show in certain scenes that branches to a featurette and back.
"feature": { "ref": "Film.mkv", "cues": [
{ "at": 1200, "action": { "type": "set", "var": "halfway", "value": 1 } },
{ "from": 2470, "to": 2485, "canvas": { "width": 1280, "height": 720 },
"buttons": [{ "id": "follow", "rect": [1080, 600, 160, 90], "label": "Follow",
"states": { "normal": { "image": "menus/rabbit.png" } },
"action": { "type": "play", "media": "rabbit-1" } }] } ] }
{ "type": "play", "media": "concert", "start": 300, "end": 540 }
endon a play item (seconds): playback stops there and counts as ended (afterruns, aplayAllgoes on). It MUST be afterstart.cueson a media entry apply whenever it plays; on a play item, only when played that way, as well as the media's. Two kinds:- Point cue
{ at, action }: the action runs when playback passesat. Not at the time playback starts from (nor on the way there, when a player starts at the keyframe before it), and not when the viewer seeks past it. - Overlay
{ from, to, canvas, buttons, defaultButton?, autoNav? }: buttons over the video fromfromtoto, with everything a menu's buttons have (states, labels,nav). While it shows, the arrows move between its buttons and Enter presses one; Back still stops playback. Overlays in one list MUST NOT overlap; when a media's and an item's overlap, the one that started last shows.
- Point cue
- Cue actions are ordinary actions, run at the cue's time:
atfor a point cue, an overlay'stofor its buttons.- Starting playback (
play,playAll,resume) interrupts the current one. When the new playback ends or the viewer stops it, the interrupted one continues from the cue's time.afteris not allowed there. cuePlayback: "replace"(top level) makes it replace the current one instead, as a disc's links do: the title playing ends and nothing returns to it. Like a disc's link, it leaves the resume point as it is: a cue that wants the title it ends to be resumable saves it first (saveResume, §6.5). The new playback returns where the old one would have.afteris allowed. This holds for every way playback starts during playback: cues, overlays' buttons, keys (§6.6) and the timer (§6.7). A manifest uses one behaviour throughout; a single replacing jump in an interrupting manifest is agotowithsaveResume, thenresume.gotoleaves playback; it is remembered forresumeat the cue's time, unless the cue saved or cleared the resume point itself (§6.5).backstops playback, like the remote's Back.seek{ at }or{ chapter }continues the playback in progress elsewhere on its timeline, without interrupting it: a disc's link to a cell, program or chapter of the title playing (LinkCN, LinkPGN, LinkPTTN), or a cell that loops by linking to its own start. Cues behave as when the viewer seeks: point cues on the way don't run, overlays follow the position. Like a link, it ends ado.seekonly works during playback: validators reject it in menus' buttons,back,timeout,firstPlayandafter.- (Level 3) Setting
@audio,@subtitleor@anglechanges that track of the playback in progress now, as well as for the next playback (a disc's SetSTN in a cell command, or a commentary button over the film). The navigator emits{ "type": "tracks", audio?, subtitle?, angle?, at }; players SHOULD switch in place and MAY restart the title where it is with the new tracks. set,ifanddowork as anywhere;activateis not allowed.
- Starting playback (
holds[{ at, for }]on a media entry (whenever it plays) or a play item (beside the media's) keep a frame on screen: playback stops on the frame atatand stays there forforseconds, or until the viewer goes on ("for": "input"). A disc's still cells: a photo gallery whose pictures wait for the remote, an information page shown for ten seconds, a menu made of a title cell (an overlay over a held frame). Rips keep a still cell as one frame, so without holds it would flash past.- A hold doesn't move the timeline: positions, chapters and cues stay where they are, and the time held is not counted.
- Cues before
athave run when the hold starts, and an overlay whose window ends atatstays up while the frame is held; cues atatrun when the hold ends, as a disc runs a cell's command after its still time. - The viewer goes on from a hold with Enter (OK), or by pressing one of an overlay's buttons. Players MAY end any hold on any key: the viewer can always move on. A hold that ends the title (a last still) holds before playback counts as ended.
- Like point cues, a hold happens when ordinary play reaches it, not when seeking past it; playback that comes round again (a loop) holds again.
- The play effect lists the item's holds (the media's and the item's, in time order) for the player to carry out. Players without Level 2 play through them.
- Players carry out a
seekeffect{ "type": "seek", position }by moving playback there, and watch for the cues listed in the play effect and reportcue(a point cue'sat, an overlay'sfrom) andcueEnd(an overlay'sto, or leaving its window by seeking). A player that can't draw over its video SHOULD still run point cues; without Level 2, cues are ignored andendplays to the end of the media.
6.4 Named actions (Level 3)
A disc's programs are shared: many buttons lead into the same title-start logic, the same dispatcher, the same end-of-episode check. Written out at every use, such logic multiplies along play-alls and resume points until a manifest is megabytes long. Named actions write it once:
"actions": {
"episode-end": { "type": "if", "test": { "var": "playingAll", "eq": 1 },
"then": { "type": "goto", "menu": "episodes", "button": "next" },
"else": { "type": "goto", "menu": "main" } }
}
...
{ "type": "play", "media": "e1", "after": { "type": "run", "action": "episode-end" } }
actionsmaps names to actions.run{ action }runs one as if it were written in place: the same history rules (a button'sgotoadds history, one in a menu'sbacksteps up), the samedostopping, and in a cue, the same cue time.- A named action MAY run others and itself: that is how a disc's loops (a program jumping back to an
earlier line) are written. One event runs at most 10,000 actions; when a loop reaches that, the
rest are dropped and the viewer stays where they were. Players MUST support loops of that length:
an action in tail position (a
set'sthen, anif's branches, ado's last step, aresume'sotherwise, arun) continues the current action rather than nesting inside it. - Named actions are shared by every menu, so they MUST NOT contain
activate(which presses a button of one menu). Where a named action may run is checked through it: run from a cue, it must not start playback withafter(§6.3), and a play'saftermust not come to another playback through it. - Players without Level 3 ignore
run, as they doset,ifanddo.
6.5 The resume point (Level 3)
There is one resume point: the playback resume continues. Stopping playback early saves it, and so
does a cue that leaves playback for a menu. It holds the whole playback (the item, what was queued after
it, its after), so resuming carries on a play-all and runs its after-action, as a disc does. The
resumed item plays with the tracks as they are now (@audio, @subtitle, @angle), since the viewer
may have changed them while watching.
A disc keeps its resume point longer, and its programs set it: a call from a title to a menu (CallSS) names the cell to come back to, and the point survives other titles until a Resume uses it.
"resumePoint": "untilUsed",
...
{ "at": 1320, "action": { "type": "do", "steps": [
{ "type": "saveResume", "chapter": 7 },
{ "type": "goto", "menu": "scenes" } ] } }
resumePoint:"untilEnd"(default): completing that media's playback forgets the point."untilUsed": the point lasts through other playback and menus until aresumeuses it, or a stop, a cue orsaveResumereplaces it, orclearResumeforgets it.saveResume{ media?, item?, at?, chapter?, queue?, after? }saves a point:mediafromchapter(its start) oratseconds (default 0).item, instead ofmedia, saves a whole play item: the resumed title keeps its ownend, cues, holds and keys (its tracks are the current ones, as for any resume), and its cues before the point don't run, as when the viewer seeks. Withoutmedia, it saves the playback in progress "here", atchapterorat, or by default the cue's time; outside playback it does nothing.atandchapterare not both given.- With
mediaoritem,queue(play items) andaftersay what follows it when it's resumed, as a stopped playback remembers them: the rest of a play-all, then its after-action, which must not start playback. That lets logic shared by many places (a named action) save a point inside a play-all, where it can't say "here". clearResumeforgets the point.- A cue that saves or clears the point and then leaves for a menu keeps what it said: leaving saves "here" only when the cue's action didn't save or clear the point itself.
- The point remembers
@lastChapteras it was when saved (Level 3), as a disc's call saves its chapter register with the resume cell. When it is resumed,@lastChapterreads that chapter until playback reaches the next chapter, then is live again. For a viewer's stop, or a save "here", that is the chapter playing anyway; it differs when a save names another position (a call's resume cell). Leaving playback from a cue keeps the chapter the cue's logic read. @resumeMediareads the point's media id,""when there is none, so logic can choose (a disc's "if a title was stopped, show Resume").- Hosts MAY restore a point from their own records (the viewer's last position from the media server) as just media and position.
6.5.1 Keeping the session between viewings
A disc player keeps its registers and its resume point until the disc comes out: stop the film, come back later, and Resume carries on with the settings the menus chose (a commentary, a viewing mode). A player MAY keep the same for each manifest between viewings, as a session:
| Field | What it holds |
|---|---|
vars | The variables (§6.1), as they were. |
player | The player state (@audio, @subtitle, @angle, …), as the navigator last had it. |
lastStop | The resume point, whole: media, position, and when they apply item, queue, after and chapter. |
Nothing else is kept: not the menu showing, the focus, Back's history, an overlay, the navigation timer, counters or a parental request. A session is plain JSON.
- Restoring it: the start of navigation takes the session. Its
varsreplace the manifest's starting values (names the session doesn't have keep theirs). Itsplayercomes first and what the player reports at the start (§6.1, its languages, region, …) overrides it, since the TV may have changed. ItslastStopbecomes the resume point. Then the manifest starts as usual:firstPlay, orrootMenu. - Starting with the resume point: a player MAY instead start by resuming, as the
resumeaction does (the item from its position, then its queue andafter). Without a resume point it starts as usual. - A player MUST discard a session when the manifest changes, by whatever version it has of it (the file's modified time, a hash, the server index's tag). Variables and resume points mean nothing to a different manifest.
- A player that keeps sessions SHOULD keep one per manifest and per viewer, and MAY forget them (after the title is finished, after a while, to save space). Forgetting one is the same as a first viewing.
6.6 Remote keys and prohibitions (Level 2)
A DVD remote has more than Back: Menu opens the disc's root menu, even in the middle of the film, and Title its title menu. A disc can also call its audio, subtitle, angle and chapter menus. And a disc says what it forbids: skipping the warning, fast-forwarding the logos, the Menu key during the film.
"keys": { "title": { "type": "goto", "menu": "titles" } },
"media": {
"film": { "ref": "Film.mkv",
"keys": { "menu": { "type": "goto", "menu": "film-root" } },
"prohibits": ["fastForward", "menuKey"] }
}
keys{ menu?, title?, audioMenu?, subtitleMenu?, angleMenu?, chapterMenu? }maps the menu keys to actions. It may be on the manifest, a menu, a media entry and a play item.- During playback, a menu key runs like a cue's action (§6.3) at the position it was pressed, which
players report with the key (
{ "type": "press", "key": "menu", "position": 1234 }): a menu it opens leaves playback, remembered forresume; playback it starts interrupts and returns. The play item's keys win over its media's, and the media's over the manifest's. - On a menu, the menu's keys win over the manifest's. They run as a step up, like
back(§6.2), and MAYactivateone of the menu's own buttons. - Without an action, Menu and Title open
rootMenu(from playback, remembered forresume); the other keys do nothing. - Players SHOULD give the Menu key to the remote's Menu key, or another key a TV remote has (Roku's
*). Title and the menu calls are optional: players MAY offer them however suits their remotes. prohibitslists what the source forbids, on a menu, a media entry or a play item:seek,chapterJump,titleJump,stop,back,previous,next,fastForward,rewind,pause,menuKey,titleKey,audioMenuKey,subtitleMenuKey,angleMenuKey,chapterMenuKey,resume,buttons,endHold,audioChange,subtitleChange,angleChange,karaokeChange,displayModeChange(a DVD's user operations), andskipIntro(skipping a menu's intro, a menu without buttons, or a transition). Other values are allowed.prohibitsAt[{ from, to?, ops }]on a media entry or a play item records what is forbidden during parts of it, on its timeline (to its end withoutto): a disc forbids things cell by cell and even within a cell. Windows may overlap. What's forbidden at a moment is the plainprohibitsplus theopsof every window covering it.- Prohibitions are information, never a requirement. The viewer can skip anything, any time, unless
the player chooses otherwise: a player MAY honour any of them and MUST NOT be required to. Recording a
prohibition never obliges a player to enforce it. The play effect lists the prohibitions of what plays
(the media's and the item's, and their
prohibitsAtwindows by start) for players that do honour them. - Enforcement belongs to players, never to the spec. Where this document gives the viewer a way out (skipping intros and transitions, §5.1 and §6; Back never trapping, §6.2; ending holds, §6.3), a player that chooses to honour the matching prohibition MAY withhold it and still conforms. The conformance cases describe the default, a player that honours none.
6.7 Time: the navigation timer and counters (Level 3)
A disc can set a timer that jumps somewhere when it runs out, whatever is showing (SetNVTMR: "the film starts in ten seconds" over a title made of stills), and keep registers that count seconds by themselves.
"vars": { "elapsed": 0 },
"counters": ["elapsed"],
...
{ "type": "timer", "after": 10, "action": { "type": "play", "media": "feature" } }
timer{ after, action }starts the navigation timer:actionruns afterafterseconds, wherever the viewer is then. There is one timer; a new one replaces it.cancelTimerstops it.- When it runs out, nobody chose it: on a menu (or during a transition) its action runs as a step up,
like a menu's
timeout(§5.2); during playback, like a cue's action (§6.3) at the position playback had reached. So its action follows a cue's rules. @navTimerreads the whole seconds left on the timer (0 when none runs).countersnames integer variables ofvarsthat count by themselves: each reads as its value plus the whole seconds since it was last set, or since the player started. Setting one starts its count again from the new value.- Navigation model: events MAY carry
time, the player's clock in seconds (any origin, never going back); counters and@navTimerread it. Players SHOULD send it with every event when the manifest has counters or timers. Atimeremits{ "type": "startNavTimer", "after" }(acancelTimer,{ "type": "cancelNavTimer" }); the player keeps the clock and reports{ "type": "navTimer", position? }when it runs out, with the playback position during playback. A report after the timer was replaced or stopped is ignored.
6.8 Parental level requests (Level 3)
A disc can ask the player to allow a higher parental level for a while (SetTmpPML), for example before an uncut version, and take another path when the player won't.
{ "type": "requestParental", "level": 7,
"then": { "type": "play", "media": "uncut" },
"else": { "type": "play", "media": "theatrical" } }
requestParental{ level, then, else? }asks for parental levellevel(1–8). If the player allows it,@parentalLevelbecomeslevelandthenruns; otherwiseelseruns (or nothing).- Players decide how to answer: at once from their own settings, or after asking the viewer (for a PIN). A player without parental controls SHOULD allow every request.
- Like a link, the request ends a
do: what a disc does when the request is refused goes inelse. - Navigation model: the navigator emits
{ "type": "askParental", "level" }and runs the branch when the player reports{ "type": "parental", "granted" }, with the same history and cue rules the request had. An answer with nothing asked is ignored; a new request replaces one still waiting. - Choosing what plays by parental level (a disc's parental block) needs no request:
ifon@parentalLevel.
7. Cross-reference rules
Validators MUST reject a manifest when:
- a
media,menu,rootMenu,defaultButton,goto.button,activate.button,navorrun.actiontarget doesn't exist, or - a named action contains
activate, or only runs named actions in a circle (it would never do anything), or activateis used anywhere but a menu'sback,timeoutorkeys, or- a
resume'sotherwiseis anotherresume, or - button ids are duplicated within a menu, or
- a chapter number is out of range, or
fallbackis empty or all of it is hidden.
Rects that extend outside the canvas are warnings, not errors.
8. Fallback list (Level 0)
"fallback": [
{ "label": "Movie", "items": [{ "label": "Play Movie", "action": { "type": "play", "media": "feature" } }] },
{ "label": "Hidden features", "hidden": true, "items": [ ... ] }
]
Every manifest MUST include a fallback list: groups of items whose actions are play or playAll. A player that can only show a list and play media supports every manifest ever written. Groups marked hidden (easter eggs) are not shown unless the viewer asks for them.
9. Unknown fields and extensions
- Readers MUST ignore properties they don't understand.
- Vendor or tool data goes in properties starting with
x-, e.g.x-dvdfor DVD provenance (see profiles/dvd.md). Validators SHOULD warn about unknown properties that don't start withx-, because they're usually typos. - A manifest with a newer minor version MUST still be read: its unknown fields are ignored.
- A manifest with a different major version MUST be rejected rather than guessed at.
10. Conformance levels
| Level | A player must support |
|---|---|
| 0: Basic | The fallback list and playback. |
| 1: Menus | Canvases and aspect, still backgrounds, buttons, state overlays, nav/autoNav, autoAction, chapters, the actions play, playAll, goto, back, resume and activate, a menu's back, and the navigation semantics. |
| 2: Enhanced | What a player shows and plays from the manifest as written: motion backgrounds, menu audio, buttonsAt, transitions and skipping (§5.1), menu timeouts (§5.2), button sets (§5.3), angles (§4.1), segments (§4.2), track numbers on play items (§6.1), cues and buttons during playback, end, seek, holds and cuePlayback (§6.3), menu keys (§6.6), playAll order and exit (§6). Prohibitions and altRects are information players MAY use. |
| 3: Disc logic | Program state: variables, player state, conditions and logic, and focus and tracks from variables (§6.1); live track changes (§6.3); named actions (§6.4); the resume point (§6.5); the navigation timer and counters (§6.7); parental level requests (§6.8). Menus per language are separate menus chosen with @menuLanguage. |
Each level includes the ones below it. Players without a level ignore its fields and actions; a manifest
says the level its menus need with level:
levelis the lowest level at which the menus work as authored. Writers SHOULD give it when the menus need Level 2 or 3. Menus whose Level 2 features only enrich them (a motion background with a still poster) MAY declare a lower level; a translated disc, where nearly every button runs logic, declares 3.- A player below a manifest's
levelSHOULD show the fallback list rather than menus that would only partly work. It MAY offer the menus anyway. Withoutlevel, players try the menus. - Validators warn when
levelis lower than the level the manifest uses, or absent when it uses Level 3.openvideomenu validateshows the level each manifest uses.
Players claim a level by passing that level's cases in conformance/. Each case lists input events and the expected state and effects, so any implementation in any language can run them.