diff options
author | Tim-Philipp Müller <tim@centricular.net> | 2013-03-27 13:11:11 +0000 |
---|---|---|
committer | Tim-Philipp Müller <tim@centricular.net> | 2013-03-27 22:40:56 +0000 |
commit | a028a05054a66a1b8cf2c6700c3784cd7502f8e5 (patch) | |
tree | 04200cd513867a848cf4d8619beb3819f5f22c83 /ext/sbc | |
parent | ca9daee444b0711a27e1a1c0ca9aa8d93dc37588 (diff) | |
download | gstreamer-plugins-bad-a028a05054a66a1b8cf2c6700c3784cd7502f8e5.tar.gz |
sbc: port encoder to GstAudioEncoder
https://bugzilla.gnome.org/show_bug.cgi?id=690582
Diffstat (limited to 'ext/sbc')
-rw-r--r-- | ext/sbc/Makefile.am | 4 | ||||
-rw-r--r-- | ext/sbc/gstsbcenc.c | 670 | ||||
-rw-r--r-- | ext/sbc/gstsbcenc.h | 40 | ||||
-rw-r--r-- | ext/sbc/gstsbcutil.c | 400 | ||||
-rw-r--r-- | ext/sbc/gstsbcutil.h | 71 |
5 files changed, 207 insertions, 978 deletions
diff --git a/ext/sbc/Makefile.am b/ext/sbc/Makefile.am index 3d4da05b2..a330371ff 100644 --- a/ext/sbc/Makefile.am +++ b/ext/sbc/Makefile.am @@ -3,13 +3,11 @@ plugin_LTLIBRARIES = libgstsbc.la noinst_HEADERS = \ gstsbcdec.h \ - gstsbcenc.h \ - gstsbcutil.h + gstsbcenc.h libgstsbc_la_SOURCES = \ gstsbcdec.c \ gstsbcenc.c \ - gstsbcutil.c \ sbc-plugin.c libgstsbc_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ diff --git a/ext/sbc/gstsbcenc.c b/ext/sbc/gstsbcenc.c index 812a3724f..d456190a5 100644 --- a/ext/sbc/gstsbcenc.c +++ b/ext/sbc/gstsbcenc.c @@ -1,8 +1,6 @@ /* GStreamer SBC audio encoder - * BlueZ - Bluetooth protocol stack for Linux - * * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> - * + * Copyright (C) 2013 Tim-Philipp Müller <tim centricular net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,130 +18,35 @@ * */ +/** + * SECTION:element-sbenc + * + * This element encodes raw integer PCM audio into a Bluetooth SBC audio. + * + * Encoding paramets such as blocks, subbands, bitpool, channel-mode, and + * allocation-mode can be set by adding a capsfilter element with appropriate + * filtercaps after the sbcenc encoder element. + * + * <refsect2> + * <title>Example pipelines</title> + * |[ + * gst-launch-1.0 -v audiotestsrc ! sbcenc ! rtpsbcpay ! udpsink + * ]| Encode a sine wave into SBC, RTP payload it and send over the network using UDP + * </refsect2> + */ + #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <string.h> -#include "gstsbcutil.h" #include "gstsbcenc.h" -#include <gst/audio/audio.h> - -#define SBC_ENC_DEFAULT_CHANNEL_MODE SBC_MODE_AUTO -#define SBC_ENC_DEFAULT_BLOCKS 0 -#define SBC_ENC_DEFAULT_SUB_BANDS 0 -#define SBC_ENC_DEFAULT_ALLOCATION_METHOD SBC_AM_AUTO -#define SBC_ENC_DEFAULT_RATE 0 -#define SBC_ENC_DEFAULT_CHANNELS 0 - -#define SBC_ENC_BITPOOL_AUTO 1 -#define SBC_ENC_BITPOOL_MIN 2 -#define SBC_ENC_BITPOOL_MIN_STR "2" -#define SBC_ENC_BITPOOL_MAX 64 -#define SBC_ENC_BITPOOL_MAX_STR "64" - GST_DEBUG_CATEGORY_STATIC (sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug -#define GST_TYPE_SBC_CHANNEL_MODE (gst_sbc_channel_mode_get_type()) - -static GType -gst_sbc_channel_mode_get_type (void) -{ - static GType sbc_channel_mode_type = 0; - static GEnumValue sbc_channel_modes[] = { - {SBC_MODE_MONO, "Mono", "mono"}, - {SBC_MODE_DUAL_CHANNEL, "Dual", "dual"}, - {SBC_MODE_STEREO, "Stereo", "stereo"}, - {SBC_MODE_JOINT_STEREO, "Joint Stereo", "joint"}, - {SBC_MODE_AUTO, "Auto", "auto"}, - {-1, NULL, NULL} - }; - - if (!sbc_channel_mode_type) { - sbc_channel_mode_type = - g_enum_register_static ("GstSbcChannelMode", sbc_channel_modes); - } - - return sbc_channel_mode_type; -} - -#define GST_TYPE_SBC_ALLOCATION_METHOD (gst_sbc_allocation_method_get_type()) - -static GType -gst_sbc_allocation_method_get_type (void) -{ - static GType sbc_allocation_method_type = 0; - static GEnumValue sbc_allocation_methods[] = { - {SBC_AM_LOUDNESS, "Loudness", "loudness"}, - {SBC_AM_SNR, "SNR", "snr"}, - {SBC_AM_AUTO, "Auto", "auto"}, - {-1, NULL, NULL} - }; - - if (!sbc_allocation_method_type) - sbc_allocation_method_type = - g_enum_register_static ("GstSbcAllocationMethod", - sbc_allocation_methods); - - return sbc_allocation_method_type; -} - -#define GST_TYPE_SBC_BLOCKS (gst_sbc_blocks_get_type()) - -static GType -gst_sbc_blocks_get_type (void) -{ - static GType sbc_blocks_type = 0; - static GEnumValue sbc_blocks[] = { - {0, "Auto", "auto"}, - {4, "4", "4"}, - {8, "8", "8"}, - {12, "12", "12"}, - {16, "16", "16"}, - {-1, NULL, NULL} - }; - - if (!sbc_blocks_type) - sbc_blocks_type = g_enum_register_static ("GstSbcBlocks", sbc_blocks); - - return sbc_blocks_type; -} - -#define GST_TYPE_SBC_SUBBANDS (gst_sbc_subbands_get_type()) - -static GType -gst_sbc_subbands_get_type (void) -{ - static GType sbc_subbands_type = 0; - static GEnumValue sbc_subbands[] = { - {0, "Auto", "auto"}, - {4, "4 subbands", "4"}, - {8, "8 subbands", "8"}, - {-1, NULL, NULL} - }; - - if (!sbc_subbands_type) - sbc_subbands_type = g_enum_register_static ("GstSbcSubbands", sbc_subbands); - - return sbc_subbands_type; -} - -enum -{ - PROP_0, - PROP_MODE, - PROP_ALLOCATION, - PROP_BLOCKS, - PROP_SUBBANDS, - PROP_BITPOOL -}; - -/* FIXME: rewrite based on GstAudioEncoder base class */ -#define parent_class gst_sbc_enc_parent_class -G_DEFINE_TYPE (GstSbcEnc, gst_sbc_enc, GST_TYPE_ELEMENT); +G_DEFINE_TYPE (GstSbcEnc, gst_sbc_enc, GST_TYPE_AUDIO_ENCODER); static GstStaticPadTemplate sbc_enc_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -160,99 +63,39 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " "allocation-method = (string) { snr, loudness }, " - "bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR - ", " SBC_ENC_BITPOOL_MAX_STR " ]")); + "bitpool = (int) [ 2, 64 ]")); -static gboolean gst_sbc_enc_fill_sbc_params (GstSbcEnc * enc, GstCaps * caps); - -static gboolean gst_sbc_enc_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); - -/* FIXME does this make sense? */ -static GstCaps * -sbc_enc_generate_srcpad_caps (GstSbcEnc * enc) -{ - GstCaps *src_caps; - GstStructure *structure; - GEnumValue *enum_value; - GEnumClass *enum_class; - GValue *value; - - src_caps = gst_caps_copy (gst_pad_get_pad_template_caps (enc->srcpad)); - structure = gst_caps_get_structure (src_caps, 0); - - /* FIXME: use gst_structure_set() */ - value = g_new0 (GValue, 1); - - if (enc->rate != 0) - gst_sbc_util_set_structure_int_param (structure, "rate", enc->rate, value); - - if (enc->channels != 0) - gst_sbc_util_set_structure_int_param (structure, "channels", - enc->channels, value); - - if (enc->subbands != 0) - gst_sbc_util_set_structure_int_param (structure, "subbands", - enc->subbands, value); - - if (enc->blocks != 0) - gst_sbc_util_set_structure_int_param (structure, "blocks", - enc->blocks, value); - - if (enc->bitpool != SBC_ENC_BITPOOL_AUTO) - gst_sbc_util_set_structure_int_param (structure, "bitpool", - enc->bitpool, value); - - if (enc->mode != SBC_ENC_DEFAULT_CHANNEL_MODE) { - enum_class = g_type_class_ref (GST_TYPE_SBC_CHANNEL_MODE); - enum_value = g_enum_get_value (enum_class, enc->mode); - gst_sbc_util_set_structure_string_param (structure, "channel-mode", - enum_value->value_nick, value); - g_type_class_unref (enum_class); - } - - if (enc->allocation != SBC_AM_AUTO) { - enum_class = g_type_class_ref (GST_TYPE_SBC_ALLOCATION_METHOD); - enum_value = g_enum_get_value (enum_class, enc->allocation); - gst_sbc_util_set_structure_string_param (structure, "allocation-method", - enum_value->value_nick, value); - g_type_class_unref (enum_class); - } - - g_free (value); - - return src_caps; -} - -static GstCaps * -sbc_enc_src_getcaps (GstPad * pad) -{ - GstSbcEnc *enc; - - enc = GST_SBC_ENC (GST_PAD_PARENT (pad)); - - return sbc_enc_generate_srcpad_caps (enc); -} +static gboolean gst_sbc_enc_start (GstAudioEncoder * enc); +static gboolean gst_sbc_enc_stop (GstAudioEncoder * enc); +static gboolean gst_sbc_enc_set_format (GstAudioEncoder * enc, + GstAudioInfo * info); +static GstFlowReturn gst_sbc_enc_handle_frame (GstAudioEncoder * enc, + GstBuffer * buffer); static gboolean -sbc_enc_negotiate_output_caps (GstSbcEnc * enc, GstCaps * in_caps) +gst_sbc_enc_set_format (GstAudioEncoder * audio_enc, GstAudioInfo * info) { + const gchar *allocation_method, *channel_mode; + GstSbcEnc *enc = GST_SBC_ENC (audio_enc); GstStructure *s; GstCaps *caps, *filter_caps; GstCaps *output_caps; + guint sampleframes_per_frame; - GST_INFO_OBJECT (enc, "input caps %" GST_PTR_FORMAT, in_caps); - - caps = gst_pad_get_allowed_caps (enc->srcpad); - if (caps == NULL) - caps = sbc_enc_src_getcaps (enc->srcpad); + enc->rate = GST_AUDIO_INFO_RATE (info); + enc->channels = GST_AUDIO_INFO_CHANNELS (info); + /* negotiate output format based on downstream caps restrictions */ + caps = gst_pad_get_allowed_caps (GST_AUDIO_ENCODER_SRC_PAD (enc)); if (caps == GST_CAPS_NONE || gst_caps_is_empty (caps)) { gst_caps_unref (caps); return FALSE; } + if (caps == NULL) + caps = gst_static_pad_template_get_caps (&sbc_enc_src_factory); + /* fixate output caps */ filter_caps = gst_caps_new_simple ("audio/x-sbc", "rate", G_TYPE_INT, enc->rate, "channels", G_TYPE_INT, enc->channels, NULL); @@ -260,15 +103,17 @@ sbc_enc_negotiate_output_caps (GstSbcEnc * enc, GstCaps * in_caps) gst_caps_unref (filter_caps); if (output_caps == NULL || gst_caps_is_empty (output_caps)) { - GST_DEBUG_OBJECT (enc, "Couldn't negotiate output caps with input rate %d " - "and input channels %d and allowed output caps %" GST_PTR_FORMAT, + GST_WARNING_OBJECT (enc, "Couldn't negotiate output caps with input rate " + "%d and input channels %d and allowed output caps %" GST_PTR_FORMAT, enc->rate, enc->channels, caps); if (output_caps) gst_caps_unref (output_caps); gst_caps_unref (caps); return FALSE; } + gst_caps_unref (caps); + caps = NULL; GST_DEBUG_OBJECT (enc, "fixating caps %" GST_PTR_FORMAT, output_caps); output_caps = gst_caps_truncate (output_caps); @@ -280,9 +125,12 @@ sbc_enc_negotiate_output_caps (GstSbcEnc * enc, GstCaps * in_caps) return FALSE; } } else { - if (gst_structure_fixate_field_string (s, "channel-mode", "joint") - || gst_structure_fixate_field_string (s, "channel-mode", "stereo")) { - gst_structure_fixate_field_string (s, "channel-mode", "dual"); + if (!gst_structure_fixate_field_string (s, "channel-mode", "joint") && + !gst_structure_fixate_field_string (s, "channel-mode", "stereo") && + !gst_structure_fixate_field_string (s, "channel-mode", "dual")) { + GST_DEBUG_OBJECT (enc, "Failed to fixate channel-mode for 2 channels"); + gst_caps_unref (output_caps); + return FALSE; } } @@ -294,304 +142,201 @@ sbc_enc_negotiate_output_caps (GstSbcEnc * enc, GstCaps * in_caps) /* in case there's anything else left to fixate */ output_caps = gst_caps_fixate (output_caps); + gst_caps_set_simple (output_caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); GST_INFO_OBJECT (enc, "output caps %" GST_PTR_FORMAT, output_caps); - if (!gst_sbc_enc_fill_sbc_params (enc, output_caps)) { - GST_WARNING_OBJECT (enc, "failed to configure encoder from output " - "caps %" GST_PTR_FORMAT, output_caps); - return FALSE; - } - - gst_pad_send_event (enc->sinkpad, gst_event_new_caps (output_caps)); - gst_caps_unref (output_caps); - - return TRUE; -} - -static gboolean -sbc_enc_sink_setcaps (GstSbcEnc * enc, GstCaps * caps) -{ - GstStructure *structure; - gint rate, channels; - structure = gst_caps_get_structure (caps, 0); - - if (!gst_structure_get_int (structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int (structure, "channels", &channels)) + /* let's see what we fixated to */ + s = gst_caps_get_structure (output_caps, 0); + gst_structure_get_int (s, "blocks", &enc->blocks); + gst_structure_get_int (s, "subbands", &enc->subbands); + gst_structure_get_int (s, "bitpool", &enc->bitpool); + allocation_method = gst_structure_get_string (s, "allocation-method"); + channel_mode = gst_structure_get_string (s, "channel-mode"); + + /* we want to be handed all available samples in handle_frame, but always + * enough to encode a frame */ + sampleframes_per_frame = enc->blocks * enc->subbands; + gst_audio_encoder_set_frame_samples_min (audio_enc, sampleframes_per_frame); + gst_audio_encoder_set_frame_samples_max (audio_enc, sampleframes_per_frame); + gst_audio_encoder_set_frame_max (audio_enc, 0); + + /* FIXME: what to do with left-over samples at the end? can we encode them? */ + gst_audio_encoder_set_hard_min (audio_enc, TRUE); + + /* and configure encoder based on the output caps we negotiated */ + if (enc->rate == 16000) + enc->sbc.frequency = SBC_FREQ_16000; + else if (enc->rate == 32000) + enc->sbc.frequency = SBC_FREQ_32000; + else if (enc->rate == 44100) + enc->sbc.frequency = SBC_FREQ_44100; + else if (enc->rate == 48000) + enc->sbc.frequency = SBC_FREQ_48000; + else return FALSE; - enc->rate = rate; - enc->channels = channels; - - return sbc_enc_negotiate_output_caps (enc, caps); -} - -static gboolean -gst_sbc_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstSbcEnc *enc = GST_SBC_ENC (parent); - gboolean ret; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CAPS:{ - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - ret = sbc_enc_sink_setcaps (enc, caps); - break; - } - default: - ret = gst_pad_event_default (pad, parent, event); - break; - } + if (enc->blocks == 4) + enc->sbc.blocks = SBC_BLK_4; + else if (enc->blocks == 8) + enc->sbc.blocks = SBC_BLK_8; + else if (enc->blocks == 12) + enc->sbc.blocks = SBC_BLK_12; + else if (enc->blocks == 16) + enc->sbc.blocks = SBC_BLK_16; + else + return FALSE; - return ret; -} + enc->sbc.subbands = (enc->subbands == 4) ? SBC_SB_4 : SBC_SB_8; + enc->sbc.bitpool = enc->bitpool; -/* configure encoder based on output caps */ -static gboolean -gst_sbc_enc_fill_sbc_params (GstSbcEnc * enc, GstCaps * caps) -{ - if (!gst_caps_is_fixed (caps)) { - GST_DEBUG_OBJECT (enc, "output caps %" GST_PTR_FORMAT " not fixed!", caps); + if (channel_mode == NULL || allocation_method == NULL) return FALSE; - } - if (!gst_sbc_util_fill_sbc_params (&enc->sbc, caps)) + if (strcmp (channel_mode, "joint") == 0) + enc->sbc.mode = SBC_MODE_JOINT_STEREO; + else if (strcmp (channel_mode, "stereo") == 0) + enc->sbc.mode = SBC_MODE_STEREO; + else if (strcmp (channel_mode, "dual") == 0) + enc->sbc.mode = SBC_MODE_DUAL_CHANNEL; + else if (strcmp (channel_mode, "mono") == 0) + enc->sbc.mode = SBC_MODE_MONO; + else if (strcmp (channel_mode, "auto") == 0) + enc->sbc.mode = SBC_MODE_JOINT_STEREO; + else return FALSE; - if (enc->rate != 0 && gst_sbc_parse_rate_from_sbc (enc->sbc.frequency) - != enc->rate) - goto fail; - - if (enc->channels != 0 && gst_sbc_get_channel_number (enc->sbc.mode) - != enc->channels) - goto fail; - - if (enc->blocks != 0 && gst_sbc_parse_blocks_from_sbc (enc->sbc.blocks) - != enc->blocks) - goto fail; + if (strcmp (allocation_method, "loudness") == 0) + enc->sbc.allocation = SBC_AM_LOUDNESS; + else if (strcmp (allocation_method, "snr") == 0) + enc->sbc.allocation = SBC_AM_SNR; + else + return FALSE; - if (enc->subbands != 0 - && gst_sbc_parse_subbands_from_sbc (enc->sbc.subbands) != enc->subbands) - goto fail; + if (!gst_audio_encoder_set_output_format (audio_enc, output_caps)) + return FALSE; - if (enc->mode != SBC_ENC_DEFAULT_CHANNEL_MODE && enc->sbc.mode != enc->mode) - goto fail; + return gst_audio_encoder_negotiate (audio_enc); +} - if (enc->allocation != SBC_AM_AUTO && enc->sbc.allocation != enc->allocation) - goto fail; +static GstFlowReturn +gst_sbc_enc_handle_frame (GstAudioEncoder * audio_enc, GstBuffer * buffer) +{ + GstSbcEnc *enc = GST_SBC_ENC (audio_enc); + GstMapInfo in_map, out_map; + GstBuffer *outbuf = NULL; + guint samples_per_frame, frames, i = 0; - if (enc->bitpool != SBC_ENC_BITPOOL_AUTO && enc->sbc.bitpool != enc->bitpool) - goto fail; + /* no fancy draining */ + if (buffer == NULL) + return GST_FLOW_OK; - enc->codesize = sbc_get_codesize (&enc->sbc); - enc->frame_length = sbc_get_frame_length (&enc->sbc); - enc->frame_duration = sbc_get_frame_duration (&enc->sbc) * 1000; + if (G_UNLIKELY (enc->channels == 0 || enc->blocks == 0 || enc->subbands == 0)) + return GST_FLOW_NOT_NEGOTIATED; - GST_DEBUG_OBJECT (enc, "codesize: %d, frame_length: %d, frame_duration:" - " %d", enc->codesize, enc->frame_length, enc->frame_duration); + samples_per_frame = enc->channels * enc->blocks * enc->subbands; - return TRUE; + if (!gst_buffer_map (buffer, &in_map, GST_MAP_READ)) + goto map_failed; -fail: - memset (&enc->sbc, 0, sizeof (sbc_t)); - return FALSE; -} + frames = in_map.size / (samples_per_frame * sizeof (gint16)); -static GstFlowReturn -sbc_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GstSbcEnc *enc = GST_SBC_ENC (parent); - GstAdapter *adapter = enc->adapter; - GstFlowReturn res = GST_FLOW_OK; + GST_LOG_OBJECT (enc, + "encoding %" G_GSIZE_FORMAT " samples into %u SBC frames", + in_map.size / (enc->channels * sizeof (gint16)), frames); - if (enc->codesize == 0) - goto not_negotiated; + if (frames > 0) { + gsize frame_len; - gst_adapter_push (adapter, buffer); + frame_len = sbc_get_frame_length (&enc->sbc); + outbuf = gst_audio_encoder_allocate_output_buffer (audio_enc, + frames * frame_len); - while (gst_adapter_available (adapter) >= enc->codesize) { - GstMapInfo out_map; - GstBuffer *outbuf; - const guint8 *data; - gint consumed; + if (outbuf == NULL) + goto no_buffer; - outbuf = gst_buffer_new_and_alloc (enc->frame_length); gst_buffer_map (outbuf, &out_map, GST_MAP_WRITE); - data = gst_adapter_map (adapter, enc->codesize); + for (i = 0; i < frames; ++i) { + gssize ret, written = 0; - consumed = sbc_encode (&enc->sbc, (gpointer) data, - enc->codesize, out_map.data, out_map.size, NULL); - - gst_adapter_unmap (adapter); - gst_buffer_unmap (outbuf, &out_map); + ret = sbc_encode (&enc->sbc, in_map.data + (i * samples_per_frame * 2), + samples_per_frame * 2, out_map.data + (i * frame_len), frame_len, + &written); - if (consumed <= 0) { - GST_DEBUG_OBJECT (enc, "consumed < 0, codesize: %d", enc->codesize); - gst_buffer_unref (outbuf); - break; + if (ret < 0 || written != frame_len) { + GST_WARNING_OBJECT (enc, "encoding error, ret = %" G_GSSIZE_FORMAT ", " + "written = %" G_GSSIZE_FORMAT, ret, written); + break; + } } - gst_adapter_flush (adapter, consumed); - /* FIXME: this is not right if we don't have a 1:1 mapping of - * input and output data */ - GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer); - /* we have only 1 frame */ - GST_BUFFER_DURATION (outbuf) = enc->frame_duration; - - res = gst_pad_push (enc->srcpad, outbuf); + gst_buffer_unmap (outbuf, &out_map); - if (res != GST_FLOW_OK) - break; + if (i > 0) + gst_buffer_set_size (outbuf, i * frame_len); + else + gst_buffer_replace (&outbuf, NULL); } - return res; +done: + + gst_buffer_unmap (buffer, &in_map); + + return gst_audio_encoder_finish_frame (audio_enc, outbuf, + i * (samples_per_frame / enc->channels)); -not_negotiated: +/* ERRORS */ +no_buffer: { - GST_ERROR_OBJECT (enc, "output caps not negotiated yet"); - gst_buffer_unref (buffer); - return GST_FLOW_NOT_NEGOTIATED; + GST_ERROR_OBJECT (enc, "could not allocate output buffer"); + goto done; + } +map_failed: + { + GST_ERROR_OBJECT (enc, "could not map input buffer"); + goto done; } } -static GstStateChangeReturn -sbc_enc_change_state (GstElement * element, GstStateChange transition) +static gboolean +gst_sbc_enc_start (GstAudioEncoder * audio_enc) { - GstSbcEnc *enc = GST_SBC_ENC (element); + GstSbcEnc *enc = GST_SBC_ENC (audio_enc); - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_DEBUG ("Setup subband codec"); - sbc_init (&enc->sbc, 0); - break; + GST_INFO_OBJECT (enc, "Setup subband codec"); + sbc_init (&enc->sbc, 0); - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG ("Finish subband codec"); - sbc_finish (&enc->sbc); - break; - - default: - break; - } - - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + return TRUE; } -static void -gst_sbc_enc_finalize (GObject * object) +static gboolean +gst_sbc_enc_stop (GstAudioEncoder * audio_enc) { - GstSbcEnc *enc = GST_SBC_ENC (object); - - if (enc->adapter != NULL) { - g_object_unref (enc->adapter); - enc->adapter = NULL; - } + GstSbcEnc *enc = GST_SBC_ENC (audio_enc); - G_OBJECT_CLASS (parent_class)->finalize (object); -} + GST_INFO_OBJECT (enc, "Finish subband codec"); + sbc_finish (&enc->sbc); -static void -gst_sbc_enc_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstSbcEnc *enc = GST_SBC_ENC (object); - - /* changes to those properties will only happen on the next caps - * negotiation */ - - switch (prop_id) { - case PROP_MODE: - enc->mode = g_value_get_enum (value); - break; - case PROP_ALLOCATION: - enc->allocation = g_value_get_enum (value); - break; - case PROP_BLOCKS: - enc->blocks = g_value_get_enum (value); - break; - case PROP_SUBBANDS: - enc->subbands = g_value_get_enum (value); - break; - case PROP_BITPOOL: - enc->bitpool = g_value_get_int (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} + enc->subbands = 0; + enc->blocks = 0; + enc->rate = 0; + enc->channels = 0; + enc->bitpool = 0; -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; - case PROP_ALLOCATION: - g_value_set_enum (value, enc->allocation); - break; - case PROP_BLOCKS: - g_value_set_enum (value, enc->blocks); - break; - case PROP_SUBBANDS: - g_value_set_enum (value, enc->subbands); - break; - case PROP_BITPOOL: - g_value_set_int (value, enc->bitpool); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + return TRUE; } static void gst_sbc_enc_class_init (GstSbcEncClass * klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstAudioEncoderClass *encoder_class = GST_AUDIO_ENCODER_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - parent_class = g_type_class_peek_parent (klass); - - object_class->set_property = gst_sbc_enc_set_property; - object_class->get_property = gst_sbc_enc_get_property; - object_class->finalize = gst_sbc_enc_finalize; - - element_class->change_state = GST_DEBUG_FUNCPTR (sbc_enc_change_state); - - g_object_class_install_property (object_class, PROP_MODE, - g_param_spec_enum ("channel-mode", "Channel Mode", - "Channel mode", GST_TYPE_SBC_CHANNEL_MODE, - SBC_ENC_DEFAULT_CHANNEL_MODE, G_PARAM_READWRITE)); - - g_object_class_install_property (object_class, PROP_ALLOCATION, - g_param_spec_enum ("allocation-method", "Allocation Method", - "Allocation method", GST_TYPE_SBC_ALLOCATION_METHOD, - SBC_ENC_DEFAULT_ALLOCATION_METHOD, G_PARAM_READWRITE)); - - g_object_class_install_property (object_class, PROP_BLOCKS, - g_param_spec_enum ("blocks", "Blocks", - "Blocks", GST_TYPE_SBC_BLOCKS, - SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); - - g_object_class_install_property (object_class, PROP_SUBBANDS, - g_param_spec_enum ("subbands", "Sub bands", - "Number of sub bands", GST_TYPE_SBC_SUBBANDS, - SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE)); - - g_object_class_install_property (object_class, PROP_BITPOOL, - g_param_spec_int ("bitpool", "Bitpool", - "Bitpool (use 1 for automatic selection)", - SBC_ENC_BITPOOL_AUTO, SBC_ENC_BITPOOL_MAX, - SBC_ENC_BITPOOL_AUTO, G_PARAM_READWRITE)); + encoder_class->start = GST_DEBUG_FUNCPTR (gst_sbc_enc_start); + encoder_class->stop = GST_DEBUG_FUNCPTR (gst_sbc_enc_stop); + encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_sbc_enc_set_format); + encoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_sbc_enc_handle_frame); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sbc_enc_sink_factory)); @@ -609,38 +354,9 @@ gst_sbc_enc_class_init (GstSbcEncClass * klass) static void gst_sbc_enc_init (GstSbcEnc * self) { - self->sinkpad = - gst_pad_new_from_static_template (&sbc_enc_sink_factory, "sink"); - gst_pad_set_event_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_sbc_enc_sink_event)); - gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); - - self->srcpad = gst_pad_new_from_static_template (&sbc_enc_src_factory, "src"); -#if 0 - gst_pad_set_getcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (sbc_enc_src_getcaps)); -#endif - gst_element_add_pad (GST_ELEMENT (self), self->srcpad); - - gst_pad_set_chain_function (self->sinkpad, GST_DEBUG_FUNCPTR (sbc_enc_chain)); - - self->subbands = SBC_ENC_DEFAULT_SUB_BANDS; - self->blocks = SBC_ENC_DEFAULT_BLOCKS; - self->mode = SBC_ENC_DEFAULT_CHANNEL_MODE; - self->allocation = SBC_ENC_DEFAULT_ALLOCATION_METHOD; - self->rate = SBC_ENC_DEFAULT_RATE; - self->channels = SBC_ENC_DEFAULT_CHANNELS; - self->bitpool = SBC_ENC_BITPOOL_AUTO; - - self->frame_length = 0; - self->frame_duration = 0; - - self->adapter = gst_adapter_new (); -} - -gboolean -gst_sbc_enc_plugin_init (GstPlugin * plugin) -{ - return gst_element_register (plugin, "sbcenc", - GST_RANK_NONE, GST_TYPE_SBC_ENC); + self->subbands = 0; + self->blocks = 0; + self->rate = 0; + self->channels = 0; + self->bitpool = 0; } diff --git a/ext/sbc/gstsbcenc.h b/ext/sbc/gstsbcenc.h index 7904c3ef1..129c6b20d 100644 --- a/ext/sbc/gstsbcenc.h +++ b/ext/sbc/gstsbcenc.h @@ -1,9 +1,6 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * +/* GStreamer SBC audio encoder * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> - * + * Copyright (C) 2013 Tim-Philipp Müller <tim centricular net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,7 +19,7 @@ */ #include <gst/gst.h> -#include <gst/base/gstadapter.h> +#include <gst/audio/audio.h> #include <sbc/sbc.h> @@ -43,33 +40,22 @@ typedef struct _GstSbcEnc GstSbcEnc; typedef struct _GstSbcEncClass GstSbcEncClass; struct _GstSbcEnc { - GstElement element; + GstAudioEncoder audio_encoder; - GstPad *sinkpad; - GstPad *srcpad; - GstAdapter *adapter; + /*< private >*/ + gint rate; + gint channels; + gint blocks; + gint subbands; + gint bitpool; - gint rate; - gint channels; - gint mode; - gint blocks; - gint allocation; - gint subbands; - gint bitpool; - - guint codesize; - gint frame_length; - gint frame_duration; - - sbc_t sbc; + sbc_t sbc; }; struct _GstSbcEncClass { - GstElementClass parent_class; + GstAudioEncoderClass audio_encoder_class; }; -GType gst_sbc_enc_get_type(void); - -gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin); +GType gst_sbc_enc_get_type (void); G_END_DECLS diff --git a/ext/sbc/gstsbcutil.c b/ext/sbc/gstsbcutil.c deleted file mode 100644 index 63fef5502..000000000 --- a/ext/sbc/gstsbcutil.c +++ /dev/null @@ -1,400 +0,0 @@ -/* SBC audio utilities - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> - * - * - * 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 <config.h> -#endif - -#include <math.h> -#include "gstsbcutil.h" - -/* - * Selects one rate from a list of possible rates - * TODO - use a better approach to this (it is selecting the last element) - */ -gint -gst_sbc_select_rate_from_list (const GValue * value) -{ - guint size = gst_value_list_get_size (value); - return g_value_get_int (gst_value_list_get_value (value, size - 1)); -} - -/* - * Selects one number of channels option from a range of possible numbers - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint -gst_sbc_select_channels_from_range (const GValue * value) -{ - return gst_value_get_int_range_max (value); -} - -/* - * Selects one number of blocks from a list of possible blocks - * TODO - use a better approach to this (it is selecting the last element) - */ -gint -gst_sbc_select_blocks_from_list (const GValue * value) -{ - guint size = gst_value_list_get_size (value); - return g_value_get_int (gst_value_list_get_value (value, size - 1)); -} - -/* - * Selects one number of subbands from a list - * TODO - use a better approach to this (it is selecting the last element) - */ -gint -gst_sbc_select_subbands_from_list (const GValue * value) -{ - guint size = gst_value_list_get_size (value); - return g_value_get_int (gst_value_list_get_value (value, size - 1)); -} - -/* - * Selects one bitpool option from a range - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint -gst_sbc_select_bitpool_from_range (const GValue * value) -{ - return gst_value_get_int_range_max (value); -} - -/* - * Selects one allocation mode from the ones on the list - * TODO - use a better approach - */ -const gchar * -gst_sbc_get_allocation_from_list (const GValue * value) -{ - guint size = gst_value_list_get_size (value); - return g_value_get_string (gst_value_list_get_value (value, size - 1)); -} - -/* - * Selects one mode from the ones on the list - */ -const gchar * -gst_sbc_get_mode_from_list (const GValue * list, gint channels) -{ - unsigned int i; - const GValue *value; - const gchar *aux; - gboolean joint, stereo, dual, mono; - guint size = gst_value_list_get_size (list); - - joint = stereo = dual = mono = FALSE; - - for (i = 0; i < size; i++) { - value = gst_value_list_get_value (list, i); - aux = g_value_get_string (value); - if (strcmp ("joint", aux) == 0) - joint = TRUE; - else if (strcmp ("stereo", aux) == 0) - stereo = TRUE; - else if (strcmp ("dual", aux) == 0) - dual = TRUE; - else if (strcmp ("mono", aux) == 0) - mono = TRUE; - } - - if (channels == 1 && mono) - return "mono"; - else if (channels == 2) { - if (joint) - return "joint"; - else if (stereo) - return "stereo"; - else if (dual) - return "dual"; - } - - return NULL; -} - -gint -gst_sbc_parse_rate_from_sbc (gint frequency) -{ - switch (frequency) { - case SBC_FREQ_16000: - return 16000; - case SBC_FREQ_32000: - return 32000; - case SBC_FREQ_44100: - return 44100; - case SBC_FREQ_48000: - return 48000; - default: - return 0; - } -} - -gint -gst_sbc_parse_rate_to_sbc (gint rate) -{ - switch (rate) { - case 16000: - return SBC_FREQ_16000; - case 32000: - return SBC_FREQ_32000; - case 44100: - return SBC_FREQ_44100; - case 48000: - return SBC_FREQ_48000; - default: - return -1; - } -} - -gint -gst_sbc_get_channel_number (gint mode) -{ - switch (mode) { - case SBC_MODE_JOINT_STEREO: - case SBC_MODE_STEREO: - case SBC_MODE_DUAL_CHANNEL: - return 2; - case SBC_MODE_MONO: - return 1; - default: - return 0; - } -} - -gint -gst_sbc_parse_subbands_from_sbc (gint subbands) -{ - switch (subbands) { - case SBC_SB_4: - return 4; - case SBC_SB_8: - return 8; - default: - return 0; - } -} - -gint -gst_sbc_parse_subbands_to_sbc (gint subbands) -{ - switch (subbands) { - case 4: - return SBC_SB_4; - case 8: - return SBC_SB_8; - default: - return -1; - } -} - -gint -gst_sbc_parse_blocks_from_sbc (gint blocks) -{ - switch (blocks) { - case SBC_BLK_4: - return 4; - case SBC_BLK_8: - return 8; - case SBC_BLK_12: - return 12; - case SBC_BLK_16: - return 16; - default: - return 0; - } -} - -gint -gst_sbc_parse_blocks_to_sbc (gint blocks) -{ - switch (blocks) { - case 4: - return SBC_BLK_4; - case 8: - return SBC_BLK_8; - case 12: - return SBC_BLK_12; - case 16: - return SBC_BLK_16; - default: - return -1; - } -} - -const gchar * -gst_sbc_parse_mode_from_sbc (gint mode) -{ - switch (mode) { - case SBC_MODE_MONO: - return "mono"; - case SBC_MODE_DUAL_CHANNEL: - return "dual"; - case SBC_MODE_STEREO: - return "stereo"; - case SBC_MODE_JOINT_STEREO: - case SBC_MODE_AUTO: - return "joint"; - default: - return NULL; - } -} - -gint -gst_sbc_parse_mode_to_sbc (const gchar * mode) -{ - if (g_ascii_strcasecmp (mode, "joint") == 0) - return SBC_MODE_JOINT_STEREO; - else if (g_ascii_strcasecmp (mode, "stereo") == 0) - return SBC_MODE_STEREO; - else if (g_ascii_strcasecmp (mode, "dual") == 0) - return SBC_MODE_DUAL_CHANNEL; - else if (g_ascii_strcasecmp (mode, "mono") == 0) - return SBC_MODE_MONO; - else if (g_ascii_strcasecmp (mode, "auto") == 0) - return SBC_MODE_JOINT_STEREO; - else - return -1; -} - -const gchar * -gst_sbc_parse_allocation_from_sbc (gint alloc) -{ - switch (alloc) { - case SBC_AM_LOUDNESS: - return "loudness"; - case SBC_AM_SNR: - return "snr"; - case SBC_AM_AUTO: - return "loudness"; - default: - return NULL; - } -} - -gint -gst_sbc_parse_allocation_to_sbc (const gchar * allocation) -{ - if (g_ascii_strcasecmp (allocation, "loudness") == 0) - return SBC_AM_LOUDNESS; - else if (g_ascii_strcasecmp (allocation, "snr") == 0) - return SBC_AM_SNR; - else - return SBC_AM_LOUDNESS; -} - -GstCaps * -gst_sbc_parse_caps_from_sbc (sbc_t * sbc) -{ - GstCaps *caps; - const gchar *mode_str; - const gchar *allocation_str; - - mode_str = gst_sbc_parse_mode_from_sbc (sbc->mode); - allocation_str = gst_sbc_parse_allocation_from_sbc (sbc->allocation); - caps = gst_caps_new_simple ("audio/x-sbc", - "rate", G_TYPE_INT, - gst_sbc_parse_rate_from_sbc (sbc->frequency), - "channels", G_TYPE_INT, - gst_sbc_get_channel_number (sbc->mode), - "channel-mode", G_TYPE_STRING, mode_str, - "subbands", G_TYPE_INT, - gst_sbc_parse_subbands_from_sbc (sbc->subbands), - "blocks", G_TYPE_INT, - gst_sbc_parse_blocks_from_sbc (sbc->blocks), - "allocation-method", G_TYPE_STRING, allocation_str, - "bitpool", G_TYPE_INT, sbc->bitpool, NULL); - - return caps; -} - -/* FIXME: this function is not needed, just use gst_structure_set() */ -/** - * Sets the int field_value to the param "field" on the structure. - * value is used to do the operation, it must be a uninitialized (zero-filled) - * GValue, it will be left unitialized at the end of the function. - */ -void -gst_sbc_util_set_structure_int_param (GstStructure * structure, - const gchar * field, gint field_value, GValue * value) -{ - value = g_value_init (value, G_TYPE_INT); - g_value_set_int (value, field_value); - gst_structure_set_value (structure, field, value); - g_value_unset (value); -} - -/** - * Sets the string field_value to the param "field" on the structure. - * value is used to do the operation, it must be a uninitialized (zero-filled) - * GValue, it will be left unitialized at the end of the function. - */ -void -gst_sbc_util_set_structure_string_param (GstStructure * structure, - const gchar * field, const gchar * field_value, GValue * value) -{ - value = g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, field_value); - gst_structure_set_value (structure, field, value); - g_value_unset (value); -} - -gboolean -gst_sbc_util_fill_sbc_params (sbc_t * sbc, GstCaps * caps) -{ - GstStructure *structure; - gint rate, channels, subbands, blocks, bitpool; - const gchar *mode; - const gchar *allocation; - - g_assert (gst_caps_is_fixed (caps)); - - structure = gst_caps_get_structure (caps, 0); - - if (!gst_structure_get_int (structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int (structure, "channels", &channels)) - return FALSE; - if (!gst_structure_get_int (structure, "subbands", &subbands)) - return FALSE; - if (!gst_structure_get_int (structure, "blocks", &blocks)) - return FALSE; - if (!gst_structure_get_int (structure, "bitpool", &bitpool)) - return FALSE; - - if (!(mode = gst_structure_get_string (structure, "channel-mode"))) - return FALSE; - if (!(allocation = gst_structure_get_string (structure, "allocation-method"))) - return FALSE; - - if (channels == 1 && strcmp (mode, "mono") != 0) - return FALSE; - - sbc->frequency = gst_sbc_parse_rate_to_sbc (rate); - sbc->blocks = gst_sbc_parse_blocks_to_sbc (blocks); - sbc->subbands = gst_sbc_parse_subbands_to_sbc (subbands); - sbc->bitpool = bitpool; - sbc->mode = gst_sbc_parse_mode_to_sbc (mode); - sbc->allocation = gst_sbc_parse_allocation_to_sbc (allocation); - - return TRUE; -} diff --git a/ext/sbc/gstsbcutil.h b/ext/sbc/gstsbcutil.h deleted file mode 100644 index 8d594c0fa..000000000 --- a/ext/sbc/gstsbcutil.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SBC audio utilities - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> - * - * - * 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 - * - */ - -#include <gst/gst.h> - -#include <sbc/sbc.h> -#include <string.h> - -#define SBC_AM_AUTO 0x02 -#define SBC_MODE_AUTO 0x04 - -gint gst_sbc_select_rate_from_list(const GValue *value); - -gint gst_sbc_select_channels_from_range(const GValue *value); - -gint gst_sbc_select_blocks_from_list(const GValue *value); - -gint gst_sbc_select_subbands_from_list(const GValue *value); - -gint gst_sbc_select_bitpool_from_range(const GValue *value); - -const gchar *gst_sbc_get_allocation_from_list(const GValue *value); - -const gchar *gst_sbc_get_mode_from_list(const GValue *value, gint channels); - -gint gst_sbc_get_channel_number(gint mode); -gint gst_sbc_parse_rate_from_sbc(gint frequency); -gint gst_sbc_parse_rate_to_sbc(gint rate); - -gint gst_sbc_parse_subbands_from_sbc(gint subbands); -gint gst_sbc_parse_subbands_to_sbc(gint subbands); - -gint gst_sbc_parse_blocks_from_sbc(gint blocks); -gint gst_sbc_parse_blocks_to_sbc(gint blocks); - -const gchar *gst_sbc_parse_mode_from_sbc(gint mode); -gint gst_sbc_parse_mode_to_sbc(const gchar *mode); - -const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc); -gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation); - -GstCaps *gst_sbc_parse_caps_from_sbc(sbc_t *sbc); - -void gst_sbc_util_set_structure_int_param(GstStructure *structure, - const gchar *field, gint field_value, - GValue *value); - -void gst_sbc_util_set_structure_string_param(GstStructure *structure, - const gchar *field, const gchar *field_value, - GValue *value); - -gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps); |