message: Add redirect message

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
This commit is contained in:
Carlos Rafael Giani 2016-07-25 11:22:36 +02:00 committed by Sebastian Dröge
parent 4981e09bc7
commit eead9cf827
7 changed files with 367 additions and 2 deletions

View 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

View 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;
}

View 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

View 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];

View 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];

View 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;

View 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