/**
* SECTION:element-rtprtxsend
+ * @title: rtprtxsend
*
* See #GstRtpRtxReceive for examples
- *
+ *
* The purpose of the sender RTX object is to keep a history of RTP packets up
* to a configurable limit (max-size-time or max-size-packets). It will listen
* for upstream custom retransmission events (GstRTPRetransmissionRequest) that
PROP_MAX_SIZE_TIME,
PROP_MAX_SIZE_PACKETS,
PROP_NUM_RTX_REQUESTS,
- PROP_NUM_RTX_PACKETS
+ PROP_NUM_RTX_PACKETS,
+ PROP_CLOCK_RATE_MAP,
};
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp, " "clock-rate = (int) [1, MAX]")
+ GST_STATIC_CAPS ("application/x-rtp")
);
static gboolean gst_rtp_rtx_send_queue_check_full (GstDataQueue * queue,
" Number of retransmission packets sent", 0, G_MAXUINT,
0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&src_factory));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&sink_factory));
+ g_object_class_install_property (gobject_class, PROP_CLOCK_RATE_MAP,
+ g_param_spec_boxed ("clock-rate-map", "Clock Rate Map",
+ "Map of payload types to their clock rates",
+ GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
+ gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
gst_element_class_set_static_metadata (gstelement_class,
"RTP Retransmission Sender", "Codec",
g_hash_table_unref (rtx->rtx_pt_map);
if (rtx->rtx_pt_map_structure)
gst_structure_free (rtx->rtx_pt_map_structure);
+ g_hash_table_unref (rtx->clock_rate_map);
+ if (rtx->clock_rate_map_structure)
+ gst_structure_free (rtx->clock_rate_map_structure);
g_object_unref (rtx->queue);
G_OBJECT_CLASS (gst_rtp_rtx_send_parent_class)->finalize (object);
NULL, (GDestroyNotify) ssrc_rtx_data_free);
rtx->rtx_ssrcs = g_hash_table_new (g_direct_hash, g_direct_equal);
rtx->rtx_pt_map = g_hash_table_new (g_direct_hash, g_direct_equal);
+ rtx->clock_rate_map = g_hash_table_new (g_direct_hash, g_direct_equal);
rtx->max_size_time = DEFAULT_MAX_SIZE_TIME;
rtx->max_size_packets = DEFAULT_MAX_SIZE_PACKETS;
fmtp = GPOINTER_TO_UINT (g_hash_table_lookup (rtx->rtx_pt_map,
GUINT_TO_POINTER (gst_rtp_buffer_get_payload_type (&rtp))));
- GST_DEBUG_OBJECT (rtx,
- "retransmit seqnum: %" G_GUINT16_FORMAT ", ssrc: %" G_GUINT32_FORMAT,
+ GST_DEBUG_OBJECT (rtx, "creating rtx buffer, orig seqnum: %u, "
+ "rtx seqnum: %u, rtx ssrc: %X", gst_rtp_buffer_get_seq (&rtp),
seqnum, ssrc);
/* gst_rtp_buffer_map does not map the payload so do it now */
/* copy extension if any */
if (rtp.size[1]) {
- mem = gst_memory_copy (rtp.map[1].memory, 0, rtp.size[1]);
+ mem = gst_allocator_alloc (NULL, rtp.size[1], NULL);
+ gst_memory_map (mem, &map, GST_MAP_WRITE);
+ memcpy (map.data, rtp.data[1], rtp.size[1]);
+ gst_memory_unmap (mem, &map);
gst_buffer_append_memory (new_buffer, mem);
}
if (!gst_structure_get_uint (s, "ssrc", &ssrc))
ssrc = -1;
- GST_DEBUG_OBJECT (rtx,
- "request seqnum: %" G_GUINT32_FORMAT ", ssrc: %" G_GUINT32_FORMAT,
+ GST_DEBUG_OBJECT (rtx, "got rtx request for seqnum: %u, ssrc: %X",
seqnum, ssrc);
GST_OBJECT_LOCK (rtx);
(GCompareDataFunc) buffer_queue_items_cmp, NULL);
if (iter) {
BufferQueueItem *item = g_sequence_get (iter);
- GST_DEBUG_OBJECT (rtx, "found %" G_GUINT16_FORMAT, item->seqnum);
+ GST_LOG_OBJECT (rtx, "found %u", item->seqnum);
rtx_buf = gst_rtp_rtx_buffer_new (rtx, item->buffer);
}
+#ifndef GST_DISABLE_DEBUG
+ else {
+ BufferQueueItem *item = NULL;
+
+ iter = g_sequence_get_begin_iter (data->queue);
+ if (!g_sequence_iter_is_end (iter))
+ item = g_sequence_get (iter);
+
+ if (item && seqnum < item->seqnum) {
+ GST_DEBUG_OBJECT (rtx, "requested seqnum %u has already been "
+ "removed from the rtx queue; the first available is %u",
+ seqnum, item->seqnum);
+ } else {
+ GST_WARNING_OBJECT (rtx, "requested seqnum %u has not been "
+ "transmitted yet in the original stream; either the remote end "
+ "is not configured correctly, or the source is too slow",
+ seqnum);
+ }
+ }
+#endif
}
GST_OBJECT_UNLOCK (rtx);
if (!gst_structure_get_uint (s, "ssrc", &ssrc))
ssrc = -1;
- GST_DEBUG_OBJECT (rtx, "collision ssrc: %" G_GUINT32_FORMAT, ssrc);
+ GST_DEBUG_OBJECT (rtx, "got ssrc collision, ssrc: %X", ssrc);
GST_OBJECT_LOCK (rtx);
- /* choose another ssrc for our retransmited stream */
+ /* choose another ssrc for our retransmitted stream */
if (g_hash_table_contains (rtx->rtx_ssrcs, GUINT_TO_POINTER (ssrc))) {
guint master_ssrc;
SSRCRtxData *data;
if (!gst_structure_get_int (s, "payload", &payload))
payload = -1;
+ if (payload == -1 || ssrc == G_MAXUINT)
+ break;
+
if (payload == -1)
GST_WARNING_OBJECT (rtx, "No payload in caps");
GST_WARNING_OBJECT (rtx, "Payload %d not in rtx-pt-map", payload);
GST_DEBUG_OBJECT (rtx,
- "got caps for payload: %d->%d, ssrc: %u->%d: %" GST_PTR_FORMAT,
+ "got caps for payload: %d->%d, ssrc: %u->%u : %" GST_PTR_FORMAT,
payload, GPOINTER_TO_INT (rtx_payload), ssrc, data->rtx_ssrc, caps);
gst_structure_get_int (s, "clock-rate", &data->clock_rate);
rtptime = gst_rtp_buffer_get_timestamp (&rtp);
gst_rtp_buffer_unmap (&rtp);
- GST_LOG_OBJECT (rtx,
- "Processing buffer seqnum: %" G_GUINT16_FORMAT ", ssrc: %"
- G_GUINT32_FORMAT, seqnum, ssrc);
+ GST_TRACE_OBJECT (rtx, "Processing buffer seqnum: %u, ssrc: %X", seqnum,
+ ssrc);
/* do not store the buffer if it's payload type is unknown */
if (g_hash_table_contains (rtx->rtx_pt_map, GUINT_TO_POINTER (payload_type))) {
data = gst_rtp_rtx_send_get_ssrc_data (rtx, ssrc);
+ if (data->clock_rate == 0 && rtx->clock_rate_map_structure) {
+ data->clock_rate =
+ GPOINTER_TO_INT (g_hash_table_lookup (rtx->clock_rate_map,
+ GUINT_TO_POINTER (payload_type)));
+ }
+
/* add current rtp buffer to queue history */
item = g_slice_new0 (BufferQueueItem);
item->seqnum = seqnum;
GST_LOG_OBJECT (rtx, "pushing rtx buffer %p", data->object);
if (G_LIKELY (GST_IS_BUFFER (data->object))) {
- gst_pad_push (rtx->srcpad, GST_BUFFER (data->object));
-
GST_OBJECT_LOCK (rtx);
+ /* Update statistics just before pushing. */
rtx->num_rtx_packets++;
GST_OBJECT_UNLOCK (rtx);
+
+ gst_pad_push (rtx->srcpad, GST_BUFFER (data->object));
} else if (GST_IS_EVENT (data->object)) {
gst_pad_push_event (rtx->srcpad, GST_EVENT (data->object));
g_value_set_uint (value, rtx->num_rtx_packets);
GST_OBJECT_UNLOCK (rtx);
break;
+ case PROP_CLOCK_RATE_MAP:
+ GST_OBJECT_LOCK (rtx);
+ g_value_set_boxed (value, rtx->clock_rate_map_structure);
+ GST_OBJECT_UNLOCK (rtx);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
rtx->max_size_packets = g_value_get_uint (value);
GST_OBJECT_UNLOCK (rtx);
break;
+ case PROP_CLOCK_RATE_MAP:
+ GST_OBJECT_LOCK (rtx);
+ if (rtx->clock_rate_map_structure)
+ gst_structure_free (rtx->clock_rate_map_structure);
+ rtx->clock_rate_map_structure = g_value_dup_boxed (value);
+ g_hash_table_remove_all (rtx->clock_rate_map);
+ gst_structure_foreach (rtx->clock_rate_map_structure,
+ structure_to_hash_table, rtx->clock_rate_map);
+ GST_OBJECT_UNLOCK (rtx);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;