/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2007 Marcel Holtmann * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gstsbcenc.h" GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug #define GST_TYPE_SBC_MODE (gst_sbc_mode_get_type()) static GType gst_sbc_mode_get_type(void) { static GType sbc_mode_type = 0; static GEnumValue sbc_modes[] = { { 0, "Auto", "auto" }, { 1, "Mono", "mono" }, { 2, "Dual Channel", "dual" }, { 3, "Stereo", "stereo" }, { 4, "Joint Stereo", "joint" }, { -1, NULL, NULL} }; if (!sbc_mode_type) sbc_mode_type = g_enum_register_static("GstSbcMode", sbc_modes); return sbc_mode_type; } enum { PROP_0, PROP_MODE, }; GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); static const GstElementDetails sbc_enc_details = GST_ELEMENT_DETAILS("Bluetooth SBC encoder", "Codec/Encoder/Audio", "Encode a SBC audio stream", "Marcel Holtmann "); static GstStaticPadTemplate sbc_enc_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("audio/x-raw-int, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " "endianness = (int) LITTLE_ENDIAN, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")); static GstStaticPadTemplate sbc_enc_src_factory = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " "mode = (string) { mono, dual, stereo, joint }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }")); static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) { GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); GstFlowReturn res = GST_FLOW_OK; GstStructure *structure; gint rate, channels; guint size, offset = 0; guint8 *data; data = GST_BUFFER_DATA(buffer); size = GST_BUFFER_SIZE(buffer); structure = gst_caps_get_structure(GST_PAD_CAPS(pad), 0); gst_structure_get_int(structure, "rate", &rate); gst_structure_get_int(structure, "channels", &channels); enc->sbc.rate = rate; enc->sbc.channels = channels; enc->sbc.subbands = 8; enc->sbc.joint = 0; while (offset < size) { GstBuffer *output; GstCaps *caps; int consumed; consumed = sbc_encode(&enc->sbc, data + offset, size - offset); if (consumed <= 0) break; caps = GST_PAD_CAPS(enc->srcpad); res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, GST_BUFFER_OFFSET_NONE, enc->sbc.len, caps, &output); if (res != GST_FLOW_OK) goto done; memcpy(GST_BUFFER_DATA(output), enc->sbc.data, enc->sbc.len); res = gst_pad_push(enc->srcpad, output); if (res != GST_FLOW_OK) goto done; offset += consumed; } if (offset < size) res = GST_FLOW_ERROR; done: gst_buffer_unref(buffer); gst_object_unref(enc); return res; } static GstStateChangeReturn sbc_enc_change_state(GstElement *element, GstStateChange transition) { GstSbcEnc *enc = GST_SBC_ENC(element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: GST_DEBUG("Setup subband codec"); sbc_init(&enc->sbc, 0); break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG("Finish subband codec"); sbc_finish(&enc->sbc); break; default: break; } return parent_class->change_state(element, transition); } static void gst_sbc_enc_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sbc_enc_sink_factory)); gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sbc_enc_src_factory)); gst_element_class_set_details(element_class, &sbc_enc_details); } static void gst_sbc_enc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstSbcEnc *enc = GST_SBC_ENC(object); switch (prop_id) { case PROP_MODE: enc->mode = g_value_get_enum(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gst_sbc_enc_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GstSbcEnc *enc = GST_SBC_ENC(object); switch (prop_id) { case PROP_MODE: g_value_set_enum(value, enc->mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gst_sbc_enc_class_init(GstSbcEncClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); GstElementClass *element_class = GST_ELEMENT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); object_class->set_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_set_property); object_class->get_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_get_property); element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state); g_object_class_install_property(object_class, PROP_MODE, g_param_spec_enum("mode", "Mode", "Encoding mode", GST_TYPE_SBC_MODE, 0, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, "SBC encoding element"); } static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) { self->sinkpad = gst_pad_new_from_static_template(&sbc_enc_sink_factory, "sink"); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); }