Files
root-virtualcam/control.sh
T
2026-05-16 09:53:25 +02:00

187 lines
8.3 KiB
Bash
Executable File

#!/system/bin/sh
CONFIG_FILE="/data/local/tmp/virtualcam_config.json"
MODDIR=$(dirname "$0")
LIB_DST="/data/local/tmp/virtualcam_libs"
FFMPEG_BIN="/data/local/tmp/ffmpeg"
MODULE_PROP="$MODDIR/module.prop"
BOOT_LOG="$MODDIR/boot.log"
PROVIDER_DST="/data/local/tmp/virtual_camera_provider"
log() {
echo "[$(date)] $*" >> "$BOOT_LOG"
}
load_config() {
[ -f "$CONFIG_FILE" ] || return
SOURCE_TYPE=$(grep -o '"source_type":"[^"]*"' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
RTSP_URL=$(grep -o '"rtsp_url":"[^"]*"' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
MP4_PATH=$(grep -o '"mp4_path":"[^"]*"' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4)
RES_W=$(grep -o '"res_w":[0-9]*' "$CONFIG_FILE" 2>/dev/null | cut -d: -f2)
RES_H=$(grep -o '"res_h":[0-9]*' "$CONFIG_FILE" 2>/dev/null | cut -d: -f2)
FPS=$(grep -o '"fps":[0-9]*' "$CONFIG_FILE" 2>/dev/null | cut -d: -f2)
ROTATION=$(grep -o '"rotation":[0-9]*' "$CONFIG_FILE" 2>/dev/null | cut -d: -f2)
: ${SOURCE_TYPE:=mp4} ${RTSP_URL:=rtsp://192.168.1.100:8554/stream}
: ${MP4_PATH:=/sdcard/Movies/2.mp4} ${RES_W:=1920} ${RES_H:=1080} ${FPS:=30} ${ROTATION:=0}
}
update_description() {
local status="$1"
local desc
case "$status" in
active) desc="[ACTIVE] Virtual Camera - Physical cameras replaced" ;;
idle) desc="[IDLE] Virtual Camera - Ready to start" ;;
error) desc="[ERROR] Virtual Camera - Check config and logs" ;;
partial) desc="[PARTIAL] Virtual Camera - Starting up..." ;;
*) desc="Virtual Camera HAL3 - Replaces Pixel 9A cameras" ;;
esac
if [ -f "$MODULE_PROP" ]; then
sed -i "s/^description=.*/description=$desc/" "$MODULE_PROP"
fi
}
get_status_json() {
PROVIDER_PID=$(pgrep -f "/virtual_camera_provider" | head -1)
FEEDER_PID=$(pgrep -f "ffmpeg.*video0" | head -1)
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)
VIDEO=$(cat /sys/class/video4linux/video0/name 2>/dev/null || echo "not found")
CHECKS="provider_$([ -n "$PROVIDER_PID" ] && echo ok || echo missing),service_$([ "$SVC" = "1" ] && echo ok || echo missing),v4l2_$([ "$V4L2" = "1" ] && echo ok || echo missing),feeder_$([ -n "$FEEDER_PID" ] && echo ok || echo stopped)"
if [ "${PROVIDER_PID:-0}" -gt 0 ] && [ "${SVC:-0}" = "1" ]; then
STATUS="active"
elif [ "${PROVIDER_PID:-0}" -gt 0 ] || [ "${SVC:-0}" = "1" ]; then
STATUS="partial"
else
STATUS="idle"
fi
echo "{\"provider_pid\":${PROVIDER_PID:-0},\"feeder_pid\":${FEEDER_PID:-0},\"v4l2_loaded\":$V4L2,\"service_registered\":$SVC,\"video0\":\"$VIDEO\",\"status\":\"$STATUS\",\"camera2_level\":\"FULL\",\"checks\":\"$CHECKS\"}"
}
get_vf_rotation() {
local rot="${ROTATION:-0}"
case "$rot" in
90) echo "transpose=1," ;;
180) echo "transpose=1,transpose=1," ;;
270) echo "transpose=2," ;;
*) echo "" ;;
esac
}
case "${1:-help}" in
status)
get_status_json
;;
start)
log "start: killing old provider"
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
mkdir -p "$LIB_DST" 2>/dev/null
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null
log "start: launching provider"
env LD_LIBRARY_PATH="$LIB_DST" "$MODDIR/vendor/bin/virtual_camera_provider" \
-d /dev/video0 -w 1920 -h 1080 -i virtual &
sleep 2
get_status_json
log "start: done"
;;
restart)
log "restart: killing provider"
pkill -9 -f "/virtual_camera_provider" 2>/dev/null || true
sleep 2
mkdir -p "$LIB_DST" 2>/dev/null
cp "$MODDIR/vendor/lib64"/*.so "$LIB_DST/" 2>/dev/null
log "restart: launching provider"
nohup env LD_LIBRARY_PATH="$LIB_DST" "$MODDIR/vendor/bin/virtual_camera_provider" \
-d /dev/video0 -w 1920 -h 1080 -i virtual > /dev/null 2>&1 &
sleep 2
get_status_json
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)
log "start_feeder: loading config"
load_config
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
sleep 1
if command -v "$FFMPEG_BIN" >/dev/null 2>&1; then
FF="$FFMPEG_BIN"
elif command -v ffmpeg >/dev/null 2>&1; then
FF="ffmpeg"
else
log "start_feeder: ERROR ffmpeg not found"
echo '{"status":"error","message":"ffmpeg not found"}'
exit 1
fi
VFILTER=$(get_vf_rotation)
log "start_feeder: source=$SOURCE_TYPE, ff=$FF, rot=$ROTATION, ${RES_W}x${RES_H}"
if [ "$SOURCE_TYPE" = "rtsp" ]; then
nohup "$FF" -rtsp_transport tcp -i "$RTSP_URL" \
-vf "${VFILTER}scale=${RES_W}:${RES_H},fps=${FPS:-30}" \
-f v4l2 -pix_fmt yuv420p /dev/video0 > /dev/null 2>&1 &
elif [ "$SOURCE_TYPE" = "mp4" ] && [ -f "$MP4_PATH" ]; then
nohup "$FF" -stream_loop -1 -re -i "$MP4_PATH" \
-vf "${VFILTER}scale=${RES_W}:${RES_H},fps=${FPS:-30}" \
-f v4l2 -pix_fmt yuv420p /dev/video0 > /dev/null 2>&1 &
else
log "start_feeder: ERROR source=$SOURCE_TYPE path=$MP4_PATH not found"
echo '{"status":"error","message":"source not found or invalid"}'
exit 1
fi
sleep 1
log "start_feeder: done"
echo '{"status":"feeder_started"}'
;;
stop_feeder)
log "stop_feeder: killing ffmpeg"
pkill -9 -f "ffmpeg.*video0" 2>/dev/null || true
log "stop_feeder: done"
echo '{"status":"feeder_stopped"}'
;;
get_config)
load_config
echo "{\"source_type\":\"$SOURCE_TYPE\",\"rtsp_url\":\"$RTSP_URL\",\"mp4_path\":\"$MP4_PATH\",\"res_w\":${RES_W:-1920},\"res_h\":${RES_H:-1080},\"fps\":${FPS:-30},\"rotation\":${ROTATION:-0}}"
;;
validate)
PROVIDER_PID=$(pgrep -f "/virtual_camera_provider" | head -1)
FEEDER_PID=$(pgrep -f "ffmpeg.*video0" | head -1)
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)
VSTATE=$(cat /sys/devices/virtual/video4linux/video0/state 2>/dev/null || echo "unknown")
VFORMAT=$(cat /sys/devices/virtual/video4linux/video0/format 2>/dev/null || echo "unknown")
CAM_DUMP=$(dumpsys media.camera 2>/dev/null | grep -c "virtual/0" || true)
log "validate: provider=$PROVIDER_PID feeder=$FEEDER_PID v4l2=$V4L2 svc=$SVC state=$VSTATE fmt=$VFORMAT cam_count=$CAM_DUMP"
echo "{"
echo " \"provider_pid\":${PROVIDER_PID:-0},"
echo " \"feeder_pid\":${FEEDER_PID:-0},"
echo " \"v4l2_loaded\":$V4L2,"
echo " \"service_registered\":$SVC,"
echo " \"v4l2_state\":\"$VSTATE\","
echo " \"v4l2_format\":\"$VFORMAT\","
echo " \"camera2_devices\":$CAM_DUMP,"
echo " \"camera2_level\":\"FULL\","
echo " \"all_checks\":\"$( \
[ -n "$PROVIDER_PID" ] && echo -n provider_ok || echo -n provider_missing; \
[ "$SVC" = "1" ] && echo -n ,service_ok || echo -n ,service_missing; \
[ "$V4L2" = "1" ] && echo -n ,v4l2_ok || echo -n ,v4l2_missing; \
[ -n "$FEEDER_PID" ] && echo -n ,feeder_ok || echo -n ,feeder_stopped; \
)\""
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}"
;;
esac