A desktop and CLI tool for extracting tracks, chapters, attachments, tags, cue sheets, and timestamps from MKV and MP4 files.
StreamExtract provides a Windows desktop GUI (WinForms), a cross-platform
Avalonia desktop app, and a command-line interface built with .NET 10. It wraps
MKVToolNix (mkvmerge, mkvextract) and
GPAC (mp4box) to extract individual streams and
metadata from media containers. The application starts the bundled native tools
directly without shell interpretation; the Linux archive uses local launchers to
set each tool's bundled library path.
- Extract audio, video, and subtitle tracks from MKV/MKA and MP4/M4V/M4A/M4B files
- Extract chapters from MKV and MP4 containers, and attachments, tags, cue sheets, timestamp files, and per-track cue sheets from MKV containers
- Drag-and-drop multiple files or pick them with the file dialog
- Per-file track and feature selection with a checkbox tree
- Sequential, progress-tracked extraction
- Output paths are validated and contained — untrusted attachment names cannot escape the output directory
- Startup validation that fails closed when a bundled tool is missing or does not match its expected SHA-256 hash
- Checks for updates on startup and shows a button linking to the download page when a newer version is available
- .NET 10 SDK to build from source
- Windows (for the WinForms GUI; it targets
net10.0-windows) - Linux (
linux-x64for the CLI and desktop app; requires no separate .NET or native-tool installation)
The native tools are bundled (Windows tools are in tools/, Linux tools are in
the release archive) and required at runtime:
mkvextract/mkvmerge— from MKVToolNixMP4Box— from GPAC
No installer is provided. To run from source:
git clone https://github.com/CodeByVibes/stream-extract.git
cd stream-extract
dotnet run --project stream-extract-winforms.csprojTo build a release:
dotnet build -c ReleaseThe build copies tools/ and licenses/ into the output directory. Launch the
app from there, or run the produced stream-extract-winforms.exe.
Download StreamExtract-x86_64.AppImage, make it executable, and launch it:
chmod +x StreamExtract-x86_64.AppImage
./StreamExtract-x86_64.AppImageThe AppImage is self-contained and includes the desktop application, bundled
native tools, and their runtime libraries. To build it yourself, see
Linux Packaging & Smoke Tests; the result is
written to dist/StreamExtract-x86_64.AppImage.
Download the linux-x64 release archive
(streamextract-linux-x64.tar.gz) and extract it:
tar -xzf streamextract-linux-x64.tar.gz
cd streamextractThe Linux release archive is self-contained for the supported linux-x64
environment. It includes the CLI, verified mkvmerge, mkvextract, and
MP4Box Linux binaries, and the MKVToolNix library closure those two tools
load. You do not need to install .NET or the native tools separately; the host
still supplies the Linux kernel and dynamic loader.
The release packaging process securely downloads pinned native tool versions,
verifies their SHA-256 checksums, and bundles them.
Packaging builds a fully static MP4Box from the pinned GPAC source release, so
the bundled binary has no dynamic library dependencies of its own. Only the
MKVToolNix CLI library closure is bundled (tools/mkvtoolnix-runtime/); the
MKVToolNix GUI and its Qt plugins are not. There is no tools/lib/ directory.
The archive still depends on the host Linux kernel and dynamic loader;
portability is limited by the loader and kernel ABI supported by the build
environment.
tools-manifest.json records and validates every bundled file — including each
MKVToolNix runtime closure file — before the CLI or desktop app processes media.
The Linux archive includes the self-contained Avalonia desktop app under
desktop/. Launch desktop/StreamExtract.Desktop; its tools/ directory is
copied beside it so importing media works without relying on the current
working directory or system-installed MKVToolNix. Releases contain both this
portable archive and the one-file AppImage.
Note
If you delete or relocate a bundled tool — or replace it with one whose
SHA-256 hash does not match — the app refuses to start and reports the
problem. Keep the tools/ folder next to the executable and leave the
bundled executables untouched.
- Add files — drag media files onto the tree, or click Open files.
- Select output folder — choose a folder, or keep Use source checked to write next to the first imported file.
- Select what to extract — check the tracks and features you want under each file. Checking a file node checks all of its children.
- Extract — click Extract. Progress is shown on the progress bar and in the log pane.
The tree is a working set: select a top-level file entry and press Delete to remove it from the list. Only the list entry is removed — the media file on disk is never touched.
The Avalonia desktop app follows the same workflow as the Windows GUI:
- Open files — drag media files onto the file tree, or click Open files.
- Select output folder — clear Use source directory and pick a folder, or keep it checked to write next to the first imported file.
- Select what to extract — check the tracks and features you want under each file.
- Extract — click Extract; Cancel stops an in-progress run.
The file tree is a working set: select a top-level file entry and press Delete to remove it from the list. Only the list entry is removed — the media file on disk is never touched.
The CLI provides full feature parity with the GUI:
# Print media information; the listed IDs are the values --tracks accepts
./streamextract info movie.mkv
# Extract specific tracks and chapters
./streamextract extract movie.mkv --tracks 1,2 --chapters --output ./out
# Extract every feature from MKV files (see --all below for the MP4 caveat)
./streamextract extract movie1.mkv movie2.mkv --all --output ./out
# Extract cue sheets, per-track cue sheets, and timestamps
./streamextract extract concert.mkv --cue-sheets \
--cues-for-selected-tracks --timestamps --output ./out--tracks <ids>— Comma-separated track IDs to extract, as reported byinfo.--chapters— Extract chapters.--attachments— Extract attachments (MKV only).--tags— Extract tags (MKV only).--cue-sheets— Extract the container-level cue sheet (MKV only).--cues-for-selected-tracks— Write one cue sheet per selected track (MKV only). Selects every track when--tracksis omitted.--timestamps— Write a timestamp file per selected track (MKV only). Selects every track when--tracksis omitted.--all— Extract every feature above. Cannot be combined with another extraction option, and fails with a usage error on containers that do not support all of them (MP4 supports tracks and chapters only).--output <dir>— Write output todir, creating it when missing. Defaults to the current directory.--verbose— Print per-file progress even when stdout is redirected.--help,-h— Print usage.--version— Print the CLI version.
Unknown track IDs, unsupported extraction modes, and malformed input are
reported as usage errors (exit code 2).
| Code | Meaning | Description |
|---|---|---|
0 |
Success | Command completed successfully. |
1 |
Extraction Error | Native tool failure or extraction error occurred. |
2 |
Usage Error | Invalid syntax, missing input file, or invalid track ID. |
130 |
Cancelled | Process was interrupted/cancelled (e.g. via Ctrl+C). |
Supported extraction options per file type:
| Option | MKV/MKA | MP4/M4V/M4A/M4B |
|---|---|---|
| Tracks (audio/video/subtitles) | yes | yes |
| Chapters | yes | yes |
| Attachments | yes | no |
| Tags | yes | no |
| Cue sheets | yes | no |
| Cues for selected tracks | yes | no |
| Timestamps | yes | no |
Output naming follows the source file name: movie.mkv yields
movie_Track1.h264 for a video track, movie_chapters.xml for chapters,
movie_tags.xml for tags, movie_cuesheet.cue for the container cue sheet,
movie_Track1_timestamps.txt for track timestamps, and
movie_Track1_cues.cue for per-track cue sheets. The number in _Track<n> is
the track ID plus one (Track1 is track ID 0), matching the track labels the
GUIs show. Attachments keep their original names.
dotnet buildThe build is warning-free.
dotnet test runs the whole solution, which includes the Windows-only WinForms
test project and therefore requires Windows. On Linux, run the portable test
project directly:
dotnet test tests/StreamExtract.Tests/StreamExtract.Tests.csprojThe portable tests are headless xUnit tests targeting the pure helpers — path
containment, request building, cue sheet generation, progress math, plugin
command builders, CLI parsing and exit codes, terminal formatting, file drop
parsing, native tool resolution and integrity verification, update parsing, and
process failure/cancellation contracts. The bundled native tools are never
invoked during tests; the process contracts are exercised against the
cross-platform .NET TestProcessHost fixture. The checkbox-tree selection
snapshot (BuildFileSelection) is covered by the Windows-only
StreamExtract.WinForms.Tests project.
To build the Linux AppImage and portable release archive and run the smoke tests locally, run these from the repository root:
# Publish the CLI and desktop app with pinned native tools, then package the
# portable archive and AppImage into dist/
build/linux/fetch-native-tools.sh
# Generate test fixtures (sample.mp4, sample.mkv, sample-rich.mkv) and run the
# bundle smoke tests
build/linux/generate-fixtures.sh
SMOKE_FIXTURES="dist/fixtures/sample.mp4
dist/fixtures/sample.mkv
dist/fixtures/sample-rich.mkv" build/linux/smoke-test.shAll output lands in the git-ignored dist/ directory:
| Path | Contents |
|---|---|
dist/publish/streamextract/ |
Staged CLI and desktop/ bundle |
dist/streamextract-linux-x64.tar.gz |
Portable release archive |
dist/StreamExtract-x86_64.AppImage |
One-file desktop AppImage |
dist/fixtures/ |
Generated smoke-test media |
Set DIST_DIR to relocate everything at once, or PUBLISH_ROOT,
ARCHIVE_PATH, APPIMAGE_PATH, and FIXTURE_DIR to relocate individual
paths.
fetch-native-tools.sh downloads the pinned MKVToolNix AppImage and GPAC source
release, verifies their SHA-256 checksums, builds a static MP4Box, and stages
the bundle. The smoke test requires at least one MKV and one MP4 fixture;
sample-rich.mkv carries an attachment and chapters, so include it to exercise
the attachment, chapter, and tag modes. Set SMOKE_FIXTURES to a
newline-delimited list of fixtures, or SMOKE_MKV_FIXTURE / SMOKE_MP4_FIXTURE
for a single file each.
build/windows/publish.ps1 publishes the Avalonia desktop app for Windows and
stages the native tool bundle next to it. It runs on Windows with PowerShell 7
and, for cross-publishing, on Linux or macOS with pwsh:
# Self-contained folder bundle -> dist/publish-win-x64/StreamExtract
pwsh -File build/windows/publish.ps1
# One executable, with everything but tools/ and licenses/ bundled inside
pwsh -File build/windows/publish.ps1 -SingleFileThe Avalonia project does not copy the native tools itself, so the script stages
tools-manifest.json, tools/, and licenses/ next to the executable. The app
verifies those tools against the manifest at startup; without them it launches,
but every extraction fails. The tools are not downloaded — the ones committed in
tools/ are verified against the SHA-256 hashes in tools-manifest.json first,
so a modified binary fails the build.
| Path | Contents |
|---|---|
dist/publish-win-x64/StreamExtract/ |
Self-contained folder bundle |
dist/StreamExtract-win-x64.zip |
Archive of the folder bundle |
dist/publish-win-x64-singlefile/StreamExtract/ |
Single-executable bundle |
dist/StreamExtract-win-x64-singlefile.zip |
Archive of the single-executable bundle |
The single-file flavor folds the runtime, the managed assemblies, and the native
libraries into one .exe and drops the symbol files that come with the native
packages. It extracts those native libraries into a cache directory on first
launch (%TEMP%\.net\..., overridable with DOTNET_BUNDLE_EXTRACT_BASE_DIR), so
its target needs a writable temporary directory; the folder bundle extracts
nothing. Both flavors default to a self-contained win-x64 release; pass
-Configuration, -RuntimeIdentifier, -OutputDirectory, -FrameworkDependent
or -SkipArchive to change that, and Get-Help build/windows/publish.ps1 -Full
for the details.
The codebase is organized into four production projects, with the tests in
tests/:
StreamExtract.Core(net10.0) — Platform-neutral core containing media container models, extractor plugins (MkvExtractorPlugin,Mp4ExtractorPlugin), process runners (ProcessRunnerwith process-tree cancellation), output path validation (OutputPathGuard), native tool resolution & integrity verification (NativeToolValidator,NativeToolResolver), request builders (ExtractionRequestBuilder), and update checking.StreamExtract.Cli(net10.0) — Standalone, self-contained cross-platform command-line interface handling argument parsing, terminal formatting, real-time extraction progress reporting, signal cancellation (SIGINT), and standardized exit codes.stream-extract-winforms(net10.0-windows) — Windows desktop WinForms GUI application for visual file selection, checkbox tree configuration, and progress logging.StreamExtract.Desktop(net10.0) — Cross-platform Avalonia desktop GUI (MVVM via CommunityToolkit.Mvvm) that sharesStreamExtract.Corewith the other front ends. Packaged for Linux as the AppImage and the archive'sdesktop/bundle; also configured forwin-x64.
Key design decisions:
- A non-zero exit code from a native tool is captured as a per-mode failure in the
returned
ExtractOutcome; the UI logs each failure and keeps going with the remaining modes instead of a false "Done". Cancellation still aborts everything. - Attachment output paths pass through
OutputPathGuard.ResolveContainedPath, so a malicious file name inside an MKV cannot escape the selected output directory. - One
mkvextractinvocation per extraction mode keeps failures attributable and matches the tool's command syntax. The exception is per-track cue sheets, which are generated from the extracted chapter XML instead of callingmkvextract's cuesheet mode (that mode has no per-track option). - At startup the app verifies the SHA-256 hash of every bundled tool and refuses to start if one is missing, modified, or reached through a symlink or reparse-point path beneath the application directory. Bundled artifact files are checked the same way.
This project uses Conventional Commits
for commit messages. Keep the build warning-free and run the tests before
submitting a pull request: dotnet test on Windows, or
dotnet test tests/StreamExtract.Tests/StreamExtract.Tests.csproj on Linux.
This project is licensed under the MIT License. The bundled native
tools are the property of their respective authors and are distributed under
their original licenses — see licenses/GPAC-LICENSE.txt and
licenses/MKVToolNix-LICENCE.txt.