Move files from gst-plugins-ugly into the "subprojects/gst-plugins-ugly/" subdir
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-ugly / gst / realmedia / rdtdepay.c
1 /* GStreamer
2  * Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de>
3  *               <2006> Wim Taymans <wim@fluendo.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24
25 #include <string.h>
26
27 #include "gstrdtbuffer.h"
28 #include "rdtdepay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rdtdepay_debug);
31 #define GST_CAT_DEFAULT rdtdepay_debug
32
33 /* RDTDepay signals and args */
34 enum
35 {
36   /* FILL ME */
37   LAST_SIGNAL
38 };
39
40 enum
41 {
42   PROP_0,
43 };
44
45 static GstStaticPadTemplate gst_rdt_depay_src_template =
46 GST_STATIC_PAD_TEMPLATE ("src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("application/vnd.rn-realmedia")
50     );
51
52 static GstStaticPadTemplate gst_rdt_depay_sink_template =
53 GST_STATIC_PAD_TEMPLATE ("sink",
54     GST_PAD_SINK,
55     GST_PAD_ALWAYS,
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
61          *
62          * "config=" 
63          */
64     )
65     );
66
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);
71
72 static void gst_rdt_depay_finalize (GObject * object);
73
74 static GstStateChangeReturn gst_rdt_depay_change_state (GstElement *
75     element, GstStateChange transition);
76
77 static gboolean gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent,
78     GstEvent * event);
79 static GstFlowReturn gst_rdt_depay_chain (GstPad * pad, GstObject * parent,
80     GstBuffer * buf);
81
82 static void
83 gst_rdt_depay_class_init (GstRDTDepayClass * klass)
84 {
85   GObjectClass *gobject_class;
86   GstElementClass *gstelement_class;
87
88   gobject_class = (GObjectClass *) klass;
89   gstelement_class = (GstElementClass *) klass;
90
91   parent_class = g_type_class_peek_parent (klass);
92
93   gobject_class->finalize = gst_rdt_depay_finalize;
94
95   gstelement_class->change_state = gst_rdt_depay_change_state;
96
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);
101
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>");
107
108   GST_DEBUG_CATEGORY_INIT (rdtdepay_debug, "rdtdepay",
109       0, "Depayloader for RDT RealMedia packets");
110 }
111
112 static void
113 gst_rdt_depay_init (GstRDTDepay * rdtdepay)
114 {
115   rdtdepay->sinkpad =
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);
120
121   rdtdepay->srcpad =
122       gst_pad_new_from_static_template (&gst_rdt_depay_src_template, "src");
123   gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->srcpad);
124 }
125
126 static void
127 gst_rdt_depay_finalize (GObject * object)
128 {
129   GstRDTDepay *rdtdepay;
130
131   rdtdepay = GST_RDT_DEPAY (object);
132
133   if (rdtdepay->header)
134     gst_buffer_unref (rdtdepay->header);
135
136   G_OBJECT_CLASS (parent_class)->finalize (object);
137 }
138
139 static gboolean
140 gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps)
141 {
142   GstStructure *structure;
143   GstRDTDepay *rdtdepay;
144   GstCaps *srccaps;
145   gint clock_rate = 1000;       /* default */
146   const GValue *value;
147   GstBuffer *header;
148
149   rdtdepay = GST_RDT_DEPAY (GST_PAD_PARENT (pad));
150
151   structure = gst_caps_get_structure (caps, 0);
152
153   if (gst_structure_has_field (structure, "clock-rate"))
154     gst_structure_get_int (structure, "clock-rate", &clock_rate);
155
156   /* config contains the RealMedia header as a buffer. */
157   value = gst_structure_get_value (structure, "config");
158   if (!value)
159     goto no_header;
160
161   header = gst_value_get_buffer (value);
162   if (!header)
163     goto no_header;
164
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);
169   else
170     rdtdepay->npt_start = 0;
171   GST_DEBUG_OBJECT (rdtdepay, "NPT start %" G_GUINT64_FORMAT,
172       rdtdepay->npt_start);
173
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);
177   else
178     rdtdepay->npt_stop = -1;
179
180   GST_DEBUG_OBJECT (rdtdepay, "NPT stop %" G_GUINT64_FORMAT,
181       rdtdepay->npt_stop);
182
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);
186   else
187     rdtdepay->play_speed = 1.0;
188
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);
192   else
193     rdtdepay->play_scale = 1.0;
194
195   /* caps seem good, configure element */
196   rdtdepay->clock_rate = clock_rate;
197
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);
202
203   if (rdtdepay->header)
204     gst_buffer_unref (rdtdepay->header);
205   rdtdepay->header = gst_buffer_ref (header);
206
207   return TRUE;
208
209   /* ERRORS */
210 no_header:
211   {
212     GST_ERROR_OBJECT (rdtdepay, "no header found in caps, no 'config' field");
213     return FALSE;
214   }
215 }
216
217 static gboolean
218 gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
219 {
220   GstRDTDepay *depay;
221   gboolean res = TRUE;
222
223   depay = GST_RDT_DEPAY (parent);
224
225   switch (GST_EVENT_TYPE (event)) {
226     case GST_EVENT_CAPS:
227     {
228       GstCaps *caps;
229
230       gst_event_parse_caps (event, &caps);
231       res = gst_rdt_depay_setcaps (pad, caps);
232       gst_event_unref (event);
233       break;
234     }
235     case GST_EVENT_FLUSH_STOP:
236       res = gst_pad_push_event (depay->srcpad, event);
237
238       gst_segment_init (&depay->segment, GST_FORMAT_UNDEFINED);
239       depay->need_newsegment = TRUE;
240       depay->next_seqnum = -1;
241       break;
242     case GST_EVENT_SEGMENT:
243     {
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);
248       break;
249     }
250     default:
251       /* pass other events forward */
252       res = gst_pad_push_event (depay->srcpad, event);
253       break;
254   }
255   return res;
256 }
257
258 static GstEvent *
259 create_segment_event (GstRDTDepay * depay, gboolean update,
260     GstClockTime position)
261 {
262   GstSegment segment;
263
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;
268
269   if (depay->npt_stop != -1)
270     segment.stop = depay->npt_stop - depay->npt_start;
271   else
272     segment.stop = -1;
273
274   segment.time = position + depay->npt_start;
275
276   return gst_event_new_segment (&segment);
277 }
278
279 static GstFlowReturn
280 gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer)
281 {
282   GstFlowReturn ret;
283
284   if (rdtdepay->need_newsegment) {
285     GstEvent *event;
286
287     event = create_segment_event (rdtdepay, FALSE, 0);
288     gst_pad_push_event (rdtdepay->srcpad, event);
289
290     rdtdepay->need_newsegment = FALSE;
291   }
292
293   if (rdtdepay->discont) {
294     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
295     rdtdepay->discont = FALSE;
296   }
297   ret = gst_pad_push (rdtdepay->srcpad, buffer);
298
299   return ret;
300 }
301
302 static GstFlowReturn
303 gst_rdt_depay_handle_data (GstRDTDepay * rdtdepay, GstClockTime outtime,
304     GstRDTPacket * packet)
305 {
306   GstFlowReturn ret;
307   GstBuffer *outbuf;
308   GstMapInfo outmap;
309   guint8 *data, *outdata;
310   guint size;
311   guint16 stream_id;
312   guint32 timestamp;
313   gint gap;
314   guint16 seqnum;
315   guint8 flags;
316   guint16 outflags;
317
318   /* get pointers to the packet data */
319   data = gst_rdt_packet_data_map (packet, &size);
320
321   outbuf = gst_buffer_new_and_alloc (12 + size);
322   GST_BUFFER_TIMESTAMP (outbuf) = outtime;
323
324   GST_DEBUG_OBJECT (rdtdepay, "have size %u", size);
325
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);
330
331   seqnum = gst_rdt_packet_data_get_seq (packet);
332
333   GST_DEBUG_OBJECT (rdtdepay, "stream_id %u, timestamp %u, seqnum %d, flags %d",
334       stream_id, timestamp, seqnum, flags);
335
336   if (rdtdepay->next_seqnum != -1) {
337     gap = gst_rdt_buffer_compare_seqnum (seqnum, rdtdepay->next_seqnum);
338
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);
343       if (gap < 0) {
344         /* seqnum > next_seqnum, we are missing some packets, this is always a
345          * DISCONT. */
346         GST_LOG_OBJECT (rdtdepay, "%d missing packets", gap);
347         rdtdepay->discont = TRUE;
348       } else {
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. */
353         if (gap < 100)
354           goto dropping;
355
356         GST_LOG_OBJECT (rdtdepay,
357             "%d > 100, packet too old, sender likely restarted", gap);
358         rdtdepay->discont = TRUE;
359       }
360     }
361   }
362   rdtdepay->next_seqnum = (seqnum + 1);
363   if (rdtdepay->next_seqnum == 0xff00)
364     rdtdepay->next_seqnum = 0;
365
366   if ((flags & 1) == 0)
367     outflags = 2;
368   else
369     outflags = 0;
370
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);
381
382   gst_rdt_packet_data_unmap (packet);
383
384   GST_DEBUG_OBJECT (rdtdepay, "Pushing packet, outtime %" GST_TIME_FORMAT,
385       GST_TIME_ARGS (outtime));
386
387   ret = gst_rdt_depay_push (rdtdepay, outbuf);
388
389   return ret;
390
391   /* ERRORS */
392 dropping:
393   {
394     GST_WARNING_OBJECT (rdtdepay, "%d <= 100, dropping old packet", gap);
395     return GST_FLOW_OK;
396   }
397 }
398
399 static GstFlowReturn
400 gst_rdt_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
401 {
402   GstRDTDepay *rdtdepay;
403   GstFlowReturn ret;
404   GstClockTime timestamp;
405   gboolean more;
406   GstRDTPacket packet;
407
408   rdtdepay = GST_RDT_DEPAY (parent);
409
410   if (GST_BUFFER_IS_DISCONT (buf)) {
411     GST_LOG_OBJECT (rdtdepay, "received discont");
412     rdtdepay->discont = TRUE;
413   }
414
415   if (rdtdepay->header) {
416     GstBuffer *out;
417
418     out = rdtdepay->header;
419     rdtdepay->header = NULL;
420
421     /* push header data first */
422     gst_rdt_depay_push (rdtdepay, out);
423   }
424
425   /* save timestamp */
426   timestamp = GST_BUFFER_TIMESTAMP (buf);
427
428   ret = GST_FLOW_OK;
429
430   GST_LOG_OBJECT (rdtdepay, "received buffer timestamp %" GST_TIME_FORMAT,
431       GST_TIME_ARGS (timestamp));
432
433   /* data is in RDT format. */
434   more = gst_rdt_buffer_get_first_packet (buf, &packet);
435   while (more) {
436     GstRDTType type;
437
438     type = gst_rdt_packet_get_type (&packet);
439     GST_DEBUG_OBJECT (rdtdepay, "Have packet of type %04x", type);
440
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);
444     } else {
445       switch (type) {
446         default:
447           GST_DEBUG_OBJECT (rdtdepay, "Ignoring packet");
448           break;
449       }
450     }
451     if (ret != GST_FLOW_OK)
452       break;
453
454     more = gst_rdt_packet_move_to_next (&packet);
455   }
456
457   gst_buffer_unref (buf);
458
459   return ret;
460 }
461
462 static GstStateChangeReturn
463 gst_rdt_depay_change_state (GstElement * element, GstStateChange transition)
464 {
465   GstRDTDepay *rdtdepay;
466   GstStateChangeReturn ret;
467
468   rdtdepay = GST_RDT_DEPAY (element);
469
470   switch (transition) {
471     case GST_STATE_CHANGE_NULL_TO_READY:
472       break;
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;
477       break;
478     default:
479       break;
480   }
481
482   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
483
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;
489       break;
490     case GST_STATE_CHANGE_READY_TO_NULL:
491       break;
492     default:
493       break;
494   }
495   return ret;
496 }