500 lines
14 KiB
Lua
500 lines
14 KiB
Lua
|
|
||
|
|
||
|
local yaw = 0.0
|
||
|
local last_yaw = 0.0
|
||
|
local init_yaw = 0.0
|
||
|
|
||
|
local pitch = 0.0
|
||
|
local last_pitch = 0.0
|
||
|
local init_pitch = 0.0
|
||
|
|
||
|
local roll = 0.0
|
||
|
local last_roll = 0.0
|
||
|
local init_roll = 0.0
|
||
|
|
||
|
local inputProjections = {
|
||
|
"hequirect",
|
||
|
"equirect",
|
||
|
"fisheye",
|
||
|
"pannini",
|
||
|
"cylindrical",
|
||
|
"sg"
|
||
|
}
|
||
|
local inputProjectionInd = 0
|
||
|
local inputProjection = "hequirect"
|
||
|
|
||
|
local outputProjections = {
|
||
|
"flat",
|
||
|
"sg"
|
||
|
}
|
||
|
|
||
|
local outputProjectionInd = 0
|
||
|
local outputProjection = "flat"
|
||
|
|
||
|
local idfov=180.0
|
||
|
local dfov=110.0
|
||
|
local last_dfov = 110.0
|
||
|
local init_dfov = 0.0
|
||
|
|
||
|
|
||
|
local doit = 0.0
|
||
|
local res = 1.0
|
||
|
local dragging = false
|
||
|
|
||
|
local smoothMouse = true
|
||
|
|
||
|
local scaling = 'linear'
|
||
|
|
||
|
local in_stereo = 'sbs'
|
||
|
|
||
|
local h_flip = '0'
|
||
|
local in_flip = ''
|
||
|
|
||
|
local interp = 'cubic'
|
||
|
|
||
|
local startTime = nil
|
||
|
|
||
|
local filterIsOn = false
|
||
|
|
||
|
local mousePos = {}
|
||
|
local lasttimePos = nil
|
||
|
local filename = nil
|
||
|
|
||
|
local fileobjectNumber = 0
|
||
|
local fileobjectFilename = ''
|
||
|
local videofilename = ''
|
||
|
local file_object = nil
|
||
|
|
||
|
local ffmpegComamndList = {}
|
||
|
|
||
|
local openNewLogFile = function()
|
||
|
if lasttimePos ~= nil then
|
||
|
fileobjectNumber = fileobjectNumber+1
|
||
|
end
|
||
|
videofilename = mp.get_property('filename')
|
||
|
fileobjectFilename = string.format('%s_3dViewHistory_%s.txt',videofilename,fileobjectNumber)
|
||
|
file_object = io.open(fileobjectFilename, 'w')
|
||
|
lasttimePos=nil
|
||
|
end
|
||
|
|
||
|
|
||
|
function SecondsToClock(seconds)
|
||
|
local seconds = tonumber(seconds)
|
||
|
if seconds <= 0 then
|
||
|
return "00:00:00";
|
||
|
else
|
||
|
hours = string.format("%02.f", math.floor(seconds/3600));
|
||
|
mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)));
|
||
|
secs = string.format("%02.2f", seconds - hours*3600 - mins *60);
|
||
|
return hours..":"..mins..":"..secs
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local writeHeadPositionChange = function()
|
||
|
|
||
|
if filename == nil then
|
||
|
filename = mp.get_property("path")
|
||
|
end
|
||
|
|
||
|
if file_object == nil then
|
||
|
return
|
||
|
else
|
||
|
local initPass=false
|
||
|
if lasttimePos == nil then
|
||
|
lasttimePos = mp.get_property("time-pos")
|
||
|
startTime = lasttimePos
|
||
|
initPass=true
|
||
|
if lasttimePos == nil then
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local newTimePos = mp.get_property("time-pos")
|
||
|
|
||
|
if newTimePos == nil then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local outputTs = string.format("%.3f-%.3f ",lasttimePos,newTimePos)
|
||
|
local changedValues = {}
|
||
|
local movementDuration = (newTimePos-lasttimePos)
|
||
|
local maximumTimeoutReached = movementDuration > 5.0
|
||
|
|
||
|
if initPass or pitch ~= last_pitch or maximumTimeoutReached then
|
||
|
changedValues[#changedValues+1]= string.format(", [expr] v360 pitch 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_pitch,pitch,lasttimePos,movementDuration)
|
||
|
end
|
||
|
last_pitch=pitch
|
||
|
|
||
|
if initPass or yaw ~= last_yaw or maximumTimeoutReached then
|
||
|
changedValues[#changedValues+1]= string.format(", [expr] v360 yaw 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_yaw,yaw,lasttimePos,movementDuration)
|
||
|
end
|
||
|
last_yaw=yaw
|
||
|
|
||
|
|
||
|
if initPass or roll ~= last_roll or maximumTimeoutReached then
|
||
|
changedValues[#changedValues+1]= string.format(", [expr] v360 roll 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_roll,roll,lasttimePos,movementDuration)
|
||
|
end
|
||
|
last_roll=roll
|
||
|
|
||
|
if initPass or dfov ~= last_dfov or maximumTimeoutReached then
|
||
|
changedValues[#changedValues+1]= string.format(", [expr] v360 d_fov 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_dfov,dfov,lasttimePos,movementDuration)
|
||
|
end
|
||
|
last_dfov=dfov
|
||
|
|
||
|
if initPass then
|
||
|
init_pitch = pitch
|
||
|
init_yaw = yaw
|
||
|
init_roll = roll
|
||
|
init_dfov = dfov
|
||
|
end
|
||
|
|
||
|
if #changedValues > 0 then
|
||
|
local commandString = ''
|
||
|
for k,changedValue in pairs(changedValues) do
|
||
|
commandString = commandString .. changedValue
|
||
|
end
|
||
|
|
||
|
commandString = commandString:sub(2)
|
||
|
|
||
|
commandString = outputTs .. commandString .. ';'
|
||
|
|
||
|
file_object:write(commandString .. '\n')
|
||
|
lasttimePos = newTimePos
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local updateAwaiting = false
|
||
|
local updateComplete = function()
|
||
|
updateAwaiting = false
|
||
|
end
|
||
|
|
||
|
local printRecordingStatus = function()
|
||
|
local startts = startTime
|
||
|
local endts = lasttimePos
|
||
|
local currenttS = mp.get_property("time-pos")
|
||
|
if file_object ~= nil and endts ~= nil and startts ~= nil then
|
||
|
endts = math.max(endts,currenttS)
|
||
|
mp.osd_message(string.format("Recording:%s",SecondsToClock(endts-startts)),10)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
local updateFilters = function ()
|
||
|
|
||
|
if not filterIsOn then
|
||
|
mp.command_native_async({"no-osd", "vf", "add", string.format("@vrrev:%sv360=%s:%s:in_stereo=%s:out_stereo=2d:id_fov=%s:d_fov=%.3f:yaw=%.3f:pitch=%s:roll=%.3f:w=%s*192.0:h=%.3f*108.0:h_flip=%s:interp=%s",in_flip,inputProjection,outputProjection,in_stereo,idfov,dfov,yaw,pitch,roll,res,res,h_flip,scaling)}, updateComplete)
|
||
|
filterIsOn=true
|
||
|
else
|
||
|
if not updateAwaiting then
|
||
|
updateAwaiting=true
|
||
|
mp.command_native_async({"no-osd", "vf", "set", string.format("@vrrev:%sv360=%s:%s:in_stereo=%s:out_stereo=2d:id_fov=%s:d_fov=%.3f:yaw=%.3f:pitch=%s:roll=%.3f:w=%s*192.0:h=%.3f*108.0:h_flip=%s:interp=%s",in_flip,inputProjection,outputProjection,in_stereo,idfov,dfov,yaw,pitch,roll,res,res,h_flip,scaling)}, updateComplete)
|
||
|
end
|
||
|
filterIsOn=true
|
||
|
end
|
||
|
printRecordingStatus()
|
||
|
writeHeadPositionChange()
|
||
|
end
|
||
|
|
||
|
local mouse_btn0_cb = function ()
|
||
|
dragging = not dragging
|
||
|
if dragging then
|
||
|
mp.set_property("cursor-autohide", "always")
|
||
|
else
|
||
|
mp.set_property("cursor-autohide", "no")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
local mouse_pan = function ()
|
||
|
|
||
|
|
||
|
if dragging then
|
||
|
|
||
|
local MousePosx, MousePosy = mp.get_mouse_pos()
|
||
|
local osd_w, osd_h = mp.get_property("osd-width"), mp.get_property("osd-height")
|
||
|
|
||
|
|
||
|
local yawpc = ((MousePosx/osd_w)-0.5)*180
|
||
|
local pitchpc = -((MousePosy/osd_h)-0.5)*180
|
||
|
|
||
|
local updateCrop = false
|
||
|
|
||
|
if smoothMouse then
|
||
|
if yaw ~= yawpc and math.abs(yaw-yawpc)<0.1 then
|
||
|
yaw = yawpc
|
||
|
updateCrop=true
|
||
|
yaw = math.max(-180,math.min(180,yaw))
|
||
|
elseif yaw ~= yawpc then
|
||
|
yaw = (yawpc+(yaw*5))/6
|
||
|
yaw = math.max(-180,math.min(180,yaw))
|
||
|
updateCrop=true
|
||
|
end
|
||
|
|
||
|
if pitch ~= pitchpc and math.abs(pitch-pitchpc)<0.1 then
|
||
|
pitch = pitchpc
|
||
|
pitch = math.max(-180,math.min(180,pitch))
|
||
|
updateCrop=true
|
||
|
elseif pitch ~= pitchpc then
|
||
|
pitch = (pitchpc+(pitch*5))/6
|
||
|
pitch = math.max(-180,math.min(180,pitch))
|
||
|
updateCrop=true
|
||
|
end
|
||
|
else
|
||
|
if yaw ~= yawpc then
|
||
|
yaw = yawpc
|
||
|
yaw = math.max(-180,math.min(180,yaw))
|
||
|
updateCrop=true
|
||
|
end
|
||
|
if pitch ~= pitchpc then
|
||
|
pitch = pitchpc
|
||
|
pitch = math.max(-180,math.min(180,pitch))
|
||
|
updateCrop=true
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
if updateCrop then
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local increment_res = function(inc)
|
||
|
res = res+inc
|
||
|
res = math.max(math.min(res,20),1)
|
||
|
mp.osd_message(string.format("Out-Width: %spx",res*108.0),0.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local increment_roll = function (inc)
|
||
|
roll = roll+inc
|
||
|
updateFilters()
|
||
|
mp.osd_message(string.format("Roll: %s°",roll),0.5)
|
||
|
end
|
||
|
|
||
|
local increment_pitch = function (inc)
|
||
|
pitch = pitch+inc
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local increment_yaw = function (inc)
|
||
|
yaw = yaw+inc
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local increment_zoom = function (inc)
|
||
|
dfov = dfov+inc
|
||
|
dfov = math.max(math.min(150,dfov),30)
|
||
|
mp.osd_message(string.format("D-Fov: %s°",dfov),0.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local toggleSmoothMouse = function()
|
||
|
smoothMouse = not smoothMouse
|
||
|
if smoothMouse then
|
||
|
mp.osd_message("Mouse smothing On",0.5)
|
||
|
else
|
||
|
mp.osd_message("Mouse smothing Off",0.5)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local switchScaler = function()
|
||
|
if scaling == 'nearest' then
|
||
|
scaling = 'cubic'
|
||
|
elseif scaling == 'cubic' then
|
||
|
scaling = 'lagrange9'
|
||
|
elseif scaling == 'lagrange9' then
|
||
|
scaling = 'lanczos'
|
||
|
elseif scaling == 'lanczos' then
|
||
|
scaling = 'linear'
|
||
|
elseif scaling == 'linear' then
|
||
|
scaling = 'nearest'
|
||
|
end
|
||
|
mp.osd_message("Scaling algorithm: " .. scaling,5.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local switchEye = function()
|
||
|
if h_flip == '0' then
|
||
|
h_flip = '1'
|
||
|
in_flip = 'hflip,'
|
||
|
mp.osd_message("Right eye",0.5)
|
||
|
else
|
||
|
h_flip = '0'
|
||
|
in_flip = ''
|
||
|
mp.osd_message("Left eye",0.5)
|
||
|
end
|
||
|
print(ih_flip,h_flip)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
|
||
|
local cycleInputProjection = function()
|
||
|
inputProjectionInd = ((inputProjectionInd+1) % (#inputProjections +1))
|
||
|
inputProjection = inputProjections[inputProjectionInd]
|
||
|
mp.osd_message(string.format("Input projection: %s ",inputProjection),0.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local cycleOutputProjection = function()
|
||
|
outputProjectionInd = ((outputProjectionInd+1) % (#outputProjections + 1))
|
||
|
outputProjection = outputProjections[outputProjectionInd]
|
||
|
mp.osd_message(string.format("Output projection: %s",outputProjection),0.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
|
||
|
local switchInputFovBounds = function()
|
||
|
if idfov == 180.0 then
|
||
|
idfov = 360.0
|
||
|
elseif idfov == 360.0 then
|
||
|
idfov = 90.0
|
||
|
else
|
||
|
idfov = 180.0
|
||
|
end
|
||
|
mp.osd_message(string.format("Input fov bounds: %s°",idfov),0.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local switchStereoMode = function()
|
||
|
if in_stereo == 'sbs' then
|
||
|
in_stereo = 'tb'
|
||
|
else
|
||
|
in_stereo = 'sbs'
|
||
|
end
|
||
|
mp.osd_message("Input format: " .. in_stereo,0.5)
|
||
|
updateFilters()
|
||
|
end
|
||
|
|
||
|
local showHelp = function()
|
||
|
mp.osd_message("Keyboard and Mouse Controls:\n? = show help\ny,h = adjust quality\ni,j,k,l,mouseClick = Look around\nu,i = roll head\n-,=,mouseWheel = zoom\nr = switch SetereoMode\nt = switch Eye\ne = switch Scaler\ng = toggle mouse smothing\nn = start and stop motion recording\n1,2 - cycle in and out projections",10)
|
||
|
end
|
||
|
|
||
|
local closeCurrentLog = function()
|
||
|
commandForFinalLog=''
|
||
|
if lasttimePos ~= nil and file_object ~= nil then
|
||
|
|
||
|
finalTimeStamp = mp.get_property("time-pos")
|
||
|
|
||
|
file_object:write('#\n')
|
||
|
|
||
|
local stats = string.format( '# Duration: %s-%s (total %s) %s seconds',
|
||
|
SecondsToClock(startTime),SecondsToClock(finalTimeStamp),SecondsToClock(finalTimeStamp-startTime),finalTimeStamp-startTime )
|
||
|
|
||
|
print('#')
|
||
|
file_object:write( stats .. '\n')
|
||
|
print(stats)
|
||
|
|
||
|
file_object:write( '# Suggested ffmpeg conversion command:\n')
|
||
|
|
||
|
local closingCommandComment = string.format('ffmpeg -y -ss %s -i "%s" -to %s -copyts -filter_complex "%sv360=%s:%s:in_stereo=%s:out_stereo=2d:id_fov=%s:d_fov=%.3f:yaw=%.3f:pitch=%.3f:roll=%.3f:w=1920.0:h=1080.0:interp=cubic:h_flip=%s,sendcmd=filename=%s_3dViewHistory_%s.txt" -avoid_negative_ts make_zero -preset slower -crf 17 "%s_2d_%03d.mp4"',
|
||
|
startTime,filename,finalTimeStamp,in_flip,inputProjection,outputProjection,in_stereo,idfov,init_dfov,init_yaw,init_pitch,init_roll,h_flip,videofilename,fileobjectNumber,videofilename,fileobjectNumber
|
||
|
)
|
||
|
|
||
|
|
||
|
file_object:write('# ' .. closingCommandComment .. '\n')
|
||
|
file_object:write('#\n')
|
||
|
|
||
|
print(closingCommandComment)
|
||
|
print('#')
|
||
|
|
||
|
commandForFinalLog = closingCommandComment
|
||
|
end
|
||
|
if file_object ~= nil then
|
||
|
file_object:close()
|
||
|
file_object = nil
|
||
|
end
|
||
|
return commandForFinalLog
|
||
|
end
|
||
|
|
||
|
local startNewLogSession = function()
|
||
|
if file_object == nil then
|
||
|
openNewLogFile()
|
||
|
writeHeadPositionChange()
|
||
|
mp.osd_message(string.format("Start Motion Record %s_3dViewHistory_%s.txt",videofilename,fileobjectNumber),0.5)
|
||
|
else
|
||
|
mp.osd_message(string.format("Stop Motion Record %s_3dViewHistory_%s.txt",videofilename,fileobjectNumber),0.5)
|
||
|
writeHeadPositionChange()
|
||
|
local command = closeCurrentLog()
|
||
|
if command then
|
||
|
ffmpegComamndList[#ffmpegComamndList+1] = command
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
local onExit = function()
|
||
|
closeCurrentLog()
|
||
|
mergedCommand = ''
|
||
|
for k,v in pairs(ffmpegComamndList) do
|
||
|
if v ~= '' then
|
||
|
mergedCommand = mergedCommand .. ' & ' .. v
|
||
|
end
|
||
|
end
|
||
|
if mergedCommand ~= '' then
|
||
|
mergedCommand = mergedCommand:sub(3)
|
||
|
print(mergedCommand)
|
||
|
batchfile = io.open('convert_3dViewHistory.bat', 'w')
|
||
|
batchfile:write(mergedCommand)
|
||
|
batchfile:close()
|
||
|
print('Batch processing file created convert_3dViewHistory.bat')
|
||
|
else
|
||
|
print('No head motions logged')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local recordingStatusTimer = nil
|
||
|
|
||
|
local initFunction = function()
|
||
|
|
||
|
mp.add_forced_key_binding("1", cycleInputProjection )
|
||
|
mp.add_forced_key_binding("2", cycleOutputProjection )
|
||
|
|
||
|
mp.add_forced_key_binding("u", function() increment_roll(-1) end, 'repeatable')
|
||
|
mp.add_forced_key_binding("o", function() increment_roll(1) end, 'repeatable')
|
||
|
|
||
|
mp.add_forced_key_binding("v", writeHeadPositionChange)
|
||
|
|
||
|
mp.add_forced_key_binding("i", function() increment_pitch(1) end, 'repeatable')
|
||
|
mp.add_forced_key_binding("k", function() increment_pitch(-1) end, 'repeatable')
|
||
|
mp.add_key_binding("l", function() increment_yaw(1) end, 'repeatable')
|
||
|
mp.add_key_binding("j", function() increment_yaw(-1) end, 'repeatable')
|
||
|
mp.add_key_binding("c", "easy_crop", updateFilters)
|
||
|
|
||
|
mp.add_forced_key_binding("y", function() increment_res(1) end, 'repeatable')
|
||
|
mp.add_forced_key_binding("h", function() increment_res(-1) end, 'repeatable')
|
||
|
|
||
|
mp.add_forced_key_binding("=", function() increment_zoom(-1) end, 'repeatable')
|
||
|
mp.add_forced_key_binding("-", function() increment_zoom(1) end, 'repeatable')
|
||
|
|
||
|
mp.add_forced_key_binding("WHEEL_DOWN", function() increment_zoom(1) end)
|
||
|
mp.add_forced_key_binding("WHEEL_UP", function() increment_zoom(-1) end)
|
||
|
|
||
|
mp.add_forced_key_binding("r", switchStereoMode)
|
||
|
mp.add_forced_key_binding("t", switchEye)
|
||
|
mp.add_forced_key_binding("e", switchScaler)
|
||
|
mp.add_forced_key_binding("g", toggleSmoothMouse)
|
||
|
mp.add_forced_key_binding("b", switchInputFovBounds)
|
||
|
mp.add_forced_key_binding("n", startNewLogSession)
|
||
|
|
||
|
mp.set_property("osc", "no")
|
||
|
mp.set_property("fullscreen", "yes")
|
||
|
mp.set_property("osd-font-size", "30")
|
||
|
mp.add_forced_key_binding("mouse_btn0",mouse_btn0_cb)
|
||
|
mp.add_forced_key_binding("mouse_move", mouse_pan)
|
||
|
|
||
|
mp.add_forced_key_binding("?", showHelp)
|
||
|
mp.add_forced_key_binding("/", showHelp)
|
||
|
|
||
|
mp.register_event("end-file", onExit)
|
||
|
mp.register_event("shutdown", onExit)
|
||
|
|
||
|
recordingStatusTimer = mp.add_periodic_timer(0.1,printRecordingStatus)
|
||
|
|
||
|
updateFilters()
|
||
|
|
||
|
end
|
||
|
|
||
|
mp.register_event("file-loaded", initFunction)
|