Remove unused variables in _class_init
[platform/upstream/gstreamer.git] / gst / rtp / gstrtpmp4vpay.c
1 /* GStreamer
2  * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.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 details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <string.h>
25
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "gstrtpmp4vpay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug);
31 #define GST_CAT_DEFAULT (rtpmp4vpay_debug)
32
33 /* elementfactory information */
34 static const GstElementDetails gst_rtp_mp4vpay_details =
35 GST_ELEMENT_DETAILS ("RTP MPEG4 Video payloader",
36     "Codec/Payloader/Network",
37     "Payload MPEG-4 video as RTP packets (RFC 3016)",
38     "Wim Taymans <wim.taymans@gmail.com>");
39
40 static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template =
41 GST_STATIC_PAD_TEMPLATE ("sink",
42     GST_PAD_SINK,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS ("video/mpeg,"
45         "mpegversion=(int) 4," "systemstream=(boolean)false")
46     );
47
48 static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template =
49 GST_STATIC_PAD_TEMPLATE ("src",
50     GST_PAD_SRC,
51     GST_PAD_ALWAYS,
52     GST_STATIC_CAPS ("application/x-rtp, "
53         "media = (string) \"video\", "
54         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
55         "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\""
56         /* two string params
57          *
58          "profile-level-id = (string) [1,MAX]"
59          "config = (string) [1,MAX]"
60          */
61     )
62     );
63
64 #define DEFAULT_SEND_CONFIG     FALSE
65
66 enum
67 {
68   ARG_0,
69   ARG_SEND_CONFIG
70 };
71
72
73 static void gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass);
74 static void gst_rtp_mp4v_pay_base_init (GstRtpMP4VPayClass * klass);
75 static void gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay);
76 static void gst_rtp_mp4v_pay_finalize (GObject * object);
77
78 static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
79     const GValue * value, GParamSpec * pspec);
80 static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
81     GValue * value, GParamSpec * pspec);
82
83 static gboolean gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload,
84     GstCaps * caps);
85 static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload *
86     payload, GstBuffer * buffer);
87 static gboolean gst_rtp_mp4v_pay_event (GstPad * pad, GstEvent * event);
88
89 static GstBaseRTPPayloadClass *parent_class = NULL;
90
91 static GType
92 gst_rtp_mp4v_pay_get_type (void)
93 {
94   static GType rtpmp4vpay_type = 0;
95
96   if (!rtpmp4vpay_type) {
97     static const GTypeInfo rtpmp4vpay_info = {
98       sizeof (GstRtpMP4VPayClass),
99       (GBaseInitFunc) gst_rtp_mp4v_pay_base_init,
100       NULL,
101       (GClassInitFunc) gst_rtp_mp4v_pay_class_init,
102       NULL,
103       NULL,
104       sizeof (GstRtpMP4VPay),
105       0,
106       (GInstanceInitFunc) gst_rtp_mp4v_pay_init,
107     };
108
109     rtpmp4vpay_type =
110         g_type_register_static (GST_TYPE_BASE_RTP_PAYLOAD, "GstRtpMP4VPay",
111         &rtpmp4vpay_info, 0);
112   }
113   return rtpmp4vpay_type;
114 }
115
116 static void
117 gst_rtp_mp4v_pay_base_init (GstRtpMP4VPayClass * klass)
118 {
119   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
120
121   gst_element_class_add_pad_template (element_class,
122       gst_static_pad_template_get (&gst_rtp_mp4v_pay_src_template));
123   gst_element_class_add_pad_template (element_class,
124       gst_static_pad_template_get (&gst_rtp_mp4v_pay_sink_template));
125
126   gst_element_class_set_details (element_class, &gst_rtp_mp4vpay_details);
127 }
128
129 static void
130 gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass)
131 {
132   GObjectClass *gobject_class;
133   GstBaseRTPPayloadClass *gstbasertppayload_class;
134
135   gobject_class = (GObjectClass *) klass;
136   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
137
138   parent_class = g_type_class_peek_parent (klass);
139
140   gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
141   gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
142
143   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SEND_CONFIG,
144       g_param_spec_boolean ("send-config", "Send Config",
145           "Send the config parameters in RTP packets as well",
146           DEFAULT_SEND_CONFIG, G_PARAM_READWRITE));
147
148   gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
149
150   gstbasertppayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
151   gstbasertppayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
152
153   GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
154       "MP4 video RTP Payloader");
155
156 }
157
158 static void
159 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay)
160 {
161   GstPad *sinkpad;
162
163   rtpmp4vpay->adapter = gst_adapter_new ();
164   rtpmp4vpay->rate = 90000;
165   rtpmp4vpay->profile = 1;
166   rtpmp4vpay->send_config = DEFAULT_SEND_CONFIG;
167
168   rtpmp4vpay->config = NULL;
169
170   sinkpad = GST_BASE_RTP_PAYLOAD_SINKPAD (rtpmp4vpay);
171
172   rtpmp4vpay->old_event_func = sinkpad->eventfunc;
173   gst_pad_set_event_function (sinkpad, gst_rtp_mp4v_pay_event);
174 }
175
176 static void
177 gst_rtp_mp4v_pay_finalize (GObject * object)
178 {
179   GstRtpMP4VPay *rtpmp4vpay;
180
181   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
182
183   if (rtpmp4vpay->config) {
184     gst_buffer_unref (rtpmp4vpay->config);
185     rtpmp4vpay->config = NULL;
186   }
187   g_object_unref (rtpmp4vpay->adapter);
188   rtpmp4vpay->adapter = NULL;
189
190   G_OBJECT_CLASS (parent_class)->finalize (object);
191 }
192
193 static gboolean
194 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
195 {
196   gchar *profile, *config;
197   GValue v = { 0 };
198   gboolean res;
199
200   profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
201   g_value_init (&v, GST_TYPE_BUFFER);
202   gst_value_set_buffer (&v, rtpmp4vpay->config);
203   config = gst_value_serialize (&v);
204
205   res = gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4vpay),
206       "profile-level-id", G_TYPE_STRING, profile,
207       "config", G_TYPE_STRING, config, NULL);
208
209   g_value_unset (&v);
210
211   g_free (profile);
212   g_free (config);
213
214   return res;
215 }
216
217 static gboolean
218 gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
219 {
220   GstRtpMP4VPay *rtpmp4vpay;
221   GstStructure *structure;
222   const GValue *codec_data;
223   gboolean res;
224
225   rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
226
227   gst_basertppayload_set_options (payload, "video", TRUE, "MP4V-ES",
228       rtpmp4vpay->rate);
229
230   res = TRUE;
231
232   structure = gst_caps_get_structure (caps, 0);
233   codec_data = gst_structure_get_value (structure, "codec_data");
234   if (codec_data) {
235     GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
236     if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
237       GstBuffer *buffer;
238       guint8 *data;
239       guint size;
240
241       buffer = gst_value_get_buffer (codec_data);
242
243       data = GST_BUFFER_DATA (buffer);
244       size = GST_BUFFER_SIZE (buffer);
245
246       if (size < 5)
247         goto done;
248
249       rtpmp4vpay->profile = data[4];
250       GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
251           data[4]);
252
253       if (rtpmp4vpay->config)
254         gst_buffer_unref (rtpmp4vpay->config);
255       rtpmp4vpay->config = gst_buffer_copy (buffer);
256       res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
257     }
258   }
259
260 done:
261   return res;
262 }
263
264 static void
265 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
266 {
267   gst_adapter_clear (rtpmp4vpay->adapter);
268 }
269
270 static GstFlowReturn
271 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
272 {
273   guint avail;
274   GstBuffer *outbuf;
275   GstFlowReturn ret;
276
277   /* the data available in the adapter is either smaller
278    * than the MTU or bigger. In the case it is smaller, the complete
279    * adapter contents can be put in one packet. In the case the
280    * adapter has more than one MTU, we need to split the MP4V data
281    * over multiple packets. */
282   avail = gst_adapter_available (rtpmp4vpay->adapter);
283
284   if (rtpmp4vpay->config == NULL) {
285     /* when we don't have a config yet, flush things out */
286     gst_adapter_flush (rtpmp4vpay->adapter, avail);
287     avail = 0;
288   }
289
290   ret = GST_FLOW_OK;
291
292   while (avail > 0) {
293     guint towrite;
294     guint8 *payload;
295     guint payload_len;
296     guint packet_len;
297
298     /* this will be the total lenght of the packet */
299     packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
300
301     /* fill one MTU or all available bytes */
302     towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmp4vpay));
303
304     /* this is the payload length */
305     payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
306
307     /* create buffer to hold the payload */
308     outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
309
310     /* copy payload */
311     payload = gst_rtp_buffer_get_payload (outbuf);
312
313     gst_adapter_copy (rtpmp4vpay->adapter, payload, 0, payload_len);
314     gst_adapter_flush (rtpmp4vpay->adapter, payload_len);
315
316     avail -= payload_len;
317
318     gst_rtp_buffer_set_marker (outbuf, avail == 0);
319
320     GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp;
321
322     ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), outbuf);
323   }
324
325   return ret;
326 }
327
328 #define VOS_STARTCODE                   0x000001B0
329 #define VOS_ENDCODE                     0x000001B1
330 #define USER_DATA_STARTCODE             0x000001B2
331 #define GOP_STARTCODE                   0x000001B3
332 #define VISUAL_OBJECT_STARTCODE         0x000001B5
333 #define VOP_STARTCODE                   0x000001B6
334
335 static gboolean
336 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
337     gint * strip)
338 {
339   guint32 code;
340   gboolean result;
341
342   *strip = 0;
343
344   if (size < 5)
345     return FALSE;
346
347   code = GST_READ_UINT32_BE (data);
348   GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
349
350   switch (code) {
351     case VOS_STARTCODE:
352     case 0x00000101:
353     {
354       gint i;
355       guint8 profile;
356       gboolean newprofile = FALSE;
357       gboolean equal;
358
359       if (code == VOS_STARTCODE) {
360         /* profile_and_level_indication */
361         profile = data[4];
362
363         GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
364
365         if (profile != enc->profile) {
366           newprofile = TRUE;
367           enc->profile = profile;
368         }
369       }
370
371       /* up to the next GOP_STARTCODE or VOP_STARTCODE is
372        * the config information */
373       code = 0xffffffff;
374       for (i = 5; i < size - 4; i++) {
375         code = (code << 8) | data[i];
376         if (code == GOP_STARTCODE || code == VOP_STARTCODE)
377           break;
378       }
379       i -= 3;
380       /* see if config changed */
381       equal = FALSE;
382       if (enc->config) {
383         if (GST_BUFFER_SIZE (enc->config) == i) {
384           equal = memcmp (GST_BUFFER_DATA (enc->config), data, i) == 0;
385         }
386       }
387       /* if config string changed or new profile, make new caps */
388       if (!equal || newprofile) {
389         if (enc->config)
390           gst_buffer_unref (enc->config);
391         enc->config = gst_buffer_new_and_alloc (i);
392         memcpy (GST_BUFFER_DATA (enc->config), data, i);
393         gst_rtp_mp4v_pay_new_caps (enc);
394       }
395       *strip = i;
396       /* we need to flush out the current packet. */
397       result = TRUE;
398       break;
399     }
400     case VOP_STARTCODE:
401       GST_DEBUG_OBJECT (enc, "VOP");
402       /* VOP startcode, we don't have to flush the packet */
403       result = FALSE;
404       break;
405     default:
406       if (code >= 0x20 && code <= 0x2f) {
407         GST_DEBUG_OBJECT (enc, "short header");
408         result = FALSE;
409       } else {
410         GST_DEBUG_OBJECT (enc, "other startcode");
411         /* all other startcodes need a flush */
412         result = TRUE;
413       }
414       break;
415   }
416   return result;
417 }
418
419 /* we expect buffers starting on startcodes. 
420  */
421 static GstFlowReturn
422 gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload,
423     GstBuffer * buffer)
424 {
425   GstRtpMP4VPay *rtpmp4vpay;
426   GstFlowReturn ret;
427   guint size, avail;
428   guint packet_len;
429   guint8 *data;
430   gboolean flush;
431   gint strip;
432   GstClockTime timestamp, duration;
433
434   ret = GST_FLOW_OK;
435
436   rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
437
438   size = GST_BUFFER_SIZE (buffer);
439   data = GST_BUFFER_DATA (buffer);
440   timestamp = GST_BUFFER_TIMESTAMP (buffer);
441   duration = GST_BUFFER_DURATION (buffer);
442   avail = gst_adapter_available (rtpmp4vpay->adapter);
443
444   if (duration == -1)
445     duration = 0;
446
447   /* empty buffer, take timestamp */
448   if (avail == 0) {
449     rtpmp4vpay->first_timestamp = timestamp;
450     rtpmp4vpay->duration = 0;
451   }
452
453   /* depay incomming data and see if we need to start a new RTP
454    * packet */
455   flush = gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, data, size, &strip);
456   if (strip) {
457     /* strip off config if requested */
458     if (!rtpmp4vpay->send_config) {
459       GstBuffer *subbuf;
460
461       GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
462           size - strip);
463
464       /* strip off header */
465       subbuf = gst_buffer_create_sub (buffer, strip, size - strip);
466       GST_BUFFER_TIMESTAMP (subbuf) = timestamp;
467       gst_buffer_unref (buffer);
468       buffer = subbuf;
469
470       size = GST_BUFFER_SIZE (buffer);
471       data = GST_BUFFER_DATA (buffer);
472     }
473   }
474
475   /* if we need to flush, do so now */
476   if (flush) {
477     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
478     rtpmp4vpay->first_timestamp = timestamp;
479     rtpmp4vpay->duration = 0;
480     avail = 0;
481   }
482
483   /* get packet length of data and see if we exceeded MTU. */
484   packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
485
486   if (gst_basertppayload_is_filled (basepayload,
487           packet_len, rtpmp4vpay->duration + duration)) {
488     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
489     rtpmp4vpay->first_timestamp = timestamp;
490     rtpmp4vpay->duration = 0;
491   }
492
493   /* push new data */
494   gst_adapter_push (rtpmp4vpay->adapter, buffer);
495
496   rtpmp4vpay->duration += duration;
497
498   return ret;
499 }
500
501 static gboolean
502 gst_rtp_mp4v_pay_event (GstPad * pad, GstEvent * event)
503 {
504   GstRtpMP4VPay *rtpmp4vpay;
505   gboolean ret;
506
507   rtpmp4vpay = GST_RTP_MP4V_PAY (gst_pad_get_parent (pad));
508
509   GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
510
511   switch (GST_EVENT_TYPE (event)) {
512     case GST_EVENT_NEWSEGMENT:
513     case GST_EVENT_EOS:
514       /* This flush call makes sure that the last buffer is always pushed
515        * to the base payloader */
516       gst_rtp_mp4v_pay_flush (rtpmp4vpay);
517       break;
518     case GST_EVENT_FLUSH_STOP:
519       gst_rtp_mp4v_pay_empty (rtpmp4vpay);
520       break;
521     default:
522       break;
523   }
524
525   ret = rtpmp4vpay->old_event_func (pad, event);
526
527   g_object_unref (rtpmp4vpay);
528
529   return ret;
530 }
531
532 static void
533 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
534     const GValue * value, GParamSpec * pspec)
535 {
536   GstRtpMP4VPay *rtpmp4vpay;
537
538   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
539
540   switch (prop_id) {
541     case ARG_SEND_CONFIG:
542       rtpmp4vpay->send_config = g_value_get_boolean (value);
543       break;
544     default:
545       break;
546   }
547 }
548
549 static void
550 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
551     GValue * value, GParamSpec * pspec)
552 {
553   GstRtpMP4VPay *rtpmp4vpay;
554
555   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
556
557   switch (prop_id) {
558     case ARG_SEND_CONFIG:
559       g_value_set_boolean (value, rtpmp4vpay->send_config);
560       break;
561     default:
562       break;
563   }
564 }
565
566 gboolean
567 gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
568 {
569   return gst_element_register (plugin, "rtpmp4vpay",
570       GST_RANK_NONE, GST_TYPE_RTP_MP4V_PAY);
571 }