Skip to content

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

mmts-dsfilter

DirectShow source/splitter filter for MMT/TLV .mmts files and .mmtsedit edit decision lists.

This filter was built for playback/debugging with MPC-BE and MPC-HC. It uses the dantto4k demuxer sources for MMT/TLV parsing and exposes HEVC video, AAC audio, and ARIB B24 subtitle streams as DirectShow output pins.

Features

  • Loads .mmts and .mmtsedit files through IFileSourceFilter.
  • Registers .mmts and .mmtsedit as DirectShow source filter extensions.
  • Exposes HEVC video output.
  • Exposes each MPT mp4a audio stream as a separate audio output pin.
  • Outputs ordinary AAC streams as ADTS/MEDIASUBTYPE_RAW_AAC1.
  • Converts AAC LATM 22.2ch audio streams to stereo PCM output for playback.
  • Preserves ordinary AAC 2ch/5.1ch streams as separate ADTS output pins when they coexist with a 22.2ch stream.
  • Supports DirectShow seeking through IMediaSeeking.
  • Queues downstream delivery to avoid renderer/decoder blocking the demuxer.
  • Starts playback and seek recovery from HEVC RAP/IRAP frames.
  • Converts ARIB B24 TTML subtitles to ASS with per-character cell layout.
  • Renders cell backgrounds as merged rectangles (no edge artifacts).
  • Renders DRCS (broadcaster-defined) glyphs as ASS vector drawings.

Repository Layout

src/          Filter implementation
msvc/         Visual Studio project and module definition
settings/     Sample INI configuration file
tools/        Local probe/debug helper sources
scripts/      Registration helper scripts
baseclasses/  DirectShow BaseClasses copy from Microsoft samples
thirdparty/   Static FFmpeg libraries and headers (see thirdparty/ffmpeg/NOTICE.txt)

Build outputs are intentionally ignored by Git.

Dependencies

  • Windows
  • Visual Studio 2022 with C++ desktop workload
  • Windows SDK
  • dantto4k checked out next to this repository:
<workspace>\
  dantto4k\
  mmts-dsfilter\

The Visual Studio project currently references ..\..\dantto4k from msvc/mmts-dsfilter.vcxproj.

FFmpeg (AAC LATM decoding for 22.2ch audio) is linked statically from thirdparty/ffmpeg, so neither vcpkg nor FFmpeg DLLs are needed. To rebuild those libraries, run thirdparty\ffmpeg\build_static_msvc.cmd <ffmpeg-source> with an FFmpeg n9.0.1 checkout; see thirdparty/ffmpeg/NOTICE.txt.

Build

From the repository root:

.\build.ps1 -Configuration Debug

or:

.\build.ps1 -Configuration Release

The filter is produced as:

msvc\x64\<Configuration>\mmts-dsfilter.ax

Before building, tools/generate-version-header.ps1 writes the version this build comes from (git describe --tags --always --dirty, e.g. v20261015-6-gc8a6086) to src/MmtsDsfilterVersion.h, which is not committed (like TVTest's TVTestVersionHashGen.bat). It shows in the file properties of mmts-dsfilter.ax (Details tab) and in the debug log. Without git it is unknown.

Debug Helper Tools

tools/mmts_audio_split.exe scans MPT audio layout changes and reports whether ordinary AAC streams can be converted to ADTS:

tools\mmts_audio_split.exe <input.mmts> [--split] [--max-mb N] [--progress-mb N]
  • --max-mb N limits scanning to the first N MB for quick investigation.
  • --progress-mb N prints progress every N MB. The default is 512 MB.
  • --split writes one file per detected audio-layout segment. It cannot be combined with --max-mb, because a partial scan may miss later layout changes.

tools/subtitle_ttml_dump.exe writes every subtitle TTML sample out as XML and prints the B24 cell grid each paragraph asks for:

tools\subtitle_ttml_dump.exe <input.mmts> [outdir] [--verbose]
  • outdir receives sub_NNN.xml per sample. Omit it to only print.
  • --verbose also prints the X position of every character.
  • A cell is one font width plus one arib-tt:letter-spacing, and a region's extent is exactly the cell count times that, so the printed laidOut and extent columns must agree. They are what the splitter positions text from, so a MISMATCH (also the exit code) is a caption it will lay out wrong - ruby drifting off the character it annotates, for instance.

tools/mmts_pin_probe.exe plays a file through the real filter in a DirectShow graph and prints how many samples each output pin delivered:

tools\mmts_pin_probe.exe <input.mmts> [--seek SEC] [--run SEC] [--ax PATH] [--expect-main-subtitle] [--expect-pin NAME]...
  • --run SEC is real playback time (default 60). Cover an MPT change - a commentary track starting, a channel boundary - to check that captions stay on the main subtitle pin and every audio pin keeps receiving its stream.
  • --ax PATH loads an unregistered build, e.g. msvc\x64\Release\mmts-dsfilter.ax; it reads the .ini next to it, so set DebugLogPath there to log the same run. Without it the registered filter is used.
  • --expect-main-subtitle fails the run unless the main caption pin received samples and no other subtitle pin did; --expect-pin NAME (repeatable) fails it unless the pin whose name starts with NAME received samples. The exit code is 1 on a failed expectation, so runs can be scripted as a regression check.
  • Demuxer-only probes miss routing bugs: the filter locks its stream lists once pins exist, which only a real graph reproduces.

Service selection

The filter keeps the selected MMT package when another service's MPT arrives. A PLT-confirmed removal of that package selects the first remaining local package, so recordings with a channel change can continue without mixing simultaneously multiplexed services. Without a PLT, the first MPT chooses the package; an unrelated MPT alone does not switch it. Seeking preserves the selection, and opening a new file resets it.

Regression Tests

Build Release first, then run from the repository root:

./tools/run_subtitle_state_test.cmd
./tools/run_subtitle_file_index_test.ps1
./tools/run_subtitle_file_index_test.ps1 -Test subtitle_render_test
./tools/run_subtitle_file_index_test.ps1 -Test playback_state_test
./tools/run_subtitle_file_index_test.ps1 -Test transport_state_test

The transport state test checks long package IDs, isolation of video/audio/ subtitle/event callbacks across services, PLT transitions, seek selection and all defined rational video frame rates. Playback tests also verify negotiated video aspect/rate, sample durations and dynamic format changes on queued samples.

The playback state test exercises production EDL seek logic, video callbacks, queued DirectShow output samples, and video/audio track changes. It checks normalized RAP-map lookups, decoder preroll before cuts and seek targets, reference/B-picture handling at cut ends, cumulative timestamps across cuts, and worker joins with every connected output branch flushed. Fixtures are written under work/playback-state.

The capture sink inspects timestamps and sample flags; it does not decode HEVC or AAC and does not replace playback/seek checks in MPC-BE or MPC-HC with real recordings. The runners require Visual Studio C++ tools and the Release build's v143 toolset and object files.

Non-destructive edits (.mmtsedit)

The filter supports a non-destructive edit decision list in a .mmtsedit JSON file placed next to the media (recording.mmts -> recording.mmtsedit). The edited program is the concatenation of the listed source segments, played without rewriting the media file:

{
  "version": 1,
  "source": "recording.mmts",
  "sourceSize": 123456789,
  "map": "recording.mmtsmap",
  "timeline": [
    { "sourceStartMs": 25000, "sourceEndMs": 1800000 },
    { "sourceStartMs": 1860000, "sourceEndMs": 3600000 }
  ]
}

Opening recording.mmts plays the original file and does not look for an edit file. To apply edits, open recording.mmtsedit directly. The filter then opens the same-named recording.mmts, verifies the recorded source size, and exposes a virtual timeline whose duration is the sum of the segment durations. A single segment behaves as a simple in/out trim; multiple segments are concatenated, with the demuxer jumping across the cut gaps (each segment starts at the nearest RAP). Create and edit these files with the mmts-edit-gui tool.

The earlier start-only .mmtsidx format has been removed; use .mmtsedit (a single segment is equivalent to the old start offset plus an end).

If a dantto4k-generated .mmtsmap exists next to the MMTS file (recording.mmtsmap), the filter also loads it during prescan. The map is used to supplement audio/subtitle track candidates and duration information, so tracks that appear after the first prescan window can still get output pins when playback starts. Seek/RAP points from the map are also used as preferred byte offsets before falling back to duration-ratio seeking.

MMTSMAP3 files generated by dantto4k v20260615 or later include audio audioMode and channel-count metadata. The filter uses this metadata to expose later-appearing 5.1ch and 22.2ch tracks with clearer pin names. When a stream changes format across MPT sections, the pin name shows the source timeline, for example Audio 1 AAC 2.0 -> LATM 22.2 -> AAC 5.1. A LATM 22.2ch input is decoded and downmixed to PCM 48000Hz 2.0; the pin name keeps the original source format visible while the DirectShow media type reports the actual PCM output format.

GitHub Release Package

The Build Release GitHub Actions workflow builds the Release configuration and creates a ZIP package containing:

mmts-dsfilter.ax
mmts-dsfilter.ini
Install_mmts-dsfilter_64.cmd
Uninstall_mmts-dsfilter_64.cmd
README.md
FFmpeg-NOTICE.txt
FFmpeg-LICENSE.txt

FFmpeg is linked into mmts-dsfilter.ax, so the package carries no FFmpeg DLLs, only its LGPL notice and license text.

The workflow can be run manually from GitHub Actions to create an artifact. It also publishes the ZIP as a GitHub Release asset when a v* tag is pushed, for example:

git tag v0.1.0
git push origin v0.1.0

The workflow checks out dantto4k from the same GitHub owner as this repository, so forks should also provide a sibling <owner>/dantto4k repository. The checkout is pinned to a dantto4k tag (ref: in .github/workflows/release.yml) because this project compiles dantto4k sources directly; bump it deliberately after verifying the build against the newer dantto4k release. Local builds are not pinned -- they use whatever is checked out in the sibling dantto4k directory.

Register

Administrator privileges are required.

From a release package, right-click each script and choose Run as administrator:

  • Install_mmts-dsfilter_64.cmd — registers the filter
  • Uninstall_mmts-dsfilter_64.cmd — unregisters the filter

The scripts call regsvr32 silently and then show a success/failure message. Do not delete mmts-dsfilter.ax, the bundled FFmpeg DLLs, or mmts-dsfilter.ini after installation. The installer does not copy files anywhere; the filter runs from the release-package folder.

For a development build, use an elevated command prompt:

regsvr32 msvc\x64\Release\mmts-dsfilter.ax

To unregister:

regsvr32 /u msvc\x64\Release\mmts-dsfilter.ax

Subtitle Settings

Place mmts-dsfilter.ini next to mmts-dsfilter.ax. The release package includes a sample INI with all options documented.

[MMTS]
; Font family used in generated ASS subtitles.
;FontName=MS Gothic

CaptionTransparency=0
BackgroundTransparency=50
ShowBackground=1
ShowRubyBackground=1
OutlineWidth=0
DelayMs=0
DebugLogPath=
VerboseLog=0
DumpSubtitleData=0
DumpSubtitleDir=
DumpSubtitleMaxFiles=200
Key Default Description
FontName MS Gothic Font family for ASS subtitles. Cell positions are fixed per character so changing the font does not shift later characters.
CaptionTransparency 0 Caption text transparency: 0 = opaque, 100 = fully transparent.
BackgroundTransparency 50 Cell background transparency: 0 = opaque, 100 = fully transparent.
ShowBackground 1 Show cell background rectangles (1) or hide them (0). Background height follows TTML lineHeight, then region extent, then font size. If TTML provides no background color a dark gray cell background is used.
ShowRubyBackground 1 Show cell background rectangles behind ruby (furigana) text.
OutlineWidth 0 Text outline width: 0 = none, 1–10 = ASS \bord value.
DelayMs 0 Subtitle display offset in milliseconds. Negative values show earlier.
DebugLogPath (empty) File path for debug logs. Empty disables file logging.
VerboseLog 0 Set to 1 to emit verbose debug logs in Release builds. Debug builds always emit verbose logs.
DumpSubtitleData 0 Set to 1 to save raw TTML samples and referenced subtitle resources for investigation.
DumpSubtitleDir (empty) Directory for subtitle dumps. Empty writes to subtitle_dump next to the filter.
DumpSubtitleMaxFiles 200 Maximum number of subtitle samples/resources to dump.

DRCS (Broadcaster-Defined Characters)

When a broadcaster transmits a custom glyph (DRCS), the filter registers the subtitle glyph resource and renders the glyph as an ASS vector drawing scaled to fit the subtitle cell. Set DumpSubtitleData=1 to capture raw TTML samples and subtitle resources for investigation.

Debug Logging

OutputDebugString output is quiet by default in Release builds. Set VerboseLog=1 in mmts-dsfilter.ini to emit lifecycle and verbose debug logs to DebugView or an attached debugger. Debug builds always emit these logs. File logging is disabled by default; set DebugLogPath to a log file path only for temporary investigations. With DebugLogPath set and VerboseLog=0, lifecycle logs are written to the file without also going to OutputDebugString.

Audio Notes

The splitter keeps one DirectShow audio output pin per MPT mp4a audio stream found during pre-scan so MPC-BE can switch tracks normally. Ordinary AAC streams are delivered only after LATM/LOAS to ADTS conversion succeeds; candidate pins are still created before that conversion is confirmed so late or fragmented initial audio samples do not hide the track. For 8K broadcasts, 22.2ch audio is carried as AAC LATM/LOAS rather than the ADTS form used for ordinary AAC tracks. These LATM streams are passed through without ADTS conversion and are advertised as:

majortype:  MEDIATYPE_Audio
subtype:    MEDIASUBTYPE_MPEG_LOAS {00001602-0000-0010-8000-00AA00389B71}
formattype: FORMAT_WaveFormatEx
wFormatTag: WAVE_FORMAT_MPEG_LOAS (0x1602)

Some downstream filters return failures on non-selected audio branches while another audio track is active. The splitter treats those failures as non-fatal and continues delivering samples to all connected audio pins.

Notes

Subtitle timing and document replacement

ARIB-TTML paragraph and span times are retained as separate display intervals. TMD 0010 uses the subtitle descriptor's NTP reference_start_time in the same absolute clock as the video MPU timestamps; zero begin remains a timed start. TMD 1111 ignores TTML time attributes and displays from arrival. Without a usable descriptor, arrival time is used as a compatibility fallback.

Before submitting ASS events, a second incremental file reader determines the end of the current document from its declared end, the next document, a clear packet, or a change/removal of the subtitle descriptor. This also clips events that were previously sent with long fixed durations and prevents an older glyph-waiting document from returning after a clear. Future display intervals are held until shortly before their start, rather than filling the output queue with all future characters at once.

The reader starts at the beginning to preserve MPT/fragment context, reads each byte only once, and caches timing records across seeks. It can therefore add read latency on the first subtitle or after a forward seek; an open-ended last caption may require scanning to EOF. Stop/seek cancels the scan. This path is for fixed recording files; its cached EOF is not a growing-file monitor.

Regression checks (VS v143 and the adjacent dantto4k checkout are required):

./build.ps1 -Configuration Release
./tools/run_subtitle_state_test.cmd
./tools/run_subtitle_file_index_test.ps1
./tools/run_subtitle_file_index_test.ps1 -Test subtitle_render_test

These cover element timing, binary MMT/TLV update/clear/descriptor sequences, and production ASS cell layout. They do not replace playback validation in the target subtitle renderer or certify all ARIB-TTML styling/monomedia features.

This project includes a copy of Microsoft DirectShow BaseClasses under baseclasses/; see baseclasses/LICENSE for that component.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages