[FFmpeg-soc] [soc]: r2798 - in mlp: libavcodec_mlpenc.diff mlpenc.c
ramiro
subversion at mplayerhq.hu
Tue Jul 15 23:49:10 CEST 2008
Author: ramiro
Date: Tue Jul 15 23:49:09 2008
New Revision: 2798
Log:
MLP Encoder - initial commit.
Added:
mlp/libavcodec_mlpenc.diff
mlp/mlpenc.c
Added: mlp/libavcodec_mlpenc.diff
==============================================================================
--- (empty file)
+++ mlp/libavcodec_mlpenc.diff Tue Jul 15 23:49:09 2008
@@ -0,0 +1,63 @@
+Index: Changelog
+===================================================================
+--- Changelog (revision 14247)
++++ Changelog (working copy)
+@@ -127,6 +127,7 @@
+ - Motion Pixels Video decoder
+ - Motion Pixels MVI demuxer
+ - removed animated GIF decoder/demuxer
++- MLP/TrueHD encoder
+
+ version 0.4.9-pre1:
+
+Index: doc/general.texi
+===================================================================
+--- doc/general.texi (revision 14247)
++++ doc/general.texi (working copy)
+@@ -380,7 +380,7 @@
+ @item MAXIS EA ADPCM @tab @tab X
+ @tab Used in Sim City 3000.
+ @item Microsoft ADPCM @tab X @tab X
+- at item MLP/TrueHD @tab @tab X
++ at item MLP/TrueHD @tab X @tab X
+ @tab Used in DVD-Audio and Blu-Ray discs.
+ @item Monkey's Audio @tab @tab X
+ @tab Only versions 3.97-3.99 are supported.
+Index: libavcodec/Makefile
+===================================================================
+--- libavcodec/Makefile (revision 14247)
++++ libavcodec/Makefile (working copy)
+@@ -109,6 +109,7 @@
+ OBJS-$(CONFIG_MJPEG_ENCODER) += mjpegenc.o mjpeg.o mpegvideo_enc.o motion_est.o ratecontrol.o mpeg12data.o mpegvideo.o
+ OBJS-$(CONFIG_MJPEGB_DECODER) += mjpegbdec.o mjpegdec.o mjpeg.o
+ OBJS-$(CONFIG_MLP_DECODER) += mlpdec.o
++OBJS-$(CONFIG_MLP_ENCODER) += mlpenc.o
+ OBJS-$(CONFIG_MMVIDEO_DECODER) += mmvideo.o
+ OBJS-$(CONFIG_MOTIONPIXELS_DECODER) += motionpixels.o
+ OBJS-$(CONFIG_MP2_DECODER) += mpegaudiodec.o mpegaudiodecheader.o mpegaudio.o mpegaudiodata.o
+Index: libavcodec/allcodecs.c
+===================================================================
+--- libavcodec/allcodecs.c (revision 14247)
++++ libavcodec/allcodecs.c (working copy)
+@@ -191,7 +191,7 @@
+ REGISTER_DECODER (IMC, imc);
+ REGISTER_DECODER (MACE3, mace3);
+ REGISTER_DECODER (MACE6, mace6);
+- REGISTER_DECODER (MLP, mlp);
++ REGISTER_ENCDEC (MLP, mlp);
+ REGISTER_ENCDEC (MP2, mp2);
+ REGISTER_DECODER (MP3, mp3);
+ REGISTER_DECODER (MP3ADU, mp3adu);
+Index: libavcodec/avcodec.h
+===================================================================
+--- libavcodec/avcodec.h (revision 14247)
++++ libavcodec/avcodec.h (working copy)
+@@ -30,7 +30,7 @@
+ #include "libavutil/avutil.h"
+
+ #define LIBAVCODEC_VERSION_MAJOR 51
+-#define LIBAVCODEC_VERSION_MINOR 60
++#define LIBAVCODEC_VERSION_MINOR 61
+ #define LIBAVCODEC_VERSION_MICRO 0
+
+ #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
Added: mlp/mlpenc.c
==============================================================================
--- (empty file)
+++ mlp/mlpenc.c Tue Jul 15 23:49:09 2008
@@ -0,0 +1,792 @@
+/*
+ * MLP encoder
+ * Copyright (c) 2008 Ramiro Polla <ramiro at lisha.ufsc.br>
+ *
+ * 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
+ */
+
+#include "avcodec.h"
+#include "bitstream.h"
+#include "libavutil/crc.h"
+
+/* TODO add comments! */
+
+#define MAJOR_HEADER_INTERVAL 16
+
+#define MAX_CHANNELS 16
+#define MAX_SUBSTREAMS 2
+#define MAX_SAMPLERATE 192000
+#define MAX_BLOCKSIZE (40 * (MAX_SAMPLERATE / 48000))
+#define MAX_BLOCKSIZE_POW2 (64 * (MAX_SAMPLERATE / 48000))
+
+#define FIR 0
+#define IIR 1
+
+typedef struct {
+ uint8_t min_channel;
+ uint8_t max_channel;
+ uint8_t max_matrix_channel;
+
+ uint8_t noise_shift;
+ uint32_t noisegen_seed;
+
+ int data_check_present;
+
+ int32_t lossless_check_data;
+} RestartHeader;
+
+typedef struct {
+ uint16_t blocksize;
+ uint8_t quant_step_size[MAX_CHANNELS];
+
+ uint8_t num_primitive_matrices;
+
+ int8_t output_shift[MAX_CHANNELS];
+
+ uint8_t param_presence_flags;
+#define PARAM_PRESENCE_FLAGS (1 << 8)
+
+#define PARAMS_DEFAULT (0xFF)
+#define PARAM_BLOCKSIZE (1 << 7)
+#define PARAM_MATRIX (1 << 6)
+#define PARAM_OUTSHIFT (1 << 5)
+#define PARAM_QUANTSTEP (1 << 4)
+#define PARAM_FIR (1 << 3)
+#define PARAM_IIR (1 << 2)
+#define PARAM_HUFFOFFSET (1 << 1)
+
+ uint8_t codebook[MAX_CHANNELS];
+ uint8_t huff_lsbs[MAX_CHANNELS];
+
+ /* TODO This should be part of the greater context. */
+ int16_t huff_offset[MAX_CHANNELS];
+#define HUFF_OFFSET_MIN -16384
+#define HUFF_OFFSET_MAX 16383
+
+} DecodingParams;
+
+typedef struct {
+ AVCodecContext *avctx;
+
+ int num_substreams;
+
+ int sample_fmt;
+ int sample_rate;
+
+ int32_t sample_buffer[MAX_BLOCKSIZE][MAX_CHANNELS+2];
+
+ uint16_t timestamp;
+
+ uint8_t mlp_channels;
+
+ DecodingParams decoding_params[MAX_SUBSTREAMS];
+ RestartHeader restart_header[MAX_SUBSTREAMS];
+} MLPEncodeContext;
+
+#define SYNC_MAJOR 0xf8726f
+
+#define SYNC_MLP 0xbb
+#define SYNC_TRUEHD 0xba
+
+#define BITS_16 0x0
+#define BITS_20 0x1
+#define BITS_24 0x2
+
+#define MAX_SAMPLERATE 192000
+
+static int mlp_sample_rate(int sample_rate)
+{
+ int sample_base = 48000;
+ uint8_t code = 0x0;
+
+ switch (sample_rate) {
+ case 44100 << 0:
+ case 44100 << 1:
+ case 44100 << 2:
+ sample_base = 44100;
+ code = 0x8;
+ case 48000 << 0:
+ case 48000 << 1:
+ case 48000 << 2:
+ break;
+ default:
+ return -1;
+ }
+
+ for (; sample_rate != sample_base; sample_rate >>= 1)
+ code++;
+
+ return code;
+}
+
+/* TODO all these checksum functions and crc stuff can be shared between
+ * encoder and decoder. */
+
+static AVCRC crc_1D[1024];
+static AVCRC crc_2D[1024];
+static AVCRC crc_63[1024];
+
+static uint16_t mlp_checksum16(const uint8_t *buf, unsigned int buf_size)
+{
+ uint16_t crc = av_crc(crc_2D, 0, buf, buf_size - 2);
+
+ crc ^= AV_RL16(buf + buf_size - 2);
+
+ return crc;
+}
+
+static uint8_t mlp_checksum8(const uint8_t *buf, unsigned int buf_size)
+{
+ uint8_t checksum = av_crc(crc_63, 0x3c, buf, buf_size - 1); // crc_63[0xa2] == 0x3c
+ checksum ^= buf[buf_size-1];
+ return checksum;
+}
+
+static uint8_t mlp_restart_checksum(const uint8_t *buf, unsigned int bit_size)
+{
+ int i;
+ int num_bytes = (bit_size + 2) / 8;
+
+ int crc = crc_1D[buf[0] & 0x3f];
+ crc = av_crc(crc_1D, crc, buf + 1, num_bytes - 2);
+ crc ^= buf[num_bytes - 1];
+
+ for (i = 0; i < ((bit_size + 2) & 7); i++) {
+ crc <<= 1;
+ if (crc & 0x100)
+ crc ^= 0x11D;
+ crc ^= (buf[num_bytes] >> (7 - i)) & 1;
+ }
+
+ return crc;
+}
+
+static uint8_t calculate_parity(const uint8_t *buf, unsigned int buf_size)
+{
+ uint32_t scratch = 0;
+ const uint8_t *buf_end = buf + buf_size;
+
+ for (; buf < buf_end - 3; buf += 4)
+ scratch ^= *((const uint32_t*)buf);
+
+ scratch ^= scratch >> 16;
+ scratch ^= scratch >> 8;
+
+ for (; buf < buf_end; buf++)
+ scratch ^= *buf;
+
+ return scratch;
+}
+
+static void write_major_sync(MLPEncodeContext *ctx, uint8_t *buf, int buf_size)
+{
+ PutBitContext pb;
+
+ init_put_bits(&pb, buf, buf_size);
+
+ put_bits(&pb, 24, SYNC_MAJOR );
+ put_bits(&pb, 8, SYNC_MLP );
+ put_bits(&pb, 4, ctx->sample_fmt );
+ put_bits(&pb, 4, ctx->sample_fmt );
+ put_bits(&pb, 4, ctx->sample_rate );
+ put_bits(&pb, 4, ctx->sample_rate );
+ put_bits(&pb, 11, 0 );
+ put_bits(&pb, 5, ctx->mlp_channels);
+
+ /* TODO copied from luckynight.mlp, 440hz.mlp and god.mlp. */
+ put_bits(&pb, 16, 0xb752 );
+ put_bits(&pb, 16, 0x4000 );
+ put_bits(&pb, 16, 0 );
+
+ put_bits(&pb, 1, 1 ); /* TODO is_vbr */
+ put_bits(&pb, 15, 0 ); /* TODO peak_bitrate */
+ put_bits(&pb, 4, 1 ); /* TODO num_substreams */
+
+ /* TODO copied from luckynight.mlp, 440hz.mlp. */
+#if 0
+god 20d763f0000808000004536
+440hz
+lucky 1054c0300008080001b538c
+#endif
+ put_bits(&pb, 4, 0x1 );
+ put_bits(&pb, 32, 0x054c0300 );
+ put_bits(&pb, 32, 0x00808000 );
+ put_bits(&pb, 8, 0x1b );
+
+ flush_put_bits(&pb);
+
+ AV_WL16(buf+26, mlp_checksum16(buf, 26));
+}
+
+/* TODO pass only PutBitContext and use pb->buffer. */
+static void write_restart_header(MLPEncodeContext *ctx, uint8_t *buf,
+ PutBitContext *pb, int substr)
+{
+ RestartHeader *rh = &ctx->restart_header[substr];
+ int32_t lossless_check = rh->lossless_check_data;
+ unsigned int start_count = put_bits_count(pb);
+ PutBitContext tmpb;
+ uint8_t checksum;
+ unsigned int ch;
+
+ lossless_check ^= lossless_check >> 16;
+ lossless_check ^= lossless_check >> 8;
+ lossless_check &= 0xFF;
+
+ put_bits(pb, 14, 0x31ea ); /* TODO 0x31eb */
+ put_bits(pb, 16, 0 ); /* TODO I don't know what this is. Ask Ian. */
+ put_bits(pb, 4, rh->min_channel );
+ put_bits(pb, 4, rh->max_channel );
+ put_bits(pb, 4, rh->max_matrix_channel);
+ put_bits(pb, 4, rh->noise_shift );
+ put_bits(pb, 23, rh->noisegen_seed );
+ put_bits(pb, 19, 0 ); /* TODO What the hell is this? */
+ put_bits(pb, 1, rh->data_check_present);
+ put_bits(pb, 8, lossless_check );
+ put_bits(pb, 16, 0 ); /* this is zero =) */
+
+ for (ch = 0; ch <= rh->max_matrix_channel; ch++)
+ put_bits(pb, 6, ch);
+
+ /* data must be flushed for the checksum to be right. */
+ tmpb = *pb;
+ flush_put_bits(&tmpb);
+
+ checksum = mlp_restart_checksum(buf, put_bits_count(pb) - start_count);
+
+ put_bits(pb, 8, checksum);
+}
+
+static av_cold int mlp_encode_init(AVCodecContext *avctx)
+{
+ MLPEncodeContext *ctx = avctx->priv_data;
+ unsigned int substr;
+
+ ctx->avctx = avctx;
+
+ ctx->sample_rate = mlp_sample_rate(avctx->sample_rate);
+ if (ctx->sample_rate < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Unsupported sample_rate.\n");
+ return -1;
+ }
+
+ /* TODO support more channels. */
+ if (avctx->channels > 2) {
+ av_log(avctx, AV_LOG_ERROR,
+ "Only mono and stereo are supported at the moment.\n");
+ return -1;
+ }
+
+ switch (avctx->sample_fmt) {
+ case SAMPLE_FMT_S16: ctx->sample_fmt = BITS_16; break;
+ /* TODO 20 bits: */
+ /* TODO Find out how to actually support 24 bits and update all occurences
+ * of hardcoded 8s with appropriate value (probably quant_step_size). */
+ case SAMPLE_FMT_S24: ctx->sample_fmt = BITS_24; break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Sample format not supported.\n");
+ return -1;
+ }
+
+ avctx->frame_size = 40 << (ctx->sample_rate & 0x7);
+ avctx->coded_frame = avcodec_alloc_frame();
+ avctx->coded_frame->key_frame = 1;
+
+ av_crc_init(crc_1D, 0, 8, 0x1D, sizeof(crc_1D));
+ av_crc_init(crc_2D, 0, 16, 0x002D, sizeof(crc_2D));
+ av_crc_init(crc_63, 0, 8, 0x63, sizeof(crc_63));
+
+ /* TODO mlp_channels is more complex, but for now
+ * we only accept mono and stereo. */
+ ctx->mlp_channels = avctx->channels - 1;
+ ctx->num_substreams = 1;
+
+ for (substr = 0; substr < ctx->num_substreams; substr++) {
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ uint8_t param_presence_flags = 0;
+ unsigned int channel;
+
+ rh->min_channel = 0;
+ rh->max_channel = avctx->channels - 1;
+ rh->max_matrix_channel = 1;
+
+ rh->noise_shift = 0;
+ rh->noisegen_seed = 0;
+
+ rh->data_check_present = 0;
+
+ dp->blocksize = avctx->frame_size;
+
+ for (channel = 0; channel <= rh->max_channel; channel++) {
+ dp->quant_step_size[channel] = 8;
+ dp->codebook [channel] = 0;
+ dp->huff_lsbs [channel] = 24;
+ }
+
+ param_presence_flags |= PARAM_BLOCKSIZE;
+/* param_presence_flags |= PARAM_MATRIX; */
+/* param_presence_flags |= PARAM_OUTSHIFT; */
+ param_presence_flags |= PARAM_QUANTSTEP;
+/* param_presence_flags |= PARAM_FIR; */
+/* param_presence_flags |= PARAM_IIR; */
+ param_presence_flags |= PARAM_HUFFOFFSET;
+
+ dp->param_presence_flags = param_presence_flags;
+ }
+
+ return 0;
+}
+
+static void write_filter_params(MLPEncodeContext *ctx, PutBitContext *pb,
+ unsigned int channel, unsigned int filter)
+{
+ return;
+}
+
+static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb,
+ unsigned int substr, int params_changed)
+{
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ unsigned int ch;
+
+ if (dp->param_presence_flags != PARAMS_DEFAULT &&
+ params_changed & PARAM_PRESENCE_FLAGS) {
+ put_bits(pb, 1, 1);
+ put_bits(pb, 8, dp->param_presence_flags);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+
+ if (dp->param_presence_flags & PARAM_BLOCKSIZE) {
+ if (params_changed & PARAM_BLOCKSIZE) {
+ put_bits(pb, 1, 1);
+ put_bits(pb, 9, dp->blocksize);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ if (dp->param_presence_flags & PARAM_MATRIX) {
+ if (params_changed & PARAM_MATRIX) {
+ put_bits(pb, 1, 1);
+#if 1
+ put_bits(pb, 4, 0);
+#else
+ /* TODO no primitive matrices yet. */
+ put_bits(pb, 4, dp->num_primitive_matrices);
+#endif
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ if (dp->param_presence_flags & PARAM_OUTSHIFT) {
+ if (params_changed & PARAM_OUTSHIFT) {
+ put_bits(pb, 1, 1);
+ for (ch = 0; ch <= rh->max_matrix_channel; ch++)
+ put_bits(pb, 4, dp->output_shift[ch] & 0xF);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ if (dp->param_presence_flags & PARAM_QUANTSTEP) {
+ if (params_changed & PARAM_QUANTSTEP) {
+ put_bits(pb, 1, 1);
+ for (ch = 0; ch <= rh->max_channel; ch++)
+ put_bits(pb, 4, dp->quant_step_size[ch]);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ for (ch = rh->min_channel; ch <= rh->max_channel; ch++) {
+ if (dp->param_presence_flags & 0xF) {
+ put_bits(pb, 1, 1);
+
+ if (dp->param_presence_flags & PARAM_FIR) {
+ if (params_changed & PARAM_FIR) {
+ put_bits(pb, 1, 1);
+ write_filter_params(ctx, pb, ch, FIR);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ if (dp->param_presence_flags & PARAM_IIR) {
+ if (params_changed & PARAM_IIR) {
+ put_bits(pb, 1, 1);
+ write_filter_params(ctx, pb, ch, IIR);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ if (dp->param_presence_flags & PARAM_HUFFOFFSET) {
+ if (params_changed & PARAM_HUFFOFFSET) {
+ put_bits(pb, 1, 1);
+ put_bits(pb, 15, dp->huff_offset[ch] & 0x7FFF);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+
+ put_bits(pb, 2, dp->codebook [ch]);
+ put_bits(pb, 5, dp->huff_lsbs[ch]);
+ } else {
+ put_bits(pb, 1, 0);
+ }
+ }
+}
+
+static void input_data(MLPEncodeContext *ctx, const short *samples,
+ int32_t *lossless_check_data)
+{
+ unsigned int substr;
+
+ for (substr = 0; substr < ctx->num_substreams; substr++) {
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ int32_t lossless_check_data_temp = 0;
+ unsigned int channel;
+ int i;
+
+ for (channel = 0; channel <= rh->max_channel; channel++) {
+ for (i = 0; i < dp->blocksize; i++) {
+ int32_t sample = samples[i * (rh->max_channel + 1) + channel];
+ sample = (sample << 8) & 0x00ffffff;
+ lossless_check_data_temp ^= sample << channel;
+ ctx->sample_buffer[i][channel] = sample;
+ }
+ }
+
+ lossless_check_data[substr] = lossless_check_data_temp;
+ }
+}
+
+static void determine_bits(MLPEncodeContext *ctx)
+{
+ unsigned int substr;
+
+ for (substr = 0; substr < ctx->num_substreams; substr++) {
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ unsigned int channel;
+
+ for (channel = 0; channel <= rh->max_channel; channel++) {
+ int16_t min = INT16_MAX, max = INT16_MIN;
+ int16_t unsign, offset;
+ uint16_t diff;
+ int nbits, i;
+
+ /* Determine extremes. */
+ for (i = 0; i < dp->blocksize; i++) {
+ int16_t sample = ctx->sample_buffer[i][channel] >> 8;
+ if (sample < min)
+ min = sample;
+ if (sample > max)
+ max = sample;
+ }
+
+ /* Set offset inside huffoffset's boundaries by adjusting extremes
+ * so that more bits are used thus shifting the offset. */
+ if (min < HUFF_OFFSET_MIN)
+ max = FFMAX(max, HUFF_OFFSET_MIN + HUFF_OFFSET_MIN - min + 1);
+ if (max > HUFF_OFFSET_MAX)
+ min = FFMIN(min, HUFF_OFFSET_MAX + HUFF_OFFSET_MAX - max - 1);
+
+ /* Determine offset and minimum number of bits. */
+ diff = max - min;
+
+ for (nbits = 16; nbits && !(diff & (1<<(nbits-1))); nbits--);
+
+ unsign = 1 << (nbits - 1);
+
+ /* If all samples are the same (nbits == 0), offset must be
+ * adjusted because of sign_shift. */
+ offset = min + diff / 2 + !!nbits;
+
+ /* Check if we can use the same offset as last access_unit to save
+ * on writing a new header. */
+ if (nbits + 8 == dp->huff_lsbs[channel]) {
+ int16_t cur_offset = dp->huff_offset[channel];
+ int16_t cur_max = cur_offset + unsign - 1;
+ int16_t cur_min = cur_offset - unsign;
+
+ if (min > cur_min && max < cur_max)
+ offset = cur_offset;
+ }
+
+ /* Update context. */
+ dp->huff_offset[channel] = offset;
+ dp->huff_lsbs [channel] = nbits + 8;
+
+ /* Unsign and offset all samples. */
+ for (i = 0; i < dp->blocksize; i++) {
+ int32_t sample = ctx->sample_buffer[i][channel] >> 8;
+ sample -= offset;
+ sample += unsign;
+ ctx->sample_buffer[i][channel] = sample;
+ }
+ }
+ }
+}
+
+static void write_block_data(MLPEncodeContext *ctx, PutBitContext *pb,
+ unsigned int substr)
+{
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ int lsb_bits[MAX_CHANNELS];
+ unsigned int i, ch;
+
+ for (ch = rh->min_channel; ch <= rh->max_channel; ch++) {
+ lsb_bits[ch] = dp->huff_lsbs[ch] - dp->quant_step_size[ch];
+ }
+
+ for (i = 0; i < dp->blocksize; i++) {
+ for (ch = rh->min_channel; ch <= rh->max_channel; ch++) {
+ put_sbits(pb, lsb_bits[ch], ctx->sample_buffer[i][ch]);
+ }
+ }
+}
+
+static int decoding_params_diff(MLPEncodeContext *ctx, DecodingParams *prev,
+ unsigned int substr, int write_all)
+{
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ unsigned int ch;
+ int retval = 0;
+
+ if (write_all)
+ return PARAM_PRESENCE_FLAGS | PARAMS_DEFAULT;
+
+ if (prev->param_presence_flags != dp->param_presence_flags)
+ retval |= PARAM_PRESENCE_FLAGS;
+
+ if (prev->blocksize != dp->blocksize)
+ retval |= PARAM_BLOCKSIZE;
+
+ if (prev->num_primitive_matrices != dp->num_primitive_matrices)
+ retval |= PARAM_MATRIX;
+
+ for (ch = 0; ch <= rh->max_matrix_channel; ch++)
+ if (prev->output_shift[ch] != dp->output_shift[ch])
+ retval |= PARAM_OUTSHIFT;
+
+ for (ch = 0; ch <= rh->max_channel; ch++)
+ if (prev->quant_step_size[ch] != dp->quant_step_size[ch])
+ retval |= PARAM_QUANTSTEP;
+
+ for (ch = rh->min_channel; ch <= rh->max_channel; ch++) {
+
+ /* TODO Check filters. */
+
+ if (prev->huff_offset[ch] != dp->huff_offset[ch])
+ retval |= PARAM_HUFFOFFSET;
+
+ if (prev->codebook [ch] != dp->codebook [ch] ||
+ prev->huff_lsbs[ch] != dp->huff_lsbs[ch])
+ retval |= 0x1;
+ }
+
+ return retval;
+}
+
+static int mlp_encode_frame(AVCodecContext *avctx, uint8_t *buf, int buf_size,
+ void *data)
+{
+ DecodingParams decoding_params[MAX_SUBSTREAMS];
+ uint16_t substream_data_len[MAX_SUBSTREAMS];
+ int32_t lossless_check_data[MAX_SUBSTREAMS];
+ MLPEncodeContext *ctx = avctx->priv_data;
+ uint8_t *buf2, *buf1, *buf0 = buf;
+ uint16_t access_unit_header = 0;
+ uint16_t parity_nibble = 0;
+ int length, total_length;
+ unsigned int substr;
+ int write_headers;
+ PutBitContext pb;
+ int end = 0;
+
+ if (avctx->frame_size > MAX_BLOCKSIZE) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid frame size (%d > %d)\n",
+ avctx->frame_size, MAX_BLOCKSIZE);
+ return -1;
+ }
+
+ memcpy(decoding_params, ctx->decoding_params, sizeof(decoding_params));
+
+ if (buf_size < 4)
+ return -1;
+
+ /* Frame header will be written at the end. */
+ buf += 4;
+ buf_size -= 4;
+
+ write_headers = !(avctx->frame_number & (MAJOR_HEADER_INTERVAL - 1));
+
+ if (write_headers) {
+ if (buf_size < 28)
+ return -1;
+ write_major_sync(ctx, buf, buf_size);
+ buf += 28;
+ buf_size -= 28;
+ }
+
+ buf1 = buf;
+
+ /* Substream headers will be written at the end. */
+ for (substr = 0; substr < ctx->num_substreams; substr++) {
+ buf += 2;
+ buf_size -= 2;
+ }
+
+ buf2 = buf;
+
+ total_length = buf - buf0;
+
+ input_data(ctx, data, lossless_check_data);
+
+ determine_bits(ctx);
+
+ for (substr = 0; substr < ctx->num_substreams; substr++) {
+ DecodingParams *dp = &ctx->decoding_params[substr];
+ RestartHeader *rh = &ctx->restart_header [substr];
+ uint8_t parity, checksum;
+ PutBitContext tmpb;
+ int params_changed;
+ int last_block = 0;
+
+ init_put_bits(&pb, buf, buf_size);
+
+ if (avctx->frame_size < dp->blocksize) {
+ dp->blocksize = avctx->frame_size;
+ last_block = 1;
+ }
+
+ params_changed = decoding_params_diff(ctx, &decoding_params[substr],
+ substr, write_headers);
+
+ if (write_headers || params_changed) {
+ put_bits(&pb, 1, 1);
+
+ if (write_headers) {
+ put_bits(&pb, 1, 1);
+
+ write_restart_header(ctx, buf, &pb, substr);
+ rh->lossless_check_data = 0;
+ } else {
+ put_bits(&pb, 1, 0);
+ }
+
+ write_decoding_params(ctx, &pb, substr, params_changed);
+ } else {
+ put_bits(&pb, 1, 0);
+ }
+
+ rh->lossless_check_data ^= lossless_check_data[substr];
+
+ write_block_data(ctx, &pb, substr);
+
+ put_bits(&pb, 1, 1); /* TODO ??? */
+
+ put_bits(&pb, (-put_bits_count(&pb)) & 15, 0);
+
+ if (last_block) {
+ /* TODO find a sample and implement shorten_by. */
+ put_bits(&pb, 32, 0xd234d234);
+ }
+
+ /* data must be flushed for the checksum and parity to be right. */
+ tmpb = pb;
+ flush_put_bits(&tmpb);
+
+ parity = calculate_parity(buf, put_bits_count(&pb) >> 3) ^ 0xa9;
+ checksum = mlp_checksum8 (buf, put_bits_count(&pb) >> 3);
+
+ put_bits(&pb, 8, parity );
+ put_bits(&pb, 8, checksum);
+
+ flush_put_bits(&pb);
+
+ end += put_bits_count(&pb) >> 3;
+ substream_data_len[substr] = end;
+
+ buf += put_bits_count(&pb) >> 3;
+ }
+
+ length = buf - buf2;
+ total_length += length;
+
+ /* Write headers. */
+ length = total_length / 2;
+
+ parity_nibble = ctx->timestamp;
+ parity_nibble ^= length;
+
+ for (substr = 0; substr < ctx->num_substreams; substr++) {
+ uint16_t substr_hdr = 0;
+
+ substr_hdr |= (0 << 15); /* extraword */
+ substr_hdr |= (0 << 14); /* ??? */
+ substr_hdr |= (1 << 13); /* checkdata */
+ substr_hdr |= (0 << 12); /* ??? */
+ substr_hdr |= (substream_data_len[substr] / 2) & 0x0FFF;
+
+ AV_WB16(buf1, substr_hdr);
+
+ parity_nibble ^= *buf1++;
+ parity_nibble ^= *buf1++;
+ }
+
+ parity_nibble ^= parity_nibble >> 8;
+ parity_nibble ^= parity_nibble >> 4;
+ parity_nibble &= 0xF;
+
+ access_unit_header |= (parity_nibble ^ 0xF) << 12;
+ access_unit_header |= length & 0xFFF;
+
+ AV_WB16(buf0 , access_unit_header);
+ AV_WB16(buf0+2, ctx->timestamp );
+
+ ctx->timestamp += avctx->frame_size;
+
+ return total_length;
+}
+
+static av_cold int mlp_encode_close(AVCodecContext *avctx)
+{
+ av_freep(&avctx->coded_frame);
+
+ return 0;
+}
+
+AVCodec mlp_encoder = {
+ "mlp",
+ CODEC_TYPE_AUDIO,
+ CODEC_ID_MLP,
+ sizeof(MLPEncodeContext),
+ mlp_encode_init,
+ mlp_encode_frame,
+ mlp_encode_close,
+ .capabilities = CODEC_CAP_SMALL_LAST_FRAME,
+ .long_name = NULL_IF_CONFIG_SMALL("Meridian Lossless Packing"),
+};
More information about the FFmpeg-soc
mailing list