gst/rtp/gstrtpmp4vpay.c: Don't try to push packets before we could find a valid confi...
[platform/upstream/gst-plugins-good.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   GstElementClass *gstelement_class;
134   GstBaseRTPPayloadClass *gstbasertppayload_class;
135
136   gobject_class = (GObjectClass *) klass;
137   gstelement_class = (GstElementClass *) klass;
138   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
139
140   parent_class = g_type_class_peek_parent (klass);
141
142   gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
143   gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
144
145   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SEND_CONFIG,
146       g_param_spec_boolean ("send-config", "Send Config",
147           "Send the config parameters in RTP packets as well",
148           DEFAULT_SEND_CONFIG, G_PARAM_READWRITE));
149
150   gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
151
152   gstbasertppayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
153   gstbasertppayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
154
155   GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
156       "MP4 video RTP Payloader");
157
158 }
159
160 static void
161 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay)
162 {
163   GstPad *sinkpad;
164
165   rtpmp4vpay->adapter = gst_adapter_new ();
166   rtpmp4vpay->rate = 90000;
167   rtpmp4vpay->profile = 1;
168   rtpmp4vpay->send_config = DEFAULT_SEND_CONFIG;
169
170   rtpmp4vpay->config = NULL;
171
172   sinkpad = GST_BASE_RTP_PAYLOAD_SINKPAD (rtpmp4vpay);
173
174   rtpmp4vpay->old_event_func = sinkpad->eventfunc;
175   gst_pad_set_event_function (sinkpad, gst_rtp_mp4v_pay_event);
176 }
177
178 static void
179 gst_rtp_mp4v_pay_finalize (GObject * object)
180 {
181   GstRtpMP4VPay *rtpmp4vpay;
182
183   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
184
185   if (rtpmp4vpay->config) {
186     gst_buffer_unref (rtpmp4vpay->config);
187     rtpmp4vpay->config = NULL;
188   }
189   g_object_unref (rtpmp4vpay->adapter);
190   rtpmp4vpay->adapter = NULL;
191
192   G_OBJECT_CLASS (parent_class)->finalize (object);
193 }
194
195 static gboolean
196 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
197 {
198   gchar *profile, *config;
199   GValue v = { 0 };
200   gboolean res;
201
202   profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
203   g_value_init (&v, GST_TYPE_BUFFER);
204   gst_value_set_buffer (&v, rtpmp4vpay->config);
205   config = gst_value_serialize (&v);
206
207   res = gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4vpay),
208       "profile-level-id", G_TYPE_STRING, profile,
209       "config", G_TYPE_STRING, config, NULL);
210
211   g_value_unset (&v);
212
213   g_free (profile);
214   g_free (config);
215
216   return res;
217 }
218
219 static gboolean
220 gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
221 {
222   GstRtpMP4VPay *rtpmp4vpay;
223   GstStructure *structure;
224   const GValue *codec_data;
225   gboolean res;
226
227   rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
228
229   gst_basertppayload_set_options (payload, "video", TRUE, "MP4V-ES",
230       rtpmp4vpay->rate);
231
232   res = TRUE;
233
234   structure = gst_caps_get_structure (caps, 0);
235   codec_data = gst_structure_get_value (structure, "codec_data");
236   if (codec_data) {
237     GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
238     if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
239       GstBuffer *buffer;
240       guint8 *data;
241       guint size;
242
243       buffer = gst_value_get_buffer (codec_data);
244
245       data = GST_BUFFER_DATA (buffer);
246       size = GST_BUFFER_SIZE (buffer);
247
248       if (size < 5)
249         goto done;
250
251       rtpmp4vpay->profile = data[4];
252       GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
253           data[4]);
254
255       if (rtpmp4vpay->config)
256         gst_buffer_unref (rtpmp4vpay->config);
257       rtpmp4vpay->config = gst_buffer_copy (buffer);
258       res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
259     }
260   }
261
262 done:
263   return res;
264 }
265
266 static void
267 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
268 {
269   gst_adapter_clear (rtpmp4vpay->adapter);
270 }
271
272 static GstFlowReturn
273 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
274 {
275   guint avail;
276   GstBuffer *outbuf;
277   GstFlowReturn ret;
278
279   /* the data available in the adapter is either smaller
280    * than the MTU or bigger. In the case it is smaller, the complete
281    * adapter contents can be put in one packet. In the case the
282    * adapter has more than one MTU, we need to split the MP4V data
283    * over multiple packets. */
284   avail = gst_adapter_available (rtpmp4vpay->adapter);
285
286   if (rtpmp4vpay->config == NULL) {
287     /* when we don't have a config yet, flush things out */
288     gst_adapter_flush (rtpmp4vpay->adapter, avail);
289     avail = 0;
290   }
291
292   ret = GST_FLOW_OK;
293
294   while (avail > 0) {
295     guint towrite;
296     guint8 *payload;
297     guint payload_len;
298     guint packet_len;
299
300     /* this will be the total lenght of the packet */
301     packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
302
303     /* fill one MTU or all available bytes */
304     towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmp4vpay));
305
306     /* this is the payload length */
307     payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
308
309     /* create buffer to hold the payload */
310     outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
311
312     /* copy payload */
313     payload = gst_rtp_buffer_get_payload (outbuf);
314
315     gst_adapter_copy (rtpmp4vpay->adapter, payload, 0, payload_len);
316     gst_adapter_flush (rtpmp4vpay->adapter, payload_len);
317
318     avail -= payload_len;
319
320     gst_rtp_buffer_set_marker (outbuf, avail == 0);
321
322     GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp;
323
324     ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), outbuf);
325   }
326
327   return ret;
328 }
329
330 #define VOS_STARTCODE                   0x000001B0
331 #define VOS_ENDCODE                     0x000001B1
332 #define USER_DATA_STARTCODE             0x000001B2
333 #define GOP_STARTCODE                   0x000001B3
334 #define VISUAL_OBJECT_STARTCODE         0x000001B5
335 #define VOP_STARTCODE                   0x000001B6
336
337 static gboolean
338 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
339     gint * strip)
340 {
341   guint32 code;
342   gboolean result;
343
344   *strip = 0;
345
346   if (size < 5)
347     return FALSE;
348
349   code = GST_READ_UINT32_BE (data);
350   GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
351
352   switch (code) {
353     case VOS_STARTCODE:
354     {
355       gint i;
356       guint8 profile;
357       gboolean newprofile = FALSE;
358       gboolean equal;
359
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       /* up to the next GOP_STARTCODE or VOP_STARTCODE is
371        * the config information */
372       code = 0xffffffff;
373       for (i = 5; i < size - 4; i++) {
374         code = (code << 8) | data[i];
375         if (code == GOP_STARTCODE || code == VOP_STARTCODE)
376           break;
377       }
378       i -= 3;
379       /* see if config changed */
380       equal = FALSE;
381       if (enc->config) {
382         if (GST_BUFFER_SIZE (enc->config) == i) {
383           equal = memcmp (GST_BUFFER_DATA (enc->config), data, i) == 0;
384         }
385       }
386       /* if config string changed or new profile, make new caps */
387       if (!equal || newprofile) {
388         if (enc->config)
389           gst_buffer_unref (enc->config);
390         enc->config = gst_buffer_new_and_alloc (i);
391         memcpy (GST_BUFFER_DATA (enc->config), data, i);
392         gst_rtp_mp4v_pay_new_caps (enc);
393       }
394       *strip = i;
395       /* we need to flush out the current packet. */
396       result = TRUE;
397       break;
398     }
399     case VOP_STARTCODE:
400       GST_DEBUG_OBJECT (enc, "VOP");
401       /* VOP startcode, we don't have to flush the packet */
402       result = FALSE;
403       break;
404     default:
405       GST_DEBUG_OBJECT (enc, "other startcode");
406       /* all other startcodes need a flush */
407       result = TRUE;
408       break;
409   }
410   return result;
411 }
412
413 /* we expect buffers starting on startcodes. 
414  */
415 static GstFlowReturn
416 gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload,
417     GstBuffer * buffer)
418 {
419   GstRtpMP4VPay *rtpmp4vpay;
420   GstFlowReturn ret;
421   guint size, avail;
422   guint packet_len;
423   guint8 *data;
424   gboolean flush;
425   gint strip;
426   GstClockTime timestamp, duration;
427
428   ret = GST_FLOW_OK;
429
430   rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
431
432   size = GST_BUFFER_SIZE (buffer);
433   data = GST_BUFFER_DATA (buffer);
434   timestamp = GST_BUFFER_TIMESTAMP (buffer);
435   duration = GST_BUFFER_DURATION (buffer);
436   avail = gst_adapter_available (rtpmp4vpay->adapter);
437
438   if (duration == -1)
439     duration = 0;
440
441   /* empty buffer, take timestamp */
442   if (avail == 0) {
443     rtpmp4vpay->first_timestamp = timestamp;
444     rtpmp4vpay->duration = 0;
445   }
446
447   /* depay incomming data and see if we need to start a new RTP
448    * packet */
449   flush = gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, data, size, &strip);
450   if (strip) {
451     /* strip off config if requested */
452     if (!rtpmp4vpay->send_config) {
453       GstBuffer *subbuf;
454
455       /* strip off header */
456       subbuf = gst_buffer_create_sub (buffer, strip, size - strip);
457       GST_BUFFER_TIMESTAMP (subbuf) = timestamp;
458       gst_buffer_unref (buffer);
459       buffer = subbuf;
460
461       size = GST_BUFFER_SIZE (buffer);
462       data = GST_BUFFER_DATA (buffer);
463     }
464   }
465
466   /* if we need to flush, do so now */
467   if (flush) {
468     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
469     rtpmp4vpay->first_timestamp = timestamp;
470     rtpmp4vpay->duration = 0;
471     avail = 0;
472   }
473
474   /* get packet length of data and see if we exceeded MTU. */
475   packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
476
477   if (gst_basertppayload_is_filled (basepayload,
478           packet_len, rtpmp4vpay->duration + duration)) {
479     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
480     rtpmp4vpay->first_timestamp = timestamp;
481     rtpmp4vpay->duration = 0;
482   }
483
484   /* push new data */
485   gst_adapter_push (rtpmp4vpay->adapter, buffer);
486
487   rtpmp4vpay->duration += duration;
488
489   return ret;
490 }
491
492 static gboolean
493 gst_rtp_mp4v_pay_event (GstPad * pad, GstEvent * event)
494 {
495   GstRtpMP4VPay *rtpmp4vpay;
496   gboolean ret;
497
498   rtpmp4vpay = GST_RTP_MP4V_PAY (gst_pad_get_parent (pad));
499
500   GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
501
502   switch (GST_EVENT_TYPE (event)) {
503     case GST_EVENT_NEWSEGMENT:
504     case GST_EVENT_EOS:
505       /* This flush call makes sure that the last buffer is always pushed
506        * to the base payloader */
507       gst_rtp_mp4v_pay_flush (rtpmp4vpay);
508       break;
509     case GST_EVENT_FLUSH_STOP:
510       gst_rtp_mp4v_pay_empty (rtpmp4vpay);
511       break;
512     default:
513       break;
514   }
515
516   ret = rtpmp4vpay->old_event_func (pad, event);
517
518   g_object_unref (rtpmp4vpay);
519
520   return ret;
521 }
522
523 static void
524 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
525     const GValue * value, GParamSpec * pspec)
526 {
527   GstRtpMP4VPay *rtpmp4vpay;
528
529   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
530
531   switch (prop_id) {
532     case ARG_SEND_CONFIG:
533       rtpmp4vpay->send_config = g_value_get_boolean (value);
534       break;
535     default:
536       break;
537   }
538 }
539
540 static void
541 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
542     GValue * value, GParamSpec * pspec)
543 {
544   GstRtpMP4VPay *rtpmp4vpay;
545
546   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
547
548   switch (prop_id) {
549     case ARG_SEND_CONFIG:
550       g_value_set_boolean (value, rtpmp4vpay->send_config);
551       break;
552     default:
553       break;
554   }
555 }
556
557 gboolean
558 gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
559 {
560   return gst_element_register (plugin, "rtpmp4vpay",
561       GST_RANK_NONE, GST_TYPE_RTP_MP4V_PAY);
562 }