2 * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com>
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.
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.
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.
26 #include <gst/rtp/gstrtpbuffer.h>
28 #include "gstrtpmp4vpay.h"
30 GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug);
31 #define GST_CAT_DEFAULT (rtpmp4vpay_debug)
33 static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template =
34 GST_STATIC_PAD_TEMPLATE ("sink",
37 GST_STATIC_CAPS ("video/mpeg,"
38 "mpegversion=(int) 4," "systemstream=(boolean)false;" "video/x-xvid")
41 static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template =
42 GST_STATIC_PAD_TEMPLATE ("src",
45 GST_STATIC_CAPS ("application/x-rtp, "
46 "media = (string) \"video\", "
47 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
48 "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\""
51 "profile-level-id = (string) [1,MAX]"
52 "config = (string) [1,MAX]"
57 #define DEFAULT_SEND_CONFIG FALSE
58 #define DEFAULT_BUFFER_LIST FALSE
59 #define DEFAULT_CONFIG_INTERVAL 0
70 static void gst_rtp_mp4v_pay_finalize (GObject * object);
72 static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
73 const GValue * value, GParamSpec * pspec);
74 static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
75 GValue * value, GParamSpec * pspec);
77 static gboolean gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload,
79 static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload *
80 payload, GstBuffer * buffer);
81 static gboolean gst_rtp_mp4v_pay_handle_event (GstPad * pad, GstEvent * event);
83 GST_BOILERPLATE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GstBaseRTPPayload,
84 GST_TYPE_BASE_RTP_PAYLOAD)
86 static void gst_rtp_mp4v_pay_base_init (gpointer klass)
88 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
90 gst_element_class_add_static_pad_template (element_class,
91 &gst_rtp_mp4v_pay_src_template);
92 gst_element_class_add_static_pad_template (element_class,
93 &gst_rtp_mp4v_pay_sink_template);
95 gst_element_class_set_details_simple (element_class,
96 "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP",
97 "Payload MPEG-4 video as RTP packets (RFC 3016)",
98 "Wim Taymans <wim.taymans@gmail.com>");
102 gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass)
104 GObjectClass *gobject_class;
105 GstBaseRTPPayloadClass *gstbasertppayload_class;
107 gobject_class = (GObjectClass *) klass;
108 gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
110 gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
111 gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
113 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SEND_CONFIG,
114 g_param_spec_boolean ("send-config", "Send Config",
115 "Send the config parameters in RTP packets as well(deprecated "
116 "see config-interval)",
117 DEFAULT_SEND_CONFIG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
119 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFER_LIST,
120 g_param_spec_boolean ("buffer-list", "Buffer Array",
122 DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
124 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CONFIG_INTERVAL,
125 g_param_spec_uint ("config-interval", "Config Send Interval",
126 "Send Config Insertion Interval in seconds (configuration headers "
127 "will be multiplexed in the data stream when detected.) (0 = disabled)",
128 0, 3600, DEFAULT_CONFIG_INTERVAL,
129 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
132 gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
134 gstbasertppayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
135 gstbasertppayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
136 gstbasertppayload_class->handle_event = gst_rtp_mp4v_pay_handle_event;
138 GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
139 "MP4 video RTP Payloader");
143 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay, GstRtpMP4VPayClass * klass)
145 rtpmp4vpay->adapter = gst_adapter_new ();
146 rtpmp4vpay->rate = 90000;
147 rtpmp4vpay->profile = 1;
148 rtpmp4vpay->buffer_list = DEFAULT_BUFFER_LIST;
149 rtpmp4vpay->send_config = DEFAULT_SEND_CONFIG;
150 rtpmp4vpay->need_config = TRUE;
151 rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL;
152 rtpmp4vpay->last_config = -1;
154 rtpmp4vpay->config = NULL;
158 gst_rtp_mp4v_pay_finalize (GObject * object)
160 GstRtpMP4VPay *rtpmp4vpay;
162 rtpmp4vpay = GST_RTP_MP4V_PAY (object);
164 if (rtpmp4vpay->config) {
165 gst_buffer_unref (rtpmp4vpay->config);
166 rtpmp4vpay->config = NULL;
168 g_object_unref (rtpmp4vpay->adapter);
169 rtpmp4vpay->adapter = NULL;
171 G_OBJECT_CLASS (parent_class)->finalize (object);
175 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
177 gchar *profile, *config;
181 profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
182 g_value_init (&v, GST_TYPE_BUFFER);
183 gst_value_set_buffer (&v, rtpmp4vpay->config);
184 config = gst_value_serialize (&v);
186 res = gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4vpay),
187 "profile-level-id", G_TYPE_STRING, profile,
188 "config", G_TYPE_STRING, config, NULL);
199 gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
201 GstRtpMP4VPay *rtpmp4vpay;
202 GstStructure *structure;
203 const GValue *codec_data;
206 rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
208 gst_basertppayload_set_options (payload, "video", TRUE, "MP4V-ES",
213 structure = gst_caps_get_structure (caps, 0);
214 codec_data = gst_structure_get_value (structure, "codec_data");
216 GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
217 if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
222 buffer = gst_value_get_buffer (codec_data);
224 data = GST_BUFFER_DATA (buffer);
225 size = GST_BUFFER_SIZE (buffer);
230 rtpmp4vpay->profile = data[4];
231 GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
234 if (rtpmp4vpay->config)
235 gst_buffer_unref (rtpmp4vpay->config);
236 rtpmp4vpay->config = gst_buffer_copy (buffer);
237 res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
246 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
248 gst_adapter_clear (rtpmp4vpay->adapter);
252 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
256 GstBuffer *outbuf_data = NULL;
258 GstBufferList *list = NULL;
259 GstBufferListIterator *it = NULL;
261 /* the data available in the adapter is either smaller
262 * than the MTU or bigger. In the case it is smaller, the complete
263 * adapter contents can be put in one packet. In the case the
264 * adapter has more than one MTU, we need to split the MP4V data
265 * over multiple packets. */
266 avail = gst_adapter_available (rtpmp4vpay->adapter);
268 if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) {
269 /* when we don't have a config yet, flush things out */
270 gst_adapter_flush (rtpmp4vpay->adapter, avail);
279 if (rtpmp4vpay->buffer_list) {
280 /* Use buffer lists. Each frame will be put into a list
281 * of buffers and the whole list will be pushed downstream
283 list = gst_buffer_list_new ();
284 it = gst_buffer_list_iterate (list);
293 /* this will be the total lenght of the packet */
294 packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
296 /* fill one MTU or all available bytes */
297 towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmp4vpay));
299 /* this is the payload length */
300 payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
302 if (rtpmp4vpay->buffer_list) {
303 /* create buffer without payload. The payload will be put
304 * in next buffer instead. Both buffers will be then added
306 outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
308 /* Take buffer with the payload from the adapter */
309 outbuf_data = gst_adapter_take_buffer (rtpmp4vpay->adapter, payload_len);
311 /* create buffer to hold the payload */
312 outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
315 payload = gst_rtp_buffer_get_payload (outbuf);
317 gst_adapter_copy (rtpmp4vpay->adapter, payload, 0, payload_len);
318 gst_adapter_flush (rtpmp4vpay->adapter, payload_len);
321 avail -= payload_len;
323 gst_rtp_buffer_set_marker (outbuf, avail == 0);
325 GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp;
327 if (rtpmp4vpay->buffer_list) {
328 /* create a new group to hold the rtp header and the payload */
329 gst_buffer_list_iterator_add_group (it);
330 gst_buffer_list_iterator_add (it, outbuf);
331 gst_buffer_list_iterator_add (it, outbuf_data);
333 ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), outbuf);
337 if (rtpmp4vpay->buffer_list) {
338 gst_buffer_list_iterator_free (it);
339 /* push the whole buffer list at once */
341 gst_basertppayload_push_list (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), list);
347 #define VOS_STARTCODE 0x000001B0
348 #define VOS_ENDCODE 0x000001B1
349 #define USER_DATA_STARTCODE 0x000001B2
350 #define GOP_STARTCODE 0x000001B3
351 #define VISUAL_OBJECT_STARTCODE 0x000001B5
352 #define VOP_STARTCODE 0x000001B6
355 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
356 gint * strip, gboolean * vopi)
367 code = GST_READ_UINT32_BE (data);
368 GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
376 gboolean newprofile = FALSE;
379 if (code == VOS_STARTCODE) {
380 /* profile_and_level_indication */
383 GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
385 if (profile != enc->profile) {
387 enc->profile = profile;
391 /* up to the next GOP_STARTCODE or VOP_STARTCODE is
392 * the config information */
394 for (i = 5; i < size - 4; i++) {
395 code = (code << 8) | data[i];
396 if (code == GOP_STARTCODE || code == VOP_STARTCODE)
400 /* see if config changed */
403 if (GST_BUFFER_SIZE (enc->config) == i) {
404 equal = memcmp (GST_BUFFER_DATA (enc->config), data, i) == 0;
407 /* if config string changed or new profile, make new caps */
408 if (!equal || newprofile) {
410 gst_buffer_unref (enc->config);
411 enc->config = gst_buffer_new_and_alloc (i);
412 memcpy (GST_BUFFER_DATA (enc->config), data, i);
413 gst_rtp_mp4v_pay_new_caps (enc);
416 /* we need to flush out the current packet. */
421 GST_DEBUG_OBJECT (enc, "VOP");
422 /* VOP startcode, we don't have to flush the packet */
424 /* vop-coding-type == I-frame */
425 if (size > 4 && (data[4] >> 6 == 0)) {
426 GST_DEBUG_OBJECT (enc, "VOP-I");
431 GST_DEBUG_OBJECT (enc, "GOP");
436 enc->need_config = FALSE;
440 if (code >= 0x20 && code <= 0x2f) {
441 GST_DEBUG_OBJECT (enc, "short header");
444 GST_DEBUG_OBJECT (enc, "other startcode");
445 /* all other startcodes need a flush */
453 /* we expect buffers starting on startcodes.
456 gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload,
459 GstRtpMP4VPay *rtpmp4vpay;
466 GstClockTime timestamp, duration;
468 gboolean send_config;
473 rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
475 size = GST_BUFFER_SIZE (buffer);
476 data = GST_BUFFER_DATA (buffer);
477 timestamp = GST_BUFFER_TIMESTAMP (buffer);
478 duration = GST_BUFFER_DURATION (buffer);
479 avail = gst_adapter_available (rtpmp4vpay->adapter);
484 /* empty buffer, take timestamp */
486 rtpmp4vpay->first_timestamp = timestamp;
487 rtpmp4vpay->duration = 0;
490 /* depay incomming data and see if we need to start a new RTP
492 flush = gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, data, size, &strip, &vopi);
494 /* strip off config if requested */
495 if (!(rtpmp4vpay->config_interval > 0)) {
498 GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
501 /* strip off header */
502 subbuf = gst_buffer_create_sub (buffer, strip, size - strip);
503 GST_BUFFER_TIMESTAMP (subbuf) = timestamp;
504 gst_buffer_unref (buffer);
507 size = GST_BUFFER_SIZE (buffer);
509 GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
510 rtpmp4vpay->last_config = timestamp;
514 /* there is a config request, see if we need to insert it */
515 if (vopi && (rtpmp4vpay->config_interval > 0) && rtpmp4vpay->config) {
516 if (rtpmp4vpay->last_config != -1) {
519 GST_LOG_OBJECT (rtpmp4vpay,
520 "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
521 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpmp4vpay->last_config));
523 /* calculate diff between last config in milliseconds */
524 if (timestamp > rtpmp4vpay->last_config) {
525 diff = timestamp - rtpmp4vpay->last_config;
530 GST_DEBUG_OBJECT (rtpmp4vpay,
531 "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
533 /* bigger than interval, queue config */
534 /* FIXME should convert timestamps to running time */
535 if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) {
536 GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config");
540 /* no known previous config time, send now */
541 GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now");
546 /* we need to send config now first */
549 GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream");
552 superbuf = gst_buffer_merge (rtpmp4vpay->config, buffer);
554 GST_BUFFER_TIMESTAMP (superbuf) = timestamp;
555 gst_buffer_unref (buffer);
558 size = GST_BUFFER_SIZE (buffer);
560 if (timestamp != -1) {
561 rtpmp4vpay->last_config = timestamp;
566 /* if we need to flush, do so now */
568 ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
569 rtpmp4vpay->first_timestamp = timestamp;
570 rtpmp4vpay->duration = 0;
574 /* get packet length of data and see if we exceeded MTU. */
575 packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
577 if (gst_basertppayload_is_filled (basepayload,
578 packet_len, rtpmp4vpay->duration + duration)) {
579 ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
580 rtpmp4vpay->first_timestamp = timestamp;
581 rtpmp4vpay->duration = 0;
585 gst_adapter_push (rtpmp4vpay->adapter, buffer);
587 rtpmp4vpay->duration += duration;
593 gst_rtp_mp4v_pay_handle_event (GstPad * pad, GstEvent * event)
595 GstRtpMP4VPay *rtpmp4vpay;
597 rtpmp4vpay = GST_RTP_MP4V_PAY (gst_pad_get_parent (pad));
599 GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
601 switch (GST_EVENT_TYPE (event)) {
602 case GST_EVENT_NEWSEGMENT:
604 /* This flush call makes sure that the last buffer is always pushed
605 * to the base payloader */
606 gst_rtp_mp4v_pay_flush (rtpmp4vpay);
608 case GST_EVENT_FLUSH_STOP:
609 gst_rtp_mp4v_pay_empty (rtpmp4vpay);
615 g_object_unref (rtpmp4vpay);
617 /* let parent handle event too */
622 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
623 const GValue * value, GParamSpec * pspec)
625 GstRtpMP4VPay *rtpmp4vpay;
627 rtpmp4vpay = GST_RTP_MP4V_PAY (object);
630 case ARG_SEND_CONFIG:
631 rtpmp4vpay->send_config = g_value_get_boolean (value);
632 /* send the configuration once every minute */
633 if (rtpmp4vpay->send_config && !(rtpmp4vpay->config_interval > 0)) {
634 rtpmp4vpay->config_interval = 60;
637 case ARG_BUFFER_LIST:
638 rtpmp4vpay->buffer_list = g_value_get_boolean (value);
640 case ARG_CONFIG_INTERVAL:
641 rtpmp4vpay->config_interval = g_value_get_uint (value);
649 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
650 GValue * value, GParamSpec * pspec)
652 GstRtpMP4VPay *rtpmp4vpay;
654 rtpmp4vpay = GST_RTP_MP4V_PAY (object);
657 case ARG_SEND_CONFIG:
658 g_value_set_boolean (value, rtpmp4vpay->send_config);
660 case ARG_BUFFER_LIST:
661 g_value_set_boolean (value, rtpmp4vpay->buffer_list);
663 case ARG_CONFIG_INTERVAL:
664 g_value_set_uint (value, rtpmp4vpay->config_interval);
672 gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
674 return gst_element_register (plugin, "rtpmp4vpay",
675 GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_PAY);