rtsp-client: allow application to decide what requirements are supported
authorOgnyan Tonchev <ognyan@axis.com>
Thu, 18 Jun 2015 11:12:04 +0000 (13:12 +0200)
committerTim-Philipp Müller <tim@centricular.com>
Tue, 23 Jun 2015 13:38:29 +0000 (14:38 +0100)
Add "check-requirements" signal and vfunc to allow application
(and subclasses) to check the requirements.

Based on patch from Hyunjun Ko <zzoon.ko@samsung.com>

https://bugzilla.gnome.org/show_bug.cgi?id=749417

gst/rtsp-server/rtsp-client.c
gst/rtsp-server/rtsp-client.h
tests/check/gst/client.c

index c41d019..55f6259 100644 (file)
@@ -127,6 +127,7 @@ enum
   SIGNAL_SEND_MESSAGE,
   SIGNAL_ANNOUNCE_REQUEST,
   SIGNAL_RECORD_REQUEST,
+  SIGNAL_CHECK_REQUIREMENTS,
   SIGNAL_LAST
 };
 
@@ -285,6 +286,24 @@ gst_rtsp_client_class_init (GstRTSPClientClass * klass)
       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
       GST_TYPE_RTSP_CONTEXT);
 
+  /**
+   * GstRTSPClient::check-requirements:
+   * @client: a #GstRTSPClient
+   * @ctx: a #GstRTSPContext
+   * @arr: a NULL-terminated array of strings
+   *
+   * Returns: a newly allocated string with comma-separated list of
+   *          unsupported options. An empty string must be returned if
+   *          all options are supported.
+   *
+   * Since: 1.6
+   */
+  gst_rtsp_client_signals[SIGNAL_CHECK_REQUIREMENTS] =
+      g_signal_new ("check-requirements", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass,
+          check_requirements), NULL, NULL, g_cclosure_marshal_generic,
+      G_TYPE_STRING, 2, GST_TYPE_RTSP_CONTEXT, G_TYPE_STRV);
+
   tunnels =
       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
   g_mutex_init (&tunnels_lock);
@@ -2602,25 +2621,29 @@ client_session_removed (GstRTSPSessionPool * pool, GstRTSPSession * session,
   g_mutex_unlock (&priv->lock);
 }
 
-/* Returns TRUE if there are no Require headers, otherwise returns FALSE
- * and also returns a newly-allocated string of (comma-separated) unsupported
- * options in the unsupported_reqs variable .
+/* Check for Require headers. Returns TRUE if there are no Require headers,
+ * otherwise lets the application decide which headers are supported.
+ * By default all headers are unsupported.
+ * If there are unsupported options, FALSE will be returned together with
+ * a newly-allocated string of (comma-separated) unsupported options in
+ * the unsupported_reqs variable.
  *
  * There may be multiple Require headers, but we must send one single
  * Unsupported header with all the unsupported options as response. If
  * an incoming Require header contained a comma-separated list of options
  * GstRtspConnection will already have split that list up into multiple
  * headers.
- *
- * TODO: allow the application to decide what features are supported
  */
 static gboolean
-check_request_requirements (GstRTSPMessage * msg, gchar ** unsupported_reqs)
+check_request_requirements (GstRTSPContext * ctx, gchar ** unsupported_reqs)
 {
   GstRTSPResult res;
   GPtrArray *arr = NULL;
+  GstRTSPMessage *msg = ctx->request;
   gchar *reqs = NULL;
   gint i;
+  gchar *sig_result = NULL;
+  gboolean result = TRUE;
 
   i = 0;
   do {
@@ -2643,12 +2666,28 @@ check_request_requirements (GstRTSPMessage * msg, gchar ** unsupported_reqs)
   /* otherwise we've now processed at all the Require headers */
   g_ptr_array_add (arr, NULL);
 
-  /* for now we don't commit to supporting anything, so will just report
-   * all of the required options as unsupported */
-  *unsupported_reqs = g_strjoinv (", ", (gchar **) arr->pdata);
+  g_signal_emit (ctx->client,
+      gst_rtsp_client_signals[SIGNAL_CHECK_REQUIREMENTS], 0, ctx,
+      (gchar **) arr->pdata, &sig_result);
 
+  if (sig_result == NULL) {
+    /* no supported options, just report all of the required ones as
+     * unsupported */
+    *unsupported_reqs = g_strjoinv (", ", (gchar **) arr->pdata);
+    result = FALSE;
+    goto done;
+  }
+
+  if (strlen (sig_result) == 0)
+    g_free (sig_result);
+  else {
+    *unsupported_reqs = sig_result;
+    result = FALSE;
+  }
+
+done:
   g_ptr_array_unref (arr);
-  return FALSE;
+  return result;
 }
 
 static void
@@ -2752,7 +2791,7 @@ handle_request (GstRTSPClient * client, GstRTSPMessage * request)
     goto not_authorized;
 
   /* handle any 'Require' headers */
-  if (!check_request_requirements (ctx->request, &unsupported_reqs))
+  if (!check_request_requirements (ctx, &unsupported_reqs))
     goto unsupported_requirement;
 
   /* the backlog must be unlimited while processing requests.
index 5137cc6..6b05448 100644 (file)
@@ -126,9 +126,10 @@ struct _GstRTSPClientClass {
 
   void     (*announce_request)        (GstRTSPClient *client, GstRTSPContext *ctx);
   void     (*record_request)          (GstRTSPClient *client, GstRTSPContext *ctx);
+  gchar*   (*check_requirements)      (GstRTSPClient *client, GstRTSPContext *ctx, gchar ** arr);
 
   /*< private >*/
-  gpointer _gst_reserved[GST_PADDING_LARGE-5];
+  gpointer _gst_reserved[GST_PADDING_LARGE-6];
 };
 
 GType                 gst_rtsp_client_get_type          (void);
index 95e8610..57d80e4 100644 (file)
@@ -24,6 +24,7 @@
 static gchar * session_id;
 static gint cseq;
 static guint expected_session_timeout = 60;
+static const gchar *expected_unsupported_header;
 
 static gboolean
 test_response_200 (GstRTSPClient * client, GstRTSPMessage * response,
@@ -109,6 +110,31 @@ test_response_454 (GstRTSPClient * client, GstRTSPMessage * response,
   return TRUE;
 }
 
+static gboolean
+test_response_551 (GstRTSPClient * client, GstRTSPMessage * response,
+    gboolean close, gpointer user_data)
+{
+  GstRTSPStatusCode code;
+  const gchar *reason;
+  GstRTSPVersion version;
+  gchar *options;
+
+  fail_unless (gst_rtsp_message_get_type (response) ==
+      GST_RTSP_MESSAGE_RESPONSE);
+
+  fail_unless (gst_rtsp_message_parse_response (response, &code, &reason,
+          &version)
+      == GST_RTSP_OK);
+  fail_unless (code == GST_RTSP_STS_OPTION_NOT_SUPPORTED);
+  fail_unless (g_str_equal (reason, "Option not supported"));
+  fail_unless (gst_rtsp_message_get_header (response, GST_RTSP_HDR_UNSUPPORTED,
+      &options, 0) == GST_RTSP_OK);
+  fail_unless (!g_strcmp0 (expected_unsupported_header, options));
+  fail_unless (version == GST_RTSP_VERSION_1_0);
+
+  return TRUE;
+}
+
 static GstRTSPClient *
 setup_client (const gchar * launch_line)
 {
@@ -151,6 +177,114 @@ teardown_client (GstRTSPClient * client)
   g_object_unref (client);
 }
 
+static gchar*
+check_requirements_cb (GstRTSPClient * client, GstRTSPContext * ctx,
+    gchar ** req, gpointer user_data)
+{
+  int index = 0;
+  GString *result = g_string_new ("");
+
+  while (req[index] != NULL) {
+    if (g_strcmp0 (req[index], "test-requirements")) {
+      if (result->len > 0)
+        g_string_append (result, ", ");
+      g_string_append (result, req[index]);
+    }
+    index++;
+  }
+
+  return  g_string_free (result, FALSE);
+}
+
+GST_START_TEST (test_require)
+{
+  GstRTSPClient *client;
+  GstRTSPMessage request = { 0, };
+  gchar *str;
+
+  client = gst_rtsp_client_new ();
+
+  /* require header without handler */
+  fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_OPTIONS,
+          "rtsp://localhost/test") == GST_RTSP_OK);
+  str = g_strdup_printf ("test-not-supported1");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+
+  expected_unsupported_header = "test-not-supported1";
+  gst_rtsp_client_set_send_func (client, test_response_551, NULL, NULL);
+  fail_unless (gst_rtsp_client_handle_message (client,
+          &request) == GST_RTSP_OK);
+  gst_rtsp_message_unset (&request);
+
+  g_signal_connect (G_OBJECT (client), "check-requirements",
+      G_CALLBACK (check_requirements_cb), NULL);
+
+  /* one supported option */
+  fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_OPTIONS,
+          "rtsp://localhost/test") == GST_RTSP_OK);
+  str = g_strdup_printf ("test-requirements");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+
+  gst_rtsp_client_set_send_func (client, test_response_200, NULL, NULL);
+  fail_unless (gst_rtsp_client_handle_message (client,
+          &request) == GST_RTSP_OK);
+  gst_rtsp_message_unset (&request);
+
+  /* unsupported option */
+  fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_OPTIONS,
+          "rtsp://localhost/test") == GST_RTSP_OK);
+  str = g_strdup_printf ("test-not-supported1");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+
+  expected_unsupported_header = "test-not-supported1";
+  gst_rtsp_client_set_send_func (client, test_response_551, NULL, NULL);
+  fail_unless (gst_rtsp_client_handle_message (client,
+          &request) == GST_RTSP_OK);
+  gst_rtsp_message_unset (&request);
+
+  /* more than one unsupported options */
+  fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_OPTIONS,
+          "rtsp://localhost/test") == GST_RTSP_OK);
+  str = g_strdup_printf ("test-not-supported1");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+  str = g_strdup_printf ("test-not-supported2");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+
+  expected_unsupported_header = "test-not-supported1, test-not-supported2";
+  gst_rtsp_client_set_send_func (client, test_response_551, NULL, NULL);
+  fail_unless (gst_rtsp_client_handle_message (client,
+          &request) == GST_RTSP_OK);
+  gst_rtsp_message_unset (&request);
+
+  /* supported and unsupported together */
+  fail_unless (gst_rtsp_message_init_request (&request, GST_RTSP_OPTIONS,
+          "rtsp://localhost/test") == GST_RTSP_OK);
+  str = g_strdup_printf ("test-not-supported1");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+  str = g_strdup_printf ("test-requirements");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+  str = g_strdup_printf ("test-not-supported2");
+  gst_rtsp_message_add_header (&request, GST_RTSP_HDR_REQUIRE, str);
+  g_free (str);
+
+  expected_unsupported_header = "test-not-supported1, test-not-supported2";
+  gst_rtsp_client_set_send_func (client, test_response_551, NULL, NULL);
+  fail_unless (gst_rtsp_client_handle_message (client,
+          &request) == GST_RTSP_OK);
+  gst_rtsp_message_unset (&request);
+
+  g_object_unref (client);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_request)
 {
   GstRTSPClient *client;
@@ -907,6 +1041,7 @@ rtspclient_suite (void)
 
   suite_add_tcase (s, tc);
   tcase_set_timeout (tc, 20);
+  tcase_add_test (tc, test_require);
   tcase_add_test (tc, test_request);
   tcase_add_test (tc, test_options);
   tcase_add_test (tc, test_describe);