Created
August 21, 2012 16:05
-
-
Save justinruggles/3416858 to your computer and use it in GitHub Desktop.
flac 24-bit encoding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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