Browse Source

obs-ffmpeg: Direct setting of encryption & auth for SRT & RIST

Also modifies UI.

This allows the direct use of passphrase (SRT & RIST) used for
encryption, user + password (RIST) as well as streamid (SRT).
Previously, these parameters had to be set in the URL in the form:
URL?option1=value1&option2=value2.
They still can but there is also the option to set them in the stream
key and username/password fields.
SRT:
- the stream_id is set in the stream key (more info on it: [1]);
- the encryption passphrase is set in the password auth field.
RIST:
- the encryption passphrase is set in the stream key;
- the srp_username and srp_password are set in the user/password auth
fields [2].

Additionally, some error logging has been added when there's a
disconnect caused by a wrong password.
Lastly, this solves a bug when auto-reconnect is set and a wrong
passphrase is provided for srt; the output would keep trying to
reconnect. With this commit, an OBS_OUTPUT_INVALID_STREAM signal is
emitted and the stream is immediately stopped.

[1] https://github.com/Haivision/srt/blob/master/docs/features/access-control.md
[2] https://code.videolan.org/rist/librist/-/wikis/Authentication-and-the-ristsrppasswd-Utility

Signed-off-by: pkv <[email protected]>
pkv 3 years ago
parent
commit
fcb6df1f63

+ 3 - 0
UI/data/locale/en-US.ini

@@ -204,6 +204,7 @@ Basic.AutoConfig.StreamPage.Service.ShowAll="Show All..."
 Basic.AutoConfig.StreamPage.Service.Custom="Custom..."
 Basic.AutoConfig.StreamPage.Server="Server"
 Basic.AutoConfig.StreamPage.StreamKey="Stream Key"
+Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: enter the encryption passphrase.\nRTMP: enter the key provided by the service.\nSRT: enter the streamid if the service uses one."
 Basic.AutoConfig.StreamPage.StreamKey.LinkToSite="(Link)"
 Basic.AutoConfig.StreamPage.EncoderKey="Encoder Key"
 Basic.AutoConfig.StreamPage.ConnectedAccount="Connected account"
@@ -890,6 +891,8 @@ Basic.Settings.Stream.StreamType="Stream Type"
 Basic.Settings.Stream.Custom.UseAuthentication="Use authentication"
 Basic.Settings.Stream.Custom.Username="Username"
 Basic.Settings.Stream.Custom.Password="Password"
+Basic.Settings.Stream.Custom.Username.ToolTip="RIST: enter the srp_username.\nRTMP: enter the username.\nSRT: not used."
+Basic.Settings.Stream.Custom.Password.ToolTip="RIST: enter the srp_password.\nRTMP: enter the password.\nSRT: enter the encryption passphrase."
 Basic.Settings.Stream.BandwidthTestMode="Enable Bandwidth Test Mode"
 Basic.Settings.Stream.TTVAddon="Twitch Chat Add-Ons"
 Basic.Settings.Stream.TTVAddon.None="None"

+ 24 - 1
UI/window-basic-settings-stream.cpp

@@ -132,6 +132,29 @@ void OBSBasicSettings::LoadStream1Settings()
 		ui->authUsername->setText(QT_UTF8(username));
 		ui->authPw->setText(QT_UTF8(password));
 		ui->useAuth->setChecked(use_auth);
+
+		/* add tooltips for stream key, user, password fields */
+		QString file = !App()->IsThemeDark()
+				       ? ":/res/images/help.svg"
+				       : ":/res/images/help_light.svg";
+		QString lStr = "<html>%1 <img src='%2' style=' \
+				vertical-align: bottom;  \
+				' /></html>";
+
+		ui->streamKeyLabel->setText(
+			lStr.arg(ui->streamKeyLabel->text(), file));
+		ui->streamKeyLabel->setToolTip(
+			QTStr("Basic.AutoConfig.StreamPage.StreamKey.ToolTip"));
+
+		ui->authUsernameLabel->setText(
+			lStr.arg(ui->authUsernameLabel->text(), file));
+		ui->authUsernameLabel->setToolTip(
+			QTStr("Basic.Settings.Stream.Custom.Username.ToolTip"));
+
+		ui->authPwLabel->setText(
+			lStr.arg(ui->authPwLabel->text(), file));
+		ui->authPwLabel->setToolTip(
+			QTStr("Basic.Settings.Stream.Custom.Password.ToolTip"));
 	} else {
 		int idx = ui->service->findText(service);
 		if (idx == -1) {
@@ -305,7 +328,7 @@ void OBSBasicSettings::UpdateKeyLink()
 	if (serviceName == "Dacast") {
 		ui->streamKeyLabel->setText(
 			QTStr("Basic.AutoConfig.StreamPage.EncoderKey"));
-	} else {
+	} else if (!IsCustomService()) {
 		ui->streamKeyLabel->setText(
 			QTStr("Basic.AutoConfig.StreamPage.StreamKey"));
 	}

+ 43 - 2
plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c

@@ -347,11 +347,50 @@ static inline int connect_mpegts_url(struct ffmpeg_output *stream, bool is_rist)
 					"Can not allocate memory.");
 		goto fail;
 	}
+	/* For SRT, pass streamid & passphrase; for RIST, pass passphrase, username
+	 * & password.
+	 */
+	if (!is_rist) {
+		SRTContext *context = (SRTContext *)uc->priv_data;
+		context->streamid = NULL;
+		if (stream->ff_data.config.key != NULL) {
+			if (strlen(stream->ff_data.config.key))
+				context->streamid =
+					av_strdup(stream->ff_data.config.key);
+		}
+		context->passphrase = NULL;
+		if (stream->ff_data.config.password != NULL) {
+			if (strlen(stream->ff_data.config.password))
+				context->passphrase = av_strdup(
+					stream->ff_data.config.password);
+		}
+	} else {
+		RISTContext *context = (RISTContext *)uc->priv_data;
+		context->secret = NULL;
+		if (stream->ff_data.config.key != NULL) {
+			if (strlen(stream->ff_data.config.key))
+				context->secret =
+					bstrdup(stream->ff_data.config.key);
+		}
+		context->username = NULL;
+		if (stream->ff_data.config.username != NULL) {
+			if (strlen(stream->ff_data.config.username))
+				context->username = bstrdup(
+					stream->ff_data.config.username);
+		}
+		context->password = NULL;
+		if (stream->ff_data.config.password != NULL) {
+			if (strlen(stream->ff_data.config.password))
+				context->password = bstrdup(
+					stream->ff_data.config.password);
+		}
+	}
 	stream->h = uc;
 	if (is_rist)
 		err = librist_open(uc, uc->url);
 	else
 		err = libsrt_open(uc, uc->url);
+
 	if (err < 0)
 		goto fail;
 	return 0;
@@ -839,13 +878,15 @@ static bool set_config(struct ffmpeg_output *stream)
 	int ret;
 	int code;
 
-	/* 1. Get URL from service & set format + mime-type. */
+	/* 1. Get URL/username/password from service & set format + mime-type. */
 	obs_service_t *service;
 	service = obs_output_get_service(stream->output);
 	if (!service)
 		return false;
 	config.url = obs_service_get_url(service);
-
+	config.username = obs_service_get_username(service);
+	config.password = obs_service_get_password(service);
+	config.key = obs_service_get_key(service);
 	config.format_name = "mpegts";
 	config.format_mime_type = "video/M2PT";
 

+ 3 - 0
plugins/obs-ffmpeg/obs-ffmpeg-output.h

@@ -37,6 +37,9 @@ struct ffmpeg_cfg {
 	int width;
 	int height;
 	int frame_size; // audio frame size
+	const char *username;
+	const char *password;
+	const char *key;
 };
 
 struct ffmpeg_audio_info {

+ 14 - 3
plugins/obs-ffmpeg/obs-ffmpeg-rist.h

@@ -44,7 +44,8 @@ typedef struct RISTContext {
 	int fifo_shift;
 	bool overrun_nonfatal;
 	char *secret;
-
+	char *username;
+	char *password;
 	struct rist_logging_settings logging_settings;
 	struct rist_peer_config peer_config;
 
@@ -100,7 +101,12 @@ static int librist_close(URLContext *h)
 {
 	RISTContext *s = h->priv_data;
 	int ret = 0;
-
+	if (s->secret)
+		bfree(s->secret);
+	if (s->username)
+		bfree(s->username);
+	if (s->password)
+		bfree(s->password);
 	s->peer = NULL;
 
 	if (s->ctx)
@@ -150,7 +156,6 @@ static int librist_open(URLContext *h, const char *uri)
 	s->packet_size = 1316;
 	s->log_level = RIST_LOG_INFO;
 	s->encryption = 0;
-	s->secret = NULL;
 	s->overrun_nonfatal = 0;
 	s->fifo_shift = FF_LIBRIST_FIFO_DEFAULT_SHIFT;
 	s->logging_settings =
@@ -219,6 +224,12 @@ static int librist_open(URLContext *h, const char *uri)
 		peer_config->recovery_length_min = s->buffer_size;
 		peer_config->recovery_length_max = s->buffer_size;
 	}
+	if (s->username && peer_config->srp_username[0] == 0)
+		av_strlcpy(peer_config->srp_username, s->username,
+			   RIST_MAX_STRING_LONG);
+	if (s->password && peer_config->srp_password[0] == 0)
+		av_strlcpy(peer_config->srp_password, s->password,
+			   RIST_MAX_STRING_LONG);
 
 	ret = rist_peer_create(s->ctx, &s->peer, &s->peer_config);
 	if (ret < 0) {

+ 30 - 3
plugins/obs-ffmpeg/obs-ffmpeg-srt.h

@@ -80,12 +80,24 @@ typedef struct SRTContext {
 
 static int libsrt_neterrno(URLContext *h)
 {
+	SRTContext *s = (SRTContext *)h->priv_data;
 	int os_errno;
 	int err = srt_getlasterror(&os_errno);
 	blog(LOG_ERROR, "[obs-ffmpeg mpegts muxer / libsrt] : %s\n",
 	     srt_getlasterror_str());
 	if (err == SRT_EASYNCRCV || err == SRT_EASYNCSND)
 		return AVERROR(EAGAIN);
+	if (err = SRT_ECONNREJ) {
+		int errj = srt_getrejectreason(s->fd);
+		if (errj == SRT_REJ_BADSECRET)
+			blog(LOG_ERROR,
+			     "[obs-ffmpeg mpegts muxer / libsrt] : Wrong password");
+		else
+			blog(LOG_ERROR,
+			     "[obs-ffmpeg mpegts muxer / libsrt] : Connection rejected, %s",
+			     srt_rejectreason_str(errj));
+	}
+
 	return os_errno ? AVERROR(os_errno) : AVERROR_UNKNOWN;
 }
 
@@ -139,6 +151,20 @@ static int libsrt_network_wait_fd(URLContext *h, int eid, int write)
 		ret = srt_epoll_wait(eid, ready, &len, error, &errlen,
 				     POLLING_TIME, 0, 0, 0, 0);
 	}
+	if (len == 1 && errlen == 1) {
+		/* Socket reported in wsock AND rsock signifies an error. */
+		int reason = srt_getrejectreason(*ready);
+
+		if (reason == SRT_REJ_BADSECRET || reason == SRT_REJ_UNSECURE) {
+			blog(LOG_ERROR,
+			     "[obs-ffmpeg mpegts muxer / libsrt] : Connection rejected, wrong password");
+			return OBS_OUTPUT_INVALID_STREAM;
+		} else {
+			blog(LOG_ERROR,
+			     "[obs-ffmpeg mpegts muxer / libsrt] : Connection rejected, %s",
+			     srt_rejectreason_str(reason));
+		}
+	}
 	if (ret < 0) {
 		if (srt_getlasterror(NULL) == SRT_ETIMEOUT)
 			ret = AVERROR(EAGAIN);
@@ -574,7 +600,6 @@ static void libsrt_set_defaults(SRTContext *s)
 	s->payload_size = SRT_LIVE_DEFAULT_PAYLOAD_SIZE;
 	s->maxbw = -1;
 	s->pbkeylen = -1;
-	s->passphrase = NULL;
 	s->mss = -1;
 	s->ffs = -1;
 	s->ipttl = -1;
@@ -592,7 +617,6 @@ static void libsrt_set_defaults(SRTContext *s)
 	s->rcvbuf = -1;
 	s->lossmaxttl = -1;
 	s->minversion = -1;
-	s->streamid = NULL;
 	s->smoother = NULL;
 	s->messageapi = -1;
 	s->transtype = SRTT_LIVE;
@@ -814,7 +838,10 @@ static int libsrt_write(URLContext *h, const uint8_t *buf, int size)
 static int libsrt_close(URLContext *h)
 {
 	SRTContext *s = (SRTContext *)h->priv_data;
-
+	if (s->streamid)
+		av_freep(&s->streamid);
+	if (s->passphrase)
+		av_freep(&s->passphrase);
 	/* Log stream stats. */
 	SRT_TRACEBSTATS perf;
 	srt_bstats(s->fd, &perf, 1);