diff --git a/conf/airframes/examples/bebop.xml b/conf/airframes/examples/bebop.xml
index c55455bfb9..c5b49a3699 100644
--- a/conf/airframes/examples/bebop.xml
+++ b/conf/airframes/examples/bebop.xml
@@ -17,7 +17,9 @@
-
+
+
+
@@ -44,9 +46,13 @@
-
+
+
+
+
+
diff --git a/conf/control_panel_example.xml b/conf/control_panel_example.xml
index 35a0d7c64c..cc568eba80 100644
--- a/conf/control_panel_example.xml
+++ b/conf/control_panel_example.xml
@@ -68,6 +68,11 @@
+
+
+
+
+
diff --git a/conf/modules/video_rtp_stream.xml b/conf/modules/video_rtp_stream.xml
index eca8a76c47..45e06e75fb 100644
--- a/conf/modules/video_rtp_stream.xml
+++ b/conf/modules/video_rtp_stream.xml
@@ -13,9 +13,11 @@
+
+
@@ -47,10 +49,11 @@
VIEWVIDEO_HOST ?= $(MODEM_HOST)
VIEWVIDEO_PORT_OUT ?= 5000
+ VIEWVIDEO_PORT2_OUT ?= 6000
VIEWVIDEO_BROADCAST ?= TRUE
VIEWVIDEO_USE_NETCAT ?= FALSE
- VIEWVID_CFLAGS = -DVIEWVIDEO_HOST=$(VIEWVIDEO_HOST) -DVIEWVIDEO_PORT_OUT=$(VIEWVIDEO_PORT_OUT)
+ VIEWVID_CFLAGS = -DVIEWVIDEO_HOST=$(VIEWVIDEO_HOST) -DVIEWVIDEO_PORT_OUT=$(VIEWVIDEO_PORT_OUT) -DVIEWVIDEO_PORT2_OUT=$(VIEWVIDEO_PORT2_OUT)
ifneq (,$(findstring $(VIEWVIDEO_USE_NETCAT),0 FALSE))
ap.CFLAGS += $(VIEWVID_CFLAGS) -DVIEWVIDEO_BROADCAST=$(VIEWVIDEO_BROADCAST)
nps.CFLAGS += $(VIEWVID_CFLAGS) -DVIEWVIDEO_BROADCAST=FALSE
diff --git a/sw/airborne/modules/computer_vision/cv.c b/sw/airborne/modules/computer_vision/cv.c
index 1a4dcdae17..aec8bff7b2 100644
--- a/sw/airborne/modules/computer_vision/cv.c
+++ b/sw/airborne/modules/computer_vision/cv.c
@@ -175,7 +175,7 @@ void cv_run_device(struct video_config_t *device, struct image_t *img)
}
if (listener->async != NULL) {
- // Send image to asynchronous thread, only update listener if succesful
+ // Send image to asynchronous thread, only update listener if successful
if (!cv_async_function(listener->async, img)) {
// Store timestamp
listener->ts = img->ts;
diff --git a/sw/airborne/modules/computer_vision/video_thread.c b/sw/airborne/modules/computer_vision/video_thread.c
index 490d816309..c057009c16 100644
--- a/sw/airborne/modules/computer_vision/video_thread.c
+++ b/sw/airborne/modules/computer_vision/video_thread.c
@@ -131,7 +131,7 @@ static void *video_thread_function(void *data)
if (dt_us < fps_period_us) {
usleep(fps_period_us - dt_us);
} else {
- //fprintf(stderr, "[%s] desired %i fps, only managing %.1f fps\n", print_tag, vid->fps, 1000000.f / dt_us);
+ fprintf(stderr, "[%s] desired %i fps, only managing %.1f fps\n", print_tag, vid->fps, 1000000.f / dt_us);
}
}
diff --git a/sw/airborne/modules/computer_vision/viewvideo.c b/sw/airborne/modules/computer_vision/viewvideo.c
index 0a2cda0779..d50ed2918e 100644
--- a/sw/airborne/modules/computer_vision/viewvideo.c
+++ b/sw/airborne/modules/computer_vision/viewvideo.c
@@ -72,7 +72,7 @@ PRINT_CONFIG_VAR(VIEWVIDEO_RTP_TIME_INC)
#ifdef VIDEO_THREAD_SHOT_PATH
#define VIEWVIDEO_SHOT_PATH VIDEO_THREAD_SHOT_PATH
#else
-#define VIEWVIDEO_SHOT_PATH /data/video/images
+#define VIEWVIDEO_SHOT_PATH /data/ftp/internal_000/images
#endif
#endif
PRINT_CONFIG_VAR(VIEWVIDEO_SHOT_PATH)
@@ -87,7 +87,7 @@ PRINT_CONFIG_VAR(VIEWVIDEO_FPS)
#ifndef VIEWVIDEO_NICE_LEVEL
#define VIEWVIDEO_NICE_LEVEL 5
#endif
-PRINT_CONFIG_VAR(VIEWVIDEO_FPS)
+PRINT_CONFIG_VAR(VIEWVIDEO_NICE_LEVEL)
// Check if we are using netcat instead of RTP/UDP
#ifndef VIEWVIDEO_USE_NETCAT
@@ -102,7 +102,8 @@ PRINT_CONFIG_VAR(VIEWVIDEO_FPS)
#include
PRINT_CONFIG_MSG("[viewvideo] Using netcat.")
#else
-struct UdpSocket video_sock;
+struct UdpSocket video_sock1;
+struct UdpSocket video_sock2;
PRINT_CONFIG_MSG("[viewvideo] Using RTP/UDP stream.")
PRINT_CONFIG_VAR(VIEWVIDEO_USE_RTP)
#endif
@@ -110,6 +111,7 @@ PRINT_CONFIG_VAR(VIEWVIDEO_USE_RTP)
/* These are defined with configure */
PRINT_CONFIG_VAR(VIEWVIDEO_HOST)
PRINT_CONFIG_VAR(VIEWVIDEO_PORT_OUT)
+PRINT_CONFIG_VAR(VIEWVIDEO_PORT2_OUT)
// Initialize the viewvideo structure with the defaults
struct viewvideo_t viewvideo = {
@@ -121,12 +123,16 @@ struct viewvideo_t viewvideo = {
#endif
};
+#if defined(VIEWVIDEO_CAMERA) && defined(VIEWVIDEO_CAMERA2) && VIEWVIDEO_BROADCAST == true
+#warning Broadcasting dual video stream causes too much udp overhead resulting in unstable streaming
+#warning We recomment using a static VIEWVIDEO_HOST address and VIEWVIDEO_BROADCAST to false
+#endif
+
/**
* Handles all the video streaming and saving of the image shots
- * This is a sepereate thread, so it needs to be thread safe!
+ * This is a separate thread, so it needs to be thread safe!
*/
-struct image_t *viewvideo_function(struct image_t *img);
-struct image_t *viewvideo_function(struct image_t *img)
+static struct image_t *viewvideo_function(struct UdpSocket *socket, struct image_t *img)
{
// Resize image if needed
struct image_t img_small;
@@ -178,10 +184,9 @@ struct image_t *viewvideo_function(struct image_t *img)
}
#else
if (viewvideo.use_rtp) {
-
// Send image with RTP
rtp_frame_send(
- &video_sock, // UDP socket
+ socket, // UDP socket
&img_jpeg,
0, // Format 422
VIEWVIDEO_QUALITY_FACTOR, // Jpeg-Quality
@@ -207,6 +212,20 @@ struct image_t *viewvideo_function(struct image_t *img)
return NULL; // No new images were created
}
+#ifdef VIEWVIDEO_CAMERA
+static struct image_t *viewvideo_function1(struct image_t *img)
+{
+ return viewvideo_function(&video_sock1, img);
+}
+#endif
+
+#ifdef VIEWVIDEO_CAMERA2
+static struct image_t *viewvideo_function2(struct image_t *img)
+{
+ return viewvideo_function(&video_sock2, img);
+}
+#endif
+
/**
* Initialize the view video
*/
@@ -214,9 +233,6 @@ void viewvideo_init(void)
{
char save_name[512];
- struct video_listener *listener = cv_add_to_device_async(&VIEWVIDEO_CAMERA, viewvideo_function, VIEWVIDEO_NICE_LEVEL);
- listener->maximum_fps = VIEWVIDEO_FPS;
-
viewvideo.is_streaming = true;
#if VIEWVIDEO_USE_NETCAT
@@ -237,8 +253,21 @@ void viewvideo_init(void)
}
#else
// Open udp socket
- udp_socket_create(&video_sock, STRINGIFY(VIEWVIDEO_HOST), VIEWVIDEO_PORT_OUT, -1, VIEWVIDEO_BROADCAST);
+#ifdef VIEWVIDEO_CAMERA1
+ if (udp_socket_create(&video_sock1, STRINGIFY(VIEWVIDEO_HOST), VIEWVIDEO_PORT_OUT, -1, VIEWVIDEO_BROADCAST)) {
+ printf("[viewvideo]: failed to open view video socket, HOST=%s, port=%d\n", STRINGIFY(VIEWVIDEO_HOST),
+ VIEWVIDEO_PORT_OUT);
+ }
+#endif
+#ifdef VIEWVIDEO_CAMERA2
+ if (udp_socket_create(&video_sock2, STRINGIFY(VIEWVIDEO_HOST), VIEWVIDEO_PORT2_OUT, -1, VIEWVIDEO_BROADCAST)) {
+ printf("[viewvideo]: failed to open view video socket, HOST=%s, port=%d\n", STRINGIFY(VIEWVIDEO_HOST),
+ VIEWVIDEO_PORT2_OUT);
+ }
+#endif
+
+ // todo: check what this is for!
// Create an SDP file for the streaming
sprintf(save_name, "%s/stream.sdp", STRINGIFY(VIEWVIDEO_SHOT_PATH));
FILE *fp = fopen(save_name, "w");
@@ -251,5 +280,18 @@ void viewvideo_init(void)
printf("[viewvideo] Failed to create SDP file.\n");
}
#endif
-}
+#ifdef VIEWVIDEO_CAMERA
+ struct video_listener *listener1 = cv_add_to_device_async(&VIEWVIDEO_CAMERA, viewvideo_function1,
+ VIEWVIDEO_NICE_LEVEL);
+ listener1->maximum_fps = VIEWVIDEO_FPS;
+ fprintf(stderr, "[viewvideo] Added asynchronous video streamer lister for CAMERA1\n");
+#endif
+
+#ifdef VIEWVIDEO_CAMERA2
+ struct video_listener *listener2 = cv_add_to_device_async(&VIEWVIDEO_CAMERA2, viewvideo_function2,
+ VIEWVIDEO_NICE_LEVEL);
+ listener2->maximum_fps = VIEWVIDEO_FPS;
+ fprintf(stderr, "[viewvideo] Added asynchronous video streamer lister for CAMERA2\n");
+#endif
+}
diff --git a/sw/tools/rtp_viewer/rtp_stream.sdp b/sw/tools/rtp_viewer/rtp_5000.sdp
similarity index 100%
rename from sw/tools/rtp_viewer/rtp_stream.sdp
rename to sw/tools/rtp_viewer/rtp_5000.sdp
diff --git a/sw/tools/rtp_viewer/rtp_6000.sdp b/sw/tools/rtp_viewer/rtp_6000.sdp
new file mode 100644
index 0000000000..0c59b59203
--- /dev/null
+++ b/sw/tools/rtp_viewer/rtp_6000.sdp
@@ -0,0 +1,3 @@
+v=0
+m=video 6000 RTP/AVP 26
+c=IN IP4 0.0.0.0
diff --git a/sw/tools/rtp_viewer/rtp_viewer.py b/sw/tools/rtp_viewer/rtp_viewer.py
index b0a1f98294..d7593bbc46 100755
--- a/sw/tools/rtp_viewer/rtp_viewer.py
+++ b/sw/tools/rtp_viewer/rtp_viewer.py
@@ -2,6 +2,8 @@
import cv2
import sys
+import getopt
+import re
from os import path, getenv
PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../')))
@@ -10,9 +12,41 @@ sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python")
from pprzlink.ivy import IvyMessagesInterface
from pprzlink.message import PprzMessage
+def Usage(scmd):
+ lpathitem = scmd.split('/')
+ fmt = '''Usage: %s [-h | --help] [-p PORT | --port=PORT] [-s SCALE | --scale=SCALE] [-r ROTATE | --rotate=ROTATE] []
+where
+\t-h | --help print this message
+\t-p PORT | --port=PORT where PORT is the port number to open for the RTP stream (5000 or 6000)
+\t-s SCALE | --scale=SCALE where SCALE is the scaling factor to apply to the incoming video stream (default: 1)
+\t-r ROTATE | --rotate=ROTATE where ROTATE is the number of clockwise 90deg rotations to apply to the stream [0-3] (default: 0)
+'''
+ print(fmt % lpathitem[-1])
+
+def GetOptions():
+ options = {'port':[], 'scale':[], 'rotate':[]}
+ try:
+ optlist, left_args = getopt.getopt(sys.argv[1:],'h:p:s:r:', ['help', 'port=', 'scale=', 'rotate='])
+ except getopt.GetoptError:
+ # print help information and exit:
+ Usage(sys.argv[0])
+ sys.exit(2)
+ for o, a in optlist:
+ if o in ("-h", "--help"):
+ Usage(sys.argv[0])
+ sys.exit()
+ elif o in ("-p", "--port"):
+ options['port'].append(int(a))
+ elif o in ("-s", "--scale"):
+ options['scale'].append(float(a))
+ elif o in ("-r", "--rotate"):
+ options['rotate'].append(int(a))
+ return options
+
class RtpViewer:
running = False
+ scale = 1
rotate = 0
frame = None
mouse = dict()
@@ -60,6 +94,10 @@ class RtpViewer:
# Draw a rectangle indicating the region of interest
cv2.rectangle(self.frame, self.mouse['start'], self.mouse['now'], (0, 255, 0), 2)
+ if self.scale != 1:
+ h, w = self.frame.shape[:2]
+ self.frame = cv2.resize(self.frame, (int(self.scale * w), int(self.scale * h)))
+
# Show the image in a window
cv2.imshow('rtp', self.frame)
@@ -72,7 +110,7 @@ class RtpViewer:
self.mouse['start'] = None
def on_mouse(self, event, x, y, flags, param):
- if event == cv2.EVENT_LBUTTONDOWN and self.rotate == 0:
+ if event == cv2.EVENT_LBUTTONDOWN and self.rotate == 0 and False:
self.mouse['start'] = (x, y)
if event == cv2.EVENT_RBUTTONDOWN:
@@ -110,11 +148,31 @@ class RtpViewer:
if __name__ == '__main__':
- viewer = RtpViewer("rtp_stream.sdp")
+ import sys
+ import os
+
+ options = GetOptions()
+ if not options['port']:
+ Usage(sys.argv[0])
+ filename = os.path.dirname(os.path.abspath(__file__)) + "/rtp_" + str(options['port'][0]) + ".sdp"
+ print(filename)
+
+ #set defaults
+ if not options['scale']:
+ options['scale'][0] = 1.
+ if not options['rotate']:
+ options['rotate'][0] = 0
+
+ print(options['scale'][0])
+ print(options['rotate'][0])
+
+ viewer = RtpViewer(filename)
+ viewer.scale = options['scale'][0]
+ viewer.rotate = options['rotate'][0]
if not viewer.cap.isOpened():
viewer.cleanup()
- sys.exit("Can't open video stream")
+ raise IOError("Can't open video stream")
viewer.run()
viewer.cleanup()