<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/8796">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Benjamin Keith Ford: Looks good to me, but someone else must approve
Joshua Colp: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">streams: Add string metadata capability<br><br>Replaces the never used opaque data array.<br><br>Updated stream tests to include get/set metadata and<br>stream clone with metadata.<br><br>Added stream metadata dump to "core show channel"<br><br>Change-Id: Id7473aa4b374d7ab53046c20e321037ba9a56863<br>---<br>M include/asterisk/stream.h<br>M main/cli.c<br>M main/sdp.c<br>M main/sdp_state.c<br>M main/stream.c<br>M tests/test_stream.c<br>6 files changed, 293 insertions(+), 61 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h<br>index c2d5a88..0a5550b 100644<br>--- a/include/asterisk/stream.h<br>+++ b/include/asterisk/stream.h<br>@@ -78,20 +78,6 @@<br> };<br> <br> /*!<br>- * \brief Stream data slots<br>- */<br>-enum ast_stream_data_slot {<br>- /*!<br>- * \brief Data slot for RTP instance<br>- */<br>- AST_STREAM_DATA_RTP_CODECS = 0,<br>- /*!<br>- * \brief Controls the size of the data pointer array<br>- */<br>- AST_STREAM_DATA_SLOT_MAX<br>-};<br>-<br>-/*!<br> * \brief Create a new media stream representation<br> *<br> * \param name A name for the stream<br>@@ -239,32 +225,47 @@<br> enum ast_stream_state ast_stream_str2state(const char *str);<br> <br> /*!<br>- * \brief Get the opaque stream data<br>+ * \brief Get a stream metadata value<br> *<br> * \param stream The media stream<br>- * \param slot The data slot to retrieve<br>+ * \param m_key An arbitrary metadata key<br> *<br>- * \retval non-NULL success<br>- * \retval NULL failure<br>+ * \retval non-NULL metadata value<br>+ * \retval NULL failure or not found<br> *<br>- * \since 15<br>+ * \since 15.5<br> */<br>-void *ast_stream_get_data(struct ast_stream *stream, enum ast_stream_data_slot slot);<br>+const char *ast_stream_get_metadata(const struct ast_stream *stream,<br>+ const char *m_key);<br> <br> /*!<br>- * \brief Set the opaque stream data<br>+ * \brief Get all stream metadata keys<br> *<br> * \param stream The media stream<br>- * \param slot The data slot to set<br>- * \param data Opaque data<br>- * \param data_free_fn Callback to free data when stream is freed. May be NULL for no action.<br> *<br>- * \return data<br>+ * \retval An ast_variable list of the metadata key/value pairs.<br>+ * \retval NULL if error or no variables are set.<br> *<br>- * \since 15<br>+ * When you're finished with the list, you must call<br>+ * ast_variables_destroy(list);<br>+ *<br>+ * \since 15.5<br> */<br>-void *ast_stream_set_data(struct ast_stream *stream, enum ast_stream_data_slot slot,<br>- void *data, ast_stream_data_free_fn data_free_fn);<br>+struct ast_variable *ast_stream_get_metadata_list(const struct ast_stream *stream);<br>+<br>+/*!<br>+ * \brief Set a stream metadata value<br>+ *<br>+ * \param stream The media stream<br>+ * \param m_key An arbitrary metadata key<br>+ * \param value String metadata value or NULL to remove existing value<br>+ *<br>+ * \retval -1 failure<br>+ * \retval 0 success<br>+ *<br>+ * \since 15.5<br>+ */<br>+int ast_stream_set_metadata(struct ast_stream *stream, const char *m_key, const char *value);<br> <br> /*!<br> * \brief Get the position of the stream in the topology<br>@@ -278,6 +279,27 @@<br> int ast_stream_get_position(const struct ast_stream *stream);<br> <br> /*!<br>+ * \brief Get rtp_codecs associated with the stream<br>+ *<br>+ * \param stream The media stream<br>+ *<br>+ * \return The rtp_codecs<br>+ *<br>+ * \since 15.5<br>+ */<br>+struct ast_rtp_codecs *ast_stream_get_rtp_codecs(const struct ast_stream *stream);<br>+<br>+/*!<br>+ * \brief Set rtp_codecs associated with the stream<br>+ *<br>+ * \param stream The media stream<br>+ * \param rtp_codecs The rtp_codecs<br>+ *<br>+ * \since 15.5<br>+ */<br>+void ast_stream_set_rtp_codecs(struct ast_stream *stream, struct ast_rtp_codecs *rtp_codecs);<br>+<br>+/*!<br> * \brief Create a stream topology<br> *<br> * \retval non-NULL success<br>diff --git a/main/cli.c b/main/cli.c<br>index 5730be1..cf51d0d 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -1614,19 +1614,29 @@<br> ast_str_append(&output, 0, " -- Streams --\n");<br> for (stream_num = 0; stream_num < ast_stream_topology_get_count(ast_channel_get_stream_topology(chan)); stream_num++) {<br> struct ast_stream *stream = ast_stream_topology_get_stream(ast_channel_get_stream_topology(chan), stream_num);<br>+ struct ast_variable *metadata = ast_stream_get_metadata_list(stream);<br> <br> ast_str_append(&output, 0,<br> "Name: %s\n"<br> " Type: %s\n"<br> " State: %s\n"<br> " Group: %d\n"<br>- " Formats: %s\n",<br>+ " Formats: %s\n"<br>+ " Metadata:\n",<br> ast_stream_get_name(stream),<br> ast_codec_media_type2str(ast_stream_get_type(stream)),<br> ast_stream_state2str(ast_stream_get_state(stream)),<br> ast_stream_get_group(stream),<br> ast_format_cap_get_names(ast_stream_get_formats(stream), &codec_buf)<br> );<br>+<br>+ if (metadata) {<br>+ struct ast_variable *v;<br>+ for(v = metadata; v; v = v->next) {<br>+ ast_str_append(&output, 0, " %s: %s\n", v->name, v->value);<br>+ }<br>+ ast_variables_destroy(metadata);<br>+ }<br> }<br> <br> ast_channel_unlock(chan);<br>diff --git a/main/sdp.c b/main/sdp.c<br>index 7e283eb..e5c8803 100644<br>--- a/main/sdp.c<br>+++ b/main/sdp.c<br>@@ -848,8 +848,7 @@<br> ast_free(codecs);<br> return NULL;<br> }<br>- ast_stream_set_data(stream, AST_STREAM_DATA_RTP_CODECS, codecs,<br>- (ast_stream_data_free_fn) rtp_codecs_free);<br>+ ast_stream_set_rtp_codecs(stream, codecs);<br> <br> if (!m_line->port) {<br> /* Stream is declined. There may not be any attributes. */<br>diff --git a/main/sdp_state.c b/main/sdp_state.c<br>index 5f9ad5e..e022277 100644<br>--- a/main/sdp_state.c<br>+++ b/main/sdp_state.c<br>@@ -1732,7 +1732,7 @@<br> case AST_MEDIA_TYPE_AUDIO:<br> case AST_MEDIA_TYPE_VIDEO:<br> ao2_bump(joint_state_stream->rtp);<br>- codecs = ast_stream_get_data(remote_stream, AST_STREAM_DATA_RTP_CODECS);<br>+ codecs = ast_stream_get_rtp_codecs(remote_stream);<br> ast_assert(codecs != NULL);<br> if (sdp_state->role == SDP_ROLE_ANSWERER) {<br> /*<br>@@ -1789,7 +1789,7 @@<br> * Setup rx payload type mapping to prefer the mapping<br> * from the peer that the RFC says we SHOULD use.<br> */<br>- codecs = ast_stream_get_data(remote_stream, AST_STREAM_DATA_RTP_CODECS);<br>+ codecs = ast_stream_get_rtp_codecs(remote_stream);<br> ast_assert(codecs != NULL);<br> ast_rtp_codecs_payloads_xover(codecs, codecs, NULL);<br> ast_rtp_codecs_payloads_copy(codecs,<br>diff --git a/main/stream.c b/main/stream.c<br>index 61eef25..dd3e765 100644<br>--- a/main/stream.c<br>+++ b/main/stream.c<br>@@ -34,6 +34,15 @@<br> #include "asterisk/strings.h"<br> #include "asterisk/format.h"<br> #include "asterisk/format_cap.h"<br>+#include "asterisk/vector.h"<br>+#include "asterisk/config.h"<br>+#include "asterisk/rtp_engine.h"<br>+<br>+struct ast_stream_metadata_entry {<br>+ size_t length;<br>+ int value_start;<br>+ char name_value[0];<br>+};<br> <br> struct ast_stream {<br> /*!<br>@@ -57,19 +66,19 @@<br> enum ast_stream_state state;<br> <br> /*!<br>- * \brief Opaque stream data<br>+ * \brief Stream metadata vector<br> */<br>- void *data[AST_STREAM_DATA_SLOT_MAX];<br>-<br>- /*!<br>- * \brief What to do with data when the stream is freed<br>- */<br>- ast_stream_data_free_fn data_free_fn[AST_STREAM_DATA_SLOT_MAX];<br>+ struct ast_variable *metadata;<br> <br> /*!<br> * \brief The group that the stream is part of<br> */<br> int group;<br>+<br>+ /*!<br>+ * \brief The rtp_codecs used by the stream<br>+ */<br>+ struct ast_rtp_codecs *rtp_codecs;<br> <br> /*!<br> * \brief Name for the stream within the context of the channel it is on<br>@@ -105,7 +114,6 @@<br> {<br> struct ast_stream *new_stream;<br> size_t stream_size;<br>- int idx;<br> const char *stream_name;<br> <br> if (!stream) {<br>@@ -126,27 +134,23 @@<br> ao2_ref(new_stream->formats, +1);<br> }<br> <br>- /* We cannot clone the opaque data because we don't know how. */<br>- for (idx = 0; idx < AST_STREAM_DATA_SLOT_MAX; ++idx) {<br>- new_stream->data[idx] = NULL;<br>- new_stream->data_free_fn[idx] = NULL;<br>- }<br>+ new_stream->metadata = ast_stream_get_metadata_list(stream);<br>+<br>+ /* rtp_codecs aren't cloned */<br> <br> return new_stream;<br> }<br> <br> void ast_stream_free(struct ast_stream *stream)<br> {<br>- int i;<br>-<br> if (!stream) {<br> return;<br> }<br> <br>- for (i = 0; i < AST_STREAM_DATA_SLOT_MAX; i++) {<br>- if (stream->data_free_fn[i]) {<br>- stream->data_free_fn[i](stream->data[i]);<br>- }<br>+ ast_variables_destroy(stream->metadata);<br>+<br>+ if (stream->rtp_codecs) {<br>+ ast_rtp_codecs_payloads_destroy(stream->rtp_codecs);<br> }<br> <br> ao2_cleanup(stream->formats);<br>@@ -238,22 +242,81 @@<br> return AST_STREAM_STATE_REMOVED;<br> }<br> <br>-void *ast_stream_get_data(struct ast_stream *stream, enum ast_stream_data_slot slot)<br>+const char *ast_stream_get_metadata(const struct ast_stream *stream, const char *m_key)<br> {<br>- ast_assert(stream != NULL);<br>+ struct ast_variable *v;<br> <br>- return stream->data[slot];<br>+ ast_assert_return(stream != NULL, NULL);<br>+ ast_assert_return(m_key != NULL, NULL);<br>+<br>+ for (v = stream->metadata; v; v = v->next) {<br>+ if (strcmp(v->name, m_key) == 0) {<br>+ return v->value;<br>+ }<br>+ }<br>+<br>+ return NULL;<br> }<br> <br>-void *ast_stream_set_data(struct ast_stream *stream, enum ast_stream_data_slot slot,<br>- void *data, ast_stream_data_free_fn data_free_fn)<br>+struct ast_variable *ast_stream_get_metadata_list(const struct ast_stream *stream)<br> {<br>- ast_assert(stream != NULL);<br>+ struct ast_variable *v;<br>+ struct ast_variable *vout = NULL;<br> <br>- stream->data[slot] = data;<br>- stream->data_free_fn[slot] = data_free_fn;<br>+ ast_assert_return(stream != NULL, NULL);<br> <br>- return data;<br>+ for (v = stream->metadata; v; v = v->next) {<br>+ struct ast_variable *vt = ast_variable_new(v->name, v->value, "");<br>+<br>+ if (!vt) {<br>+ ast_variables_destroy(vout);<br>+ return NULL;<br>+ }<br>+<br>+ ast_variable_list_append(&vout, vt);<br>+ }<br>+<br>+ return vout;<br>+}<br>+<br>+int ast_stream_set_metadata(struct ast_stream *stream, const char *m_key, const char *value)<br>+{<br>+ struct ast_variable *v;<br>+ struct ast_variable *prev;<br>+<br>+ ast_assert_return(stream != NULL, -1);<br>+ ast_assert_return(m_key != NULL, -1);<br>+<br>+ prev = NULL;<br>+ v = stream->metadata;<br>+ while(v) {<br>+ struct ast_variable *next = v->next;<br>+ if (strcmp(v->name, m_key) == 0) {<br>+ if (prev) {<br>+ prev->next = next;<br>+ } else {<br>+ stream->metadata = next;<br>+ }<br>+ ast_free(v);<br>+ break;<br>+ } else {<br>+ prev = v;<br>+ }<br>+ v = next;<br>+ }<br>+<br>+ if (!value) {<br>+ return 0;<br>+ }<br>+<br>+ v = ast_variable_new(m_key, value, "");<br>+ if (!v) {<br>+ return -1;<br>+ }<br>+<br>+ ast_variable_list_append(&stream->metadata, v);<br>+<br>+ return 0;<br> }<br> <br> int ast_stream_get_position(const struct ast_stream *stream)<br>@@ -263,6 +326,24 @@<br> return stream->position;<br> }<br> <br>+struct ast_rtp_codecs *ast_stream_get_rtp_codecs(const struct ast_stream *stream)<br>+{<br>+ ast_assert(stream != NULL);<br>+<br>+ return stream->rtp_codecs;<br>+}<br>+<br>+void ast_stream_set_rtp_codecs(struct ast_stream *stream, struct ast_rtp_codecs *rtp_codecs)<br>+{<br>+ ast_assert(stream != NULL);<br>+<br>+ if (stream->rtp_codecs) {<br>+ ast_rtp_codecs_payloads_destroy(rtp_codecs);<br>+ }<br>+<br>+ stream->rtp_codecs = rtp_codecs;<br>+}<br>+<br> #define TOPOLOGY_INITIAL_STREAM_COUNT 2<br> struct ast_stream_topology *ast_stream_topology_alloc(void)<br> {<br>diff --git a/tests/test_stream.c b/tests/test_stream.c<br>index 8c88704..50f0ccb 100644<br>--- a/tests/test_stream.c<br>+++ b/tests/test_stream.c<br>@@ -38,6 +38,7 @@<br> #include "asterisk/format_cap.h"<br> #include "asterisk/format_cache.h"<br> #include "asterisk/channel.h"<br>+#include "asterisk/uuid.h"<br> <br> AST_TEST_DEFINE(stream_create)<br> {<br>@@ -224,6 +225,70 @@<br> return AST_TEST_PASS;<br> }<br> <br>+AST_TEST_DEFINE(stream_metadata)<br>+{<br>+ RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free);<br>+ char track_label[AST_UUID_STR_LEN + 1];<br>+ const char *stream_track_label;<br>+ int rc;<br>+<br>+ switch (cmd) {<br>+ case TEST_INIT:<br>+ info->name = "stream_metadata";<br>+ info->category = "/main/stream/";<br>+ info->summary = "stream metadata unit test";<br>+ info->description =<br>+ "Test that metadata operations on a stream works";<br>+ return AST_TEST_NOT_RUN;<br>+ case TEST_EXECUTE:<br>+ break;<br>+ }<br>+<br>+ stream = ast_stream_alloc("test", AST_MEDIA_TYPE_AUDIO);<br>+ if (!stream) {<br>+ ast_test_status_update(test, "Failed to create media stream given proper arguments\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ stream_track_label = ast_stream_get_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (stream_track_label) {<br>+ ast_test_status_update(test, "New stream HAD a track label\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ ast_uuid_generate_str(track_label, sizeof(track_label));<br>+ rc = ast_stream_set_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL", track_label);<br>+ if (rc != 0) {<br>+ ast_test_status_update(test, "Failed to add track label\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ stream_track_label = ast_stream_get_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (!stream_track_label) {<br>+ ast_test_status_update(test, "Changed stream does not have a track label\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ if (strcmp(stream_track_label, track_label) != 0) {<br>+ ast_test_status_update(test, "Changed stream did not return same track label\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ rc = ast_stream_set_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL", NULL);<br>+ if (rc != 0) {<br>+ ast_test_status_update(test, "Failed to remove track label\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ stream_track_label = ast_stream_get_metadata(stream, "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (stream_track_label) {<br>+ ast_test_status_update(test, "Changed stream still had a track label after we removed it\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ return AST_TEST_PASS;<br>+}<br>+<br> AST_TEST_DEFINE(stream_topology_create)<br> {<br> RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free);<br>@@ -254,6 +319,11 @@<br> RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free);<br> RAII_VAR(struct ast_stream_topology *, cloned, NULL, ast_stream_topology_free);<br> struct ast_stream *audio_stream, *video_stream;<br>+ char audio_track_label[AST_UUID_STR_LEN + 1];<br>+ char video_track_label[AST_UUID_STR_LEN + 1];<br>+ const char *original_track_label;<br>+ const char *cloned_track_label;<br>+ int rc;<br> <br> switch (cmd) {<br> case TEST_INIT:<br>@@ -279,6 +349,13 @@<br> return AST_TEST_FAIL;<br> }<br> <br>+ ast_uuid_generate_str(audio_track_label, sizeof(audio_track_label));<br>+ rc = ast_stream_set_metadata(audio_stream, "AST_STREAM_METADATA_TRACK_LABEL", audio_track_label);<br>+ if (rc != 0) {<br>+ ast_test_status_update(test, "Failed to add track label\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br> if (ast_stream_topology_append_stream(topology, audio_stream) == -1) {<br> ast_test_status_update(test, "Failed to append valid audio stream to stream topology\n");<br> ast_stream_free(audio_stream);<br>@@ -288,6 +365,13 @@<br> video_stream = ast_stream_alloc("video", AST_MEDIA_TYPE_VIDEO);<br> if (!video_stream) {<br> ast_test_status_update(test, "Failed to create a video stream for testing stream topology\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ ast_uuid_generate_str(video_track_label, sizeof(video_track_label));<br>+ rc = ast_stream_set_metadata(video_stream, "AST_STREAM_METADATA_TRACK_LABEL", video_track_label);<br>+ if (rc != 0) {<br>+ ast_test_status_update(test, "Failed to add track label\n");<br> return AST_TEST_FAIL;<br> }<br> <br>@@ -313,8 +397,42 @@<br> return AST_TEST_FAIL;<br> }<br> <br>+ original_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(topology, 0),<br>+ "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (!original_track_label) {<br>+ ast_test_status_update(test, "Original topology stream 0 does not contain metadata\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+ cloned_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(cloned, 0),<br>+ "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (!cloned_track_label) {<br>+ ast_test_status_update(test, "Cloned topology stream 0 does not contain metadata\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+ if (strcmp(original_track_label, cloned_track_label) != 0) {<br>+ ast_test_status_update(test, "Cloned topology stream 0 track label was not the same as the original\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br> if (ast_stream_get_type(ast_stream_topology_get_stream(cloned, 1)) != ast_stream_get_type(ast_stream_topology_get_stream(topology, 1))) {<br> ast_test_status_update(test, "Cloned video stream does not contain same type as original\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+<br>+ original_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(topology, 1),<br>+ "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (!original_track_label) {<br>+ ast_test_status_update(test, "Original topology stream 1 does not contain metadata\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+ cloned_track_label = ast_stream_get_metadata(ast_stream_topology_get_stream(cloned, 1),<br>+ "AST_STREAM_METADATA_TRACK_LABEL");<br>+ if (!cloned_track_label) {<br>+ ast_test_status_update(test, "Cloned topology stream 1 does not contain metadata\n");<br>+ return AST_TEST_FAIL;<br>+ }<br>+ if (strcmp(original_track_label, cloned_track_label) != 0) {<br>+ ast_test_status_update(test, "Cloned topology stream 1 track label was not the same as the original\n");<br> return AST_TEST_FAIL;<br> }<br> <br>@@ -2139,6 +2257,7 @@<br> AST_TEST_UNREGISTER(stream_set_type);<br> AST_TEST_UNREGISTER(stream_set_formats);<br> AST_TEST_UNREGISTER(stream_set_state);<br>+ AST_TEST_UNREGISTER(stream_metadata);<br> AST_TEST_UNREGISTER(stream_topology_create);<br> AST_TEST_UNREGISTER(stream_topology_clone);<br> AST_TEST_UNREGISTER(stream_topology_clone);<br>@@ -2169,6 +2288,7 @@<br> AST_TEST_REGISTER(stream_set_type);<br> AST_TEST_REGISTER(stream_set_formats);<br> AST_TEST_REGISTER(stream_set_state);<br>+ AST_TEST_REGISTER(stream_metadata);<br> AST_TEST_REGISTER(stream_topology_create);<br> AST_TEST_REGISTER(stream_topology_clone);<br> AST_TEST_REGISTER(stream_topology_append_stream);<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/8796">change 8796</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/8796"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Id7473aa4b374d7ab53046c20e321037ba9a56863 </div>
<div style="display:none"> Gerrit-Change-Number: 8796 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>