187 lines
8.3 KiB
Bash
Executable File
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
|