gstreamer/subprojects/gst-plugins-bad/gst/rtmp2/gstrtmp2locationhandler.c
Sebastian Dröge 889a3fe932 rtmp2: Set default flash version to NULL
This is consistent with the librtmp-based old rtmp plugin and ffmpeg.
While some servers require a valid flash-version, others are failing
with a too long or any flash-version at all.

By changing to the same default as in the old plugin and in ffmpeg,
GStreamer will at least behave the same and will work and fail with the
same servers without setting a flash-version.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5293>
2023-09-11 08:23:33 +00:00

299 lines
10 KiB
C

/* GStreamer
* Copyright (C) 2017 Make.TV, Inc. <info@make.tv>
* Contact: Jan Alexander Steffens (heftig) <jsteffens@make.tv>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "gstrtmp2locationhandler.h"
#include "rtmp/rtmputils.h"
#include "rtmp/rtmpclient.h"
#include <string.h>
#define DEFAULT_SCHEME GST_RTMP_SCHEME_RTMP
#define DEFAULT_HOST "localhost"
#define DEFAULT_APPLICATION "live"
#define DEFAULT_STREAM "myStream"
#define DEFAULT_LOCATION "rtmp://" DEFAULT_HOST "/" DEFAULT_APPLICATION "/" DEFAULT_STREAM
#define DEFAULT_SECURE_TOKEN NULL
#define DEFAULT_USERNAME NULL
#define DEFAULT_PASSWORD NULL
#define DEFAULT_AUTHMOD GST_RTMP_AUTHMOD_AUTO
#define DEFAULT_TIMEOUT 5
#define DEFAULT_FLASH_VERSION NULL
G_DEFINE_INTERFACE (GstRtmpLocationHandler, gst_rtmp_location_handler, 0);
#define GST_CAT_DEFAULT gst_rtmp_location_handler_debug_category
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void
gst_rtmp_location_handler_default_init (GstRtmpLocationHandlerInterface * iface)
{
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtmp2locationhandler", 0,
"RTMP2 Location Handling");
g_object_interface_install_property (iface, g_param_spec_string ("location",
"Location", "Location of RTMP stream to access", DEFAULT_LOCATION,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_enum ("scheme",
"Scheme", "RTMP connection scheme",
GST_TYPE_RTMP_SCHEME, DEFAULT_SCHEME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("host",
"Host", "RTMP server host name", DEFAULT_HOST,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_int ("port", "Port",
"RTMP server port", 1, 65535,
gst_rtmp_scheme_get_default_port (DEFAULT_SCHEME),
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_string ("application", "Application",
"RTMP application path", DEFAULT_APPLICATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("stream",
"Stream", "RTMP stream path", DEFAULT_STREAM,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("username",
"User name", "RTMP authorization user name", DEFAULT_USERNAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("password",
"Password", "RTMP authorization password", DEFAULT_PASSWORD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_string ("secure-token", "Secure token",
"RTMP authorization token", DEFAULT_SECURE_TOKEN,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_enum ("authmod",
"Authorization mode", "RTMP authorization mode",
GST_TYPE_RTMP_AUTHMOD, DEFAULT_AUTHMOD,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_uint ("timeout",
"Timeout", "RTMP timeout in seconds", 0, G_MAXUINT, DEFAULT_TIMEOUT,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtmpLocationHandler::tls-validation-flags:
*
* TLS certificate validation flags used to validate server
* certificate.
*
* GLib guarantees that if certificate verification fails, at least one
* error will be set, but it does not guarantee that all possible errors
* will be set. Accordingly, you may not safely decide to ignore any
* particular type of error.
*
* For example, it would be incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if
* you want to allow expired certificates, because this could potentially be
* the only error flag set even if other problems exist with the
* certificate.
*/
g_object_interface_install_property (iface,
g_param_spec_flags ("tls-validation-flags", "TLS validation flags",
"TLS validation flags to use", G_TYPE_TLS_CERTIFICATE_FLAGS,
G_TLS_CERTIFICATE_VALIDATE_ALL,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_string ("flash-version", "Flash version",
"Flash version reported to the server", DEFAULT_FLASH_VERSION,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static GstURIType
uri_handler_get_type_sink (GType type)
{
return GST_URI_SINK;
}
static GstURIType
uri_handler_get_type_src (GType type)
{
return GST_URI_SRC;
}
static const gchar *const *
uri_handler_get_protocols (GType type)
{
return gst_rtmp_scheme_get_strings ();
}
static gchar *
uri_handler_get_uri (GstURIHandler * handler)
{
GstRtmpLocation location = { 0, };
gchar *string;
g_object_get (handler, "scheme", &location.scheme, "host", &location.host,
"port", &location.port, "application", &location.application,
"stream", &location.stream, NULL);
string = gst_rtmp_location_get_string (&location, TRUE);
gst_rtmp_location_clear (&location);
return string;
}
static gboolean
uri_handler_set_uri (GstURIHandler * handler, const gchar * string,
GError ** error)
{
GstRtmpLocationHandler *self = GST_RTMP_LOCATION_HANDLER (handler);
GstUri *uri;
const gchar *scheme_sep, *path_sep, *stream_sep, *host, *userinfo;
GstRtmpScheme scheme;
guint port;
gboolean ret = FALSE;
GST_DEBUG_OBJECT (self, "setting URI from %s", GST_STR_NULL (string));
g_return_val_if_fail (string, FALSE);
scheme_sep = strstr (string, "://");
if (!scheme_sep) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks scheme: %s", string);
return FALSE;
}
path_sep = strchr (scheme_sep + 3, '/');
if (!path_sep) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks path: %s", string);
return FALSE;
}
stream_sep = strrchr (path_sep + 1, '/');
if (!stream_sep) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks stream: %s", string);
return FALSE;
}
{
gchar *string_without_path = g_strndup (string, path_sep - string);
uri = gst_uri_from_string_escaped (string_without_path);
g_free (string_without_path);
}
if (!uri) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
"URI failed to parse: %s", string);
return FALSE;
}
gst_uri_normalize (uri);
scheme = gst_rtmp_scheme_from_uri (uri);
if (scheme < 0) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI has bad scheme: %s", string);
goto out;
}
host = gst_uri_get_host (uri);
if (!host) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks hostname: %s", string);
goto out;
}
port = gst_uri_get_port (uri);
if (port == GST_URI_NO_PORT) {
port = gst_rtmp_scheme_get_default_port (scheme);
}
{
const gchar *path = path_sep + 1, *stream = stream_sep + 1;
gchar *application = g_strndup (path, stream_sep - path);
GST_DEBUG_OBJECT (self, "setting location to %s://%s:%u/%s stream %s",
gst_rtmp_scheme_to_string (scheme), host, port, application, stream);
g_object_set (self, "scheme", scheme, "host", host, "port", port,
"application", application, "stream", stream, "username", NULL,
"password", NULL, NULL);
g_free (application);
}
userinfo = gst_uri_get_userinfo (uri);
if (userinfo) {
gchar *user, *pass;
gchar **split = g_strsplit (userinfo, ":", 2);
if (!split || !split[0] || !split[1]) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"Failed to parse username:password data");
g_strfreev (split);
goto out;
}
if (g_strrstr (split[1], ":") != NULL)
GST_WARNING_OBJECT (self, "userinfo %s contains more than one ':', will "
"assume that the first ':' delineates user:pass. You should escape "
"the user and pass before adding to the URI.", userinfo);
user = g_uri_unescape_string (split[0], NULL);
pass = g_uri_unescape_string (split[1], NULL);
g_strfreev (split);
g_object_set (self, "username", user, "password", pass, NULL);
g_free (user);
g_free (pass);
}
ret = TRUE;
out:
gst_uri_unref (uri);
return ret;
}
void
gst_rtmp_location_handler_implement_uri_handler (GstURIHandlerInterface * iface,
GstURIType type)
{
switch (type) {
case GST_URI_SINK:
iface->get_type = uri_handler_get_type_sink;
break;
case GST_URI_SRC:
iface->get_type = uri_handler_get_type_src;
break;
default:
g_return_if_reached ();
}
iface->get_protocols = uri_handler_get_protocols;
iface->get_uri = uri_handler_get_uri;
iface->set_uri = uri_handler_set_uri;
}
gboolean
gst_rtmp_location_handler_set_uri (GstRtmpLocationHandler * handler,
const gchar * uri)
{
GError *error = NULL;
gboolean ret;
g_return_val_if_fail (GST_IS_RTMP_LOCATION_HANDLER (handler), FALSE);
ret = gst_uri_handler_set_uri (GST_URI_HANDLER (handler), uri, &error);
if (!ret) {
GST_ERROR_OBJECT (handler, "Failed to set URI: %s", error->message);
g_object_set (handler, "scheme", DEFAULT_SCHEME, "host", NULL,
"port", gst_rtmp_scheme_get_default_port (DEFAULT_SCHEME),
"application", NULL, "stream", NULL, NULL);
g_error_free (error);
}
return ret;
}