From 2c3fcdcf8ddf01608b196493d16f775678da24ab Mon Sep 17 00:00:00 2001 From: "F. Duncanh" Date: Fri, 26 Sep 2025 01:10:41 -0400 Subject: [PATCH] add -vrtp option (requested by @tovrstra ) --- README.html | 19 ++++++++++++-- README.md | 16 ++++++++++-- README.txt | 19 ++++++++++++-- renderers/video_renderer.c | 53 +++++++++++++++++++++----------------- renderers/video_renderer.h | 2 +- uxplay.1 | 7 +++++ uxplay.cpp | 17 ++++++++++-- 7 files changed, 101 insertions(+), 32 deletions(-) diff --git a/README.html b/README.html index 2779dd3..cce5d90 100644 --- a/README.html +++ b/README.html @@ -21,6 +21,13 @@ href="#bluetooth-le-beacon-setup">given below. It is hoped that users will submit Pull Requests contributing scripts for automating beacon setup on all platforms. (Python may be an appropriate language choice)

+
  • NEW on github: option +-vrtp <rest-of-pipeline> bypasses rendering by +UxPlay, and instead transmits rtp packets of decrypted h264 or h265 +video to an external renderer (e.g. OBS Studio) at an address specified +in rest-of-pipeline. (Note: this is video only, an option +“-rtp” which muxes audio and video into a mpeg4 container still needs to +be created: Pull Requests welcomed).

  • NEW on github: (for Linux/*BSD Desktop Environments using D-Bus). New option -scrsv <n> provides screensaver inhibition (e.g., to prevent screensaver function @@ -1250,6 +1257,12 @@ display video), and only used to render audio, which will be AAC lossily-compressed audio in mirror mode with unrendered video, and superior-quality ALAC Apple Lossless audio in Airplay audio-only mode.

    +

    -vrtp pipeline: forward rtp packets of +decrypted video to somewhere else, without rendering. Uses rtph264pay or +rtph265pay as appropriate: pipeline should start with any +rtph26xpay options (such as config_interval= or aggregate-mode =), +followed by a sending method: e.g., +"config-interval=1 ! udpsink host=127.0.0.1 port=5000“.

    -v4l2 Video settings for hardware h264 video decoding in the GPU by Video4Linux2. Equivalent to -vd v4l2h264dec -vc v4l2convert.

    @@ -1933,10 +1946,12 @@ an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen, introduced 2017, running tvOS 12.2.1), so it does not seem to matter what version UxPlay claims to be.

    Changelog

    -

    xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option +

    xxxx 2025-09-25 Render Audio cover-art inside UxPlay with -ca option (no file specified). (D-Bus based) option -scrsv to inhibit screensaver while UxPlay is running (Linux/*BSD only). Add support for -Service Discovery using a Bluetooth LE beacon.

    +Service Discovery using a Bluetooth LE beacon. Add -vrtp option for +forwarding decrypted h264/5 video to an external renderer (e.g., OBS +Studio)

    1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with -pw option. Update llhttp to v 9.3.0

    1.72.1 2025-06-06 minor update: fix regression in -reg option; add diff --git a/README.md b/README.md index 9208d5b..ca43052 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ __It is hoped that users will submit Pull Requests contributing scripts for automating beacon setup on all platforms. (Python may be an appropriate language choice)__ +- **NEW on github**: option `-vrtp ` bypasses rendering by UxPlay, and instead + transmits rtp packets of decrypted h264 or h265 video to + an external renderer (e.g. OBS Studio) at an address specified in `rest-of-pipeline`. + (Note: this is video only, an option "-rtp" which muxes audio and video into a mpeg4 container still needs to be created: + Pull Requests welcomed). + - **NEW on github**: (for Linux/*BSD Desktop Environments using D-Bus). New option `-scrsv ` provides screensaver inhibition (e.g., to prevent screensaver function while watching mirrored videos without keyboard or mouse activity): n = 0 (off) n=1 (on during video activity) n=2 (always on while UxPlay is running). @@ -1252,6 +1258,11 @@ video), and only used to render audio, which will be AAC lossily-compressed audio in mirror mode with unrendered video, and superior-quality ALAC Apple Lossless audio in Airplay audio-only mode. +**-vrtp *pipeline***: forward rtp packets of decrypted video to somewhere else, without rendering. +Uses rtph264pay or rtph265pay as appropriate: *pipeline* should start with any +rtph26xpay options (such as config_interval= or aggregate-mode =), followed by +a sending method: *e.g.*, `"config-interval=1 ! udpsink host=127.0.0.1 port=5000`". + **-v4l2** Video settings for hardware h264 video decoding in the GPU by Video4Linux2. Equivalent to `-vd v4l2h264dec -vc v4l2convert`. @@ -1957,10 +1968,11 @@ introduced 2017, running tvOS 12.2.1), so it does not seem to matter what version UxPlay claims to be. # Changelog -xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option (no file +xxxx 2025-09-25 Render Audio cover-art inside UxPlay with -ca option (no file specified). (D-Bus based) option -scrsv to inhibit screensaver while UxPlay is running (Linux/*BSD only). Add support for Service Discovery using a -Bluetooth LE beacon. +Bluetooth LE beacon. Add -vrtp option for forwarding decrypted h264/5 video +to an external renderer (e.g., OBS Studio) 1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with -pw option. Update llhttp to v 9.3.0 diff --git a/README.txt b/README.txt index 3e0017d..9091dcc 100644 --- a/README.txt +++ b/README.txt @@ -16,6 +16,13 @@ setup on all platforms. (Python may be an appropriate language choice)** +- **NEW on github**: option `-vrtp ` bypasses + rendering by UxPlay, and instead transmits rtp packets of decrypted + h264 or h265 video to an external renderer (e.g. OBS Studio) at an + address specified in `rest-of-pipeline`. (Note: this is video only, + an option "-rtp" which muxes audio and video into a mpeg4 container + still needs to be created: Pull Requests welcomed). + - **NEW on github**: (for Linux/\*BSD Desktop Environments using D-Bus). New option `-scrsv ` provides screensaver inhibition (e.g., to prevent screensaver function while watching mirrored @@ -1276,6 +1283,12 @@ video), and only used to render audio, which will be AAC lossily-compressed audio in mirror mode with unrendered video, and superior-quality ALAC Apple Lossless audio in Airplay audio-only mode. +**-vrtp *pipeline***: forward rtp packets of decrypted video to +somewhere else, without rendering. Uses rtph264pay or rtph265pay as +appropriate: *pipeline* should start with any rtph26xpay options (such +as config_interval= or aggregate-mode =), followed by a sending method: +*e.g.*, `"config-interval=1 ! udpsink host=127.0.0.1 port=5000`". + **-v4l2** Video settings for hardware h264 video decoding in the GPU by Video4Linux2. Equivalent to `-vd v4l2h264dec -vc v4l2convert`. @@ -2006,10 +2019,12 @@ what version UxPlay claims to be. # Changelog -xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option (no +xxxx 2025-09-25 Render Audio cover-art inside UxPlay with -ca option (no file specified). (D-Bus based) option -scrsv ``{=html} to inhibit screensaver while UxPlay is running (Linux/\*BSD only). Add support for -Service Discovery using a Bluetooth LE beacon. +Service Discovery using a Bluetooth LE beacon. Add -vrtp option for +forwarding decrypted h264/5 video to an external renderer (e.g., OBS +Studio) 1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with -pw option. Update llhttp to v 9.3.0 diff --git a/renderers/video_renderer.c b/renderers/video_renderer.c index 9cea88e..96ce25e 100644 --- a/renderers/video_renderer.c +++ b/renderers/video_renderer.c @@ -222,11 +222,12 @@ GstElement *make_video_sink(const char *videosink, const char *videosink_options return video_sink; } -void video_renderer_init(logger_t *render_logger, const char *server_name, videoflip_t videoflip[2], const char *parser, +void video_renderer_init(logger_t *render_logger, const char *server_name, videoflip_t videoflip[2], const char *parser, const char * rtp_pipeline, const char *decoder, const char *converter, const char *videosink, const char *videosink_options, bool initial_fullscreen, bool video_sync, bool h265_support, bool coverart_support, guint playbin_version, const char *uri) { GError *error = NULL; GstCaps *caps = NULL; + bool rtp = (bool) strlen(rtp_pipeline); hls_video = (uri != NULL); /* videosink choices that are auto */ auto_videosink = (strstr(videosink, "autovideosink") || strstr(videosink, "fpsdisplaysink")); @@ -332,30 +333,36 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, video g_string_append(launch, "queue ! "); g_string_append(launch, parser); g_string_append(launch, " ! "); - g_string_append(launch, decoder); + if (!rtp) { + g_string_append(launch, decoder); + } else { + g_string_append(launch, "rtph264pay "); + g_string_append(launch, rtp_pipeline); + } } - g_string_append(launch, " ! "); - append_videoflip(launch, &videoflip[0], &videoflip[1]); - g_string_append(launch, converter); - g_string_append(launch, " ! "); - g_string_append(launch, "videoscale ! "); - if (jpeg_pipeline) { - g_string_append(launch, " imagefreeze allow-replace=TRUE ! "); + if (!rtp || jpeg_pipeline) { + g_string_append(launch, " ! "); + append_videoflip(launch, &videoflip[0], &videoflip[1]); + g_string_append(launch, converter); + g_string_append(launch, " ! "); + g_string_append(launch, "videoscale ! "); + if (jpeg_pipeline) { + g_string_append(launch, " imagefreeze allow-replace=TRUE ! "); + } + g_string_append(launch, videosink); + g_string_append(launch, " name="); + g_string_append(launch, videosink); + g_string_append(launch, "_"); + g_string_append(launch, renderer_type[i]->codec); + g_string_append(launch, videosink_options); + if (video_sync && !jpeg_pipeline) { + g_string_append(launch, " sync=true"); + sync = true; + } else { + g_string_append(launch, " sync=false"); + sync = false; + } } - g_string_append(launch, videosink); - g_string_append(launch, " name="); - g_string_append(launch, videosink); - g_string_append(launch, "_"); - g_string_append(launch, renderer_type[i]->codec); - g_string_append(launch, videosink_options); - if (video_sync && !jpeg_pipeline) { - g_string_append(launch, " sync=true"); - sync = true; - } else { - g_string_append(launch, " sync=false"); - sync = false; - } - if (!strcmp(renderer_type[i]->codec, h264)) { char *pos = launch->str; while ((pos = strstr(pos,h265))){ diff --git a/renderers/video_renderer.h b/renderers/video_renderer.h index 8e96496..d280592 100644 --- a/renderers/video_renderer.h +++ b/renderers/video_renderer.h @@ -47,7 +47,7 @@ typedef enum videoflip_e { typedef struct video_renderer_s video_renderer_t; -void video_renderer_init (logger_t *logger, const char *server_name, videoflip_t videoflip[2], const char *parser, + void video_renderer_init (logger_t *logger, const char *server_name, videoflip_t videoflip[2], const char *parser, const char *rtp_pipeline, const char *decoder, const char *converter, const char *videosink, const char *videosink_options, bool initial_fullscreen, bool video_sync, bool h265_support, bool coverart_support, guint playbin_version, const char *uri); diff --git a/uxplay.1 b/uxplay.1 index 32bec7b..7a1fde2 100644 --- a/uxplay.1 +++ b/uxplay.1 @@ -98,6 +98,13 @@ UxPlay 1.72: An open\-source AirPlay mirroring (+ audio streaming) server: .TP \fB\-vs\fR 0 Streamed audio only, with no video display window. .TP +\fB\-vrtp\fI pl\fR Use rtph26[4,5]pay to send decoded video elsewhere: "pl" +.IP + is the remaining pipeline, starting with rtph26*pay options: +.IP + e.g. "config-interval=1 ! udpsink host=127.0.0.1 port=5000" +.PP +.TP \fB\-v4l2\fR Use Video4Linux2 for GPU hardware h264 video decoding. .TP \fB\-bt709\fR Sometimes needed for Raspberry Pi models using Video4Linux2. diff --git a/uxplay.cpp b/uxplay.cpp index 218d629..1b6cd7b 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -190,6 +190,8 @@ static uint32_t rtptime_coverart_expired = 0; static std::string artist; static std::string coverart_artist; static std::string ble_filename = ""; +static std::string rtp_pipeline = ""; + //Support for D-Bus-based screensaver inhibition (org.freedesktop.ScreenSaver) static unsigned int scrsv; @@ -879,6 +881,9 @@ static void print_info (char *name) { printf(" gtksink,waylandsink,kmssink,fbdevsink,osxvideosink,\n"); printf(" d3d11videosink,d3v12videosink, etc.\n"); printf("-vs 0 Streamed audio only, with no video display window\n"); + printf("-vrtp pl Use rtph26[4,5]pay to send decoded video elsewhere: \"pl\"\n"); + printf(" is the remaining pipeline, starting with rtph26*pay options:\n"); + printf(" e.g. \"config-interval=1 ! udpsink host=127.0.0.1 port=5000\"\n"); printf("-v4l2 Use Video4Linux2 for GPU hardware h264 decoding\n"); printf("-bt709 Sometimes needed for Raspberry Pi models using Video4Linux2 \n"); printf("-srgb Display \"Full range\" [0-255] color, not \"Limited Range\"[16-235]\n"); @@ -1297,6 +1302,14 @@ static void parse_arguments (int argc, char *argv[]) { fprintf(stderr, "invalid \"-reset %s\"; -reset n must have n >= 0, default n = %d seconds\n", argv[i], MISSED_FEEDBACK_LIMIT); exit(1); } + } else if (arg == "-vrtp") { + if (!option_has_value(i, argc, arg, argv[i+1])) { + fprintf(stderr,"option \"-vrtp\" must be followed by a pipeline for sending the video stream:\n" + "e.g., \" ! udpsink host=127.0.0.1 port -= 5000\"\n"); + exit(1); + } + rtp_pipeline.erase(); + rtp_pipeline.append(argv[++i]); } else if (arg == "-vdmp") { dump_video = true; if (i < argc - 1 && *argv[i+1] != '-') { @@ -2817,7 +2830,7 @@ int main (int argc, char *argv[]) { LOGI("audio_disabled"); } if (use_video) { - video_renderer_init(render_logger, server_name.c_str(), videoflip, video_parser.c_str(), + video_renderer_init(render_logger, server_name.c_str(), videoflip, video_parser.c_str(), rtp_pipeline.c_str(), video_decoder.c_str(), video_converter.c_str(), videosink.c_str(), videosink_options.c_str(), fullscreen, video_sync, h265_support, render_coverart, playbin_version, NULL); @@ -2914,7 +2927,7 @@ int main (int argc, char *argv[]) { raop_remove_known_connections(raop); } const char *uri = (url.empty() ? NULL : url.c_str()); - video_renderer_init(render_logger, server_name.c_str(), videoflip, video_parser.c_str(), + video_renderer_init(render_logger, server_name.c_str(), videoflip, video_parser.c_str(),rtp_pipeline.c_str(), video_decoder.c_str(), video_converter.c_str(), videosink.c_str(), videosink_options.c_str(), fullscreen, video_sync, h265_support, render_coverart, playbin_version, uri);