Advanced configuration
⚙️ Advanced Configuration Guide
Advanced customisation and integration for power users.
Table of Contents
- Custom Loudness Targets
- Advanced Keybindings
- Integration with External Tools
- Network Stream Monitoring
- Multi-Instance Workflows
- Performance Optimisation
- Custom Timecode Formats
Custom Loudness Targets
Adding Organisation-Specific Standards
Edit scripts/audiomap.lua to add custom loudness targets:
local LOUDNESS_TARGETS = { ebu_r128 = -23, atsc = -24, podcast = -16,
-- Add custom targets itv_uk = -23, -- ITV UK standard bbc_r1 = -20, -- BBC Radio 1 netflix = -27, -- Netflix dialogue normalisation youtube = -14, -- YouTube normalisation spotify = -14, -- Spotify normalisation apple_music = -16, -- Apple Music tidal = -14, -- Tidal}Update the cycling order:
local function toggle_loudness() local targets = { "none", "ebu_r128", "atsc", "podcast", "netflix", -- Add to cycle "youtube", -- Add to cycle } -- [rest of function]endTrue Peak Limiting Adjustment
Modify true peak limiting for specific delivery requirements:
local function build_loudness_filter(target_lufs) if target_lufs == 0 then return nil end
-- Default: -1.0 dBTP (EBU R128 / ATSC) -- Netflix requires: -2.0 dBTP -- Adjust as needed: local true_peak = -1.0 -- Change to -2.0 for Netflix
local filter = string.format( "loudnorm=I=%.1f:TP=%.1f:LRA=11", target_lufs, true_peak ) return filterendAdvanced Keybindings
Macro Keybindings
Create complex workflows with macro keys. Add to input.conf:
# QC preset: Reset audio, enable EBU R128, activate qc-accurate profileF9 script-message-to audiomap reset; script-message-to audiomap toggle_loudness; apply-profile qc-accurate; show-text "QC Mode: Active"
# Quick export: Screenshot burst (10 frames at 1fps)F10 run "bash" "-c" "for i in {1..10}; do sleep 1; echo screenshot > /tmp/mpv-commands; done"
# Channel pair cycling for quick reviewCtrl+9 script-message-to audiomap pair 1; show-text "CH1+2"; add timer 2 "script-message-to audiomap pair 2; show-text 'CH3+4'"; add timer 4 "script-message-to audiomap pair 3; show-text 'CH5+6'"MIDI Controller Integration
Use MIDI controllers for tactile control (requires lua-midi):
-- Add to scripts/midi_control.lualocal midi = require 'midi'local mp = require 'mp'
-- Map MIDI CC to channel selectionmidi.cc(1, function(value) local pair = math.floor(value / 16) + 1 -- CC value 0-127 → pairs 1-8 mp.commandv("script-message-to", "audiomap", "pair", tostring(pair))end)
-- Map MIDI fader to volumemidi.cc(7, function(value) local volume = (value / 127) * 150 -- Map to 0-150% mp.set_property("volume", volume)end)External Control via IPC
Enable JSON IPC for external control:
Add to mpv.conf:
input-ipc-server=/tmp/mpvsocketControl from command line:
# Change to channel pair 3 (CH5+6)echo '{"command": ["script-message-to", "audiomap", "pair", "3"]}' | socat - /tmp/mpvsocket
# Query current timecodeecho '{"command": ["get_property", "time-pos"]}' | socat - /tmp/mpvsocketPython example:
import socketimport json
def send_command(command): sock = socket.socket(socket.AF_UNIX) sock.connect('/tmp/mpvsocket') sock.send((json.dumps({"command": command}) + '\n').encode()) response = sock.recv(4096) sock.close() return json.loads(response)
# Route to CH7+8send_command(["script-message-to", "audiomap", "pair", "4"])
# Get current timecodetc = send_command(["get_property", "time-pos"])print(f"Current position: {tc['data']:.3f} seconds")Integration with External Tools
FFmpeg Preprocessing Pipeline
Pre-process files before QC:
#!/bin/bash# qc-prep.sh - Prepare file for QC review
INPUT="$1"OUTPUT="${INPUT%.*}_qc.mov"
ffmpeg -i "$INPUT" \ -c:v prores_ks -profile:v 3 -qscale:v 5 \ -c:a pcm_s24le \ -vf "drawtext=text='%{pts\:hms}':x=10:y=10:fontsize=48:fontcolor=yellow" \ "$OUTPUT"
mpv --profile=qc-accurate "$OUTPUT"Avid Media Composer Integration
Export timecode data for Avid:
-- Add to scripts/export_tc.lualocal mp = require 'mp'local utils = require 'mp.utils'
mp.register_script_message("export_tc", function() local tc = mp.get_property("time-pos") local fps = mp.get_property_number("container-fps") local filename = mp.get_property("filename")
-- Format for Avid EDL local data = string.format("001 %s V C %s %s\n", filename, format_tc(tc, fps), format_tc(tc, fps))
local file = io.open("avid_export.edl", "a") file:write(data) file:close()
mp.osd_message("Timecode exported to avid_export.edl")end)
mp.add_key_binding("Ctrl+e", "export_tc", function() mp.commandv("script-message", "export_tc")end)DaVinci Resolve EDL Generation
# Create EDL from mpv A-B loops# Usage: Loop section with 'L', then run this command
echo "TITLE: QC Notes" > resolve_import.edlecho "FCM: NON-DROP FRAME" >> resolve_import.edl
# Get loop points from mpvmpv --msg-level=all=info file.mp4 | grep "ab-loop" >> resolve_import.edlNetwork Stream Monitoring
UDP Multicast Monitoring
Configure for live stream monitoring:
# Add to mpv.conf[stream-monitor]profile-desc="Live stream monitoring"cache=yescache-secs=1demuxer-readahead-secs=2video-sync=audioframedrop=vountimed=yesLaunch:
mpv --profile=stream-monitor udp://239.1.1.1:5000RTMP/SRT Stream Inspection
# SRT with statisticsmpv --profile=stream-monitor \ --script-opts=stats-bindlist=i \ srt://remote-host:5000?mode=caller
# RTMP with buffer analysismpv --profile=stream-monitor \ --demuxer-max-bytes=10M \ rtmp://streamserver/live/streamkeyMulti-View Monitoring
Monitor multiple streams simultaneously using mpv-grid:
#!/bin/bash# quad-monitor.sh - Four-stream monitoring
mpv --geometry=960x540+0+0 --profile=stream-monitor udp://239.1.1.1:5000 &mpv --geometry=960x540+960+0 --profile=stream-monitor udp://239.1.1.2:5000 &mpv --geometry=960x540+0+540 --profile=stream-monitor udp://239.1.1.3:5000 &mpv --geometry=960x540+960+540 --profile=stream-monitor udp://239.1.1.4:5000 &Multi-Instance Workflows
Comparison Viewing
View source and transcode side-by-side:
# Left: Sourcempv --geometry=960x1080+0+0 --title="Source" source.mov &
# Right: Transcodempv --geometry=960x1080+960+0 --title="Transcode" output.mp4 &
# Synchronise playbackmpv-sync left right # Requires external scriptReference Monitoring
Keep reference material open whilst working:
# Main QC windowmpv --profile=qc-accurate --title="QC Review" dailies.mp4 &
# Reference window (always on top)mpv --ontop --geometry=640x360+1280+0 --title="Reference" reference.mp4 &Performance Optimisation
GPU Shader Caching
Improve start-up performance:
# Add to mpv.confgpu-shader-cache-dir=~/.cache/mpv/shadersicc-cache-dir=~/.cache/mpv/icc
# Pre-compile shadersshader-cache-dir=/tmp/mpv-shader-cacheLarge File Optimisation
For very large files (>50GB):
[large-files]profile-desc="Optimisation for large files"demuxer-max-bytes=1Gdemuxer-readahead-secs=60cache-secs=30demuxer-max-back-bytes=500Mhr-seek-demuxer-offset=1Low-Latency Mode
Minimise monitoring latency:
[low-latency]profile-desc="Minimal latency monitoring"cache=novideo-sync=audiointerpolation=novd-lavc-threads=1audio-buffer=0.05untimed=yesCustom Timecode Formats
Feet+Frames Display
Add 35mm film-style feet+frames:
-- Add to timecode.lualocal function format_feet_frames(seconds, fps) local frames = math.floor(seconds * fps) -- 35mm 4-perf: 16 frames per foot local feet = math.floor(frames / 16) local ff = frames % 16 return string.format("%d+%02d", feet, ff)endAbsolute Frame Number
Display absolute frame count:
local function format_frame_number(seconds, fps) local frames = math.floor(seconds * fps + 0.5) return string.format("Frame: %d", frames)endTimecode with Subframes
For audio post-production (80 subframes per frame):
local function format_tc_subframes(seconds, fps) local total_subframes = math.floor(seconds * fps * 80 + 0.5) local frames = math.floor(total_subframes / 80) local subframes = total_subframes % 80
-- Format as HH:MM:SS:FF.SF local h = math.floor(frames / (fps * 3600)) local m = math.floor((frames % (fps * 3600)) / (fps * 60)) local s = math.floor((frames % (fps * 60)) / fps) local f = frames % fps
return string.format("%02d:%02d:%02d:%02d.%02d", h, m, s, f, subframes)endScripting Examples
Auto-Screenshot on Scene Change
-- scripts/auto_screenshot.lualocal mp = require 'mp'
local last_pts = 0local threshold = 0.3 -- Scene change threshold
mp.observe_property("vf-metadata", "native", function(_, metadata) if not metadata then return end
local scene_score = metadata["lavfi.scene"] if scene_score and tonumber(scene_score) > threshold then local pts = mp.get_property_number("time-pos") if pts - last_pts > 1.0 then -- Minimum 1 second between shots mp.commandv("screenshot", "video") mp.osd_message("Scene change detected - screenshot captured") last_pts = pts end endend)Automated QC Report Generation
-- scripts/qc_report.lualocal mp = require 'mp'local utils = require 'mp.utils'
local report = {}
mp.register_script_message("qc_mark", function(severity, note) local tc = mp.get_property("time-pos") local fps = mp.get_property_number("container-fps")
table.insert(report, { timecode = format_timecode(tc, fps), severity = severity, note = note })
mp.osd_message(string.format("QC Mark: %s", note))end)
mp.register_script_message("qc_export", function() local filename = mp.get_property("filename") local output = io.open(filename .. "_qc_report.txt", "w")
output:write(string.format("QC Report: %s\n", filename)) output:write(string.format("Generated: %s\n\n", os.date()))
for _, mark in ipairs(report) do output:write(string.format("[%s] %s: %s\n", mark.timecode, mark.severity, mark.note)) end
output:close() mp.osd_message("QC report exported")end)
-- Keybindingsmp.add_key_binding("Ctrl+m", "qc_mark_minor", function() mp.commandv("script-message", "qc_mark", "MINOR", "Issue noted")end)
mp.add_key_binding("Ctrl+Shift+m", "qc_mark_major", function() mp.commandv("script-message", "qc_mark", "MAJOR", "Significant issue")end)
mp.add_key_binding("Ctrl+r", "qc_export_report", function() mp.commandv("script-message", "qc_export")end)For more examples and community scripts, see the mpv user scripts repository.