2 * Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de>
3 * <2006> Wim Taymans <wim@fluendo.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
27 #include "gstrdtbuffer.h"
30 GST_DEBUG_CATEGORY_STATIC (rdtdepay_debug);
31 #define GST_CAT_DEFAULT rdtdepay_debug
33 /* RDTDepay signals and args */
45 static GstStaticPadTemplate gst_rdt_depay_src_template =
46 GST_STATIC_PAD_TEMPLATE ("src",
49 GST_STATIC_CAPS ("application/vnd.rn-realmedia")
52 static GstStaticPadTemplate gst_rdt_depay_sink_template =
53 GST_STATIC_PAD_TEMPLATE ("sink",
56 GST_STATIC_CAPS ("application/x-rdt, "
57 "media = (string) \"application\", "
58 "clock-rate = (int) [1, MAX ], "
59 "encoding-name = (string) \"X-REAL-RDT\""
60 /* All optional parameters
67 #define gst_rdt_depay_parent_class parent_class
68 G_DEFINE_TYPE (GstRDTDepay, gst_rdt_depay, GST_TYPE_ELEMENT);
69 GST_ELEMENT_REGISTER_DEFINE (rdtdepay, "rdtdepay",
70 GST_RANK_MARGINAL, GST_TYPE_RDT_DEPAY);
72 static void gst_rdt_depay_finalize (GObject * object);
74 static GstStateChangeReturn gst_rdt_depay_change_state (GstElement *
75 element, GstStateChange transition);
77 static gboolean gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent,
79 static GstFlowReturn gst_rdt_depay_chain (GstPad * pad, GstObject * parent,
83 gst_rdt_depay_class_init (GstRDTDepayClass * klass)
85 GObjectClass *gobject_class;
86 GstElementClass *gstelement_class;
88 gobject_class = (GObjectClass *) klass;
89 gstelement_class = (GstElementClass *) klass;
91 parent_class = g_type_class_peek_parent (klass);
93 gobject_class->finalize = gst_rdt_depay_finalize;
95 gstelement_class->change_state = gst_rdt_depay_change_state;
97 gst_element_class_add_static_pad_template (gstelement_class,
98 &gst_rdt_depay_src_template);
99 gst_element_class_add_static_pad_template (gstelement_class,
100 &gst_rdt_depay_sink_template);
102 gst_element_class_set_static_metadata (gstelement_class, "RDT packet parser",
103 "Codec/Depayloader/Network",
104 "Extracts RealMedia from RDT packets",
105 "Lutz Mueller <lutz at topfrose dot de>, "
106 "Wim Taymans <wim@fluendo.com>");
108 GST_DEBUG_CATEGORY_INIT (rdtdepay_debug, "rdtdepay",
109 0, "Depayloader for RDT RealMedia packets");
113 gst_rdt_depay_init (GstRDTDepay * rdtdepay)
116 gst_pad_new_from_static_template (&gst_rdt_depay_sink_template, "sink");
117 gst_pad_set_chain_function (rdtdepay->sinkpad, gst_rdt_depay_chain);
118 gst_pad_set_event_function (rdtdepay->sinkpad, gst_rdt_depay_sink_event);
119 gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->sinkpad);
122 gst_pad_new_from_static_template (&gst_rdt_depay_src_template, "src");
123 gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->srcpad);
127 gst_rdt_depay_finalize (GObject * object)
129 GstRDTDepay *rdtdepay;
131 rdtdepay = GST_RDT_DEPAY (object);
133 if (rdtdepay->header)
134 gst_buffer_unref (rdtdepay->header);
136 G_OBJECT_CLASS (parent_class)->finalize (object);
140 gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps)
142 GstStructure *structure;
143 GstRDTDepay *rdtdepay;
145 gint clock_rate = 1000; /* default */
149 rdtdepay = GST_RDT_DEPAY (GST_PAD_PARENT (pad));
151 structure = gst_caps_get_structure (caps, 0);
153 if (gst_structure_has_field (structure, "clock-rate"))
154 gst_structure_get_int (structure, "clock-rate", &clock_rate);
156 /* config contains the RealMedia header as a buffer. */
157 value = gst_structure_get_value (structure, "config");
161 header = gst_value_get_buffer (value);
165 /* get other values for newsegment */
166 value = gst_structure_get_value (structure, "npt-start");
167 if (value && G_VALUE_HOLDS_UINT64 (value))
168 rdtdepay->npt_start = g_value_get_uint64 (value);
170 rdtdepay->npt_start = 0;
171 GST_DEBUG_OBJECT (rdtdepay, "NPT start %" G_GUINT64_FORMAT,
172 rdtdepay->npt_start);
174 value = gst_structure_get_value (structure, "npt-stop");
175 if (value && G_VALUE_HOLDS_UINT64 (value))
176 rdtdepay->npt_stop = g_value_get_uint64 (value);
178 rdtdepay->npt_stop = -1;
180 GST_DEBUG_OBJECT (rdtdepay, "NPT stop %" G_GUINT64_FORMAT,
183 value = gst_structure_get_value (structure, "play-speed");
184 if (value && G_VALUE_HOLDS_DOUBLE (value))
185 rdtdepay->play_speed = g_value_get_double (value);
187 rdtdepay->play_speed = 1.0;
189 value = gst_structure_get_value (structure, "play-scale");
190 if (value && G_VALUE_HOLDS_DOUBLE (value))
191 rdtdepay->play_scale = g_value_get_double (value);
193 rdtdepay->play_scale = 1.0;
195 /* caps seem good, configure element */
196 rdtdepay->clock_rate = clock_rate;
198 /* set caps on pad and on header */
199 srccaps = gst_caps_new_empty_simple ("application/vnd.rn-realmedia");
200 gst_pad_set_caps (rdtdepay->srcpad, srccaps);
201 gst_caps_unref (srccaps);
203 if (rdtdepay->header)
204 gst_buffer_unref (rdtdepay->header);
205 rdtdepay->header = gst_buffer_ref (header);
212 GST_ERROR_OBJECT (rdtdepay, "no header found in caps, no 'config' field");
218 gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
223 depay = GST_RDT_DEPAY (parent);
225 switch (GST_EVENT_TYPE (event)) {
230 gst_event_parse_caps (event, &caps);
231 res = gst_rdt_depay_setcaps (pad, caps);
232 gst_event_unref (event);
235 case GST_EVENT_FLUSH_STOP:
236 res = gst_pad_push_event (depay->srcpad, event);
238 gst_segment_init (&depay->segment, GST_FORMAT_UNDEFINED);
239 depay->need_newsegment = TRUE;
240 depay->next_seqnum = -1;
242 case GST_EVENT_SEGMENT:
244 gst_event_copy_segment (event, &depay->segment);
245 /* don't pass the event downstream, we generate our own segment
246 * including the NTP time and other things we receive in caps */
247 gst_event_unref (event);
251 /* pass other events forward */
252 res = gst_pad_push_event (depay->srcpad, event);
259 create_segment_event (GstRDTDepay * depay, gboolean update,
260 GstClockTime position)
264 gst_segment_init (&segment, GST_FORMAT_TIME);
265 segment.rate = depay->play_speed;
266 segment.applied_rate = depay->play_scale;
267 segment.start = position;
269 if (depay->npt_stop != -1)
270 segment.stop = depay->npt_stop - depay->npt_start;
274 segment.time = position + depay->npt_start;
276 return gst_event_new_segment (&segment);
280 gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer)
284 if (rdtdepay->need_newsegment) {
287 event = create_segment_event (rdtdepay, FALSE, 0);
288 gst_pad_push_event (rdtdepay->srcpad, event);
290 rdtdepay->need_newsegment = FALSE;
293 if (rdtdepay->discont) {
294 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
295 rdtdepay->discont = FALSE;
297 ret = gst_pad_push (rdtdepay->srcpad, buffer);
303 gst_rdt_depay_handle_data (GstRDTDepay * rdtdepay, GstClockTime outtime,
304 GstRDTPacket * packet)
309 guint8 *data, *outdata;
318 /* get pointers to the packet data */
319 data = gst_rdt_packet_data_map (packet, &size);
321 outbuf = gst_buffer_new_and_alloc (12 + size);
322 GST_BUFFER_TIMESTAMP (outbuf) = outtime;
324 GST_DEBUG_OBJECT (rdtdepay, "have size %u", size);
326 /* copy over some things */
327 stream_id = gst_rdt_packet_data_get_stream_id (packet);
328 timestamp = gst_rdt_packet_data_get_timestamp (packet);
329 flags = gst_rdt_packet_data_get_flags (packet);
331 seqnum = gst_rdt_packet_data_get_seq (packet);
333 GST_DEBUG_OBJECT (rdtdepay, "stream_id %u, timestamp %u, seqnum %d, flags %d",
334 stream_id, timestamp, seqnum, flags);
336 if (rdtdepay->next_seqnum != -1) {
337 gap = gst_rdt_buffer_compare_seqnum (seqnum, rdtdepay->next_seqnum);
339 /* if we have no gap, all is fine */
340 if (G_UNLIKELY (gap != 0)) {
341 GST_LOG_OBJECT (rdtdepay, "got packet %u, expected %u, gap %d", seqnum,
342 rdtdepay->next_seqnum, gap);
344 /* seqnum > next_seqnum, we are missing some packets, this is always a
346 GST_LOG_OBJECT (rdtdepay, "%d missing packets", gap);
347 rdtdepay->discont = TRUE;
349 /* seqnum < next_seqnum, we have seen this packet before or the sender
350 * could be restarted. If the packet is not too old, we throw it away as
351 * a duplicate, otherwise we mark discont and continue. 100 misordered
352 * packets is a good threshold. See also RFC 4737. */
356 GST_LOG_OBJECT (rdtdepay,
357 "%d > 100, packet too old, sender likely restarted", gap);
358 rdtdepay->discont = TRUE;
362 rdtdepay->next_seqnum = (seqnum + 1);
363 if (rdtdepay->next_seqnum == 0xff00)
364 rdtdepay->next_seqnum = 0;
366 if ((flags & 1) == 0)
371 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
372 outdata = outmap.data;
373 GST_WRITE_UINT16_BE (outdata + 0, 0); /* version */
374 GST_WRITE_UINT16_BE (outdata + 2, size + 12); /* length */
375 GST_WRITE_UINT16_BE (outdata + 4, stream_id); /* stream */
376 GST_WRITE_UINT32_BE (outdata + 6, timestamp); /* timestamp */
377 GST_WRITE_UINT16_BE (outdata + 10, outflags); /* flags */
378 memcpy (outdata + 12, data, size);
379 gst_buffer_unmap (outbuf, &outmap);
380 gst_buffer_resize (outbuf, 0, 12 + size);
382 gst_rdt_packet_data_unmap (packet);
384 GST_DEBUG_OBJECT (rdtdepay, "Pushing packet, outtime %" GST_TIME_FORMAT,
385 GST_TIME_ARGS (outtime));
387 ret = gst_rdt_depay_push (rdtdepay, outbuf);
394 GST_WARNING_OBJECT (rdtdepay, "%d <= 100, dropping old packet", gap);
400 gst_rdt_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
402 GstRDTDepay *rdtdepay;
404 GstClockTime timestamp;
408 rdtdepay = GST_RDT_DEPAY (parent);
410 if (GST_BUFFER_IS_DISCONT (buf)) {
411 GST_LOG_OBJECT (rdtdepay, "received discont");
412 rdtdepay->discont = TRUE;
415 if (rdtdepay->header) {
418 out = rdtdepay->header;
419 rdtdepay->header = NULL;
421 /* push header data first */
422 gst_rdt_depay_push (rdtdepay, out);
426 timestamp = GST_BUFFER_TIMESTAMP (buf);
430 GST_LOG_OBJECT (rdtdepay, "received buffer timestamp %" GST_TIME_FORMAT,
431 GST_TIME_ARGS (timestamp));
433 /* data is in RDT format. */
434 more = gst_rdt_buffer_get_first_packet (buf, &packet);
438 type = gst_rdt_packet_get_type (&packet);
439 GST_DEBUG_OBJECT (rdtdepay, "Have packet of type %04x", type);
441 if (GST_RDT_IS_DATA_TYPE (type)) {
442 GST_DEBUG_OBJECT (rdtdepay, "We have a data packet");
443 ret = gst_rdt_depay_handle_data (rdtdepay, timestamp, &packet);
447 GST_DEBUG_OBJECT (rdtdepay, "Ignoring packet");
451 if (ret != GST_FLOW_OK)
454 more = gst_rdt_packet_move_to_next (&packet);
457 gst_buffer_unref (buf);
462 static GstStateChangeReturn
463 gst_rdt_depay_change_state (GstElement * element, GstStateChange transition)
465 GstRDTDepay *rdtdepay;
466 GstStateChangeReturn ret;
468 rdtdepay = GST_RDT_DEPAY (element);
470 switch (transition) {
471 case GST_STATE_CHANGE_NULL_TO_READY:
473 case GST_STATE_CHANGE_READY_TO_PAUSED:
474 gst_segment_init (&rdtdepay->segment, GST_FORMAT_UNDEFINED);
475 rdtdepay->next_seqnum = -1;
476 rdtdepay->need_newsegment = TRUE;
482 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
484 switch (transition) {
485 case GST_STATE_CHANGE_PAUSED_TO_READY:
486 if (rdtdepay->header)
487 gst_buffer_unref (rdtdepay->header);
488 rdtdepay->header = NULL;
490 case GST_STATE_CHANGE_READY_TO_NULL: