Version: 0.1.7 (build 8)
macOS host app plus Quick Look Preview Extension scaffold for:
mkvwebmogg/ogvopus(audio-only)
MKVQuickLook provides a Finder Quick Look preview for these formats on macOS by shipping a small host app with a bundled Quick Look Preview Extension and embedded VLCKit playback backend.
This app is currently distributed as an ad hoc signed DMG build and is not notarized.
- macOS Gatekeeper may warn when users open it for the first time.
- That is expected for the current release process.
- Users may need to open it via Finder context menu or allow it in System Settings after the first launch attempt.
- host app with main window, settings/help UI, and playback lab
- Quick Look Preview Extension registered for owned target file types
VLCKit 3.7.2fetched intoVendor/by a bootstrap script- direct VLCKit-backed playback path for supported files
- audio-only
.opusfiles use the same VLCKit backend with an audio preview UI - Finder registration and Launch Services ownership wired into the app bundle metadata
- full Quick Look preview autoplays; compact Finder preview remains summary-only
- renderer, metadata, and UI regressions covered by automated tests
- hover overlay play/pause on the video frame (like native Quick Look)
- seek and volume sliders jump immediately to click position
There is a short but perceptible delay for both seek and volume changes. These are not software bugs. They are inherent to how digital audio and compressed video work.
When you move the volume slider, the API call sets the new level instantly. The delay you hear is the audio output pipeline latency.
VLCKit renders decoded audio into a ring buffer. That buffer feeds Core Audio, which feeds the hardware DAC. The buffer exists to absorb CPU timing jitter — without it, even a brief stall would cause audible crackling, pops, or dropout. On macOS the buffer depth is typically 80–200 ms. Volume changes apply only to audio that has not yet entered the buffer. The audio already queued plays at the old level and drains through first. You cannot eliminate this without destroying the buffer, which causes immediate playback glitches. Even macOS's own system volume control has a milder version of this effect for the same reason.
Video frames use inter-frame compression. P-frames and B-frames reference earlier frames and cannot be decoded by themselves. Only keyframes (I-frames) are self-contained. Seeking to any arbitrary position requires:
- Find the nearest keyframe before the target position in the file
- Decode every frame from that keyframe forward to the target frame
A typical H.264 or HEVC stream has a keyframe every 2–10 seconds. At 30 fps that can mean decoding up to 150–300 frames before the target frame can be displayed. For HD content this takes 100–500 ms even with hardware-assisted decoding. Native Apple players appear faster because AVFoundation has direct access to the Apple Silicon hardware video decoder with tighter integration; VLCKit's pipeline operates at a higher abstraction level.
The UI remains responsive during seeks — the slider position updates immediately. The video frame catches up as the decoder finishes.
VLCKit is bundled automatically as part of this app.
- It does not need to be pre-installed on the Mac.
- The app does not download it at runtime.
- The framework is downloaded for development by
./scripts/bootstrap-vlckit.shintoVendor/VLCKit.xcframework. - During the build, Xcode embeds
VLCKit.frameworkinto the Quick Look extension bundle at:MKVQuickLook.app/Contents/PlugIns/MKVQuickLookPreviewExtension.appex/Contents/Frameworks/
This means the shipped app is self-contained with respect to the playback backend.
- macOS 14 or later
- Xcode 16.2 or later recommended
xcodegenif you want to regenerate the project fromproject.yml
project.yml: XcodeGen specMKVQuickLook.xcodeproj: generated Xcode projectCHANGELOG.md: versioned change historyscripts/bootstrap-vlckit.sh: downloads the pinned official VideoLAN binary packageVendor/: local dependency install location created by the bootstrap script
Repository policy:
- release artifacts in
dist/are local build outputs and must not be committed - large local sample media belongs in
example-videos/and must not be committed VLCKitis fetched on demand rather than committed into Git history- vendored debug symbols are intentionally not kept because they add hundreds of megabytes and are not required to build or run the app
Fetch the pinned VLCKit package first:
./scripts/bootstrap-vlckit.shIf you just cloned the repo and Vendor/ looks empty or incomplete, that is expected. The binary dependency is no longer committed to Git history and must be populated locally by the bootstrap script.
Generate the project if needed:
xcodegen generateBuild from the command line without signing:
xcodebuild \
-project MKVQuickLook.xcodeproj \
-scheme MKVQuickLook \
-configuration Debug \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO \
buildBuild, copy to ~/Applications, refresh Quick Look, and open the app once:
./scripts/install-local.shThat script also applies ad hoc bundle signatures to the framework, extension, and app bundle, because plain CODE_SIGNING_ALLOWED=NO debug builds are not enough for reliable Quick Look extension registration.
install-local.sh automatically bootstraps VLCKit first if it is missing.
Refresh Quick Look manually without reinstalling:
./scripts/reset-quicklook.shCreate a release-style DMG in dist/:
./scripts/build-release-dmg.shWhat this script does:
- bootstraps
VLCKitif needed - builds the app in
Release - applies ad hoc signatures to the framework, extension, and app bundle
- stages the app with an
Applicationssymlink - creates
dist/MKVQuickLook-v<version>.dmg
This is suitable for GitHub Releases. It is not notarized.
dist/ is ignored by Git. Build the DMG locally, then upload it to a GitHub Release instead of committing it into the repository history.
The downloadable DMG should be published as a GitHub Release asset, not as a tracked repo file.
- Open the GitHub repository.
- Click
Releases. - Click
Draft a new release. - Create or select a tag such as
v0.1.7. - Set the release title, for example
v0.1.7. - Upload the DMG from
dist/. - Publish the release.
If gh is installed:
git tag v0.1.7
git push origin v0.1.7
gh release create v0.1.7 dist/MKVQuickLook-v0.1.7.dmg \
--title "v0.1.7" \
--notes-file CHANGELOG.mdThat gives users a normal release download page while keeping the Git repository smaller and cleaner.
This repository now includes a GitHub Actions workflow at .github/workflows/release.yml.
Behavior:
- pushing a tag matching
v*triggers the workflow - the workflow bootstraps
VLCKit - the workflow runs the non-GUI test suite
- it builds the DMG with
./scripts/build-release-dmg.sh - it creates or updates the GitHub Release for that tag
- it uploads the generated DMG as a release asset
Typical release flow:
git push origin main
git tag v0.1.7
git push origin v0.1.7That is the correct trigger model for this project. Releasing on every plain git push would be the wrong design.
Important:
- the DMG asset does not appear instantly on the Release page
- GitHub first shows the automatic source archives
- the DMG appears only after the GitHub Actions
Releaseworkflow finishes successfully - if the DMG is missing, check the
Actionstab first
- Build the app in Xcode or with
xcodebuild. - Install it with
./scripts/install-local.shor copy it to an Applications folder. - Launch the app once.
- In Finder, select a supported file and press Space.
Notes about the current samples:
example-videos/*.mkvresolves toorg.matroska.mkvexample-videos/*.webmresolves toorg.webmproject.webm- the current
example-videos/big_buck_bunny_240p.oggsample resolves to audio on this system, not Theora video
So if you want to validate the original Ogg/Theora requirement specifically, add a real .ogv or Theora-in-Ogg sample.
Large sample media files must not be committed to this repository.
For local development, create a folder named example-videos/ at the repo root and place your test files there. That folder is intentionally ignored by Git so each developer can keep local fixtures without bloating the repository history.
Suggested local contents:
- one or more
.mkvfiles, including at least one problematic real-world sample - one
.webmfile - one
.opusaudio-only sample - one
.ogvor Theora-in-Ogg sample if Ogg video support is being tested
Keep those files local only. If reproducible media fixtures are needed for automated tests, prefer tiny purpose-built samples or generated fixtures instead of large real-world files.
After a fresh clone, run:
./scripts/bootstrap-vlckit.shThis downloads the pinned official VLCKit 3.7.2 binary package from VideoLAN, verifies its checksum, and installs only the files this repo needs into Vendor/.
The install and release scripts also call this automatically, but it is still the correct first setup step for developers.
In other words:
- clone the repo
- run
./scripts/bootstrap-vlckit.sh - build, test, install, or create a DMG
Do not treat an empty Vendor/ after clone as a broken repo state. That is now the intended setup.
What is intentionally not included:
- large sample media in
example-videos/ - generated release artifacts in
dist/ - committed
VLCKitruntime binaries - vendored
VLCKitdebug symbols
If the dependency strategy changes later, the README must be updated so the required bootstrap step is impossible to miss.
If Finder does not refresh the extension state:
qlmanage -r
qlmanage -r cacheYou may also need to relaunch Finder.
The app now emits timestamped playback control logs through the unified logging system with:
- subsystem:
com.robertwildling.MKVQuickLook - category:
Playback
To watch live control latency while using Finder Quick Look or the in-app Playback Lab:
log stream --style compact \
--predicate 'subsystem == "com.robertwildling.MKVQuickLook" AND category == "Playback"'To inspect recent history:
log show --style compact --last 5m \
--predicate 'subsystem == "com.robertwildling.MKVQuickLook" AND category == "Playback"'What gets logged:
- seek slider UI begin/change/end
- controller handoff
- player seek request / coalesced apply / final apply
- first VLC time-change callback after seek
- volume slider UI change
- controller handoff
- player volume apply
- player state transitions
For volume, compare:
[volume] ui-change[volume] controller-change[volume] apply[volume] metrics
For seeking, compare:
[seek] ui-begin/[seek] ui-change/[seek] ui-end[seek] controller-begin/[seek] controller-change/[seek] controller-end[seek] request[seek] apply-coalescedor[seek] apply-final[seek] time-changed
If lag remains after apply, the remaining delay is downstream in VLCKit / decode / output buffering rather than in the Swift control path.
Validate current control behavior in Finder on macOS 14 and 15.
