• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2010> 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 /**
21  * SECTION:element-rtpac3pay
22  * @title: rtpac3pay
23  * @see_also: rtpac3depay
24  *
25  * Payload AC3 audio into RTP packets according to RFC 4184.
26  * For detailed information see: http://www.rfc-editor.org/rfc/rfc4184.txt
27  *
28  * ## Example pipeline
29  * |[
30  * gst-launch-1.0 -v audiotestsrc ! avenc_ac3 ! rtpac3pay ! udpsink
31  * ]| This example pipeline will encode and payload AC3 stream. Refer to
32  * the rtpac3depay example to depayload and decode the RTP stream.
33  *
34  */
35 
36 #ifdef HAVE_CONFIG_H
37 #  include "config.h"
38 #endif
39 
40 #include <string.h>
41 
42 #include <gst/rtp/gstrtpbuffer.h>
43 #include <gst/audio/audio.h>
44 
45 #include "gstrtpelements.h"
46 #include "gstrtpac3pay.h"
47 #include "gstrtputils.h"
48 
49 GST_DEBUG_CATEGORY_STATIC (rtpac3pay_debug);
50 #define GST_CAT_DEFAULT (rtpac3pay_debug)
51 
52 static GstStaticPadTemplate gst_rtp_ac3_pay_sink_template =
53     GST_STATIC_PAD_TEMPLATE ("sink",
54     GST_PAD_SINK,
55     GST_PAD_ALWAYS,
56     GST_STATIC_CAPS ("audio/ac3; " "audio/x-ac3; ")
57     );
58 
59 static GstStaticPadTemplate gst_rtp_ac3_pay_src_template =
60 GST_STATIC_PAD_TEMPLATE ("src",
61     GST_PAD_SRC,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS ("application/x-rtp, "
64         "media = (string) \"audio\", "
65         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
66         "clock-rate = (int) { 32000, 44100, 48000 }, "
67         "encoding-name = (string) \"AC3\"")
68     );
69 
70 static void gst_rtp_ac3_pay_finalize (GObject * object);
71 
72 static GstStateChangeReturn gst_rtp_ac3_pay_change_state (GstElement * element,
73     GstStateChange transition);
74 
75 static gboolean gst_rtp_ac3_pay_setcaps (GstRTPBasePayload * payload,
76     GstCaps * caps);
77 static gboolean gst_rtp_ac3_pay_sink_event (GstRTPBasePayload * payload,
78     GstEvent * event);
79 static GstFlowReturn gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay);
80 static GstFlowReturn gst_rtp_ac3_pay_handle_buffer (GstRTPBasePayload * payload,
81     GstBuffer * buffer);
82 
83 #define gst_rtp_ac3_pay_parent_class parent_class
84 G_DEFINE_TYPE (GstRtpAC3Pay, gst_rtp_ac3_pay, GST_TYPE_RTP_BASE_PAYLOAD);
85 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtpac3pay, "rtpac3pay",
86     GST_RANK_SECONDARY, GST_TYPE_RTP_AC3_PAY, rtp_element_init (plugin));
87 
88 static void
gst_rtp_ac3_pay_class_init(GstRtpAC3PayClass * klass)89 gst_rtp_ac3_pay_class_init (GstRtpAC3PayClass * klass)
90 {
91   GObjectClass *gobject_class;
92   GstElementClass *gstelement_class;
93   GstRTPBasePayloadClass *gstrtpbasepayload_class;
94 
95   GST_DEBUG_CATEGORY_INIT (rtpac3pay_debug, "rtpac3pay", 0,
96       "AC3 Audio RTP Depayloader");
97 
98   gobject_class = (GObjectClass *) klass;
99   gstelement_class = (GstElementClass *) klass;
100   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
101 
102   gobject_class->finalize = gst_rtp_ac3_pay_finalize;
103 
104   gstelement_class->change_state = gst_rtp_ac3_pay_change_state;
105 
106   gst_element_class_add_static_pad_template (gstelement_class,
107       &gst_rtp_ac3_pay_src_template);
108   gst_element_class_add_static_pad_template (gstelement_class,
109       &gst_rtp_ac3_pay_sink_template);
110 
111   gst_element_class_set_static_metadata (gstelement_class,
112       "RTP AC3 audio payloader", "Codec/Payloader/Network/RTP",
113       "Payload AC3 audio as RTP packets (RFC 4184)",
114       "Wim Taymans <wim.taymans@gmail.com>");
115 
116   gstrtpbasepayload_class->set_caps = gst_rtp_ac3_pay_setcaps;
117   gstrtpbasepayload_class->sink_event = gst_rtp_ac3_pay_sink_event;
118   gstrtpbasepayload_class->handle_buffer = gst_rtp_ac3_pay_handle_buffer;
119 }
120 
121 static void
gst_rtp_ac3_pay_init(GstRtpAC3Pay * rtpac3pay)122 gst_rtp_ac3_pay_init (GstRtpAC3Pay * rtpac3pay)
123 {
124   rtpac3pay->adapter = gst_adapter_new ();
125 }
126 
127 static void
gst_rtp_ac3_pay_finalize(GObject * object)128 gst_rtp_ac3_pay_finalize (GObject * object)
129 {
130   GstRtpAC3Pay *rtpac3pay;
131 
132   rtpac3pay = GST_RTP_AC3_PAY (object);
133 
134   g_object_unref (rtpac3pay->adapter);
135 
136   G_OBJECT_CLASS (parent_class)->finalize (object);
137 }
138 
139 static void
gst_rtp_ac3_pay_reset(GstRtpAC3Pay * pay)140 gst_rtp_ac3_pay_reset (GstRtpAC3Pay * pay)
141 {
142   pay->first_ts = -1;
143   pay->duration = 0;
144   gst_adapter_clear (pay->adapter);
145   GST_DEBUG_OBJECT (pay, "reset depayloader");
146 }
147 
148 static gboolean
gst_rtp_ac3_pay_setcaps(GstRTPBasePayload * payload,GstCaps * caps)149 gst_rtp_ac3_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
150 {
151   gboolean res;
152   gint rate;
153   GstStructure *structure;
154 
155   structure = gst_caps_get_structure (caps, 0);
156 
157   if (!gst_structure_get_int (structure, "rate", &rate))
158     rate = 90000;               /* default */
159 
160   gst_rtp_base_payload_set_options (payload, "audio", TRUE, "AC3", rate);
161   res = gst_rtp_base_payload_set_outcaps (payload, NULL);
162 
163   return res;
164 }
165 
166 static gboolean
gst_rtp_ac3_pay_sink_event(GstRTPBasePayload * payload,GstEvent * event)167 gst_rtp_ac3_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
168 {
169   gboolean res;
170   GstRtpAC3Pay *rtpac3pay;
171 
172   rtpac3pay = GST_RTP_AC3_PAY (payload);
173 
174   switch (GST_EVENT_TYPE (event)) {
175     case GST_EVENT_EOS:
176       /* make sure we push the last packets in the adapter on EOS */
177       gst_rtp_ac3_pay_flush (rtpac3pay);
178       break;
179     case GST_EVENT_FLUSH_STOP:
180       gst_rtp_ac3_pay_reset (rtpac3pay);
181       break;
182     default:
183       break;
184   }
185 
186   res = GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
187 
188   return res;
189 }
190 
191 struct frmsize_s
192 {
193   guint16 bit_rate;
194   guint16 frm_size[3];
195 };
196 
197 static const struct frmsize_s frmsizecod_tbl[] = {
198   {32, {64, 69, 96}},
199   {32, {64, 70, 96}},
200   {40, {80, 87, 120}},
201   {40, {80, 88, 120}},
202   {48, {96, 104, 144}},
203   {48, {96, 105, 144}},
204   {56, {112, 121, 168}},
205   {56, {112, 122, 168}},
206   {64, {128, 139, 192}},
207   {64, {128, 140, 192}},
208   {80, {160, 174, 240}},
209   {80, {160, 175, 240}},
210   {96, {192, 208, 288}},
211   {96, {192, 209, 288}},
212   {112, {224, 243, 336}},
213   {112, {224, 244, 336}},
214   {128, {256, 278, 384}},
215   {128, {256, 279, 384}},
216   {160, {320, 348, 480}},
217   {160, {320, 349, 480}},
218   {192, {384, 417, 576}},
219   {192, {384, 418, 576}},
220   {224, {448, 487, 672}},
221   {224, {448, 488, 672}},
222   {256, {512, 557, 768}},
223   {256, {512, 558, 768}},
224   {320, {640, 696, 960}},
225   {320, {640, 697, 960}},
226   {384, {768, 835, 1152}},
227   {384, {768, 836, 1152}},
228   {448, {896, 975, 1344}},
229   {448, {896, 976, 1344}},
230   {512, {1024, 1114, 1536}},
231   {512, {1024, 1115, 1536}},
232   {576, {1152, 1253, 1728}},
233   {576, {1152, 1254, 1728}},
234   {640, {1280, 1393, 1920}},
235   {640, {1280, 1394, 1920}}
236 };
237 
238 static GstFlowReturn
gst_rtp_ac3_pay_flush(GstRtpAC3Pay * rtpac3pay)239 gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay)
240 {
241   guint avail, FT, NF, mtu;
242   GstBuffer *outbuf;
243   GstFlowReturn ret;
244 
245   /* the data available in the adapter is either smaller
246    * than the MTU or bigger. In the case it is smaller, the complete
247    * adapter contents can be put in one packet. In the case the
248    * adapter has more than one MTU, we need to split the AC3 data
249    * over multiple packets. */
250   avail = gst_adapter_available (rtpac3pay->adapter);
251 
252   ret = GST_FLOW_OK;
253 
254   FT = 0;
255   /* number of frames */
256   NF = rtpac3pay->NF;
257 
258   mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpac3pay);
259 
260   GST_LOG_OBJECT (rtpac3pay, "flushing %u bytes", avail);
261 
262   while (avail > 0) {
263     guint towrite;
264     guint8 *payload;
265     guint payload_len;
266     guint packet_len;
267     GstRTPBuffer rtp = { NULL, };
268     GstBuffer *payload_buffer;
269 
270     /* this will be the total length of the packet */
271     packet_len = gst_rtp_buffer_calc_packet_len (2 + 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 to hold the payload */
280     outbuf =
281         gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
282         (rtpac3pay), 2, 0, 0);
283 
284     if (FT == 0) {
285       /* check if it all fits */
286       if (towrite < packet_len) {
287         guint maxlen;
288 
289         GST_LOG_OBJECT (rtpac3pay, "we need to fragment");
290         /* check if we will be able to put at least 5/8th of the total
291          * frame in this first frame. */
292         if ((avail * 5) / 8 >= (payload_len - 2))
293           FT = 1;
294         else
295           FT = 2;
296         /* check how many fragments we will need */
297         maxlen = gst_rtp_buffer_calc_payload_len (mtu - 2, 0, 0);
298         NF = (avail + maxlen - 1) / maxlen;
299       }
300     } else if (FT != 3) {
301       /* remaining fragment */
302       FT = 3;
303     }
304 
305     /*
306      *  0                   1
307      *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
308      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309      * |    MBZ    | FT|       NF      |
310      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311      *
312      * FT: 0: one or more complete frames
313      *     1: initial 5/8 fragment
314      *     2: initial fragment not 5/8
315      *     3: other fragment
316      * NF: amount of frames if FT = 0, else number of fragments.
317      */
318     gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
319     GST_LOG_OBJECT (rtpac3pay, "FT %u, NF %u", FT, NF);
320     payload = gst_rtp_buffer_get_payload (&rtp);
321     payload[0] = (FT & 3);
322     payload[1] = NF;
323     payload_len -= 2;
324 
325     if (avail == payload_len)
326       gst_rtp_buffer_set_marker (&rtp, TRUE);
327     gst_rtp_buffer_unmap (&rtp);
328 
329     payload_buffer =
330         gst_adapter_take_buffer_fast (rtpac3pay->adapter, payload_len);
331 
332     gst_rtp_copy_audio_meta (rtpac3pay, outbuf, payload_buffer);
333 
334     outbuf = gst_buffer_append (outbuf, payload_buffer);
335 
336     avail -= payload_len;
337 
338     GST_BUFFER_PTS (outbuf) = rtpac3pay->first_ts;
339     GST_BUFFER_DURATION (outbuf) = rtpac3pay->duration;
340 
341     ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpac3pay), outbuf);
342   }
343 
344   return ret;
345 }
346 
347 static GstFlowReturn
gst_rtp_ac3_pay_handle_buffer(GstRTPBasePayload * basepayload,GstBuffer * buffer)348 gst_rtp_ac3_pay_handle_buffer (GstRTPBasePayload * basepayload,
349     GstBuffer * buffer)
350 {
351   GstRtpAC3Pay *rtpac3pay;
352   GstFlowReturn ret;
353   gsize avail, left, NF;
354   GstMapInfo map;
355   guint8 *p;
356   guint packet_len;
357   GstClockTime duration, timestamp;
358 
359   rtpac3pay = GST_RTP_AC3_PAY (basepayload);
360 
361   gst_buffer_map (buffer, &map, GST_MAP_READ);
362   duration = GST_BUFFER_DURATION (buffer);
363   timestamp = GST_BUFFER_PTS (buffer);
364 
365   if (GST_BUFFER_IS_DISCONT (buffer)) {
366     GST_DEBUG_OBJECT (rtpac3pay, "DISCONT");
367     gst_rtp_ac3_pay_reset (rtpac3pay);
368   }
369 
370   /* count the amount of incoming packets */
371   NF = 0;
372   left = map.size;
373   p = map.data;
374   while (TRUE) {
375     guint bsid, fscod, frmsizecod, frame_size;
376 
377     if (left < 6)
378       break;
379 
380     if (p[0] != 0x0b || p[1] != 0x77)
381       break;
382 
383     bsid = p[5] >> 3;
384     if (bsid > 8)
385       break;
386 
387     frmsizecod = p[4] & 0x3f;
388     fscod = p[4] >> 6;
389 
390     GST_DEBUG_OBJECT (rtpac3pay, "fscod %u, %u", fscod, frmsizecod);
391 
392     if (fscod >= 3 || frmsizecod >= 38)
393       break;
394 
395     frame_size = frmsizecod_tbl[frmsizecod].frm_size[fscod] * 2;
396     if (frame_size > left)
397       break;
398 
399     NF++;
400     GST_DEBUG_OBJECT (rtpac3pay, "found frame %" G_GSIZE_FORMAT " of size %u",
401         NF, frame_size);
402 
403     p += frame_size;
404     left -= frame_size;
405   }
406   gst_buffer_unmap (buffer, &map);
407   if (NF == 0)
408     goto no_frames;
409 
410   avail = gst_adapter_available (rtpac3pay->adapter);
411 
412   /* get packet length of previous data and this new data,
413    * payload length includes a 4 byte header */
414   packet_len = gst_rtp_buffer_calc_packet_len (2 + avail + map.size, 0, 0);
415 
416   /* if this buffer is going to overflow the packet, flush what we
417    * have. */
418   if (gst_rtp_base_payload_is_filled (basepayload,
419           packet_len, rtpac3pay->duration + duration)) {
420     ret = gst_rtp_ac3_pay_flush (rtpac3pay);
421     avail = 0;
422   } else {
423     ret = GST_FLOW_OK;
424   }
425 
426   if (avail == 0) {
427     GST_DEBUG_OBJECT (rtpac3pay,
428         "first packet, save timestamp %" GST_TIME_FORMAT,
429         GST_TIME_ARGS (timestamp));
430     rtpac3pay->first_ts = timestamp;
431     rtpac3pay->duration = 0;
432     rtpac3pay->NF = 0;
433   }
434 
435   gst_adapter_push (rtpac3pay->adapter, buffer);
436   rtpac3pay->duration += duration;
437   rtpac3pay->NF += NF;
438 
439   return ret;
440 
441   /* ERRORS */
442 no_frames:
443   {
444     GST_WARNING_OBJECT (rtpac3pay, "no valid AC3 frames found");
445     return GST_FLOW_OK;
446   }
447 }
448 
449 static GstStateChangeReturn
gst_rtp_ac3_pay_change_state(GstElement * element,GstStateChange transition)450 gst_rtp_ac3_pay_change_state (GstElement * element, GstStateChange transition)
451 {
452   GstRtpAC3Pay *rtpac3pay;
453   GstStateChangeReturn ret;
454 
455   rtpac3pay = GST_RTP_AC3_PAY (element);
456 
457   switch (transition) {
458     case GST_STATE_CHANGE_READY_TO_PAUSED:
459       gst_rtp_ac3_pay_reset (rtpac3pay);
460       break;
461     default:
462       break;
463   }
464 
465   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
466 
467   switch (transition) {
468     case GST_STATE_CHANGE_PAUSED_TO_READY:
469       gst_rtp_ac3_pay_reset (rtpac3pay);
470       break;
471     default:
472       break;
473   }
474   return ret;
475 }
476