← Home

Export video clips with mpv

2024-07-22

mpv script to export the current A-B loop as a video clip using ffmpeg. Uses hardware acceleration (VAAPI) and stream copy for fast exports.

Add to ~/.config/mpv/input.conf:

Ctrl+2 script-message export-loop-clip

Save as ~/.config/mpv/scripts/export_clip.lua:

utils = require "mp.utils"

-- find available filename
function slow_start()
  local basename = mp.get_property("filename"):gsub("%.([^%.]+)$", "")
  local screenshot_folder = mp.get_property("screenshot-directory") or ""
  function filename(idx)
    return utils.join_path(
      screenshot_folder,
      basename .. '-' .. string.format("%04d",idx) .. '.mp4'
    )
  end
  -- multiply
  local inc = 1
  while utils.file_info(filename(inc)) do
    inc = inc * 2
  end
  -- narrowing
  local lower_bound = math.floor(inc / 2)
  while inc - lower_bound > 1 do
    local mid = math.floor((inc + lower_bound) / 2)
    local is_file = utils.file_info(filename(mid))
    if is_file then
      lower_bound = mid
    else
      inc = mid
    end
  end
  return filename(inc)
end

function export_loop_clip()
  local a = mp.get_property_number("ab-loop-a")
  local b = mp.get_property_number("ab-loop-b")
  local path = mp.get_property("path")
  if a and b then
    local outfile = slow_start()
    local cmd = {
      'ffmpeg',
      '-hwaccel', 'vaapi',
      '-hwaccel_output_format','vaapi',
      '-ss', tostring(a),
      '-i', path,
      '-t', tostring(b-a),
      '-c:v','copy',
      '-c:a','copy',
      '-v', 'error',
      outfile
    }
    local args = { args = cmd }
    local result = utils.subprocess(args)
    if result["status"] == 0 then
      mp.msg.info('save clip ' .. outfile)
      mp.osd_message('save clip ' .. outfile)
    else
      mp.msg.info(result["error_string"] .. result["status"])
      mp.osd_message("export loop clip error")
    end
  end
end
mp.register_script_message("export-loop-clip", export_loop_clip)

function set_ab_loop_a()
  local pos = mp.get_property_number("time-pos")
  mp.set_property_number("ab-loop-a", pos)
  mp.osd_message('set A-B loop A: ' .. tostring(pos))
end
mp.register_script_message("set-ab-loop-a", set_ab_loop_a)

function set_ab_loop_b()
  local pos = mp.get_property_number("time-pos")
  mp.set_property_number("ab-loop-b", pos)
  mp.osd_message('set A-B loop B: ' .. tostring(pos))
end
mp.register_script_message("set-ab-loop-b", set_ab_loop_b)

function seek_ab_loop_a()
  local pos = mp.get_property_number("ab-loop-a")
  if pos then
    mp.set_property_number("time-pos", pos)
  end
end
mp.register_script_message("seek-ab-loop-a", seek_ab_loop_a)

function seek_ab_loop_b()
  local pos = mp.get_property_number("ab-loop-b")
  if pos then
    mp.set_property_number("time-pos", pos)
  end
end
mp.register_script_message("seek-ab-loop-b", seek_ab_loop_b)

Usage: set A-B loop points with l key, then Ctrl+2 to export. Clips are saved to your screenshot directory.