diff --git a/docs/libs/gst-plugins-base-libs-docs.sgml b/docs/libs/gst-plugins-base-libs-docs.sgml index 6b6c9966ff..8149d29ab9 100644 --- a/docs/libs/gst-plugins-base-libs-docs.sgml +++ b/docs/libs/gst-plugins-base-libs-docs.sgml @@ -110,6 +110,7 @@ gstreamer-plugins-base-&GST_API_VERSION;.pc and adding -lgstrtp-&GST_API_VERSION; to the library flags. + diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index 5256114fff..687c1e67d3 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -1374,6 +1374,9 @@ GST_RTP_BASE_DEPAYLOAD_SRCPAD gst_rtp_base_depayload_push gst_rtp_base_depayload_push_list +gst_rtp_base_depayload_is_source_info_enabled +gst_rtp_base_depayload_set_source_info_enabled + GstRTPBaseDepayloadPrivate GST_TYPE_RTP_BASE_DEPAYLOAD @@ -1404,6 +1407,11 @@ gst_rtp_base_payload_push gst_rtp_base_payload_push_list gst_rtp_base_payload_set_options gst_rtp_base_payload_set_outcaps + +gst_rtp_base_payload_allocate_output_buffer +gst_rtp_base_payload_get_source_count +gst_rtp_base_payload_is_source_info_enabled +gst_rtp_base_payload_set_source_info_enabled GST_TYPE_RTP_BASE_PAYLOAD GST_RTP_BASE_PAYLOAD @@ -1414,6 +1422,23 @@ GST_IS_RTP_BASE_PAYLOAD_CLASS gst_rtp_base_payload_get_type +
+gstrtpmeta +gst/rtp/rtp.h +GstRTPSourceMeta +gst_buffer_add_rtp_source_meta +gst_buffer_get_rtp_source_meta +gst_rtp_source_meta_append_csrc +gst_rtp_source_meta_get_info +gst_rtp_source_meta_get_source_count +gst_rtp_source_meta_set_ssrc +GST_RTP_SOURCE_META_MAX_CSRC_COUNT + +gst_rtp_source_meta_api_get_type +GST_RTP_SOURCE_META_API_TYPE +GST_RTP_SOURCE_META_INFO +
+
gstrtcpbuffer gst/rtp/rtp.h diff --git a/gst-libs/gst/rtp/Makefile.am b/gst-libs/gst/rtp/Makefile.am index 4dce448945..f6b9963247 100644 --- a/gst-libs/gst/rtp/Makefile.am +++ b/gst-libs/gst/rtp/Makefile.am @@ -10,7 +10,8 @@ libgstrtpinclude_HEADERS = \ gstrtphdrext.h \ gstrtpbaseaudiopayload.h \ gstrtpbasepayload.h \ - gstrtpbasedepayload.h + gstrtpbasedepayload.h \ + gstrtpmeta.h lib_LTLIBRARIES = libgstrtp-@GST_API_VERSION@.la @@ -20,7 +21,8 @@ libgstrtp_@GST_API_VERSION@_la_SOURCES = gstrtpbuffer.c \ gstrtphdrext.c \ gstrtpbaseaudiopayload.c \ gstrtpbasepayload.c \ - gstrtpbasedepayload.c + gstrtpbasedepayload.c \ + gstrtpmeta.c built_sources = gstrtp-enumtypes.c built_headers = gstrtp-enumtypes.h diff --git a/gst-libs/gst/rtp/gstrtpbaseaudiopayload.c b/gst-libs/gst/rtp/gstrtpbaseaudiopayload.c index 52a4c095a3..3787448667 100644 --- a/gst-libs/gst/rtp/gstrtpbaseaudiopayload.c +++ b/gst-libs/gst/rtp/gstrtpbaseaudiopayload.c @@ -110,6 +110,7 @@ struct _GstRTPBaseAudioPayloadPrivate guint cached_max_length; guint cached_ptime_multiple; guint cached_align; + guint cached_csrc_count; gboolean buffer_list; }; @@ -453,7 +454,8 @@ gst_rtp_base_audio_payload_push (GstRTPBaseAudioPayload * baseaudiopayload, payload_len, GST_TIME_ARGS (timestamp)); /* create buffer to hold the payload */ - outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + outbuf = gst_rtp_base_payload_allocate_output_buffer (basepayload, + payload_len, 0, 0); /* copy payload */ gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); @@ -519,7 +521,7 @@ gst_rtp_base_audio_payload_push_buffer (GstRTPBaseAudioPayload * payload_len, GST_TIME_ARGS (timestamp)); /* create just the RTP header buffer */ - outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); + outbuf = gst_rtp_base_payload_allocate_output_buffer (basepayload, 0, 0, 0); /* set metadata */ gst_rtp_base_audio_payload_set_meta (baseaudiopayload, outbuf, payload_len, @@ -628,7 +630,7 @@ gst_rtp_base_audio_payload_flush (GstRTPBaseAudioPayload * baseaudiopayload, /* create buffer to hold the payload */ - outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); + outbuf = gst_rtp_base_payload_allocate_output_buffer (basepayload, 0, 0, 0); paybuf = gst_adapter_take_buffer_fast (adapter, payload_len); @@ -653,8 +655,8 @@ gst_rtp_base_audio_payload_flush (GstRTPBaseAudioPayload * baseaudiopayload, * mtu and min/max_ptime values. We cache those so that we don't have to redo * all the calculations */ static gboolean -gst_rtp_base_audio_payload_get_lengths (GstRTPBasePayload * - basepayload, guint * min_payload_len, guint * max_payload_len, +gst_rtp_base_audio_payload_get_lengths (GstRTPBasePayload * basepayload, + guint csrc_count, guint * min_payload_len, guint * max_payload_len, guint * align) { GstRTPBaseAudioPayload *payload; @@ -672,13 +674,16 @@ gst_rtp_base_audio_payload_get_lengths (GstRTPBasePayload * mtu = GST_RTP_BASE_PAYLOAD_MTU (payload); - /* check cached values */ + /* check cached values. Since csrc_count may vary for each packet, we only + * check whether the new value exceeds the cached value and thus result in + * smaller payload. */ if (G_LIKELY (priv->cached_mtu == mtu && priv->cached_ptime_multiple == basepayload->ptime_multiple && priv->cached_ptime == basepayload->ptime && priv->cached_max_ptime == basepayload->max_ptime - && priv->cached_min_ptime == basepayload->min_ptime)) { + && priv->cached_min_ptime == basepayload->min_ptime + && priv->cached_csrc_count >= csrc_count)) { /* if nothing changed, return cached values */ *min_payload_len = priv->cached_min_length; *max_payload_len = priv->cached_max_length; @@ -697,7 +702,7 @@ gst_rtp_base_audio_payload_get_lengths (GstRTPBasePayload * maxptime_octets = G_MAXUINT; } /* MTU max */ - max_mtu = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); + max_mtu = gst_rtp_buffer_calc_payload_len (mtu, 0, csrc_count); /* round down to alignment */ max_mtu = ALIGN_DOWN (max_mtu, *align); @@ -733,6 +738,7 @@ gst_rtp_base_audio_payload_get_lengths (GstRTPBasePayload * priv->cached_min_length = *min_payload_len; priv->cached_max_length = *max_payload_len; priv->cached_align = *align; + priv->cached_csrc_count = csrc_count; return TRUE; } @@ -875,8 +881,9 @@ gst_rtp_base_audio_payload_handle_buffer (GstRTPBasePayload * } } - if (!gst_rtp_base_audio_payload_get_lengths (basepayload, &min_payload_len, - &max_payload_len, &align)) + if (!gst_rtp_base_audio_payload_get_lengths (basepayload, + gst_rtp_base_payload_get_source_count (basepayload, buffer), + &min_payload_len, &max_payload_len, &align)) goto config_error; GST_DEBUG_OBJECT (payload, diff --git a/gst-libs/gst/rtp/gstrtpbasedepayload.c b/gst-libs/gst/rtp/gstrtpbasedepayload.c index 9047a011b3..c9c5204e1c 100644 --- a/gst-libs/gst/rtp/gstrtpbasedepayload.c +++ b/gst-libs/gst/rtp/gstrtpbasedepayload.c @@ -30,6 +30,7 @@ #endif #include "gstrtpbasedepayload.h" +#include "gstrtpmeta.h" GST_DEBUG_CATEGORY_STATIC (rtpbasedepayload_debug); #define GST_CAT_DEFAULT (rtpbasedepayload_debug) @@ -57,6 +58,9 @@ struct _GstRTPBaseDepayloadPrivate GstCaps *last_caps; GstEvent *segment_event; guint32 segment_seqnum; /* Note: this is a GstEvent seqnum */ + + gboolean source_info; + GstBuffer *input_buffer; }; /* Filter signals and args */ @@ -66,10 +70,13 @@ enum LAST_SIGNAL }; +#define DEFAULT_SOURCE_INFO FALSE + enum { PROP_0, PROP_STATS, + PROP_SOURCE_INFO, PROP_LAST }; @@ -183,6 +190,18 @@ gst_rtp_base_depayload_class_init (GstRTPBaseDepayloadClass * klass) g_param_spec_boxed ("stats", "Statistics", "Various statistics", GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GstRTPBaseDepayload:source-info: + * + * Add RTP source information found in RTP header as meta to output buffer. + * + * Since: 1.16 + **/ + g_object_class_install_property (gobject_class, PROP_SOURCE_INFO, + g_param_spec_boolean ("source-info", "RTP source information", + "Add RTP source information as buffer meta", + DEFAULT_SOURCE_INFO, G_PARAM_READWRITE)); + gstelement_class->change_state = gst_rtp_base_depayload_change_state; klass->packet_lost = gst_rtp_base_depayload_packet_lost; @@ -231,6 +250,7 @@ gst_rtp_base_depayload_init (GstRTPBaseDepayload * filter, priv->dts = -1; priv->pts = -1; priv->duration = -1; + priv->source_info = DEFAULT_SOURCE_INFO; gst_segment_init (&filter->segment, GST_FORMAT_UNDEFINED); } @@ -442,6 +462,8 @@ gst_rtp_base_depayload_handle_buffer (GstRTPBaseDepayload * filter, filter->need_newsegment = FALSE; } + priv->input_buffer = in; + if (process_rtp_packet_func != NULL) { out_buf = process_rtp_packet_func (filter, &rtp); gst_rtp_buffer_unmap (&rtp); @@ -458,6 +480,7 @@ gst_rtp_base_depayload_handle_buffer (GstRTPBaseDepayload * filter, } gst_buffer_unref (in); + priv->input_buffer = NULL; return ret; @@ -727,6 +750,30 @@ create_segment_event (GstRTPBaseDepayload * filter, guint rtptime, return event; } +static void +add_rtp_source_meta (GstBuffer * outbuf, GstBuffer * rtpbuf) +{ + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + GstRTPSourceMeta *meta; + guint32 ssrc; + + if (!gst_rtp_buffer_map (rtpbuf, GST_MAP_READ, &rtp)) + return; + + ssrc = gst_rtp_buffer_get_ssrc (&rtp); + meta = gst_buffer_add_rtp_source_meta (outbuf, &ssrc, NULL, 0); + if (meta != NULL) { + gint i; + gint csrc_count = gst_rtp_buffer_get_csrc_count (&rtp); + for (i = 0; i < csrc_count; i++) { + guint32 csrc = gst_rtp_buffer_get_csrc (&rtp, i); + gst_rtp_source_meta_append_csrc (meta, &csrc, 1); + } + } + + gst_rtp_buffer_unmap (&rtp); +} + static gboolean set_headers (GstBuffer ** buffer, guint idx, GstRTPBaseDepayload * depayload) { @@ -759,6 +806,9 @@ set_headers (GstBuffer ** buffer, guint idx, GstRTPBaseDepayload * depayload) priv->dts = GST_CLOCK_TIME_NONE; priv->duration = GST_CLOCK_TIME_NONE; + if (priv->source_info && priv->input_buffer) + add_rtp_source_meta (*buffer, priv->input_buffer); + return TRUE; } @@ -959,7 +1009,15 @@ static void gst_rtp_base_depayload_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (object); + switch (prop_id) { + case PROP_SOURCE_INFO: + gst_rtp_base_depayload_set_source_info_enabled (depayload, + g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -979,8 +1037,44 @@ gst_rtp_base_depayload_get_property (GObject * object, guint prop_id, g_value_take_boxed (value, gst_rtp_base_depayload_create_stats (depayload)); break; + case PROP_SOURCE_INFO: + g_value_set_boolean (value, + gst_rtp_base_depayload_is_source_info_enabled (depayload)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } + +/** + * gst_rtp_base_depayload_set_source_info_enabled: + * @depayload: a #GstRTPBaseDepayload + * @enable: whether to add meta about RTP sources to buffer + * + * Enable or disable adding #GstRTPSourceMeta to depayloaded buffers. + * + * Since: 1.16 + **/ +void +gst_rtp_base_depayload_set_source_info_enabled (GstRTPBaseDepayload * depayload, + gboolean enable) +{ + depayload->priv->source_info = enable; +} + +/** + * gst_rtp_base_depayload_is_source_info_enabled: + * @depayload: a #GstRTPBaseDepayload + * + * Queries whether #GstRTPSourceMeta will be added to depayloaded buffers. + * + * Returns: %TRUE if source-info is enabled. + * + * Since: 1.16 + **/ +gboolean +gst_rtp_base_depayload_is_source_info_enabled (GstRTPBaseDepayload * depayload) +{ + return depayload->priv->source_info; +} diff --git a/gst-libs/gst/rtp/gstrtpbasedepayload.h b/gst-libs/gst/rtp/gstrtpbasedepayload.h index f409ff9743..42af745071 100644 --- a/gst-libs/gst/rtp/gstrtpbasedepayload.h +++ b/gst-libs/gst/rtp/gstrtpbasedepayload.h @@ -120,6 +120,13 @@ GstFlowReturn gst_rtp_base_depayload_push (GstRTPBaseDepayload *filter, GST_RTP_API GstFlowReturn gst_rtp_base_depayload_push_list (GstRTPBaseDepayload *filter, GstBufferList *out_list); +GST_RTP_API +gboolean gst_rtp_base_depayload_is_source_info_enabled (GstRTPBaseDepayload * depayload); + +GST_RTP_API +void gst_rtp_base_depayload_set_source_info_enabled (GstRTPBaseDepayload * depayload, + gboolean enable); + #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTPBaseDepayload, gst_object_unref) diff --git a/gst-libs/gst/rtp/gstrtpbasepayload.c b/gst-libs/gst/rtp/gstrtpbasepayload.c index b518ac735f..23129d9ea8 100644 --- a/gst-libs/gst/rtp/gstrtpbasepayload.c +++ b/gst-libs/gst/rtp/gstrtpbasepayload.c @@ -29,6 +29,7 @@ #include #include "gstrtpbasepayload.h" +#include "gstrtpmeta.h" GST_DEBUG_CATEGORY_STATIC (rtpbasepayload_debug); #define GST_CAT_DEFAULT (rtpbasepayload_debug) @@ -44,6 +45,9 @@ struct _GstRTPBasePayloadPrivate gboolean pt_set; + gboolean source_info; + GstBuffer *input_meta_buffer; + guint64 base_offset; gint64 base_rtime; guint64 base_rtime_hz; @@ -84,6 +88,7 @@ enum #define DEFAULT_PERFECT_RTPTIME TRUE #define DEFAULT_PTIME_MULTIPLE 0 #define DEFAULT_RUNNING_TIME GST_CLOCK_TIME_NONE +#define DEFAULT_SOURCE_INFO FALSE enum { @@ -100,6 +105,7 @@ enum PROP_PERFECT_RTPTIME, PROP_PTIME_MULTIPLE, PROP_STATS, + PROP_SOURCE_INFO, PROP_LAST }; @@ -299,6 +305,19 @@ gst_rtp_base_payload_class_init (GstRTPBasePayloadClass * klass) g_param_spec_boxed ("stats", "Statistics", "Various statistics", GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GstRTPBasePayload:source-info: + * + * Enable writing the CSRC field in allocated RTP header based on RTP source + * information found in the input buffer's #GstRTPSourceMeta. + * + * Since: 1.16 + **/ + g_object_class_install_property (gobject_class, PROP_SOURCE_INFO, + g_param_spec_boolean ("source-info", "RTP source information", + "Write CSRC based on buffer meta RTP source information", + DEFAULT_SOURCE_INFO, G_PARAM_READWRITE)); + gstelement_class->change_state = gst_rtp_base_payload_change_state; klass->get_caps = gst_rtp_base_payload_getcaps_default; @@ -351,6 +370,7 @@ gst_rtp_base_payload_init (GstRTPBasePayload * rtpbasepayload, gpointer g_class) priv->ts_offset_random = (rtpbasepayload->ts_offset == -1); priv->ssrc_random = (rtpbasepayload->ssrc == -1); priv->pt_set = FALSE; + priv->source_info = DEFAULT_SOURCE_INFO; rtpbasepayload->max_ptime = DEFAULT_MAX_PTIME; rtpbasepayload->min_ptime = DEFAULT_MIN_PTIME; @@ -633,6 +653,15 @@ gst_rtp_base_payload_chain (GstPad * pad, GstObject * parent, if (!rtpbasepayload->priv->negotiated) goto not_negotiated; + if (rtpbasepayload->priv->source_info) { + /* Save a copy of meta (instead of taking an extra reference before + * handle_buffer) to make the meta available when allocating a output + * buffer. */ + rtpbasepayload->priv->input_meta_buffer = gst_buffer_new (); + gst_buffer_copy_into (rtpbasepayload->priv->input_meta_buffer, buffer, + GST_BUFFER_COPY_META, 0, -1); + } + if (gst_pad_check_reconfigure (GST_RTP_BASE_PAYLOAD_SRCPAD (rtpbasepayload))) { if (!gst_rtp_base_payload_negotiate (rtpbasepayload)) { gst_pad_mark_reconfigure (GST_RTP_BASE_PAYLOAD_SRCPAD (rtpbasepayload)); @@ -646,6 +675,8 @@ gst_rtp_base_payload_chain (GstPad * pad, GstObject * parent, ret = rtpbasepayload_class->handle_buffer (rtpbasepayload, buffer); + gst_buffer_replace (&rtpbasepayload->priv->input_meta_buffer, NULL); + return ret; /* ERRORS */ @@ -1156,6 +1187,25 @@ map_failed: } } +static gboolean +foreach_metadata_drop (GstBuffer * buffer, GstMeta ** meta, gpointer user_data) +{ + GType drop_api_type = (GType) GPOINTER_TO_INT (user_data); + const GstMetaInfo *info = (*meta)->info; + + if (info->api == drop_api_type) + *meta = NULL; + + return TRUE; +} + +static gboolean +filter_meta (GstBuffer ** buffer, guint idx, gpointer user_data) +{ + return gst_buffer_foreach_meta (*buffer, foreach_metadata_drop, + GINT_TO_POINTER (GST_RTP_SOURCE_META_API_TYPE)); +} + /* Updates the SSRC, payload type, seqnum and timestamp of the RTP buffer * before the buffer is pushed. */ static GstFlowReturn @@ -1252,11 +1302,14 @@ gst_rtp_base_payload_prepare_push (GstRTPBasePayload * payload, } /* set ssrc, payload type, seq number, caps and rtptime */ + /* remove unwanted meta */ if (is_list) { gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj), set_headers, &data); + gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj), filter_meta, NULL); } else { GstBuffer *buf = GST_BUFFER_CAST (obj); set_headers (&buf, 0, &data); + filter_meta (&buf, 0, NULL); } priv->next_seqnum = data.seqnum; @@ -1267,8 +1320,8 @@ gst_rtp_base_payload_prepare_push (GstRTPBasePayload * payload, (is_list) ? -1 : gst_buffer_get_size (GST_BUFFER (obj)), payload->seqnum, data.rtptime, GST_TIME_ARGS (data.pts)); - if (g_atomic_int_compare_and_exchange (&payload-> - priv->notified_first_timestamp, 1, 0)) { + if (g_atomic_int_compare_and_exchange (&payload->priv-> + notified_first_timestamp, 1, 0)) { g_object_notify (G_OBJECT (payload), "timestamp"); g_object_notify (G_OBJECT (payload), "seqnum"); } @@ -1351,6 +1404,62 @@ gst_rtp_base_payload_push (GstRTPBasePayload * payload, GstBuffer * buffer) return res; } +/** + * gst_rtp_base_payload_allocate_output_buffer: + * @payload: a #GstRTPBasePayload + * @payload_len: the length of the payload + * @pad_len: the amount of padding + * @csrc_count: the minimum number of CSRC entries + * + * Allocate a new #GstBuffer with enough data to hold an RTP packet with + * minimum @csrc_count CSRCs, a payload length of @payload_len and padding of + * @pad_len. If @payload has #GstRTPBasePayload:source-info %TRUE additional + * CSRCs may be allocated and filled with RTP source information. + * + * Returns: A newly allocated buffer that can hold an RTP packet with given + * parameters. + * + * Since: 1.16 + */ +GstBuffer * +gst_rtp_base_payload_allocate_output_buffer (GstRTPBasePayload * payload, + guint payload_len, guint8 pad_len, guint8 csrc_count) +{ + GstBuffer *buffer = NULL; + + if (payload->priv->input_meta_buffer != NULL) { + GstRTPSourceMeta *meta = + gst_buffer_get_rtp_source_meta (payload->priv->input_meta_buffer); + if (meta != NULL) { + guint total_csrc_count, idx, i; + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + + total_csrc_count = csrc_count + meta->csrc_count + + (meta->ssrc_valid ? 1 : 0); + total_csrc_count = MIN (total_csrc_count, 15); + buffer = gst_rtp_buffer_new_allocate (payload_len, pad_len, + total_csrc_count); + + gst_rtp_buffer_map (buffer, GST_MAP_READWRITE, &rtp); + + /* Skip CSRC fields requested by derived class and fill CSRCs from meta. + * Finally append the SSRC as a new CSRC. */ + idx = csrc_count; + for (i = 0; i < meta->csrc_count && idx < 15; i++, idx++) + gst_rtp_buffer_set_csrc (&rtp, idx, meta->csrc[i]); + if (meta->ssrc_valid && idx < 15) + gst_rtp_buffer_set_csrc (&rtp, idx, meta->ssrc); + + gst_rtp_buffer_unmap (&rtp); + } + } + + if (buffer == NULL) + buffer = gst_rtp_buffer_new_allocate (payload_len, pad_len, csrc_count); + + return buffer; +} + static GstStructure * gst_rtp_base_payload_create_stats (GstRTPBasePayload * rtpbasepayload) { @@ -1421,6 +1530,10 @@ gst_rtp_base_payload_set_property (GObject * object, guint prop_id, case PROP_PTIME_MULTIPLE: rtpbasepayload->ptime_multiple = g_value_get_int64 (value); break; + case PROP_SOURCE_INFO: + gst_rtp_base_payload_set_source_info_enabled (rtpbasepayload, + g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1484,6 +1597,10 @@ gst_rtp_base_payload_get_property (GObject * object, guint prop_id, g_value_take_boxed (value, gst_rtp_base_payload_create_stats (rtpbasepayload)); break; + case PROP_SOURCE_INFO: + g_value_set_boolean (value, + gst_rtp_base_payload_is_source_info_enabled (rtpbasepayload)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1551,3 +1668,68 @@ gst_rtp_base_payload_change_state (GstElement * element, } return ret; } + +/** + * gst_rtp_base_payload_set_source_info_enabled: + * @payload: a #GstRTPBasePayload + * @enable: whether to add contributing sources to RTP packets + * + * Enable or disable adding contributing sources to RTP packets from + * #GstRTPSourceMeta. + * + * Since: 1.16 + **/ +void +gst_rtp_base_payload_set_source_info_enabled (GstRTPBasePayload * payload, + gboolean enable) +{ + payload->priv->source_info = enable; +} + +/** + * gst_rtp_base_payload_is_source_info_enabled: + * @payload: a #GstRTPBasePayload + * + * Queries whether the payloader will add contributing sources (CSRCs) to the + * RTP header from #GstRTPSourceMeta. + * + * Returns: %TRUE if source-info is enabled. + * + * Since: 1.16 + **/ +gboolean +gst_rtp_base_payload_is_source_info_enabled (GstRTPBasePayload * payload) +{ + return payload->priv->source_info; +} + + +/** + * gst_rtp_base_payload_get_source_count: + * @payload: a #GstRTPBasePayload + * @buffer: (transfer none): a #GstBuffer, typically the buffer to payload + * + * Count the total number of RTP sources found in the meta of @buffer, which + * will be automically added by gst_rtp_base_payload_allocate_output_buffer(). + * If #GstRTPBasePayload:source-info is %FALSE the count will be 0. + * + * Returns: The number of sources. + * + * Since: 1.16 + **/ +guint +gst_rtp_base_payload_get_source_count (GstRTPBasePayload * payload, + GstBuffer * buffer) +{ + guint count = 0; + + g_return_val_if_fail (buffer != NULL, 0); + + if (gst_rtp_base_payload_is_source_info_enabled (payload)) { + GstRTPSourceMeta *meta = gst_buffer_get_rtp_source_meta (buffer); + if (meta != NULL) + count = gst_rtp_source_meta_get_source_count (meta); + } + + return count; +} diff --git a/gst-libs/gst/rtp/gstrtpbasepayload.h b/gst-libs/gst/rtp/gstrtpbasepayload.h index b477e8a216..8b9e984aeb 100644 --- a/gst-libs/gst/rtp/gstrtpbasepayload.h +++ b/gst-libs/gst/rtp/gstrtpbasepayload.h @@ -172,6 +172,22 @@ GST_RTP_API GstFlowReturn gst_rtp_base_payload_push_list (GstRTPBasePayload *payload, GstBufferList *list); +GST_RTP_API +GstBuffer * gst_rtp_base_payload_allocate_output_buffer (GstRTPBasePayload * payload, + guint payload_len, guint8 pad_len, + guint8 csrc_count); + +GST_RTP_API +void gst_rtp_base_payload_set_source_info_enabled (GstRTPBasePayload * payload, + gboolean enable); + +GST_RTP_API +gboolean gst_rtp_base_payload_is_source_info_enabled (GstRTPBasePayload * payload); + +GST_RTP_API +guint gst_rtp_base_payload_get_source_count (GstRTPBasePayload * payload, + GstBuffer * buffer); + #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTPBasePayload, gst_object_unref) #endif diff --git a/gst-libs/gst/rtp/gstrtpmeta.c b/gst-libs/gst/rtp/gstrtpmeta.c new file mode 100644 index 0000000000..9028db82db --- /dev/null +++ b/gst-libs/gst/rtp/gstrtpmeta.c @@ -0,0 +1,229 @@ +/* GStreamer + * Copyright (C) <2016> Stian Selnes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstrtpmeta.h" +#include + +/** + * SECTION:gstrtpmeta + * @title: GstMeta for RTP + * @short_description: RTP related GstMeta + * + */ + +/** + * gst_buffer_add_rtp_source_meta: + * @buffer: a #GstBuffer + * @ssrc: (allow-none) (transfer none): pointer to the SSRC + * @csrc: (allow-none) (transfer none): pointer to the CSRCs + * @csrc_count: number of elements in @csrc + * + * Attaches RTP source information to @buffer. + * + * Returns: (transfer none): the #GstRTPSourceMeta on @buffer. + * + * Since: 1.16 + */ +GstRTPSourceMeta * +gst_buffer_add_rtp_source_meta (GstBuffer * buffer, const guint32 * ssrc, + const guint * csrc, guint csrc_count) +{ + gint i; + GstRTPSourceMeta *meta; + + g_return_val_if_fail (buffer != NULL, NULL); + g_return_val_if_fail (csrc_count <= GST_RTP_SOURCE_META_MAX_CSRC_COUNT, NULL); + g_return_val_if_fail (csrc_count == 0 || csrc != NULL, NULL); + + meta = (GstRTPSourceMeta *) gst_buffer_add_meta (buffer, + GST_RTP_SOURCE_META_INFO, NULL); + if (!meta) + return NULL; + + if (ssrc != NULL) { + meta->ssrc = *ssrc; + meta->ssrc_valid = TRUE; + } else { + meta->ssrc_valid = FALSE; + } + + meta->csrc_count = csrc_count; + for (i = 0; i < csrc_count; i++) { + meta->csrc[i] = csrc[i]; + } + + return meta; +} + +/** + * gst_buffer_get_rtp_source_meta: + * @buffer: a #GstBuffer + * + * Find the #GstRTPSourceMeta on @buffer. + * + * Returns: (transfer none): the #GstRTPSourceMeta or %NULL when there + * is no such metadata on @buffer. + * + * Since: 1.16 + */ +GstRTPSourceMeta * +gst_buffer_get_rtp_source_meta (GstBuffer * buffer) +{ + return (GstRTPSourceMeta *) gst_buffer_get_meta (buffer, + gst_rtp_source_meta_api_get_type ()); +} + +static gboolean +gst_rtp_source_meta_transform (GstBuffer * dst, GstMeta * meta, + GstBuffer * src, GQuark type, gpointer data) +{ + if (GST_META_TRANSFORM_IS_COPY (type)) { + GstRTPSourceMeta *smeta = (GstRTPSourceMeta *) meta; + GstRTPSourceMeta *dmeta; + guint32 *ssrc = smeta->ssrc_valid ? &smeta->ssrc : NULL; + + dmeta = gst_buffer_add_rtp_source_meta (dst, ssrc, smeta->csrc, + smeta->csrc_count); + if (dmeta == NULL) + return FALSE; + } else { + /* return FALSE, if transform type is not supported */ + return FALSE; + } + + return TRUE; +} + +/** + * gst_rtp_source_meta_get_source_count: + * @meta: a #GstRTPSourceMeta + * + * Count the total number of RTP sources found in @meta, both SSRC and CSRC. + * + * Returns: The number of RTP sources + * + * Since: 1.16 + */ +guint +gst_rtp_source_meta_get_source_count (const GstRTPSourceMeta * meta) +{ + /* Never return more than a count of 15 so that the returned value + * conveniently can be used as argument 'csrc_count' in + * gst_rtp_buffer-functions. */ + guint ssrc_count = meta->ssrc_valid ? 1 : 0; + return MIN (meta->csrc_count + ssrc_count, 15); +} + +/** + * gst_rtp_source_meta_set_ssrc: + * @meta: a #GstRTPSourceMeta + * @ssrc: (allow-none) (transfer none): pointer to the SSRC + * + * Sets @ssrc in @meta. If @ssrc is %NULL the ssrc of @meta will be unset. + * + * Returns: %TRUE on success, %FALSE otherwise. + * + * Since: 1.16 + **/ +gboolean +gst_rtp_source_meta_set_ssrc (GstRTPSourceMeta * meta, guint32 * ssrc) +{ + if (ssrc != NULL) { + meta->ssrc = *ssrc; + meta->ssrc_valid = TRUE; + } else { + meta->ssrc_valid = FALSE; + } + + return TRUE; +} + +/** + * gst_rtp_source_meta_append_csrc: + * @meta: a #GstRTPSourceMeta + * @csrc: the csrcs to append + * @csrc_count: number of elements in @csrc + * + * Appends @csrc to the list of contributing sources in @meta. + * + * Returns: %TRUE if all elements in @csrc was added, %FALSE otherwise. + * + * Since: 1.16 + **/ +gboolean +gst_rtp_source_meta_append_csrc (GstRTPSourceMeta * meta, const guint32 * csrc, + guint csrc_count) +{ + gint i; + guint new_csrc_count = meta->csrc_count + csrc_count; + + if (new_csrc_count > GST_RTP_SOURCE_META_MAX_CSRC_COUNT) + return FALSE; + + for (i = 0; i < csrc_count; i++) + meta->csrc[meta->csrc_count + i] = csrc[i]; + meta->csrc_count = new_csrc_count; + + return TRUE; +} + +GType +gst_rtp_source_meta_api_get_type (void) +{ + static volatile GType type = 0; + static const gchar *tags[] = { NULL }; + + if (g_once_init_enter (&type)) { + GType _type = gst_meta_api_type_register ("GstRTPSourceMetaAPI", tags); + g_once_init_leave (&type, _type); + } + return type; +} + +static gboolean +gst_rtp_source_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) +{ + GstRTPSourceMeta *dmeta = (GstRTPSourceMeta *) meta; + + dmeta->ssrc_valid = FALSE; + dmeta->csrc_count = 0; + + return TRUE; +} + +const GstMetaInfo * +gst_rtp_source_meta_get_info (void) +{ + static const GstMetaInfo *rtp_source_meta_info = NULL; + + if (g_once_init_enter (&rtp_source_meta_info)) { + const GstMetaInfo *meta = gst_meta_register (GST_RTP_SOURCE_META_API_TYPE, + "GstRTPSourceMeta", + sizeof (GstRTPSourceMeta), + gst_rtp_source_meta_init, + (GstMetaFreeFunction) NULL, + gst_rtp_source_meta_transform); + g_once_init_leave (&rtp_source_meta_info, meta); + } + return rtp_source_meta_info; +} diff --git a/gst-libs/gst/rtp/gstrtpmeta.h b/gst-libs/gst/rtp/gstrtpmeta.h new file mode 100644 index 0000000000..956cff7ef9 --- /dev/null +++ b/gst-libs/gst/rtp/gstrtpmeta.h @@ -0,0 +1,79 @@ +/* GStreamer + * Copyright (C) <2016> Stian Selnes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_META_H__ +#define __GST_RTP_META_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_RTP_SOURCE_META_API_TYPE (gst_rtp_source_meta_api_get_type()) +#define GST_RTP_SOURCE_META_INFO (gst_rtp_source_meta_get_info()) +typedef struct _GstRTPSourceMeta GstRTPSourceMeta; + +#define GST_RTP_SOURCE_META_MAX_CSRC_COUNT 15 + +/** + * GstRTPSourceMeta: + * @meta: parent #GstMeta + * @ssrc: the SSRC + * @ssrc_valid: whether @ssrc is set and valid + * @csrc: (allow-none): pointer to the CSRCs + * @csrc_count: number of elements in @csrc + * + * Meta describing the source(s) of the buffer. + * + * Since: 1.16 + */ +struct _GstRTPSourceMeta +{ + GstMeta meta; + + guint32 ssrc; + gboolean ssrc_valid; + guint32 csrc[GST_RTP_SOURCE_META_MAX_CSRC_COUNT]; + guint csrc_count; +}; + +GST_RTP_API +GType gst_rtp_source_meta_api_get_type (void); + +GST_RTP_API +GstRTPSourceMeta * gst_buffer_add_rtp_source_meta (GstBuffer * buf, const guint32 * ssrc, + const guint32 * csrc, guint csrc_count); +GST_RTP_API +GstRTPSourceMeta * gst_buffer_get_rtp_source_meta (GstBuffer * buf); + +GST_RTP_API +guint gst_rtp_source_meta_get_source_count (const GstRTPSourceMeta * meta); + +GST_RTP_API +gboolean gst_rtp_source_meta_set_ssrc (GstRTPSourceMeta * meta, guint32 * ssrc); + +GST_RTP_API +gboolean gst_rtp_source_meta_append_csrc (GstRTPSourceMeta * meta, + const guint32 * csrc, guint csrc_count); +GST_RTP_API +const GstMetaInfo * gst_rtp_source_meta_get_info (void); + +G_END_DECLS + +#endif /* __GST_RTP_META_H__ */ diff --git a/gst-libs/gst/rtp/meson.build b/gst-libs/gst/rtp/meson.build index 25d3900ddc..f47ec65924 100644 --- a/gst-libs/gst/rtp/meson.build +++ b/gst-libs/gst/rtp/meson.build @@ -3,6 +3,7 @@ rtp_sources = [ 'gstrtcpbuffer.c', 'gstrtppayloads.c', 'gstrtphdrext.c', + 'gstrtpmeta.c', 'gstrtpbaseaudiopayload.c', 'gstrtpbasepayload.c', 'gstrtpbasedepayload.c' @@ -16,6 +17,7 @@ rtp_headers = [ 'gstrtpbuffer.h', 'gstrtpdefs.h', 'gstrtphdrext.h', + 'gstrtpmeta.h', 'gstrtppayloads.h', 'rtp-prelude.h', 'rtp.h', diff --git a/gst-libs/gst/rtp/rtp.h b/gst-libs/gst/rtp/rtp.h index 546d4aef38..0e6633bd81 100644 --- a/gst-libs/gst/rtp/rtp.h +++ b/gst-libs/gst/rtp/rtp.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #endif /* __GST_RTP_H__ */ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index f1569a6b65..6223650e99 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -245,6 +245,7 @@ check_PROGRAMS = \ libs/rtp \ libs/rtpbasedepayload \ libs/rtpbasepayload \ + libs/rtpmeta \ libs/rtsp \ libs/rtspconnection \ libs/sdp \ @@ -581,6 +582,13 @@ libs_rtpbasedepayload_LDADD = \ $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_API_VERSION@.la \ $(GST_BASE_LIBS) $(LDADD) +libs_rtpmeta_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(AM_CFLAGS) +libs_rtpmeta_LDADD = \ + $(top_builddir)/gst-libs/gst/rtp/libgstrtp-@GST_API_VERSION@.la \ + $(GST_BASE_LIBS) $(LDADD) + libs_rtsp_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(AM_CFLAGS) diff --git a/tests/check/libs/.gitignore b/tests/check/libs/.gitignore index 64a1eb63e7..78cf13f11c 100644 --- a/tests/check/libs/.gitignore +++ b/tests/check/libs/.gitignore @@ -29,6 +29,7 @@ profile rtp rtpbasedepayload rtpbasepayload +rtpmeta rtsp rtspconnection sdp diff --git a/tests/check/libs/rtpbasedepayload.c b/tests/check/libs/rtpbasedepayload.c index f24b0c46cb..01f1a372d2 100644 --- a/tests/check/libs/rtpbasedepayload.c +++ b/tests/check/libs/rtpbasedepayload.c @@ -23,8 +23,8 @@ #include #include -#include -#include +#include +#include #define DEFAULT_CLOCK_RATE (42) @@ -317,22 +317,14 @@ validate_event (guint index, const gchar * name, const gchar * field, ...) va_end (var_args); } -#define push_rtp_buffer(state, field, ...) \ - push_rtp_buffer_full ((state), GST_FLOW_OK, (field), __VA_ARGS__) -#define push_rtp_buffer_fails(state, error, field, ...) \ - push_rtp_buffer_full ((state), (error), (field), __VA_ARGS__) - static void -push_rtp_buffer_full (State * state, GstFlowReturn expected, - const gchar * field, ...) +rtp_buffer_set_valist (GstBuffer * buf, const gchar * field, va_list var_args, + gboolean * extra_ref_) { - GstBuffer *buf = gst_rtp_buffer_new_allocate (0, 0, 0); GstRTPBuffer rtp = { NULL }; gboolean mapped = FALSE; gboolean extra_ref = FALSE; - va_list var_args; - va_start (var_args, field); while (field) { if (!g_strcmp0 (field, "pts")) { GstClockTime pts = va_arg (var_args, GstClockTime); @@ -366,13 +358,18 @@ push_rtp_buffer_full (State * state, GstFlowReturn expected, gst_rtp_buffer_set_ssrc (&rtp, ssrc); } else if (!g_strcmp0 (field, "extra-ref")) { extra_ref = va_arg (var_args, gboolean); + if (extra_ref_) + *extra_ref_ = extra_ref; + } else if (!g_strcmp0 (field, "csrc")) { + guint idx = va_arg (var_args, guint); + guint csrc = va_arg (var_args, guint); + gst_rtp_buffer_set_csrc (&rtp, idx, csrc); } else { fail ("test cannot set unknown buffer field '%s'", field); } } field = va_arg (var_args, const gchar *); } - va_end (var_args); if (mapped) { gst_rtp_buffer_unmap (&rtp); @@ -380,6 +377,34 @@ push_rtp_buffer_full (State * state, GstFlowReturn expected, if (extra_ref) gst_buffer_ref (buf); +} + +static void +rtp_buffer_set (GstBuffer * buf, const gchar * field, ...) +{ + va_list var_args; + + va_start (var_args, field); + rtp_buffer_set_valist (buf, field, var_args, NULL); + va_end (var_args); +} + +#define push_rtp_buffer(state, field, ...) \ + push_rtp_buffer_full ((state), GST_FLOW_OK, (field), __VA_ARGS__) +#define push_rtp_buffer_fails(state, error, field, ...) \ + push_rtp_buffer_full ((state), (error), (field), __VA_ARGS__) + +static void +push_rtp_buffer_full (State * state, GstFlowReturn expected, + const gchar * field, ...) +{ + GstBuffer *buf = gst_rtp_buffer_new_allocate (0, 0, 0); + va_list var_args; + gboolean extra_ref = FALSE; + + va_start (var_args, field); + rtp_buffer_set_valist (buf, field, var_args, &extra_ref); + va_end (var_args); fail_unless_equals_int (gst_pad_push (state->srcpad, buf), expected); @@ -388,7 +413,7 @@ push_rtp_buffer_full (State * state, GstFlowReturn expected, } #define push_buffer(state, field, ...) \ - push_buffer_full ((state), GST_FLOW_OK, (field), __VA_ARGS__) + push_buffer_full ((state), GST_FLOW_OK, (field), __VA_ARGS__) static void push_buffer_full (State * state, GstFlowReturn expected, @@ -1237,7 +1262,64 @@ GST_START_TEST (rtp_base_depayload_clock_base_test) destroy_depayloader (state); } -GST_END_TEST static Suite * +GST_END_TEST +/* basedepayloader has a property source-info that will add + * GstRTPSourceMeta to the output buffer with RTP source information, such as + * SSRC and CSRCs. The is useful for letting downstream know about the origin + * of the stream. */ +GST_START_TEST (rtp_base_depayload_source_info_test) +{ + GstHarness *h; + GstRtpDummyDepay *depay; + GstBuffer *buffer; + GstRTPSourceMeta *meta; + guint seq = 0; + + depay = rtp_dummy_depay_new (); + h = gst_harness_new_with_element (GST_ELEMENT_CAST (depay), "sink", "src"); + gst_harness_set_src_caps_str (h, "application/x-rtp"); + + /* Property enabled should always add meta, also when there is only SSRC and + * no CSRC. */ + g_object_set (depay, "source-info", TRUE, NULL); + buffer = gst_rtp_buffer_new_allocate (0, 0, 0); + rtp_buffer_set (buffer, "seq", seq++, "ssrc", 0x11, NULL); + buffer = gst_harness_push_and_pull (h, buffer); + fail_unless ((meta = gst_buffer_get_rtp_source_meta (buffer))); + fail_unless (meta->ssrc_valid); + fail_unless_equals_int (meta->ssrc, 0x11); + fail_unless_equals_int (meta->csrc_count, 0); + gst_buffer_unref (buffer); + + /* Both SSRC and CSRC should be added to the meta */ + buffer = gst_rtp_buffer_new_allocate (0, 0, 2); + rtp_buffer_set (buffer, "seq", seq++, "ssrc", 0x11, "csrc", 0, 0x22, + "csrc", 1, 0x33, NULL); + buffer = gst_harness_push_and_pull (h, buffer); + fail_unless ((meta = gst_buffer_get_rtp_source_meta (buffer))); + fail_unless (meta->ssrc_valid); + fail_unless_equals_int (meta->ssrc, 0x11); + fail_unless_equals_int (meta->csrc_count, 2); + fail_unless_equals_int (meta->csrc[0], 0x22); + fail_unless_equals_int (meta->csrc[1], 0x33); + gst_buffer_unref (buffer); + + /* Property disabled should never add meta */ + g_object_set (depay, "source-info", FALSE, NULL); + buffer = gst_rtp_buffer_new_allocate (0, 0, 0); + rtp_buffer_set (buffer, "seq", seq++, "ssrc", 0x11, NULL); + buffer = gst_harness_push_and_pull (h, buffer); + fail_if (gst_buffer_get_rtp_source_meta (buffer)); + gst_buffer_unref (buffer); + + g_object_unref (depay); + gst_harness_teardown (h); +} + +GST_END_TEST; + + +static Suite * rtp_basepayloading_suite (void) { Suite *s = suite_create ("rtp_base_depayloading_test"); @@ -1265,6 +1347,8 @@ rtp_basepayloading_suite (void) tcase_add_test (tc_chain, rtp_base_depayload_play_speed_test); tcase_add_test (tc_chain, rtp_base_depayload_clock_base_test); + tcase_add_test (tc_chain, rtp_base_depayload_source_info_test); + return s; } diff --git a/tests/check/libs/rtpbasepayload.c b/tests/check/libs/rtpbasepayload.c index 9385ce949c..9bda374079 100644 --- a/tests/check/libs/rtpbasepayload.c +++ b/tests/check/libs/rtpbasepayload.c @@ -23,8 +23,8 @@ #include #include -#include -#include +#include +#include #define DEFAULT_CLOCK_RATE (42) #define BUFFER_BEFORE_LIST (10) @@ -121,7 +121,9 @@ gst_rtp_dummy_pay_handle_buffer (GstRTPBasePayload * pay, GstBuffer * buffer) } } - paybuffer = gst_rtp_buffer_new_allocate (0, 0, 0); + paybuffer = + gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD (pay), + 0, 0, 0); GST_BUFFER_PTS (paybuffer) = GST_BUFFER_PTS (buffer); GST_BUFFER_OFFSET (paybuffer) = GST_BUFFER_OFFSET (buffer); @@ -444,20 +446,11 @@ validate_buffers_received (guint received_buffers) } static void -validate_buffer (guint index, const gchar * field, ...) +validate_buffer_valist (GstBuffer * buf, const gchar * field, va_list var_args) { - GstBuffer *buf; GstRTPBuffer rtp = { NULL }; gboolean mapped = FALSE; - va_list var_args; - fail_if (index >= g_list_length (buffers)); - buf = GST_BUFFER (g_list_nth_data (buffers, index)); - fail_if (buf == NULL); - - GST_TRACE ("%" GST_PTR_FORMAT, buf); - - va_start (var_args, field); while (field) { if (!g_strcmp0 (field, "pts")) { GstClockTime pts = va_arg (var_args, GstClockTime); @@ -489,19 +482,53 @@ validate_buffer (guint index, const gchar * field, ...) } else if (!g_strcmp0 (field, "ssrc")) { guint32 ssrc = va_arg (var_args, guint); fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), ssrc); + } else if (!g_strcmp0 (field, "csrc")) { + guint idx = va_arg (var_args, guint); + guint csrc = va_arg (var_args, guint); + fail_unless_equals_int (gst_rtp_buffer_get_csrc (&rtp, idx), csrc); + } else if (!g_strcmp0 (field, "csrc-count")) { + guint csrc_count = va_arg (var_args, guint); + fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp), + csrc_count); } else { fail ("test cannot validate unknown buffer field '%s'", field); } } field = va_arg (var_args, const gchar *); } - va_end (var_args); if (mapped) { gst_rtp_buffer_unmap (&rtp); } } +static void +validate_buffer1 (GstBuffer * buf, const gchar * field, ...) +{ + va_list var_args; + + va_start (var_args, field); + validate_buffer_valist (buf, field, var_args); + va_end (var_args); +} + +static void +validate_buffer (guint index, const gchar * field, ...) +{ + GstBuffer *buf; + va_list var_args; + + fail_if (index >= g_list_length (buffers)); + buf = GST_BUFFER (g_list_nth_data (buffers, index)); + fail_if (buf == NULL); + + GST_TRACE ("%" GST_PTR_FORMAT, buf); + + va_start (var_args, field); + validate_buffer_valist (buf, field, var_args); + va_end (var_args); +} + static void get_buffer_field (guint index, const gchar * field, ...) { @@ -1775,6 +1802,59 @@ GST_START_TEST (rtp_base_payload_property_stats_test) GST_END_TEST; +/* basepayloader has a property source-info that makes it aware of RTP + * source information passed as GstRTPSourceMeta on the input buffers. All + * sources found in the meta will be added to the list of CSRCs in the RTP + * header. A useful scenario for this is, for instance, to signal which + * sources contributed to a mixed audio stream. */ +GST_START_TEST (rtp_base_payload_property_source_info_test) +{ + GstHarness *h; + GstRtpDummyPay *pay; + GstBuffer *buffer; + guint csrc_count = 2; + const guint32 csrc[] = { 0x11, 0x22 }; + const guint32 ssrc = 0x33; + + pay = rtp_dummy_pay_new (); + h = gst_harness_new_with_element (GST_ELEMENT_CAST (pay), "sink", "src"); + gst_harness_set_src_caps_str (h, "application/x-rtp"); + + /* Input buffer has no meta, payloader should not add CSRC */ + g_object_set (pay, "source-info", TRUE, NULL); + buffer = gst_rtp_buffer_new_allocate (0, 0, 0); + buffer = gst_harness_push_and_pull (h, buffer); + validate_buffer1 (buffer, "csrc-count", 0, NULL); + fail_if (gst_buffer_get_rtp_source_meta (buffer)); + gst_buffer_unref (buffer); + + /* Input buffer has meta, payloader should add CSRC */ + buffer = gst_rtp_buffer_new_allocate (0, 0, 0); + fail_unless (gst_buffer_add_rtp_source_meta (buffer, &ssrc, csrc, + csrc_count)); + buffer = gst_harness_push_and_pull (h, buffer); + /* The meta SSRC should be added as the last contributing source */ + validate_buffer1 (buffer, "csrc-count", 3, "csrc", 0, csrc[0], + "csrc", 1, csrc[1], "csrc", 2, ssrc, NULL); + fail_if (gst_buffer_get_rtp_source_meta (buffer)); + gst_buffer_unref (buffer); + + /* When property is disabled, the meta should be ignored and no CSRC + * added. */ + g_object_set (pay, "source-info", FALSE, NULL); + buffer = gst_rtp_buffer_new_allocate (0, 0, 0); + fail_unless (gst_buffer_add_rtp_source_meta (buffer, NULL, csrc, csrc_count)); + buffer = gst_harness_push_and_pull (h, buffer); + validate_buffer1 (buffer, "csrc-count", 0, NULL); + fail_if (gst_buffer_get_rtp_source_meta (buffer)); + gst_buffer_unref (buffer); + + g_object_unref (pay); + gst_harness_teardown (h); +} + +GST_END_TEST; + /* push a single buffer to the payloader which should successfully payload it * into an RTP packet. besides the payloaded RTP packet there should be the * three events initial events: stream-start, caps and segment. because of that @@ -1878,6 +1958,7 @@ rtp_basepayloading_suite (void) tcase_add_test (tc_chain, rtp_base_payload_property_perfect_rtptime_test); tcase_add_test (tc_chain, rtp_base_payload_property_ptime_multiple_test); tcase_add_test (tc_chain, rtp_base_payload_property_stats_test); + tcase_add_test (tc_chain, rtp_base_payload_property_source_info_test); tcase_add_test (tc_chain, rtp_base_payload_framerate_attribute); tcase_add_test (tc_chain, rtp_base_payload_max_framerate_attribute); diff --git a/tests/check/libs/rtpmeta.c b/tests/check/libs/rtpmeta.c new file mode 100644 index 0000000000..312f3916eb --- /dev/null +++ b/tests/check/libs/rtpmeta.c @@ -0,0 +1,110 @@ +/* GStreamer RTP meta unit tests + * Copyright (C) 2016 Stian Selnes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General + * Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +GST_START_TEST (test_rtp_source_meta_set_get_sources) +{ + GstBuffer *buffer; + GstRTPSourceMeta *meta; + guint32 ssrc = 1000, ssrc2 = 2000; + const guint32 csrc[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + }; + + buffer = gst_buffer_new (); + meta = gst_buffer_add_rtp_source_meta (buffer, &ssrc, csrc, 12); + + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 12 + 1); + fail_unless (meta->ssrc_valid); + fail_unless_equals_int (meta->ssrc, ssrc); + for (gint i = 0; i < 12; i++) + fail_unless_equals_int (meta->csrc[i], csrc[i]); + + /* Unset the ssrc */ + fail_unless (gst_rtp_source_meta_set_ssrc (meta, NULL)); + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 12); + fail_if (meta->ssrc_valid); + + /* Set the ssrc again */ + fail_unless (gst_rtp_source_meta_set_ssrc (meta, &ssrc2)); + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 12 + 1); + fail_unless (meta->ssrc_valid); + fail_unless_equals_int (meta->ssrc, ssrc2); + + /* Append multiple csrcs */ + fail_unless (gst_rtp_source_meta_append_csrc (meta, &csrc[12], 2)); + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 14 + 1); + for (gint i = 0; i < 14; i++) + fail_unless_equals_int (meta->csrc[i], csrc[i]); + + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +GST_START_TEST (test_rtp_source_meta_set_get_max_sources) +{ + GstBuffer *buffer; + GstRTPSourceMeta *meta; + guint32 ssrc = 1000; + const guint32 csrc[16] = { 0, }; + + buffer = gst_buffer_new (); + meta = gst_buffer_add_rtp_source_meta (buffer, &ssrc, csrc, 14); + + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 14 + 1); + fail_unless_equals_int (meta->csrc_count, 14); + fail_unless (meta->ssrc_valid); + fail_unless_equals_int (meta->ssrc, ssrc); + + /* Append one more csrc */ + /* The source count should cap at 15 for convenient use with + * gst_rtp_buffer-functions! */ + fail_unless (gst_rtp_source_meta_append_csrc (meta, &csrc[14], 1)); + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 15); + fail_unless_equals_int (meta->csrc_count, 15); + + /* Try to append one more csrc, but we've reached max */ + fail_if (gst_rtp_source_meta_append_csrc (meta, &csrc[15], 1)); + fail_unless_equals_int (gst_rtp_source_meta_get_source_count (meta), 15); + fail_unless_equals_int (meta->csrc_count, 15); + + gst_buffer_unref (buffer); +} + +GST_END_TEST; + +static Suite * +rtp_meta_suite (void) +{ + Suite *s = suite_create ("rtp_meta_tests"); + TCase *tc_chain; + + suite_add_tcase (s, (tc_chain = tcase_create ("GstRTPSourceMeta"))); + tcase_add_test (tc_chain, test_rtp_source_meta_set_get_sources); + tcase_add_test (tc_chain, test_rtp_source_meta_set_get_max_sources); + + return s; +} + +GST_CHECK_MAIN (rtp_meta)