summaryrefslogtreecommitdiffstats
path: root/TOOLS/lua/autocrop.lua
diff options
context:
space:
mode:
Diffstat (limited to 'TOOLS/lua/autocrop.lua')
-rw-r--r--TOOLS/lua/autocrop.lua283
1 files changed, 128 insertions, 155 deletions
diff --git a/TOOLS/lua/autocrop.lua b/TOOLS/lua/autocrop.lua
index af6021b75a..ba38f487cc 100644
--- a/TOOLS/lua/autocrop.lua
+++ b/TOOLS/lua/autocrop.lua
@@ -1,99 +1,71 @@
--[[
-This script uses the lavfi cropdetect filter to automatically
-insert a crop filter with appropriate parameters for the
-currently playing video.
-
-It will automatically crop the video, when playback starts.
-
-Also It registers the key-binding "C" (shift+c). You can manually
-crop the video by pressing the "C" (shift+c) key.
-
-If the "C" key is pressed again, the crop filter is removed
-restoring playback to its original state.
-
-The workflow is as follows: First, it inserts the filter
-vf=lavfi=cropdetect. After <detect_seconds> (default is 1)
-seconds, it then inserts the filter vf=crop=w:h:x:y, where
-w,h,x,y are determined from the vf-metadata gathered by
-cropdetect. The cropdetect filter is removed immediately after
-the crop filter is inserted as it is no longer needed.
-
-Since the crop parameters are determined from the 1 second of
-video between inserting the cropdetect and crop filters, the "C"
-key should be pressed at a position in the video where the crop
-region is unambiguous (i.e., not a black frame, black background
-title card, or dark scene).
-
-The default options can be overridden by adding
-script-opts-append=autocrop-<parameter>=<value> into mpv.conf
-
-List of available parameters (For default values, see <options>):
-
-auto: bool - Whether to automatically apply crop at the start of
- playback. If you don't want to crop automatically, set it to
- false or add "script-opts-append=autocrop-auto=no" into
- mpv.conf.
-
-auto_delay: seconds - Delay before starting crop in auto mode.
- You can try to increase this value to avoid dark scene or
- fade in at beginning. Automatic cropping will not occur if
- the value is larger than the remaining playback time.
-
-detect_limit: number[0-255] - Black threshold for cropdetect.
- Smaller values will generally result in less cropping.
- See limit of https://ffmpeg.org/ffmpeg-filters.html#cropdetect
-
-detect_round: number[2^n] - The value which the width/height
- should be divisible by. Smaller values ​​have better detection
- accuracy. If you have problems with other filters,
- you can try to set it to 4 or 16.
- See round of https://ffmpeg.org/ffmpeg-filters.html#cropdetect
-
-detect_min_ratio: number[0.0-1.0] - The ratio of the minimum clip
- size to the original. If the picture is over cropped or under
- cropped, try adjusting this value.
-
-detect_seconds: seconds - How long to gather cropdetect data.
- Increasing this may be desirable to allow cropdetect more
- time to collect data.
---]]
+This script uses the lavfi cropdetect filter and the video-crop property to
+automatically crop the currently playing video with appropriate parameters.
+
+It automatically crops the video when playback starts.
+
+You can also manually crop the video by pressing the "C" (shift+c) key.
+Pressing it again undoes the crop.
+
+The workflow is as follows: First, it inserts the cropdetect filter. After
+<detect_seconds> (default is 1) seconds, it then sets video-crop based on the
+vf-metadata values gathered by cropdetect. The cropdetect filter is removed
+after video-crop is set as it is no longer needed.
+
+Since the crop parameters are determined from the 1 second of video between
+inserting the cropdetect filter and setting video-crop, the "C" key should be
+pressed at a position in the video where the crop region is unambiguous (i.e.,
+not a black frame, black background title card, or dark scene).
-require "mp.msg"
-require 'mp.options'
+If non-copy-back hardware decoding is in use, hwdec is temporarily disabled for
+the duration of cropdetect as the filter would fail otherwise.
+These are the default options. They can be overridden by adding
+script-opts-append=autocrop-<parameter>=<value> to mpv.conf.
+--]]
local options = {
+ -- Whether to automatically apply crop at the start of playback. If you
+ -- don't want to crop automatically, add
+ -- script-opts-append=autocrop-auto=no to mpv.conf.
auto = true,
+ -- Delay before starting crop in auto mode. You can try to increase this
+ -- value to avoid dark scenes or fade ins at beginning. Automatic cropping
+ -- will not occur if the value is larger than the remaining playback time.
auto_delay = 4,
+ -- Black threshold for cropdetect. Smaller values will generally result in
+ -- less cropping. See limit of
+ -- https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_limit = "24/255",
+ -- The value which the width/height should be divisible by. Smaller
+ -- values have better detection accuracy. If you have problems with
+ -- other filters, you can try to set it to 4 or 16. See round of
+ -- https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_round = 2,
+ -- The ratio of the minimum clip size to the original. A number from 0 to
+ -- 1. If the picture is over cropped, try adjusting this value.
detect_min_ratio = 0.5,
- detect_seconds = 1
+ -- How long to gather cropdetect data. Increasing this may be desirable to
+ -- allow cropdetect more time to collect data.
+ detect_seconds = 1,
+ -- Whether the OSD shouldn't be used when cropdetect and video-crop are
+ -- applied and removed.
+ suppress_osd = false,
}
-read_options(options)
-local label_prefix = mp.get_script_name()
-local labels = {
- crop = string.format("%s-crop", label_prefix),
- cropdetect = string.format("%s-cropdetect", label_prefix)
-}
+require "mp.options".read_options(options)
-timers = {
+local cropdetect_label = mp.get_script_name() .. "-cropdetect"
+
+local timers = {
auto_delay = nil,
detect_crop = nil
}
-function is_filter_present(label)
- local filters = mp.get_property_native("vf")
- for index, filter in pairs(filters) do
- if filter["label"] == label then
- return true
- end
- end
- return false
-end
+local hwdec_backup
-function is_enough_time(seconds)
+local command_prefix = options.suppress_osd and 'no-osd' or ''
+local function is_enough_time(seconds)
-- Plus 1 second for deviation.
local time_needed = seconds + 1
local playtime_remaining = mp.get_property_native("playtime-remaining")
@@ -101,80 +73,82 @@ function is_enough_time(seconds)
return playtime_remaining and time_needed < playtime_remaining
end
-function is_cropable()
- local vid = mp.get_property_native("vid")
- local is_album = vid and mp.get_property_native(
- string.format("track-list/%d/albumart", vid)
- ) or false
+local function is_cropable(time_needed)
+ if mp.get_property_native('current-tracks/video/image') ~= false then
+ mp.msg.warn("autocrop only works for videos.")
+ return false
+ end
+
+ if not is_enough_time(time_needed) then
+ mp.msg.warn("Not enough time to detect crop.")
+ return false
+ end
- return vid and not is_album
+ return true
end
-function remove_filter(label)
- if is_filter_present(label) then
- mp.command(string.format('vf remove @%s', label))
- return true
+local function remove_cropdetect()
+ for _, filter in pairs(mp.get_property_native("vf")) do
+ if filter.label == cropdetect_label then
+ mp.command(
+ string.format("%s vf remove @%s", command_prefix, filter.label))
+
+ return
+ end
end
- return false
end
-function cleanup()
-
- -- Remove all existing filters.
- for key, value in pairs(labels) do
- remove_filter(value)
+local function restore_hwdec()
+ if hwdec_backup then
+ mp.set_property("hwdec", hwdec_backup)
+ hwdec_backup = nil
end
+end
+
+local function cleanup()
+ remove_cropdetect()
-- Kill all timers.
for index, timer in pairs(timers) do
if timer then
timer:kill()
- timer = nil
+ timers[index] = nil
end
end
+
+ restore_hwdec()
end
-function detect_crop()
+local function apply_crop(meta)
+ -- Verify if it is necessary to crop.
+ local is_effective = meta.w and meta.h and meta.x and meta.y and
+ (meta.x > 0 or meta.y > 0
+ or meta.w < meta.max_w or meta.h < meta.max_h)
- -- If it's not cropable, exit.
- if not is_cropable() then
- mp.msg.warn("autocrop only works for videos.")
- return
+ -- Verify it is not over cropped.
+ local is_excessive = false
+ if is_effective and (meta.w < meta.min_w or meta.h < meta.min_h) then
+ mp.msg.info("The area to be cropped is too large.")
+ mp.msg.info("You might need to decrease detect_min_ratio.")
+ is_excessive = true
end
- -- Verify if there is enough time to detect crop.
- local time_needed = options.detect_seconds
-
- if not is_enough_time(time_needed) then
- mp.msg.warn("Not enough time to detect crop.")
+ if not is_effective or is_excessive then
+ -- Clear any existing crop.
+ mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix))
return
end
- -- Insert the cropdetect filter.
- local limit = options.detect_limit
- local round = options.detect_round
-
- mp.command(
- string.format(
- 'vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
- labels.cropdetect, limit, round
- )
- )
-
- -- Wait to gather data.
- timers.detect_crop = mp.add_timeout(time_needed, detect_end)
+ -- Apply crop.
+ mp.command(string.format("%s set file-local-options/video-crop %sx%s+%s+%s",
+ command_prefix, meta.w, meta.h, meta.x, meta.y))
end
-function detect_end()
-
+local function detect_end()
-- Get the metadata and remove the cropdetect filter.
- local cropdetect_metadata =
- mp.get_property_native(
- string.format("vf-metadata/%s",
- labels.cropdetect
- )
- )
- remove_filter(labels.cropdetect)
+ local cropdetect_metadata = mp.get_property_native(
+ "vf-metadata/" .. cropdetect_label)
+ remove_cropdetect()
-- Remove the timer of detect crop.
if timers.detect_crop then
@@ -182,7 +156,9 @@ function detect_end()
timers.detect_crop = nil
end
- local meta = {}
+ restore_hwdec()
+
+ local meta
-- Verify the existence of metadata.
if cropdetect_metadata then
@@ -195,7 +171,7 @@ function detect_end()
else
mp.msg.error("No crop data.")
mp.msg.info("Was the cropdetect filter successfully inserted?")
- mp.msg.info("Does your version of ffmpeg/libav support AVFrame metadata?")
+ mp.msg.info("Does your version of FFmpeg support AVFrame metadata?")
return
end
@@ -217,44 +193,41 @@ function detect_end()
else
mp.msg.error("Got empty crop data.")
mp.msg.info("You might need to increase detect_seconds.")
- return
end
apply_crop(meta)
end
-function apply_crop(meta)
-
- -- Verify if it is necessary to crop.
- local is_effective = meta.x > 0 or meta.y > 0
- or meta.w < meta.max_w or meta.h < meta.max_h
+local function detect_crop()
+ local time_needed = options.detect_seconds
- if not is_effective then
- mp.msg.info("No area detected for cropping.")
+ if not is_cropable(time_needed) then
return
end
- -- Verify it is not over cropped.
- local is_excessive = meta.w < meta.min_w and meta.h < meta.min_h
-
- if is_excessive then
- mp.msg.info("The area to be cropped is too large.")
- mp.msg.info("You might need to decrease detect_min_ratio.")
- return
+ local hwdec_current = mp.get_property("hwdec-current")
+ if hwdec_current:find("-copy$") == nil and hwdec_current ~= "no" and
+ hwdec_current ~= "crystalhd" and hwdec_current ~= "rkmpp" then
+ hwdec_backup = mp.get_property("hwdec")
+ mp.set_property("hwdec", "no")
end
- -- Remove existing crop.
- remove_filter(labels.crop)
+ -- Insert the cropdetect filter.
+ local limit = options.detect_limit
+ local round = options.detect_round
- -- Apply crop.
mp.command(
- string.format("vf pre @%s:lavfi-crop=w=%s:h=%s:x=%s:y=%s",
- labels.crop, meta.w, meta.h, meta.x, meta.y
+ string.format(
+ '%s vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
+ command_prefix, cropdetect_label, limit, round
)
)
+
+ -- Wait to gather data.
+ timers.detect_crop = mp.add_timeout(time_needed, detect_end)
end
-function on_start()
+local function on_start()
-- Clean up at the beginning.
cleanup()
@@ -275,8 +248,7 @@ function on_start()
-- Verify if there is enough time for autocrop.
local time_needed = options.auto_delay + options.detect_seconds
- if not is_enough_time(time_needed) then
- mp.msg.warn("Not enough time for autocrop.")
+ if not is_cropable(time_needed) then
return
end
@@ -294,7 +266,7 @@ function on_start()
end
end
-function on_toggle()
+local function on_toggle()
-- If it is during auto_delay, kill the timer.
if timers.auto_delay then
@@ -303,7 +275,8 @@ function on_toggle()
end
-- Cropped => Remove it.
- if remove_filter(labels.crop) then
+ if mp.get_property("video-crop") ~= "" then
+ mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix))
return
end
@@ -313,7 +286,7 @@ function on_toggle()
return
end
- -- Neither => Do delectcrop.
+ -- Neither => Detect crop.
detect_crop()
end