121 lines
3.2 KiB
Lua
121 lines
3.2 KiB
Lua
local httpserver = require "httpserver"
|
|
local CACHE_DIR = "~/Videos/virtcam"
|
|
|
|
local function run(cmd)
|
|
print(cmd)
|
|
local proc = io.popen(cmd .. " >/tmp/virtcam.log 2>&1")
|
|
assert(proc)
|
|
assert(proc:close())
|
|
end
|
|
|
|
run("sudo modprobe v4l2loopback -r -f")
|
|
run("sudo modprobe v4l2loopback video_nr=21 card_label=virtcam exclusive_caps=1")
|
|
run("mkdir -p " .. CACHE_DIR)
|
|
|
|
Ffmpeg = { cmd = "", proc = nil, name = "", started = false }
|
|
|
|
function Ffmpeg:new(o)
|
|
o = o or {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end
|
|
|
|
function Ffmpeg:start()
|
|
assert(self.proc == nil)
|
|
self.proc = io.popen(self.cmd .. " >/tmp/virtcam.log 2>&1", "w")
|
|
assert(self.proc)
|
|
print(self.name .. " started")
|
|
self.started = true
|
|
end
|
|
|
|
function Ffmpeg:stop()
|
|
assert(self.proc)
|
|
self.proc:write("q")
|
|
self.proc:flush()
|
|
assert(self.proc:close())
|
|
self.proc = nil
|
|
print(self.name .. " stopped")
|
|
self.started = false
|
|
end
|
|
|
|
Actualcam = Ffmpeg:new {
|
|
name = "actualcam",
|
|
cmd = "ffmpeg -i /dev/video0 -vcodec copy -f v4l2 /dev/video21",
|
|
}
|
|
|
|
|
|
Recorder = Ffmpeg:new {
|
|
name = "recorder",
|
|
cmd = "ffmpeg -y -i /dev/video21 " .. CACHE_DIR .. "/seq.mp4",
|
|
}
|
|
|
|
Looper = Ffmpeg:new {
|
|
name = "looper",
|
|
cmd = "ffmpeg -re -stream_loop -1 -i " ..
|
|
CACHE_DIR .. "/loop.mp4 -f v4l2 -vcodec rawvideo -pix_fmt yuyv422 -framerate 30 /dev/video21 ",
|
|
}
|
|
|
|
|
|
local all = { Actualcam, Recorder, Looper }
|
|
|
|
local function run_only(the_one)
|
|
for _, fp in ipairs(all) do
|
|
if fp ~= the_one and fp.started then
|
|
fp:stop()
|
|
end
|
|
end
|
|
if the_one and not the_one.started then
|
|
print("try to start")
|
|
the_one:start()
|
|
end
|
|
end
|
|
|
|
local stage = "stopped"
|
|
|
|
httpserver.NewHttpServer("127.0.0.1", 10200, {
|
|
["POST /prepare"] = function(_, _)
|
|
if stage == "stopped" then
|
|
-- run("pactl set-source-mute @DEFAULT_SOURCE@ 1")
|
|
run_only(Actualcam)
|
|
stage = "actual"
|
|
end
|
|
end,
|
|
["POST /actual"] = function(_, _)
|
|
if stage ~= "actual" then
|
|
-- run("pactl set-source-mute @DEFAULT_SOURCE@ 0")
|
|
run_only(Actualcam)
|
|
stage = "actual"
|
|
end
|
|
end,
|
|
["POST /fake"] = function(_, _)
|
|
if stage ~= "fake" then
|
|
-- run("pactl set-source-mute @DEFAULT_SOURCE@ 1")
|
|
run_only(Actualcam)
|
|
Recorder:start()
|
|
run("sleep 3")
|
|
Recorder:stop()
|
|
run("ffmpeg -y -i " .. CACHE_DIR .. "/seq.mp4 -vf reverse " .. CACHE_DIR .. "/reversed.mp4")
|
|
run("ffmpeg -y -i " ..
|
|
CACHE_DIR ..
|
|
"/seq.mp4 -i " ..
|
|
CACHE_DIR ..
|
|
"/reversed.mp4 -filter_complex '[0:v] [1:v] concat=n=2:v=1 [v]' -map '[v]' " .. CACHE_DIR .. "/loop.mp4")
|
|
run_only(Looper)
|
|
stage = "fake"
|
|
end
|
|
end,
|
|
["POST /stop"] = function(_, _)
|
|
if stage ~= "stopped" then
|
|
run_only(nil)
|
|
stage = "stopped"
|
|
end
|
|
end,
|
|
["POST /toggle-mute"] = function(_, _)
|
|
run("pactl set-source-mute @DEFAULT_SOURCE@ toggle")
|
|
end,
|
|
["POST /mute"] = function(_, _)
|
|
run("pactl set-source-mute @DEFAULT_SOURCE@ 1")
|
|
end,
|
|
})
|