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: (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);