message: Add redirect message
authorCarlos Rafael Giani <dv@pseudoterminal.org>
Mon, 25 Jul 2016 09:22:36 +0000 (11:22 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Mon, 25 Jul 2016 09:59:21 +0000 (12:59 +0300)
Redirection messages are already used in fragmented sources and in
uridecodebin, so it makes sense to introduce these as an official message
type.

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

docs/gst/gstreamer-sections.txt
gst/gstmessage.c
gst/gstmessage.h
gst/gstquark.c
gst/gstquark.h
tests/check/gst/gstmessage.c
win32/common/libgstreamer.def

index 4d99108..f8f8a0f 100644 (file)
@@ -1678,6 +1678,11 @@ gst_message_streams_selected_add
 gst_message_streams_selected_get_size
 gst_message_streams_selected_get_stream
 
+gst_message_new_redirect
+gst_message_add_redirect_entry
+gst_message_parse_redirect_entry
+gst_message_get_num_redirect_entries
+
 <SUBSECTION Standard>
 GstMessageClass
 GST_MESSAGE
index a0b89b2..2ee64ea 100644 (file)
@@ -109,6 +109,7 @@ static GstMessageQuarks message_quarks[] = {
   {GST_MESSAGE_PROPERTY_NOTIFY, "property-notify", 0},
   {GST_MESSAGE_STREAM_COLLECTION, "stream-collection", 0},
   {GST_MESSAGE_STREAMS_SELECTED, "streams-selected", 0},
+  {GST_MESSAGE_REDIRECT, "redirect", 0},
   {0, NULL, 0}
 };
 
@@ -2914,3 +2915,243 @@ gst_message_parse_streams_selected (GstMessage * message,
     gst_structure_id_get (GST_MESSAGE_STRUCTURE (message),
         GST_QUARK (COLLECTION), GST_TYPE_STREAM_COLLECTION, collection, NULL);
 }
+
+/**
+ * gst_message_new_redirect:
+ * @src: The #GstObject whose property changed (may or may not be a #GstElement)
+ * @location: (transfer none): location string for the new entry
+ * @tag_list: (transfer full) (allow-none): tag list for the new entry
+ * @entry_struct: (transfer full) (allow-none): structure for the new entry
+ *
+ * Creates a new redirect message and adds a new entry to it. Redirect messages
+ * are posted when an element detects that the actual data has to be retrieved
+ * from a different location. This is useful if such a redirection cannot be
+ * handled inside a source element, for example when HTTP 302/303 redirects
+ * return a non-HTTP URL.
+ *
+ * The redirect message can hold multiple entries. The first one is added
+ * when the redirect message is created, with the given location, tag_list,
+ * entry_struct arguments. Use gst_message_add_redirect_entry() to add more
+ * entries.
+ *
+ * Each entry has a location, a tag list, and a structure. All of these are
+ * optional. The tag list and structure are useful for additional metadata,
+ * such as bitrate statistics for the given location.
+ *
+ * By default, message recipients should treat entries in the order they are
+ * stored. The recipient should therefore try entry #0 first, and if this
+ * entry is not acceptable or working, try entry #1 etc. Senders must make
+ * sure that they add entries in this order. However, recipients are free to
+ * ignore the order and pick an entry that is "best" for them. One example
+ * would be a recipient that scans the entries for the one with the highest
+ * bitrate tag.
+ *
+ * The specified location string is copied. However, ownership over the tag
+ * list and structure are transferred to the message.
+ *
+ * Returns: a newly allocated #GstMessage
+ *
+ * Since: 1.10
+ */
+GstMessage *
+gst_message_new_redirect (GstObject * src, const gchar * location,
+    GstTagList * tag_list, const GstStructure * entry_struct)
+{
+  GstStructure *structure;
+  GstMessage *message;
+  GValue entry_locations_gvalue = G_VALUE_INIT;
+  GValue entry_taglists_gvalue = G_VALUE_INIT;
+  GValue entry_structures_gvalue = G_VALUE_INIT;
+
+  g_return_val_if_fail (location != NULL, NULL);
+
+  g_value_init (&entry_locations_gvalue, GST_TYPE_LIST);
+  g_value_init (&entry_taglists_gvalue, GST_TYPE_LIST);
+  g_value_init (&entry_structures_gvalue, GST_TYPE_LIST);
+
+  structure = gst_structure_new_id_empty (GST_QUARK (MESSAGE_REDIRECT));
+  gst_structure_id_take_value (structure, GST_QUARK (REDIRECT_ENTRY_LOCATIONS),
+      &entry_locations_gvalue);
+  gst_structure_id_take_value (structure, GST_QUARK (REDIRECT_ENTRY_TAGLISTS),
+      &entry_taglists_gvalue);
+  gst_structure_id_take_value (structure, GST_QUARK (REDIRECT_ENTRY_STRUCTURES),
+      &entry_structures_gvalue);
+
+  message = gst_message_new_custom (GST_MESSAGE_REDIRECT, src, structure);
+  g_assert (message != NULL);
+
+  gst_message_add_redirect_entry (message, location, tag_list, entry_struct);
+
+  return message;
+}
+
+/**
+ * gst_message_add_redirect_entry:
+ * @message: a #GstMessage of type %GST_MESSAGE_REDIRECT
+ * @location: (transfer none): location string for the new entry
+ * @tag_list: (transfer full) (allow-none): tag list for the new entry
+ * @entry_struct: (transfer full) (allow-none): structure for the new entry
+ *
+ * Creates and appends a new entry.
+ *
+ * The specified location string is copied. However, ownership over the tag
+ * list and structure are transferred to the message.
+ *
+ * Since: 1.10
+ */
+void
+gst_message_add_redirect_entry (GstMessage * message, const gchar * location,
+    GstTagList * tag_list, const GstStructure * entry_struct)
+{
+  GValue val = G_VALUE_INIT;
+  GstStructure *structure;
+  GValue *entry_locations_gvalue;
+  GValue *entry_taglists_gvalue;
+  GValue *entry_structures_gvalue;
+
+  g_return_if_fail (location != NULL);
+  g_return_if_fail (GST_IS_MESSAGE (message));
+  g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_REDIRECT);
+
+  structure = GST_MESSAGE_STRUCTURE (message);
+
+  entry_locations_gvalue =
+      (GValue *) gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_LOCATIONS));
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (entry_locations_gvalue));
+  entry_taglists_gvalue =
+      (GValue *) gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_TAGLISTS));
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (entry_taglists_gvalue));
+  entry_structures_gvalue =
+      (GValue *) gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_STRUCTURES));
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (entry_structures_gvalue));
+
+  g_value_init (&val, G_TYPE_STRING);
+  if (location)
+    g_value_set_string (&val, location);
+  gst_value_list_append_and_take_value (entry_locations_gvalue, &val);
+
+  g_value_init (&val, GST_TYPE_TAG_LIST);
+  if (tag_list)
+    g_value_take_boxed (&val, tag_list);
+  gst_value_list_append_and_take_value (entry_taglists_gvalue, &val);
+
+  g_value_init (&val, GST_TYPE_STRUCTURE);
+  if (entry_struct)
+    g_value_take_boxed (&val, entry_struct);
+  gst_value_list_append_and_take_value (entry_structures_gvalue, &val);
+}
+
+/**
+ * gst_message_parse_redirect_entry:
+ * @message: a #GstMessage of type %GST_MESSAGE_REDIRECT
+ * @entry_index: index of the entry to parse
+ * @location: (out) (transfer none) (allow-none): return location for
+ *     the pointer to the entry's location string, or %NULL
+ * @tag_list: (out) (transfer none) (allow-none): return location for
+ *     the pointer to the entry's tag list, or %NULL
+ * @entry_struct: (out) (transfer none) (allow-none): return location
+ *     for the pointer to the entry's structure, or %NULL
+ *
+ * Parses the location and/or structure from the entry with the given index.
+ * The index must be between 0 and gst_message_get_num_redirect_entries() - 1.
+ * Returned pointers are valid for as long as this message exists.
+ *
+ * Since: 1.10
+ */
+void
+gst_message_parse_redirect_entry (GstMessage * message, gsize entry_index,
+    const gchar ** location, GstTagList ** tag_list,
+    const GstStructure ** entry_struct)
+{
+  const GValue *val;
+  GstStructure *structure;
+  const GValue *entry_locations_gvalue;
+  const GValue *entry_taglists_gvalue;
+  const GValue *entry_structures_gvalue;
+
+  g_return_if_fail (GST_IS_MESSAGE (message));
+  g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_REDIRECT);
+
+  if (G_UNLIKELY (!location && !tag_list && !entry_struct))
+    return;
+
+  structure = GST_MESSAGE_STRUCTURE (message);
+
+  entry_locations_gvalue =
+      gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_LOCATIONS));
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (entry_locations_gvalue));
+  entry_taglists_gvalue =
+      gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_TAGLISTS));
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (entry_taglists_gvalue));
+  entry_structures_gvalue =
+      gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_STRUCTURES));
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (entry_structures_gvalue));
+
+  if (location) {
+    val = gst_value_list_get_value (entry_locations_gvalue, entry_index);
+    g_return_if_fail (val != NULL);
+    *location = g_value_get_string (val);
+  }
+
+  if (tag_list) {
+    val = gst_value_list_get_value (entry_taglists_gvalue, entry_index);
+    g_return_if_fail (val != NULL);
+    *tag_list = (GstTagList *) g_value_get_boxed (val);
+  }
+
+  if (entry_struct) {
+    val = gst_value_list_get_value (entry_structures_gvalue, entry_index);
+    g_return_if_fail (val != NULL);
+    *entry_struct = (const GstStructure *) g_value_get_boxed (val);
+  }
+}
+
+/**
+ * gst_message_get_num_redirect_entries:
+ * @message: a #GstMessage of type %GST_MESSAGE_REDIRECT
+ *
+ * Returns: the number of entries stored in the message
+ *
+ * Since: 1.10
+ */
+gsize
+gst_message_get_num_redirect_entries (GstMessage * message)
+{
+  GstStructure *structure;
+  const GValue *entry_locations_gvalue;
+  const GValue *entry_taglists_gvalue;
+  const GValue *entry_structures_gvalue;
+  gsize size;
+
+  g_return_val_if_fail (GST_IS_MESSAGE (message), 0);
+  g_return_val_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_REDIRECT, 0);
+
+  structure = GST_MESSAGE_STRUCTURE (message);
+
+  entry_locations_gvalue =
+      gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_LOCATIONS));
+  g_return_val_if_fail (GST_VALUE_HOLDS_LIST (entry_locations_gvalue), 0);
+  entry_taglists_gvalue =
+      gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_TAGLISTS));
+  g_return_val_if_fail (GST_VALUE_HOLDS_LIST (entry_taglists_gvalue), 0);
+  entry_structures_gvalue =
+      gst_structure_id_get_value (structure,
+      GST_QUARK (REDIRECT_ENTRY_STRUCTURES));
+  g_return_val_if_fail (GST_VALUE_HOLDS_LIST (entry_structures_gvalue), 0);
+
+  size = gst_value_list_get_size (entry_locations_gvalue);
+
+  g_return_val_if_fail ((size ==
+          gst_value_list_get_size (entry_structures_gvalue))
+      && (size == gst_value_list_get_size (entry_taglists_gvalue)), 0);
+
+  return size;
+}
index 0fb0bf8..68213eb 100644 (file)
@@ -114,6 +114,9 @@ typedef struct _GstMessage GstMessage;
  *     is available (Since 1.10)
  * @GST_MESSAGE_STREAMS_SELECTED: Message indicating the active selection of
  *     #GstStreams has changed (Since 1.10)
+ * @GST_MESSAGE_REDIRECT: Message indicating to request the application to
+ *     try to play the given URL(s). Useful if for example a HTTP 302/303
+ *     response is received with a non-HTTP URL inside. (Since 1.10)
  * @GST_MESSAGE_ANY: mask for all of the above messages.
  *
  * The different message types that are available.
@@ -165,6 +168,7 @@ typedef enum
   GST_MESSAGE_PROPERTY_NOTIFY   = GST_MESSAGE_EXTENDED + 3,
   GST_MESSAGE_STREAM_COLLECTION = GST_MESSAGE_EXTENDED + 4,
   GST_MESSAGE_STREAMS_SELECTED  = GST_MESSAGE_EXTENDED + 5,
+  GST_MESSAGE_REDIRECT          = GST_MESSAGE_EXTENDED + 6,
   GST_MESSAGE_ANY               = (gint) (0xffffffff)
 } GstMessageType;
 
@@ -623,6 +627,12 @@ void            gst_message_parse_streams_selected (GstMessage * message, GstStr
 guint           gst_message_streams_selected_get_size (GstMessage * message);
 GstStream      *gst_message_streams_selected_get_stream (GstMessage *message, guint idx);
 
+/* REDIRECT */
+GstMessage *    gst_message_new_redirect             (GstObject * src, const gchar * location, GstTagList * tag_list, const GstStructure * entry_struct) G_GNUC_MALLOC;
+void            gst_message_add_redirect_entry       (GstMessage * message, const gchar * location, GstTagList * tag_list, const GstStructure * entry_struct);
+void            gst_message_parse_redirect_entry     (GstMessage * message, gsize entry_index, const gchar ** location, GstTagList ** tag_list, const GstStructure ** entry_struct);
+gsize           gst_message_get_num_redirect_entries (GstMessage * message);
+
 #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstMessage, gst_message_unref)
 #endif
index 8869345..c2a204d 100644 (file)
@@ -73,7 +73,8 @@ static const gchar *_quark_strings[] = {
   "uri-redirection-permanent", "GstMessagePropertyNotify", "property-name",
   "property-value", "streams", "GstEventSelectStreams",
   "GstMessageStreamCollection", "collection", "stream", "stream-collection",
-  "GstMessageStreamsSelected"
+  "GstMessageStreamsSelected", "GstMessageRedirect", "redirect-entry-locations",
+  "redirect-entry-taglists", "redirect-entry-structures"
 };
 
 GQuark _priv_gst_quark_table[GST_QUARK_MAX];
index dd0cde4..fda5deb 100644 (file)
@@ -212,7 +212,11 @@ typedef enum _GstQuarkId
   GST_QUARK_STREAM = 181,
   GST_QUARK_EVENT_STREAM_COLLECTION = 182,
   GST_QUARK_MESSAGE_STREAMS_SELECTED = 183,
-  GST_QUARK_MAX = 184
+  GST_QUARK_MESSAGE_REDIRECT = 184,
+  GST_QUARK_REDIRECT_ENTRY_LOCATIONS = 185,
+  GST_QUARK_REDIRECT_ENTRY_TAGLISTS = 186,
+  GST_QUARK_REDIRECT_ENTRY_STRUCTURES = 187,
+  GST_QUARK_MAX = 188
 } GstQuarkId;
 
 extern GQuark _priv_gst_quark_table[GST_QUARK_MAX];
index 77110ba..9c8c27a 100644 (file)
@@ -501,6 +501,106 @@ GST_START_TEST (test_parsing)
     gst_caps_unref (caps1);
     gst_caps_unref (caps2);
   }
+  /* GST_MESSAGE_REDIRECT */
+  {
+    const gchar *parsed_location;
+    GstTagList *parsed_tag_list;
+    const GstStructure *parsed_structure;
+    const gchar *test_location = "some-location";
+    const gchar *test_struct_name = "test-struct";
+    const gchar *test_value_name = "foo";
+    const gint test_value = 12345;
+    const guint test_bitrate = 120000;
+    gint value;
+    guint bitrate;
+    GstTagList *test_tag_list;
+    GstStructure *test_structure;
+
+    test_structure =
+        gst_structure_new (test_struct_name, test_value_name, G_TYPE_INT,
+        test_value, NULL);
+
+    /* Create a test tag list. It is ref'd  before adding an entry to be able
+     * to test that new_redirect takes ownership */
+    test_tag_list = gst_tag_list_new (GST_TAG_BITRATE, test_bitrate, NULL);
+
+    /* Create the message and add the first entry, which only has a location
+     * and a tag list */
+    gst_tag_list_ref (test_tag_list);
+    message =
+        gst_message_new_redirect (NULL, test_location, test_tag_list, NULL);
+    fail_if (message == NULL);
+    fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_REDIRECT);
+    fail_unless (GST_MESSAGE_SRC (message) == NULL);
+
+    /* Add the second entry, which only has a location and a structure */
+    gst_message_add_redirect_entry (message, test_location, NULL,
+        gst_structure_copy (test_structure));
+
+    /* Add the third entry, which has a location, a taglist, and a structure */
+    gst_tag_list_ref (test_tag_list);
+    gst_message_add_redirect_entry (message, test_location, test_tag_list,
+        gst_structure_copy (test_structure));
+
+    fail_unless (gst_message_get_num_redirect_entries (message) == 3);
+
+    /* Check that the location of the first entry is correct and that the
+     * structure pointer is set to NULL */
+    parsed_location = NULL;
+    parsed_tag_list = NULL;
+    parsed_structure = (const GstStructure *) 0x1;
+    gst_message_parse_redirect_entry (message, 0, &parsed_location,
+        &parsed_tag_list, &parsed_structure);
+    fail_unless (parsed_location != NULL);
+    fail_unless (parsed_tag_list != NULL);
+    fail_unless (parsed_structure == NULL);
+    fail_unless (!strcmp (parsed_location, test_location));
+    fail_unless (gst_tag_list_get_uint (parsed_tag_list, GST_TAG_BITRATE,
+            &bitrate) && (bitrate == test_bitrate));
+
+    /* Check that the structure of the second entry is correct and that the
+     * tag list pointer is set to NULL */
+    parsed_location = NULL;
+    parsed_tag_list = (GstTagList *) 0x1;
+    parsed_structure = NULL;
+    gst_message_parse_redirect_entry (message, 1, &parsed_location,
+        &parsed_tag_list, &parsed_structure);
+    fail_unless (parsed_location != NULL);
+    fail_unless (parsed_tag_list == NULL);
+    fail_unless (parsed_structure != NULL);
+    fail_unless (!strcmp (parsed_location, test_location));
+    fail_unless (!strcmp (gst_structure_get_name (parsed_structure),
+            test_struct_name));
+    fail_unless (gst_structure_get_int (parsed_structure, test_value_name,
+            &value) && (value == test_value));
+
+    /* Check that the location, tag list, and structure pointers of the
+     * third entry are correct */
+    parsed_location = NULL;
+    parsed_tag_list = NULL;
+    parsed_structure = NULL;
+    gst_message_parse_redirect_entry (message, 2, &parsed_location,
+        &parsed_tag_list, &parsed_structure);
+    fail_unless (parsed_location != NULL);
+    fail_unless (parsed_tag_list != NULL);
+    fail_unless (parsed_structure != NULL);
+    fail_unless (!strcmp (parsed_location, test_location));
+    fail_unless (!strcmp (gst_structure_get_name (parsed_structure),
+            test_struct_name));
+    fail_unless (gst_tag_list_get_uint (parsed_tag_list, GST_TAG_BITRATE,
+            &bitrate) && (bitrate == test_bitrate));
+    fail_unless (gst_structure_get_int (parsed_structure, test_value_name,
+            &value) && (value == test_value));
+
+    gst_message_unref (message);
+
+    /* Since the message takes ownership over the tag list, its refcount
+     * must have been decreased after each added entry */
+    fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (test_tag_list), 1);
+
+    gst_structure_free (test_structure);
+    gst_tag_list_unref (test_tag_list);
+  }
 }
 
 GST_END_TEST;
index 3770d16..ef9e8da 100644 (file)
@@ -700,6 +700,8 @@ EXPORTS
        gst_memory_resize
        gst_memory_share
        gst_memory_unmap
+       gst_message_add_redirect_entry
+       gst_message_get_num_redirect_entries
        gst_message_get_seqnum
        gst_message_get_stream_status_object
        gst_message_get_structure
@@ -728,6 +730,7 @@ EXPORTS
        gst_message_new_progress
        gst_message_new_property_notify
        gst_message_new_qos
+       gst_message_new_redirect
        gst_message_new_request_state
        gst_message_new_reset_time
        gst_message_new_segment_done
@@ -765,6 +768,7 @@ EXPORTS
        gst_message_parse_qos
        gst_message_parse_qos_stats
        gst_message_parse_qos_values
+       gst_message_parse_redirect_entry
        gst_message_parse_request_state
        gst_message_parse_reset_time
        gst_message_parse_segment_done