1
0
mirror of https://github.com/mpv-player/mpv synced 2025-04-23 15:47:54 +00:00
mpv/TOOLS/python/autodeint.py
8lurry fea5db26bb
python: optimize source, add feature
- lint
- better error message through check_error when mpv api(s) return with an error
- add function hook_add
- add function hook_continue
- add event handler for MPV_EVENT_HOOK
2025-04-09 13:43:16 +06:00

152 lines
5.0 KiB
Python

# This script uses the lavfi idet filter to automatically insert the
# appropriate deinterlacing filter based on a short section of the
# currently playing video.
#
# It registers the key-binding ctrl+d, which when pressed, inserts the filters
# ``vf=idet,lavfi-pullup,idet``. After 4 seconds, it removes these
# filters and decides whether the content is progressive, interlaced, or
# telecined and the interlacing field dominance.
#
# Based on this information, it may set mpv's ``deinterlace`` property (which
# usually inserts the bwdif filter), or insert the ``pullup`` filter if the
# content is telecined. It also sets field dominance with lavfi setfield.
#
# OPTIONS:
# The default detection time may be overridden by adding
#
# --script-opts=autodeint.detect_seconds=<number of seconds>
#
# to mpv's arguments. This may be desirable to allow idet more
# time to collect data.
#
# To see counts of the various types of frames for each detection phase,
# the verbosity can be increased with
#
# --msg-level=autodeint=v
from mpvclient import mpv # type: ignore
script_name = mpv.name
detect_label = f"{script_name}-detect"
pullup_label = script_name
dominance_label = f"{script_name}-dominance"
ivtc_detect_label = f"{script_name}-ivtc-detect"
progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3
# number of seconds to gather cropdetect data
try:
detect_seconds = float(mpv.get_opt(f"{script_name}.detect_seconds", 4))
except ValueError:
detect_seconds = 4
def del_filter_if_present(label):
# necessary because mp.command('vf del @label:filter') raises an
# error if the filter doesn't exist
vfs = mpv.get_property_node("vf")
for i, vf in enumerate(vfs):
if vf["label"] == label:
vfs.pop(i)
mpv.set_property_node("vf", vfs)
return True
return False
def add_vf(label, filter): # noqa: A002
return mpv.command_string(f"vf add @{label}:{filter}")
def stop_detect():
del_filter_if_present(detect_label)
del_filter_if_present(ivtc_detect_label)
def judge(label):
# get the metadata
result = mpv.get_property_node(f"vf-metadata/{label}")
num_tff = float(result["lavfi.idet.multiple.tff"])
num_bff = float(result["lavfi.idet.multiple.bff"])
num_progressive = float(result["lavfi.idet.multiple.progressive"])
num_undetermined = float(result["lavfi.idet.multiple.undetermined"])
num_interlaced = num_tff + num_bff
num_determined = num_interlaced + num_progressive
mpv.info(label + " progressive = " + str(num_progressive))
mpv.info(label + " interlaced-tff = " + str(num_tff))
mpv.info(label + " interlaced-bff = " + str(num_bff))
mpv.info(label + " undetermined = " + str(num_undetermined))
if num_determined < num_undetermined:
mpv.warn("majority undetermined frames")
if num_progressive > 20*num_interlaced:
return progressive
elif num_tff > 10*num_bff:
return interlaced_tff
elif num_bff > 10*num_tff:
return interlaced_bff
else:
return interlaced
def select_filter():
# handle the first detection filter results
verdict = judge(detect_label)
ivtc_verdict = judge(ivtc_detect_label)
dominance = "auto"
if verdict == progressive:
mpv.info("progressive: doing nothing")
stop_detect()
del_filter_if_present(dominance_label)
del_filter_if_present(pullup_label)
return
else:
if verdict == interlaced_tff:
dominance = "tff"
add_vf(dominance_label, "setfield=mode=" + dominance)
elif verdict == interlaced_bff:
dominance = "bff"
add_vf(dominance_label, "setfield=mode=" + dominance)
else:
del_filter_if_present(dominance_label)
# handle the ivtc detection filter results
if ivtc_verdict == progressive:
mpv.info(f"telecined with {dominance} field dominance: using pullup")
stop_detect()
else:
mpv.info("interlaced with " + dominance +
" field dominance: setting deinterlace property")
del_filter_if_present(pullup_label)
mpv.set_property_string("deinterlace","yes")
stop_detect()
@mpv.add_binding("ctrl+d")
def start_detect():
# exit if detection is already in progress
if "select_filter" in mpv.timers:
mpv.warn("already detecting!")
return
mpv.set_property_string("deinterlace", "no")
del_filter_if_present(pullup_label)
del_filter_if_present(dominance_label)
# insert the detection filters
if not (add_vf(detect_label, "idet") and
add_vf(dominance_label, "setfield=mode=auto") and
add_vf(pullup_label, "lavfi-pullup") and
add_vf(ivtc_detect_label, "idet")):
mpv.error("failed to insert detection filters")
return
def wrap_select_filter():
select_filter()
mpv.clear_timer("select_filter")
# wait to gather data
mpv.add_timeout(detect_seconds, wrap_select_filter, name="select_filter")