diff --git a/README.md b/README.md index f3c22e9..b6c3eb3 100644 --- a/README.md +++ b/README.md @@ -183,23 +183,31 @@ ENABLE_NFSv4="true" --- RTSP streaming: -You can choose to enable or disable audio. Set your login credentials here, server listening port, and the stream bitrate. -(RTSP_ENC_PARAMETER variable accepts numbers only. 0=FIXQP, 1=CBR, 2=VBR, 4=CAPPED VBR, 8=CAPPED QUALITY. Currently only 2, 4, and 8 are working) +The RTSP server outputs the two supported video streams from the camera, 1080p/360p (1296p/480p for the DB3). You can choose to enable a single stream of your choice, or both. Audio is also available. Set your login credentials here, server listening port, and the stream bitrate. +(ENC_PARAMETER variable accepts numbers only. 0=FIXQP, 1=CBR, 2=VBR, 4=CAPPED VBR, 8=CAPPED QUALITY. Currently only 2, 4, and 8 are working) ``` -RTSP_ENABLED="true" -RTSP_ENABLE_AUDIO="true" RTSP_LOGIN="admin" RTSP_PASSWORD="" -RTSP_PORT="8554" -RTSP_MAX_BITRATE="2048" -RTSP_TARGET_BITRATE="1024" -RTSP_ENC_PARAMETER="2" + +RTSP_HI_RES_ENABLED="true" +RTSP_HI_RES_ENABLE_AUDIO="true" +RTSP_HI_RES_PORT="8554" +RTSP_HI_RES_MAX_BITRATE="2048" +RTSP_HI_RES_TARGET_BITRATE="1024" +RTSP_HI_RES_ENC_PARAMETER="2" + +RTSP_LOW_RES_ENABLED="false" +RTSP_LOW_RES_ENABLE_AUDIO="false" +RTSP_LOW_RES_PORT="8555" +RTSP_LOW_RES_MAX_BITRATE="" +RTSP_LOW_RES_TARGET_BITRATE="" +RTSP_LOW_RES_ENC_PARAMETER="" ``` the stream will be located at ```rtsp://login:password@IP_ADDRESS:8554/unicast``` -Notes: If you don't set the password, then the password will be the unique MAC address of the camera, in all uppercase, including the colons... for example:. AA:BB:CC:00:11:22. It's typically printed on the camera. VLC seems to work fine for playback, ffmpeg and others have severe artifacts in the stream during playback. +Notes: If you don't set the password, then the password will be the unique MAC address of the camera, in all uppercase, including the colons... for example:. AA:BB:CC:00:11:22. It's typically printed on the camera. Higher video bitrates may overload your Wi-Fi connection, so a wired connection is recommended. Huge credit to @mnakada for his libcallback library: [https://github.com/mnakada/atomcam_tools](https://github.com/mnakada/atomcam_tools) @@ -207,6 +215,7 @@ Huge credit to @mnakada for his libcallback library: [https://github.com/mnakada ## Latest Updates +* 05-15-22: patched libcallback to support both video streams from the camera. Added support for them in run_mmc.sh. * 05-14-22: Added ability to specify RTSP bitrate parameters. Note that changing bitrate in the mobile app will briefly reset the bitrate for the RTSP stream. * 05-14-22: Update v4l2rtspserver, tinyalsa, alsa-lib. Patch busybox for older official FW's failing to run scripts, fix choppy/static audio in libcallback * 05-09-22: fix bug in run_mmc.sh that did not store the wlan mac when using a wired usb or ethernet connection for the rtsp server diff --git a/SD_ROOT/wz_mini/etc/init.d/v3_post.sh b/SD_ROOT/wz_mini/etc/init.d/v3_post.sh index 6eebc52..57272f2 100755 --- a/SD_ROOT/wz_mini/etc/init.d/v3_post.sh +++ b/SD_ROOT/wz_mini/etc/init.d/v3_post.sh @@ -11,11 +11,21 @@ set -x echo "v3_post.sh exec" -if [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_ENABLED\=") == "RTSP_ENABLED\=\"true\"" ]] && ! [[ -e /tmp/dbgflag ]]; then + +if [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_HI_RES_ENABLED\=") == "RTSP_HI_RES_ENABLED\=\"true\"" ]] || [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_LOW_RES_ENABLED\=") == "RTSP_LOW_RES_ENABLED\=\"true\"" ]] && ! [[ -e /tmp/dbgflag ]]; then + if [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_LOW_RES_ENABLED\=") == "RTSP_LOW_RES_ENABLED\=\"true\"" ]] && [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_HI_RES_ENABLED\=") == "RTSP_HI_RES_ENABLED\=\"true\"" ]]; then + echo "load video loopback driver at video1 video2" + insmod /opt/wz_mini/lib/modules/v4l2loopback.ko video_nr=1,2 + elif [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_LOW_RES_ENABLED\=") == "RTSP_LOW_RES_ENABLED\=\"true\"" ]]; then + echo "load video loopback driver at video2" + insmod /opt/wz_mini/lib/modules/v4l2loopback.ko video_nr=2 + elif [[ $(cat /opt/wz_mini/run_mmc.sh | grep "RTSP_HI_RES_ENABLED\=") == "RTSP_HI_RES_ENABLED\=\"true\"" ]]; then + echo "load video loopback driver at video1" + insmod /opt/wz_mini/lib/modules/v4l2loopback.ko video_nr=1 + fi + cp /system/bin/iCamera /opt/wz_mini/tmp/.storage/ mount -o ro,bind /opt/wz_mini/usr/bin/iCamera /system/bin/iCamera - echo "load video loopback driver at video1" - insmod /opt/wz_mini/lib/modules/v4l2loopback.ko video_nr=1 fi ##LIBRARY DEBUG diff --git a/SD_ROOT/wz_mini/lib/libcallback.so b/SD_ROOT/wz_mini/lib/libcallback.so index 5075b1e..d7b110a 100644 Binary files a/SD_ROOT/wz_mini/lib/libcallback.so and b/SD_ROOT/wz_mini/lib/libcallback.so differ diff --git a/SD_ROOT/wz_mini/run_mmc.sh b/SD_ROOT/wz_mini/run_mmc.sh index e92a20c..599e338 100755 --- a/SD_ROOT/wz_mini/run_mmc.sh +++ b/SD_ROOT/wz_mini/run_mmc.sh @@ -30,14 +30,22 @@ REMOTE_SPOTLIGHT="false" REMOTE_SPOTLIGHT_HOST="0.0.0.0" #####VIDEO STREAM##### -RTSP_ENABLED="false" -RTSP_ENABLE_AUDIO="false" RTSP_LOGIN="admin" RTSP_PASSWORD="" -RTSP_PORT="8554" -RTSP_MAX_BITRATE="" -RTSP_TARGET_BITRATE="" -RTSP_ENC_PARAMETER="" + +RTSP_HI_RES_ENABLED="false" +RTSP_HI_RES_ENABLE_AUDIO="false" +RTSP_HI_RES_PORT="8554" +RTSP_HI_RES_MAX_BITRATE="" +RTSP_HI_RES_TARGET_BITRATE="" +RTSP_HI_RES_ENC_PARAMETER="" + +RTSP_LOW_RES_ENABLED="false" +RTSP_LOW_RES_ENABLE_AUDIO="false" +RTSP_LOW_RES_PORT="8555" +RTSP_LOW_RES_MAX_BITRATE="" +RTSP_LOW_RES_TARGET_BITRATE="" +RTSP_LOW_RES_ENC_PARAMETER="" #####GENERAL##### ENABLE_SWAP="true" @@ -345,7 +353,7 @@ else echo "remote accessory disabled" fi -if [[ "$RTSP_ENABLED" == "true" ]]; then +if [[ "$RTSP_HI_RES_ENABLED" == "true" ]]; then if [[ "$ENABLE_SWAP" == "true" ]]; then echo "swap already enabled" @@ -361,23 +369,22 @@ if [[ "$RTSP_ENABLED" == "true" ]]; then RTSP_PASSWORD=$(cat /opt/wz_mini/tmp/wlan0_mac) fi - 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 -U $RTSP_LOGIN:$RTSP_PASSWORD -P $RTSP_PORT & + if [[ "$RTSP_HI_RES_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 -U $RTSP_LOGIN:$RTSP_PASSWORD -P $RTSP_HI_RES_PORT & else echo "rtsp audio disabled" - LD_LIBRARY_PATH=/media/mmc/wz_mini/lib /media/mmc/wz_mini/bin/v4l2rtspserver -s /dev/video1 -U $RTSP_LOGIN:$RTSP_PASSWORD -P $RTSP_PORT & + LD_LIBRARY_PATH=/media/mmc/wz_mini/lib /media/mmc/wz_mini/bin/v4l2rtspserver -s /dev/video1 -U $RTSP_LOGIN:$RTSP_PASSWORD -P $RTSP_HI_RES_PORT & fi - - if [[ "$RTSP_ENC_PARAMETER" != "" ]]; then + if [[ "$RTSP_HI_RES_ENC_PARAMETER" != "" ]]; then watch -n5 -t "/system/bin/impdbg --enc_rc_s 0:44:4:$RTSP_ENC_PARAMETER" > /dev/null 2>&1 & fi - if [[ "$RTSP_MAX_BITRATE" != "" ]]; then + if [[ "$RTSP_HI_RES_MAX_BITRATE" != "" ]]; then watch -n5 -t "/system/bin/impdbg --enc_rc_s 0:48:4:$RTSP_MAX_BITRATE" > /dev/null 2>&1 & fi - if [[ "$RTSP_TARGET_BITRATE" != "" ]]; then + if [[ "$RTSP_HI_RES_TARGET_BITRATE" != "" ]]; then watch -n5 -t "/system/bin/impdbg --enc_rc_s 0:52:4:$RTSP_TARGET_BITRATE" > /dev/null 2>&1 & fi @@ -386,6 +393,46 @@ if [[ "$RTSP_ENABLED" == "true" ]]; then fi +if [[ "$RTSP_LOW_RES_ENABLED" == "true" ]]; then + + if [[ "$ENABLE_SWAP" == "true" ]]; then + echo "swap already enabled" + else + swap_enable + fi + + /opt/wz_mini/bin/cmd video on1 + /opt/wz_mini/bin/cmd audio on + + + if [[ "$RTSP_PASSWORD" = "" ]]; then + RTSP_PASSWORD=$(cat /opt/wz_mini/tmp/wlan0_mac) + fi + + if [[ "$RTSP_LOW_RES_ENABLE_AUDIO" == "true" ]]; then + LD_LIBRARY_PATH=/media/mmc/wz_mini/lib /media/mmc/wz_mini/bin/v4l2rtspserver -C 1 -a S16_LE /dev/video2,hw:Loopback,0 -U $RTSP_LOGIN:$RTSP_PASSWORD -P $RTSP_LOW_RES_PORT & + else + echo "rtsp audio disabled" + LD_LIBRARY_PATH=/media/mmc/wz_mini/lib /media/mmc/wz_mini/bin/v4l2rtspserver -s /dev/video2 -U $RTSP_LOGIN:$RTSP_PASSWORD -P $RTSP_LOW_RES_PORT & + fi + + if [[ "$RTSP_LOW_RES_ENC_PARAMETER" != "" ]]; then + watch -n5 -t "/system/bin/impdbg --enc_rc_s 1:44:4:$RTSP_ENC_PARAMETER" > /dev/null 2>&1 & + fi + + if [[ "$RTSP_LOW_RES_MAX_BITRATE" != "" ]]; then + watch -n5 -t "/system/bin/impdbg --enc_rc_s 1:48:4:$RTSP_MAX_BITRATE" > /dev/null 2>&1 & + fi + + if [[ "$RTSP_LOW_RES_TARGET_BITRATE" != "" ]]; then + watch -n5 -t "/system/bin/impdbg --enc_rc_s 1:52:4:$RTSP_TARGET_BITRATE" > /dev/null 2>&1 & + fi + + else + echo "rtsp disabled" + +fi + touch /opt/wz_mini/tmp/.run_mmc_firstrun sync;echo 3 > /proc/sys/vm/drop_caches sleep 3 diff --git a/libcallback_wz_mod/libcallback.so b/libcallback_wz_mod/libcallback.so index 5075b1e..d7b110a 100755 Binary files a/libcallback_wz_mod/libcallback.so and b/libcallback_wz_mod/libcallback.so differ diff --git a/libcallback_wz_mod/video_callback.c b/libcallback_wz_mod/video_callback.c index 3532afa..53e665f 100644 --- a/libcallback_wz_mod/video_callback.c +++ b/libcallback_wz_mod/video_callback.c @@ -19,7 +19,9 @@ typedef int (* framecb)(struct frames_st *); static int (*real_local_sdk_video_set_encode_frame_callback)(int ch, void *callback); static void *video_encode_cb = NULL; +static void *video_encode_cb1 = NULL; static int VideoCaptureEnable = 0; +static int VideoCaptureEnable1 = 0; char *VideoCapture(int fd, char *tokenPtr) { @@ -27,12 +29,22 @@ char *VideoCapture(int fd, char *tokenPtr) { if(!p) return VideoCaptureEnable ? "on" : "off"; if(!strcmp(p, "on")) { VideoCaptureEnable = 1; - fprintf(stderr, "[command] video capute on\n", p); + fprintf(stderr, "[command] video capture ch0 on\n", p); + return "ok"; + } + if(!strcmp(p, "on1")) { + VideoCaptureEnable1 = 1; + fprintf(stderr, "[command] video capture ch1 on\n", p); return "ok"; } if(!strcmp(p, "off")) { VideoCaptureEnable = 0; - fprintf(stderr, "[command] video capute off\n", p); + fprintf(stderr, "[command] video capture ch0 off\n", p); + return "ok"; + } + if(!strcmp(p, "off1")) { + VideoCaptureEnable1 = 0; + fprintf(stderr, "[command] video capture ch1 off\n", p); return "ok"; } return "error"; @@ -43,6 +55,7 @@ static uint32_t video_encode_capture(struct frames_st *frames) { static int firstEntry = 0; static int v4l2Fd = -1; +//primary stream 0 if(!firstEntry) { firstEntry++; int err; @@ -86,19 +99,78 @@ static uint32_t video_encode_capture(struct frames_st *frames) { return ((framecb)video_encode_cb)(frames); } +//secondary stream 1 +static uint32_t video_encode_capture1(struct frames_st *frames) { + + static int firstEntry = 0; + static int v4l2Fd = -1; + + if(!firstEntry) { + firstEntry++; + int err; + const char *v4l2_device_path = "/dev/video2"; + const char *productf="/configs/.product_db3"; + fprintf(stderr,"Opening V4L2 device: %s \n", v4l2_device_path); + v4l2Fd = open(v4l2_device_path, O_WRONLY, 0777); + if(v4l2Fd < 0) fprintf(stderr,"Failed to open V4L2 device: %s\n", v4l2_device_path); + struct v4l2_format vid_format; + memset(&vid_format, 0, sizeof(vid_format)); + vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + if( access( productf, F_OK ) == 0 ) { + /* doorbell resolution */ + printf("[command] video product 640x480"); + vid_format.fmt.pix.width = 640; + vid_format.fmt.pix.height = 480; + } else { + /* v3 and panv2 res */ + printf("[command] video product 640x320"); + vid_format.fmt.pix.width = 640; + vid_format.fmt.pix.height = 320; + } + + vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + vid_format.fmt.pix.sizeimage = 0; + vid_format.fmt.pix.field = V4L2_FIELD_NONE; + vid_format.fmt.pix.bytesperline = 0; + vid_format.fmt.pix.colorspace = V4L2_PIX_FMT_YUV420; + err = ioctl(v4l2Fd, VIDIOC_S_FMT, &vid_format); + if(err < 0) fprintf(stderr,"Unable to set V4L2 device video format: %d\n", err); + err = ioctl(v4l2Fd, VIDIOC_STREAMON, &vid_format); + if(err < 0) fprintf(stderr,"Unable to perform VIDIOC_STREAMON: %d\n", err); + } + + if( (v4l2Fd >= 0) && VideoCaptureEnable) { + uint32_t *buf = frames->buf; + int size = write(v4l2Fd, frames->buf, frames->length); + if(size != frames->length) fprintf(stderr,"Stream write error: %s\n", size); + } + return ((framecb)video_encode_cb1)(frames); +} + + + int local_sdk_video_set_encode_frame_callback(int ch, void *callback) { fprintf(stderr, "local_sdk_video_set_encode_frame_callback streamChId=%d, callback=0x%x\n", ch, callback); static int ch_count = 0; -/* two callbacks typically detected, unknown what the difference is between them, but if they are both hooked, the app breaks. grab just one of them. */ - +/* two callbacks for video stream 0 are typically detected, unknown what the difference is between them, but if they are both hooked, the app breaks. grab just one of them. */ + //stream 0 if( (ch == 0) && ch_count == 2) { video_encode_cb = callback; fprintf(stderr,"enc func injection save video_encode_cb=0x%x\n", video_encode_cb); callback = video_encode_capture; } fprintf(stderr,"ch count is %x\n", ch_count); + + //stream 1 + if( (ch == 1) && ch_count == 1) { + video_encode_cb1 = callback; + fprintf(stderr,"enc func injection save video_encode_cb=0x%x\n", video_encode_cb1); + callback = video_encode_capture1; + } + ch_count=ch_count+1; return real_local_sdk_video_set_encode_frame_callback(ch, callback);