Repository navigation
1.0.5: interlaced video follows the shared field-per-grain convention - #23
Merged
Merged
Conversation
Capture writes frame k (from the frame's start at the frame rate) as two field grains, 2k with the first field's lines and 2k + 1 with the other lines (v210 and 8-bit YUV); before, one grain per frame at the field-rate index held frame lines 0-539 and every odd index stayed invalid. NTSC is interlaced_bff in the flow definition and NMOS (was tff for every mode). ANC of an interlaced input runs at the field rate (2x the frame rate): field 1's packets at grain 2k, field 2's at 2k + 1, the video field's index. Before, packets were written at the field index into a frame-rate data flow. Playout reads an interlaced flow as frames: grains 2k and 2k + 1 woven with line parity (idx & 1) ^ bff into the DeckLink frame; read position, preroll, offsets, catch-up, the sync wait and the audio per frame use the frame rate (1920 samples at 1080i50, was 960). Before, one field per output frame went into the top half and the read advanced at the field rate. The web UI maps interlaced flows to HD1080i50/HD1080i5994/PAL/NTSC. The mock marks the odd lines' counter band grey in interlaced modes and can dump played frames (MOCK_PLAYBACK_FRAME_FILE). Progressive is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01St7QwmonpoG9EEEqaLFXLi
…rnings - RFC 8331 F bits: interlaced ANC grain 2k gets 0b10 (field 1), 2k + 1 gets 0b11 (field 2); progressive keeps 0b00 (as mxl-test-player 1.2.3). - ANC line -> field: NTSC line 0 (unknown) is field 1, not field 2. - NMOS ANC flow grain_rate and the ANC receiver caps follow the MXL flow definition: the field rate for interlaced modes; video keeps the frame rate. - VideoReader logs mxl_flow_def_unreadable when the flow definition cannot be read or parsed (the flow is then read as progressive), and returns a frame with an invalid first field without waiting for the second. - Docs: ANC empty grains only when the other field has packets; grain counters count two grains per interlaced frame. - Tests: F bits, line 0, invalid first field, and an InputChannel capturing PAL from a fake card (field grains, counters, ANC per field with F bits). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01St7QwmonpoG9EEEqaLFXLi
LeeO86
added a commit
that referenced
this pull request
Oct 16, 2026
The output's audio read position was frame index x floor(48000 / rate) samples: 1601 instead of 1601.6 at 29.97, 800 instead of 800.8 at 59.94. At today's TAI frame indexes that is 7.8 resp. 20.7 days behind the sample index the sources write, so these outputs played silence for good. The position is now a frame number; each frame's block runs from the 48 kHz sample index of the frame's TAI start to that of the next frame (mxlTimestampToIndex(48000, mxlIndexToTimestamp(frame rate, k))), giving the 1602/1601 and 801/800 cadence. Scratch buffers are reserved for the largest block, and the read-offset cap uses it. Whole rates (25, 50, 24, 30, 60, 23.98) give exactly the same sample indexes as before. Rebased on #23: its interlaced output test now requires sound for NTSC too, and the NMOS ANC source's grain_rate uses the field rate like its flow (ancRateOf). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01St7QwmonpoG9EEEqaLFXLi
LeeO86
added a commit
that referenced
this pull request
Oct 16, 2026
…rid (#24). The output's audio read position was frame index x floor(48000 / rate) samples: 1601 instead of 1601.6 at 29.97, 800 instead of 800.8 at 59.94. At today's TAI frame indexes that is 7.8 resp. 20.7 days behind the sample index the sources write, so these outputs played silence for good. The position is now a frame number; each frame's block runs from the 48 kHz sample index of the frame's TAI start to that of the next frame (mxlTimestampToIndex(48000, mxlIndexToTimestamp(frame rate, k))), giving the 1602/1601 and 801/800 cadence. Scratch buffers are reserved for the largest block, and the read-offset cap uses it. Whole rates (25, 50, 24, 30, 60, 23.98) give exactly the same sample indexes as before. Rebased on #23: its interlaced output test now requires sound for NTSC too, and the NMOS ANC source's grain_rate uses the field rate like its flow (ancRateOf). Claude-Session: https://claude.ai/code/session_01St7QwmonpoG9EEEqaLFXLi Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes interlaced handling (audit 2026-10-16, sections A, B and D) per the shared convention (MXL 218ddaa): an interlaced flow holds one field per grain at twice the frame rate; grain 2k = the first field in time of TAI frame k, 2k + 1 = the second; tff first field = even lines, bff = odd lines; ANC at the field rate with the video field's index. Spec: new SPECIFICATION §3.2.2.
Capture (SDI → MXL)
interlaced_bff(DeckLinkbmdLowerFieldFirst) in the flow definition and NMOS; PAL and 1080i stay tff. Before: tff hard-coded.Playout (MXL → SDI)
VideoReaderreads an interlaced flow as frames: grains 2k and 2k + 1 woven with line parity(idx & 1) ^ bff; its rate, head, ring size and indexes are frames, so the read position, preroll, read offset, catch-up, slips and the audio per frame (1920 samples at 1080i50, was 960) all use the frame rate. The sync-group wait waits for the second field's time. A frame with an invalid field repeats (source_invalid). A re-created flow that switches progressive ↔ interlaced restarts the channel.Other
HD1080i50/HD1080i5994/PAL/NTSC(it looked forHD1080i25, which does not exist).MOCK_PLAYBACK_FRAME_FILEdumps played frames for lab checks.Tests
Round 2 (4786562)
Fbits: interlaced ANC grain 2k = 0b10, 2k+1 = 0b11; progressive 0b00.grain_rateand ANC receiver caps at the field rate for interlaced modes (video keeps the frame rate). Lab: 1080i50 data flow 50/1, video 25/1.VideoReaderwarnsmxl_flow_def_unreadableinstead of silently reading as progressive; an invalid first field returns the frame invalid at once.InputChannelinterlaced capture with a fake card.Not changed / found
frame index × floor(48000/rate)samples, which is off the TAI sample grid (1601 vs 1601.6 per frame), so their audio is likely silent. Pre-existing, not part of this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01St7QwmonpoG9EEEqaLFXLi