ffmpeg's mxf muxer cannot write a growing file — its header/index duration stays N/A until the footer at close (proven: file grows on disk but readable duration never advances), so Premiere never sees growth. Replace the growing master muxer with bmx/raw2bmx --growing-file, the reference growing-OP1a writer. Capture image builds bmx (bbc/bmx v1.6) from source (bmxlib-tools absent in bookworm). Growing pipeline: one ffmpeg decodes SDI -> split into MPEG-2 422 essence + PCM (to named FIFOs) + the H.264 HLS preview; raw2bmx muxes the growing OP1a MXF to the share, updating IndexDuration incrementally. FIFO open-order deadlock fixed by parent-priming both FIFOs. Stop forwards SIGINT so ffmpeg EOFs and raw2bmx finalizes the footer; stop() awaits raw2bmx exit before the promotion worker uploads. Raster/fps -> raw2bmx essence flag via deriveGrowingRaster (default 1080i59.94). Proven live (zampp2): IndexDuration grows 43->223->403 frames at 3/8/15s mid-write (ffmpeg stayed N/A); finalized file valid; HLS preview intact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
146 lines
7.1 KiB
Docker
146 lines
7.1 KiB
Docker
# ── Stage 1: Build FFmpeg with DeckLink + NVENC (HEVC/H264) support ─────────
|
|
# All-Intra HEVC NVENC is the master codec for growing-file ingest (see
|
|
# docs/design/2026-05-29-all-intra-hevc-ingest.md). This stage gets the
|
|
# nv-codec-headers (header-only, no driver / no full CUDA toolkit needed)
|
|
# so ffmpeg's configure can light up hevc_nvenc / h264_nvenc / cuvid.
|
|
# At runtime, /dev/nvidia* + the host driver libs (via the NVIDIA Container
|
|
# Toolkit) supply the actual encoder.
|
|
FROM debian:bookworm AS ffmpeg-builder
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
build-essential nasm yasm pkg-config git ca-certificates python3 \
|
|
libssl-dev libx264-dev libx265-dev libvpx-dev libopus-dev \
|
|
libmp3lame-dev libsrt-openssl-dev \
|
|
libzmq3-dev zlib1g-dev libstdc++-12-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Copy in BMD DeckLink SDK headers and patch script
|
|
COPY sdk/ /decklink-sdk/
|
|
COPY patch_decklink.py /patch_decklink.py
|
|
COPY decklink-sdk16.patch /decklink-sdk16.patch
|
|
|
|
# nv-codec-headers — just the ffnvcodec public headers + a pkg-config file.
|
|
# Pin to a tag known to work with FFmpeg 7.1 (n12.x series).
|
|
RUN git clone --depth=1 --branch n12.1.14.0 https://github.com/FFmpeg/nv-codec-headers.git /nv-codec-headers \
|
|
&& make -C /nv-codec-headers PREFIX=/usr/local install
|
|
|
|
# Pull FFmpeg 7.1 source
|
|
RUN git clone --depth=1 --branch release/7.1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg
|
|
|
|
# Patch FFmpeg DeckLink code for SDK 16.x API changes
|
|
RUN python3 /patch_decklink.py
|
|
|
|
WORKDIR /ffmpeg
|
|
# NVENC adds: --enable-nvenc (encoder), --enable-cuvid (decoder), --enable-ffnvcodec.
|
|
# We deliberately do NOT enable --enable-cuda-nvcc / --enable-libnpp here — those
|
|
# require the full ~3GB CUDA toolkit and are only needed for GPU filters like
|
|
# yadif_cuda / scale_cuda. If §5's GPU deinterlace stretch goal goes ahead,
|
|
# rebuild this image off nvidia/cuda:12.x-devel and flip those flags on.
|
|
RUN ./configure \
|
|
--prefix=/usr/local \
|
|
--extra-cflags="-I/decklink-sdk -I/usr/local/include" \
|
|
--extra-ldflags="-L/usr/local/lib" \
|
|
--enable-gpl \
|
|
--enable-nonfree \
|
|
--enable-libx264 \
|
|
--enable-libx265 \
|
|
--enable-libvpx \
|
|
--enable-libopus \
|
|
--enable-libmp3lame \
|
|
--enable-libsrt \
|
|
--enable-libzmq \
|
|
--enable-decklink \
|
|
--enable-ffnvcodec \
|
|
--enable-nvenc \
|
|
--enable-cuvid \
|
|
--disable-doc \
|
|
--disable-debug \
|
|
--disable-ffplay \
|
|
&& make -j$(nproc) \
|
|
&& make install
|
|
|
|
# Sanity-check: hevc_nvenc and h264_nvenc must be present in the encoder list,
|
|
# otherwise the resulting image is useless for the All-Intra HEVC pipeline.
|
|
RUN /usr/local/bin/ffmpeg -hide_banner -encoders 2>&1 | grep -E 'nvenc' \
|
|
|| (echo 'FATAL: nvenc encoders missing from ffmpeg build' && exit 1)
|
|
|
|
# ── Stage 1b: Build bmx (raw2bmx / bmxtranswrap) from source ─────────────────
|
|
# bmx (bmxlib + libMXF + libMXF++) is the reference GROWING OP1a MXF writer. It
|
|
# writes a fresh IndexTableSegment (with an updated IndexDuration) into a new
|
|
# body partition at a periodic interval, so the recorded duration is readable —
|
|
# and INCREASES — from the header+index alone while the file is still being
|
|
# written (no footer needed). This is what makes the master a TRUE Premiere
|
|
# growing file. ffmpeg's MXF muxer cannot do this (its real duration/index lands
|
|
# only in the footer at av_write_trailer, so duration probes N/A until close).
|
|
#
|
|
# Debian/Ubuntu have no `bmxlib-tools` package (verified absent in bookworm), so
|
|
# we build from the BBC source. liburiparser/uuid/lzma/zlib/expat are the build
|
|
# deps; the runtime needs only libexpat1 + liburiparser1 + libuuid1 (added in
|
|
# the runtime stage below). Pinned to the bbc/bmx default branch (v1.6.x).
|
|
FROM debian:bookworm AS bmx-builder
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
build-essential cmake git ca-certificates pkg-config \
|
|
liburiparser-dev uuid-dev liblzma-dev zlib1g-dev libexpat1-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
# Pin to a release tag so the produced soname (libMXF.so.1.6 etc.) stays stable
|
|
# for the COPY in the runtime stage. v1.6 is the BBC bmx series verified here.
|
|
RUN git clone --recursive --branch v1.6 https://github.com/bbc/bmx.git /bmx \
|
|
|| git clone --recursive https://github.com/bbc/bmx.git /bmx
|
|
WORKDIR /bmx/build
|
|
RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local .. \
|
|
&& make -j"$(nproc)" && make install && ldconfig
|
|
# Sanity-check: raw2bmx must run, otherwise the growing-MXF pipeline is broken.
|
|
RUN /usr/local/bin/raw2bmx -h >/dev/null 2>&1 && echo 'raw2bmx OK'
|
|
|
|
# ── Stage 2: Runtime image ───────────────────────────────────────────────────
|
|
FROM node:20-bookworm
|
|
|
|
# Runtime deps for compiled ffmpeg libs.
|
|
# cifs-utils provides mount.cifs so growing-files capture can mount the SMB
|
|
# landing-zone share inside the (privileged) container at start (Approach A).
|
|
# util-linux supplies mount/umount/mountpoint.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
libx264-164 libx265-199 libvpx7 libopus0 libmp3lame0 \
|
|
libsrt1.5-openssl libzmq5 libstdc++6 libc++1 libc++abi1 \
|
|
cifs-utils util-linux \
|
|
libexpat1 liburiparser1 libuuid1 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Copy compiled ffmpeg/ffprobe
|
|
COPY --from=ffmpeg-builder /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
|
|
COPY --from=ffmpeg-builder /usr/local/bin/ffprobe /usr/local/bin/ffprobe
|
|
COPY --from=ffmpeg-builder /usr/local/lib/ /usr/local/lib/
|
|
|
|
# DeckLink runtime .so
|
|
COPY lib/libDeckLinkAPI.so /usr/lib/libDeckLinkAPI.so
|
|
COPY lib/libDeckLinkPreviewAPI.so /usr/lib/libDeckLinkPreviewAPI.so
|
|
|
|
# bmx (raw2bmx / bmxtranswrap / mxf2raw) — the growing OP1a MXF writer used for
|
|
# the edit-while-record master. Copy the built binaries + shared libs; runtime
|
|
# deps (libexpat1/liburiparser1/libuuid1) were installed above.
|
|
COPY --from=bmx-builder /usr/local/bin/raw2bmx /usr/local/bin/raw2bmx
|
|
COPY --from=bmx-builder /usr/local/bin/bmxtranswrap /usr/local/bin/bmxtranswrap
|
|
COPY --from=bmx-builder /usr/local/bin/mxf2raw /usr/local/bin/mxf2raw
|
|
COPY --from=bmx-builder /usr/local/lib/libMXF.so.1.6 /usr/local/lib/
|
|
COPY --from=bmx-builder /usr/local/lib/libMXF++.so.1.6 /usr/local/lib/
|
|
COPY --from=bmx-builder /usr/local/lib/libbmx.so.1.6 /usr/local/lib/
|
|
RUN cd /usr/local/lib \
|
|
&& ln -sf libMXF.so.1.6 libMXF.so.1 && ln -sf libMXF.so.1 libMXF.so \
|
|
&& ln -sf libMXF++.so.1.6 libMXF++.so.1 && ln -sf libMXF++.so.1 libMXF++.so \
|
|
&& ln -sf libbmx.so.1.6 libbmx.so.1 && ln -sf libbmx.so.1 libbmx.so \
|
|
&& ldconfig
|
|
# Verify raw2bmx resolves its libs and runs in the final image.
|
|
RUN raw2bmx -h >/dev/null 2>&1 && echo 'raw2bmx runtime OK'
|
|
|
|
# Mount points the recorder lifecycle expects to exist.
|
|
# /live — HLS preview output (bound from host LIVE_DIR by node-agent)
|
|
# /growing — growing-file master output (bound from host /mnt/NVME/MAM/growing)
|
|
RUN mkdir -p /live /growing
|
|
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm install --omit=dev
|
|
COPY . .
|
|
|
|
EXPOSE 3001
|
|
CMD ["node", "src/index.js"]
|