rtp: Update codes based on 1.18.4
[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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, 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 #include <gst/video/video.h>
28
29 #include "gstrtpmp4vpay.h"
30 #include "gstrtputils.h"
31
32 GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug);
33 #define GST_CAT_DEFAULT (rtpmp4vpay_debug)
34
35 static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template =
36     GST_STATIC_PAD_TEMPLATE ("sink",
37     GST_PAD_SINK,
38     GST_PAD_ALWAYS,
39     GST_STATIC_CAPS ("video/mpeg,"
40         "mpegversion=(int) 4, systemstream=(boolean)false;" "video/x-divx")
41     );
42
43 static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template =
44 GST_STATIC_PAD_TEMPLATE ("src",
45     GST_PAD_SRC,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS ("application/x-rtp, "
48         "media = (string) \"video\", "
49         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
50         "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\""
51         /* two string params
52          *
53          "profile-level-id = (string) [1,MAX]"
54          "config = (string) [1,MAX]"
55          */
56     )
57     );
58
59 #define DEFAULT_CONFIG_INTERVAL 0
60
61 enum
62 {
63   PROP_0,
64   PROP_CONFIG_INTERVAL
65 };
66
67
68 static void gst_rtp_mp4v_pay_finalize (GObject * object);
69
70 static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
71     const GValue * value, GParamSpec * pspec);
72 static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec);
74
75 static gboolean gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload,
76     GstCaps * caps);
77 static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload *
78     payload, GstBuffer * buffer);
79 static gboolean gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay,
80     GstEvent * event);
81
82 #define gst_rtp_mp4v_pay_parent_class parent_class
83 G_DEFINE_TYPE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GST_TYPE_RTP_BASE_PAYLOAD);
84
85 static void
86 gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass)
87 {
88   GObjectClass *gobject_class;
89   GstElementClass *gstelement_class;
90   GstRTPBasePayloadClass *gstrtpbasepayload_class;
91
92   gobject_class = (GObjectClass *) klass;
93   gstelement_class = (GstElementClass *) klass;
94   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
95
96   gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
97   gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
98
99   gst_element_class_add_static_pad_template (gstelement_class,
100       &gst_rtp_mp4v_pay_src_template);
101   gst_element_class_add_static_pad_template (gstelement_class,
102       &gst_rtp_mp4v_pay_sink_template);
103
104   gst_element_class_set_static_metadata (gstelement_class,
105       "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP",
106       "Payload MPEG-4 video as RTP packets (RFC 3016)",
107       "Wim Taymans <wim.taymans@gmail.com>");
108
109   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
110       g_param_spec_int ("config-interval", "Config Send Interval",
111           "Send Config Insertion Interval in seconds (configuration headers "
112           "will be multiplexed in the data stream when detected.) "
113           "(0 = disabled, -1 = send with every IDR frame)",
114           -1, 3600, DEFAULT_CONFIG_INTERVAL,
115           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
116       );
117
118   gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
119
120   gstrtpbasepayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
121   gstrtpbasepayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
122   gstrtpbasepayload_class->sink_event = gst_rtp_mp4v_pay_sink_event;
123
124   GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
125       "MP4 video RTP Payloader");
126 }
127
128 static void
129 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay)
130 {
131   rtpmp4vpay->adapter = gst_adapter_new ();
132   rtpmp4vpay->rate = 90000;
133   rtpmp4vpay->profile = 1;
134   rtpmp4vpay->need_config = TRUE;
135   rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL;
136   rtpmp4vpay->last_config = -1;
137
138   rtpmp4vpay->config = NULL;
139 }
140
141 static void
142 gst_rtp_mp4v_pay_finalize (GObject * object)
143 {
144   GstRtpMP4VPay *rtpmp4vpay;
145
146   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
147
148   if (rtpmp4vpay->config) {
149     gst_buffer_unref (rtpmp4vpay->config);
150     rtpmp4vpay->config = NULL;
151   }
152   g_object_unref (rtpmp4vpay->adapter);
153   rtpmp4vpay->adapter = NULL;
154
155   G_OBJECT_CLASS (parent_class)->finalize (object);
156 }
157
158 static gboolean
159 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
160 {
161   gchar *profile, *config;
162   GValue v = { 0 };
163   gboolean res;
164
165   profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
166   g_value_init (&v, GST_TYPE_BUFFER);
167   gst_value_set_buffer (&v, rtpmp4vpay->config);
168   config = gst_value_serialize (&v);
169
170   res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4vpay),
171       "profile-level-id", G_TYPE_STRING, profile,
172       "config", G_TYPE_STRING, config, NULL);
173
174   g_value_unset (&v);
175
176   g_free (profile);
177   g_free (config);
178
179   return res;
180 }
181
182 static gboolean
183 gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
184 {
185   GstRtpMP4VPay *rtpmp4vpay;
186   GstStructure *structure;
187   const GValue *codec_data;
188   gboolean res;
189
190   rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
191
192   gst_rtp_base_payload_set_options (payload, "video", TRUE, "MP4V-ES",
193       rtpmp4vpay->rate);
194
195   res = TRUE;
196
197   structure = gst_caps_get_structure (caps, 0);
198   codec_data = gst_structure_get_value (structure, "codec_data");
199   if (codec_data) {
200     GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
201     if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
202       GstBuffer *buffer;
203
204       buffer = gst_value_get_buffer (codec_data);
205
206       if (gst_buffer_get_size (buffer) < 5)
207         goto done;
208
209       gst_buffer_extract (buffer, 4, &rtpmp4vpay->profile, 1);
210       GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
211           rtpmp4vpay->profile);
212
213       if (rtpmp4vpay->config)
214         gst_buffer_unref (rtpmp4vpay->config);
215       rtpmp4vpay->config = gst_buffer_copy (buffer);
216       res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
217     }
218   }
219
220 done:
221   return res;
222 }
223
224 static void
225 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
226 {
227   gst_adapter_clear (rtpmp4vpay->adapter);
228 }
229
230 #define RTP_HEADER_LEN 12
231
232 static GstFlowReturn
233 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
234 {
235   guint avail, mtu;
236   GstBuffer *outbuf;
237   GstBuffer *outbuf_data = NULL;
238   GstFlowReturn ret;
239   GstBufferList *list = NULL;
240
241   /* the data available in the adapter is either smaller
242    * than the MTU or bigger. In the case it is smaller, the complete
243    * adapter contents can be put in one packet. In the case the
244    * adapter has more than one MTU, we need to split the MP4V data
245    * over multiple packets. */
246   avail = gst_adapter_available (rtpmp4vpay->adapter);
247
248   if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) {
249     /* when we don't have a config yet, flush things out */
250     gst_adapter_flush (rtpmp4vpay->adapter, avail);
251     avail = 0;
252   }
253
254   if (!avail)
255     return GST_FLOW_OK;
256
257   mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4vpay);
258
259   /* Use buffer lists. Each frame will be put into a list
260    * of buffers and the whole list will be pushed downstream
261    * at once */
262   list = gst_buffer_list_new_sized ((avail / (mtu - RTP_HEADER_LEN)) + 1);
263
264   while (avail > 0) {
265     guint towrite;
266     guint payload_len;
267     guint packet_len;
268     GstRTPBuffer rtp = { NULL };
269
270     /* this will be the total length of the packet */
271     packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
272
273     /* fill one MTU or all available bytes */
274     towrite = MIN (packet_len, mtu);
275
276     /* this is the payload length */
277     payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
278
279     /* create buffer without payload. The payload will be put
280      * in next buffer instead. Both buffers will be merged */
281     outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
282
283     /* Take buffer with the payload from the adapter */
284     outbuf_data = gst_adapter_take_buffer_fast (rtpmp4vpay->adapter,
285         payload_len);
286
287     avail -= payload_len;
288
289     gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
290     gst_rtp_buffer_set_marker (&rtp, avail == 0);
291     gst_rtp_buffer_unmap (&rtp);
292     gst_rtp_copy_video_meta (rtpmp4vpay, outbuf, outbuf_data);
293     outbuf = gst_buffer_append (outbuf, outbuf_data);
294
295     GST_BUFFER_PTS (outbuf) = rtpmp4vpay->first_timestamp;
296
297     /* add to list */
298     gst_buffer_list_insert (list, -1, outbuf);
299   }
300
301   /* push the whole buffer list at once */
302   ret =
303       gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmp4vpay), list);
304
305   return ret;
306 }
307
308 #define VOS_STARTCODE                   0x000001B0
309 #define VOS_ENDCODE                     0x000001B1
310 #define USER_DATA_STARTCODE             0x000001B2
311 #define GOP_STARTCODE                   0x000001B3
312 #define VISUAL_OBJECT_STARTCODE         0x000001B5
313 #define VOP_STARTCODE                   0x000001B6
314
315 static gboolean
316 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
317     gint * strip, gboolean * vopi)
318 {
319   guint32 code;
320   gboolean result;
321   *vopi = FALSE;
322
323   *strip = 0;
324
325   if (size < 5)
326     return FALSE;
327
328   code = GST_READ_UINT32_BE (data);
329   GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
330
331   switch (code) {
332     case VOS_STARTCODE:
333     case 0x00000101:
334     {
335       gint i;
336       guint8 profile;
337       gboolean newprofile = FALSE;
338       gboolean equal;
339
340       if (code == VOS_STARTCODE) {
341         /* profile_and_level_indication */
342         profile = data[4];
343
344         GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
345
346         if (profile != enc->profile) {
347           newprofile = TRUE;
348           enc->profile = profile;
349         }
350       }
351
352       /* up to the next GOP_STARTCODE or VOP_STARTCODE is
353        * the config information */
354       code = 0xffffffff;
355       for (i = 5; i < size - 4; i++) {
356         code = (code << 8) | data[i];
357         if (code == GOP_STARTCODE || code == VOP_STARTCODE)
358           break;
359       }
360       i -= 3;
361       /* see if config changed */
362       equal = FALSE;
363       if (enc->config) {
364         if (gst_buffer_get_size (enc->config) == i) {
365           equal = gst_buffer_memcmp (enc->config, 0, data, i) == 0;
366         }
367       }
368       /* if config string changed or new profile, make new caps */
369       if (!equal || newprofile) {
370         if (enc->config)
371           gst_buffer_unref (enc->config);
372         enc->config = gst_buffer_new_and_alloc (i);
373
374         gst_buffer_fill (enc->config, 0, data, i);
375
376         gst_rtp_mp4v_pay_new_caps (enc);
377       }
378       *strip = i;
379       /* we need to flush out the current packet. */
380       result = TRUE;
381       break;
382     }
383     case VOP_STARTCODE:
384       GST_DEBUG_OBJECT (enc, "VOP");
385       /* VOP startcode, we don't have to flush the packet */
386       result = FALSE;
387       /* vop-coding-type == I-frame */
388       if (size > 4 && (data[4] >> 6 == 0)) {
389         GST_DEBUG_OBJECT (enc, "VOP-I");
390         *vopi = TRUE;
391       }
392       break;
393     case GOP_STARTCODE:
394       GST_DEBUG_OBJECT (enc, "GOP");
395       *vopi = TRUE;
396       result = TRUE;
397       break;
398     case 0x00000100:
399       enc->need_config = FALSE;
400       result = TRUE;
401       break;
402     default:
403       if (code >= 0x20 && code <= 0x2f) {
404         GST_DEBUG_OBJECT (enc, "short header");
405         result = FALSE;
406       } else {
407         GST_DEBUG_OBJECT (enc, "other startcode");
408         /* all other startcodes need a flush */
409         result = TRUE;
410       }
411       break;
412   }
413   return result;
414 }
415
416 /* we expect buffers starting on startcodes.
417  */
418 static GstFlowReturn
419 gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload * basepayload,
420     GstBuffer * buffer)
421 {
422   GstRtpMP4VPay *rtpmp4vpay;
423   GstFlowReturn ret;
424   guint avail;
425   guint packet_len;
426   GstMapInfo map;
427   gsize size;
428   gboolean flush;
429   gint strip;
430   GstClockTime timestamp, duration;
431   gboolean vopi;
432   gboolean send_config;
433   GstClockTime running_time = GST_CLOCK_TIME_NONE;
434
435   ret = GST_FLOW_OK;
436   send_config = FALSE;
437
438   rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
439
440   gst_buffer_map (buffer, &map, GST_MAP_READ);
441   size = map.size;
442   timestamp = GST_BUFFER_PTS (buffer);
443   duration = GST_BUFFER_DURATION (buffer);
444   avail = gst_adapter_available (rtpmp4vpay->adapter);
445
446   if (duration == -1)
447     duration = 0;
448
449   /* empty buffer, take timestamp */
450   if (avail == 0) {
451     rtpmp4vpay->first_timestamp = timestamp;
452     rtpmp4vpay->duration = 0;
453   }
454
455   /* depay incoming data and see if we need to start a new RTP
456    * packet */
457   flush =
458       gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, map.data, size, &strip, &vopi);
459   gst_buffer_unmap (buffer, &map);
460
461   if (strip) {
462     /* strip off config if requested, do not strip off if the
463      * config_interval is set to -1 */
464     if (!(rtpmp4vpay->config_interval > 0)
465         && !(rtpmp4vpay->config_interval == -1)) {
466       GstBuffer *subbuf;
467
468       GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
469           (gint) size - strip);
470
471       /* strip off header */
472       subbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, strip,
473           size - strip);
474       GST_BUFFER_PTS (subbuf) = timestamp;
475       gst_buffer_unref (buffer);
476       buffer = subbuf;
477
478       size = gst_buffer_get_size (buffer);
479     } else {
480       running_time =
481           gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
482           timestamp);
483
484       GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
485       rtpmp4vpay->last_config = running_time;
486     }
487   }
488
489   /* there is a config request, see if we need to insert it */
490   if (vopi && (rtpmp4vpay->config_interval > 0) && rtpmp4vpay->config) {
491     running_time =
492         gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
493         timestamp);
494
495     if (rtpmp4vpay->last_config != -1) {
496       guint64 diff;
497
498       GST_LOG_OBJECT (rtpmp4vpay,
499           "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
500           GST_TIME_ARGS (running_time),
501           GST_TIME_ARGS (rtpmp4vpay->last_config));
502
503       /* calculate diff between last config in milliseconds */
504       if (running_time > rtpmp4vpay->last_config) {
505         diff = running_time - rtpmp4vpay->last_config;
506       } else {
507         diff = 0;
508       }
509
510       GST_DEBUG_OBJECT (rtpmp4vpay,
511           "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
512
513       /* bigger than interval, queue config */
514       if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) {
515         GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config");
516         send_config = TRUE;
517       }
518     } else {
519       /* no known previous config time, send now */
520       GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now");
521       send_config = TRUE;
522     }
523   }
524
525   if (vopi && (rtpmp4vpay->config_interval == -1)) {
526     GST_DEBUG_OBJECT (rtpmp4vpay, "sending config before current IDR frame");
527     /* send config before every IDR frame */
528     send_config = TRUE;
529   }
530
531   if (send_config) {
532     /* we need to send config now first */
533     GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream");
534
535     /* insert header */
536     buffer = gst_buffer_append (gst_buffer_ref (rtpmp4vpay->config), buffer);
537
538     GST_BUFFER_PTS (buffer) = timestamp;
539     size = gst_buffer_get_size (buffer);
540
541     if (running_time != -1) {
542       rtpmp4vpay->last_config = running_time;
543     }
544   }
545
546   /* if we need to flush, do so now */
547   if (flush) {
548     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
549     rtpmp4vpay->first_timestamp = timestamp;
550     rtpmp4vpay->duration = 0;
551     avail = 0;
552   }
553
554   /* get packet length of data and see if we exceeded MTU. */
555   packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
556
557   if (gst_rtp_base_payload_is_filled (basepayload,
558           packet_len, rtpmp4vpay->duration + duration)) {
559     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
560     rtpmp4vpay->first_timestamp = timestamp;
561     rtpmp4vpay->duration = 0;
562   }
563
564   /* push new data */
565   gst_adapter_push (rtpmp4vpay->adapter, buffer);
566
567   rtpmp4vpay->duration += duration;
568
569   return ret;
570 }
571
572 static gboolean
573 gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay, GstEvent * event)
574 {
575   GstRtpMP4VPay *rtpmp4vpay;
576
577   rtpmp4vpay = GST_RTP_MP4V_PAY (pay);
578
579   GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
580
581   switch (GST_EVENT_TYPE (event)) {
582     case GST_EVENT_SEGMENT:
583     case GST_EVENT_EOS:
584       /* This flush call makes sure that the last buffer is always pushed
585        * to the base payloader */
586       gst_rtp_mp4v_pay_flush (rtpmp4vpay);
587       break;
588     case GST_EVENT_FLUSH_STOP:
589       gst_rtp_mp4v_pay_empty (rtpmp4vpay);
590       break;
591     default:
592       break;
593   }
594
595   /* let parent handle event too */
596   return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pay, event);
597 }
598
599 static void
600 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
601     const GValue * value, GParamSpec * pspec)
602 {
603   GstRtpMP4VPay *rtpmp4vpay;
604
605   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
606
607   switch (prop_id) {
608     case PROP_CONFIG_INTERVAL:
609       rtpmp4vpay->config_interval = g_value_get_int (value);
610       break;
611     default:
612       break;
613   }
614 }
615
616 static void
617 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
618     GValue * value, GParamSpec * pspec)
619 {
620   GstRtpMP4VPay *rtpmp4vpay;
621
622   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
623
624   switch (prop_id) {
625     case PROP_CONFIG_INTERVAL:
626       g_value_set_int (value, rtpmp4vpay->config_interval);
627       break;
628     default:
629       break;
630   }
631 }
632
633 gboolean
634 gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
635 {
636   /* Note: This element is marked at a "+1" rank to make sure that
637    * auto-plugging of payloaders for MPEG4 elementary streams don't
638    * end up using the 'rtpmp4gpay' element (generic mpeg4) which isn't
639    * as well supported as this RFC */
640   return gst_element_register (plugin, "rtpmp4vpay",
641       GST_RANK_SECONDARY + 1, GST_TYPE_RTP_MP4V_PAY);
642 }