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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
26 #include <gst/rtp/gstrtpbuffer.h>
27 #include <gst/video/video.h>
29 #include "gstrtpelements.h"
30 #include "gstrtpmp4vpay.h"
31 #include "gstrtputils.h"
33 GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug);
34 #define GST_CAT_DEFAULT (rtpmp4vpay_debug)
36 static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template =
37 GST_STATIC_PAD_TEMPLATE ("sink",
40 GST_STATIC_CAPS ("video/mpeg,"
41 "mpegversion=(int) 4, systemstream=(boolean)false;" "video/x-divx")
44 static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template =
45 GST_STATIC_PAD_TEMPLATE ("src",
48 GST_STATIC_CAPS ("application/x-rtp, "
49 "media = (string) \"video\", "
50 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
51 "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\""
54 "profile-level-id = (string) [1,MAX]"
55 "config = (string) [1,MAX]"
60 #define DEFAULT_CONFIG_INTERVAL 0
69 static void gst_rtp_mp4v_pay_finalize (GObject * object);
71 static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
72 const GValue * value, GParamSpec * pspec);
73 static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
74 GValue * value, GParamSpec * pspec);
76 static gboolean gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload,
78 static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload *
79 payload, GstBuffer * buffer);
80 static gboolean gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay,
83 #define gst_rtp_mp4v_pay_parent_class parent_class
84 G_DEFINE_TYPE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GST_TYPE_RTP_BASE_PAYLOAD);
85 /* Note: This element is marked at a "+1" rank to make sure that
86 * auto-plugging of payloaders for MPEG4 elementary streams don't
87 * end up using the 'rtpmp4gpay' element (generic mpeg4) which isn't
88 * as well supported as this RFC */
89 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpmp4vpay, "rtpmp4vpay",
90 GST_RANK_SECONDARY + 1, GST_TYPE_RTP_MP4V_PAY, rtp_element_init (plugin));
93 gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass)
95 GObjectClass *gobject_class;
96 GstElementClass *gstelement_class;
97 GstRTPBasePayloadClass *gstrtpbasepayload_class;
99 gobject_class = (GObjectClass *) klass;
100 gstelement_class = (GstElementClass *) klass;
101 gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
103 gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
104 gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
106 gst_element_class_add_static_pad_template (gstelement_class,
107 &gst_rtp_mp4v_pay_src_template);
108 gst_element_class_add_static_pad_template (gstelement_class,
109 &gst_rtp_mp4v_pay_sink_template);
111 gst_element_class_set_static_metadata (gstelement_class,
112 "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP",
113 "Payload MPEG-4 video as RTP packets (RFC 3016)",
114 "Wim Taymans <wim.taymans@gmail.com>");
116 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
117 g_param_spec_int ("config-interval", "Config Send Interval",
118 "Send Config Insertion Interval in seconds (configuration headers "
119 "will be multiplexed in the data stream when detected.) "
120 "(0 = disabled, -1 = send with every IDR frame)",
121 -1, 3600, DEFAULT_CONFIG_INTERVAL,
122 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
125 gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
127 gstrtpbasepayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
128 gstrtpbasepayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
129 gstrtpbasepayload_class->sink_event = gst_rtp_mp4v_pay_sink_event;
131 GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
132 "MP4 video RTP Payloader");
136 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay)
138 rtpmp4vpay->adapter = gst_adapter_new ();
139 rtpmp4vpay->rate = 90000;
140 rtpmp4vpay->profile = 1;
141 rtpmp4vpay->need_config = TRUE;
142 rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL;
143 rtpmp4vpay->last_config = -1;
145 rtpmp4vpay->config = NULL;
149 gst_rtp_mp4v_pay_finalize (GObject * object)
151 GstRtpMP4VPay *rtpmp4vpay;
153 rtpmp4vpay = GST_RTP_MP4V_PAY (object);
155 if (rtpmp4vpay->config) {
156 gst_buffer_unref (rtpmp4vpay->config);
157 rtpmp4vpay->config = NULL;
159 g_object_unref (rtpmp4vpay->adapter);
160 rtpmp4vpay->adapter = NULL;
162 G_OBJECT_CLASS (parent_class)->finalize (object);
166 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
168 gchar *profile, *config;
172 profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
173 g_value_init (&v, GST_TYPE_BUFFER);
174 gst_value_set_buffer (&v, rtpmp4vpay->config);
175 config = gst_value_serialize (&v);
177 res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4vpay),
178 "profile-level-id", G_TYPE_STRING, profile,
179 "config", G_TYPE_STRING, config, NULL);
190 gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
192 GstRtpMP4VPay *rtpmp4vpay;
193 GstStructure *structure;
194 const GValue *codec_data;
197 rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
199 gst_rtp_base_payload_set_options (payload, "video", TRUE, "MP4V-ES",
204 structure = gst_caps_get_structure (caps, 0);
205 codec_data = gst_structure_get_value (structure, "codec_data");
207 GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
208 if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
211 buffer = gst_value_get_buffer (codec_data);
213 if (gst_buffer_get_size (buffer) < 5)
216 gst_buffer_extract (buffer, 4, &rtpmp4vpay->profile, 1);
217 GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
218 rtpmp4vpay->profile);
220 if (rtpmp4vpay->config)
221 gst_buffer_unref (rtpmp4vpay->config);
222 rtpmp4vpay->config = gst_buffer_copy (buffer);
223 res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
232 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
234 gst_adapter_clear (rtpmp4vpay->adapter);
237 #define RTP_HEADER_LEN 12
240 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
244 GstBuffer *outbuf_data = NULL;
246 GstBufferList *list = NULL;
248 /* the data available in the adapter is either smaller
249 * than the MTU or bigger. In the case it is smaller, the complete
250 * adapter contents can be put in one packet. In the case the
251 * adapter has more than one MTU, we need to split the MP4V data
252 * over multiple packets. */
253 avail = gst_adapter_available (rtpmp4vpay->adapter);
255 if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) {
256 /* when we don't have a config yet, flush things out */
257 gst_adapter_flush (rtpmp4vpay->adapter, avail);
264 mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4vpay);
266 /* Use buffer lists. Each frame will be put into a list
267 * of buffers and the whole list will be pushed downstream
269 list = gst_buffer_list_new_sized ((avail / (mtu - RTP_HEADER_LEN)) + 1);
275 GstRTPBuffer rtp = { NULL };
277 /* this will be the total length of the packet */
278 packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
280 /* fill one MTU or all available bytes */
281 towrite = MIN (packet_len, mtu);
283 /* this is the payload length */
284 payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
286 /* create buffer without payload. The payload will be put
287 * in next buffer instead. Both buffers will be merged */
289 gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
290 (rtpmp4vpay), 0, 0, 0);
292 /* Take buffer with the payload from the adapter */
293 outbuf_data = gst_adapter_take_buffer_fast (rtpmp4vpay->adapter,
296 avail -= payload_len;
298 gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
299 gst_rtp_buffer_set_marker (&rtp, avail == 0);
301 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_MARKER);
302 gst_rtp_buffer_unmap (&rtp);
303 gst_rtp_copy_video_meta (rtpmp4vpay, outbuf, outbuf_data);
304 outbuf = gst_buffer_append (outbuf, outbuf_data);
306 GST_BUFFER_PTS (outbuf) = rtpmp4vpay->first_timestamp;
309 gst_buffer_list_insert (list, -1, outbuf);
312 /* push the whole buffer list at once */
314 gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmp4vpay), list);
319 #define VOS_STARTCODE 0x000001B0
320 #define VOS_ENDCODE 0x000001B1
321 #define USER_DATA_STARTCODE 0x000001B2
322 #define GOP_STARTCODE 0x000001B3
323 #define VISUAL_OBJECT_STARTCODE 0x000001B5
324 #define VOP_STARTCODE 0x000001B6
327 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
328 gint * strip, gboolean * vopi)
339 code = GST_READ_UINT32_BE (data);
340 GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
348 gboolean newprofile = FALSE;
351 if (code == VOS_STARTCODE) {
352 /* profile_and_level_indication */
355 GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
357 if (profile != enc->profile) {
359 enc->profile = profile;
363 /* up to the next GOP_STARTCODE or VOP_STARTCODE is
364 * the config information */
366 for (i = 5; i < size - 4; i++) {
367 code = (code << 8) | data[i];
368 if (code == GOP_STARTCODE || code == VOP_STARTCODE)
372 /* see if config changed */
375 if (gst_buffer_get_size (enc->config) == i) {
376 equal = gst_buffer_memcmp (enc->config, 0, data, i) == 0;
379 /* if config string changed or new profile, make new caps */
380 if (!equal || newprofile) {
382 gst_buffer_unref (enc->config);
383 enc->config = gst_buffer_new_and_alloc (i);
385 gst_buffer_fill (enc->config, 0, data, i);
387 gst_rtp_mp4v_pay_new_caps (enc);
390 /* we need to flush out the current packet. */
395 GST_DEBUG_OBJECT (enc, "VOP");
396 /* VOP startcode, we don't have to flush the packet */
398 /* vop-coding-type == I-frame */
399 if (size > 4 && (data[4] >> 6 == 0)) {
400 GST_DEBUG_OBJECT (enc, "VOP-I");
405 GST_DEBUG_OBJECT (enc, "GOP");
410 enc->need_config = FALSE;
414 if (code >= 0x20 && code <= 0x2f) {
415 GST_DEBUG_OBJECT (enc, "short header");
418 GST_DEBUG_OBJECT (enc, "other startcode");
419 /* all other startcodes need a flush */
427 /* we expect buffers starting on startcodes.
430 gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload * basepayload,
433 GstRtpMP4VPay *rtpmp4vpay;
441 GstClockTime timestamp, duration;
443 gboolean send_config;
444 GstClockTime running_time = GST_CLOCK_TIME_NONE;
449 rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
451 gst_buffer_map (buffer, &map, GST_MAP_READ);
453 timestamp = GST_BUFFER_PTS (buffer);
454 duration = GST_BUFFER_DURATION (buffer);
455 avail = gst_adapter_available (rtpmp4vpay->adapter);
460 /* empty buffer, take timestamp */
462 rtpmp4vpay->first_timestamp = timestamp;
463 rtpmp4vpay->duration = 0;
466 /* depay incoming data and see if we need to start a new RTP
469 gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, map.data, size, &strip, &vopi);
470 gst_buffer_unmap (buffer, &map);
473 /* strip off config if requested, do not strip off if the
474 * config_interval is set to -1 */
475 if (!(rtpmp4vpay->config_interval > 0)
476 && !(rtpmp4vpay->config_interval == -1)) {
479 GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
480 (gint) size - strip);
482 /* strip off header */
483 subbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, strip,
485 GST_BUFFER_PTS (subbuf) = timestamp;
486 gst_buffer_unref (buffer);
489 size = gst_buffer_get_size (buffer);
492 gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
495 GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
496 rtpmp4vpay->last_config = running_time;
500 /* there is a config request, see if we need to insert it */
501 if (vopi && (rtpmp4vpay->config_interval > 0) && rtpmp4vpay->config) {
503 gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
506 if (rtpmp4vpay->last_config != -1) {
509 GST_LOG_OBJECT (rtpmp4vpay,
510 "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
511 GST_TIME_ARGS (running_time),
512 GST_TIME_ARGS (rtpmp4vpay->last_config));
514 /* calculate diff between last config in milliseconds */
515 if (running_time > rtpmp4vpay->last_config) {
516 diff = running_time - rtpmp4vpay->last_config;
521 GST_DEBUG_OBJECT (rtpmp4vpay,
522 "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
524 /* bigger than interval, queue config */
525 if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) {
526 GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config");
530 /* no known previous config time, send now */
531 GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now");
536 if (vopi && (rtpmp4vpay->config_interval == -1)) {
537 GST_DEBUG_OBJECT (rtpmp4vpay, "sending config before current IDR frame");
538 /* send config before every IDR frame */
543 /* we need to send config now first */
544 GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream");
547 buffer = gst_buffer_append (gst_buffer_ref (rtpmp4vpay->config), buffer);
549 GST_BUFFER_PTS (buffer) = timestamp;
550 size = gst_buffer_get_size (buffer);
552 if (running_time != -1) {
553 rtpmp4vpay->last_config = running_time;
557 /* if we need to flush, do so now */
559 ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
560 rtpmp4vpay->first_timestamp = timestamp;
561 rtpmp4vpay->duration = 0;
565 /* get packet length of data and see if we exceeded MTU. */
566 packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
568 if (gst_rtp_base_payload_is_filled (basepayload,
569 packet_len, rtpmp4vpay->duration + duration)) {
570 ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
571 rtpmp4vpay->first_timestamp = timestamp;
572 rtpmp4vpay->duration = 0;
576 gst_adapter_push (rtpmp4vpay->adapter, buffer);
578 rtpmp4vpay->duration += duration;
584 gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay, GstEvent * event)
586 GstRtpMP4VPay *rtpmp4vpay;
588 rtpmp4vpay = GST_RTP_MP4V_PAY (pay);
590 GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
592 switch (GST_EVENT_TYPE (event)) {
593 case GST_EVENT_SEGMENT:
595 /* This flush call makes sure that the last buffer is always pushed
596 * to the base payloader */
597 gst_rtp_mp4v_pay_flush (rtpmp4vpay);
599 case GST_EVENT_FLUSH_STOP:
600 gst_rtp_mp4v_pay_empty (rtpmp4vpay);
606 /* let parent handle event too */
607 return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pay, event);
611 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
612 const GValue * value, GParamSpec * pspec)
614 GstRtpMP4VPay *rtpmp4vpay;
616 rtpmp4vpay = GST_RTP_MP4V_PAY (object);
619 case PROP_CONFIG_INTERVAL:
620 rtpmp4vpay->config_interval = g_value_get_int (value);
628 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
629 GValue * value, GParamSpec * pspec)
631 GstRtpMP4VPay *rtpmp4vpay;
633 rtpmp4vpay = GST_RTP_MP4V_PAY (object);
636 case PROP_CONFIG_INTERVAL:
637 g_value_set_int (value, rtpmp4vpay->config_interval);