external_ffmpeg/libavcodec/libfluoride-sbcenc.c

258 lines
8.6 KiB
C

/*
* Bluetooth low-complexity, subband codec (SBC)
*
* 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 "avcodec.h"
#include "codec_internal.h"
#include "encode.h"
#include "libavutil/opt.h"
#include "libavutil/intreadwrite.h"
#include <sbc_encoder.h>
typedef struct SBCEncContext {
AVClass *class;
SBC_ENC_PARAMS context;
int64_t max_delay;
int frame_length;
const char *sbc_param;
int nb_out_pkts;
} SBCEncContext;
static int sbc_encoder_parse_param(const char *sbc_param, SBC_ENC_PARAMS *param)
{
AVDictionaryEntry *tag;
AVDictionary *format_opt = NULL;
int ret;
if (sbc_param == NULL || param == NULL)
goto error;
ret = av_dict_parse_string(&format_opt, sbc_param, "=", ":", 0);
if (ret != 0)
goto error;
tag = av_dict_get(format_opt, "channel_mode", NULL, 0);
if (tag == NULL)
goto error;
param->s16ChannelMode = strtoul(tag->value, NULL, 0);
tag = av_dict_get(format_opt, "blocks", NULL, 0);
if (tag == NULL)
goto error;
param->s16NumOfBlocks = strtoul(tag->value, NULL, 0);
tag = av_dict_get(format_opt, "subbands", NULL, 0);
if (tag == NULL)
goto error;
param->s16NumOfSubBands = strtoul(tag->value, NULL, 0);
tag = av_dict_get(format_opt, "alloc_method", NULL, 0);
if (tag == NULL)
goto error;
param->s16AllocationMethod = strtoul(tag->value, NULL, 0);
tag = av_dict_get(format_opt, "bitpool", NULL, 0);
if (tag == NULL)
goto error;
param->s16BitPool = strtoul(tag->value, NULL, 0);
av_dict_free(&format_opt);
return 0;
error:
if (format_opt != NULL)
av_dict_free(&format_opt);
return AVERROR(EINVAL);
}
static int sbc_encode_init(AVCodecContext *avctx)
{
SBCEncContext *sbc = avctx->priv_data;
SBC_ENC_PARAMS *param = &sbc->context;
uint8_t joint;
uint8_t dual;
if (avctx->profile == FF_PROFILE_SBC_MSBC) {
if (avctx->ch_layout.nb_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);
}
param->s16SamplingFreq = SBC_sf16000;
param->s16ChannelMode = SBC_MONO;
param->s16NumOfSubBands = 8;
param->s16NumOfChannels = 1;
param->s16NumOfBlocks = 15;
param->s16AllocationMethod = SBC_LOUDNESS;
param->s16BitPool = 26;
param->Format = SBC_FORMAT_MSBC;
avctx->frame_size = 8 * SBC_MAX_NUM_OF_BLOCKS;
} else {
if (avctx->global_quality > 255*FF_QP2LAMBDA) {
av_log(avctx, AV_LOG_ERROR, "bitpool > 255 is not allowed.\n");
return AVERROR(EINVAL);
}
if (sbc_encoder_parse_param(sbc->sbc_param, param) != 0) {
if (avctx->ch_layout.nb_channels == 1) {
param->s16ChannelMode = SBC_MONO;
if (sbc->max_delay <= 3000 || avctx->bit_rate > 270000)
param->s16NumOfSubBands = 4;
else
param->s16NumOfSubBands = 8;
} else {
if (avctx->bit_rate < 180000 || avctx->bit_rate > 420000)
param->s16ChannelMode = SBC_JOINT_STEREO;
else
param->s16ChannelMode = SBC_STEREO;
if (sbc->max_delay <= 4000 || avctx->bit_rate > 420000)
param->s16NumOfSubBands = 4;
else
param->s16NumOfSubBands = 8;
}
/* sbc algorithmic delay is ((s16NumOfBlocks + 10) * s16NumOfSubBands - 2) / sample_rate */
param->s16NumOfBlocks = av_clip(((sbc->max_delay * avctx->sample_rate + 2)
/ (1000000 * param->s16NumOfSubBands)) - 10, 4, 16) & ~3;
param->s16AllocationMethod = SBC_LOUDNESS;
}
param->u16BitRate = avctx->bit_rate / 1000;
param->s16NumOfChannels = avctx->ch_layout.nb_channels;
switch (avctx->sample_rate)
{
case 16000:
param->s16SamplingFreq = SBC_sf16000;
break;
case 32000:
param->s16SamplingFreq = SBC_sf32000;
break;
case 44100:
param->s16SamplingFreq = SBC_sf44100;
break;
default:
param->s16SamplingFreq = SBC_sf48000;
break;
}
avctx->frame_size = 4*((param->s16NumOfSubBands >> 3) + 1) * 4*(param->s16NumOfBlocks >> 2);
SBC_Encoder_Init(param);
}
/* Calculate frame_length */
joint = param->s16ChannelMode == SBC_JOINT_STEREO;
dual = param->s16ChannelMode == SBC_DUAL;
sbc->frame_length = 4 + (4 * param->s16NumOfSubBands * param->s16NumOfChannels) / 8
+ ((param->s16NumOfBlocks * param->s16BitPool * (1 + dual)
+ joint * param->s16NumOfSubBands) + 7) / 8;
avctx->frame_size *= sbc->nb_out_pkts;
return 0;
}
static int sbc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *av_frame, int *got_packet_ptr)
{
SBCEncContext *sbc = avctx->priv_data;
char *pcm, *sbc_data;
uint32_t encret;
int ret, i;
int per;
/* input must be large enough to encode a complete frame */
if (av_frame->nb_samples < avctx->frame_size)
return 0;
if ((ret = ff_alloc_packet(avctx, avpkt, sbc->frame_length * sbc->nb_out_pkts)) < 0)
return ret;
pcm = av_frame->extended_data[0];
sbc_data = avpkt->data;
per = av_frame->ch_layout.nb_channels * av_get_bytes_per_sample(av_frame->format);
for (i = 0; i < sbc->nb_out_pkts; i++) {
encret = SBC_Encode(&sbc->context, (int16_t *)pcm, sbc_data);
if (encret != sbc->frame_length) {
av_log(avctx, AV_LOG_ERROR, "SBC encode frame error, frame length %d, act %" PRIu32 "\n",
sbc->frame_length, encret);
return AVERROR(EIO);
}
sbc_data += sbc->frame_length;
pcm += avctx->frame_size * per / sbc->nb_out_pkts;
}
*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 },
{ "sbc_param", "", OFFSET(sbc_param), AV_OPT_TYPE_STRING, {.str=NULL}, AE },
{ "nb_out_pkts", "set out packets num", OFFSET(nb_out_pkts), AV_OPT_TYPE_INT, {.i64 = 1}, 1, 32, AE },
{ NULL },
};
static const AVClass sbc_class = {
.class_name = "sbc encoder",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFCodec ff_libfluoride_sbc_encoder = {
.p.name = "libfluoride_sbc",
.p.long_name = NULL_IF_CONFIG_SMALL("libfluoride SBC (low-complexity subband codec)"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_SBC,
.p.priv_class = &sbc_class,
.priv_data_size = sizeof(SBCEncContext),
.init = sbc_encode_init,
FF_CODEC_ENCODE_CB(sbc_encode_frame),
.p.capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME,
.p.supported_samplerates = (const int[]) { 16000, 32000, 44100, 48000, 0 },
.p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE },
.p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO,
AV_CHANNEL_LAYOUT_STEREO, { 0 } },
};