1 /* RTP Retransmission receiver element for GStreamer
5 * Copyright (C) 2013 Collabora Ltd.
6 * @author Julien Isorce <julien.isorce@collabora.co.uk>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 * SECTION:element-rtprtxreceive
26 * @see_also: rtprtxsend, rtpsession, rtpjitterbuffer
28 * The receiver will listen to the custom retransmission events from the
29 * downstream jitterbuffer and will remember the SSRC1 of the stream and
30 * seqnum that was requested. When it sees a packet with one of the stored
31 * seqnum, it associates the SSRC2 of the stream with the SSRC1 of the
32 * master stream. From then it knows that SSRC2 is the retransmission
33 * stream of SSRC1. This algorithm is stated in RFC 4588. For this
34 * algorithm to work, RFC4588 also states that no two pending retransmission
35 * requests can exist for the same seqnum and different SSRCs or else it
36 * would be impossible to associate the retransmission with the original
38 * When the RTX receiver has associated the retransmission packets,
39 * it can depayload and forward them to the source pad of the element.
40 * RTX is SSRC-multiplexed. See #GstRtpRtxSend
43 * <title>Example pipelines</title>
45 * gst-launch-1.0 rtpsession name=rtpsession \
46 * audiotestsrc ! speexenc ! rtpspeexpay pt=97 ! rtprtxsend rtx-payload-type=99 ! \
47 * identity drop-probability=0.1 ! rtpsession.send_rtp_sink \
48 * rtpsession.send_rtp_src ! udpsink host="127.0.0.1" port=5000 \
49 * udpsrc port=5001 ! rtpsession.recv_rtcp_sink \
50 * rtpsession.send_rtcp_src ! udpsink host="127.0.0.1" port=5002 sync=false async=false
51 * ]| Send audio stream through port 5000. (5001 and 5002 are just the rtcp link with the receiver)
53 * gst-launch-1.0 rtpsession name=rtpsession \
54 * udpsrc port=5000 caps="application/x-rtp,media=(string)audio,clock-rate=(int)44100,encoding-name=(string)SPEEX,encoding-params=(string)1,octet-align=(string)1" ! \
55 * rtpsession.recv_rtp_sink \
56 * rtpsession.recv_rtp_src ! rtprtxreceive rtx-payload-types="99" ! rtpjitterbuffer do-retransmission=true ! rtpspeexdepay ! \
57 * speexdec ! audioconvert ! autoaudiosink \
58 * rtpsession.send_rtcp_src ! udpsink host="127.0.0.1" port=5001 \
59 * udpsrc port=5002 ! rtpsession.recv_rtcp_sink sync=fakse async=false
60 * ]| Receive audio stream from port 5000. (5001 and 5002 are just the rtcp link with the sender)
61 * On sender side make sure to use a different payload type for the stream and
62 * its associated retransmission stream (see #GstRtpRtxSend). Note that several retransmission streams can
63 * have the same payload type so this is not deterministic. Actually the
64 * rtprtxreceiver element does the association using seqnum values.
65 * On receiver side set all the retransmission payload types (Those informations are retrieve
67 * You should still hear a clear sound when setting drop-probability to something greater than 0.
68 * The rtpjitterbuffer will generate a custom upstream event GstRTPRetransmissionRequest when
69 * it assumes that one packet is missing. Then this request is translated to a FB NACK in the rtcp link
70 * Finally the rtpsession of the sender side re-convert it in a GstRTPRetransmissionRequest that will
71 * be handle by rtprtxsend.
72 * When increasing this value it may be possible that even the retransmission stream would be dropped
73 * so the receiver will ask to resend the packets again and again until it actually receive them.
74 * If the value is too high the rtprtxsend will not be able to retrieve the packet in its list of
75 * stored packets. For learning purpose you could try to increase the max-size-packets or max-size-time
76 * rtprtxsender's properties.
77 * Also note that you should use rtprtxsend through rtpbin and its set-aux-send property. See #GstRtpBin.
79 * gst-launch-1.0 rtpsession name=rtpsession0 \
80 * audiotestsrc wave=0 ! speexenc ! rtpspeexpay pt=97 ! rtprtxsend rtx-payload-type=99 seqnum-offset=1 ! \
81 * identity drop-probability=0.1 ! rtpsession0.send_rtp_sink \
82 * rtpsession0.send_rtp_src ! udpsink host="127.0.0.1" port=5000 \
83 * udpsrc port=5001 ! rtpsession0.recv_rtcp_sink \
84 * rtpsession0.send_rtcp_src ! udpsink host="127.0.0.1" port=5002 sync=false async=false \
85 * rtpsession name=rtpsession1 \
86 * audiotestsrc wave=0 ! speexenc ! rtpspeexpay pt=97 ! rtprtxsend rtx-payload-type=99 seqnum-offset=10 ! \
87 * identity drop-probability=0.1 ! rtpsession1.send_rtp_sink \
88 * rtpsession1.send_rtp_src ! udpsink host="127.0.0.1" port=5000 \
89 * udpsrc port=5004 ! rtpsession1.recv_rtcp_sink \
90 * rtpsession1.send_rtcp_src ! udpsink host="127.0.0.1" port=5002 sync=false async=false
91 * ]| Send two audio streams to port 5000.
93 * gst-launch-1.0 rtpsession name=rtpsession
94 * udpsrc port=5000 caps="application/x-rtp,media=(string)audio,clock-rate=(int)44100,encoding-name=(string)SPEEX,encoding-params=(string)1,octet-align=(string)1" ! \
95 * rtpsession.recv_rtp_sink \
96 * rtpsession.recv_rtp_src ! rtprtxreceive rtx-payload-types="99" ! rtpssrcdemux name=demux \
97 * demux. ! queue ! rtpjitterbuffer do-retransmission=true ! rtpspeexdepay ! speexdec ! audioconvert ! autoaudiosink \
98 * demux. ! queue ! rtpjitterbuffer do-retransmission=true ! rtpspeexdepay ! speexdec ! audioconvert ! autoaudiosink \
99 * rtpsession.send_rtcp_src ! ! tee name=t ! queue ! udpsink host="127.0.0.1" port=5001 t. ! queue ! udpsink host="127.0.0.1" port=5004 \
100 * udpsrc port=5002 ! rtpsession.recv_rtcp_sink sync=fakse async=false
101 * ]| Receive audio stream from port 5000.
102 * On sender side the two streams have the same payload type for master streams, Same about retransmission streams.
103 * The streams are sent to the network through two distincts sessions.
104 * But we need to set a different seqnum-offset to make sure their seqnum navigate at a different rate like in concrete cases.
105 * We could also choose the same seqnum offset but we would require to set a different initial seqnum value.
106 * This is also why the rtprtxreceive can succeed to do the association between master and retransmission stream.
107 * On receiver side the same session is used to receive the two streams. So the rtpssrcdemux is here to demultiplex
108 * those two streams. The rtprtxreceive is responsible for reconstructing the original packets from the two retransmission streams.
109 * You can play with the drop-probability value for one or both streams.
110 * You should hear a clear sound. (after a few seconds the two streams wave feel synchronized)
113 * Last reviewed on 2013-11-08 (1.x)
121 #include <gst/rtp/gstrtpbuffer.h>
125 #include "gstrtprtxreceive.h"
127 GST_DEBUG_CATEGORY_STATIC (gst_rtp_rtx_receive_debug);
128 #define GST_CAT_DEFAULT gst_rtp_rtx_receive_debug
133 PROP_PAYLOAD_TYPE_MAP,
134 PROP_NUM_RTX_REQUESTS,
135 PROP_NUM_RTX_PACKETS,
136 PROP_NUM_RTX_ASSOC_PACKETS,
140 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
143 GST_STATIC_CAPS ("application/x-rtp")
146 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
149 GST_STATIC_CAPS ("application/x-rtp")
152 static gboolean gst_rtp_rtx_receive_src_event (GstPad * pad, GstObject * parent,
154 static GstFlowReturn gst_rtp_rtx_receive_chain (GstPad * pad,
155 GstObject * parent, GstBuffer * buffer);
157 static GstStateChangeReturn gst_rtp_rtx_receive_change_state (GstElement *
158 element, GstStateChange transition);
160 static void gst_rtp_rtx_receive_set_property (GObject * object, guint prop_id,
161 const GValue * value, GParamSpec * pspec);
162 static void gst_rtp_rtx_receive_get_property (GObject * object, guint prop_id,
163 GValue * value, GParamSpec * pspec);
164 static void gst_rtp_rtx_receive_finalize (GObject * object);
166 G_DEFINE_TYPE (GstRtpRtxReceive, gst_rtp_rtx_receive, GST_TYPE_ELEMENT);
169 gst_rtp_rtx_receive_class_init (GstRtpRtxReceiveClass * klass)
171 GObjectClass *gobject_class;
172 GstElementClass *gstelement_class;
174 gobject_class = (GObjectClass *) klass;
175 gstelement_class = (GstElementClass *) klass;
177 gobject_class->get_property = gst_rtp_rtx_receive_get_property;
178 gobject_class->set_property = gst_rtp_rtx_receive_set_property;
179 gobject_class->finalize = gst_rtp_rtx_receive_finalize;
181 g_object_class_install_property (gobject_class, PROP_PAYLOAD_TYPE_MAP,
182 g_param_spec_boxed ("payload-type-map", "Payload Type Map",
183 "Map of original payload types to their retransmission payload types",
184 GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
186 g_object_class_install_property (gobject_class, PROP_NUM_RTX_REQUESTS,
187 g_param_spec_uint ("num-rtx-requests", "Num RTX Requests",
188 "Number of retransmission events received", 0, G_MAXUINT,
189 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
191 g_object_class_install_property (gobject_class, PROP_NUM_RTX_PACKETS,
192 g_param_spec_uint ("num-rtx-packets", "Num RTX Packets",
193 " Number of retransmission packets received", 0, G_MAXUINT,
194 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
196 g_object_class_install_property (gobject_class, PROP_NUM_RTX_ASSOC_PACKETS,
197 g_param_spec_uint ("num-rtx-assoc-packets",
198 "Num RTX Associated Packets", "Number of retransmission packets "
199 "correctly associated with retransmission requests", 0, G_MAXUINT,
200 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
202 gst_element_class_add_pad_template (gstelement_class,
203 gst_static_pad_template_get (&src_factory));
204 gst_element_class_add_pad_template (gstelement_class,
205 gst_static_pad_template_get (&sink_factory));
207 gst_element_class_set_static_metadata (gstelement_class,
208 "RTP Retransmission receiver", "Codec",
209 "Receive retransmitted RTP packets according to RFC4588",
210 "Julien Isorce <julien.isorce@collabora.co.uk>");
212 gstelement_class->change_state =
213 GST_DEBUG_FUNCPTR (gst_rtp_rtx_receive_change_state);
217 gst_rtp_rtx_receive_reset (GstRtpRtxReceive * rtx)
219 g_mutex_lock (&rtx->lock);
220 g_hash_table_remove_all (rtx->ssrc2_ssrc1_map);
221 g_hash_table_remove_all (rtx->seqnum_ssrc1_map);
222 rtx->num_rtx_requests = 0;
223 rtx->num_rtx_packets = 0;
224 rtx->num_rtx_assoc_packets = 0;
225 g_mutex_unlock (&rtx->lock);
229 gst_rtp_rtx_receive_finalize (GObject * object)
231 GstRtpRtxReceive *rtx = GST_RTP_RTX_RECEIVE (object);
233 gst_rtp_rtx_receive_reset (rtx);
235 if (rtx->ssrc2_ssrc1_map) {
236 g_hash_table_destroy (rtx->ssrc2_ssrc1_map);
237 rtx->ssrc2_ssrc1_map = NULL;
240 if (rtx->seqnum_ssrc1_map) {
241 g_hash_table_destroy (rtx->seqnum_ssrc1_map);
242 rtx->seqnum_ssrc1_map = NULL;
245 g_hash_table_unref (rtx->rtx_pt_map);
246 if (rtx->pending_rtx_pt_map)
247 gst_structure_free (rtx->pending_rtx_pt_map);
249 g_mutex_clear (&rtx->lock);
251 G_OBJECT_CLASS (gst_rtp_rtx_receive_parent_class)->finalize (object);
255 gst_rtp_rtx_receive_init (GstRtpRtxReceive * rtx)
257 GstElementClass *klass = GST_ELEMENT_GET_CLASS (rtx);
260 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
262 GST_PAD_SET_PROXY_CAPS (rtx->srcpad);
263 GST_PAD_SET_PROXY_ALLOCATION (rtx->srcpad);
264 gst_pad_set_event_function (rtx->srcpad,
265 GST_DEBUG_FUNCPTR (gst_rtp_rtx_receive_src_event));
266 gst_element_add_pad (GST_ELEMENT (rtx), rtx->srcpad);
269 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
271 GST_PAD_SET_PROXY_CAPS (rtx->sinkpad);
272 GST_PAD_SET_PROXY_ALLOCATION (rtx->sinkpad);
273 gst_pad_set_chain_function (rtx->sinkpad,
274 GST_DEBUG_FUNCPTR (gst_rtp_rtx_receive_chain));
275 gst_element_add_pad (GST_ELEMENT (rtx), rtx->sinkpad);
277 rtx->ssrc2_ssrc1_map = g_hash_table_new (g_direct_hash, g_direct_equal);
278 rtx->seqnum_ssrc1_map = g_hash_table_new (g_direct_hash, g_direct_equal);
280 rtx->rtx_pt_map = g_hash_table_new (g_direct_hash, g_direct_equal);
281 rtx->rtx_pt_map_changed = FALSE;
283 g_mutex_init (&rtx->lock);
287 gst_rtp_rtx_receive_src_event (GstPad * pad, GstObject * parent,
290 GstRtpRtxReceive *rtx = GST_RTP_RTX_RECEIVE (parent);
293 switch (GST_EVENT_TYPE (event)) {
294 case GST_EVENT_CUSTOM_UPSTREAM:
296 const GstStructure *s = gst_event_get_structure (event);
298 /* This event usually comes from the downstream gstrtpjitterbuffer */
299 if (gst_structure_has_name (s, "GstRTPRetransmissionRequest")) {
305 /* retrieve seqnum of the packet that need to be restransmisted */
306 if (!gst_structure_get_uint (s, "seqnum", &seqnum))
309 /* retrieve ssrc of the packet that need to be restransmisted
310 * it's usefull when reconstructing the original packet from the rtx packet */
311 if (!gst_structure_get_uint (s, "ssrc", &ssrc))
314 GST_DEBUG_OBJECT (rtx,
315 "request seqnum: %" G_GUINT16_FORMAT ", ssrc: %" G_GUINT32_FORMAT,
318 g_mutex_lock (&rtx->lock);
320 /* increase number of seen requests for our statistics */
321 ++rtx->num_rtx_requests;
323 /* First, we lookup in our map to see if we have already associate this
324 * master stream ssrc with its retransmisted stream.
325 * Every ssrc are unique so we can use the same hash table
326 * for both retrieving the ssrc1 from ssrc2 and also ssrc2 from ssrc1
328 if (g_hash_table_lookup_extended (rtx->ssrc2_ssrc1_map,
329 GUINT_TO_POINTER (ssrc), NULL, &ssrc2)
330 && GPOINTER_TO_UINT (ssrc2) != GPOINTER_TO_UINT (ssrc)) {
331 GST_DEBUG ("Retransmited stream %" G_GUINT32_FORMAT
332 " already associated to its master", GPOINTER_TO_UINT (ssrc2));
334 /* not already associated but also we have to check that we have not
335 * already considered this request.
337 if (g_hash_table_lookup_extended (rtx->seqnum_ssrc1_map,
338 GUINT_TO_POINTER (seqnum), NULL, &ssrc1)) {
339 if (GPOINTER_TO_UINT (ssrc1) == ssrc) {
340 /* do nothing because we have already considered this request
341 * The jitter may be too impatient of the rtx packet has been
343 * It does not mean we reject the event, we still want to forward
344 * the request to the gstrtpsession to be translater into a FB NACK
346 GST_DEBUG ("Duplicated request seqnum: %" G_GUINT16_FORMAT
347 ", ssrc1: %" G_GUINT32_FORMAT, seqnum, ssrc);
350 * the receiver MUST NOT have two outstanding requests for the
351 * same packet sequence number in two different original streams
352 * before the association is resolved. Otherwise it's impossible
353 * to associate a rtx stream and its master stream
355 GST_DEBUG ("reject request for seqnum %" G_GUINT16_FORMAT
356 "of master stream %" G_GUINT32_FORMAT, seqnum, ssrc);
359 /* remove seqnum in order to reuse the spot */
360 g_hash_table_remove (rtx->seqnum_ssrc1_map,
361 GUINT_TO_POINTER (seqnum));
363 /* do not forward the event as we are rejecting this request */
364 g_mutex_unlock (&rtx->lock);
365 gst_event_unref (event);
369 /* the request has not been already considered
370 * insert it for the first time */
372 ("packet number %" G_GUINT16_FORMAT " of master stream %"
373 G_GUINT32_FORMAT " needs to be retransmited", seqnum, ssrc);
374 g_hash_table_insert (rtx->seqnum_ssrc1_map,
375 GUINT_TO_POINTER (seqnum), GUINT_TO_POINTER (ssrc));
379 g_mutex_unlock (&rtx->lock);
381 /* Transfer event upstream so that the request can acutally by translated
382 * through gstrtpsession through the network */
383 res = gst_pad_event_default (pad, parent, event);
387 res = gst_pad_event_default (pad, parent, event);
394 structure_to_hash_table_inv (GQuark field_id, const GValue * value,
397 const gchar *field_str;
401 field_str = g_quark_to_string (field_id);
402 field_uint = atoi (field_str);
403 value_uint = g_value_get_uint (value);
404 g_hash_table_insert ((GHashTable *) hash, GUINT_TO_POINTER (value_uint),
405 GUINT_TO_POINTER (field_uint));
410 /* Copy fixed header and extension. Replace current ssrc by ssrc1,
411 * remove OSN and replace current seq num by OSN.
412 * Copy memory to avoid to manually copy each rtp buffer field.
415 _gst_rtp_buffer_new_from_rtx (GstRTPBuffer * rtp, guint32 ssrc1,
416 guint16 orign_seqnum, guint8 origin_payload_type)
418 GstMemory *mem = NULL;
419 GstRTPBuffer new_rtp = GST_RTP_BUFFER_INIT;
420 GstBuffer *new_buffer = gst_buffer_new ();
422 guint payload_len = 0;
424 /* copy fixed header */
425 mem = gst_memory_copy (rtp->map[0].memory, 0, rtp->size[0]);
426 gst_buffer_append_memory (new_buffer, mem);
428 /* copy extension if any */
430 mem = gst_memory_copy (rtp->map[1].memory, 0, rtp->size[1]);
431 gst_buffer_append_memory (new_buffer, mem);
434 /* copy payload and remove OSN */
435 payload_len = rtp->size[2] - 2;
436 mem = gst_allocator_alloc (NULL, payload_len, NULL);
438 gst_memory_map (mem, &map, GST_MAP_WRITE);
440 memcpy (map.data, (guint8 *) rtp->data[2] + 2, payload_len);
441 gst_memory_unmap (mem, &map);
442 gst_buffer_append_memory (new_buffer, mem);
444 /* the sender always constructs rtx packets without padding,
445 * But the receiver can still receive rtx packets with padding.
449 guint pad_len = rtp->size[3];
451 mem = gst_allocator_alloc (NULL, pad_len, NULL);
453 gst_memory_map (mem, &map, GST_MAP_WRITE);
454 map.data[pad_len - 1] = pad_len;
455 gst_memory_unmap (mem, &map);
457 gst_buffer_append_memory (new_buffer, mem);
460 /* set ssrc and seq num */
461 gst_rtp_buffer_map (new_buffer, GST_MAP_WRITE, &new_rtp);
462 gst_rtp_buffer_set_ssrc (&new_rtp, ssrc1);
463 gst_rtp_buffer_set_seq (&new_rtp, orign_seqnum);
464 gst_rtp_buffer_set_payload_type (&new_rtp, origin_payload_type);
465 gst_rtp_buffer_unmap (&new_rtp);
471 gst_rtp_rtx_receive_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
473 GstRtpRtxReceive *rtx = GST_RTP_RTX_RECEIVE (parent);
474 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
475 GstFlowReturn ret = GST_FLOW_OK;
476 GstBuffer *new_buffer = NULL;
481 guint16 orign_seqnum = 0;
482 guint8 payload_type = 0;
483 guint8 origin_payload_type = 0;
484 gboolean is_rtx = FALSE;
485 gboolean drop = FALSE;
487 /* map current rtp packet to parse its header */
488 gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
489 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
490 seqnum = gst_rtp_buffer_get_seq (&rtp);
491 payload_type = gst_rtp_buffer_get_payload_type (&rtp);
493 /* check if we have a retransmission packet (this information comes from SDP) */
494 g_mutex_lock (&rtx->lock);
496 /* transfer payload type while holding the lock */
497 if (rtx->rtx_pt_map_changed) {
498 g_hash_table_remove_all (rtx->rtx_pt_map);
499 gst_structure_foreach (rtx->pending_rtx_pt_map, structure_to_hash_table_inv,
501 rtx->rtx_pt_map_changed = FALSE;
505 g_hash_table_lookup_extended (rtx->rtx_pt_map,
506 GUINT_TO_POINTER (payload_type), NULL, NULL);
508 g_mutex_unlock (&rtx->lock);
511 /* read OSN in the rtx payload */
512 orign_seqnum = GST_READ_UINT16_BE (gst_rtp_buffer_get_payload (&rtp));
513 origin_payload_type =
514 GPOINTER_TO_UINT (g_hash_table_lookup (rtx->rtx_pt_map,
515 GUINT_TO_POINTER (payload_type)));
518 g_mutex_lock (&rtx->lock);
520 /* if the current packet is from a retransmission stream */
522 /* increase our statistic */
523 ++rtx->num_rtx_packets;
525 /* first we check if we already have associated this retransmission stream
526 * to a master stream */
527 if (g_hash_table_lookup_extended (rtx->ssrc2_ssrc1_map,
528 GUINT_TO_POINTER (ssrc), NULL, &ssrc1)) {
530 ("packet is from retransmission stream %" G_GUINT32_FORMAT
531 " already associated to master stream %" G_GUINT32_FORMAT, ssrc,
532 GPOINTER_TO_UINT (ssrc1));
535 /* the current retransmisted packet has its rtx stream not already
536 * associated to a master stream, so retrieve it from our request
538 if (g_hash_table_lookup_extended (rtx->seqnum_ssrc1_map,
539 GUINT_TO_POINTER (orign_seqnum), NULL, &ssrc1)) {
541 ("associate retransmisted stream %" G_GUINT32_FORMAT
542 " to master stream %" G_GUINT32_FORMAT " thanks to packet %"
543 G_GUINT16_FORMAT "", ssrc, GPOINTER_TO_UINT (ssrc1), orign_seqnum);
546 /* free the spot so that this seqnum can be used to do another
548 g_hash_table_remove (rtx->seqnum_ssrc1_map,
549 GUINT_TO_POINTER (orign_seqnum));
551 /* actually do the association between rtx stream and master stream */
552 g_hash_table_insert (rtx->ssrc2_ssrc1_map, GUINT_TO_POINTER (ssrc2),
555 /* just put a guard */
556 if (GPOINTER_TO_UINT (ssrc1) == ssrc2)
558 ("RTX receiver ssrc2_ssrc1_map bad state, ssrc %" G_GUINT32_FORMAT
559 " are the same\n", ssrc);
561 /* also do the association between master stream and rtx stream
562 * every ssrc are unique so we can use the same hash table
563 * for both retrieving the ssrc1 from ssrc2 and also ssrc2 from ssrc1
565 g_hash_table_insert (rtx->ssrc2_ssrc1_map, ssrc1,
566 GUINT_TO_POINTER (ssrc2));
568 /* we are not able to associate this rtx packet with a master stream */
570 ("drop rtx packet because its orign_seqnum %" G_GUINT16_FORMAT
571 " is not in pending retransmission requests", orign_seqnum);
577 /* if not dropped the packet was successfully associated */
579 ++rtx->num_rtx_assoc_packets;
581 g_mutex_unlock (&rtx->lock);
583 /* just drop the packet if the association could not have been made */
585 gst_rtp_buffer_unmap (&rtp);
586 gst_buffer_unref (buffer);
590 /* create the retransmission packet */
593 _gst_rtp_buffer_new_from_rtx (&rtp, GPOINTER_TO_UINT (ssrc1),
594 orign_seqnum, origin_payload_type);
596 gst_rtp_buffer_unmap (&rtp);
598 /* push the packet */
600 gst_buffer_unref (buffer);
601 GST_LOG_OBJECT (rtx, "push packet seqnum:%" G_GUINT16_FORMAT
602 " from a restransmission stream ssrc2:%" G_GUINT32_FORMAT " (src %"
603 G_GUINT32_FORMAT ")", orign_seqnum, ssrc2, GPOINTER_TO_UINT (ssrc1));
604 ret = gst_pad_push (rtx->srcpad, new_buffer);
606 GST_LOG_OBJECT (rtx, "push packet seqnum:%" G_GUINT16_FORMAT
607 " from a master stream ssrc: %" G_GUINT32_FORMAT, seqnum, ssrc);
608 ret = gst_pad_push (rtx->srcpad, buffer);
615 gst_rtp_rtx_receive_get_property (GObject * object,
616 guint prop_id, GValue * value, GParamSpec * pspec)
618 GstRtpRtxReceive *rtx = GST_RTP_RTX_RECEIVE (object);
621 case PROP_PAYLOAD_TYPE_MAP:
622 g_mutex_lock (&rtx->lock);
623 g_value_set_boxed (value, rtx->pending_rtx_pt_map);
624 g_mutex_unlock (&rtx->lock);
626 case PROP_NUM_RTX_REQUESTS:
627 g_mutex_lock (&rtx->lock);
628 g_value_set_uint (value, rtx->num_rtx_requests);
629 g_mutex_unlock (&rtx->lock);
631 case PROP_NUM_RTX_PACKETS:
632 g_mutex_lock (&rtx->lock);
633 g_value_set_uint (value, rtx->num_rtx_packets);
634 g_mutex_unlock (&rtx->lock);
636 case PROP_NUM_RTX_ASSOC_PACKETS:
637 g_mutex_lock (&rtx->lock);
638 g_value_set_uint (value, rtx->num_rtx_assoc_packets);
639 g_mutex_unlock (&rtx->lock);
642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648 gst_rtp_rtx_receive_set_property (GObject * object,
649 guint prop_id, const GValue * value, GParamSpec * pspec)
651 GstRtpRtxReceive *rtx = GST_RTP_RTX_RECEIVE (object);
654 case PROP_PAYLOAD_TYPE_MAP:
655 g_mutex_lock (&rtx->lock);
656 if (rtx->pending_rtx_pt_map)
657 gst_structure_free (rtx->pending_rtx_pt_map);
658 rtx->pending_rtx_pt_map = g_value_dup_boxed (value);
659 rtx->rtx_pt_map_changed = TRUE;
660 g_mutex_unlock (&rtx->lock);
663 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
668 static GstStateChangeReturn
669 gst_rtp_rtx_receive_change_state (GstElement * element,
670 GstStateChange transition)
672 GstStateChangeReturn ret;
673 GstRtpRtxReceive *rtx;
675 rtx = GST_RTP_RTX_RECEIVE (element);
677 switch (transition) {
683 GST_ELEMENT_CLASS (gst_rtp_rtx_receive_parent_class)->change_state
684 (element, transition);
686 switch (transition) {
687 case GST_STATE_CHANGE_PAUSED_TO_READY:
688 gst_rtp_rtx_receive_reset (rtx);
698 gst_rtp_rtx_receive_plugin_init (GstPlugin * plugin)
700 GST_DEBUG_CATEGORY_INIT (gst_rtp_rtx_receive_debug, "rtprtxreceive", 0,
701 "rtp retransmission receiver");
703 return gst_element_register (plugin, "rtprtxreceive", GST_RANK_NONE,
704 GST_TYPE_RTP_RTX_RECEIVE);