Initial commit: phonestream project

This commit is contained in:
Nikolay
2026-07-03 16:08:02 +03:00
commit 7858eaeb2c
5 changed files with 920 additions and 0 deletions
Executable
+256
View File
@@ -0,0 +1,256 @@
#!/bin/bash
# PhoneStream — Redmi Note 9 Pro → виртуальная веб-камера через USB
# Использование: ./phonestream.sh [start|stop|status|menu]
V4L2_DEV="/dev/video20"
MODULE="v4l2loopback"
PHONE_ID="c3c2c293"
# Профили: описание|разрешение|fps|битрейт
PROFILES=(
"Максимум (5MP 4:3)|2608x1960|30|25M"
"Широкий (4MP 16:9)|2688x1512|30|20M"
"2.5K (5MP 4:3)|2592x1944|30|20M"
"Full HD (1080p)|1920x1080|30|10M"
"Full HD 60fps|1920x1080|60|15M"
"HD 120fps (slow-mo)|1280x720|120|10M"
"HD 240fps (slow-mo)|1280x720|240|10M"
)
DEFAULT_PROFILE=0 # Максимум по умолчанию
# ─── Цвета ───
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[✓]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
err() { echo -e "${RED}[✗]${NC} $1"; }
# ─── Проверка зависимостей ───
check_deps() {
local missing=()
for cmd in scrcpy adb v4l2-ctl; do
command -v "$cmd" &>/dev/null || missing+=("$cmd")
done
if [ ${#missing[@]} -gt 0 ]; then
err "Не найдены: ${missing[*]}"
echo " sudo apt install scrcpy v4l2loopback-dkms v4l2loopback-utils ffmpeg adb"
exit 1
fi
}
# ─── Загрузка v4l2loopback ───
load_module() {
if lsmod | grep -q "$MODULE"; then
log "v4l2loopback уже загружен"
return
fi
log "Загружаю v4l2loopback..."
sudo modprobe "$MODULE" devices=1 video_nr=20 card_label="PhoneCam" exclusive_caps=1
sleep 1
if [ -e "$V4L2_DEV" ]; then
log "Виртуальная камера $V4L2_DEV готова"
else
err "Не удалось создать $V4L2_DEV"
exit 1
fi
}
# ─── Выгрузка модуля ───
unload_module() {
if ! lsmod | grep -q "$MODULE"; then
log "v4l2loopback не загружен"
return
fi
# Сначала убить все scrcpy
pkill -f "scrcpy.*v4l2-sink" 2>/dev/null
sleep 1
sudo rmmod "$MODULE" 2>/dev/null
log "v4l2loopback выгружен"
}
# ─── Проверка телефона ───
check_phone() {
if ! adb devices 2>/dev/null | grep -q "$PHONE_ID"; then
err "Телефон не подключён по USB"
echo " 1. Подключи телефон по USB"
echo " 2. Включи отладку по USB (Настройки → Для разработчиков)"
echo " 3. Разреши отладку на телефоне"
exit 1
fi
log "Телефон подключён: $PHONE_ID"
}
# ─── Запуск стрима ───
start_stream() {
local idx=${1:-$DEFAULT_PROFILE}
local profile="${PROFILES[$idx]}"
local res=$(echo "$profile" | cut -d'|' -f2)
local fps=$(echo "$profile" | cut -d'|' -f3)
local bitrate=$(echo "$profile" | cut -d'|' -f4)
local name=$(echo "$profile" | cut -d'|' -f1)
# Проверяем не запущен ли уже
if pgrep -f "scrcpy.*v4l2-sink" >/dev/null 2>&1; then
warn "Стрим уже запущен. Останови сначала: $0 stop"
return
fi
check_deps
load_module
check_phone
# Закрываем камеру на телефоне
adb shell am force-stop com.android.camera 2>/dev/null
adb shell am force-stop com.android.camera2 2>/dev/null
adb shell am force-stop com.google.android.GoogleCamera 2>/dev/null
sleep 0.5
log "Запуск: $name (${res}@${fps}fps, ${bitrate}bps)"
local high_speed_flag=""
if [ "$fps" -gt 60 ]; then
high_speed_flag="--camera-high-speed"
fi
scrcpy \
--no-audio \
--no-playback \
--video-source=camera \
--camera-id=0 \
--camera-size="$res" \
--camera-fps="$fps" \
--video-bit-rate="$bitrate" \
--v4l2-sink="$V4L2_DEV" \
$high_speed_flag \
2>&1 &
local pid=$!
sleep 3
if kill -0 "$pid" 2>/dev/null; then
log "Стрим запущен (PID: $pid)"
log "Камера доступна: $V4L2_DEV"
echo ""
echo " Для остановки: $0 stop"
else
err "Не удалось запустить стрим"
wait "$pid" 2>/dev/null
exit 1
fi
}
# ─── Остановка ───
stop_stream() {
pkill -f "scrcpy.*v4l2-sink" 2>/dev/null
sleep 1
# Принудительно если не закрылся
pkill -9 -f "scrcpy.*v4l2-sink" 2>/dev/null
log "Стрим остановлен"
}
# ─── Статус ───
status() {
echo -e "${CYAN}═══ PhoneStream Status ═══${NC}"
echo ""
# v4l2loopback
if lsmod | grep -q "$MODULE"; then
echo -e " Модуль v4l2loopback: ${GREEN}загружен${NC}"
else
echo -e " Модуль v4l2loopback: ${RED}не загружен${NC}"
fi
# /dev/video20
if [ -e "$V4L2_DEV" ]; then
echo -e " Виртуальная камера: ${GREEN}$V4L2_DEV${NC}"
local info=$(v4l2-ctl -d "$V4L2_DEV" --all 2>/dev/null | grep "Width/Height" | head -1)
if [ -n "$info" ]; then
echo -e " Разрешение: ${GREEN}$info${NC}"
fi
else
echo -e " Виртуальная камера: ${RED}отсутствует${NC}"
fi
# Телефон
if adb devices 2>/dev/null | grep -q "$PHONE_ID"; then
echo -e " Телефон: ${GREEN}подключён${NC}"
else
echo -e " Телефон: ${RED}не подключён${NC}"
fi
# Процесс
local pid=$(pgrep -f "scrcpy.*v4l2-sink" 2>/dev/null | head -1)
if [ -n "$pid" ]; then
echo -e " Стрим: ${GREEN}активен (PID: $pid)${NC}"
else
echo -e " Стрим: ${RED}не активен${NC}"
fi
}
# ─── Меню выбора профиля ───
menu() {
echo -e "${CYAN}═══ PhoneStream — Выбери профиль ═══${NC}"
echo ""
for i in "${!PROFILES[@]}"; do
local name=$(echo "${PROFILES[$i]}" | cut -d'|' -f1)
local res=$(echo "${PROFILES[$i]}" | cut -d'|' -f2)
local fps=$(echo "${PROFILES[$i]}" | cut -d'|' -f3)
if [ "$i" -eq "$DEFAULT_PROFILE" ]; then
echo -e " ${GREEN}$i)${NC} $name ${YELLOW}[по умолчанию]${NC}"
else
echo -e " ${GREEN}$i)${NC} $name"
fi
done
echo ""
read -p " → Выбор [0]: " choice
choice=${choice:-$DEFAULT_PROFILE}
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -lt "${#PROFILES[@]}" ]; then
start_stream "$choice"
else
err "Неверный выбор"
exit 1
fi
}
# ─── Точка входа ───
case "${1:-start}" in
start)
start_stream "$2"
;;
stop)
stop_stream
;;
restart)
stop_stream
sleep 1
start_stream "$2"
;;
status)
status
;;
menu)
menu
;;
*)
echo "Использование: $0 {start|stop|restart|status|menu} [номер_профиля]"
echo ""
echo " Профили:"
for i in "${!PROFILES[@]}"; do
local name=$(echo "${PROFILES[$i]}" | cut -d'|' -f1)
echo " $i — $name"
done
echo ""
echo " Примеры:"
echo " $0 # Запуск с максимальным профилем"
echo " $0 start 3 # Запуск Full HD"
echo " $0 stop # Остановка"
echo " $0 menu # Интерактивный выбор"
exit 1
;;
esac