diff --git a/README.md b/README.md index a8fb72c..6333556 100644 --- a/README.md +++ b/README.md @@ -122,10 +122,22 @@ socat TCP4-LISTEN:9000,reuseaddr,fork /dev/ttyUSB0,raw,echo=0 Change ```/dev/ttyUSB0``` to whatever path your spotlight enumerated to if necessary. The camera will now be able to control the spotlight. +--- +To enable RTSP streaming, change the following lines, you can choose to enable or disable audio. + +``` +RTSP_ENABLED="false" +RTSP_ENABLE_AUDIO="false" +``` +the stream will be located at ```rtsp://IP_ADDRESS:8554/unicast``` + +Note: VLC seems to work fine for playback, ffmpeg and others have severe artifacts in the stream during playback. + --- ## Latest Updates +* 04-19-22: Add RTSP Server functionality * 04-17-22: Add remote spotlight accessory capability * 04-15-22: Enable USB Direct functionality. Allows you to connect camera using a USB cable to a device supporting CDC_NCM devices to get an internet connection, no USB Ethernet Adapter required. * 04-14-22: Fix kernel command line memory mappings, resolves stability issues diff --git a/SD_ROOT/wz_mini/bin/v4l2rtspserver b/SD_ROOT/wz_mini/bin/v4l2rtspserver new file mode 100755 index 0000000..481b22c Binary files /dev/null and b/SD_ROOT/wz_mini/bin/v4l2rtspserver differ diff --git a/SD_ROOT/wz_mini/etc/alsa.conf b/SD_ROOT/wz_mini/etc/alsa.conf new file mode 100644 index 0000000..f22918f --- /dev/null +++ b/SD_ROOT/wz_mini/etc/alsa.conf @@ -0,0 +1,634 @@ +# +# ALSA library configuration file +# + +# pre-load the configuration files + +@hooks [ + { + func load + files [ + { + @func concat + strings [ + { @func datadir } + "/alsa.conf.d/" + ] + } + "/etc/asound.conf" + "~/.asoundrc" + ] + errors false + } +] + +# load card-specific configuration files (on request) + +cards.@hooks [ + { + func load + files [ + { + @func concat + strings [ + { @func datadir } + "/cards/aliases.conf" + ] + } + ] + } + { + func load_for_all_cards + files [ + { + @func concat + strings [ + { @func datadir } + "/cards/" + { @func private_string } + ".conf" + ] + } + ] + errors false + } +] + +# +# defaults +# + +# show all name hints also for definitions without hint {} section +defaults.namehint.showall off +# show just basic name hints +defaults.namehint.basic on +# show extended name hints +defaults.namehint.extended off +# +defaults.ctl.card 0 +defaults.pcm.card 0 +defaults.pcm.device 0 +defaults.pcm.subdevice -1 +defaults.pcm.nonblock 1 +defaults.pcm.compat 0 +defaults.pcm.minperiodtime 5000 # in us +defaults.pcm.ipc_key 5678293 +defaults.pcm.ipc_gid audio +defaults.pcm.ipc_perm 0660 +defaults.pcm.dmix.max_periods 0 +defaults.pcm.dmix.rate 48000 +defaults.pcm.dmix.format "unchanged" +defaults.pcm.dmix.card defaults.pcm.card +defaults.pcm.dmix.device defaults.pcm.device +defaults.pcm.dsnoop.card defaults.pcm.card +defaults.pcm.dsnoop.device defaults.pcm.device +defaults.pcm.front.card defaults.pcm.card +defaults.pcm.front.device defaults.pcm.device +defaults.pcm.rear.card defaults.pcm.card +defaults.pcm.rear.device defaults.pcm.device +defaults.pcm.center_lfe.card defaults.pcm.card +defaults.pcm.center_lfe.device defaults.pcm.device +defaults.pcm.side.card defaults.pcm.card +defaults.pcm.side.device defaults.pcm.device +defaults.pcm.surround21.card defaults.pcm.card +defaults.pcm.surround21.device defaults.pcm.device +defaults.pcm.surround40.card defaults.pcm.card +defaults.pcm.surround40.device defaults.pcm.device +defaults.pcm.surround41.card defaults.pcm.card +defaults.pcm.surround41.device defaults.pcm.device +defaults.pcm.surround50.card defaults.pcm.card +defaults.pcm.surround50.device defaults.pcm.device +defaults.pcm.surround51.card defaults.pcm.card +defaults.pcm.surround51.device defaults.pcm.device +defaults.pcm.surround71.card defaults.pcm.card +defaults.pcm.surround71.device defaults.pcm.device +defaults.pcm.iec958.card defaults.pcm.card +defaults.pcm.iec958.device defaults.pcm.device +defaults.pcm.modem.card defaults.pcm.card +defaults.pcm.modem.device defaults.pcm.device +# truncate files via file or tee PCM +defaults.pcm.file_format "raw" +defaults.pcm.file_truncate true +defaults.rawmidi.card 0 +defaults.rawmidi.device 0 +defaults.rawmidi.subdevice -1 +defaults.hwdep.card 0 +defaults.hwdep.device 0 +defaults.timer.class 2 +defaults.timer.sclass 0 +defaults.timer.card 0 +defaults.timer.device 0 +defaults.timer.subdevice 0 + +# +# PCM interface +# + +# redirect to load-on-demand extended pcm definitions +pcm.cards cards.pcm + +pcm.default cards.pcm.default +pcm.sysdefault cards.pcm.default +pcm.front cards.pcm.front +pcm.rear cards.pcm.rear +pcm.center_lfe cards.pcm.center_lfe +pcm.side cards.pcm.side +pcm.surround21 cards.pcm.surround21 +pcm.surround40 cards.pcm.surround40 +pcm.surround41 cards.pcm.surround41 +pcm.surround50 cards.pcm.surround50 +pcm.surround51 cards.pcm.surround51 +pcm.surround71 cards.pcm.surround71 +pcm.iec958 cards.pcm.iec958 +pcm.spdif iec958 +pcm.hdmi cards.pcm.hdmi +pcm.dmix cards.pcm.dmix +pcm.dsnoop cards.pcm.dsnoop +pcm.modem cards.pcm.modem +pcm.phoneline cards.pcm.phoneline + +pcm.hw { + @args [ CARD DEV SUBDEV ] + @args.CARD { + type string + default { + @func getenv + vars [ + ALSA_PCM_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.pcm.card + } + } + } + @args.DEV { + type integer + default { + @func igetenv + vars [ + ALSA_PCM_DEVICE + ] + default { + @func refer + name defaults.pcm.device + } + } + } + @args.SUBDEV { + type integer + default { + @func refer + name defaults.pcm.subdevice + } + } + type hw + card $CARD + device $DEV + subdevice $SUBDEV + hint { + show { + @func refer + name defaults.namehint.extended + } + description "Direct hardware device without any conversions" + } +} + +pcm.plughw { + @args [ CARD DEV SUBDEV ] + @args.CARD { + type string + default { + @func getenv + vars [ + ALSA_PCM_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.pcm.card + } + } + } + @args.DEV { + type integer + default { + @func igetenv + vars [ + ALSA_PCM_DEVICE + ] + default { + @func refer + name defaults.pcm.device + } + } + } + @args.SUBDEV { + type integer + default { + @func refer + name defaults.pcm.subdevice + } + } + type plug + slave.pcm { + type hw + card $CARD + device $DEV + subdevice $SUBDEV + } + hint { + show { + @func refer + name defaults.namehint.extended + } + description "Hardware device with all software conversions" + } +} + +pcm.plug { + @args [ SLAVE ] + @args.SLAVE { + type string + } + type plug + slave.pcm $SLAVE +} + +pcm.shm { + @args [ SOCKET PCM ] + @args.SOCKET { + type string + } + @args.PCM { + type string + } + type shm + server $SOCKET + pcm $PCM +} + +pcm.tee { + @args [ SLAVE FILE FORMAT ] + @args.SLAVE { + type string + } + @args.FILE { + type string + } + @args.FORMAT { + type string + default { + @func refer + name defaults.pcm.file_format + } + } + type file + slave.pcm $SLAVE + file $FILE + format $FORMAT + truncate { + @func refer + name defaults.pcm.file_truncate + } +} + +pcm.file { + @args [ FILE FORMAT ] + @args.FILE { + type string + } + @args.FORMAT { + type string + default { + @func refer + name defaults.pcm.file_format + } + } + type file + slave.pcm null + file $FILE + format $FORMAT + truncate { + @func refer + name defaults.pcm.file_truncate + } +} + +pcm.null { + type null + hint { + show { + @func refer + name defaults.namehint.basic + } + description "Discard all samples (playback) or generate zero samples (capture)" + } +} + +# +# Control interface +# + +ctl.sysdefault { + type hw + card { + @func getenv + vars [ + ALSA_CTL_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.ctl.card + } + } + hint.description "Default control device" +} +ctl.default ctl.sysdefault + +ctl.hw { + @args [ CARD ] + @args.CARD { + type string + default { + @func getenv + vars [ + ALSA_CTL_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.ctl.card + } + } + } + type hw + card $CARD + hint.description "Direct control device" +} + +ctl.shm { + @args [ SOCKET CTL ] + @args.SOCKET { + type string + } + @args.CTL { + type string + } + type shm + server $SOCKET + ctl $CTL +} + +# +# RawMidi interface +# + +rawmidi.default { + type hw + card { + @func getenv + vars [ + ALSA_RAWMIDI_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.rawmidi.card + } + } + device { + @func igetenv + vars [ + ALSA_RAWMIDI_DEVICE + ] + default { + @func refer + name defaults.rawmidi.device + } + } + hint.description "Default raw MIDI device" +} + +rawmidi.hw { + @args [ CARD DEV SUBDEV ] + @args.CARD { + type string + default { + @func getenv + vars [ + ALSA_RAWMIDI_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.rawmidi.card + } + } + } + @args.DEV { + type integer + default { + @func igetenv + vars [ + ALSA_RAWMIDI_DEVICE + ] + default { + @func refer + name defaults.rawmidi.device + } + } + } + @args.SUBDEV { + type integer + default -1 + } + type hw + card $CARD + device $DEV + subdevice $SUBDEV + hint { + description "Direct rawmidi driver device" + device $DEV + } +} + +rawmidi.virtual { + @args [ MERGE ] + @args.MERGE { + type string + default 1 + } + type virtual + merge $MERGE +} + +# +# Sequencer interface +# + +seq.default { + type hw + hint.description "Default sequencer device" +} + +seq.hw { + type hw +} + +# +# HwDep interface +# + +hwdep.default { + type hw + card { + @func getenv + vars [ + ALSA_HWDEP_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.hwdep.card + } + } + device { + @func igetenv + vars [ + ALSA_HWDEP_DEVICE + ] + default { + @func refer + name defaults.hwdep.device + } + } + hint.description "Default hardware dependent device" +} + +hwdep.hw { + @args [ CARD DEV ] + @args.CARD { + type string + default { + @func getenv + vars [ + ALSA_HWDEP_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.hwdep.card + } + } + } + @args.DEV { + type integer + default { + @func igetenv + vars [ + ALSA_HWDEP_DEVICE + ] + default { + @func refer + name defaults.hwdep.device + } + } + } + type hw + card $CARD + device $DEV + hint { + description "Direct hardware dependent device" + device $DEV + } +} + +# +# Timer interface +# + +timer_query.default { + type hw +} + +timer_query.hw { + type hw +} + +timer.default { + type hw + class { + @func refer + name defaults.timer.class + } + sclass { + @func refer + name defaults.timer.sclass + } + card { + @func refer + name defaults.timer.card + } + device { + @func refer + name defaults.timer.device + } + subdevice { + @func refer + name defaults.timer.subdevice + } + hint.description "Default timer device" +} + +timer.hw { + @args [ CLASS SCLASS CARD DEV SUBDEV ] + @args.CLASS { + type integer + default { + @func refer + name defaults.timer.class + } + } + @args.SCLASS { + type integer + default { + @func refer + name defaults.timer.sclass + } + } + @args.CARD { + type string + default { + @func refer + name defaults.timer.card + } + } + @args.DEV { + type integer + default { + @func refer + name defaults.timer.device + } + } + @args.SUBDEV { + type integer + default { + @func refer + name defaults.timer.subdevice + } + } + type hw + class $CLASS + sclass $SCLASS + card $CARD + device $DEV + subdevice $SUBDEV + hint { + description "Direct timer device" + device $DEV + } +} diff --git a/SD_ROOT/wz_mini/etc/init.d/v3_init.sh b/SD_ROOT/wz_mini/etc/init.d/v3_init.sh index 40a8ae7..64fc4c1 100755 --- a/SD_ROOT/wz_mini/etc/init.d/v3_init.sh +++ b/SD_ROOT/wz_mini/etc/init.d/v3_init.sh @@ -16,11 +16,39 @@ echo ' set -x +echo "mounting tempfs for workspace" mount -t tmpfs /tmp -cp /opt/wz_mini/etc/shadow /tmp/.shadow -mount --bind /tmp/.shadow /etc/shadow +mount -t tmpfs /run + +echo "create workspace directory" +mkdir /run/.storage + +if [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_ENABLED\=") == "RTSP_ENABLED\=\"true\"" ]]; then +cp /etc/init.d/rcS /run/.storage/rcS +sed -i '/^".*/aset -x' /run/.storage/rcS +sed -i '/^# Mount configs.*/i cp /system/bin/iCamera /run/.storage/\nmount -o ro,bind /opt/wz_mini/usr/bin/iCamera /system/bin/iCamera\n tail -f /system/bin/iCamera > /dev/null 2>&1 &' /run/.storage/rcS +sed -i '/sbin:/s/$/:\/opt\/wz_mini\/bin/' /run/.storage/rcS +sed -i '/system\/\lib/s/$/:\/opt\/wz_mini\/lib/' /run/.storage/rcS +mount --bind /run/.storage/rcS /etc/init.d/rcS +echo "load video loopback driver at video1" +insmod /opt/wz_mini/lib/modules/v4l2loopback.ko video_nr=1 +fi + +if [[ $(cat /opt/wz_mini/run_mmc.sh | grep "DEBUG_ENABLED\=") == "DEBUG_ENABLED\=\"true\"" ]]; then +cp /etc/init.d/rcS /run/.storage/rcS +sed -i '/app_init.sh/,+2d' /run/.storage/rcS +sed -i '/^# Run init/i/bin/sh /etc/profile' /run/.storage/rcS +mount --bind /run/.storage/rcS /etc/init.d/rcS +fi + +echo "replace stock password" +cp /opt/wz_mini/etc/shadow /run/.storage/shadow +mount --bind /run/.storage/shadow /etc/shadow chmod 400 /etc/shadow +echo "bind /etc/profile for local/ssh shells" +mount --bind /opt/wz_mini/etc/profile /etc/profile + if [[ -f /opt/wz_mini/swap.gz ]]; then echo "swap archive present, extracting" gzip -d /opt/wz_mini/swap.gz @@ -30,6 +58,21 @@ else echo "swap archive not present, not extracting" fi +echo "mount configs partition for dropbear" +mount -t jffs2 /dev/mtdblock6 /configs + +if [[ -d /configs/.ssh ]]; then + echo "dropbear ssh config dir present" + umount /configs +else + echo "dropbear ssh config dir not present, creating" + mkdir /configs/.ssh + umount /configs +fi + +echo "Run dropbear ssh server" +/opt/wz_mini/bin/dropbearmulti dropbear -R -m + { sleep 30; /media/mmc/wz_mini/run_mmc.sh 2> /media/mmc/wz_mini/log/wz_mini_hacks.log; } & /linuxrc diff --git a/SD_ROOT/wz_mini/etc/profile b/SD_ROOT/wz_mini/etc/profile index f53a70b..d70d891 100644 --- a/SD_ROOT/wz_mini/etc/profile +++ b/SD_ROOT/wz_mini/etc/profile @@ -8,9 +8,9 @@ if [ "$PS1" ]; then fi # Set terminal env -export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/media/mmc/wz_mini/bin +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/opt/wz_mini/bin export PATH=/system/bin:$PATH -export LD_LIBRARY_PATH=/system/lib:/media/mmc/wz_mini/lib +export LD_LIBRARY_PATH=/system/lib:/opt/wz_mini/lib export LD_LIBRARY_PATH=/thirdlib:$LD_LIBRARY_PATH #export TZ=UTC-8 diff --git a/SD_ROOT/wz_mini/lib/libasound.so.2 b/SD_ROOT/wz_mini/lib/libasound.so.2 new file mode 100755 index 0000000..a70201c Binary files /dev/null and b/SD_ROOT/wz_mini/lib/libasound.so.2 differ diff --git a/SD_ROOT/wz_mini/lib/libcallback.so b/SD_ROOT/wz_mini/lib/libcallback.so new file mode 100644 index 0000000..f4048b6 Binary files /dev/null and b/SD_ROOT/wz_mini/lib/libcallback.so differ diff --git a/SD_ROOT/wz_mini/lib/libtinyalsa.so.2.0.0 b/SD_ROOT/wz_mini/lib/libtinyalsa.so.2.0.0 new file mode 100644 index 0000000..00ed1d4 Binary files /dev/null and b/SD_ROOT/wz_mini/lib/libtinyalsa.so.2.0.0 differ diff --git a/SD_ROOT/wz_mini/lib/modules/v4l2loopback.ko b/SD_ROOT/wz_mini/lib/modules/v4l2loopback.ko new file mode 100644 index 0000000..a693adf Binary files /dev/null and b/SD_ROOT/wz_mini/lib/modules/v4l2loopback.ko differ diff --git a/SD_ROOT/wz_mini/run_mmc.sh b/SD_ROOT/wz_mini/run_mmc.sh index d24d0d8..9b1b22b 100755 --- a/SD_ROOT/wz_mini/run_mmc.sh +++ b/SD_ROOT/wz_mini/run_mmc.sh @@ -15,8 +15,26 @@ USB_DIRECT_MAC_ADDR="02:01:02:03:04:08" REMOTE_SPOTLIGHT="false" REMOTE_SPOTLIGHT_HOST="0.0.0.0" +RTSP_ENABLED="false" +RTSP_ENABLE_AUDIO="false" + +DEBUG_ENABLED="false" + +##################################### +##########CONFIG END################# +##################################### + echo "run_mmc.sh start" > /dev/kmsg +swap_enable() { + if [[ -f /media/mmc/wz_mini/swap ]]; then + echo "swap exists, enable" + swapon /media/mmc/wz_mini/swap + else + echo "swap missing, system stability with usb potentially comprimised" + fi +} + if [[ -d /configs/.ssh ]]; then echo "dropbear ssh config dir present" else @@ -26,12 +44,7 @@ fi if [[ "$ENABLE_USB_ETH" == "true" ]]; then - if [[ -f /media/mmc/wz_mini/swap ]]; then - echo "swap exists, enable" - swapon /media/mmc/wz_mini/swap - else - echo "swap missing, system stability with usb potentially comprimised" - fi + swap_enable ifconfig eth0 down ifconfig wlan0 down @@ -49,9 +62,7 @@ if [[ "$ENABLE_USB_ETH" == "true" ]]; then echo "usb ethernet disabled" fi - if [[ "$ENABLE_USB_DIRECT" == "true" ]]; then -# if [[ ! -d /sys/class/net/usb0* ]]; then ##ONLY WORKS WITH g_ethernet enabled kernel ifconfig usb0 down ifconfig wlan0 down @@ -72,8 +83,8 @@ fi if [[ "$DISABLE_FW_UPGRADE" == "true" ]]; then mkdir /tmp/Upgrade mount -t tmpfs -o size=1,nr_inodes=1 none /tmp/Upgrade - echo -e "127.0.0.1 localhost \n127.0.0.1 wyze-upgrade-service.wyzecam.com" > /tmp/.hosts_wz - mount --bind /tmp/.hosts_wz /etc/hosts + echo -e "127.0.0.1 localhost \n127.0.0.1 wyze-upgrade-service.wyzecam.com" > /run/.storage/hosts_wz + mount --bind /run/.storage/hosts_wz /etc/hosts fi if [[ "$REMOTE_SPOTLIGHT" == "true" ]]; then @@ -81,18 +92,34 @@ if [[ "$REMOTE_SPOTLIGHT" == "true" ]]; then fi +if [[ "$RTSP_ENABLED" == "true" ]]; then + swap_enable + mkdir /tmp/alsa + cp /media/mmc/wz_mini/etc/alsa.conf /tmp/alsa + + if [[ "$RTSP_ENABLE_AUDIO" == "true" ]]; then + LD_LIBRARY_PATH=/media/mmc/wz_mini/lib /media/mmc/wz_mini/bin/v4l2rtspserver -C 1 -a S16_LE /dev/video1,hw:Loopback,0 & + else + echo "rtsp audio disabled" + LD_LIBRARY_PATH=/media/mmc/wz_mini/lib /media/mmc/wz_mini/bin/v4l2rtspserver -s /dev/video1 & + fi + else + echo "rtsp disabled" +fi + echo "set hostname" hostname $HOSTNAME -echo "bind /etc/profile for local shell" -mount --bind /media/mmc/wz_mini/etc/profile /etc/profile - -echo "Run dropbear ssh server" -/media/mmc/wz_mini/bin/dropbearmulti dropbear -R -m +echo "clean up tail" +pkill tail sleep 3 +################################################# +##############CUSTOM BEGIN####################### +################################################# + #Place commands here to run 30 seconds after boot #such as mount nfs, ping, etc -#mount -t nfs -o nolock,rw,noatime,nodiratime 192.168.1.1:/volume1 /media/mmc/record +#mount -t nfs -o nolock,rw,noatime,nodiratime 192.168.1.1:/volume1 /media/mmc/record & diff --git a/SD_ROOT/wz_mini/usr/bin/iCamera b/SD_ROOT/wz_mini/usr/bin/iCamera new file mode 100644 index 0000000..a2e68f6 --- /dev/null +++ b/SD_ROOT/wz_mini/usr/bin/iCamera @@ -0,0 +1,5 @@ +#!/bin/sh + +set -x +echo "inject callback library and run" +LD_PRELOAD='libcallback.so:libtinyalsa.so.2.0.0:libsetunbuf.so' /run/.storage/iCamera