pinfo->header_ext = gst_rtp_buffer_get_extension_bytes (&rtp,
&pinfo->header_ext_bit_pattern);
}
+
+ if (pinfo->ntp64_ext_id != 0 && pinfo->send && !pinfo->have_ntp64_ext) {
+ guint8 *data;
+ guint size;
+
+ /* Remember here that there is a 64-bit NTP header extension on this buffer
+ * or any of the other buffers in the buffer list.
+ * Later we update this after making the buffer(list) writable.
+ */
+ if ((gst_rtp_buffer_get_extension_onebyte_header (&rtp,
+ pinfo->ntp64_ext_id, 0, (gpointer *) & data, &size)
+ && size == 8)
+ || (gst_rtp_buffer_get_extension_twobytes_header (&rtp, NULL,
+ pinfo->ntp64_ext_id, 0, (gpointer *) & data, &size)
+ && size == 8)) {
+ pinfo->have_ntp64_ext = TRUE;
+ }
+ }
+
gst_rtp_buffer_unmap (&rtp);
}
pinfo->payload_len = 0;
pinfo->packets = 0;
pinfo->marker = FALSE;
+ pinfo->ntp64_ext_id = send ? sess->send_ntp64_ext_id : 0;
+ pinfo->have_ntp64_ext = FALSE;
if (is_list) {
GstBufferList *list = GST_BUFFER_LIST_CAST (data);
}
static void
+rtp_session_process_sr_req (RTPSession * sess, guint32 sender_ssrc,
+ guint32 media_ssrc)
+{
+ RTPSource *src;
+
+ /* Request a new SR in feedback profiles ASAP */
+ if (sess->rtp_profile != GST_RTP_PROFILE_AVPF
+ && sess->rtp_profile != GST_RTP_PROFILE_SAVPF)
+ return;
+
+ src = find_source (sess, sender_ssrc);
+ /* Our own RTCP packet */
+ if (src && src->internal)
+ return;
+
+ src = find_source (sess, media_ssrc);
+ /* Not an SSRC we're producing */
+ if (!src || !src->internal)
+ return;
+
+ GST_DEBUG_OBJECT (sess, "Handling RTCP-SR-REQ");
+ /* FIXME: 5s max_delay hard-coded here as we have to give some
+ * high enough value */
+ sess->sr_req_pending = TRUE;
+ rtp_session_send_rtcp (sess, 5 * GST_SECOND);
+}
+
+static void
rtp_session_process_twcc (RTPSession * sess, guint32 sender_ssrc,
guint32 media_ssrc, guint8 * fci_data, guint fci_length)
{
rtp_session_process_nack (sess, sender_ssrc, media_ssrc,
fci_data, fci_length, current_time);
break;
+ case GST_RTCP_RTPFB_TYPE_RTCP_SR_REQ:
+ rtp_session_process_sr_req (sess, sender_ssrc, media_ssrc);
+ break;
case GST_RTCP_RTPFB_TYPE_TWCC:
rtp_session_process_twcc (sess, sender_ssrc, media_ssrc,
fci_data, fci_length);
}
}
+static guint8
+_get_extmap_id_for_attribute (const GstStructure * s, const gchar * ext_name)
+{
+ guint i;
+ guint8 extmap_id = 0;
+ guint n_fields = gst_structure_n_fields (s);
+
+ for (i = 0; i < n_fields; i++) {
+ const gchar *field_name = gst_structure_nth_field_name (s, i);
+ if (g_str_has_prefix (field_name, "extmap-")) {
+ const gchar *str = gst_structure_get_string (s, field_name);
+ if (str && g_strcmp0 (str, ext_name) == 0) {
+ gint64 id = g_ascii_strtoll (field_name + 7, NULL, 10);
+ if (id > 0 && id < 15) {
+ extmap_id = id;
+ break;
+ }
+ }
+ }
+ }
+ return extmap_id;
+}
+
/**
* rtp_session_update_send_caps:
* @sess: an #RTPSession
sess->internal_ssrc_from_caps_or_property = FALSE;
}
+ sess->send_ntp64_ext_id =
+ _get_extmap_id_for_attribute (s,
+ GST_RTP_HDREXT_BASE GST_RTP_HDREXT_NTP_64);
+
rtp_twcc_manager_parse_send_ext_id (sess->twcc, s);
}
+static void
+update_ntp64_header_ext_data (RTPPacketInfo * pinfo, GstBuffer * buffer)
+{
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ if (gst_rtp_buffer_map (buffer, GST_MAP_READWRITE, &rtp)) {
+ guint16 bits;
+ guint8 *data;
+ guint wordlen;
+
+ if (gst_rtp_buffer_get_extension_data (&rtp, &bits, (gpointer *) & data,
+ &wordlen)) {
+ gsize len = wordlen * 4;
+
+ /* One-byte header */
+ if (bits == 0xBEDE) {
+ /* One-byte header extension */
+ while (TRUE) {
+ guint8 ext_id, ext_len;
+
+ if (len < 1)
+ break;
+
+ ext_id = GST_READ_UINT8 (data) >> 4;
+ ext_len = (GST_READ_UINT8 (data) & 0xF) + 1;
+ data += 1;
+ len -= 1;
+ if (ext_id == 0) {
+ /* Skip padding */
+ continue;
+ } else if (ext_id == 15) {
+ /* Stop parsing */
+ break;
+ }
+
+ /* extension doesn't fit into the header */
+ if (ext_len > len)
+ break;
+
+ if (ext_id == pinfo->ntp64_ext_id && ext_len == 8) {
+ if (pinfo->ntpnstime != GST_CLOCK_TIME_NONE) {
+ guint64 ntptime = gst_util_uint64_scale (pinfo->ntpnstime,
+ G_GUINT64_CONSTANT (1) << 32,
+ GST_SECOND);
+
+ GST_WRITE_UINT64_BE (data, ntptime);
+ } else {
+ /* Replace extension with padding */
+ memset (data - 1, 0, 1 + ext_len);
+ }
+ }
+
+ /* skip to the next extension */
+ data += ext_len;
+ len -= ext_len;
+ }
+ } else if ((bits >> 4) == 0x100) {
+ /* Two-byte header extension */
+
+ while (TRUE) {
+ guint8 ext_id, ext_len;
+
+ if (len < 1)
+ break;
+
+ ext_id = GST_READ_UINT8 (data);
+ data += 1;
+ len -= 1;
+ if (ext_id == 0) {
+ /* Skip padding */
+ continue;
+ }
+
+ ext_len = GST_READ_UINT8 (data);
+ data += 1;
+ len -= 1;
+
+ /* extension doesn't fit into the header */
+ if (ext_len > len)
+ break;
+
+ if (ext_id == pinfo->ntp64_ext_id && ext_len == 8) {
+ if (pinfo->ntpnstime != GST_CLOCK_TIME_NONE) {
+ guint64 ntptime = gst_util_uint64_scale (pinfo->ntpnstime,
+ G_GUINT64_CONSTANT (1) << 32,
+ GST_SECOND);
+
+ GST_WRITE_UINT64_BE (data, ntptime);
+ } else {
+ /* Replace extension with padding */
+ memset (data - 2, 0, 2 + ext_len);
+ }
+ }
+
+ /* skip to the next extension */
+ data += ext_len;
+ len -= ext_len;
+ }
+ }
+ }
+ gst_rtp_buffer_unmap (&rtp);
+ }
+}
+
+static void
+update_ntp64_header_ext (RTPPacketInfo * pinfo)
+{
+ /* Early return if we don't know the header extension id or the packets
+ * don't contain the header extension */
+ if (pinfo->ntp64_ext_id == 0 || !pinfo->have_ntp64_ext)
+ return;
+
+ /* If no NTP time is known then the header extension will be replaced with
+ * padding, otherwise it will be updated */
+ GST_TRACE
+ ("Updating NTP-64 header extension for SSRC %08x packet with RTP time %u and running time %"
+ GST_TIME_FORMAT " to %" GST_TIME_FORMAT, pinfo->ssrc, pinfo->rtptime,
+ GST_TIME_ARGS (pinfo->running_time), GST_TIME_ARGS (pinfo->ntpnstime));
+
+ if (GST_IS_BUFFER_LIST (pinfo->data)) {
+ GstBufferList *list;
+ guint i = 0;
+
+ pinfo->data = gst_buffer_list_make_writable (pinfo->data);
+
+ list = GST_BUFFER_LIST (pinfo->data);
+
+ for (i = 0; i < gst_buffer_list_length (list); i++) {
+ GstBuffer *buffer = gst_buffer_list_get_writable (list, i);
+
+ update_ntp64_header_ext_data (pinfo, buffer);
+ }
+ } else {
+ pinfo->data = gst_buffer_make_writable (pinfo->data);
+ update_ntp64_header_ext_data (pinfo, pinfo->data);
+ }
+}
+
/**
* rtp_session_send_rtp:
* @sess: an #RTPSession
*/
GstFlowReturn
rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list,
- GstClockTime current_time, GstClockTime running_time)
+ GstClockTime current_time, GstClockTime running_time, guint64 ntpnstime)
{
GstFlowReturn result;
RTPSource *source;
RTP_SESSION_LOCK (sess);
if (!update_packet_info (sess, &pinfo, TRUE, TRUE, is_list, data,
- current_time, running_time, -1))
+ current_time, running_time, ntpnstime))
goto invalid_packet;
+ /* Update any 64-bit NTP header extensions with the actual NTP time here */
+ update_ntp64_header_ext (&pinfo);
rtp_twcc_manager_send_packet (sess->twcc, &pinfo);
source = obtain_internal_source (sess, pinfo.ssrc, &created, current_time);
gst_rtcp_buffer_map (data->rtcp, GST_MAP_READWRITE, rtcp);
- if (data->is_early && sess->reduced_size_rtcp)
- return;
-
- if (RTP_SOURCE_IS_SENDER (own)) {
+ if (RTP_SOURCE_IS_SENDER (own) && (!data->is_early || !sess->reduced_size_rtcp
+ || sess->sr_req_pending)) {
guint64 ntptime;
guint32 rtptime;
guint32 packet_count, octet_count;
+ sess->sr_req_pending = FALSE;
+
/* we are a sender, create SR */
GST_DEBUG ("create SR for SSRC %08x", own->ssrc);
gst_rtcp_buffer_add_packet (rtcp, GST_RTCP_TYPE_SR, packet);
sess->timestamp_sender_reports ? ntptime : 0,
sess->timestamp_sender_reports ? rtptime : 0,
packet_count, octet_count);
- } else {
+ } else if (!data->is_early || !sess->reduced_size_rtcp) {
/* we are only receiver, create RR */
GST_DEBUG ("create RR for SSRC %08x", own->ssrc);
gst_rtcp_buffer_add_packet (rtcp, GST_RTCP_TYPE_RR, packet);
return;
}
+ GST_DEBUG ("generating TWCC feedback for source %08x", source->ssrc);
+
while ((buf = rtp_twcc_manager_get_feedback (sess->twcc, source->ssrc))) {
ReportOutput *output = g_slice_new (ReportOutput);
output->source = g_object_ref (source);
RTPSession *sess = data->sess;
gboolean is_bye = FALSE;
ReportOutput *output;
+ gboolean sr_req_pending = sess->sr_req_pending;
/* only generate RTCP for active internal sources */
if (!source->internal || source->sent_bye)
g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
(GHFunc) session_report_blocks, data);
}
- if (!data->has_sdes && (!data->is_early || !sess->reduced_size_rtcp))
+ if (!data->has_sdes && (!data->is_early || !sess->reduced_size_rtcp
+ || sr_req_pending))
session_sdes (sess, data);
if (data->have_fir)