Skip to content

Instantly share code, notes, and snippets.

@justinruggles
Created August 21, 2012 16:05
Show Gist options
  • Save justinruggles/3416858 to your computer and use it in GitHub Desktop.
Save justinruggles/3416858 to your computer and use it in GitHub Desktop.
flac 24-bit encoding
diff --git a/libavcodec/flacenc.c b/libavcodec/flacenc.c
index 94e381d..3dcafb1 100644
--- a/libavcodec/flacenc.c
+++ b/libavcodec/flacenc.c
@@ -52,6 +52,7 @@ typedef struct CompressionOptions {
int prediction_order_method;
int min_partition_order;
int max_partition_order;
+ int bits_per_sample;
} CompressionOptions;
typedef struct RiceContext {
@@ -86,6 +87,8 @@ typedef struct FlacEncodeContext {
int channels;
int samplerate;
int sr_code[2];
+ int bits_per_sample;
+ int bps_code;
int max_blocksize;
int min_framesize;
int max_framesize;
@@ -98,6 +101,9 @@ typedef struct FlacEncodeContext {
AVCodecContext *avctx;
LPCContext lpc_ctx;
struct AVMD5 *md5ctx;
+ uint8_t *md5_buffer;
+ int md5_buffer_size;
+ DSPContext dsp;
} FlacEncodeContext;
@@ -118,7 +124,7 @@ static void write_streaminfo(FlacEncodeContext *s, uint8_t *header)
put_bits(&pb, 24, s->max_framesize);
put_bits(&pb, 20, s->samplerate);
put_bits(&pb, 3, s->channels-1);
- put_bits(&pb, 5, 15); /* bits per sample - 1 */
+ put_bits(&pb, 5, s->bits_per_sample - 1);
/* write 36-bit sample count in 2 put_bits() calls */
put_bits(&pb, 24, (s->sample_count & 0xFFFFFF000LL) >> 12);
put_bits(&pb, 12, s->sample_count & 0x000000FFFLL);
@@ -213,13 +219,32 @@ static av_cold int flac_encode_init(AVCodecContext *avctx)
int freq = avctx->sample_rate;
int channels = avctx->channels;
FlacEncodeContext *s = avctx->priv_data;
- int i, level, ret;
+ int i, level, ret, bps;
uint8_t *streaminfo;
s->avctx = avctx;
- if (avctx->sample_fmt != AV_SAMPLE_FMT_S16)
- return -1;
+ switch (avctx->sample_fmt) {
+ case AV_SAMPLE_FMT_S16:
+ s->bits_per_sample = 16;
+ s->bps_code = 4;
+ break;
+ case AV_SAMPLE_FMT_S32:
+ if (s->options.bits_per_sample)
+ bps = s->options.bits_per_sample;
+ else
+ bps = avctx->bits_per_raw_sample;
+ if (bps != 24) {
+ av_log(avctx, AV_LOG_ERROR, "bits per sample of %d is not "
+ "supported.\n", bps);
+ return AVERROR(EINVAL);
+ }
+ s->bits_per_sample = bps;
+ s->bps_code = 6;
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
if (channels < 1 || channels > FLAC_MAX_CHANNELS)
return -1;
@@ -417,7 +442,8 @@ static av_cold int flac_encode_init(AVCodecContext *avctx)
/* set maximum encoded frame size in verbatim mode */
s->max_framesize = ff_flac_get_max_frame_size(s->avctx->frame_size,
- s->channels, 16);
+ s->channels,
+ s->bits_per_sample);
/* initialize MD5 context */
s->md5ctx = av_malloc(av_md5_size);
@@ -425,6 +451,9 @@ static av_cold int flac_encode_init(AVCodecContext *avctx)
return AVERROR(ENOMEM);
av_md5_init(s->md5ctx);
+ /* initialize DSP context */
+ dsputil_init(&s->dsp, avctx);
+
streaminfo = av_malloc(FLAC_STREAMINFO_SIZE);
if (!streaminfo)
return AVERROR(ENOMEM);
@@ -475,7 +504,7 @@ static void init_frame(FlacEncodeContext *s)
}
for (ch = 0; ch < s->channels; ch++)
- frame->subframes[ch].obits = 16;
+ frame->subframes[ch].obits = s->bits_per_sample;
frame->verbatim_only = 0;
}
@@ -484,15 +513,23 @@ static void init_frame(FlacEncodeContext *s)
/**
* Copy channel-interleaved input samples into separate subframes.
*/
-static void copy_samples(FlacEncodeContext *s, const int16_t *samples)
+static void copy_samples(FlacEncodeContext *s, const void *samples)
{
int i, j, ch;
FlacFrame *frame;
- frame = &s->frame;
- for (i = 0, j = 0; i < frame->blocksize; i++)
- for (ch = 0; ch < s->channels; ch++, j++)
- frame->subframes[ch].samples[i] = samples[j];
+#define COPY_SAMPLES(bits, shift) do { \
+ const int ## bits ## _t *samples0 = samples; \
+ frame = &s->frame; \
+ for (i = 0, j = 0; i < frame->blocksize; i++) \
+ for (ch = 0; ch < s->channels; ch++, j++) \
+ frame->subframes[ch].samples[i] = samples0[j] >> shift; \
+} while (0)
+
+ if (s->bits_per_sample == 16)
+ COPY_SAMPLES(16, 0);
+ else
+ COPY_SAMPLES(32, 8);
}
@@ -740,9 +777,9 @@ static void encode_residual_fixed(int32_t *res, const int32_t *smp, int n,
#define LPC1(x) {\
int c = coefs[(x)-1];\
- p0 += c * s;\
+ p0 += MUL64(c, s);\
s = smp[i-(x)+1];\
- p1 += c * s;\
+ p1 += MUL64(c, s);\
}
static av_always_inline void encode_residual_lpc_unrolled(int32_t *res,
@@ -752,7 +789,7 @@ static av_always_inline void encode_residual_lpc_unrolled(int32_t *res,
int i;
for (i = order; i < n; i += 2) {
int s = smp[i-order];
- int p0 = 0, p1 = 0;
+ int64_t p0 = 0, p1 = 0;
if (big) {
switch (order) {
case 32: LPC1(32)
@@ -816,12 +853,12 @@ static void encode_residual_lpc(int32_t *res, const int32_t *smp, int n,
for (i = order; i < n; i += 2) {
int j;
int s = smp[i];
- int p0 = 0, p1 = 0;
+ int64_t p0 = 0, p1 = 0;
for (j = 0; j < order; j++) {
int c = coefs[j];
- p1 += c * s;
+ p1 += MUL64(c, s);
s = smp[i-j-1];
- p0 += c * s;
+ p0 += MUL64(c, s);
}
res[i ] = smp[i ] - (p0 >> shift);
res[i+1] = smp[i+1] - (p1 >> shift);
@@ -1144,7 +1181,7 @@ static void write_frame_header(FlacEncodeContext *s)
else
put_bits(&s->pb, 4, frame->ch_mode);
- put_bits(&s->pb, 3, 4); /* bits-per-sample code */
+ put_bits(&s->pb, 3, s->bps_code);
put_bits(&s->pb, 1, 0);
write_utf8(&s->pb, s->frame_count);
@@ -1244,17 +1281,43 @@ static int write_frame(FlacEncodeContext *s, uint8_t *frame, int buf_size)
}
-static void update_md5_sum(FlacEncodeContext *s, const int16_t *samples)
+static int update_md5_sum(FlacEncodeContext *s, const void *samples)
{
-#if HAVE_BIGENDIAN
- int i;
- for (i = 0; i < s->frame.blocksize * s->channels; i++) {
- int16_t smp = av_le2ne16(samples[i]);
- av_md5_update(s->md5ctx, (uint8_t *)&smp, 2);
+ int size = s->frame.blocksize * s->channels;
+ int bps = s->bits_per_sample / 8;
+
+ if (bps > 2 || HAVE_BIGENDIAN) {
+ if (!s->md5_buffer || s->md5_buffer_size < size * bps) {
+ uint8_t *tmp = av_realloc(s->md5_buffer, size * bps);
+ if (!tmp)
+ return AVERROR(ENOMEM);
+ s->md5_buffer = tmp;
+ s->md5_buffer_size = size * bps;
+ }
}
+
+ if (bps == 2) {
+#if HAVE_BIGENDIAN
+ s->dsp.bswap16_buf(s->md5_buffer, (const uint16_t *)samples, size);
+ av_md5_update(s->md5ctx, s->md5_buffer, size * bps);
#else
- av_md5_update(s->md5ctx, (const uint8_t *)samples, s->frame.blocksize*s->channels*2);
+ av_md5_update(s->md5ctx, samples, size * bps);
#endif
+ } else {
+ int i;
+ const int32_t *samples0 = samples;
+ uint8_t *tmp = s->md5_buffer;
+
+ for (i = 0; i < size; i++) {
+ int32_t v = samples0[i] >> 8;
+ *tmp++ = (v ) & 0xFF;
+ *tmp++ = (v >> 8) & 0xFF;
+ *tmp++ = (v >> 16) & 0xFF;
+ }
+ av_md5_update(s->md5ctx, s->md5_buffer, size * bps);
+ }
+
+ return 0;
}
@@ -1262,8 +1325,7 @@ static int flac_encode_frame(AVCodecContext *avctx, uint8_t *frame,
int buf_size, void *data)
{
FlacEncodeContext *s;
- const int16_t *samples = data;
- int frame_bytes, out_bytes;
+ int frame_bytes, out_bytes, ret;
s = avctx->priv_data;
@@ -1278,12 +1340,13 @@ static int flac_encode_frame(AVCodecContext *avctx, uint8_t *frame,
/* change max_framesize for small final frame */
if (avctx->frame_size < s->frame.blocksize) {
s->max_framesize = ff_flac_get_max_frame_size(avctx->frame_size,
- s->channels, 16);
+ s->channels,
+ s->bits_per_sample);
}
init_frame(s);
- copy_samples(s, samples);
+ copy_samples(s, data);
channel_decorrelation(s);
@@ -1291,9 +1354,11 @@ static int flac_encode_frame(AVCodecContext *avctx, uint8_t *frame,
/* fallback to verbatim mode if the compressed frame is larger than it
would be if encoded uncompressed. */
- if (frame_bytes > s->max_framesize) {
+ if (frame_bytes < 0 || frame_bytes > s->max_framesize) {
s->frame.verbatim_only = 1;
frame_bytes = encode_frame(s);
+ if (frame_bytes < 0)
+ return -1;
}
if (buf_size < frame_bytes) {
@@ -1305,7 +1370,10 @@ static int flac_encode_frame(AVCodecContext *avctx, uint8_t *frame,
s->frame_count++;
avctx->coded_frame->pts = s->sample_count;
s->sample_count += avctx->frame_size;
- update_md5_sum(s, samples);
+ if ((ret = update_md5_sum(s, data)) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "error updating MD5 checksum\n");
+ return ret;
+ }
if (out_bytes > s->max_encoded_framesize)
s->max_encoded_framesize = out_bytes;
if (out_bytes < s->min_framesize)
@@ -1320,6 +1388,8 @@ static av_cold int flac_encode_close(AVCodecContext *avctx)
if (avctx->priv_data) {
FlacEncodeContext *s = avctx->priv_data;
av_freep(&s->md5ctx);
+ av_freep(&s->md5_buffer);
+ s->md5_buffer_size = 0;
ff_lpc_end(&s->lpc_ctx);
}
av_freep(&avctx->extradata);
@@ -1346,6 +1416,7 @@ static const AVOption options[] = {
{ "8level", NULL, 0, AV_OPT_TYPE_CONST, {.dbl = ORDER_METHOD_8LEVEL }, INT_MIN, INT_MAX, FLAGS, "predm" },
{ "search", NULL, 0, AV_OPT_TYPE_CONST, {.dbl = ORDER_METHOD_SEARCH }, INT_MIN, INT_MAX, FLAGS, "predm" },
{ "log", NULL, 0, AV_OPT_TYPE_CONST, {.dbl = ORDER_METHOD_LOG }, INT_MIN, INT_MAX, FLAGS, "predm" },
+{ "bits_per_sample", NULL, offsetof(FlacEncodeContext, options.bits_per_sample), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, 24, FLAGS },
{ NULL },
};
@@ -1365,7 +1436,9 @@ AVCodec ff_flac_encoder = {
.encode = flac_encode_frame,
.close = flac_encode_close,
.capabilities = CODEC_CAP_SMALL_LAST_FRAME | CODEC_CAP_DELAY,
- .sample_fmts = (const enum AVSampleFormat[]){AV_SAMPLE_FMT_S16,AV_SAMPLE_FMT_NONE},
+ .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16,
+ AV_SAMPLE_FMT_S32,
+ AV_SAMPLE_FMT_NONE },
.long_name = NULL_IF_CONFIG_SMALL("FLAC (Free Lossless Audio Codec)"),
.priv_class = &flac_encoder_class,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment