From: George Joseph Date: Fri, 13 Apr 2018 20:14:47 +0000 (-0600) Subject: streams: Add string metadata capability X-Git-Tag: 15.5.0-rc1~92^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=471be2b2c62201d8988d6557d02e3ab324de9ebc;p=thirdparty%2Fasterisk.git streams: Add string metadata capability Replaces the never used opaque data array. Updated stream tests to include get/set metadata and stream clone with metadata. Added stream metadata dump to "core show channel" Change-Id: Id7473aa4b374d7ab53046c20e321037ba9a56863 --- diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h index bbde73d1aa..99998c7e33 100644 --- a/include/asterisk/stream.h +++ b/include/asterisk/stream.h @@ -77,20 +77,6 @@ enum ast_stream_state { AST_STREAM_STATE_INACTIVE, }; -/*! - * \brief Stream data slots - */ -enum ast_stream_data_slot { - /*! - * \brief Data slot for RTP instance - */ - AST_STREAM_DATA_RTP_CODECS = 0, - /*! - * \brief Controls the size of the data pointer array - */ - AST_STREAM_DATA_SLOT_MAX -}; - /*! * \brief Create a new media stream representation * @@ -228,32 +214,47 @@ void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state const char *ast_stream_state2str(enum ast_stream_state state); /*! - * \brief Get the opaque stream data + * \brief Get a stream metadata value * * \param stream The media stream - * \param slot The data slot to retrieve + * \param m_key An arbitrary metadata key * - * \retval non-NULL success - * \retval NULL failure + * \retval non-NULL metadata value + * \retval NULL failure or not found * - * \since 15 + * \since 15.5 */ -void *ast_stream_get_data(struct ast_stream *stream, enum ast_stream_data_slot slot); +const char *ast_stream_get_metadata(const struct ast_stream *stream, + const char *m_key); /*! - * \brief Set the opaque stream data + * \brief Get all stream metadata keys * * \param stream The media stream - * \param slot The data slot to set - * \param data Opaque data - * \param data_free_fn Callback to free data when stream is freed. May be NULL for no action. * - * \return data + * \retval An ast_variable list of the metadata key/value pairs. + * \retval NULL if error or no variables are set. * - * \since 15 + * When you're finished with the list, you must call + * ast_variables_destroy(list); + * + * \since 15.5 + */ +struct ast_variable *ast_stream_get_metadata_list(const struct ast_stream *stream); + +/*! + * \brief Set a stream metadata value + * + * \param stream The media stream + * \param m_key An arbitrary metadata key + * \param value String metadata value or NULL to remove existing value + * + * \retval -1 failure + * \retval 0 success + * + * \since 15.5 */ -void *ast_stream_set_data(struct ast_stream *stream, enum ast_stream_data_slot slot, - void *data, ast_stream_data_free_fn data_free_fn); +int ast_stream_set_metadata(struct ast_stream *stream, const char *m_key, const char *value); /*! * \brief Get the position of the stream in the topology diff --git a/main/cli.c b/main/cli.c index 556c09bd81..b99272661b 100644 --- a/main/cli.c +++ b/main/cli.c @@ -1646,19 +1646,29 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar ast_str_append(&output, 0, " -- Streams --\n"); for (stream_num = 0; stream_num < ast_stream_topology_get_count(ast_channel_get_stream_topology(chan)); stream_num++) { struct ast_stream *stream = ast_stream_topology_get_stream(ast_channel_get_stream_topology(chan), stream_num); + struct ast_variable *metadata = ast_stream_get_metadata_list(stream); ast_str_append(&output, 0, "Name: %s\n" " Type: %s\n" " State: %s\n" " Group: %d\n" - " Formats: %s\n", + " Formats: %s\n" + " Metadata:\n", ast_stream_get_name(stream), ast_codec_media_type2str(ast_stream_get_type(stream)), ast_stream_state2str(ast_stream_get_state(stream)), ast_stream_get_group(stream), ast_format_cap_get_names(ast_stream_get_formats(stream), &codec_buf) ); + + if (metadata) { + struct ast_variable *v; + for(v = metadata; v; v = v->next) { + ast_str_append(&output, 0, " %s: %s\n", v->name, v->value); + } + ast_variables_destroy(metadata); + } } ast_channel_unlock(chan); diff --git a/main/stream.c b/main/stream.c index 8597485f7c..5d4970a856 100644 --- a/main/stream.c +++ b/main/stream.c @@ -34,6 +34,14 @@ #include "asterisk/strings.h" #include "asterisk/format.h" #include "asterisk/format_cap.h" +#include "asterisk/vector.h" +#include "asterisk/config.h" + +struct ast_stream_metadata_entry { + size_t length; + int value_start; + char name_value[0]; +}; struct ast_stream { /*! @@ -57,14 +65,9 @@ struct ast_stream { enum ast_stream_state state; /*! - * \brief Opaque stream data - */ - void *data[AST_STREAM_DATA_SLOT_MAX]; - - /*! - * \brief What to do with data when the stream is freed + * \brief Stream metadata vector */ - ast_stream_data_free_fn data_free_fn[AST_STREAM_DATA_SLOT_MAX]; + struct ast_variable *metadata; /*! * \brief The group that the stream is part of @@ -105,7 +108,6 @@ struct ast_stream *ast_stream_clone(const struct ast_stream *stream, const char { struct ast_stream *new_stream; size_t stream_size; - int idx; const char *stream_name; if (!stream) { @@ -126,28 +128,18 @@ struct ast_stream *ast_stream_clone(const struct ast_stream *stream, const char ao2_ref(new_stream->formats, +1); } - /* We cannot clone the opaque data because we don't know how. */ - for (idx = 0; idx < AST_STREAM_DATA_SLOT_MAX; ++idx) { - new_stream->data[idx] = NULL; - new_stream->data_free_fn[idx] = NULL; - } + new_stream->metadata = ast_stream_get_metadata_list(stream); return new_stream; } void ast_stream_free(struct ast_stream *stream) { - int i; - if (!stream) { return; } - for (i = 0; i < AST_STREAM_DATA_SLOT_MAX; i++) { - if (stream->data_free_fn[i]) { - stream->data_free_fn[i](stream->data[i]); - } - } + ast_variables_destroy(stream->metadata); ao2_cleanup(stream->formats); ast_free(stream); @@ -221,22 +213,81 @@ const char *ast_stream_state2str(enum ast_stream_state state) } } -void *ast_stream_get_data(struct ast_stream *stream, enum ast_stream_data_slot slot) +const char *ast_stream_get_metadata(const struct ast_stream *stream, const char *m_key) { - ast_assert(stream != NULL); + struct ast_variable *v; + + ast_assert_return(stream != NULL, NULL); + ast_assert_return(m_key != NULL, NULL); + + for (v = stream->metadata; v; v = v->next) { + if (strcmp(v->name, m_key) == 0) { + return v->value; + } + } - return stream->data[slot]; + return NULL; } -void *ast_stream_set_data(struct ast_stream *stream, enum ast_stream_data_slot slot, - void *data, ast_stream_data_free_fn data_free_fn) +struct ast_variable *ast_stream_get_metadata_list(const struct ast_stream *stream) { - ast_assert(stream != NULL); + struct ast_variable *v; + struct ast_variable *vout = NULL; - stream->data[slot] = data; - stream->data_free_fn[slot] = data_free_fn; + ast_assert_return(stream != NULL, NULL); - return data; + for (v = stream->metadata; v; v = v->next) { + struct ast_variable *vt = ast_variable_new(v->name, v->value, ""); + + if (!vt) { + ast_variables_destroy(vout); + return NULL; + } + + ast_variable_list_append(&vout, vt); + } + + return vout; +} + +int ast_stream_set_metadata(struct ast_stream *stream, const char *m_key, const char *value) +{ + struct ast_variable *v; + struct ast_variable *prev; + + ast_assert_return(stream != NULL, -1); + ast_assert_return(m_key != NULL, -1); + + prev = NULL; + v = stream->metadata; + while(v) { + struct ast_variable *next = v->next; + if (strcmp(v->name, m_key) == 0) { + if (prev) { + prev->next = next; + } else { + stream->metadata = next; + } + ast_free(v); + break; + } else { + prev = v; + } + v = next; + } + + if (!value) { + return 0; + } + + v = ast_variable_new(m_key, value, ""); + if (!v) { + return -1; + } + + ast_variable_list_append(&stream->metadata, v); + + return 0; } int ast_stream_get_position(const struct ast_stream *stream) diff --git a/tests/test_stream.c b/tests/test_stream.c index 8c887048cf..50f0ccbfdf 100644 --- a/tests/test_stream.c +++ b/tests/test_stream.c @@ -38,6 +38,7 @@ #include "asterisk/format_cap.h" #include "asterisk/format_cache.h" #include "asterisk/channel.h" +#include "asterisk/uuid.h" AST_TEST_DEFINE(stream_create) { @@ -224,6 +225,70 @@ AST_TEST_DEFINE(stream_set_state) return AST_TEST_PASS; } +AST_TEST_DEFINE(stream_metadata) +{ + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free); + char track_label[AST_UUID_STR_LEN + 1]; + const char *stream_track_label; + int rc; + + switch (cmd) { + case TEST_INIT: + info->name = "stream_metadata"; + info->category = "/main/stream/"; + info->summary = "stream metadata unit test"; + info->description = + "Test that metadata operations on a stream works"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + stream = ast_stream_alloc("test", AST_MEDIA_TYPE_AUDIO); + if (!stream) { + ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); + return AST_TEST_FAIL; + } + + stream_track_label = ast_stream_get_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL"); + if (stream_track_label) { + ast_test_status_update(test, "New stream HAD a track label\n"); + return AST_TEST_FAIL; + } + + ast_uuid_generate_str(track_label, sizeof(track_label)); + rc = ast_stream_set_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL", track_label); + if (rc != 0) { + ast_test_status_update(test, "Failed to add track label\n"); + return AST_TEST_FAIL; + } + + stream_track_label = ast_stream_get_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL"); + if (!stream_track_label) { + ast_test_status_update(test, "Changed stream does not have a track label\n"); + return AST_TEST_FAIL; + } + + if (strcmp(stream_track_label, track_label) != 0) { + ast_test_status_update(test, "Changed stream did not return same track label\n"); + return AST_TEST_FAIL; + } + + rc = ast_stream_set_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL", NULL); + if (rc != 0) { + ast_test_status_update(test, "Failed to remove track label\n"); + return AST_TEST_FAIL; + } + + stream_track_label = ast_stream_get_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL"); + if (stream_track_label) { + ast_test_status_update(test, "Changed stream still had a track label after we removed it\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + AST_TEST_DEFINE(stream_topology_create) { RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); @@ -254,6 +319,11 @@ AST_TEST_DEFINE(stream_topology_clone) RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); RAII_VAR(struct ast_stream_topology *, cloned, NULL, ast_stream_topology_free); struct ast_stream *audio_stream, *video_stream; + char audio_track_label[AST_UUID_STR_LEN + 1]; + char video_track_label[AST_UUID_STR_LEN + 1]; + const char *original_track_label; + const char *cloned_track_label; + int rc; switch (cmd) { case TEST_INIT: @@ -279,6 +349,13 @@ AST_TEST_DEFINE(stream_topology_clone) return AST_TEST_FAIL; } + ast_uuid_generate_str(audio_track_label, sizeof(audio_track_label)); + rc = ast_stream_set_metadata(audio_stream, "AST_STREAM_METADATA_TRACK_LABEL", audio_track_label); + if (rc != 0) { + ast_test_status_update(test, "Failed to add track label\n"); + return AST_TEST_FAIL; + } + if (ast_stream_topology_append_stream(topology, audio_stream) == -1) { ast_test_status_update(test, "Failed to append valid audio stream to stream topology\n"); ast_stream_free(audio_stream); @@ -291,6 +368,13 @@ AST_TEST_DEFINE(stream_topology_clone) return AST_TEST_FAIL; } + ast_uuid_generate_str(video_track_label, sizeof(video_track_label)); + rc = ast_stream_set_metadata(video_stream, "AST_STREAM_METADATA_TRACK_LABEL", video_track_label); + if (rc != 0) { + ast_test_status_update(test, "Failed to add track label\n"); + return AST_TEST_FAIL; + } + if (ast_stream_topology_append_stream(topology, video_stream) == -1) { ast_test_status_update(test, "Failed to append valid video stream to stream topology\n"); ast_stream_free(video_stream); @@ -313,11 +397,45 @@ AST_TEST_DEFINE(stream_topology_clone) return AST_TEST_FAIL; } + original_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(topology, 0), + "AST_STREAM_METADATA_TRACK_LABEL"); + if (!original_track_label) { + ast_test_status_update(test, "Original topology stream 0 does not contain metadata\n"); + return AST_TEST_FAIL; + } + cloned_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(cloned, 0), + "AST_STREAM_METADATA_TRACK_LABEL"); + if (!cloned_track_label) { + ast_test_status_update(test, "Cloned topology stream 0 does not contain metadata\n"); + return AST_TEST_FAIL; + } + if (strcmp(original_track_label, cloned_track_label) != 0) { + ast_test_status_update(test, "Cloned topology stream 0 track label was not the same as the original\n"); + return AST_TEST_FAIL; + } + if (ast_stream_get_type(ast_stream_topology_get_stream(cloned, 1)) != ast_stream_get_type(ast_stream_topology_get_stream(topology, 1))) { ast_test_status_update(test, "Cloned video stream does not contain same type as original\n"); return AST_TEST_FAIL; } + original_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(topology, 1), + "AST_STREAM_METADATA_TRACK_LABEL"); + if (!original_track_label) { + ast_test_status_update(test, "Original topology stream 1 does not contain metadata\n"); + return AST_TEST_FAIL; + } + cloned_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(cloned, 1), + "AST_STREAM_METADATA_TRACK_LABEL"); + if (!cloned_track_label) { + ast_test_status_update(test, "Cloned topology stream 1 does not contain metadata\n"); + return AST_TEST_FAIL; + } + if (strcmp(original_track_label, cloned_track_label) != 0) { + ast_test_status_update(test, "Cloned topology stream 1 track label was not the same as the original\n"); + return AST_TEST_FAIL; + } + return AST_TEST_PASS; } @@ -2139,6 +2257,7 @@ static int unload_module(void) AST_TEST_UNREGISTER(stream_set_type); AST_TEST_UNREGISTER(stream_set_formats); AST_TEST_UNREGISTER(stream_set_state); + AST_TEST_UNREGISTER(stream_metadata); AST_TEST_UNREGISTER(stream_topology_create); AST_TEST_UNREGISTER(stream_topology_clone); AST_TEST_UNREGISTER(stream_topology_clone); @@ -2169,6 +2288,7 @@ static int load_module(void) AST_TEST_REGISTER(stream_set_type); AST_TEST_REGISTER(stream_set_formats); AST_TEST_REGISTER(stream_set_state); + AST_TEST_REGISTER(stream_metadata); AST_TEST_REGISTER(stream_topology_create); AST_TEST_REGISTER(stream_topology_clone); AST_TEST_REGISTER(stream_topology_append_stream);