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/audio/audio.h>
29 #include "gstrtpelements.h"
30 #include "gstrtpmpapay.h"
31 #include "gstrtputils.h"
33 GST_DEBUG_CATEGORY_STATIC (rtpmpapay_debug);
34 #define GST_CAT_DEFAULT (rtpmpapay_debug)
36 static GstStaticPadTemplate gst_rtp_mpa_pay_sink_template =
37 GST_STATIC_PAD_TEMPLATE ("sink",
40 GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1")
43 static GstStaticPadTemplate gst_rtp_mpa_pay_src_template =
44 GST_STATIC_PAD_TEMPLATE ("src",
47 GST_STATIC_CAPS ("application/x-rtp, "
48 "media = (string) \"audio\", "
49 "payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", "
50 "clock-rate = (int) 90000; "
52 "media = (string) \"audio\", "
53 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
54 "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"")
57 static void gst_rtp_mpa_pay_finalize (GObject * object);
59 static GstStateChangeReturn gst_rtp_mpa_pay_change_state (GstElement * element,
60 GstStateChange transition);
62 static gboolean gst_rtp_mpa_pay_setcaps (GstRTPBasePayload * payload,
64 static gboolean gst_rtp_mpa_pay_sink_event (GstRTPBasePayload * payload,
66 static GstFlowReturn gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay);
67 static GstFlowReturn gst_rtp_mpa_pay_handle_buffer (GstRTPBasePayload * payload,
70 #define gst_rtp_mpa_pay_parent_class parent_class
71 G_DEFINE_TYPE (GstRtpMPAPay, gst_rtp_mpa_pay, GST_TYPE_RTP_BASE_PAYLOAD);
72 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpmpapay, "rtpmpapay",
73 GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_PAY, rtp_element_init (plugin));
76 gst_rtp_mpa_pay_class_init (GstRtpMPAPayClass * klass)
78 GObjectClass *gobject_class;
79 GstElementClass *gstelement_class;
80 GstRTPBasePayloadClass *gstrtpbasepayload_class;
82 GST_DEBUG_CATEGORY_INIT (rtpmpapay_debug, "rtpmpapay", 0,
83 "MPEG Audio RTP Depayloader");
85 gobject_class = (GObjectClass *) klass;
86 gstelement_class = (GstElementClass *) klass;
87 gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
89 gobject_class->finalize = gst_rtp_mpa_pay_finalize;
91 gstelement_class->change_state = gst_rtp_mpa_pay_change_state;
93 gst_element_class_add_static_pad_template (gstelement_class,
94 &gst_rtp_mpa_pay_src_template);
95 gst_element_class_add_static_pad_template (gstelement_class,
96 &gst_rtp_mpa_pay_sink_template);
98 gst_element_class_set_static_metadata (gstelement_class,
99 "RTP MPEG audio payloader", "Codec/Payloader/Network/RTP",
100 "Payload MPEG audio as RTP packets (RFC 2038)",
101 "Wim Taymans <wim.taymans@gmail.com>");
103 gstrtpbasepayload_class->set_caps = gst_rtp_mpa_pay_setcaps;
104 gstrtpbasepayload_class->sink_event = gst_rtp_mpa_pay_sink_event;
105 gstrtpbasepayload_class->handle_buffer = gst_rtp_mpa_pay_handle_buffer;
109 gst_rtp_mpa_pay_init (GstRtpMPAPay * rtpmpapay)
111 rtpmpapay->adapter = gst_adapter_new ();
113 GST_RTP_BASE_PAYLOAD (rtpmpapay)->pt = GST_RTP_PAYLOAD_MPA;
117 gst_rtp_mpa_pay_finalize (GObject * object)
119 GstRtpMPAPay *rtpmpapay;
121 rtpmpapay = GST_RTP_MPA_PAY (object);
123 g_object_unref (rtpmpapay->adapter);
124 rtpmpapay->adapter = NULL;
126 G_OBJECT_CLASS (parent_class)->finalize (object);
130 gst_rtp_mpa_pay_reset (GstRtpMPAPay * pay)
134 gst_adapter_clear (pay->adapter);
135 GST_DEBUG_OBJECT (pay, "reset depayloader");
139 gst_rtp_mpa_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
143 gst_rtp_base_payload_set_options (payload, "audio",
144 payload->pt != GST_RTP_PAYLOAD_MPA, "MPA", 90000);
145 res = gst_rtp_base_payload_set_outcaps (payload, NULL);
151 gst_rtp_mpa_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
154 GstRtpMPAPay *rtpmpapay;
156 rtpmpapay = GST_RTP_MPA_PAY (payload);
158 switch (GST_EVENT_TYPE (event)) {
160 /* make sure we push the last packets in the adapter on EOS */
161 gst_rtp_mpa_pay_flush (rtpmpapay);
163 case GST_EVENT_FLUSH_STOP:
164 gst_rtp_mpa_pay_reset (rtpmpapay);
170 ret = GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
175 #define RTP_HEADER_LEN 12
178 gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay)
186 /* the data available in the adapter is either smaller
187 * than the MTU or bigger. In the case it is smaller, the complete
188 * adapter contents can be put in one packet. In the case the
189 * adapter has more than one MTU, we need to split the MPA data
190 * over multiple packets. The frag_offset in each packet header
191 * needs to be updated with the position in the MPA frame. */
192 avail = gst_adapter_available (rtpmpapay->adapter);
197 gst_buffer_list_new_sized (avail / (GST_RTP_BASE_PAYLOAD_MTU (rtpmpapay) -
198 RTP_HEADER_LEN) + 1);
206 GstRTPBuffer rtp = { NULL };
209 /* this will be the total length of the packet */
210 packet_len = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0);
212 /* fill one MTU or all available bytes */
213 towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpmpapay));
215 /* this is the payload length */
216 payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
218 /* create buffer to hold the payload */
220 gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
221 (rtpmpapay), 4, 0, 0);
223 gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
227 gst_rtp_buffer_set_payload_type (&rtp, GST_RTP_PAYLOAD_MPA);
231 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
232 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
233 * | MBZ | Frag_offset |
234 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
236 payload = gst_rtp_buffer_get_payload (&rtp);
239 payload[2] = frag_offset >> 8;
240 payload[3] = frag_offset & 0xff;
242 avail -= payload_len;
243 frag_offset += payload_len;
246 gst_rtp_buffer_set_marker (&rtp, TRUE);
247 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_MARKER);
250 gst_rtp_buffer_unmap (&rtp);
252 paybuf = gst_adapter_take_buffer_fast (rtpmpapay->adapter, payload_len);
253 gst_rtp_copy_audio_meta (rtpmpapay, outbuf, paybuf);
254 outbuf = gst_buffer_append (outbuf, paybuf);
256 GST_BUFFER_PTS (outbuf) = rtpmpapay->first_ts;
257 GST_BUFFER_DURATION (outbuf) = rtpmpapay->duration;
258 gst_buffer_list_add (list, outbuf);
261 ret = gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmpapay), list);
267 gst_rtp_mpa_pay_handle_buffer (GstRTPBasePayload * basepayload,
270 GstRtpMPAPay *rtpmpapay;
274 GstClockTime duration, timestamp;
276 rtpmpapay = GST_RTP_MPA_PAY (basepayload);
278 size = gst_buffer_get_size (buffer);
279 duration = GST_BUFFER_DURATION (buffer);
280 timestamp = GST_BUFFER_PTS (buffer);
282 if (GST_BUFFER_IS_DISCONT (buffer)) {
283 GST_DEBUG_OBJECT (rtpmpapay, "DISCONT");
284 gst_rtp_mpa_pay_reset (rtpmpapay);
287 avail = gst_adapter_available (rtpmpapay->adapter);
289 /* get packet length of previous data and this new data,
290 * payload length includes a 4 byte header */
291 packet_len = gst_rtp_buffer_calc_packet_len (4 + avail + size, 0, 0);
293 /* if this buffer is going to overflow the packet, flush what we
295 if (gst_rtp_base_payload_is_filled (basepayload,
296 packet_len, rtpmpapay->duration + duration)) {
297 ret = gst_rtp_mpa_pay_flush (rtpmpapay);
304 GST_DEBUG_OBJECT (rtpmpapay,
305 "first packet, save timestamp %" GST_TIME_FORMAT,
306 GST_TIME_ARGS (timestamp));
307 rtpmpapay->first_ts = timestamp;
308 rtpmpapay->duration = 0;
311 gst_adapter_push (rtpmpapay->adapter, buffer);
312 rtpmpapay->duration = duration;
317 static GstStateChangeReturn
318 gst_rtp_mpa_pay_change_state (GstElement * element, GstStateChange transition)
320 GstRtpMPAPay *rtpmpapay;
321 GstStateChangeReturn ret;
323 rtpmpapay = GST_RTP_MPA_PAY (element);
325 switch (transition) {
326 case GST_STATE_CHANGE_READY_TO_PAUSED:
327 gst_rtp_mpa_pay_reset (rtpmpapay);
333 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
335 switch (transition) {
336 case GST_STATE_CHANGE_PAUSED_TO_READY:
337 gst_rtp_mpa_pay_reset (rtpmpapay);