diff mbox

[FFmpeg-devel,5/9] sbc: implement SBC encoder (low-complexity subband codec)

Message ID 20180227235613.vit3rh2vprgqppaw@gnuage.org
State Superseded
Headers show

Commit Message

Aurelien Jacobs Feb. 27, 2018, 11:56 p.m. UTC
On Sat, Feb 24, 2018 at 09:31:30PM +0000, Rostislav Pehlivanov wrote:
> On 24 February 2018 at 12:01, Aurelien Jacobs <aurel@gnuage.org> wrote:
> 
> > On Thu, Feb 22, 2018 at 06:18:48PM +0000, Rostislav Pehlivanov wrote:
> > > On 21 February 2018 at 22:37, Aurelien Jacobs <aurel@gnuage.org> wrote:
> > >
> > > > This was originally based on libsbc, and was fully integrated into
> > ffmpeg.
> > > > ---
> > > >  doc/general.texi         |   2 +-
> > > >  libavcodec/Makefile      |   1 +
> > > >  libavcodec/allcodecs.c   |   1 +
> > > >  libavcodec/sbcdsp.c      | 382 ++++++++++++++++++++++++++++++
> > > > +++++++++++++
> > > >  libavcodec/sbcdsp.h      |  83 ++++++++++
> > > >  libavcodec/sbcdsp_data.c | 329 +++++++++++++++++++++++++++++++++++++
> > > >  libavcodec/sbcdsp_data.h |  55 +++++++
> > > >  libavcodec/sbcenc.c      | 411 ++++++++++++++++++++++++++++++
> > > > +++++++++++++++++
> > > >  8 files changed, 1263 insertions(+), 1 deletion(-)
> > > >  create mode 100644 libavcodec/sbcdsp.c
> > > >  create mode 100644 libavcodec/sbcdsp.h
> > > >  create mode 100644 libavcodec/sbcdsp_data.c
> > > >  create mode 100644 libavcodec/sbcdsp_data.h
> > > >  create mode 100644 libavcodec/sbcenc.c
> > > >
> > > > +
> > > > +#define OFFSET(x) offsetof(SBCEncContext, x)
> > > > +#define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> > > > +static const AVOption options[] = {
> > > > +    { "joint_stereo", "use joint stereo",
> > > > +      OFFSET(joint_stereo), AV_OPT_TYPE_BOOL, { .i64 =  0 }, 0,   1,
> > AE },
> > > >
> > > +    { "dual_channel", "use dual channel",
> > > > +      OFFSET(dual_channel), AV_OPT_TYPE_BOOL, { .i64 =  0 }, 0,   1,
> > AE },
> > > >
> > >
> > > Erm those 2 things should be decided by the encoder, not by exposing them
> > > to the user. The encoder should decide which mode has lower distortion
> > for
> > > a given signal.
> >
> > See bellow.
> >
> > > > +    { "subbands",     "number of subbands (4 or 8)",
> > > > +      OFFSET(subbands),     AV_OPT_TYPE_INT,  { .i64 =  8 }, 4,   8,
> > AE },
> > > >
> > >
> > > The encoder doesn't check if the value isn't 4 or 8 so 5, 6 and 7 are all
> > > accepted. Similar issue to the previous option too.
> >
> > OK, fixed.
> >
> > > > +    { "bitpool",      "bitpool value",
> > > > +      OFFSET(bitpool),      AV_OPT_TYPE_INT,  { .i64 = 32 }, 0, 255,
> > AE },
> > > >
> > >
> > > This should be controlled by the bitrate setting. Either have a function
> > to
> > > translate bitrate to bitpool value or a table which approximately maps
> > > bitrate values supplied to bitpools. You could expose it directly as well
> > > as mapping it to a bitrate value by using the global_quality setting so
> > it
> > > shouldn't be a custom encoder option.
> >
> > Indeed, this is better this way, thanks for the suggestion.
> >
> > > > +    { "blocks",       "number of blocks (4, 8, 12 or 16)",
> > > > +      OFFSET(blocks),       AV_OPT_TYPE_INT,  { .i64 = 16 }, 4,  16,
> > AE },
> > > > +    { "snr",          "use SNR mode (instead of loudness)",
> > > > +      OFFSET(allocation),   AV_OPT_TYPE_BOOL, { .i64 =  0 }, 0,   1,
> > AE },
> > > >
> > >
> > > SNR mode too needs to be decided by the encoder rather than exposing it
> > as
> > > a setting.
> >
> > See bellow.
> >
> > > > +    { "msbc",         "use mSBC mode (wideband speech mono SBC)",
> > > >
> > >
> > > Add a profile fallback setting for this as well, like in aac where
> > -aac_ltp
> > > turns LTP mode on and -profile:a aac_ltp does the same.
> >
> > Not sure of the benefits of having 2 redundant way to do this, but OK.
> >
> > > You don't have to make the encoder decide which stereo coupling mode or
> > > snr/loudness setting to use, you can implement that with a later patch.
> > > I think you should remove the "blocks" and "subbands" settings as well
> > and
> > > instead replace those with a single "latency" setting like the native
> > Opus
> > > encoder in milliseconds which would adjust both of them on init to set
> > the
> > > frame size. This would also allow the encoder to change them. Again, you
> > > don't have to do this now, you can send a patch which adds a "latency"
> > > option later.
> >
> > I can see the value in what you propose, and I think that indeed, it
> > would be great for the encoder to choose by itself the most appropriate
> > parameters by default.
> > But on the other hand, we are talking about a codec targetting some
> > hardware decoders (blutetooth speakers, headsets...), and all those
> > parameters have to be negotiated between the encoder and the hardware
> > decoder. So I think it is mandatory to keep all those parameters
> > accessible to be able to setup the encoder according to the target
> > hardware capabilities.
> >
> >
> Hardware isn't as limited as you might think it is, and there's also no
> negotiation happening between encoders and decoders IRL (except for modern
> streaming protocols which suck). While its true that we've had some issues
> with hardware decoders not supporting rarely used features we wait for
> users to report them and don't assume that all hardware violates the specs.

It is not really about violating the specs, but more about very cheap or
old hardware that may only support some limited combination of settings.
And the bluetooth stack that will negotiate those settings also
according to the actual RF link quality.

> And looking at the code, only 2 settings strike out as being able to cause
> issues: the frame size, controlled by subbands and blocks and the msbc
> mode.

I don't think msbc is allowed for bluetooth, but indeed, I think
subbands and blocks are the one that may be troublesome, along with
the bitpool.

> There's no way in hell that dual stereo and joint coding are
> unsupported (except for mono streams obviously but we can detect that)

Agreed.

> like I said, you can add a setting to control the frame size later on, and
> the SNR setting is far too trivial to not be implemented.
> Hence I think that only the msbc setting and the latency need to be exposed.
> If someone reports that a device doesn't support e.g. non-SNR mode we'll
> add an override. We can easily add options, but removing them requires a 2
> year deprecation period. In all cases, the encoder should decide the
> parameters unless told otherwise, rather than having the user do so.

OK. It is indeed easier to add options later on when the need arise,
rather than removing them (even though I can't imagine why we would
remove them).
So I've updated the patch with only a msbc and a delay option and
I've added some sane parameters decisions, mainly based on the following
study :
https://pdfs.semanticscholar.org/1f19/561d03bc88b67728375566c95bbf77e730d5.pdf
This seems to give pretty good results.

Comments

Rostislav Pehlivanov Feb. 28, 2018, 12:59 a.m. UTC | #1
On 27 February 2018 at 23:56, Aurelien Jacobs <aurel@gnuage.org> wrote:

>
> So I've updated the patch with only a msbc and a delay option and
> I've added some sane parameters decisions, mainly based on the following
> study :
> https://pdfs.semanticscholar.org/1f19/561d03bc88b67728375566c95bbf77
> e730d5.pdf
> This seems to give pretty good results.
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
I think you ought to use a float for this, like:

>{ "sbc_delay", "Maximum delay in milliseconds", offsetof(<context>,
<option>), AV_OPT_TYPE_FLOAT, { .dbl = <some reasonable default> }, <min>,
<max>, <flags>, <option name in structure> },

Apart from that, what's up with the weird if (!sbc->init) { } block in the
main encoding function? Shouldn't that be done in sbc_encode_init()?
diff mbox

Patch

From 5c6f310c9287dd5713e3b48092e24778a80e1a36 Mon Sep 17 00:00:00 2001
From: Aurelien Jacobs <aurel@gnuage.org>
Date: Sun, 17 Dec 2017 19:59:30 +0100
Subject: [PATCH 5/9] sbc: implement SBC encoder (low-complexity subband codec)

This was originally based on libsbc, and was fully integrated into ffmpeg.
---
 doc/general.texi           |   2 +-
 libavcodec/Makefile        |   1 +
 libavcodec/allcodecs.c     |   1 +
 libavcodec/avcodec.h       |   2 +
 libavcodec/options_table.h |   1 +
 libavcodec/profiles.c      |   5 +
 libavcodec/profiles.h      |   1 +
 libavcodec/sbcdsp.c        | 382 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/sbcdsp.h        |  83 +++++++++
 libavcodec/sbcdsp_data.c   | 329 ++++++++++++++++++++++++++++++++++
 libavcodec/sbcdsp_data.h   |  55 ++++++
 libavcodec/sbcenc.c        | 428 +++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 1289 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/sbcdsp.c
 create mode 100644 libavcodec/sbcdsp.h
 create mode 100644 libavcodec/sbcdsp_data.c
 create mode 100644 libavcodec/sbcdsp_data.h
 create mode 100644 libavcodec/sbcenc.c

diff --git a/doc/general.texi b/doc/general.texi
index 5827af3841..de13a7695e 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -1142,7 +1142,7 @@  following image formats are supported:
     @tab Real low bitrate AC-3 codec
 @item RealAudio Lossless     @tab     @tab  X
 @item RealAudio SIPR / ACELP.NET @tab     @tab  X
-@item SBC (low-complexity subband codec) @tab     @tab  X
+@item SBC (low-complexity subband codec) @tab  X  @tab  X
     @tab Used in Bluetooth A2DP
 @item Shorten                @tab     @tab  X
 @item Sierra VMD audio       @tab     @tab  X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 01bc15d9b7..ff6c9f8b2c 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -588,6 +588,7 @@  OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
 OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
 OBJS-$(CONFIG_SBC_DECODER)             += sbcdec.o sbcdec_data.o sbc.o
+OBJS-$(CONFIG_SBC_ENCODER)             += sbcenc.o sbc.o sbcdsp.o sbcdsp_data.o
 OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o svq13.o h263data.o
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
                                           h263.o ituh263enc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 4ddf92f006..71719595c6 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -453,6 +453,7 @@  extern AVCodec ff_ra_144_encoder;
 extern AVCodec ff_ra_144_decoder;
 extern AVCodec ff_ra_288_decoder;
 extern AVCodec ff_ralf_decoder;
+extern AVCodec ff_sbc_encoder;
 extern AVCodec ff_sbc_decoder;
 extern AVCodec ff_shorten_decoder;
 extern AVCodec ff_sipr_decoder;
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 769f210c07..a8322fb62a 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2926,6 +2926,8 @@  typedef struct AVCodecContext {
 #define FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS                0xc3
 #define FF_PROFILE_MJPEG_JPEG_LS                         0xf7
 
+#define FF_PROFILE_SBC_MSBC                         1
+
     /**
      * level
      * - encoding: Set by user.
diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h
index ac9ce4b301..5a5eae65fb 100644
--- a/libavcodec/options_table.h
+++ b/libavcodec/options_table.h
@@ -300,6 +300,7 @@  static const AVOption avcodec_options[] = {
 {"mpeg4_main", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG4_MAIN }, INT_MIN, INT_MAX, V|E, "profile"},
 {"mpeg4_asp",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG4_ADVANCED_SIMPLE }, INT_MIN, INT_MAX, V|E, "profile"},
 {"main10",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_HEVC_MAIN_10 }, INT_MIN, INT_MAX, V|E, "profile"},
+{"msbc",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_SBC_MSBC }, INT_MIN, INT_MAX, A|E, "profile"},
 {"level", NULL, OFFSET(level), AV_OPT_TYPE_INT, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, "level"},
 {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, "level"},
 {"lowres", "decode at 1= 1/2, 2=1/4, 3=1/8 resolutions", OFFSET(lowres), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, V|A|D},
diff --git a/libavcodec/profiles.c b/libavcodec/profiles.c
index 30498efedf..721587b3a6 100644
--- a/libavcodec/profiles.c
+++ b/libavcodec/profiles.c
@@ -140,4 +140,9 @@  const AVProfile ff_vp9_profiles[] = {
     { FF_PROFILE_UNKNOWN },
 };
 
+const AVProfile ff_sbc_profiles[] = {
+    { FF_PROFILE_SBC_MSBC, "mSBC" },
+    { FF_PROFILE_UNKNOWN },
+};
+
 #endif /* !CONFIG_SMALL */
diff --git a/libavcodec/profiles.h b/libavcodec/profiles.h
index eb18b406af..3783c10c39 100644
--- a/libavcodec/profiles.h
+++ b/libavcodec/profiles.h
@@ -31,5 +31,6 @@  extern const AVProfile ff_mpeg2_video_profiles[];
 extern const AVProfile ff_mpeg4_video_profiles[];
 extern const AVProfile ff_vc1_profiles[];
 extern const AVProfile ff_vp9_profiles[];
+extern const AVProfile ff_sbc_profiles[];
 
 #endif /* AVCODEC_PROFILES_H */
diff --git a/libavcodec/sbcdsp.c b/libavcodec/sbcdsp.c
new file mode 100644
index 0000000000..e155387f0d
--- /dev/null
+++ b/libavcodec/sbcdsp.c
@@ -0,0 +1,382 @@ 
+/*
+ * Bluetooth low-complexity, subband codec (SBC)
+ *
+ * Copyright (C) 2017  Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2012-2013  Intel Corporation
+ * Copyright (C) 2008-2010  Nokia Corporation
+ * Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SBC basic "building bricks"
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "libavutil/common.h"
+#include "libavutil/intmath.h"
+#include "libavutil/intreadwrite.h"
+#include "sbc.h"
+#include "sbcdsp.h"
+#include "sbcdsp_data.h"
+
+/*
+ * A reference C code of analysis filter with SIMD-friendly tables
+ * reordering and code layout. This code can be used to develop platform
+ * specific SIMD optimizations. Also it may be used as some kind of test
+ * for compiler autovectorization capabilities (who knows, if the compiler
+ * is very good at this stuff, hand optimized assembly may be not strictly
+ * needed for some platform).
+ *
+ * Note: It is also possible to make a simple variant of analysis filter,
+ * which needs only a single constants table without taking care about
+ * even/odd cases. This simple variant of filter can be implemented without
+ * input data permutation. The only thing that would be lost is the
+ * possibility to use pairwise SIMD multiplications. But for some simple
+ * CPU cores without SIMD extensions it can be useful. If anybody is
+ * interested in implementing such variant of a filter, sourcecode from
+ * bluez versions 4.26/4.27 can be used as a reference and the history of
+ * the changes in git repository done around that time may be worth checking.
+ */
+
+static av_always_inline void sbc_analyze_simd(const int16_t *in, int32_t *out,
+                                              const int16_t *consts,
+                                              unsigned subbands)
+{
+    int32_t t1[8];
+    int16_t t2[8];
+    int i, j, hop = 0;
+
+    /* rounding coefficient */
+    for (i = 0; i < subbands; i++)
+        t1[i] = 1 << (SBC_PROTO_FIXED_SCALE - 1);
+
+    /* low pass polyphase filter */
+    for (hop = 0; hop < 10*subbands; hop += 2*subbands)
+        for (i = 0; i < 2*subbands; i++)
+            t1[i >> 1] += in[hop + i] * consts[hop + i];
+
+    /* scaling */
+    for (i = 0; i < subbands; i++)
+        t2[i] = t1[i] >> SBC_PROTO_FIXED_SCALE;
+
+    memset(t1, 0, sizeof(t1));
+
+    /* do the cos transform */
+    for (i = 0; i < subbands/2; i++)
+        for (j = 0; j < 2*subbands; j++)
+            t1[j>>1] += t2[i * 2 + (j&1)] * consts[10*subbands + i*2*subbands + j];
+
+    for (i = 0; i < subbands; i++)
+        out[i] = t1[i] >> (SBC_COS_TABLE_FIXED_SCALE - SCALE_OUT_BITS);
+}
+
+static void sbc_analyze_4_simd(const int16_t *in, int32_t *out,
+                               const int16_t *consts)
+{
+    sbc_analyze_simd(in, out, consts, 4);
+}
+
+static void sbc_analyze_8_simd(const int16_t *in, int32_t *out,
+                               const int16_t *consts)
+{
+    sbc_analyze_simd(in, out, consts, 8);
+}
+
+static inline void sbc_analyze_4b_4s_simd(SBCDSPContext *s,
+                                          int16_t *x, int32_t *out, int out_stride)
+{
+    /* Analyze blocks */
+    s->sbc_analyze_4(x + 12, out, ff_sbcdsp_analysis_consts_fixed4_simd_odd);
+    out += out_stride;
+    s->sbc_analyze_4(x + 8, out, ff_sbcdsp_analysis_consts_fixed4_simd_even);
+    out += out_stride;
+    s->sbc_analyze_4(x + 4, out, ff_sbcdsp_analysis_consts_fixed4_simd_odd);
+    out += out_stride;
+    s->sbc_analyze_4(x + 0, out, ff_sbcdsp_analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_simd(SBCDSPContext *s,
+                                          int16_t *x, int32_t *out, int out_stride)
+{
+    /* Analyze blocks */
+    s->sbc_analyze_8(x + 24, out, ff_sbcdsp_analysis_consts_fixed8_simd_odd);
+    out += out_stride;
+    s->sbc_analyze_8(x + 16, out, ff_sbcdsp_analysis_consts_fixed8_simd_even);
+    out += out_stride;
+    s->sbc_analyze_8(x + 8, out, ff_sbcdsp_analysis_consts_fixed8_simd_odd);
+    out += out_stride;
+    s->sbc_analyze_8(x + 0, out, ff_sbcdsp_analysis_consts_fixed8_simd_even);
+}
+
+static inline void sbc_analyze_1b_8s_simd_even(SBCDSPContext *s,
+                                               int16_t *x, int32_t *out,
+                                               int out_stride);
+
+static inline void sbc_analyze_1b_8s_simd_odd(SBCDSPContext *s,
+                                              int16_t *x, int32_t *out,
+                                              int out_stride)
+{
+    s->sbc_analyze_8(x, out, ff_sbcdsp_analysis_consts_fixed8_simd_odd);
+    s->sbc_analyze_8s = sbc_analyze_1b_8s_simd_even;
+}
+
+static inline void sbc_analyze_1b_8s_simd_even(SBCDSPContext *s,
+                                               int16_t *x, int32_t *out,
+                                               int out_stride)
+{
+    s->sbc_analyze_8(x, out, ff_sbcdsp_analysis_consts_fixed8_simd_even);
+    s->sbc_analyze_8s = sbc_analyze_1b_8s_simd_odd;
+}
+
+/*
+ * Input data processing functions. The data is endian converted if needed,
+ * channels are deintrleaved and audio samples are reordered for use in
+ * SIMD-friendly analysis filter function. The results are put into "X"
+ * array, getting appended to the previous data (or it is better to say
+ * prepended, as the buffer is filled from top to bottom). Old data is
+ * discarded when neededed, but availability of (10 * nrof_subbands)
+ * contiguous samples is always guaranteed for the input to the analysis
+ * filter. This is achieved by copying a sufficient part of old data
+ * to the top of the buffer on buffer wraparound.
+ */
+
+static int sbc_enc_process_input_4s(int position, const uint8_t *pcm,
+                                    int16_t X[2][SBC_X_BUFFER_SIZE],
+                                    int nsamples, int nchannels)
+{
+    int c;
+
+    /* handle X buffer wraparound */
+    if (position < nsamples) {
+        for (c = 0; c < nchannels; c++)
+            memcpy(&X[c][SBC_X_BUFFER_SIZE - 40], &X[c][position],
+                            36 * sizeof(int16_t));
+        position = SBC_X_BUFFER_SIZE - 40;
+    }
+
+    /* copy/permutate audio samples */
+    for (; nsamples >= 8; nsamples -= 8, pcm += 16 * nchannels) {
+        position -= 8;
+        for (c = 0; c < nchannels; c++) {
+            int16_t *x = &X[c][position];
+            x[0] = AV_RN16(pcm + 14*nchannels + 2*c);
+            x[1] = AV_RN16(pcm +  6*nchannels + 2*c);
+            x[2] = AV_RN16(pcm + 12*nchannels + 2*c);
+            x[3] = AV_RN16(pcm +  8*nchannels + 2*c);
+            x[4] = AV_RN16(pcm +  0*nchannels + 2*c);
+            x[5] = AV_RN16(pcm +  4*nchannels + 2*c);
+            x[6] = AV_RN16(pcm +  2*nchannels + 2*c);
+            x[7] = AV_RN16(pcm + 10*nchannels + 2*c);
+        }
+    }
+
+    return position;
+}
+
+static int sbc_enc_process_input_8s(int position, const uint8_t *pcm,
+                                    int16_t X[2][SBC_X_BUFFER_SIZE],
+                                    int nsamples, int nchannels)
+{
+    int c;
+
+    /* handle X buffer wraparound */
+    if (position < nsamples) {
+        for (c = 0; c < nchannels; c++)
+            memcpy(&X[c][SBC_X_BUFFER_SIZE - 72], &X[c][position],
+                            72 * sizeof(int16_t));
+        position = SBC_X_BUFFER_SIZE - 72;
+    }
+
+    if (position % 16 == 8) {
+        position -= 8;
+        nsamples -= 8;
+        for (c = 0; c < nchannels; c++) {
+            int16_t *x = &X[c][position];
+            x[0] = AV_RN16(pcm + 14*nchannels + 2*c);
+            x[2] = AV_RN16(pcm + 12*nchannels + 2*c);
+            x[3] = AV_RN16(pcm +  0*nchannels + 2*c);
+            x[4] = AV_RN16(pcm + 10*nchannels + 2*c);
+            x[5] = AV_RN16(pcm +  2*nchannels + 2*c);
+            x[6] = AV_RN16(pcm +  8*nchannels + 2*c);
+            x[7] = AV_RN16(pcm +  4*nchannels + 2*c);
+            x[8] = AV_RN16(pcm +  6*nchannels + 2*c);
+        }
+        pcm += 16 * nchannels;
+    }
+
+    /* copy/permutate audio samples */
+    for (; nsamples >= 16; nsamples -= 16, pcm += 32 * nchannels) {
+        position -= 16;
+        for (c = 0; c < nchannels; c++) {
+            int16_t *x = &X[c][position];
+            x[0]  = AV_RN16(pcm + 30*nchannels + 2*c);
+            x[1]  = AV_RN16(pcm + 14*nchannels + 2*c);
+            x[2]  = AV_RN16(pcm + 28*nchannels + 2*c);
+            x[3]  = AV_RN16(pcm + 16*nchannels + 2*c);
+            x[4]  = AV_RN16(pcm + 26*nchannels + 2*c);
+            x[5]  = AV_RN16(pcm + 18*nchannels + 2*c);
+            x[6]  = AV_RN16(pcm + 24*nchannels + 2*c);
+            x[7]  = AV_RN16(pcm + 20*nchannels + 2*c);
+            x[8]  = AV_RN16(pcm + 22*nchannels + 2*c);
+            x[9]  = AV_RN16(pcm +  6*nchannels + 2*c);
+            x[10] = AV_RN16(pcm + 12*nchannels + 2*c);
+            x[11] = AV_RN16(pcm +  0*nchannels + 2*c);
+            x[12] = AV_RN16(pcm + 10*nchannels + 2*c);
+            x[13] = AV_RN16(pcm +  2*nchannels + 2*c);
+            x[14] = AV_RN16(pcm +  8*nchannels + 2*c);
+            x[15] = AV_RN16(pcm +  4*nchannels + 2*c);
+        }
+    }
+
+    if (nsamples == 8) {
+        position -= 8;
+        for (c = 0; c < nchannels; c++) {
+            int16_t *x = &X[c][position];
+            x[-7] = AV_RN16(pcm + 14*nchannels + 2*c);
+            x[1]  = AV_RN16(pcm +  6*nchannels + 2*c);
+            x[2]  = AV_RN16(pcm + 12*nchannels + 2*c);
+            x[3]  = AV_RN16(pcm +  0*nchannels + 2*c);
+            x[4]  = AV_RN16(pcm + 10*nchannels + 2*c);
+            x[5]  = AV_RN16(pcm +  2*nchannels + 2*c);
+            x[6]  = AV_RN16(pcm +  8*nchannels + 2*c);
+            x[7]  = AV_RN16(pcm +  4*nchannels + 2*c);
+        }
+    }
+
+    return position;
+}
+
+static void sbc_calc_scalefactors(int32_t sb_sample_f[16][2][8],
+                                  uint32_t scale_factor[2][8],
+                                  int blocks, int channels, int subbands)
+{
+    int ch, sb, blk;
+    for (ch = 0; ch < channels; ch++) {
+        for (sb = 0; sb < subbands; sb++) {
+            uint32_t x = 1 << SCALE_OUT_BITS;
+            for (blk = 0; blk < blocks; blk++) {
+                int32_t tmp = FFABS(sb_sample_f[blk][ch][sb]);
+                if (tmp != 0)
+                    x |= tmp - 1;
+            }
+            scale_factor[ch][sb] = (31 - SCALE_OUT_BITS) - ff_clz(x);
+        }
+    }
+}
+
+static int sbc_calc_scalefactors_j(int32_t sb_sample_f[16][2][8],
+                                   uint32_t scale_factor[2][8],
+                                   int blocks, int subbands)
+{
+    int blk, joint = 0;
+    int32_t tmp0, tmp1;
+    uint32_t x, y;
+
+    /* last subband does not use joint stereo */
+    int sb = subbands - 1;
+    x = 1 << SCALE_OUT_BITS;
+    y = 1 << SCALE_OUT_BITS;
+    for (blk = 0; blk < blocks; blk++) {
+        tmp0 = FFABS(sb_sample_f[blk][0][sb]);
+        tmp1 = FFABS(sb_sample_f[blk][1][sb]);
+        if (tmp0 != 0)
+            x |= tmp0 - 1;
+        if (tmp1 != 0)
+            y |= tmp1 - 1;
+    }
+    scale_factor[0][sb] = (31 - SCALE_OUT_BITS) - ff_clz(x);
+    scale_factor[1][sb] = (31 - SCALE_OUT_BITS) - ff_clz(y);
+
+    /* the rest of subbands can use joint stereo */
+    while (--sb >= 0) {
+        int32_t sb_sample_j[16][2];
+        x = 1 << SCALE_OUT_BITS;
+        y = 1 << SCALE_OUT_BITS;
+        for (blk = 0; blk < blocks; blk++) {
+            tmp0 = sb_sample_f[blk][0][sb];
+            tmp1 = sb_sample_f[blk][1][sb];
+            sb_sample_j[blk][0] = (tmp0 >> 1) + (tmp1 >> 1);
+            sb_sample_j[blk][1] = (tmp0 >> 1) - (tmp1 >> 1);
+            tmp0 = FFABS(tmp0);
+            tmp1 = FFABS(tmp1);
+            if (tmp0 != 0)
+                x |= tmp0 - 1;
+            if (tmp1 != 0)
+                y |= tmp1 - 1;
+        }
+        scale_factor[0][sb] = (31 - SCALE_OUT_BITS) -
+            ff_clz(x);
+        scale_factor[1][sb] = (31 - SCALE_OUT_BITS) -
+            ff_clz(y);
+        x = 1 << SCALE_OUT_BITS;
+        y = 1 << SCALE_OUT_BITS;
+        for (blk = 0; blk < blocks; blk++) {
+            tmp0 = FFABS(sb_sample_j[blk][0]);
+            tmp1 = FFABS(sb_sample_j[blk][1]);
+            if (tmp0 != 0)
+                x |= tmp0 - 1;
+            if (tmp1 != 0)
+                y |= tmp1 - 1;
+        }
+        x = (31 - SCALE_OUT_BITS) - ff_clz(x);
+        y = (31 - SCALE_OUT_BITS) - ff_clz(y);
+
+        /* decide whether to use joint stereo for this subband */
+        if ((scale_factor[0][sb] + scale_factor[1][sb]) > x + y) {
+            joint |= 1 << (subbands - 1 - sb);
+            scale_factor[0][sb] = x;
+            scale_factor[1][sb] = y;
+            for (blk = 0; blk < blocks; blk++) {
+                sb_sample_f[blk][0][sb] = sb_sample_j[blk][0];
+                sb_sample_f[blk][1][sb] = sb_sample_j[blk][1];
+            }
+        }
+    }
+
+    /* bitmask with the information about subbands using joint stereo */
+    return joint;
+}
+
+/*
+ * Detect CPU features and setup function pointers
+ */
+av_cold void ff_sbcdsp_init(SBCDSPContext *s)
+{
+    /* Default implementation for analyze functions */
+    s->sbc_analyze_4 = sbc_analyze_4_simd;
+    s->sbc_analyze_8 = sbc_analyze_8_simd;
+    s->sbc_analyze_4s = sbc_analyze_4b_4s_simd;
+    if (s->increment == 1)
+        s->sbc_analyze_8s = sbc_analyze_1b_8s_simd_odd;
+    else
+        s->sbc_analyze_8s = sbc_analyze_4b_8s_simd;
+
+    /* Default implementation for input reordering / deinterleaving */
+    s->sbc_enc_process_input_4s = sbc_enc_process_input_4s;
+    s->sbc_enc_process_input_8s = sbc_enc_process_input_8s;
+
+    /* Default implementation for scale factors calculation */
+    s->sbc_calc_scalefactors = sbc_calc_scalefactors;
+    s->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j;
+}
diff --git a/libavcodec/sbcdsp.h b/libavcodec/sbcdsp.h
new file mode 100644
index 0000000000..66ed7d324e
--- /dev/null
+++ b/libavcodec/sbcdsp.h
@@ -0,0 +1,83 @@ 
+/*
+ * Bluetooth low-complexity, subband codec (SBC)
+ *
+ * Copyright (C) 2017  Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2008-2010  Nokia Corporation
+ * Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SBC basic "building bricks"
+ */
+
+#ifndef AVCODEC_SBCDSP_H
+#define AVCODEC_SBCDSP_H
+
+#include "sbc.h"
+#include "sbcdsp_data.h"
+
+#define SCALE_OUT_BITS 15
+#define SBC_X_BUFFER_SIZE 328
+
+typedef struct sbc_dsp_context SBCDSPContext;
+
+struct sbc_dsp_context {
+    int position;
+    /* Number of consecutive blocks handled by the encoder */
+    uint8_t increment;
+    DECLARE_ALIGNED(SBC_ALIGN, int16_t, X)[2][SBC_X_BUFFER_SIZE];
+    void (*sbc_analyze_4)(const int16_t *in, int32_t *out, const int16_t *consts);
+    void (*sbc_analyze_8)(const int16_t *in, int32_t *out, const int16_t *consts);
+    /* Polyphase analysis filter for 4 subbands configuration,
+     * it handles "increment" blocks at once */
+    void (*sbc_analyze_4s)(SBCDSPContext *s,
+                           int16_t *x, int32_t *out, int out_stride);
+    /* Polyphase analysis filter for 8 subbands configuration,
+     * it handles "increment" blocks at once */
+    void (*sbc_analyze_8s)(SBCDSPContext *s,
+                           int16_t *x, int32_t *out, int out_stride);
+    /* Process input data (deinterleave, endian conversion, reordering),
+     * depending on the number of subbands and input data byte order */
+    int (*sbc_enc_process_input_4s)(int position, const uint8_t *pcm,
+                                    int16_t X[2][SBC_X_BUFFER_SIZE],
+                                    int nsamples, int nchannels);
+    int (*sbc_enc_process_input_8s)(int position, const uint8_t *pcm,
+                                    int16_t X[2][SBC_X_BUFFER_SIZE],
+                                    int nsamples, int nchannels);
+    /* Scale factors calculation */
+    void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8],
+                                  uint32_t scale_factor[2][8],
+                                  int blocks, int channels, int subbands);
+    /* Scale factors calculation with joint stereo support */
+    int (*sbc_calc_scalefactors_j)(int32_t sb_sample_f[16][2][8],
+                                   uint32_t scale_factor[2][8],
+                                   int blocks, int subbands);
+};
+
+/*
+ * Initialize pointers to the functions which are the basic "building bricks"
+ * of SBC codec. Best implementation is selected based on target CPU
+ * capabilities.
+ */
+void ff_sbcdsp_init(SBCDSPContext *s);
+
+#endif /* AVCODEC_SBCDSP_H */
diff --git a/libavcodec/sbcdsp_data.c b/libavcodec/sbcdsp_data.c
new file mode 100644
index 0000000000..78c07c0077
--- /dev/null
+++ b/libavcodec/sbcdsp_data.c
@@ -0,0 +1,329 @@ 
+/*
+ * Bluetooth low-complexity, subband codec (SBC)
+ *
+ * Copyright (C) 2017  Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2008-2010  Nokia Corporation
+ * Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * miscellaneous SBC tables
+ */
+
+#include "sbcdsp_data.h"
+
+#define F_PROTO(x) ((int32_t) (((x) * 2) * ((int32_t) 1 << 15) + 0.5))
+#define F_COS(x)   ((int32_t) (((x)    ) * ((int32_t) 1 << 15) + 0.5))
+
+/*
+ * Constant tables for the use in SIMD optimized analysis filters
+ * Each table consists of two parts:
+ * 1. reordered "proto" table
+ * 2. reordered "cos" table
+ *
+ * Due to non-symmetrical reordering, separate tables for "even"
+ * and "odd" cases are needed
+ */
+
+DECLARE_ALIGNED(SBC_ALIGN, const int16_t, ff_sbcdsp_analysis_consts_fixed4_simd_even)[40 + 16] = {
+#define C0 1.0932568993
+#define C1 1.3056875580
+#define C2 1.3056875580
+#define C3 1.6772280856
+
+#define F(x) F_PROTO(x)
+     F(0.00000000E+00 * C0),  F(3.83720193E-03 * C0),
+     F(5.36548976E-04 * C1),  F(2.73370904E-03 * C1),
+     F(3.06012286E-03 * C2),  F(3.89205149E-03 * C2),
+     F(0.00000000E+00 * C3), -F(1.49188357E-03 * C3),
+     F(1.09137620E-02 * C0),  F(2.58767811E-02 * C0),
+     F(2.04385087E-02 * C1),  F(3.21939290E-02 * C1),
+     F(7.76463494E-02 * C2),  F(6.13245186E-03 * C2),
+     F(0.00000000E+00 * C3), -F(2.88757392E-02 * C3),
+     F(1.35593274E-01 * C0),  F(2.94315332E-01 * C0),
+     F(1.94987841E-01 * C1),  F(2.81828203E-01 * C1),
+    -F(1.94987841E-01 * C2),  F(2.81828203E-01 * C2),
+     F(0.00000000E+00 * C3), -F(2.46636662E-01 * C3),
+    -F(1.35593274E-01 * C0),  F(2.58767811E-02 * C0),
+    -F(7.76463494E-02 * C1),  F(6.13245186E-03 * C1),
+    -F(2.04385087E-02 * C2),  F(3.21939290E-02 * C2),
+     F(0.00000000E+00 * C3),  F(2.88217274E-02 * C3),
+    -F(1.09137620E-02 * C0),  F(3.83720193E-03 * C0),
+    -F(3.06012286E-03 * C1),  F(3.89205149E-03 * C1),
+    -F(5.36548976E-04 * C2),  F(2.73370904E-03 * C2),
+     F(0.00000000E+00 * C3), -F(1.86581691E-03 * C3),
+#undef F
+#define F(x) F_COS(x)
+     F(0.7071067812 / C0),  F(0.9238795325 / C1),
+    -F(0.7071067812 / C0),  F(0.3826834324 / C1),
+    -F(0.7071067812 / C0), -F(0.3826834324 / C1),
+     F(0.7071067812 / C0), -F(0.9238795325 / C1),
+     F(0.3826834324 / C2), -F(1.0000000000 / C3),
+    -F(0.9238795325 / C2), -F(1.0000000000 / C3),
+     F(0.9238795325 / C2), -F(1.0000000000 / C3),
+    -F(0.3826834324 / C2), -F(1.0000000000 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+DECLARE_ALIGNED(SBC_ALIGN, const int16_t, ff_sbcdsp_analysis_consts_fixed4_simd_odd)[40 + 16] = {
+#define C0 1.3056875580
+#define C1 1.6772280856
+#define C2 1.0932568993
+#define C3 1.3056875580
+
+#define F(x) F_PROTO(x)
+     F(2.73370904E-03 * C0),  F(5.36548976E-04 * C0),
+    -F(1.49188357E-03 * C1),  F(0.00000000E+00 * C1),
+     F(3.83720193E-03 * C2),  F(1.09137620E-02 * C2),
+     F(3.89205149E-03 * C3),  F(3.06012286E-03 * C3),
+     F(3.21939290E-02 * C0),  F(2.04385087E-02 * C0),
+    -F(2.88757392E-02 * C1),  F(0.00000000E+00 * C1),
+     F(2.58767811E-02 * C2),  F(1.35593274E-01 * C2),
+     F(6.13245186E-03 * C3),  F(7.76463494E-02 * C3),
+     F(2.81828203E-01 * C0),  F(1.94987841E-01 * C0),
+    -F(2.46636662E-01 * C1),  F(0.00000000E+00 * C1),
+     F(2.94315332E-01 * C2), -F(1.35593274E-01 * C2),
+     F(2.81828203E-01 * C3), -F(1.94987841E-01 * C3),
+     F(6.13245186E-03 * C0), -F(7.76463494E-02 * C0),
+     F(2.88217274E-02 * C1),  F(0.00000000E+00 * C1),
+     F(2.58767811E-02 * C2), -F(1.09137620E-02 * C2),
+     F(3.21939290E-02 * C3), -F(2.04385087E-02 * C3),
+     F(3.89205149E-03 * C0), -F(3.06012286E-03 * C0),
+    -F(1.86581691E-03 * C1),  F(0.00000000E+00 * C1),
+     F(3.83720193E-03 * C2),  F(0.00000000E+00 * C2),
+     F(2.73370904E-03 * C3), -F(5.36548976E-04 * C3),
+#undef F
+#define F(x) F_COS(x)
+     F(0.9238795325 / C0), -F(1.0000000000 / C1),
+     F(0.3826834324 / C0), -F(1.0000000000 / C1),
+    -F(0.3826834324 / C0), -F(1.0000000000 / C1),
+    -F(0.9238795325 / C0), -F(1.0000000000 / C1),
+     F(0.7071067812 / C2),  F(0.3826834324 / C3),
+    -F(0.7071067812 / C2), -F(0.9238795325 / C3),
+    -F(0.7071067812 / C2),  F(0.9238795325 / C3),
+     F(0.7071067812 / C2), -F(0.3826834324 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+DECLARE_ALIGNED(SBC_ALIGN, const int16_t, ff_sbcdsp_analysis_consts_fixed8_simd_even)[80 + 64] = {
+#define C0 2.7906148894
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.5377944043
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO(x)
+     F(0.00000000E+00 * C0),  F(2.01182542E-03 * C0),
+     F(1.56575398E-04 * C1),  F(1.78371725E-03 * C1),
+     F(3.43256425E-04 * C2),  F(1.47640169E-03 * C2),
+     F(5.54620202E-04 * C3),  F(1.13992507E-03 * C3),
+    -F(8.23919506E-04 * C4),  F(0.00000000E+00 * C4),
+     F(2.10371989E-03 * C5),  F(3.49717454E-03 * C5),
+     F(1.99454554E-03 * C6),  F(1.64973098E-03 * C6),
+     F(1.61656283E-03 * C7),  F(1.78805361E-04 * C7),
+     F(5.65949473E-03 * C0),  F(1.29371806E-02 * C0),
+     F(8.02941163E-03 * C1),  F(1.53184106E-02 * C1),
+     F(1.04584443E-02 * C2),  F(1.62208471E-02 * C2),
+     F(1.27472335E-02 * C3),  F(1.59045603E-02 * C3),
+    -F(1.46525263E-02 * C4),  F(0.00000000E+00 * C4),
+     F(8.85757540E-03 * C5),  F(5.31873032E-02 * C5),
+     F(2.92408442E-03 * C6),  F(3.90751381E-02 * C6),
+    -F(4.91578024E-03 * C7),  F(2.61098752E-02 * C7),
+     F(6.79989431E-02 * C0),  F(1.46955068E-01 * C0),
+     F(8.29847578E-02 * C1),  F(1.45389847E-01 * C1),
+     F(9.75753918E-02 * C2),  F(1.40753505E-01 * C2),
+     F(1.11196689E-01 * C3),  F(1.33264415E-01 * C3),
+    -F(1.23264548E-01 * C4),  F(0.00000000E+00 * C4),
+     F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+     F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+     F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+    -F(6.79989431E-02 * C0),  F(1.29371806E-02 * C0),
+    -F(5.31873032E-02 * C1),  F(8.85757540E-03 * C1),
+    -F(3.90751381E-02 * C2),  F(2.92408442E-03 * C2),
+    -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+     F(1.46404076E-02 * C4),  F(0.00000000E+00 * C4),
+     F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+     F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+     F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+    -F(5.65949473E-03 * C0),  F(2.01182542E-03 * C0),
+    -F(3.49717454E-03 * C1),  F(2.10371989E-03 * C1),
+    -F(1.64973098E-03 * C2),  F(1.99454554E-03 * C2),
+    -F(1.78805361E-04 * C3),  F(1.61656283E-03 * C3),
+    -F(9.02154502E-04 * C4),  F(0.00000000E+00 * C4),
+     F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+     F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+     F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS(x)
+     F(0.7071067812 / C0),  F(0.8314696123 / C1),
+    -F(0.7071067812 / C0), -F(0.1950903220 / C1),
+    -F(0.7071067812 / C0), -F(0.9807852804 / C1),
+     F(0.7071067812 / C0), -F(0.5555702330 / C1),
+     F(0.7071067812 / C0),  F(0.5555702330 / C1),
+    -F(0.7071067812 / C0),  F(0.9807852804 / C1),
+    -F(0.7071067812 / C0),  F(0.1950903220 / C1),
+     F(0.7071067812 / C0), -F(0.8314696123 / C1),
+     F(0.9238795325 / C2),  F(0.9807852804 / C3),
+     F(0.3826834324 / C2),  F(0.8314696123 / C3),
+    -F(0.3826834324 / C2),  F(0.5555702330 / C3),
+    -F(0.9238795325 / C2),  F(0.1950903220 / C3),
+    -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+    -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+     F(0.3826834324 / C2), -F(0.8314696123 / C3),
+     F(0.9238795325 / C2), -F(0.9807852804 / C3),
+    -F(1.0000000000 / C4),  F(0.5555702330 / C5),
+    -F(1.0000000000 / C4), -F(0.9807852804 / C5),
+    -F(1.0000000000 / C4),  F(0.1950903220 / C5),
+    -F(1.0000000000 / C4),  F(0.8314696123 / C5),
+    -F(1.0000000000 / C4), -F(0.8314696123 / C5),
+    -F(1.0000000000 / C4), -F(0.1950903220 / C5),
+    -F(1.0000000000 / C4),  F(0.9807852804 / C5),
+    -F(1.0000000000 / C4), -F(0.5555702330 / C5),
+     F(0.3826834324 / C6),  F(0.1950903220 / C7),
+    -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+     F(0.9238795325 / C6),  F(0.8314696123 / C7),
+    -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+    -F(0.3826834324 / C6),  F(0.9807852804 / C7),
+     F(0.9238795325 / C6), -F(0.8314696123 / C7),
+    -F(0.9238795325 / C6),  F(0.5555702330 / C7),
+     F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
+
+DECLARE_ALIGNED(SBC_ALIGN, const int16_t, ff_sbcdsp_analysis_consts_fixed8_simd_odd)[80 + 64] = {
+#define C0 2.5377944043
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.7906148894
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO(x)
+     F(0.00000000E+00 * C0), -F(8.23919506E-04 * C0),
+     F(1.56575398E-04 * C1),  F(1.78371725E-03 * C1),
+     F(3.43256425E-04 * C2),  F(1.47640169E-03 * C2),
+     F(5.54620202E-04 * C3),  F(1.13992507E-03 * C3),
+     F(2.01182542E-03 * C4),  F(5.65949473E-03 * C4),
+     F(2.10371989E-03 * C5),  F(3.49717454E-03 * C5),
+     F(1.99454554E-03 * C6),  F(1.64973098E-03 * C6),
+     F(1.61656283E-03 * C7),  F(1.78805361E-04 * C7),
+     F(0.00000000E+00 * C0), -F(1.46525263E-02 * C0),
+     F(8.02941163E-03 * C1),  F(1.53184106E-02 * C1),
+     F(1.04584443E-02 * C2),  F(1.62208471E-02 * C2),
+     F(1.27472335E-02 * C3),  F(1.59045603E-02 * C3),
+     F(1.29371806E-02 * C4),  F(6.79989431E-02 * C4),
+     F(8.85757540E-03 * C5),  F(5.31873032E-02 * C5),
+     F(2.92408442E-03 * C6),  F(3.90751381E-02 * C6),
+    -F(4.91578024E-03 * C7),  F(2.61098752E-02 * C7),
+     F(0.00000000E+00 * C0), -F(1.23264548E-01 * C0),
+     F(8.29847578E-02 * C1),  F(1.45389847E-01 * C1),
+     F(9.75753918E-02 * C2),  F(1.40753505E-01 * C2),
+     F(1.11196689E-01 * C3),  F(1.33264415E-01 * C3),
+     F(1.46955068E-01 * C4), -F(6.79989431E-02 * C4),
+     F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+     F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+     F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+     F(0.00000000E+00 * C0),  F(1.46404076E-02 * C0),
+    -F(5.31873032E-02 * C1),  F(8.85757540E-03 * C1),
+    -F(3.90751381E-02 * C2),  F(2.92408442E-03 * C2),
+    -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+     F(1.29371806E-02 * C4), -F(5.65949473E-03 * C4),
+     F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+     F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+     F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+     F(0.00000000E+00 * C0), -F(9.02154502E-04 * C0),
+    -F(3.49717454E-03 * C1),  F(2.10371989E-03 * C1),
+    -F(1.64973098E-03 * C2),  F(1.99454554E-03 * C2),
+    -F(1.78805361E-04 * C3),  F(1.61656283E-03 * C3),
+     F(2.01182542E-03 * C4),  F(0.00000000E+00 * C4),
+     F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+     F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+     F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS(x)
+    -F(1.0000000000 / C0),  F(0.8314696123 / C1),
+    -F(1.0000000000 / C0), -F(0.1950903220 / C1),
+    -F(1.0000000000 / C0), -F(0.9807852804 / C1),
+    -F(1.0000000000 / C0), -F(0.5555702330 / C1),
+    -F(1.0000000000 / C0),  F(0.5555702330 / C1),
+    -F(1.0000000000 / C0),  F(0.9807852804 / C1),
+    -F(1.0000000000 / C0),  F(0.1950903220 / C1),
+    -F(1.0000000000 / C0), -F(0.8314696123 / C1),
+     F(0.9238795325 / C2),  F(0.9807852804 / C3),
+     F(0.3826834324 / C2),  F(0.8314696123 / C3),
+    -F(0.3826834324 / C2),  F(0.5555702330 / C3),
+    -F(0.9238795325 / C2),  F(0.1950903220 / C3),
+    -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+    -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+     F(0.3826834324 / C2), -F(0.8314696123 / C3),
+     F(0.9238795325 / C2), -F(0.9807852804 / C3),
+     F(0.7071067812 / C4),  F(0.5555702330 / C5),
+    -F(0.7071067812 / C4), -F(0.9807852804 / C5),
+    -F(0.7071067812 / C4),  F(0.1950903220 / C5),
+     F(0.7071067812 / C4),  F(0.8314696123 / C5),
+     F(0.7071067812 / C4), -F(0.8314696123 / C5),
+    -F(0.7071067812 / C4), -F(0.1950903220 / C5),
+    -F(0.7071067812 / C4),  F(0.9807852804 / C5),
+     F(0.7071067812 / C4), -F(0.5555702330 / C5),
+     F(0.3826834324 / C6),  F(0.1950903220 / C7),
+    -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+     F(0.9238795325 / C6),  F(0.8314696123 / C7),
+    -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+    -F(0.3826834324 / C6),  F(0.9807852804 / C7),
+     F(0.9238795325 / C6), -F(0.8314696123 / C7),
+    -F(0.9238795325 / C6),  F(0.5555702330 / C7),
+     F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
diff --git a/libavcodec/sbcdsp_data.h b/libavcodec/sbcdsp_data.h
new file mode 100644
index 0000000000..10fad5caa5
--- /dev/null
+++ b/libavcodec/sbcdsp_data.h
@@ -0,0 +1,55 @@ 
+/*
+ * Bluetooth low-complexity, subband codec (SBC)
+ *
+ * Copyright (C) 2017  Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2008-2010  Nokia Corporation
+ * Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * miscellaneous SBC tables
+ */
+
+#ifndef AVCODEC_SBCDSP_DATA_H
+#define AVCODEC_SBCDSP_DATA_H
+
+#include "sbc.h"
+
+#define SBC_PROTO_FIXED_SCALE      16
+#define SBC_COS_TABLE_FIXED_SCALE  15
+
+/*
+ * Constant tables for the use in SIMD optimized analysis filters
+ * Each table consists of two parts:
+ * 1. reordered "proto" table
+ * 2. reordered "cos" table
+ *
+ * Due to non-symmetrical reordering, separate tables for "even"
+ * and "odd" cases are needed
+ */
+
+extern const int16_t ff_sbcdsp_analysis_consts_fixed4_simd_even[];
+extern const int16_t ff_sbcdsp_analysis_consts_fixed4_simd_odd[];
+extern const int16_t ff_sbcdsp_analysis_consts_fixed8_simd_even[];
+extern const int16_t ff_sbcdsp_analysis_consts_fixed8_simd_odd[];
+
+#endif /* AVCODEC_SBCDSP_DATA_H */
diff --git a/libavcodec/sbcenc.c b/libavcodec/sbcenc.c
new file mode 100644
index 0000000000..5183d07188
--- /dev/null
+++ b/libavcodec/sbcenc.c
@@ -0,0 +1,428 @@ 
+/*
+ * Bluetooth low-complexity, subband codec (SBC)
+ *
+ * Copyright (C) 2017  Aurelien Jacobs <aurel@gnuage.org>
+ * Copyright (C) 2012-2013  Intel Corporation
+ * Copyright (C) 2008-2010  Nokia Corporation
+ * Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2008  Brad Midgley <bmidgley@xmission.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * SBC encoder implementation
+ */
+
+#include <stdbool.h>
+#include "libavutil/opt.h"
+#include "avcodec.h"
+#include "internal.h"
+#include "profiles.h"
+#include "put_bits.h"
+#include "sbc.h"
+#include "sbcdsp.h"
+
+typedef struct SBCEncContext {
+    AVClass *class;
+
+    uint8_t frequency;
+    int blocks;
+    int subbands;
+    uint8_t mode;
+    int allocation;
+    int bitpool;
+
+    int64_t max_delay;
+
+    bool init;
+    int msbc;
+    DECLARE_ALIGNED(SBC_ALIGN, struct sbc_frame, frame);
+    DECLARE_ALIGNED(SBC_ALIGN, SBCDSPContext, dsp);
+} SBCEncContext;
+
+static int sbc_analyze_audio(SBCDSPContext *s, struct sbc_frame *frame)
+{
+    int ch, blk;
+    int16_t *x;
+
+    switch (frame->subbands) {
+    case 4:
+        for (ch = 0; ch < frame->channels; ch++) {
+            x = &s->X[ch][s->position - 4 *
+                    s->increment + frame->blocks * 4];
+            for (blk = 0; blk < frame->blocks;
+                        blk += s->increment) {
+                s->sbc_analyze_4s(
+                    s, x,
+                    frame->sb_sample_f[blk][ch],
+                    frame->sb_sample_f[blk + 1][ch] -
+                    frame->sb_sample_f[blk][ch]);
+                x -= 4 * s->increment;
+            }
+        }
+        return frame->blocks * 4;
+
+    case 8:
+        for (ch = 0; ch < frame->channels; ch++) {
+            x = &s->X[ch][s->position - 8 *
+                    s->increment + frame->blocks * 8];
+            for (blk = 0; blk < frame->blocks;
+                        blk += s->increment) {
+                s->sbc_analyze_8s(
+                    s, x,
+                    frame->sb_sample_f[blk][ch],
+                    frame->sb_sample_f[blk + 1][ch] -
+                    frame->sb_sample_f[blk][ch]);
+                x -= 8 * s->increment;
+            }
+        }
+        return frame->blocks * 8;
+
+    default:
+        return AVERROR(EIO);
+    }
+}
+
+/*
+ * Packs the SBC frame from frame into the memory in avpkt.
+ * Returns the length of the packed frame.
+ */
+static size_t sbc_pack_frame(AVPacket *avpkt, struct sbc_frame *frame,
+                             int joint, bool msbc)
+{
+    PutBitContext pb;
+
+    /* Will copy the header parts for CRC-8 calculation here */
+    uint8_t crc_header[11] = { 0 };
+    int crc_pos;
+
+    uint32_t audio_sample;
+
+    int ch, sb, blk;        /* channel, subband, block and bit counters */
+    int bits[2][8];         /* bits distribution */
+    uint32_t levels[2][8];  /* levels are derived from that */
+    uint32_t sb_sample_delta[2][8];
+
+    if (msbc) {
+        avpkt->data[0] = MSBC_SYNCWORD;
+        avpkt->data[1] = 0;
+        avpkt->data[2] = 0;
+    } else {
+        avpkt->data[0] = SBC_SYNCWORD;
+
+        avpkt->data[1]  = (frame->frequency  & 0x03) << 6;
+        avpkt->data[1] |= (frame->block_mode & 0x03) << 4;
+        avpkt->data[1] |= (frame->mode       & 0x03) << 2;
+        avpkt->data[1] |= (frame->allocation & 0x01) << 1;
+        avpkt->data[1] |= (frame->subbands == 8);
+
+        avpkt->data[2] = frame->bitpool;
+
+        if (frame->bitpool > frame->subbands << (4 + (frame->mode == STEREO
+                                                   || frame->mode == JOINT_STEREO)))
+            return -5;
+    }
+
+    /* Can't fill in crc yet */
+    crc_header[0] = avpkt->data[1];
+    crc_header[1] = avpkt->data[2];
+    crc_pos = 16;
+
+    init_put_bits(&pb, avpkt->data + 4, avpkt->size);
+
+    if (frame->mode == JOINT_STEREO) {
+        put_bits(&pb, frame->subbands, joint);
+        crc_header[crc_pos >> 3] = joint;
+        crc_pos += frame->subbands;
+    }
+
+    for (ch = 0; ch < frame->channels; ch++) {
+        for (sb = 0; sb < frame->subbands; sb++) {
+            put_bits(&pb, 4, frame->scale_factor[ch][sb] & 0x0F);
+            crc_header[crc_pos >> 3] <<= 4;
+            crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+            crc_pos += 4;
+        }
+    }
+
+    /* align the last crc byte */
+    if (crc_pos % 8)
+        crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8);
+
+    avpkt->data[3] = sbc_crc8(frame->crc_ctx, crc_header, crc_pos);
+
+    ff_sbc_calculate_bits(frame, bits);
+
+    for (ch = 0; ch < frame->channels; ch++) {
+        for (sb = 0; sb < frame->subbands; sb++) {
+            levels[ch][sb] = ((1 << bits[ch][sb]) - 1) <<
+                (32 - (frame->scale_factor[ch][sb] +
+                    SCALE_OUT_BITS + 2));
+            sb_sample_delta[ch][sb] = (uint32_t) 1 <<
+                (frame->scale_factor[ch][sb] +
+                    SCALE_OUT_BITS + 1);
+        }
+    }
+
+    for (blk = 0; blk < frame->blocks; blk++) {
+        for (ch = 0; ch < frame->channels; ch++) {
+            for (sb = 0; sb < frame->subbands; sb++) {
+
+                if (bits[ch][sb] == 0)
+                    continue;
+
+                audio_sample = ((uint64_t) levels[ch][sb] *
+                    (sb_sample_delta[ch][sb] +
+                    frame->sb_sample_f[blk][ch][sb])) >> 32;
+
+                put_bits(&pb, bits[ch][sb], audio_sample);
+            }
+        }
+    }
+
+    flush_put_bits(&pb);
+
+    return (put_bits_count(&pb) + 7) / 8;
+}
+
+static void sbc_encoder_init(bool msbc, SBCDSPContext *s,
+                             const struct sbc_frame *frame)
+{
+    memset(&s->X, 0, sizeof(s->X));
+    s->position = (SBC_X_BUFFER_SIZE - frame->subbands * 9) & ~7;
+    s->increment = msbc ? 1 : 4;
+
+    ff_sbcdsp_init(s);
+}
+
+static int sbc_encode_init(AVCodecContext *avctx)
+{
+    SBCEncContext *sbc = avctx->priv_data;
+
+    if (avctx->profile == FF_PROFILE_SBC_MSBC)
+        sbc->msbc = 1;
+
+    if (sbc->msbc) {
+        if (avctx->channels != 1) {
+            av_log(avctx, AV_LOG_ERROR, "mSBC require mono channel.\n");
+            return AVERROR(EINVAL);
+        }
+
+        if (avctx->sample_rate != 16000) {
+            av_log(avctx, AV_LOG_ERROR, "mSBC require 16 kHz samplerate.\n");
+            return AVERROR(EINVAL);
+        }
+
+        sbc->mode = SBC_MODE_MONO;
+        sbc->subbands = SBC_SB_8;
+        sbc->blocks = MSBC_BLOCKS;
+        sbc->allocation = SBC_AM_LOUDNESS;
+        sbc->bitpool = 26;
+
+        avctx->frame_size = 8 * MSBC_BLOCKS;
+    } else {
+        int d;
+
+        if (avctx->channels == 1) {
+            sbc->mode = SBC_MODE_MONO;
+            if (sbc->max_delay <= 3000 || avctx->bit_rate > 270000)
+                sbc->subbands = 4;
+            else
+                sbc->subbands = 8;
+        } else {
+            if (avctx->bit_rate < 180000 || avctx->bit_rate > 420000)
+                sbc->mode = SBC_MODE_JOINT_STEREO;
+            else
+                sbc->mode = SBC_MODE_STEREO;
+            if (sbc->max_delay <= 4000 || avctx->bit_rate > 420000)
+                sbc->subbands = 4;
+            else
+                sbc->subbands = 8;
+        }
+        /* sbc algorithmic delay is ((blocks + 10) * subbands - 2) / sample_rate */
+        sbc->blocks = av_clip(((sbc->max_delay * avctx->sample_rate + 2)
+                               / (1000000 * sbc->subbands)) - 10, 4, 16) & ~3;
+
+        sbc->allocation = SBC_AM_LOUDNESS;
+
+        d = sbc->blocks * ((sbc->mode == SBC_MODE_DUAL_CHANNEL) + 1);
+        sbc->bitpool = (((avctx->bit_rate * sbc->subbands * sbc->blocks) / avctx->sample_rate)
+                        - 4*sbc->subbands*avctx->channels
+                        - (sbc->mode = SBC_MODE_JOINT_STEREO)*sbc->subbands - 32 + d/2) / d;
+        if (avctx->global_quality > 0)
+            sbc->bitpool = avctx->global_quality / FF_QP2LAMBDA;
+
+        if (sbc->bitpool > 255) {
+            av_log(avctx, AV_LOG_ERROR, "bitpool > 255 is not allowed.\n");
+            return AVERROR(EINVAL);
+        }
+
+        sbc->subbands >>= 3;
+        sbc->blocks = (sbc->blocks >> 2) - 1;
+
+        avctx->frame_size = 4*(sbc->subbands + 1) * 4*(sbc->blocks + 1);
+    }
+
+    for (int i = 0; avctx->codec->supported_samplerates[i]; i++)
+        if (avctx->sample_rate == avctx->codec->supported_samplerates[i])
+            sbc->frequency = i;
+
+    return 0;
+}
+
+/* Returns the output block size in bytes */
+static size_t sbc_get_frame_length(SBCEncContext *sbc)
+{
+    int ret;
+    uint8_t subbands, channels, blocks, joint, bitpool;
+
+    if (sbc->init && sbc->frame.bitpool == sbc->bitpool)
+        return sbc->frame.length;
+
+    subbands = sbc->subbands ? 8 : 4;
+    if (sbc->msbc)
+        blocks = MSBC_BLOCKS;
+    else
+        blocks = 4 + (sbc->blocks * 4);
+    channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+    joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
+    bitpool = sbc->bitpool;
+
+    ret = 4 + (4 * subbands * channels) / 8;
+    /* This term is not always evenly divide so we round it up */
+    if (channels == 1 || sbc->mode == SBC_MODE_DUAL_CHANNEL)
+        ret += ((blocks * channels * bitpool) + 7) / 8;
+    else
+        ret += (((joint ? subbands : 0) + blocks * bitpool) + 7) / 8;
+
+    return ret;
+}
+
+static int sbc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                            const AVFrame *frame, int *got_packet_ptr)
+{
+    SBCEncContext *sbc = avctx->priv_data;
+    int (*sbc_enc_process_input)(int position,
+            const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+            int nsamples, int nchannels);
+    int ret, j = 0;
+
+    if (!sbc)
+        return AVERROR(EIO);
+
+    if (!sbc->init) {
+        sbc->frame.frequency = sbc->frequency;
+        sbc->frame.mode = sbc->mode;
+        sbc->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+        sbc->frame.allocation = sbc->allocation;
+        sbc->frame.subband_mode = sbc->subbands;
+        sbc->frame.subbands = sbc->subbands ? 8 : 4;
+        sbc->frame.block_mode = sbc->blocks;
+        if (sbc->msbc)
+            sbc->frame.blocks = MSBC_BLOCKS;
+        else
+            sbc->frame.blocks = 4 + (sbc->blocks * 4);
+        sbc->frame.bitpool = sbc->bitpool;
+        sbc->frame.codesize = sbc->frame.subbands * sbc->frame.blocks
+                              * sbc->frame.channels * 2;
+        sbc->frame.length = sbc_get_frame_length(sbc);
+        sbc->frame.crc_ctx = av_crc_get_table(AV_CRC_8_EBU);
+
+        sbc_encoder_init(sbc->msbc, &sbc->dsp, &sbc->frame);
+        sbc->init = true;
+    } else if (sbc->frame.bitpool != sbc->bitpool) {
+        sbc->frame.length = sbc_get_frame_length(sbc);
+        sbc->frame.bitpool = sbc->bitpool;
+    }
+
+    /* input must be large enough to encode a complete frame */
+    if (frame->nb_samples * sbc->frame.channels * 2 < sbc->frame.codesize)
+        return 0;
+
+    if ((ret = ff_alloc_packet2(avctx, avpkt, sbc->frame.length, 0)) < 0)
+        return ret;
+
+    /* Select the needed input data processing function and call it */
+    if (sbc->frame.subbands == 8) {
+        sbc_enc_process_input = sbc->dsp.sbc_enc_process_input_8s;
+    } else {
+        sbc_enc_process_input = sbc->dsp.sbc_enc_process_input_4s;
+    }
+
+    sbc->dsp.position = sbc_enc_process_input(
+        sbc->dsp.position, frame->data[0],
+        sbc->dsp.X, sbc->frame.subbands * sbc->frame.blocks,
+        sbc->frame.channels);
+
+    sbc_analyze_audio(&sbc->dsp, &sbc->frame);
+
+    if (sbc->frame.mode == JOINT_STEREO)
+        j = sbc->dsp.sbc_calc_scalefactors_j(sbc->frame.sb_sample_f,
+                                             sbc->frame.scale_factor,
+                                             sbc->frame.blocks,
+                                             sbc->frame.subbands);
+    else
+        sbc->dsp.sbc_calc_scalefactors(sbc->frame.sb_sample_f,
+                                       sbc->frame.scale_factor,
+                                       sbc->frame.blocks,
+                                       sbc->frame.channels,
+                                       sbc->frame.subbands);
+    emms_c();
+    sbc_pack_frame(avpkt, &sbc->frame, j, sbc->msbc);
+
+    *got_packet_ptr = 1;
+    return 0;
+}
+
+#define OFFSET(x) offsetof(SBCEncContext, x)
+#define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+    { "sbc_delay",    "set maximum algorithmic latency",
+      OFFSET(max_delay),    AV_OPT_TYPE_DURATION, { .i64 =  13000 }, 1000,   13000, AE },
+    { "msbc",         "use mSBC mode (wideband speech mono SBC)",
+      OFFSET(msbc),         AV_OPT_TYPE_BOOL, { .i64 =  0 }, 0,   1, AE },
+    { NULL },
+};
+
+static const AVClass sbc_class = {
+    .class_name = "sbc encoder",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVCodec ff_sbc_encoder = {
+    .name                  = "sbc",
+    .long_name             = NULL_IF_CONFIG_SMALL("SBC (low-complexity subband codec)"),
+    .type                  = AVMEDIA_TYPE_AUDIO,
+    .id                    = AV_CODEC_ID_SBC,
+    .priv_data_size        = sizeof(SBCEncContext),
+    .init                  = sbc_encode_init,
+    .encode2               = sbc_encode_frame,
+    .capabilities          = AV_CODEC_CAP_SMALL_LAST_FRAME,
+    .caps_internal         = FF_CODEC_CAP_INIT_THREADSAFE,
+    .channel_layouts       = (const uint64_t[]) { AV_CH_LAYOUT_MONO,
+                                                  AV_CH_LAYOUT_STEREO, 0},
+    .sample_fmts           = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16,
+                                                             AV_SAMPLE_FMT_NONE },
+    .supported_samplerates = (const int[]) { 16000, 32000, 44100, 48000, 0 },
+    .priv_class            = &sbc_class,
+    .profiles              = NULL_IF_CONFIG_SMALL(ff_sbc_profiles),
+};
-- 
2.16.2