First decent version of the module
This commit is contained in:
Executable → Regular
+18
-20
@@ -70,8 +70,18 @@ case "${1:-help}" in
|
|||||||
status)
|
status)
|
||||||
get_status_json
|
get_status_json
|
||||||
;;
|
;;
|
||||||
|
stop)
|
||||||
|
log "stop: killing provider and feeder"
|
||||||
|
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
|
||||||
|
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
|
||||||
|
echo 1 > /data/local/tmp/virtualcam_disabled 2>/dev/null
|
||||||
|
log "stop: disabled flag set"
|
||||||
|
get_status_json
|
||||||
|
log "stop: done"
|
||||||
|
;;
|
||||||
start)
|
start)
|
||||||
log "start: killing old provider"
|
log "start: removing disable flag"
|
||||||
|
rm -f /data/local/tmp/virtualcam_disabled 2>/dev/null
|
||||||
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
|
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
|
||||||
mkdir -p "$LIB_DST" 2>/dev/null
|
mkdir -p "$LIB_DST" 2>/dev/null
|
||||||
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null
|
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null
|
||||||
@@ -85,6 +95,7 @@ case "${1:-help}" in
|
|||||||
restart)
|
restart)
|
||||||
log "restart: killing provider"
|
log "restart: killing provider"
|
||||||
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
|
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
|
||||||
|
rm -f /data/local/tmp/virtualcam_disabled 2>/dev/null
|
||||||
sleep 2
|
sleep 2
|
||||||
mkdir -p "$LIB_DST" 2>/dev/null
|
mkdir -p "$LIB_DST" 2>/dev/null
|
||||||
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null
|
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null
|
||||||
@@ -95,13 +106,9 @@ case "${1:-help}" in
|
|||||||
get_status_json
|
get_status_json
|
||||||
log "restart: done"
|
log "restart: done"
|
||||||
;;
|
;;
|
||||||
stop)
|
|
||||||
log "stop: killing provider"
|
|
||||||
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
|
|
||||||
log "stop: done"
|
|
||||||
echo '{"status":"stopped"}'
|
|
||||||
;;
|
|
||||||
start_feeder)
|
start_feeder)
|
||||||
|
log "start_feeder: removing feeder disable flag"
|
||||||
|
rm -f /data/local/tmp/virtualcam_feeder_disabled 2>/dev/null
|
||||||
log "start_feeder: loading config"
|
log "start_feeder: loading config"
|
||||||
load_config
|
load_config
|
||||||
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
|
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
|
||||||
@@ -135,10 +142,11 @@ case "${1:-help}" in
|
|||||||
echo '{"status":"feeder_started"}'
|
echo '{"status":"feeder_started"}'
|
||||||
;;
|
;;
|
||||||
stop_feeder)
|
stop_feeder)
|
||||||
log "stop_feeder: killing ffmpeg"
|
log "stop_feeder: killing ffmpeg and setting feeder disable flag"
|
||||||
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
|
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
|
||||||
|
echo 1 > /data/local/tmp/virtualcam_feeder_disabled 2>/dev/null
|
||||||
log "stop_feeder: done"
|
log "stop_feeder: done"
|
||||||
echo '{"status":"feeder_stopped"}'
|
get_status_json
|
||||||
;;
|
;;
|
||||||
get_config)
|
get_config)
|
||||||
load_config
|
load_config
|
||||||
@@ -170,17 +178,7 @@ case "${1:-help}" in
|
|||||||
)\""
|
)\""
|
||||||
echo "}"
|
echo "}"
|
||||||
;;
|
;;
|
||||||
device_info)
|
|
||||||
dumpsys media.camera 2>/dev/null > /data/local/tmp/cam_dump.txt 2>/dev/null
|
|
||||||
F0=$(dumpsys media.camera 2>/dev/null | grep -A10 "virtual/0 (" | grep Facing | head -1)
|
|
||||||
F1=$(dumpsys media.camera 2>/dev/null | grep -A10 "virtual/1 (" | grep Facing | head -1)
|
|
||||||
L0=$(dumpsys media.camera 2>/dev/null | grep -A10 "virtual/0 (" | grep "FULL\|LEVEL" | head -1)
|
|
||||||
L1=$(dumpsys media.camera 2>/dev/null | grep -A10 "virtual/1 (" | grep "FULL\|LEVEL" | head -1)
|
|
||||||
echo "Camera0: Facing=$F0 Level=$L0"
|
|
||||||
echo "Camera1: Facing=$F1 Level=$L1"
|
|
||||||
echo "Full dump: /data/local/tmp/cam_dump.txt"
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 {status|start|stop|restart|start_feeder|stop_feeder|get_config|validate|device_info}"
|
echo "Usage: $0 {status|start|stop|restart|start_feeder|stop_feeder|get_config|validate}"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ cp "$MODDIR/vendor/bin/virtual_camera_provider" "$PROVIDER_DST" 2>/dev/null || t
|
|||||||
chmod 755 "$PROVIDER_DST" 2>/dev/null || true
|
chmod 755 "$PROVIDER_DST" 2>/dev/null || true
|
||||||
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null || true
|
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null || true
|
||||||
|
|
||||||
# Start provider in background (failure won't block boot)
|
# Start provider in background (always at boot, disable flag is for runtime only)
|
||||||
if [ -x "$PROVIDER_DST" ]; then
|
if [ -x "$PROVIDER_DST" ]; then
|
||||||
nohup env LD_LIBRARY_PATH="$LIB_DST" "$PROVIDER_DST" -d /dev/video0 -w 1920 -h 1080 -i virtual </dev/null >/dev/null 2>&1 &
|
nohup env LD_LIBRARY_PATH="$LIB_DST" "$PROVIDER_DST" -d /dev/video0 -w 1920 -h 1080 -i virtual </dev/null >/dev/null 2>&1 &
|
||||||
echo "[$(date)] provider started"
|
echo "[$(date)] provider started"
|
||||||
|
|||||||
+25
-22
@@ -4,6 +4,7 @@ PROVIDER_DST="/data/local/tmp/virtual_camera_provider"
|
|||||||
LIB_DST="/data/local/tmp/virtualcam_libs"
|
LIB_DST="/data/local/tmp/virtualcam_libs"
|
||||||
BOOT_LOG="$MODDIR/boot.log"
|
BOOT_LOG="$MODDIR/boot.log"
|
||||||
MODULE_PROP="$MODDIR/module.prop"
|
MODULE_PROP="$MODDIR/module.prop"
|
||||||
|
DISABLE_FLAG="/data/local/tmp/virtualcam_disabled"
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date)] $*" >> "$BOOT_LOG"
|
echo "[$(date)] $*" >> "$BOOT_LOG"
|
||||||
@@ -11,34 +12,35 @@ log() {
|
|||||||
|
|
||||||
log "service.sh starting"
|
log "service.sh starting"
|
||||||
|
|
||||||
|
# Clear any stale disable flag from a previous session
|
||||||
|
# This ensures the module always starts fresh after boot
|
||||||
|
rm -f /data/local/tmp/virtualcam_disabled
|
||||||
|
|
||||||
# Wait for boot to settle
|
# Wait for boot to settle
|
||||||
sleep 30
|
sleep 30
|
||||||
|
|
||||||
# Load v4l2loopback if not already loaded (safe here, boot is complete)
|
# Load v4l2loopback if not already loaded
|
||||||
if ! lsmod 2>/dev/null | grep -q v4l2loopback; then
|
if ! lsmod 2>/dev/null | grep -q v4l2loopback; then
|
||||||
log "service: loading v4l2loopback"
|
log "service: loading v4l2loopback"
|
||||||
insmod "$MODDIR/v4l2loopback.ko" devices=2 video_nr=0,1 card_label="VirtualCam" exclusive_caps=0 2>/dev/null && \
|
insmod "$MODDIR/v4l2loopback.ko" devices=2 video_nr=0,1 card_label="VirtualCam" exclusive_caps=0 2>/dev/null && \
|
||||||
log "service: v4l2loopback loaded" || log "service: v4l2loopback load failed"
|
log "service: v4l2loopback loaded" || log "service: v4l2loopback load failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Configure v4l2loopback for seamless loop transitions:
|
# Configure v4l2loopback
|
||||||
# sustain_framerate=1 repeats the last written frame when the writer pauses
|
sleep 2
|
||||||
# timeout=0 prevents kernel from injecting black timeout images
|
|
||||||
sleep 2 # Wait for device nodes to appear
|
|
||||||
if [ -c /dev/video0 ]; then
|
if [ -c /dev/video0 ]; then
|
||||||
v4l2-ctl -d /dev/video0 -c sustain_framerate=1 >/dev/null 2>&1 || true
|
v4l2-ctl -d /dev/video0 -c sustain_framerate=1 >/dev/null 2>&1 || true
|
||||||
v4l2-ctl -d /dev/video0 -c timeout=0 >/dev/null 2>&1 || true
|
v4l2-ctl -d /dev/video0 -c timeout=0 >/dev/null 2>&1 || true
|
||||||
log "service: v4l2loopback configured (sustain_framerate=1, timeout=0)"
|
log "service: v4l2loopback configured"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start feeder FIRST so /dev/video0 has a format set before provider starts
|
# Start feeder then provider at boot (unless disabled flag exists)
|
||||||
# This prevents mmap EACCES in V4L2 reader
|
if [ ! -f "$DISABLE_FLAG" ]; then
|
||||||
if [ -f "/data/local/tmp/virtualcam_config.json" ]; then
|
if [ -f "/data/local/tmp/virtualcam_config.json" ]; then
|
||||||
log "service: starting feeder before provider"
|
log "service: starting feeder before provider"
|
||||||
/system/bin/sh "$MODDIR/control.sh" start_feeder >/dev/null 2>&1
|
/system/bin/sh "$MODDIR/control.sh" start_feeder >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Now start provider (feeder already writing, so mmap will succeed)
|
|
||||||
if ! pgrep -f virtual_camera_provider >/dev/null 2>&1 && [ -x "$PROVIDER_DST" ]; then
|
if ! pgrep -f virtual_camera_provider >/dev/null 2>&1 && [ -x "$PROVIDER_DST" ]; then
|
||||||
log "service: starting provider"
|
log "service: starting provider"
|
||||||
nohup env LD_LIBRARY_PATH="$LIB_DST" "$PROVIDER_DST" \
|
nohup env LD_LIBRARY_PATH="$LIB_DST" "$PROVIDER_DST" \
|
||||||
@@ -46,6 +48,9 @@ if ! pgrep -f virtual_camera_provider >/dev/null 2>&1 && [ -x "$PROVIDER_DST" ];
|
|||||||
</dev/null >/dev/null 2>&1 &
|
</dev/null >/dev/null 2>&1 &
|
||||||
log "service: provider launched"
|
log "service: provider launched"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
log "service: disabled flag exists, skipping auto-start"
|
||||||
|
fi
|
||||||
|
|
||||||
# Periodic watchdog + description update
|
# Periodic watchdog + description update
|
||||||
while true; do
|
while true; do
|
||||||
@@ -54,7 +59,6 @@ while true; do
|
|||||||
V4L2=$(lsmod 2>/dev/null | grep -q v4l2loopback && echo 1 || echo 0)
|
V4L2=$(lsmod 2>/dev/null | grep -q v4l2loopback && echo 1 || echo 0)
|
||||||
SVC=$(service list 2>/dev/null | grep -q "virtual/0" && echo 1 || echo 0)
|
SVC=$(service list 2>/dev/null | grep -q "virtual/0" && echo 1 || echo 0)
|
||||||
|
|
||||||
# Determine status
|
|
||||||
if [ -n "$PROVIDER_PID" ] && [ "$SVC" = "1" ]; then
|
if [ -n "$PROVIDER_PID" ] && [ "$SVC" = "1" ]; then
|
||||||
STATUS="active"
|
STATUS="active"
|
||||||
elif [ -n "$PROVIDER_PID" ] || [ "$SVC" = "1" ]; then
|
elif [ -n "$PROVIDER_PID" ] || [ "$SVC" = "1" ]; then
|
||||||
@@ -78,21 +82,21 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restart provider if dead
|
# Only auto-restart if not disabled
|
||||||
if [ -z "$PROVIDER_PID" ] && [ -x "$PROVIDER_DST" ]; then
|
if [ ! -f "$DISABLE_FLAG" ] && [ ! -f "/data/local/tmp/virtualcam_feeder_disabled" ]; then
|
||||||
log "service: provider died, restarting"
|
|
||||||
nohup env LD_LIBRARY_PATH="$LIB_DST" "$PROVIDER_DST" \
|
|
||||||
-d /dev/video0 -w 1920 -h 1080 -i virtual \
|
|
||||||
</dev/null >/dev/null 2>&1 &
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Auto-start feeder if not running and config exists
|
|
||||||
if [ -z "$FEEDER_PID" ] && [ -f "/data/local/tmp/virtualcam_config.json" ]; then
|
if [ -z "$FEEDER_PID" ] && [ -f "/data/local/tmp/virtualcam_config.json" ]; then
|
||||||
log "service: auto-starting feeder"
|
log "service: auto-starting feeder"
|
||||||
/system/bin/sh "$MODDIR/control.sh" start_feeder >/dev/null 2>&1
|
/system/bin/sh "$MODDIR/control.sh" start_feeder >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Memory check: kill provider/feeder if RSS exceeds 512MB (safety limit)
|
if [ -z "$FEEDER_PID" ] && [ -f "/data/local/tmp/virtualcam_config.json" ]; then
|
||||||
|
log "service: auto-starting feeder"
|
||||||
|
/system/bin/sh "$MODDIR/control.sh" start_feeder >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Memory check
|
||||||
for pid in $PROVIDER_PID $FEEDER_PID; do
|
for pid in $PROVIDER_PID $FEEDER_PID; do
|
||||||
[ -z "$pid" ] && continue
|
[ -z "$pid" ] && continue
|
||||||
rss_kb=$(cat /proc/$pid/status 2>/dev/null | grep VmRSS | awk '{print $2}')
|
rss_kb=$(cat /proc/$pid/status 2>/dev/null | grep VmRSS | awk '{print $2}')
|
||||||
@@ -102,14 +106,13 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Check if stock HAL came back and kill it
|
# Kill stock HAL if it reappears
|
||||||
STOCK_PIDS=$(ps -A | grep -i "camera.provider" | grep -v "virtual_camera_provider" | awk '{print $1}')
|
STOCK_PIDS=$(ps -A | grep -i "camera.provider" | grep -v "virtual_camera_provider" | awk '{print $1}')
|
||||||
if [ -n "$STOCK_PIDS" ]; then
|
if [ -n "$STOCK_PIDS" ]; then
|
||||||
for pid in $STOCK_PIDS; do
|
for pid in $STOCK_PIDS; do
|
||||||
log "service: killing stock HAL PID $pid"
|
log "service: killing stock HAL PID $pid"
|
||||||
kill -9 "$pid" 2>/dev/null || true
|
kill -9 "$pid" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
# Try to stop the service via property if possible
|
|
||||||
setprop ctl.stop vendor.camera-provider-2-7-google 2>/dev/null || true
|
setprop ctl.stop vendor.camera-provider-2-7-google 2>/dev/null || true
|
||||||
setprop ctl.stop vendor.camera-provider-pixel 2>/dev/null || true
|
setprop ctl.stop vendor.camera-provider-pixel 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -19,6 +19,5 @@
|
|||||||
<button id="btn-unfeed" class="btn danger">Stop Feeder</button>
|
<button id="btn-unfeed" class="btn danger">Stop Feeder</button>
|
||||||
<button id="btn-refresh" class="btn">Refresh</button>
|
<button id="btn-refresh" class="btn">Refresh</button>
|
||||||
</div></div>
|
</div></div>
|
||||||
<div class="card"><div class="card-title">Device Info <button id="btn-deviceinfo" class="btn-sm">Refresh</button></div><div id="device-info" class="device-info"><p class="muted">Tap Refresh to load</p></div></div>
|
|
||||||
<div class="card"><div class="card-title">Log <span id="log-count" class="log-count"></span></div><div id="console" class="console"></div></div>
|
<div class="card"><div class="card-title">Log <span id="log-count" class="log-count"></span></div><div id="console" class="console"></div></div>
|
||||||
</div><script src="script.js"></script></body></html>
|
</div><script src="script.js"></script></body></html>
|
||||||
|
|||||||
+13
-24
@@ -4,45 +4,34 @@ function log(m,t){const e=document.getElementById('console');if(!e)return;const
|
|||||||
function run(a){try{ksu.exec(CMD+' '+a);log(a+' OK','ok')}catch(e){log(a+' error: '+e.message,'err')}updateDash()}
|
function run(a){try{ksu.exec(CMD+' '+a);log(a+' OK','ok')}catch(e){log(a+' error: '+e.message,'err')}updateDash()}
|
||||||
function updateDash(){try{
|
function updateDash(){try{
|
||||||
const r=typeof ksu!=='undefined'?ksu.exec(CMD+' status'):null;
|
const r=typeof ksu!=='undefined'?ksu.exec(CMD+' status'):null;
|
||||||
if(!r||typeof r!=='string'){document.getElementById('status-dash').innerHTML='<div class="stat" style="grid-column:1/3;color:#f85149">No data</div>';return}
|
if(!r||typeof r!=='string'){document.getElementById('status-dash').innerHTML='<div style="grid-column:1/3;color:#f85149;text-align:center;padding:12px">No data</div>';return}
|
||||||
const s=JSON.parse(r);
|
const s=JSON.parse(r);
|
||||||
const d=document.getElementById('status-dash');if(!d)return;
|
const d=document.getElementById('status-dash');if(!d)return;
|
||||||
const sc=s.status==='active'?'green':(s.status==='partial'?'yellow':'red');
|
const colorMap={active:'green',partial:'yellow',stopped:'red',idle:'red'};
|
||||||
const st=s.status==='active'?'ACTIVE':(s.status==='partial'?'PARTIAL':'STOPPED');
|
const sc=colorMap[s.status]||'yellow';
|
||||||
let v=[];
|
const st=s.status.toUpperCase();
|
||||||
if(s.camera2_level) v=[['camera2','green',s.camera2_level],['Checks',s.checks&&s.checks.includes('provider_ok')?'green':'red',s.checks||'?']];
|
d.innerHTML=[['Status',sc,st],['Provider',s.provider_pid>0?'green':'red',s.provider_pid>0?'PID '+s.provider_pid:'Stopped'],['Feeder',s.feeder_pid>0?'green':'red',s.feeder_pid>0?'PID '+s.feeder_pid:'Stopped'],['Service',s.service_registered==1?'green':'red',s.service_registered==1?'Registered':'Missing'],['v4l2',s.v4l2_loaded==1?'green':'red',s.v4l2_loaded==1?'Loaded':'Missing'],['Device','yellow',s.video0||'N/A']].map(i=>`<div class="stat"><span class="l">${i[0]}</span><span class="v ${i[1]}">${i[2]}</span></div>`).join('');
|
||||||
d.innerHTML=[['Status',sc,st],['Provider',s.provider_pid>0?'green':'red',s.provider_pid>0?'PID '+s.provider_pid:'Stopped'],['Feeder',s.feeder_pid>0?'green':'red',s.feeder_pid>0?'PID '+s.feeder_pid:'Stopped'],['Service',s.service_registered==1?'green':'red',s.service_registered==1?'Registered':'Missing'],['v4l2',s.v4l2_loaded==1?'green':'red',s.v4l2_loaded==1?'Loaded':'Missing'],['Device','yellow',s.video0||'N/A'],...v].map(i=>`<div class="stat"><span class="l">${i[0]}</span><span class="v ${i[1]}">${i[2]}</span></div>`).join('');
|
document.getElementById('subtitle').textContent=s.status==='active'?'Physical cameras replaced':s.status==='partial'?'Starting up...':'Not running';
|
||||||
document.getElementById('subtitle').textContent=s.status==='active'?'Feeding video to /dev/video0':'Physical cameras NOT replaced';
|
// Toggle buttons based on state
|
||||||
|
document.getElementById('btn-start').style.display=s.provider_pid>0?'none':'block';
|
||||||
|
document.getElementById('btn-stop').style.display=s.provider_pid>0?'block':'none';
|
||||||
|
document.getElementById('btn-restart').style.display=s.provider_pid>0?'block':'none';
|
||||||
|
document.getElementById('btn-feed').style.display=s.feeder_pid>0?'none':'block';
|
||||||
|
document.getElementById('btn-unfeed').style.display=s.feeder_pid>0?'block':'none';
|
||||||
if(s.status!==_ps||s.provider_pid!==_pp||s.feeder_pid!==_pf){_ps=s.status;_pp=s.provider_pid;_pf=s.feeder_pid;log('Status: '+st+' pid='+s.provider_pid+' feeder='+s.feeder_pid,'ok')}
|
if(s.status!==_ps||s.provider_pid!==_pp||s.feeder_pid!==_pf){_ps=s.status;_pp=s.provider_pid;_pf=s.feeder_pid;log('Status: '+st+' pid='+s.provider_pid+' feeder='+s.feeder_pid,'ok')}
|
||||||
}catch(e){log('Dash error: '+e.message,'err')}}
|
}catch(e){log('Dash error: '+e.message,'err')}}
|
||||||
function loadDeviceInfo(){try{
|
|
||||||
const r=ksu.exec(CMD+' device_info');
|
|
||||||
document.getElementById('device-info').innerHTML=r?'<pre style="background:#0d1117;border:1px solid #30363d;border-radius:4px;padding:8px;font-size:10px;overflow-x:auto;white-space:pre-wrap">'+r.replace(/</g,'<').replace(/>/g,'>')+'</pre>':'<p class="muted">No data</p>';
|
|
||||||
log('Device info loaded','ok');
|
|
||||||
}catch(e){log('DevInfo error: '+e.message,'err')}}
|
|
||||||
document.getElementById('btn-start')?.addEventListener('click',()=>run('start'));
|
document.getElementById('btn-start')?.addEventListener('click',()=>run('start'));
|
||||||
document.getElementById('btn-stop')?.addEventListener('click',()=>run('stop'));
|
document.getElementById('btn-stop')?.addEventListener('click',()=>run('stop'));
|
||||||
document.getElementById('btn-restart')?.addEventListener('click',()=>run('restart'));
|
document.getElementById('btn-restart')?.addEventListener('click',()=>run('restart'));
|
||||||
document.getElementById('btn-feed')?.addEventListener('click',()=>run('start_feeder'));
|
document.getElementById('btn-feed')?.addEventListener('click',()=>run('start_feeder'));
|
||||||
document.getElementById('btn-unfeed')?.addEventListener('click',()=>run('stop_feeder'));
|
document.getElementById('btn-unfeed')?.addEventListener('click',()=>run('stop_feeder'));
|
||||||
|
document.getElementById('btn-refresh')?.addEventListener('click',updateDash);
|
||||||
document.getElementById('btn-save')?.addEventListener('click',()=>{
|
document.getElementById('btn-save')?.addEventListener('click',()=>{
|
||||||
const src=document.querySelector('.src-btn.active')?.dataset.src||'mp4';
|
const src=document.querySelector('.src-btn.active')?.dataset.src||'mp4';
|
||||||
const json=JSON.stringify({source_type:src,rtsp_url:document.getElementById('rtsp-url').value,mp4_path:document.getElementById('mp4-path').value,res_w:parseInt(document.getElementById('res-w').value)||1920,res_h:parseInt(document.getElementById('res-h').value)||1080,fps:parseInt(document.getElementById('fps').value)||30,rotation:parseInt(document.getElementById('rotation').value)||0});
|
const json=JSON.stringify({source_type:src,rtsp_url:document.getElementById('rtsp-url').value,mp4_path:document.getElementById('mp4-path').value,res_w:parseInt(document.getElementById('res-w').value)||1920,res_h:parseInt(document.getElementById('res-h').value)||1080,fps:parseInt(document.getElementById('fps').value)||30,rotation:parseInt(document.getElementById('rotation').value)||0});
|
||||||
ksu.exec("echo '"+json+"' > /data/local/tmp/virtualcam_config.json");
|
ksu.exec("echo '"+json+"' > /data/local/tmp/virtualcam_config.json");
|
||||||
log('Config saved','ok');
|
log('Config saved','ok');
|
||||||
});
|
});
|
||||||
document.getElementById('btn-refresh')?.addEventListener('click',updateDash);
|
|
||||||
document.getElementById('btn-deviceinfo')?.addEventListener('click',loadDeviceInfo);
|
|
||||||
document.querySelectorAll('.src-btn').forEach(b=>b.addEventListener('click',()=>{
|
|
||||||
document.querySelectorAll('.src-btn').forEach(x=>x.classList.remove('active'));
|
|
||||||
b.classList.add('active');
|
|
||||||
document.getElementById('rtsp-config').classList.toggle('hidden',b.dataset.src!=='rtsp');
|
|
||||||
document.getElementById('mp4-config').classList.toggle('hidden',b.dataset.src!=='mp4');
|
|
||||||
}));
|
|
||||||
updateDash();
|
|
||||||
setInterval(updateDash,5000);
|
|
||||||
document.getElementById('btn-refresh')?.addEventListener('click',updateDash);
|
|
||||||
document.getElementById('btn-deviceinfo')?.addEventListener('click',loadDeviceInfo);
|
|
||||||
document.querySelectorAll('.src-btn').forEach(b=>b.addEventListener('click',()=>{
|
document.querySelectorAll('.src-btn').forEach(b=>b.addEventListener('click',()=>{
|
||||||
document.querySelectorAll('.src-btn').forEach(x=>x.classList.remove('active'));
|
document.querySelectorAll('.src-btn').forEach(x=>x.classList.remove('active'));
|
||||||
b.classList.add('active');
|
b.classList.add('active');
|
||||||
|
|||||||
Reference in New Issue
Block a user