Currently in my mpv.conf I use video-aspect=16:9 to scale all videos, specifically the old 4:3 ones.
Since mpv is my default player, I get the odd family video that is shot on a smartphone vertically, and it also stretches out to 16:9, which makes it look unwatchable as you could imagine.
I was wondering if there was a method I could use with scripts or otherwise to only scale/stretch 4:3 videos to 16:9, otherwise to ignore or keep original aspect ratio.
On a side note:
Is posting questions like this appropriate or acceptable here? I realize that this is meant for bug reports mainly though I wasn't sure if it would also be ok to ask for help on conf/script issues. If not, then apologies, please feel free to close the issue, I'm just unaware if it's acceptable or not.
Thanks.
Of course. Use the lua function https://mpv.io/manual/master/#lua-scripting-mp-get-property-number(name-[,def]) to get the property https://mpv.io/manual/master/#command-interface-video-params/aspect and in case it fulfills your condition you can use this function https://mpv.io/manual/master/#lua-scripting-mp-set-property-number(name,-value) to set this property https://mpv.io/manual/master/#command-interface-video-aspect accordingly.
Thank you very much for the guidance and help.
I seem to be stuck, mp.get_property_number("video-params/aspect") always returns as nil property unavailable.
function set_specific_ratio()
--local originalAspect = mp.get_property_number("video-params/aspect")
if mp.get_property_number("video-params/aspect") == 4.3 then
mp.set_property_number("video-aspect", 16.9)
end
end
mp.register_event("start-file", set_specific_ratio)
mpv 0.28.0 (C) 2000-2017 mpv/MPlayer/mplayer2 projects
built on Mon Dec 25 01:09:58 CET 2017
ffmpeg library versions:
libavutil 56.7.100
libavcodec 58.8.100
libavformat 58.3.100
libswscale 5.0.101
libavfilter 7.7.100
libswresample 3.0.101
ffmpeg version: N-89595-g40d4b13228
I'm doing something horribly wrong, right?
start-file is too early for that. At that point in time the video and demuxer haven't had a change to meet each other yet.
I'd give video-reconfig a try. At this point, all the information about your video and the window should be present. It could be that changing the aspect ratio triggers video-reconfig again as resizing the window does so too. In that case, you need to watch out for a loop.
As an alternative file-loaded might also work.
Of course you can also observe the video-params/aspect property using mp.observe_property and act once it's no longer nil (and maybe unregister your observer afterwards).
As a last option you can register a key binding that triggers the script using mp.add_key_binding or... do all of them together.
Thanks again @Argon- for the continued help. You were absolutely right, video-reconfig worked perfectly.
I ended up using a crude comparison condition to see if ratio is 4:3 then I used mp.set_property instead of mp.set_property_number to set an exact 16:9 value instead of a not so accurate 1.7 ratio.
function set_specific_ratio()
local originalAspect = mp.get_property_number("video-params/aspect")
-- A very crude way to find 4:3 ratio. it varies as a float from 1.30 and up.
if originalAspect >= 1.30 and originalAspect <= 1.39 then
mp.set_property("video-aspect", "16:9")
end
end
mp.register_event("video-reconfig", set_specific_ratio)
I've tested it with a few videos and it seems to be doing great. Thanks again.
Edit:
I'll try to follow your advice about using mp.observe_property, I think it might yield a better result.
Just a slight update, if anyone else wanted to use it. I had to make sure to run the condition only if value wasn't nil because Lua was returning an error on exit.
function set_specific_ratio()
local originalAspect = mp.get_property_number("video-params/aspect")
-- Make sure value isn't nil to run condition, otherwise Lua will return error on exit.
if originalAspect ~= nil then
-- A very crude way to find 4:3 ratio. it varries as a float from 1.30 and up.
if originalAspect >= 1.30 and originalAspect <= 1.39 then
mp.set_property("video-aspect", "16:9")
end
end
end
mp.register_event("video-reconfig", set_specific_ratio)
@Argon- Hate to bother you yet again on something so trivial, so please, don't feel pressured to answer, you've helped me quite a bit already. I'm trying to take the chance to learn the functions as well. I took your advice on observing video-params/aspect for change, then unregister the observer once the condition is set, so it doesn't run more than once unnecessarily.
Everything seems to be working fine and terminal doesn't output any errors. I'm just unsure if I'm using unobserve correctly.
function setBoxRatio(name, value)
if value ~= nil then
if value >= 1.30 and value <= 1.39 then
mp.set_property("video-aspect", "16:9")
end
mp.unobserve_property(setBoxRatio)
end
end
mp.observe_property("video-params/aspect", "number", setBoxRatio)
Should be pretty straight forward. Observe value, when it's not nil and ratio matches, change aspect ratio, then unobserve after checking value.
But it does work, doesn't it?
By the looks of it it is alright. Thinking about it now it's probably not sufficient though... think about a playlist with movies of different aspect ratio. This approach would only work for the first video, wouldn't it?
The observer should be registered for every newly loaded video (instead of once, at the start of script execution). In this case, one should probably make sure to also unregister the old one (in case it wasn't done already)
It works, yeah. But you were absolutely right again. If a playlist has mixed ratios, it won't work if the first video isn't 4:3, and that means if the first video is 4:3, it changes all aspect ratio to 16:9, even if not 4:3 within the playlist.
I came up with this solution, which works perfectly with a mixed ratio playlist and no errors in terminal. Is it too much?
function setBoxRatio(name, value)
if value ~= nil then
-- If 4:3, change to 16:9
if value >= 1.30 and value <= 1.39 then
mp.set_property("video-aspect", "16:9")
-- Otherwise, keep default aspect ratio
else
mp.set_property("video-aspect", "-1")
end
mp.unobserve_property(setBoxRatio)
end
end
function prepareRatioCheck()
mp.observe_property("video-params/aspect", "number", setBoxRatio)
end
mp.register_event("file-loaded", prepareRatioCheck)
The logic in my head is "Check if new file is loaded, if yes, check ratio, if box ratio, turn wide, else, keep original ratio then unobserve".
I've tested with a mixture of ratios playlists in different orders, seems to be working great.
Edit:
I'm definitely doing something wrong with unobserve. While the script works perfectly with mixed ratios, it seems to be skipping a file. As in 1 works, 2 doesn't, 3 works, 4 doesn't, 5 works ...etc
The cause for skipping is the ratio check if / else statements.
Oh well. I've bothered you enough with all this, lol. Thank you very much for all your help and guidance, I definitely learned a lot and I'll keep on experimenting.
I was finally able to achieve what I wanted, working in all conditions and finally managed to unobserve correctly without it resetting. It was so simple, I got mad a little when I saw what I was doing wrong. 馃槃
function setBoxRatio(name, value)
if value ~= nil then
if value >= 1.30 and value <= 1.39 then
mp.set_property("video-aspect", "16:9")
end
end
mp.unobserve_property(setBoxRatio)
end
function prepareRatioCheck()
mp.set_property("video-aspect", "-1")
mp.observe_property("video-params/aspect", "number", setBoxRatio)
end
mp.register_event("start-file", prepareRatioCheck)
Thank you @Argon- for your patience and putting up with me in learning the basics, much appreciated.
Are you sure this works when you unobserve even when the value was nil?
It works, yeah. I only moved it outside of the if block just in case I run a music file (no ratio/nil), that would keep the observer running, since it would only be unobserved if value isn't nil.
To be honest, the only reason I know unobserve is working is because if I comment it out completely, the aspect ratio changes from 4:3 to 16:9 back and forth every second.
Unobserve in that placement adjusts ratio as needed and works as intended.
Is my logic flawed or am I doing something wrong?
I'm thinking about a case where setBoxRatio is run but video-params/aspect is still nil. Loading is async so that could happen, depending on the event you are using. In this case you'd already unregister the observer before it can return a non-nil value?
True, good point. I'll move it back in the if block and try to figure out a way to only run the observer in prepareRatioCheck if it's a video file, I might use file extension match or something, haven't thought of it yet.
Thanks, @Argon- , really. Your pointers are helping me learn more every time. I'll definitely post the changes to get your opinion, should be basic enough, something like:
function prepareRatioCheck()
--if file == video then
mp.set_property("video-aspect", "-1")
mp.observe_property("video-params/aspect", "number", setBoxRatio)
--end
end
You could check if a VO window exists before you do aspect-related stuff, like it is done here: https://github.com/mpv-player/mpv/blob/master/player/lua/stats.lua#L160
Oh, wow, thanks for that. I managed to get it kind of working, but it doesn't work for the first video in the playlist, because script runs too soon in first file. I've changed event from start-file to file-loaded, still same behavior.
I thought about using observe for vo-configured, but then I'll end up with the same problem but with another observe/unobserve, I think. For the moment, I used mp.msg to track progress of the script within terminal, until I figure out how to make it run for all videos, including the first one.
Would using a file extension check instead be a bad idea? For example, autoload's extensions list but for video only:
EXTENSIONS = {
'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp'
}
Then in prepareRatioCheck I do something like "If filename extension found in extensions list, run script". Just need to look in refrences to find out what outputes filename.ext.
Would that be bad?
Ok, I managed to do it with a file extension check, and now the script will only run and observe if it's a video file, and unobserve correctly.
What do you think?
-- This script changes the aspect ratio of all 4:3 videos to 16:9
-- It does so by first checking if the file is a video by doing a
-- file extension check, then checks the video parameter aspect ratio
-- after that, if the aspect ratio is 4:3 it changes it to 16:9 then
-- ends the script.
-- Big thanks to Argon-, without his help, I couldn't have done it.
-- Also thanks to autoload authors, specifically for these parts of the script
-- function Set(t), EXTENSIONS, function get_extension(path)
local msg = require 'mp.msg'
local utils = require 'mp.utils'
function Set (t)
local set = {}
for _, v in pairs(t) do set[v] = true end
return set
end
EXTENSIONS = Set {
'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp'
}
function get_extension(path)
match = string.match(path, "%.([^%.]+)$" )
if match == nil then
return "nomatch"
else
return match
end
end
function setBoxRatio(name, value)
if value ~= nil then
if value >= 1.30 and value <= 1.39 then
mp.set_property("video-aspect", "16:9")
msg.info("Aspect-ratio changed from 4:3 to 16:9")
else
msg.info("Aspect-ratio unchanged, video is not 4:3.")
end
mp.unobserve_property(setBoxRatio)
msg.info("Finished check, no longer observing aspect-ratio and ended script.")
end
end
function prepareRatioCheck()
local path = mp.get_property("path", "")
local dir, filename = utils.split_path(path)
if EXTENSIONS[string.lower(get_extension(filename))] then
mp.set_property("video-aspect", "-1")
msg.info("Aspect-ratio has been reset to default to initialize ratio check.")
mp.observe_property("video-params/aspect", "number", setBoxRatio)
msg.info("Observing aspect-ratio value...")
else
msg.info("Not video file, script didn't run.")
end
end
mp.register_event("file-loaded", prepareRatioCheck)
Sample output: (Mixed ratios, and an audio file)
cplayer: Playing: E:\mpv\001 box.MP4
autoload: Adding 002 wide.mkv
autoload: Adding 003 box.MP4
autoload: Adding 004 vertical.mp4
autoload: Adding audio.mp3
cplayer: (+) Video --vid=1 (*) (h264 480x360 23.976fps)
cplayer: (+) Audio --aid=1 --alang=und (*) (aac 2ch 44100Hz)
boxtowide: Aspect-ratio has been reset to default to initialize ratio check.
boxtowide: Observing aspect-ratio value...
cplayer: AO: [wasapi] 48000Hz stereo 2ch float
cplayer: VO: [gpu] 480x360 yuv420p
boxtowide: Aspect-ratio changed from 4:3 to 16:9
statusline: AV: 00:00:00 / 00:24:38 (0%) A-V: 0.000 DS: 2.000/0 Cache: 904s+108MB
[+--------------------------------------------------------------------------------------------------------------------]
boxtowide: Finished check, no longer observing aspect-ratio and ended script.
statusline: AV: 00:00:00 / 00:24:38 (0%) A-V: -0.031 DS: 2.500/0 Cache: 918s+109MB
[+--------------------------------------------------------------------------------------------------------------------]
cplayer: VO: [gpu] 480x360 => 640x360 yuv420p
statusline: AV: 00:00:03 / 00:24:38 (0%) A-V: 0.005 DS: 2.537/0 Cache: 1474s+123MB
[+--------------------------------------------------------------------------------------------------------------------]
cplayer:
cplayer: Playing: E:\mpv\002 wide.mkv
cplayer: (+) Video --vid=1 (*) (h264 720x404 23.976fps)
cplayer: (+) Audio --aid=1 (*) (aac 2ch 48000Hz)
boxtowide: Aspect-ratio has been reset to default to initialize ratio check.
boxtowide: Observing aspect-ratio value...
cplayer: AO: [wasapi] 48000Hz stereo 2ch float
cplayer: VO: [gpu] 720x404 yuv420p
boxtowide: Aspect-ratio unchanged, video is not 4:3.
statusline: AV: 00:00:00 / 00:18:50 (0%) A-V: 0.000 DS: 2.000/0 Cache: 394s+165MB
[+--------------------------------------------------------------------------------------------------------------------]
boxtowide: Finished check, no longer observing aspect-ratio and ended script.
statusline: AV: 00:00:02 / 00:18:50 (0%) A-V: -0.011 DS: 2.538/0 Cache: 1127s+184MB
[+--------------------------------------------------------------------------------------------------------------------]
cplayer:
cplayer: Playing: E:\mpv\003 box.MP4
cplayer: (+) Video --vid=1 (*) (h264 480x360 23.976fps)
cplayer: (+) Audio --aid=1 --alang=und (*) (aac 2ch 44100Hz)
boxtowide: Aspect-ratio has been reset to default to initialize ratio check.
boxtowide: Observing aspect-ratio value...
cplayer: AO: [wasapi] 48000Hz stereo 2ch float
cplayer: VO: [gpu] 480x360 yuv420p
boxtowide: Aspect-ratio changed from 4:3 to 16:9
boxtowide: Finished check, no longer observing aspect-ratio and ended script.
statusline: AV: 00:00:00 / 00:24:36 (0%) A-V: -0.028 DS: 2.500/0 Cache: 368s+85MB
[+--------------------------------------------------------------------------------------------------------------------]
cplayer: VO: [gpu] 480x360 => 640x360 yuv420p
statusline: AV: 00:00:01 / 00:24:36 (0%) A-V: 0.007 DS: 2.581/0 Cache: 1473s+115MB
[+--------------------------------------------------------------------------------------------------------------------]
cplayer:
cplayer: Playing: E:\mpv\004 vertical.mp4
cplayer: (+) Video --vid=1 (*) (h264 360x640 16.655fps)
cplayer: (+) Audio --aid=1 --alang=eng (*) (aac 2ch 48000Hz)
boxtowide: Aspect-ratio has been reset to default to initialize ratio check.
boxtowide: Observing aspect-ratio value...
cplayer: AO: [wasapi] 48000Hz stereo 2ch float
cplayer: VO: [gpu] 360x640 yuv420p
boxtowide: Aspect-ratio unchanged, video is not 4:3.
statusline: AV: 00:00:00 / 00:00:46 (0%) A-V: 0.000 DS: 4.000/0 Cache: 45s+5MB
[+--------------------------------------------------------------------------------------------------------------------]
boxtowide: Finished check, no longer observing aspect-ratio and ended script.
statusline: AV: 00:00:01 / 00:00:46 (2%) A-V: -0.003 DS: 3.875/0 Cache: 44s+5MB
[---+-----------------------------------------------------------------------------------------------------------------]
cplayer:
cplayer: Playing: E:\mpv\audio.mp3
cplayer: (+) Audio --aid=1 (mp3 2ch 44100Hz)
statusline: 004 vertical.mp4
statusline: 4/5
boxtowide: Not video file, script didn't run.
cplayer: AO: [wasapi] 48000Hz stereo 2ch float
I don't get why it has to be so complicated :o
Quick testing shows this should work just fine:
function setBoxRatio(name, value)
if value ~= nil then
if value >= 1.30 and value <= 1.39 then
mp.set_property("video-aspect", "16:9")
end
mp.unobserve_property(setBoxRatio)
end
end
mp.register_event("file-loaded", function() mp.observe_property("video-params/aspect", "number", setBoxRatio) end)
Yes, here the observer sticks with non-video files. But why is this an issue? It's not taking up resources. As non-videos (as in: no window) don't have an aspect ratio, they will never emit a change and therefore trigger your script to run.
I mean it feels a bit "dirty" to have them stick around, yes, but there shouldn't be much harm in doing so. If you really want to clean them up, you could register to the end-file event and remove the observer when a file stops playing instead. That should also work, although I didn't test it. One bonus for this approach: you can handle videos with "dynamic aspect ratio" as well! Not that something like that exits though...
Why would you even unobserve the property? Shouldn't this work just fine?
mp.observe_property("video-params/aspect", "number", function(name, value)
if value >= 1.30 and value <= 1.39 then
mp.set_property("video-aspect", "16:9")
end
end)
No need to overcomplicate things.
I wasn't aware that observed properties are "global", i.e. I assumed a property is observed per video. Of course, in this case you can accomplish it even shorter (just add a check for value ~= nil).
@haasn @Argon- No, that wouldn't work. To be more exact, it will change the aspect ratio of all videos in playlist to 16:9, once a 4:3 is played. That's why I had --video-aspect=-1 first, to reset it.
It was actually thanks to @Argon- pointers that made me realize a mixed ratio playlist would yield that result.
About observe/unobserve. Thank you, I honestly didn't know it wouldn't be needed. I thought it would be better. I don't know but I kept thinking of the possible effect of something like an endless while loop during entire playback time of each video. It seems I was wrong.
So you guys think I should skip the unobserve and the "run script if video only" parts?