expand tabs
[platform/upstream/gstreamer.git] / gst-libs / gst / rtp / gstbasertppayload.c
1 /* GStreamer
2  * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more 
13  */
14
15 #ifdef HAVE_CONFIG_H
16 #  include "config.h"
17 #endif
18
19 #include <string.h>
20
21 #include <gst/rtp/gstrtpbuffer.h>
22
23 #include "gstbasertppayload.h"
24
25 GST_DEBUG_CATEGORY (basertppayload_debug);
26 #define GST_CAT_DEFAULT (basertppayload_debug)
27
28 /* BaseRTPPayload signals and args */
29 enum
30 {
31   /* FILL ME */
32   LAST_SIGNAL
33 };
34
35 #define DEFAULT_MTU                     1024
36 #define DEFAULT_PT                      96
37 #define DEFAULT_SSRC                    -1
38 #define DEFAULT_TIMESTAMP_OFFSET        -1
39 #define DEFAULT_SEQNUM_OFFSET           -1
40 #define DEFAULT_MAX_PTIME               -1
41
42 enum
43 {
44   PROP_0,
45   PROP_MTU,
46   PROP_PT,
47   PROP_SSRC,
48   PROP_TIMESTAMP_OFFSET,
49   PROP_SEQNUM_OFFSET,
50   PROP_MAX_PTIME,
51   PROP_TIMESTAMP,
52   PROP_SEQNUM
53 };
54
55 static void gst_basertppayload_class_init (GstBaseRTPPayloadClass * klass);
56 static void gst_basertppayload_base_init (GstBaseRTPPayloadClass * klass);
57 static void gst_basertppayload_init (GstBaseRTPPayload * basertppayload,
58     gpointer g_class);
59 static void gst_basertppayload_finalize (GObject * object);
60
61 static gboolean gst_basertppayload_setcaps (GstPad * pad, GstCaps * caps);
62 static gboolean gst_basertppayload_event (GstPad * pad, GstEvent * event);
63 static GstFlowReturn gst_basertppayload_chain (GstPad * pad,
64     GstBuffer * buffer);
65
66 static void gst_basertppayload_set_property (GObject * object, guint prop_id,
67     const GValue * value, GParamSpec * pspec);
68 static void gst_basertppayload_get_property (GObject * object, guint prop_id,
69     GValue * value, GParamSpec * pspec);
70
71 static GstStateChangeReturn gst_basertppayload_change_state (GstElement *
72     element, GstStateChange transition);
73
74 static GstElementClass *parent_class = NULL;
75
76 GType
77 gst_basertppayload_get_type (void)
78 {
79   static GType basertppayload_type = 0;
80
81   if (!basertppayload_type) {
82     static const GTypeInfo basertppayload_info = {
83       sizeof (GstBaseRTPPayloadClass),
84       (GBaseInitFunc) gst_basertppayload_base_init,
85       NULL,
86       (GClassInitFunc) gst_basertppayload_class_init,
87       NULL,
88       NULL,
89       sizeof (GstBaseRTPPayload),
90       0,
91       (GInstanceInitFunc) gst_basertppayload_init,
92     };
93
94     basertppayload_type =
95         g_type_register_static (GST_TYPE_ELEMENT, "GstBaseRTPPayload",
96         &basertppayload_info, G_TYPE_FLAG_ABSTRACT);
97   }
98   return basertppayload_type;
99 }
100
101 static void
102 gst_basertppayload_base_init (GstBaseRTPPayloadClass * klass)
103 {
104 }
105
106 static void
107 gst_basertppayload_class_init (GstBaseRTPPayloadClass * klass)
108 {
109   GObjectClass *gobject_class;
110   GstElementClass *gstelement_class;
111
112   gobject_class = (GObjectClass *) klass;
113   gstelement_class = (GstElementClass *) klass;
114
115   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
116
117   gobject_class->finalize = gst_basertppayload_finalize;
118
119   gobject_class->set_property = gst_basertppayload_set_property;
120   gobject_class->get_property = gst_basertppayload_get_property;
121
122   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MTU,
123       g_param_spec_uint ("mtu", "MTU",
124           "Maximum size of one packet",
125           28, G_MAXUINT, DEFAULT_MTU, G_PARAM_READWRITE));
126   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PT,
127       g_param_spec_uint ("pt", "payload type",
128           "The payload type of the packets",
129           0, 0x80, DEFAULT_PT, G_PARAM_READWRITE));
130   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSRC,
131       g_param_spec_uint ("ssrc", "SSRC",
132           "The SSRC of the packets (-1 == random)",
133           0, G_MAXUINT, DEFAULT_SSRC, G_PARAM_READWRITE));
134   g_object_class_install_property (G_OBJECT_CLASS (klass),
135       PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset",
136           "Timestamp Offset",
137           "Offset to add to all outgoing timestamps (-1 = random)", -1,
138           G_MAXINT, DEFAULT_TIMESTAMP_OFFSET, G_PARAM_READWRITE));
139   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM_OFFSET,
140       g_param_spec_int ("seqnum-offset", "Sequence number Offset",
141           "Offset to add to all outgoing seqnum (-1 = random)", -1, G_MAXINT,
142           DEFAULT_SEQNUM_OFFSET, G_PARAM_READWRITE));
143   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_PTIME,
144       g_param_spec_int64 ("max-ptime", "Max packet time",
145           "Maximum duration of the packet data in ns (-1 = unlimited up to MTU)",
146           -1, G_MAXINT64, DEFAULT_MAX_PTIME, G_PARAM_READWRITE));
147
148   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMESTAMP,
149       g_param_spec_uint ("timestamp", "Timestamp",
150           "The RTP timestamp of the last processed packet",
151           0, G_MAXUINT, 0, G_PARAM_READABLE));
152   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEQNUM,
153       g_param_spec_uint ("seqnum", "Sequence number",
154           "The RTP sequence number of the last processed packet",
155           0, G_MAXUINT, 0, G_PARAM_READABLE));
156
157   gstelement_class->change_state = gst_basertppayload_change_state;
158
159   GST_DEBUG_CATEGORY_INIT (basertppayload_debug, "basertppayload", 0,
160       "Base class for RTP Payloaders");
161 }
162
163 static void
164 gst_basertppayload_init (GstBaseRTPPayload * basertppayload, gpointer g_class)
165 {
166   GstPadTemplate *templ;
167
168   templ =
169       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
170   g_return_if_fail (templ != NULL);
171
172   basertppayload->srcpad = gst_pad_new_from_template (templ, "src");
173   gst_element_add_pad (GST_ELEMENT (basertppayload), basertppayload->srcpad);
174
175   templ =
176       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
177   g_return_if_fail (templ != NULL);
178
179   basertppayload->sinkpad = gst_pad_new_from_template (templ, "sink");
180   gst_pad_set_setcaps_function (basertppayload->sinkpad,
181       gst_basertppayload_setcaps);
182   gst_pad_set_event_function (basertppayload->sinkpad,
183       gst_basertppayload_event);
184   gst_pad_set_chain_function (basertppayload->sinkpad,
185       gst_basertppayload_chain);
186   gst_element_add_pad (GST_ELEMENT (basertppayload), basertppayload->sinkpad);
187
188   basertppayload->seq_rand = g_rand_new ();
189   basertppayload->ssrc_rand = g_rand_new ();
190   basertppayload->ts_rand = g_rand_new ();
191
192   basertppayload->mtu = DEFAULT_MTU;
193   basertppayload->pt = DEFAULT_PT;
194   basertppayload->seqnum_offset = DEFAULT_SEQNUM_OFFSET;
195   basertppayload->ssrc = DEFAULT_SSRC;
196   basertppayload->ts_offset = DEFAULT_TIMESTAMP_OFFSET;
197   basertppayload->max_ptime = DEFAULT_MAX_PTIME;
198
199   basertppayload->clock_rate = 0;
200 }
201
202 static void
203 gst_basertppayload_finalize (GObject * object)
204 {
205   GstBaseRTPPayload *basertppayload;
206
207   basertppayload = GST_BASE_RTP_PAYLOAD (object);
208
209   g_rand_free (basertppayload->seq_rand);
210   basertppayload->seq_rand = NULL;
211   g_rand_free (basertppayload->ssrc_rand);
212   basertppayload->ssrc_rand = NULL;
213   g_rand_free (basertppayload->ts_rand);
214   basertppayload->ts_rand = NULL;
215
216   G_OBJECT_CLASS (parent_class)->finalize (object);
217 }
218
219 static gboolean
220 gst_basertppayload_setcaps (GstPad * pad, GstCaps * caps)
221 {
222   GstBaseRTPPayload *basertppayload;
223   GstBaseRTPPayloadClass *basertppayload_class;
224   gboolean ret = TRUE;
225
226   basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
227   basertppayload_class = GST_BASE_RTP_PAYLOAD_GET_CLASS (basertppayload);
228
229   if (basertppayload_class->set_caps)
230     ret = basertppayload_class->set_caps (basertppayload, caps);
231
232   gst_object_unref (basertppayload);
233
234   return ret;
235 }
236
237 static gboolean
238 gst_basertppayload_event (GstPad * pad, GstEvent * event)
239 {
240   GstBaseRTPPayload *basertppayload;
241   gboolean res;
242
243   basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
244
245   switch (GST_EVENT_TYPE (event)) {
246     case GST_EVENT_FLUSH_START:
247       res = gst_pad_event_default (pad, event);
248       break;
249     case GST_EVENT_FLUSH_STOP:
250       res = gst_pad_event_default (pad, event);
251       gst_segment_init (&basertppayload->segment, GST_FORMAT_UNDEFINED);
252       break;
253     case GST_EVENT_NEWSEGMENT:
254     {
255       gboolean update;
256       gdouble rate;
257       GstFormat fmt;
258       gint64 start, stop, position;
259
260       gst_event_parse_new_segment (event, &update, &rate, &fmt, &start, &stop,
261           &position);
262       gst_segment_set_newsegment (&basertppayload->segment, update, rate, fmt,
263           start, stop, position);
264     }
265       /* fallthrough */
266     default:
267       res = gst_pad_event_default (pad, event);
268       break;
269   }
270
271   gst_object_unref (basertppayload);
272
273   return res;
274 }
275
276
277 static GstFlowReturn
278 gst_basertppayload_chain (GstPad * pad, GstBuffer * buffer)
279 {
280   GstBaseRTPPayload *basertppayload;
281   GstBaseRTPPayloadClass *basertppayload_class;
282   GstFlowReturn ret;
283
284   basertppayload = GST_BASE_RTP_PAYLOAD (gst_pad_get_parent (pad));
285   basertppayload_class = GST_BASE_RTP_PAYLOAD_GET_CLASS (basertppayload);
286
287   if (!basertppayload_class->handle_buffer)
288     goto no_function;
289
290   ret = basertppayload_class->handle_buffer (basertppayload, buffer);
291
292   gst_object_unref (basertppayload);
293
294   return ret;
295
296   /* ERRORS */
297 no_function:
298   {
299     GST_ELEMENT_ERROR (basertppayload, STREAM, NOT_IMPLEMENTED, (NULL),
300         ("subclass did not implement handle_buffer function"));
301     gst_object_unref (basertppayload);
302     return GST_FLOW_ERROR;
303   }
304 }
305
306 void
307 gst_basertppayload_set_options (GstBaseRTPPayload * payload,
308     gchar * media, gboolean dynamic, gchar * encoding_name, guint32 clock_rate)
309 {
310   g_return_if_fail (payload != NULL);
311   g_return_if_fail (clock_rate != 0);
312
313   g_free (payload->media);
314   payload->media = g_strdup (media);
315   payload->dynamic = dynamic;
316   g_free (payload->encoding_name);
317   payload->encoding_name = g_strdup (encoding_name);
318   payload->clock_rate = clock_rate;
319 }
320
321 gboolean
322 gst_basertppayload_set_outcaps (GstBaseRTPPayload * payload, gchar * fieldname,
323     ...)
324 {
325   GstCaps *srccaps;
326   GstStructure *s;
327
328   srccaps = gst_caps_new_simple ("application/x-rtp",
329       "media", G_TYPE_STRING, payload->media,
330       "payload", G_TYPE_INT, GST_BASE_RTP_PAYLOAD_PT (payload),
331       "clock-rate", G_TYPE_INT, payload->clock_rate,
332       "encoding-name", G_TYPE_STRING, payload->encoding_name,
333       "ssrc", G_TYPE_UINT, payload->current_ssrc,
334       "clock-base", G_TYPE_UINT, payload->ts_base,
335       "seqnum-base", G_TYPE_UINT, payload->seqnum_base, NULL);
336   s = gst_caps_get_structure (srccaps, 0);
337
338   if (fieldname) {
339     va_list varargs;
340
341     va_start (varargs, fieldname);
342     gst_structure_set_valist (s, fieldname, varargs);
343     va_end (varargs);
344   }
345
346   gst_pad_set_caps (GST_BASE_RTP_PAYLOAD_SRCPAD (payload), srccaps);
347   gst_caps_unref (srccaps);
348
349   return TRUE;
350 }
351
352 gboolean
353 gst_basertppayload_is_filled (GstBaseRTPPayload * payload,
354     guint size, GstClockTime duration)
355 {
356   if (size > payload->mtu)
357     return TRUE;
358
359   if (payload->max_ptime != -1 && duration >= payload->max_ptime)
360     return TRUE;
361
362   return FALSE;
363 }
364
365 GstFlowReturn
366 gst_basertppayload_push (GstBaseRTPPayload * payload, GstBuffer * buffer)
367 {
368   GstFlowReturn res;
369   GstClockTime timestamp;
370   guint32 ts;
371
372   if (payload->clock_rate == 0)
373     goto no_rate;
374
375   gst_rtp_buffer_set_ssrc (buffer, payload->current_ssrc);
376
377   gst_rtp_buffer_set_payload_type (buffer, payload->pt);
378
379   /* can wrap around, which is perfectly fine */
380   gst_rtp_buffer_set_seq (buffer, payload->seqnum++);
381
382   /* add our random offset to the timestamp */
383   ts = payload->ts_base;
384
385   timestamp = GST_BUFFER_TIMESTAMP (buffer);
386   if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
387     gint64 rtime;
388
389     rtime =
390         gst_segment_to_running_time (&payload->segment, GST_FORMAT_TIME,
391         timestamp);
392     rtime = gst_util_uint64_scale_int (rtime, payload->clock_rate, GST_SECOND);
393
394     ts += rtime;
395   }
396   gst_rtp_buffer_set_timestamp (buffer, ts);
397
398   payload->timestamp = ts;
399
400   /* set caps */
401   gst_buffer_set_caps (buffer, GST_PAD_CAPS (payload->srcpad));
402
403   GST_DEBUG_OBJECT (payload, "Pushing packet size %d, seq=%d, ts=%u",
404       GST_BUFFER_SIZE (buffer), payload->seqnum - 1, ts);
405
406   res = gst_pad_push (payload->srcpad, buffer);
407
408   return res;
409
410   /* ERRORS */
411 no_rate:
412   {
413     GST_ELEMENT_ERROR (payload, STREAM, NOT_IMPLEMENTED, (NULL),
414         ("subclass did not specify clock_rate"));
415     gst_buffer_unref (buffer);
416     return GST_FLOW_ERROR;
417   }
418 }
419
420 static void
421 gst_basertppayload_set_property (GObject * object, guint prop_id,
422     const GValue * value, GParamSpec * pspec)
423 {
424   GstBaseRTPPayload *basertppayload;
425
426   basertppayload = GST_BASE_RTP_PAYLOAD (object);
427
428   switch (prop_id) {
429     case PROP_MTU:
430       basertppayload->mtu = g_value_get_uint (value);
431       break;
432     case PROP_PT:
433       basertppayload->pt = g_value_get_uint (value);
434       break;
435     case PROP_SSRC:
436       basertppayload->ssrc = g_value_get_uint (value);
437       break;
438     case PROP_TIMESTAMP_OFFSET:
439       basertppayload->ts_offset = g_value_get_int (value);
440       break;
441     case PROP_SEQNUM_OFFSET:
442       basertppayload->seqnum_offset = g_value_get_int (value);
443       break;
444     case PROP_MAX_PTIME:
445       basertppayload->max_ptime = g_value_get_int64 (value);
446       break;
447     default:
448       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
449       break;
450   }
451 }
452
453 static void
454 gst_basertppayload_get_property (GObject * object, guint prop_id,
455     GValue * value, GParamSpec * pspec)
456 {
457   GstBaseRTPPayload *basertppayload;
458
459   basertppayload = GST_BASE_RTP_PAYLOAD (object);
460
461   switch (prop_id) {
462     case PROP_MTU:
463       g_value_set_uint (value, basertppayload->mtu);
464       break;
465     case PROP_PT:
466       g_value_set_uint (value, basertppayload->pt);
467       break;
468     case PROP_SSRC:
469       g_value_set_uint (value, basertppayload->ssrc);
470       break;
471     case PROP_TIMESTAMP_OFFSET:
472       g_value_set_int (value, basertppayload->ts_offset);
473       break;
474     case PROP_SEQNUM_OFFSET:
475       g_value_set_int (value, basertppayload->seqnum_offset);
476       break;
477     case PROP_MAX_PTIME:
478       g_value_set_int64 (value, basertppayload->max_ptime);
479       break;
480     case PROP_TIMESTAMP:
481       g_value_set_uint (value, basertppayload->timestamp);
482       break;
483     case PROP_SEQNUM:
484       g_value_set_uint (value, basertppayload->seqnum);
485       break;
486     default:
487       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
488       break;
489   }
490 }
491
492 static GstStateChangeReturn
493 gst_basertppayload_change_state (GstElement * element,
494     GstStateChange transition)
495 {
496   GstBaseRTPPayload *basertppayload;
497   GstStateChangeReturn ret;
498
499   basertppayload = GST_BASE_RTP_PAYLOAD (element);
500
501   switch (transition) {
502     case GST_STATE_CHANGE_NULL_TO_READY:
503       break;
504     case GST_STATE_CHANGE_READY_TO_PAUSED:
505       gst_segment_init (&basertppayload->segment, GST_FORMAT_UNDEFINED);
506
507       if (basertppayload->seqnum_offset == -1)
508         basertppayload->seqnum_base =
509             g_rand_int_range (basertppayload->seq_rand, 0, G_MAXUINT16);
510       else
511         basertppayload->seqnum_base = basertppayload->seqnum_offset;
512       basertppayload->seqnum = basertppayload->seqnum_base;
513
514       if (basertppayload->ssrc == -1)
515         basertppayload->current_ssrc = g_rand_int (basertppayload->ssrc_rand);
516       else
517         basertppayload->current_ssrc = basertppayload->ssrc;
518
519       if (basertppayload->ts_offset == -1)
520         basertppayload->ts_base = g_rand_int (basertppayload->ts_rand);
521       else
522         basertppayload->ts_base = basertppayload->ts_offset;
523       break;
524     default:
525       break;
526   }
527
528   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
529
530   switch (transition) {
531     case GST_STATE_CHANGE_READY_TO_NULL:
532       break;
533     default:
534       break;
535   }
536   return ret;
537 }