Skip to content
Nathan Butler edited this page May 1, 2026 · 2 revisions

libopx

.NET library and tooling for parsing, filtering, converting, and restriping MXF, teletext, and subtitle formats used in broadcast workflows.

Repo: nathanpbutler/libopx
Type: .NET 9 library + CLI + GUI applications


Quick Reference

What you want to do Command / API
Filter teletext by magazine/rows opx filter -m 8 -r 20-22 input.vbi
Extract streams from MXF opx extract -d -n -o out input.mxf
Restripe MXF timecodes opx restripe -t 10:00:00:00 input.mxf
Convert between formats opx convert input.vbi output.t42
Parse lines (library) FormatIO.Open("file.vbi").ParseLinesAsync()
Parse packets (library) FormatIO.Open("file.mxf").ParsePacketsAsync()
Run Blazor GUI dotnet run --project apps/opxBlazor
Run desktop restriper dotnet run --project apps/simpleRestriper

Components

Component Path Purpose
libopx (NuGet) lib/ Core library -- format parsing, conversion, restriping
opx CLI apps/opx/ Command-line tool wrapping the library
opxBlazor apps/opxBlazor/ Blazor Server GUI for filtering and viewing teletext
simpleRestriper apps/simpleRestriper/ Avalonia desktop app for batch MXF timecode restriping

Supported Formats

Format Extension Type Read Write
VBI .vbi Line-based Yes Yes
VBI Double .vbid Line-based Yes Yes
T42 .t42 Line-based Yes Yes
ANC .bin Packet-based Yes --
MXF .mxf Packet-based Yes Restripe
TS .ts Packet-based Yes --
RCWT .rcwt Output -- Yes
STL .stl Output -- Yes

Dependencies

Library (libopx)

Zero external NuGet dependencies. Only build-time SourceLink for symbol generation.

CLI (opx)

  • System.CommandLine (v2.0.2) -- Command-line argument parsing

Blazor GUI (opxBlazor)

  • MudBlazor (v9.1.0) -- Material Design UI components
  • Targets .NET 10.0

Desktop GUI (simpleRestriper)

  • Avalonia (v11.3.*) -- Cross-platform desktop UI framework
  • Targets .NET 10.0

Installation

As a NuGet package

dotnet add package libopx

From source

git clone https://github.com/nathanpbutler/libopx.git
cd libopx
dotnet build libopx.sln

Publish CLI as self-contained executable

dotnet publish apps/opx -c Release -r win-x64 --self-contained
dotnet publish apps/opx -c Release -r linux-x64 --self-contained
dotnet publish apps/opx -c Release -r osx-x64 --self-contained

Verify: dotnet test tests/libopx.Tests.csproj -- all tests should pass.


Configuration

No external configuration files. All settings are code-based constants or passed via CLI arguments / API parameters.

Key defaults defined in Constants.cs:

Constant Value Purpose
Default magazine 8 Magazine filter when none specified
Caption rows 1--24 Row range for -c / caps mode
VBI line size 720 bytes Single-width VBI
VBI double line size 1440 bytes Double-width VBI
T42 line size 42 bytes Teletext packet size
KLV key size 16 bytes MXF key length

Usage

CLI -- Filter teletext

opx filter -m 1 -r 0,23 input.vbi
cat input.vbi | opx filter -c
opx filter --pid 70 input.ts

CLI -- Extract MXF streams

opx extract -k d,v input.mxf
opx extract -d -n -o output_base input.mxf
opx extract -d --klv input.mxf

CLI -- Restripe MXF timecodes

opx restripe -t 10:00:00:00 input.mxf
opx restripe -t 00:00:00:00 -pp input.mxf

CLI -- Convert formats

opx convert input.vbi output.t42
opx convert -c input.mxf output.stl
opx convert --pid 70 input.ts output.t42
opx convert -m 8 -r 20-22 input.t42 output.vbi

Library -- Fluent API

// Async line parsing with filtering
using var io = FormatIO.Open("input.vbi")
    .Filter(magazine: 8, rows: [20, 21, 22]);

await foreach (var line in io.ParseLinesAsync())
    Console.WriteLine(line);

// Format conversion
using var converter = FormatIO.Open("input.vbi")
    .Filter(useCaps: true)
    .ConvertTo(Format.STL);

await converter.SaveToAsync("output.stl");

// MXF packet parsing with key filtering
using var mxf = FormatIO.Open("input.mxf")
    .WithKeys(KeyType.Data);

await foreach (var packet in mxf.ParsePacketsAsync())
    Console.WriteLine(packet);

// MXF restripe
FormatIO.Open("input.mxf").Restripe("10:00:00:00");

Blazor GUI

dotnet run --project apps/opxBlazor

Open http://localhost:5000. Upload a file, set magazine/row/page filters, and view results in the data grid.

Desktop Restriper

dotnet run --project apps/simpleRestriper

Drag-drop MXF files, enter a new timecode (or check "Zero"), and click Restripe. Progress bars track each file.


Testing

dotnet test tests/libopx.Tests.csproj              # All tests
dotnet test --filter "MemoryBenchmarkTests"         # Memory profiling
dotnet test --filter "FormatIOTests"                # Specific test class

Test sample files are downloaded automatically from GitHub releases via SampleFiles.EnsureAsync(). Set OPX_SAMPLES_VERSION to pin a specific version (defaults to v1.0.0).


Troubleshooting

Symptom Likely cause Fix
CS0618 warnings during build Deprecated SMPTE APIs still referenced in MXF.cs and tests Expected -- safe to ignore
Test failures on first run Sample files not downloaded Ensure network access for SampleFiles.EnsureAsync()
InvalidOperationException on convert Unsupported format conversion pair Check supported conversion routes: VBI/T42 to RCWT/STL/VBI/T42
MXF restripe fails File opened read-only Use FormatIO.Open(path).Restripe() which opens read-write internally
Timecode validation error Drop-frame mismatch or out-of-range frames Verify timecode format matches file's frame rate and drop-frame mode
TS parsing returns no lines Wrong PID Use --pid to specify the teletext PID, or omit for auto-detection via PAT/PMT