From 26c5c57de07dc6563efce87f8f7500a59b4832e1 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Tue, 10 Jan 2023 19:54:15 +0100 Subject: [PATCH 1/9] Add support to Theta X --- gst/thetauvc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gst/thetauvc.c b/gst/thetauvc.c index a9535d7..91bc98a 100644 --- a/gst/thetauvc.c +++ b/gst/thetauvc.c @@ -41,6 +41,7 @@ #define USBVID_RICOH 0x05ca #define USBPID_THETAV_UVC 0x2712 #define USBPID_THETAZ1_UVC 0x2715 +#define USBPID_THETAX_UVC 0x2717 struct thetauvc_mode { @@ -102,6 +103,7 @@ thetauvc_find_devices(uvc_context_t *ctx, uvc_device_t ***devs) continue; if (desc->idProduct == USBPID_THETAV_UVC + || desc->idProduct == USBPID_THETAX_UVC || desc->idProduct == USBPID_THETAZ1_UVC) { void *tmp_ptr; From 48e4e95c9818c664a1b90be039c41c8aede5e373 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Thu, 2 Mar 2023 12:55:32 +0100 Subject: [PATCH 2/9] Add script to set v4l2loopback --- scripts/set_v4l.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 scripts/set_v4l.sh diff --git a/scripts/set_v4l.sh b/scripts/set_v4l.sh new file mode 100755 index 0000000..386dbaf --- /dev/null +++ b/scripts/set_v4l.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +video_id=60 + +v_dev=/dev/video${video_id} + +if ! ls "$v_dev" 2> /dev/null +then + if lsmod | grep -wq v4l2loopback + then + sudo modprobe -r v4l2loopback + fi + sudo modprobe v4l2loopback video_nr=$video_id card_label="theta_x" exclusive_caps=1 +fi From 2b9e0bc3a691c46c105d7ad084b28e57cf59ddea Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Tue, 28 Mar 2023 14:43:49 +0200 Subject: [PATCH 3/9] [WIP] Draft pipeline working for Theta X in Jeson Nano --- gst/gst_viewer.c | 378 +++++++++++++++++++++++------------------------ gst/thetauvc.c | 6 +- 2 files changed, 189 insertions(+), 195 deletions(-) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index 8087a58..8a0fc51 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -45,16 +45,16 @@ #define MAX_PIPELINE_LEN 1024 struct gst_src { - GstElement *pipeline; - GstElement *appsrc; - - GMainLoop *loop; - GTimer *timer; - guint framecount; - guint id; - guint bus_watch_id; - uint32_t dwFrameInterval; - uint32_t dwClockFrequency; + GstElement *pipeline; + GstElement *appsrc; + + GMainLoop *loop; + GTimer *timer; + guint framecount; + guint id; + guint bus_watch_id; + uint32_t dwFrameInterval; + uint32_t dwClockFrequency; }; struct gst_src src; @@ -62,212 +62,206 @@ struct gst_src src; static gboolean gst_bus_cb(GstBus *bus, GstMessage *message, gpointer data) { - GError *err; - gchar *dbg; - - switch (GST_MESSAGE_TYPE(message)) { - case GST_MESSAGE_ERROR: - gst_message_parse_error(message, &err, &dbg); - g_print("Error: %s\n", err->message); - g_error_free(err); - g_free(dbg); - g_main_loop_quit(src.loop); - break; - - default: - break; - } - - return TRUE; + GError *err; + gchar *dbg; + + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, &dbg); + g_print("Error: %s\n", err->message); + g_error_free(err); + g_free(dbg); + g_main_loop_quit(src.loop); + break; + + default: + break; + } + + return TRUE; } int gst_src_init(int *argc, char ***argv, char *pipeline) { - GstCaps *caps; - GstBus *bus; - char pipeline_str[MAX_PIPELINE_LEN]; - - snprintf(pipeline_str, MAX_PIPELINE_LEN, "appsrc name=ap ! queue ! h264parse ! queue ! %s ", pipeline); - - gst_init(argc, argv); - src.timer = g_timer_new(); - src.loop = g_main_loop_new(NULL, TRUE); - src.pipeline = gst_parse_launch(pipeline_str, NULL); - - g_assert(src.pipeline); - if (src.pipeline == NULL) - return FALSE; - gst_pipeline_set_clock(GST_PIPELINE(src.pipeline), gst_system_clock_obtain()); - - src.appsrc = gst_bin_get_by_name(GST_BIN(src.pipeline), "ap"); - - caps = gst_caps_new_simple("video/x-h264", - "framerate", GST_TYPE_FRACTION, 30000, 1001, - "stream-format", G_TYPE_STRING, "byte-stream", - "profile", G_TYPE_STRING, "constrained-baseline", NULL); - gst_app_src_set_caps(GST_APP_SRC(src.appsrc), caps); - - bus = gst_pipeline_get_bus(GST_PIPELINE(src.pipeline)); - src.bus_watch_id = gst_bus_add_watch(bus, gst_bus_cb, NULL); - gst_object_unref(bus); - return TRUE; + GstCaps *caps; + GstBus *bus; + char pipeline_str[MAX_PIPELINE_LEN]; + + snprintf(pipeline_str, MAX_PIPELINE_LEN, "appsrc name=ap ! queue ! h264parse ! queue ! %s ", pipeline); + + gst_init(argc, argv); + src.timer = g_timer_new(); + src.loop = g_main_loop_new(NULL, TRUE); + src.pipeline = gst_parse_launch(pipeline_str, NULL); + + g_assert(src.pipeline); + if (src.pipeline == NULL) + return FALSE; + gst_pipeline_set_clock(GST_PIPELINE(src.pipeline), gst_system_clock_obtain()); + + src.appsrc = gst_bin_get_by_name(GST_BIN(src.pipeline), "ap"); + + caps = gst_caps_new_simple("video/x-h264", + "framerate", GST_TYPE_FRACTION, 30000, 1001, + "stream-format", G_TYPE_STRING, "byte-stream", + "profile", G_TYPE_STRING, "constrained-baseline", NULL); + gst_app_src_set_caps(GST_APP_SRC(src.appsrc), caps); + + bus = gst_pipeline_get_bus(GST_PIPELINE(src.pipeline)); + src.bus_watch_id = gst_bus_add_watch(bus, gst_bus_cb, NULL); + gst_object_unref(bus); + return TRUE; } void * keywait(void *arg) { - struct gst_src *s; - char keyin[4]; + struct gst_src *s; + char keyin[4]; - read(1, keyin, 1); + read(1, keyin, 1); - s = (struct gst_src *)arg; - g_main_loop_quit(s->loop); + s = (struct gst_src *)arg; + g_main_loop_quit(s->loop); } void cb(uvc_frame_t *frame, void *ptr) { - struct gst_src *s; - GstBuffer *buffer; - GstFlowReturn ret; - GstMapInfo map; - gdouble ms; - uint32_t pts; - - s = (struct gst_src *)ptr; - ms = g_timer_elapsed(s->timer, NULL); - - buffer = gst_buffer_new_allocate(NULL, frame->data_bytes, NULL);; - GST_BUFFER_PTS(buffer) = frame->sequence * s->dwFrameInterval*100; - GST_BUFFER_DTS(buffer) = GST_CLOCK_TIME_NONE; - GST_BUFFER_DURATION(buffer) = s->dwFrameInterval*100; - GST_BUFFER_OFFSET(buffer) = frame->sequence; - s->framecount++; - - gst_buffer_map(buffer, &map, GST_MAP_WRITE); - memcpy(map.data, frame->data, frame->data_bytes); - gst_buffer_unmap(buffer, &map); - - g_signal_emit_by_name(s->appsrc, "push-buffer", buffer, &ret); - gst_buffer_unref(buffer); - - if (ret != GST_FLOW_OK) - fprintf(stderr, "pushbuffer errorn"); - return; + struct gst_src *s; + GstBuffer *buffer; + GstFlowReturn ret; + GstMapInfo map; + gdouble ms; + uint32_t pts; + + s = (struct gst_src *)ptr; + ms = g_timer_elapsed(s->timer, NULL); + + buffer = gst_buffer_new_allocate(NULL, frame->data_bytes, NULL);; + GST_BUFFER_PTS(buffer) = frame->sequence * s->dwFrameInterval*100; + GST_BUFFER_DTS(buffer) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION(buffer) = s->dwFrameInterval*100; + GST_BUFFER_OFFSET(buffer) = frame->sequence; + s->framecount++; + + gst_buffer_map(buffer, &map, GST_MAP_WRITE); + memcpy(map.data, frame->data, frame->data_bytes); + gst_buffer_unmap(buffer, &map); + + g_signal_emit_by_name(s->appsrc, "push-buffer", buffer, &ret); + gst_buffer_unref(buffer); + + if (ret != GST_FLOW_OK) + fprintf(stderr, "pushbuffer errorn"); + return; } int main(int argc, char **argv) { - uvc_context_t *ctx; - uvc_device_t *dev; - uvc_device_t **devlist; - uvc_device_handle_t *devh; - uvc_stream_ctrl_t ctrl; - uvc_error_t res; - - pthread_t thr; - pthread_attr_t attr; - - struct gst_src *s; - int idx; - char *pipe_proc; - char *cmd_name; - - cmd_name = rindex(argv[0], '/'); - if (cmd_name == NULL) - cmd_name = argv[0]; - else - cmd_name++; - - if (strcmp(cmd_name, "gst_loopback") == 0) - pipe_proc = "decodebin ! autovideoconvert ! " - "video/x-raw,format=I420 ! identity drop-allocation=true !" - "v4l2sink device=/dev/video1 sync=false"; - else - pipe_proc = " decodebin ! autovideosink sync=false"; - - if (!gst_src_init(&argc, &argv, pipe_proc)) - return -1; - - res = uvc_init(&ctx, NULL); - if (res != UVC_SUCCESS) { - uvc_perror(res, "uvc_init"); - return res; - } - - if (argc > 1 && strcmp("-l", argv[1]) == 0) { - res = thetauvc_find_devices(ctx, &devlist); - if (res != UVC_SUCCESS) { - uvc_perror(res,""); - uvc_exit(ctx); - return res; - } - - idx = 0; - printf("No : %-18s : %-10s\n", "Product", "Serial"); - while (devlist[idx] != NULL) { - uvc_device_descriptor_t *desc; - - if (uvc_get_device_descriptor(devlist[idx], &desc) != UVC_SUCCESS) - continue; - - printf("%2d : %-18s : %-10s\n", idx, desc->product, - desc->serialNumber); - - uvc_free_device_descriptor(desc); - idx++; - } - - uvc_free_device_list(devlist, 1); - uvc_exit(ctx); - exit(0); - } - - src.framecount = 0; - res = thetauvc_find_device(ctx, &dev, 0); - if (res != UVC_SUCCESS) { - fprintf(stderr, "THETA not found\n"); - goto exit; - } - - res = uvc_open(dev, &devh); - if (res != UVC_SUCCESS) { - fprintf(stderr, "Can't open THETA\n"); - goto exit; - } - - gst_element_set_state(src.pipeline, GST_STATE_PLAYING); - pthread_create(&thr, NULL, keywait, &src); - - res = thetauvc_get_stream_ctrl_format_size(devh, - THETAUVC_MODE_UHD_2997, &ctrl); - src.dwFrameInterval = ctrl.dwFrameInterval; - src.dwClockFrequency = ctrl.dwClockFrequency; - - res = uvc_start_streaming(devh, &ctrl, cb, &src, 0); - if (res == UVC_SUCCESS) { - fprintf(stderr, "start, hit any key to stop\n"); - g_main_loop_run(src.loop); - - fprintf(stderr, "stop\n"); - uvc_stop_streaming(devh); - - gst_element_set_state(src.pipeline, GST_STATE_NULL); - g_source_remove(src.bus_watch_id); - g_main_loop_unref(src.loop); - - pthread_cancel(thr); - pthread_join(thr, NULL); - } - - uvc_close(devh); + uvc_context_t *ctx; + uvc_device_t *dev; + uvc_device_t **devlist; + uvc_device_handle_t *devh; + uvc_stream_ctrl_t ctrl; + uvc_error_t res; + + pthread_t thr; + pthread_attr_t attr; + + struct gst_src *s; + int idx; + char *pipe_proc; + char *cmd_name; + + cmd_name = rindex(argv[0], '/'); + if (cmd_name == NULL) + cmd_name = argv[0]; + else + cmd_name++; + + if (strcmp(cmd_name, "gst_loopback") == 0) + pipe_proc = "decodebin ! autovideoconvert ! " + "video/x-raw,format=I420 ! identity drop-allocation=true !" + "v4l2sink device=/dev/video1 sync=false"; + else + // pipe_proc = " decodebin ! autovideosink sync=false"; + // pipe_proc = " h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 ! rtprateshape max-delay-us=150000 max-bitrate=15000000 ! udpsink host=192.168.1.150 port=6666"; + pipe_proc = " omxh264dec ! omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames preset-level=FastPreset bitrate=5000000 iframeinterval=300 ! h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 ! rtprateshape max-delay-us=150000 max-bitrate=15000000 ! udpsink host=192.168.1.150 port=6666"; + // pipe_proc = " -v decodebin ! x264enc tune=zerolatency bitrate=500 speed-preset=superfast ! h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 ! rtprateshape max-delay-us=150000 max-bitrate=15000000 ! udpsink host=192.168.1.150 port=6666"; + // pipe_proc = " -v decodebin ! x264enc tune=zerolatency bitrate=500 speed-preset=superfast ! rtph264pay ! udpsink host=127.0.0.1 port=6666"; + + if (!gst_src_init(&argc, &argv, pipe_proc)) + return -1; + + res = uvc_init(&ctx, NULL); + if (res != UVC_SUCCESS) { + uvc_perror(res, "uvc_init"); + return res; + } + + if (argc > 1 && strcmp("-l", argv[1]) == 0) { + res = thetauvc_print_devices(ctx, stdout); + if (res != UVC_SUCCESS) { + uvc_perror(res,"Error printing devices: "); + uvc_exit(ctx); + return res; + } + uvc_exit(ctx); + exit(0); + } + + src.framecount = 0; + res = thetauvc_find_device(ctx, &dev, 0); + if (res != UVC_SUCCESS) { + uvc_perror(res,"THETA not found"); + goto exit; + } + + res = uvc_open(dev, &devh); + if (res != UVC_SUCCESS) { + uvc_perror(res,"Can't open THETA"); + goto exit; + } + + uvc_print_diag(devh, stdout); + + gst_element_set_state(src.pipeline, GST_STATE_PLAYING); + pthread_create(&thr, NULL, keywait, &src); + + res = thetauvc_get_stream_ctrl_format_size(devh, + THETAUVC_MODE_UHD_2997, &ctrl); + src.dwFrameInterval = ctrl.dwFrameInterval; + src.dwClockFrequency = ctrl.dwClockFrequency; + + if (res != UVC_SUCCESS) { + uvc_perror(res,"Can't set stream"); + } + uvc_perror(res,"go on"); + + res = uvc_start_streaming(devh, &ctrl, cb, &src, 0); + if (res == UVC_SUCCESS) { + fprintf(stderr, "start, hit any key to stop\n"); + g_main_loop_run(src.loop); + + fprintf(stderr, "stop\n"); + uvc_stop_streaming(devh); + + gst_element_set_state(src.pipeline, GST_STATE_NULL); + g_source_remove(src.bus_watch_id); + g_main_loop_unref(src.loop); + + pthread_cancel(thr); + pthread_join(thr, NULL); + } + + uvc_close(devh); exit: - uvc_exit(ctx); - return res; + uvc_exit(ctx); + return res; } diff --git a/gst/thetauvc.c b/gst/thetauvc.c index 91bc98a..af54e5e 100644 --- a/gst/thetauvc.c +++ b/gst/thetauvc.c @@ -58,13 +58,13 @@ static thetauvc_mode_t stream_mode[] = { .mode = THETAUVC_MODE_UHD_2997, .width = 3840, .height = 1920, - .fps = 29 + .fps = 30 }, { .mode = THETAUVC_MODE_FHD_2997, .width = 1920, .height = 960, - .fps = 29 + .fps = 30 }, { .mode = THETAUVC_MODE_NUM, @@ -256,7 +256,7 @@ thetauvc_get_stream_ctrl_format_size(uvc_device_handle_t *devh, return UVC_ERROR_INVALID_MODE; m = &stream_mode[mode]; - + res = uvc_get_stream_ctrl_format_size(devh, ctrl, UVC_FRAME_FORMAT_H264, m->width, m->height, m->fps); From dcd145a3bfb072fb32667acd0f791adbeff65499 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Tue, 28 Mar 2023 22:11:59 +0200 Subject: [PATCH 4/9] Add corrected modes for Theta X && Parse stream options --- gst/gst_viewer.c | 102 ++++++++++++++++++++++++++++++++++++++++++----- gst/thetauvc.c | 14 ++++++- gst/thetauvc.h | 3 ++ 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index 8a0fc51..57081ac 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -43,6 +43,13 @@ #include "thetauvc.h" #define MAX_PIPELINE_LEN 1024 +#define DEFAULT_CAM_MODE "UHD" +#define DEFAULT_BITRATE 5000000 +#define DEFAULT_KFI 300 +// #define DEFAULT_RTP_ADDRESS "127.0.0.1" +#define DEFAULT_RTP_ADDRESS "192.168.1.150" +#define DEFAULT_RTP_PORT 6666 +#define DEFAULT_RTP_MAX_DELAY 150000 struct gst_src { GstElement *pipeline; @@ -59,6 +66,28 @@ struct gst_src { struct gst_src src; +struct stream_parameters { + enum thetauvc_mode_code mode; + uint32_t bitrate; // bps + uint32_t max_bitrate; // bps + uint32_t kfi; // Key Frame Interval (# frames) + char *address; + uint16_t port; + uint32_t max_delay; // us +}; + +void print_help() +{ + printf("Options:\n"); + printf("\t-m: Resolution Mode. Accepted values: UHD, FHD. Default: %s\n", DEFAULT_CAM_MODE); + printf("\t-b: bitrate (bps). Default: %i\n", DEFAULT_BITRATE); + printf("\t-k: Keyframe Interval (frames). Default: %i\n", DEFAULT_KFI); + printf("\t-a: RTP address. Default: %s\n", DEFAULT_RTP_ADDRESS); + printf("\t-p: RTP port. Default: %i\n", DEFAULT_RTP_PORT); + printf("\t-d: RTP max delay (us). Default: %i\n", DEFAULT_RTP_MAX_DELAY); + exit(0); +} + static gboolean gst_bus_cb(GstBus *bus, GstMessage *message, gpointer data) { @@ -173,10 +202,13 @@ main(int argc, char **argv) pthread_t thr; pthread_attr_t attr; + struct stream_parameters cam_params; + struct gst_src *s; int idx; - char *pipe_proc; + char pipe_proc[MAX_PIPELINE_LEN]; char *cmd_name; + int opt; cmd_name = rindex(argv[0], '/'); if (cmd_name == NULL) @@ -184,16 +216,68 @@ main(int argc, char **argv) else cmd_name++; + cam_params.bitrate = DEFAULT_BITRATE; + cam_params.max_bitrate = cam_params.bitrate * 1.5; + if (strcmp(DEFAULT_CAM_MODE, "UHD")) + cam_params.mode = THETAUVC_MODE_UHD_30; + else if (strcmp(DEFAULT_CAM_MODE, "FHD")) + cam_params.mode = THETAUVC_MODE_FHD_30; + else + cam_params.mode = THETAUVC_MODE_UHD_2997; + cam_params.kfi = DEFAULT_KFI; + cam_params.address = DEFAULT_RTP_ADDRESS; + cam_params.port = DEFAULT_RTP_PORT; + cam_params.max_delay = DEFAULT_RTP_MAX_DELAY; + while ((opt = getopt(argc, argv, "b:d:m:k:a:p:h")) != -1) { + switch (opt) { + case 'b': cam_params.bitrate = (uint32_t) atoi(optarg); break; + case 'd': cam_params.max_delay = atoi(optarg); break; + case 'm': + { + if (strcmp(optarg, "UHD")) + cam_params.mode = THETAUVC_MODE_UHD_30; + else if (strcmp(optarg, "FHD")) + cam_params.mode = THETAUVC_MODE_FHD_30; + else + cam_params.mode = THETAUVC_MODE_UHD_2997; + break; + } + case 'k': cam_params.kfi = atoi(optarg); break; + case 'a': cam_params.address = optarg; break; + case 'p': cam_params.port = atoi(optarg); break; + case 'h': print_help(); break; + default: + { + fprintf(stderr, "Usage: %s [-bdmkaph] (-h for options meaning)\n", argv[0]); + exit(EXIT_FAILURE); + } + } + } if (strcmp(cmd_name, "gst_loopback") == 0) - pipe_proc = "decodebin ! autovideoconvert ! " + sprintf(pipe_proc, + "decodebin ! autovideoconvert ! " "video/x-raw,format=I420 ! identity drop-allocation=true !" - "v4l2sink device=/dev/video1 sync=false"; + "v4l2sink device=/dev/video1 sync=false" + ); else - // pipe_proc = " decodebin ! autovideosink sync=false"; - // pipe_proc = " h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 ! rtprateshape max-delay-us=150000 max-bitrate=15000000 ! udpsink host=192.168.1.150 port=6666"; - pipe_proc = " omxh264dec ! omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames preset-level=FastPreset bitrate=5000000 iframeinterval=300 ! h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 ! rtprateshape max-delay-us=150000 max-bitrate=15000000 ! udpsink host=192.168.1.150 port=6666"; - // pipe_proc = " -v decodebin ! x264enc tune=zerolatency bitrate=500 speed-preset=superfast ! h264parse config-interval=1 ! rtph264pay pt=96 config-interval=1 ! rtprateshape max-delay-us=150000 max-bitrate=15000000 ! udpsink host=192.168.1.150 port=6666"; - // pipe_proc = " -v decodebin ! x264enc tune=zerolatency bitrate=500 speed-preset=superfast ! rtph264pay ! udpsink host=127.0.0.1 port=6666"; + sprintf(pipe_proc, + " omxh264dec ! " + "omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames " + "preset-level=FastPreset bitrate=%i, iframeinterval=%i ! " + "h264parse config-interval=1 ! " + "rtph264pay pt=96 config-interval=1 ! " + "rtprateshape max-delay-us=%i max-bitrate=%i ! " + "udpsink host=%s port=%i", + cam_params.bitrate, + cam_params.kfi, + cam_params.max_delay, + cam_params.max_bitrate, + cam_params.address, + cam_params.port + ); + + // TODO Remove (for developing purposes) + // printf(pipe_proc); if (!gst_src_init(&argc, &argv, pipe_proc)) return -1; @@ -234,7 +318,7 @@ main(int argc, char **argv) pthread_create(&thr, NULL, keywait, &src); res = thetauvc_get_stream_ctrl_format_size(devh, - THETAUVC_MODE_UHD_2997, &ctrl); + cam_params.mode, &ctrl); src.dwFrameInterval = ctrl.dwFrameInterval; src.dwClockFrequency = ctrl.dwClockFrequency; diff --git a/gst/thetauvc.c b/gst/thetauvc.c index af54e5e..097d8fb 100644 --- a/gst/thetauvc.c +++ b/gst/thetauvc.c @@ -58,12 +58,24 @@ static thetauvc_mode_t stream_mode[] = { .mode = THETAUVC_MODE_UHD_2997, .width = 3840, .height = 1920, - .fps = 30 + .fps = 29 }, { .mode = THETAUVC_MODE_FHD_2997, .width = 1920, .height = 960, + .fps = 29 + }, + { + .mode = THETAUVC_MODE_UHD_30, + .width = 3840, + .height = 1920, + .fps = 30 + }, + { + .mode = THETAUVC_MODE_FHD_30, + .width = 1920, + .height = 960, .fps = 30 }, { diff --git a/gst/thetauvc.h b/gst/thetauvc.h index baac47f..b05120c 100644 --- a/gst/thetauvc.h +++ b/gst/thetauvc.h @@ -37,9 +37,12 @@ extern "C" { #endif +// _30 are the valid ones for Theta X enum thetauvc_mode_code { THETAUVC_MODE_UHD_2997 = 0, THETAUVC_MODE_FHD_2997, + THETAUVC_MODE_UHD_30, + THETAUVC_MODE_FHD_30, THETAUVC_MODE_NUM }; From 58cc01211b5ff9278ba7e368b701ab1dfee1aa3c Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Thu, 13 Apr 2023 12:56:16 +0200 Subject: [PATCH 5/9] Exit with error --- gst/gst_viewer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index 57081ac..1a760e0 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -344,6 +344,7 @@ main(int argc, char **argv) } uvc_close(devh); + return UVC_ERROR_OTHER; exit: uvc_exit(ctx); From 2ea300359a96491b5eb2b1c6dabe23196ae53832 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Thu, 13 Apr 2023 19:32:40 +0200 Subject: [PATCH 6/9] Fix typo bug in br & minor changes w/ logs --- gst/gst_viewer.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index 1a760e0..3b64e7e 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -120,6 +120,8 @@ gst_src_init(int *argc, char ***argv, char *pipeline) snprintf(pipeline_str, MAX_PIPELINE_LEN, "appsrc name=ap ! queue ! h264parse ! queue ! %s ", pipeline); + fprintf(stderr, "%s/n", pipeline_str); + gst_init(argc, argv); src.timer = g_timer_new(); src.loop = g_main_loop_new(NULL, TRUE); @@ -263,7 +265,7 @@ main(int argc, char **argv) sprintf(pipe_proc, " omxh264dec ! " "omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames " - "preset-level=FastPreset bitrate=%i, iframeinterval=%i ! " + "preset-level=FastPreset bitrate=%i iframeinterval=%i ! " "h264parse config-interval=1 ! " "rtph264pay pt=96 config-interval=1 ! " "rtprateshape max-delay-us=%i max-bitrate=%i ! " @@ -276,9 +278,6 @@ main(int argc, char **argv) cam_params.port ); - // TODO Remove (for developing purposes) - // printf(pipe_proc); - if (!gst_src_init(&argc, &argv, pipe_proc)) return -1; @@ -329,10 +328,9 @@ main(int argc, char **argv) res = uvc_start_streaming(devh, &ctrl, cb, &src, 0); if (res == UVC_SUCCESS) { - fprintf(stderr, "start, hit any key to stop\n"); g_main_loop_run(src.loop); - fprintf(stderr, "stop\n"); + fprintf(stderr, "stop streaming\n"); uvc_stop_streaming(devh); gst_element_set_state(src.pipeline, GST_STATE_NULL); From 26d65b461be70ab6e5e2631c23d2c6c8f7b631c6 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Thu, 18 May 2023 11:50:31 +0200 Subject: [PATCH 7/9] Add support to RAW video and TS recording --- gst/gst_viewer.c | 65 +++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index 3b64e7e..477ea12 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -38,11 +38,11 @@ #include #include - #include "libuvc/libuvc.h" #include "thetauvc.h" #define MAX_PIPELINE_LEN 1024 +#define MAX_OUTPUT_PIPELINE_LEN 512 // It must be less than previous #define DEFAULT_CAM_MODE "UHD" #define DEFAULT_BITRATE 5000000 #define DEFAULT_KFI 300 @@ -74,17 +74,19 @@ struct stream_parameters { char *address; uint16_t port; uint32_t max_delay; // us + char *filename; }; void print_help() { printf("Options:\n"); printf("\t-m: Resolution Mode. Accepted values: UHD, FHD. Default: %s\n", DEFAULT_CAM_MODE); - printf("\t-b: bitrate (bps). Default: %i\n", DEFAULT_BITRATE); + printf("\t-b: bitrate (bps). Default: %i. If set to 0, raw input from camera\n", DEFAULT_BITRATE); printf("\t-k: Keyframe Interval (frames). Default: %i\n", DEFAULT_KFI); printf("\t-a: RTP address. Default: %s\n", DEFAULT_RTP_ADDRESS); printf("\t-p: RTP port. Default: %i\n", DEFAULT_RTP_PORT); printf("\t-d: RTP max delay (us). Default: %i\n", DEFAULT_RTP_MAX_DELAY); + printf("\t-f: Filename. If set, only recording to disk (TS stream). No RTP will be generated\n"); exit(0); } @@ -116,9 +118,10 @@ gst_src_init(int *argc, char ***argv, char *pipeline) { GstCaps *caps; GstBus *bus; - char pipeline_str[MAX_PIPELINE_LEN]; + char pipeline_str[MAX_PIPELINE_LEN+50]; - snprintf(pipeline_str, MAX_PIPELINE_LEN, "appsrc name=ap ! queue ! h264parse ! queue ! %s ", pipeline); + snprintf(pipeline_str, MAX_PIPELINE_LEN+50, + "appsrc name=ap ! queue ! h264parse config-interval=1 ! queue ! %s ", pipeline); fprintf(stderr, "%s/n", pipeline_str); @@ -230,7 +233,8 @@ main(int argc, char **argv) cam_params.address = DEFAULT_RTP_ADDRESS; cam_params.port = DEFAULT_RTP_PORT; cam_params.max_delay = DEFAULT_RTP_MAX_DELAY; - while ((opt = getopt(argc, argv, "b:d:m:k:a:p:h")) != -1) { + cam_params.filename = "\0"; + while ((opt = getopt(argc, argv, "b:d:m:k:a:p:f:h")) != -1) { switch (opt) { case 'b': cam_params.bitrate = (uint32_t) atoi(optarg); break; case 'd': cam_params.max_delay = atoi(optarg); break; @@ -247,6 +251,7 @@ main(int argc, char **argv) case 'k': cam_params.kfi = atoi(optarg); break; case 'a': cam_params.address = optarg; break; case 'p': cam_params.port = atoi(optarg); break; + case 'f': cam_params.filename = optarg; break; case 'h': print_help(); break; default: { @@ -256,27 +261,41 @@ main(int argc, char **argv) } } if (strcmp(cmd_name, "gst_loopback") == 0) - sprintf(pipe_proc, + snprintf(pipe_proc, MAX_PIPELINE_LEN, "decodebin ! autovideoconvert ! " "video/x-raw,format=I420 ! identity drop-allocation=true !" - "v4l2sink device=/dev/video1 sync=false" - ); - else - sprintf(pipe_proc, - " omxh264dec ! " - "omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames " - "preset-level=FastPreset bitrate=%i iframeinterval=%i ! " - "h264parse config-interval=1 ! " - "rtph264pay pt=96 config-interval=1 ! " - "rtprateshape max-delay-us=%i max-bitrate=%i ! " - "udpsink host=%s port=%i", - cam_params.bitrate, - cam_params.kfi, - cam_params.max_delay, - cam_params.max_bitrate, - cam_params.address, - cam_params.port + "v4l2sink device=/dev/video60 sync=false" ); + else { + char output_str[MAX_OUTPUT_PIPELINE_LEN]; + if (strlen(cam_params.filename) == 0) { + sprintf(output_str, + "h264parse config-interval=1 ! " + "rtph264pay pt=96 config-interval=1 ! " + "rtprateshape max-delay-us=%i max-bitrate=%i ! " + "udpsink host=%s port=%i", + cam_params.max_delay, + cam_params.max_bitrate, + cam_params.address, + cam_params.port + ); + } else { + sprintf(output_str, "mpegtsmux ! filesink location=%s -e", cam_params.filename); + } + + if (cam_params.bitrate == 0) { + snprintf(pipe_proc, MAX_PIPELINE_LEN, "%s", output_str); + } else { + snprintf(pipe_proc, MAX_PIPELINE_LEN, + "omxh264dec ! " + "omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames " + "preset-level=FastPreset bitrate=%i iframeinterval=%i ! %s", + cam_params.bitrate, + cam_params.kfi, + output_str + ); + } + } if (!gst_src_init(&argc, &argv, pipe_proc)) return -1; From d67717869e8f18d3b25b5053bb05abc7975fdad1 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Thu, 18 May 2023 12:17:36 +0200 Subject: [PATCH 8/9] Fix bug about resolution mode --- gst/gst_viewer.c | 8 ++++---- gst/thetauvc.c | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index 477ea12..bba49fb 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -223,9 +223,9 @@ main(int argc, char **argv) cam_params.bitrate = DEFAULT_BITRATE; cam_params.max_bitrate = cam_params.bitrate * 1.5; - if (strcmp(DEFAULT_CAM_MODE, "UHD")) + if (strcmp(DEFAULT_CAM_MODE, "UHD") == 0) cam_params.mode = THETAUVC_MODE_UHD_30; - else if (strcmp(DEFAULT_CAM_MODE, "FHD")) + else if (strcmp(DEFAULT_CAM_MODE, "FHD") == 0) cam_params.mode = THETAUVC_MODE_FHD_30; else cam_params.mode = THETAUVC_MODE_UHD_2997; @@ -240,9 +240,9 @@ main(int argc, char **argv) case 'd': cam_params.max_delay = atoi(optarg); break; case 'm': { - if (strcmp(optarg, "UHD")) + if (strcmp(optarg, "UHD") == 0) cam_params.mode = THETAUVC_MODE_UHD_30; - else if (strcmp(optarg, "FHD")) + else if (strcmp(optarg, "FHD") == 0) cam_params.mode = THETAUVC_MODE_FHD_30; else cam_params.mode = THETAUVC_MODE_UHD_2997; diff --git a/gst/thetauvc.c b/gst/thetauvc.c index 097d8fb..417f31e 100644 --- a/gst/thetauvc.c +++ b/gst/thetauvc.c @@ -268,6 +268,8 @@ thetauvc_get_stream_ctrl_format_size(uvc_device_handle_t *devh, return UVC_ERROR_INVALID_MODE; m = &stream_mode[mode]; + + fprintf(stderr, "Select Mode: %i, Width: %i, Height: %i, FPS: %i\n", mode, m->width, m->height, m->fps); res = uvc_get_stream_ctrl_format_size(devh, ctrl, UVC_FRAME_FORMAT_H264, m->width, m->height, m->fps); From a78de12af849fa9e446131041553e99f60b8bc96 Mon Sep 17 00:00:00 2001 From: Juan Torres Date: Thu, 18 May 2023 19:10:37 +0200 Subject: [PATCH 9/9] Fix issues with gst pipeline --- gst/gst_viewer.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gst/gst_viewer.c b/gst/gst_viewer.c index bba49fb..2641cae 100644 --- a/gst/gst_viewer.c +++ b/gst/gst_viewer.c @@ -123,7 +123,7 @@ gst_src_init(int *argc, char ***argv, char *pipeline) snprintf(pipeline_str, MAX_PIPELINE_LEN+50, "appsrc name=ap ! queue ! h264parse config-interval=1 ! queue ! %s ", pipeline); - fprintf(stderr, "%s/n", pipeline_str); + fprintf(stderr, "%s\n", pipeline_str); gst_init(argc, argv); src.timer = g_timer_new(); @@ -270,7 +270,6 @@ main(int argc, char **argv) char output_str[MAX_OUTPUT_PIPELINE_LEN]; if (strlen(cam_params.filename) == 0) { sprintf(output_str, - "h264parse config-interval=1 ! " "rtph264pay pt=96 config-interval=1 ! " "rtprateshape max-delay-us=%i max-bitrate=%i ! " "udpsink host=%s port=%i", @@ -284,12 +283,14 @@ main(int argc, char **argv) } if (cam_params.bitrate == 0) { - snprintf(pipe_proc, MAX_PIPELINE_LEN, "%s", output_str); + // TODO There is some CAPS issue that generate wrong PTS + snprintf(pipe_proc, MAX_PIPELINE_LEN, "video/x-h264,framerate=30000/1001 ! %s", output_str); } else { snprintf(pipe_proc, MAX_PIPELINE_LEN, "omxh264dec ! " "omxh264enc insert-sps-pps=true profile=main control-rate=constant-skip-frames " - "preset-level=FastPreset bitrate=%i iframeinterval=%i ! %s", + "preset-level=FastPreset bitrate=%i iframeinterval=%i ! " + "h264parse config-interval=1 ! %s", cam_params.bitrate, cam_params.kfi, output_str