diff --git a/examples/Dockerfile b/examples/Dockerfile new file mode 100644 index 00000000..a5a70508 --- /dev/null +++ b/examples/Dockerfile @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. +# SPDX-License-Identifier: Apache-2.0 +FROM debian:trixie-slim AS base + +RUN apt-get update && apt-get install -y \ + gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-base \ + curl zip unzip tar \ + dumb-init \ + && rm -r /var/lib/apt/lists/* + + +RUN useradd -m mxl + +FROM base AS builder + +ARG CLANG_VERSION=19 +RUN apt-get update && apt-get install -y \ + build-essential bison flex \ + cmake ninja-build \ + pkg-config \ + git \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev + +USER mxl + +WORKDIR /home/mxl + +RUN git clone https://github.com/microsoft/vcpkg.git "${HOME}/vcpkg" \ + && cd "${HOME}/vcpkg" \ + && ./bootstrap-vcpkg.sh -disableMetrics + +RUN mkdir mxl +COPY CMakeLists.txt mxl/CMakeLists.txt +COPY CMakePresets.json mxl/CMakePresets.json +COPY Doxyfile.in mxl/Doxyfile.in +COPY vcpkg.json mxl/vcpkg.json +COPY lib mxl/lib +COPY cmake mxl/cmake +COPY tools mxl/tools +COPY utils mxl/utils + +RUN cmake -S "${HOME}/mxl" --preset Linux-GCC-Release -DCMAKE_INSTALL_PREFIX=/usr +RUN ninja -C "${HOME}/mxl/build/Linux-GCC-Release" + +USER 0:0 +RUN ninja -C "/home/mxl/mxl/build/Linux-GCC-Release" install + +FROM base AS mxl +COPY --from=builder /usr/lib/x86_64-linux-gnu/libmxl* /usr/lib/x86_64-linux-gnu/ +COPY --from=builder /usr/bin/mxl-* /usr/bin/ +USER mxl +WORKDIR /home/mxl +ENTRYPOINT ["/bin/bash", "-c"] + +FROM mxl AS mxl-info +ENTRYPOINT ["/usr/bin/mxl-info"] + +FROM mxl AS mxl-gst-testsrc +ENTRYPOINT ["/usr/bin/mxl-gst-testsrc"] + +FROM mxl AS mxl-fake-reader +COPY examples/scripts/fake-reader.sh /home/mxl/fake-reader.sh +ENTRYPOINT ["/usr/bin/dumb-init", "--", "/home/mxl/fake-reader.sh"] diff --git a/examples/Dockerfile.reader.txt b/examples/Dockerfile.reader.txt deleted file mode 100644 index ca061916..00000000 --- a/examples/Dockerfile.reader.txt +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. -# SPDX-License-Identifier: Apache-2.0 - -FROM debian:trixie-slim - -WORKDIR /app - -RUN apt update && apt-get -y install\ - procps \ - patchelf && rm -rf /var/lib/apt/lists/* - -COPY build/Linux-Clang-Release/lib/*.so* /app/ -COPY build/Linux-Clang-Release/lib/internal/*.so* /app/ -COPY build/Linux-Clang-Release/tools/mxl-info/mxl-info /app/ - -RUN patchelf --set-rpath /app mxl-info - -CMD ["sh", "-c", "while true; do /app/mxl-info -d /domain -l || true; sleep 2; done"] diff --git a/examples/Dockerfile.writer.audio.loop.txt b/examples/Dockerfile.writer.audio.loop.txt deleted file mode 100644 index 86e12ed5..00000000 --- a/examples/Dockerfile.writer.audio.loop.txt +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. -# SPDX-License-Identifier: Apache-2.0 - -FROM debian:trixie-slim - -WORKDIR /app - -RUN apt update && apt-get -y install \ - gstreamer1.0-libav \ - gstreamer1.0-plugins-base \ - gstreamer1.0-plugins-good \ - gstreamer1.0-plugins-bad \ - gstreamer1.0-x \ - patchelf && rm -rf /var/lib/apt/lists/* - -RUN mkdir /app/gst-plugins - -COPY build/Linux-Clang-Release/lib/*.so* /app/ -COPY build/Linux-Clang-Release/lib/internal/*.so* /app/ -COPY build/Linux-Clang-Release/tools/mxl-gst/mxl-gst-looping-filesrc /app/ -COPY build/Linux-Clang-Release/utils/gst-looping-filesrc /app/gst-plugins -ADD https://tsduck.io/streams/hotbird-13.0E/hotbird130E-ts6000-2018-05-11.ts /app/audio.ts - -RUN patchelf --set-rpath /app mxl-gst-looping-filesrc - -ENV GST_PLUGIN_PATH="/app/gst-plugins:${GST_PLUGIN_PATH}" - -CMD ["/app/mxl-gst-looping-filesrc", "-d", "/domain", "-i", "/app/audio.ts"] - - diff --git a/examples/Dockerfile.writer.txt b/examples/Dockerfile.writer.txt deleted file mode 100644 index c75e3bae..00000000 --- a/examples/Dockerfile.writer.txt +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. -# SPDX-License-Identifier: Apache-2.0 - -FROM debian:trixie-slim - -WORKDIR /app - -RUN apt update && apt-get -y install\ - gstreamer1.0-plugins-good\ - gstreamer1.0-x \ - patchelf && rm -rf /var/lib/apt/lists/* - -COPY build/Linux-Clang-Release/lib/*.so* /app/ -COPY build/Linux-Clang-Release/lib/internal/*.so* /app/ -COPY build/Linux-Clang-Release/lib/tests/data/*.json /app/ -COPY build/Linux-Clang-Release/tools/mxl-gst/mxl-gst-testsrc /app/ - -RUN patchelf --set-rpath /app mxl-gst-testsrc - -CMD ["/app/mxl-gst-testsrc", "-d", "/domain", "-v", "/app/v210_flow.json", "-a", "/app/audio_flow.json"] diff --git a/examples/Dockerfile.writer.video.loop.txt b/examples/Dockerfile.writer.video.loop.txt deleted file mode 100644 index 7a3451a7..00000000 --- a/examples/Dockerfile.writer.video.loop.txt +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. -# SPDX-License-Identifier: Apache-2.0 - -FROM debian:trixie-slim - -WORKDIR /app - -RUN apt update && apt-get -y install \ - gstreamer1.0-libav \ - gstreamer1.0-plugins-base \ - gstreamer1.0-plugins-good \ - gstreamer1.0-plugins-bad \ - gstreamer1.0-x \ - patchelf && rm -rf /var/lib/apt/lists/* - -RUN mkdir /app/gst-plugins - -COPY build/Linux-Clang-Release/lib/*.so* /app/ -COPY build/Linux-Clang-Release/lib/internal/*.so* /app/ -COPY build/Linux-Clang-Release/tools/mxl-gst/mxl-gst-looping-filesrc /app/ -COPY build/Linux-Clang-Release/utils/gst-looping-filesrc /app/gst-plugins -ADD https://tsduck.io/streams/uk-freeview/546000000.ts /app/video.ts - -RUN patchelf --set-rpath /app mxl-gst-looping-filesrc - -ENV GST_PLUGIN_PATH="/app/gst-plugins:${GST_PLUGIN_PATH}" - -CMD ["/app/mxl-gst-looping-filesrc", "-d", "/domain", "-i", "/app/video.ts"] - - diff --git a/examples/docker-compose.yaml b/examples/docker-compose.yaml index faecd476..b0d9b183 100644 --- a/examples/docker-compose.yaml +++ b/examples/docker-compose.yaml @@ -1,69 +1,83 @@ -# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. +# SPDX-FileCopyrightText: 2026 Contributors to the Media eXchange Layer project. # SPDX-License-Identifier: Apache-2.0 - +name: mxl-example +volumes: + mxl-domain: + driver: local + driver_opts: + device: none + type: tmpfs + o: nosuid,strictatime,uid=1000,gid=1000,mode=0755 services: - init: - image: alpine - command: ["sh", "-c", "mkdir -p /dev/shm/mxl"] + # Writes a test video flow to the domain + video-flow-writer: + build: + dockerfile: examples/Dockerfile + context: .. + target: mxl-gst-testsrc volumes: + - mxl-domain:/mxl-domain - type: bind - source: /dev/shm - target: /dev/shm - restart: no - - writer-media-function: - image: mxl-writer - profiles: [test-source] - depends_on: - - init + source: flow-configs + target: /home/mxl/flow-configs + command: -d /mxl-domain -v flow-configs/flow-video-v210.json + # Writes a test audio flow to the domain + audio-flow-writer: build: + dockerfile: examples/Dockerfile context: .. - dockerfile: examples/Dockerfile.writer.txt - restart: unless-stopped + target: mxl-gst-testsrc volumes: + - type: volume + source: mxl-domain + target: /mxl-domain - type: bind - source: /dev/shm/mxl - target: /domain - - writer-looper-video-media-function: - image: mxl-writer-video-looper - profiles: [looping-file-source] - depends_on: - - init + source: flow-configs + target: /home/mxl/flow-configs + command: -d /mxl-domain -a flow-configs/flow-audio.json + # Simulates a video flow reader + video-fake-reader: build: + dockerfile: examples/Dockerfile context: .. - dockerfile: examples/Dockerfile.writer.video.loop.txt - restart: unless-stopped + target: mxl-fake-reader volumes: - - type: bind - source: /dev/shm/mxl - target: /domain - - writer-looper-audio-media-function: - image: mxl-writer-audio-looper - profiles: [looping-file-source] - depends_on: - - init + - type: volume + source: mxl-domain + target: /mxl-domain + command: --domain /mxl-domain --video-flow-id 5fbec3b1-1b0f-417d-9059-8b94a47197ed --interval 1 + # Simulates an audio flow reader + audio-fake-reader: build: + dockerfile: examples/Dockerfile context: .. - dockerfile: examples/Dockerfile.writer.audio.loop.txt - restart: unless-stopped + target: mxl-fake-reader volumes: - - type: bind - source: /dev/shm/mxl - target: /domain - - reader-media-function: - image: mxl-reader - depends_on: - - init + - type: volume + source: mxl-domain + target: /mxl-domain + command: --domain /mxl-domain --audio-flow-id 5fbec3b1-1b0f-417d-9059-8b94a47197ed --interval 1 + # Prints the video flow information every 5 seconds. + video-flow-info: build: + dockerfile: examples/Dockerfile context: .. - dockerfile: examples/Dockerfile.reader.txt - restart: unless-stopped + target: mxl-info volumes: - - type: bind - source: /dev/shm/mxl - target: /domain - stdin_open: true - tty: true + - type: volume + source: mxl-domain + target: /mxl-domain + entrypoint: ["/usr/bin/dumb-init", "--"] + command: ["/bin/bash", "-c", "trap 'exit 0' SIGINT SIGTERM; while true; do mxl-info mxl:///mxl-domain?id=5fbec3b1-1b0f-417d-9059-8b94a47197ed; sleep 5; done"] + # Prints the audio flow information every 5 seconds. + audio-flow-info: + build: + dockerfile: examples/Dockerfile + context: .. + target: mxl-info + volumes: + - type: volume + source: mxl-domain + target: /mxl-domain + entrypoint: ["/usr/bin/dumb-init", "--"] + command: ["/bin/bash", "-c", "trap 'exit 0' SIGINT SIGTERM; while true; do mxl-info mxl:///mxl-domain?id=b3bb5be7-9fe9-4324-a5bb-4c70e1084449; sleep 5; done"] diff --git a/examples/flow-configs/flow-audio.json b/examples/flow-configs/flow-audio.json new file mode 100644 index 00000000..4cc83c8f --- /dev/null +++ b/examples/flow-configs/flow-audio.json @@ -0,0 +1,23 @@ +{ + "$copyright": "SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project.", + "$license": "SPDX-License-Identifier: Apache-2.0", + "description": "MXL Audio Flow", + "format": "urn:x-nmos:format:audio", + "tags": { + "urn:x-nmos:tag:grouphint/v1.0": [ + "Media Function XYZ:Audio" + ] + }, + "label": "MXL Audio Flow", + "version": "1441812152:154331951", + "id": "b3bb5be7-9fe9-4324-a5bb-4c70e1084449", + "media_type": "audio/float32", + "sample_rate": { + "numerator": 48000 + }, + "channel_count": 2, + "bit_depth": 32, + "parents": [], + "source_id": "2aa143ac-0ab7-4d75-bc32-5c00c13d186f", + "device_id": "169feb2c-3fae-42a5-ae2e-f6f8cbce29cf" +} diff --git a/examples/flow-configs/flow-audio.json.license b/examples/flow-configs/flow-audio.json.license new file mode 100644 index 00000000..acfd4c53 --- /dev/null +++ b/examples/flow-configs/flow-audio.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/flow-configs/flow-video-v210.json b/examples/flow-configs/flow-video-v210.json new file mode 100644 index 00000000..103537b1 --- /dev/null +++ b/examples/flow-configs/flow-video-v210.json @@ -0,0 +1,43 @@ +{ + "$copyright": "SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project.", + "$license": "SPDX-License-Identifier: Apache-2.0", + "description": "MXL Test Flow, 1080p29", + "id": "5fbec3b1-1b0f-417d-9059-8b94a47197ed", + "tags": { + "urn:x-nmos:tag:grouphint/v1.0": [ + "Media Function XYZ:Video" + ] + }, + "format": "urn:x-nmos:format:video", + "label": "MXL Test Flow, 1080p29", + "parents": [], + "media_type": "video/v210", + "grain_rate": { + "numerator": 30000, + "denominator": 1001 + }, + "frame_width": 1920, + "frame_height": 1080, + "interlace_mode": "progressive", + "colorspace": "BT709", + "components": [ + { + "name": "Y", + "width": 1920, + "height": 1080, + "bit_depth": 10 + }, + { + "name": "Cb", + "width": 960, + "height": 1080, + "bit_depth": 10 + }, + { + "name": "Cr", + "width": 960, + "height": 1080, + "bit_depth": 10 + } + ] +} \ No newline at end of file diff --git a/examples/flow-configs/flow-video-v210.json.license b/examples/flow-configs/flow-video-v210.json.license new file mode 100644 index 00000000..acfd4c53 --- /dev/null +++ b/examples/flow-configs/flow-video-v210.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/scripts/bind-compose-domain.sh b/examples/scripts/bind-compose-domain.sh new file mode 100755 index 00000000..4b8b8738 --- /dev/null +++ b/examples/scripts/bind-compose-domain.sh @@ -0,0 +1,22 @@ +#! /bin/bash +# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. +# SPDX-License-Identifier: Apache-2.0 + +target_dir="${1}" +if [[ -z "${target_dir}" ]]; then + echo "Usage: ${0} " + exit 1 +fi + +domain_dir="$(docker inspect mxl-example_mxl-domain | jq -r '.[0] | .Mountpoint')" +if [[ -z "${domain_dir}" || "${domain_dir}" == "null" ]]; then + echo "Failed to get local volume directory." + exit 1 +fi + +mkdir -vp "${target_dir}" +target_dir="$(realpath "${target_dir}")" + +echo "Local volume directory: ${domain_dir}" +echo "Creating bind mount to: ${target_dir}, this might require elevated permissions" +sudo mount --bind "${domain_dir}" "${target_dir}" -o "uid=$(id -u),gid=$(id -g)" diff --git a/examples/scripts/fake-reader.sh b/examples/scripts/fake-reader.sh new file mode 100755 index 00000000..3d89ed29 --- /dev/null +++ b/examples/scripts/fake-reader.sh @@ -0,0 +1,80 @@ +#! /bin/bash +# SPDX-FileCopyrightText: 2025 Contributors to the Media eXchange Layer project. +# SPDX-License-Identifier: Apache-2.0 + +trap "exit 0" SIGINT SIGTERM + +program_name="${0}" +while [[ $# -gt 0 ]]; do + case $1 in + -d | --domain) + mxl_domain="${2}" + shift + shift + ;; + -v | --video-flow-id) + mxl_video_flow_id="${2}" + shift + shift + ;; + -a | --audio-flow-id) + mxl_audio_flow_id="${2}" + shift + shift + ;; + -i | --interval) + read_interval="${2}" + shift + shift + ;; + -*) + echo "Unknown option $i" + exit 1 + ;; + *) ;; + esac +done + +function flow_last_write_time() { + head -c224 <"${flow_dir}/data" | tail -c8 | od -vtu8 | head -n1 | awk '{print $2}' +} + +function flow_head_index() { + head -c208 <"${flow_dir}/data" | tail -c8 | od -vtu8 | head -n1 | awk '{print $2}' +} + +if [[ -z "${mxl_domain}" ]] || [[ -z "${mxl_video_flow_id}" ]] && [[ -z "${mxl_audio_flow_id}" ]]; then + echo "usage: ${program_name} -d (-v | -a ) [-i ]" + exit 1 +fi + +is_video=0 +if [ ! -z "${mxl_video_flow_id}" ]; then + flow_id="${mxl_video_flow_id}" + flow_dir="${mxl_domain}/${mxl_video_flow_id}.mxl-flow" + is_video=1 +else + flow_id="${mxl_audio_flow_id}" + flow_dir="${mxl_domain}/${mxl_audio_flow_id}.mxl-flow" +fi + +have_flow=1 +read_interval="${read_interval:-"0.1"}" + +while true; do + sleep "${read_interval}" + if [[ ! -f "${flow_dir}/data" ]]; then + if [[ "${have_flow}" != 0 ]]; then + echo "Waiting for flow ${flow_id} to be created" + have_flow=0 + fi + continue + fi + have_flow=1 + touch "${flow_dir}/access" + if [[ "${is_video}" = "1" ]]; then + echo "read video flow ${flow_id} head index $(flow_head_index "${flow_id}"), last write: $(flow_last_write_time "${flow_id}")" + else + echo "read audio flow ${flow_id} head index $(flow_head_index "${flow_id}")" + fi +done