Home | History | Annotate | Download | only in audio
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This library is free software; you can redistribute it and/or
      9  *  modify it under the terms of the GNU Lesser General Public
     10  *  License as published by the Free Software Foundation; either
     11  *  version 2.1 of the License, or (at your option) any later version.
     12  *
     13  *  This library is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  *  Lesser General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU Lesser General Public
     19  *  License along with this library; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include "gstpragma.h"
     29 #include "gstrtpsbcpay.h"
     30 #include <math.h>
     31 #include <string.h>
     32 
     33 #define RTP_SBC_PAYLOAD_HEADER_SIZE 1
     34 #define DEFAULT_MIN_FRAMES 0
     35 #define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE)
     36 
     37 #if __BYTE_ORDER == __LITTLE_ENDIAN
     38 
     39 struct rtp_payload {
     40 	guint8 frame_count:4;
     41 	guint8 rfa0:1;
     42 	guint8 is_last_fragment:1;
     43 	guint8 is_first_fragment:1;
     44 	guint8 is_fragmented:1;
     45 } __attribute__ ((packed));
     46 
     47 #elif __BYTE_ORDER == __BIG_ENDIAN
     48 
     49 struct rtp_payload {
     50 	guint8 is_fragmented:1;
     51 	guint8 is_first_fragment:1;
     52 	guint8 is_last_fragment:1;
     53 	guint8 rfa0:1;
     54 	guint8 frame_count:4;
     55 } __attribute__ ((packed));
     56 
     57 #else
     58 #error "Unknown byte order"
     59 #endif
     60 
     61 enum {
     62 	PROP_0,
     63 	PROP_MIN_FRAMES
     64 };
     65 
     66 GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug);
     67 #define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug
     68 
     69 GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload,
     70 		GST_TYPE_BASE_RTP_PAYLOAD);
     71 
     72 static const GstElementDetails gst_rtp_sbc_pay_details =
     73 	GST_ELEMENT_DETAILS("RTP packet payloader",
     74 				"Codec/Payloader/Network",
     75 				"Payload SBC audio as RTP packets",
     76 				"Thiago Sousa Santos "
     77 				"<thiagoss (at) lcc.ufcg.edu.br>");
     78 
     79 static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory =
     80 	GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
     81 		GST_STATIC_CAPS("audio/x-sbc, "
     82 				"rate = (int) { 16000, 32000, 44100, 48000 }, "
     83 				"channels = (int) [ 1, 2 ], "
     84 				"mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
     85 				"blocks = (int) { 4, 8, 12, 16 }, "
     86 				"subbands = (int) { 4, 8 }, "
     87 				"allocation = (string) { \"snr\", \"loudness\" }, "
     88 				"bitpool = (int) [ 2, 64 ]")
     89 	);
     90 
     91 static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory =
     92 	GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
     93 		GST_STATIC_CAPS(
     94 			"application/x-rtp, "
     95 			"media = (string) \"audio\","
     96 			"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
     97 			"clock-rate = (int) { 16000, 32000, 44100, 48000 },"
     98 			"encoding-name = (string) \"SBC\"")
     99 	);
    100 
    101 static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id,
    102 				const GValue *value, GParamSpec *pspec);
    103 static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id,
    104 				GValue *value, GParamSpec *pspec);
    105 
    106 static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels,
    107 		gint blocks, gint bitpool, const gchar *channel_mode)
    108 {
    109 	gint len;
    110 	gint join;
    111 
    112 	len = 4 + (4 * subbands * channels)/8;
    113 
    114 	if (strcmp(channel_mode, "mono") == 0 ||
    115 		strcmp(channel_mode, "dual") == 0)
    116 		len += ((blocks * channels * bitpool) + 7) / 8;
    117 	else {
    118 		join = strcmp(channel_mode, "joint") == 0 ? 1 : 0;
    119 		len += ((join * subbands + blocks * bitpool) + 7) / 8;
    120 	}
    121 
    122 	return len;
    123 }
    124 
    125 static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload,
    126 			GstCaps *caps)
    127 {
    128 	GstRtpSBCPay *sbcpay;
    129 	gint rate, subbands, channels, blocks, bitpool;
    130 	gint frame_len;
    131 	const gchar *channel_mode;
    132 	GstStructure *structure;
    133 
    134 	sbcpay = GST_RTP_SBC_PAY(payload);
    135 
    136 	structure = gst_caps_get_structure(caps, 0);
    137 	if (!gst_structure_get_int(structure, "rate", &rate))
    138 		return FALSE;
    139 	if (!gst_structure_get_int(structure, "channels", &channels))
    140 		return FALSE;
    141 	if (!gst_structure_get_int(structure, "blocks", &blocks))
    142 		return FALSE;
    143 	if (!gst_structure_get_int(structure, "bitpool", &bitpool))
    144 		return FALSE;
    145 	if (!gst_structure_get_int(structure, "subbands", &subbands))
    146 		return FALSE;
    147 
    148 	channel_mode = gst_structure_get_string(structure, "mode");
    149 	if (!channel_mode)
    150 		return FALSE;
    151 
    152 	frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks,
    153 				bitpool, channel_mode);
    154 
    155 	sbcpay->frame_length = frame_len;
    156 
    157 	gst_basertppayload_set_options(payload, "audio", TRUE, "SBC", rate);
    158 
    159 	GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len);
    160 
    161 	return gst_basertppayload_set_outcaps(payload, NULL);
    162 }
    163 
    164 static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay)
    165 {
    166 	guint available;
    167 	guint max_payload;
    168 	GstBuffer *outbuf;
    169 	guint8 *payload_data;
    170 	guint frame_count;
    171 	guint payload_length;
    172 	struct rtp_payload *payload;
    173 
    174 	if (sbcpay->frame_length == 0) {
    175 		GST_ERROR_OBJECT(sbcpay, "Frame length is 0");
    176 		return GST_FLOW_ERROR;
    177 	}
    178 
    179 	available = gst_adapter_available(sbcpay->adapter);
    180 
    181 	max_payload = gst_rtp_buffer_calc_payload_len(
    182 		GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE,
    183 		0, 0);
    184 
    185 	max_payload = MIN(max_payload, available);
    186 	frame_count = max_payload / sbcpay->frame_length;
    187 	payload_length = frame_count * sbcpay->frame_length;
    188 	if (payload_length == 0) /* Nothing to send */
    189 		return GST_FLOW_OK;
    190 
    191 	outbuf = gst_rtp_buffer_new_allocate(payload_length +
    192 			RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0);
    193 
    194 	gst_rtp_buffer_set_payload_type(outbuf,
    195 			GST_BASE_RTP_PAYLOAD_PT(sbcpay));
    196 
    197 	payload_data = gst_rtp_buffer_get_payload(outbuf);
    198 	payload = (struct rtp_payload *) payload_data;
    199 	memset(payload, 0, sizeof(struct rtp_payload));
    200 	payload->frame_count = frame_count;
    201 
    202 	gst_adapter_copy(sbcpay->adapter, payload_data +
    203 			RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length);
    204 	gst_adapter_flush(sbcpay->adapter, payload_length);
    205 
    206 	GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp;
    207 	GST_DEBUG_OBJECT(sbcpay, "Pushing %d bytes", payload_length);
    208 
    209 	return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf);
    210 }
    211 
    212 static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload,
    213 			GstBuffer *buffer)
    214 {
    215 	GstRtpSBCPay *sbcpay;
    216 	guint available;
    217 
    218 	/* FIXME check for negotiation */
    219 
    220 	sbcpay = GST_RTP_SBC_PAY(payload);
    221 	sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer);
    222 
    223 	gst_adapter_push(sbcpay->adapter, buffer);
    224 
    225 	available = gst_adapter_available(sbcpay->adapter);
    226 	if (available + RTP_SBC_HEADER_TOTAL >=
    227 				GST_BASE_RTP_PAYLOAD_MTU(sbcpay) ||
    228 			(available >
    229 				(sbcpay->min_frames * sbcpay->frame_length)))
    230 		return gst_rtp_sbc_pay_flush_buffers(sbcpay);
    231 
    232 	return GST_FLOW_OK;
    233 }
    234 
    235 static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad,
    236 				GstEvent *event)
    237 {
    238 	GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad));
    239 
    240 	switch (GST_EVENT_TYPE(event)) {
    241 	case GST_EVENT_EOS:
    242 		gst_rtp_sbc_pay_flush_buffers(sbcpay);
    243 		break;
    244 	default:
    245 		break;
    246 	}
    247 
    248 	return FALSE;
    249 }
    250 
    251 static void gst_rtp_sbc_pay_base_init(gpointer g_class)
    252 {
    253 	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
    254 
    255 	gst_element_class_add_pad_template(element_class,
    256 		gst_static_pad_template_get(&gst_rtp_sbc_pay_sink_factory));
    257 	gst_element_class_add_pad_template(element_class,
    258 		gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory));
    259 
    260 	gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details);
    261 }
    262 
    263 static void gst_rtp_sbc_pay_finalize(GObject *object)
    264 {
    265 	GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object);
    266 	g_object_unref(sbcpay->adapter);
    267 
    268 	GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
    269 }
    270 
    271 static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass)
    272 {
    273 	GObjectClass *gobject_class;
    274 	GstBaseRTPPayloadClass *payload_class =
    275 		GST_BASE_RTP_PAYLOAD_CLASS(klass);
    276 
    277 	gobject_class = G_OBJECT_CLASS(klass);
    278 	parent_class = g_type_class_peek_parent(klass);
    279 
    280 	gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize);
    281 	gobject_class->set_property = GST_DEBUG_FUNCPTR(
    282 			gst_rtp_sbc_pay_set_property);
    283 	gobject_class->get_property = GST_DEBUG_FUNCPTR(
    284 			gst_rtp_sbc_pay_get_property);
    285 
    286 	payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps);
    287 	payload_class->handle_buffer = GST_DEBUG_FUNCPTR(
    288 			gst_rtp_sbc_pay_handle_buffer);
    289 	payload_class->handle_event = GST_DEBUG_FUNCPTR(
    290 			gst_rtp_sbc_pay_handle_event);
    291 
    292 	/* properties */
    293 	g_object_class_install_property(G_OBJECT_CLASS(klass),
    294 		PROP_MIN_FRAMES,
    295 		g_param_spec_int("min-frames", "minimum frame number",
    296 		"Minimum quantity of frames to send in one packet "
    297 		"(-1 for maximum allowed by the mtu)",
    298 		-1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE));
    299 
    300 	GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0,
    301 				"RTP SBC payloader");
    302 }
    303 
    304 static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id,
    305 					const GValue *value, GParamSpec *pspec)
    306 {
    307 	GstRtpSBCPay *sbcpay;
    308 
    309 	sbcpay = GST_RTP_SBC_PAY(object);
    310 
    311 	switch (prop_id) {
    312 	case PROP_MIN_FRAMES:
    313 		sbcpay->min_frames = g_value_get_int(value);
    314 		break;
    315 	default:
    316 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    317 	break;
    318 	}
    319 }
    320 
    321 static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id,
    322 					GValue *value, GParamSpec *pspec)
    323 {
    324 	GstRtpSBCPay *sbcpay;
    325 
    326 	sbcpay = GST_RTP_SBC_PAY(object);
    327 
    328 	switch (prop_id) {
    329 	case PROP_MIN_FRAMES:
    330 		g_value_set_int(value, sbcpay->min_frames);
    331 		break;
    332 	default:
    333 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    334 	break;
    335 	}
    336 }
    337 
    338 static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass)
    339 {
    340 	self->adapter = gst_adapter_new();
    341 	self->frame_length = 0;
    342 	self->timestamp = 0;
    343 
    344 	self->min_frames = DEFAULT_MIN_FRAMES;
    345 }
    346 
    347 gboolean gst_rtp_sbc_pay_plugin_init(GstPlugin *plugin)
    348 {
    349 	return gst_element_register(plugin, "rtpsbcpay", GST_RANK_NONE,
    350 							GST_TYPE_RTP_SBC_PAY);
    351 }
    352 
    353