]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/ac3enc.c
ac3enc: Add codec-specific options for writing AC-3 metadata.
[ffmpeg] / libavcodec / ac3enc.c
index 833904fa15ab079d46ed35cdbea385ec8a95a802..f7a366b07df69b81d3e7ec15d9795969b2b8eae5 100644 (file)
@@ -4,20 +4,20 @@
  * Copyright (c) 2006-2010 Justin Ruggles <justin.ruggles@gmail.com>
  * Copyright (c) 2006-2010 Prakash Punnoor <prakash@punnoor.de>
  *
- * This file is part of FFmpeg.
+ * This file is part of Libav.
  *
- * FFmpeg is free software; you can redistribute it and/or
+ * Libav 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,
+ * Libav 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
+ * License along with Libav; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
@@ -32,6 +32,7 @@
 #include "libavutil/audioconvert.h"
 #include "libavutil/avassert.h"
 #include "libavutil/crc.h"
+#include "libavutil/opt.h"
 #include "avcodec.h"
 #include "put_bits.h"
 #include "dsputil.h"
 #endif
 
 
+/**
+ * Encoding Options used by AVOption.
+ */
+typedef struct AC3EncOptions {
+    /* AC-3 metadata options*/
+    int dialogue_level;
+    int bitstream_mode;
+    float center_mix_level;
+    float surround_mix_level;
+    int dolby_surround_mode;
+    int audio_production_info;
+    int mixing_level;
+    int room_type;
+    int copyright;
+    int original;
+    int extended_bsi_1;
+    int preferred_stereo_downmix;
+    float ltrt_center_mix_level;
+    float ltrt_surround_mix_level;
+    float loro_center_mix_level;
+    float loro_surround_mix_level;
+    int extended_bsi_2;
+    int dolby_surround_ex_mode;
+    int dolby_headphone_mode;
+    int ad_converter_type;
+
+    /* other encoding options */
+    int allow_per_frame_metadata;
+} AC3EncOptions;
+
 /**
  * Data for a single audio block.
  */
@@ -78,7 +109,7 @@ typedef struct AC3Block {
     int16_t  **band_psd;                        ///< psd per critical band
     int16_t  **mask;                            ///< masking curve
     uint16_t **qmant;                           ///< quantized mantissas
-    int8_t   exp_shift[AC3_MAX_CHANNELS];       ///< exponent shift values
+    uint8_t  coeff_shift[AC3_MAX_CHANNELS];     ///< fixed-point coefficient shift values
     uint8_t  new_rematrixing_strategy;          ///< send new rematrixing flags in this block
     uint8_t  rematrixing_flags[4];              ///< rematrixing flags
 } AC3Block;
@@ -87,6 +118,8 @@ typedef struct AC3Block {
  * AC-3 encoder private context.
  */
 typedef struct AC3EncodeContext {
+    AVClass *av_class;                      ///< AVClass used for AVOption
+    AC3EncOptions options;                  ///< encoding options
     PutBitContext pb;                       ///< bitstream writer context
     DSPContext dsp;
     AC3DSPContext ac3dsp;                   ///< AC-3 optimized functions
@@ -111,9 +144,18 @@ typedef struct AC3EncodeContext {
     int channels;                           ///< total number of channels               (nchans)
     int lfe_on;                             ///< indicates if there is an LFE channel   (lfeon)
     int lfe_channel;                        ///< channel index of the LFE channel
+    int has_center;                         ///< indicates if there is a center channel
+    int has_surround;                       ///< indicates if there are one or more surround channels
     int channel_mode;                       ///< channel mode                           (acmod)
     const uint8_t *channel_map;             ///< channel map used to reorder channels
 
+    int center_mix_level;                   ///< center mix level code
+    int surround_mix_level;                 ///< surround mix level code
+    int ltrt_center_mix_level;              ///< Lt/Rt center mix level code
+    int ltrt_surround_mix_level;            ///< Lt/Rt surround mix level code
+    int loro_center_mix_level;              ///< Lo/Ro center mix level code
+    int loro_surround_mix_level;            ///< Lo/Ro surround mix level code
+
     int cutoff;                             ///< user-specified cutoff frequency, in Hz
     int bandwidth_code[AC3_MAX_CHANNELS];   ///< bandwidth code (0 to 60)               (chbwcod)
     int nb_coefs[AC3_MAX_CHANNELS];
@@ -157,6 +199,78 @@ typedef struct AC3EncodeContext {
 } AC3EncodeContext;
 
 
+#define CMIXLEV_NUM_OPTIONS 3
+static const float cmixlev_options[CMIXLEV_NUM_OPTIONS] = {
+    LEVEL_MINUS_3DB, LEVEL_MINUS_4POINT5DB, LEVEL_MINUS_6DB
+};
+
+#define SURMIXLEV_NUM_OPTIONS 3
+static const float surmixlev_options[SURMIXLEV_NUM_OPTIONS] = {
+    LEVEL_MINUS_3DB, LEVEL_MINUS_6DB, LEVEL_ZERO
+};
+
+#define EXTMIXLEV_NUM_OPTIONS 8
+static const float extmixlev_options[EXTMIXLEV_NUM_OPTIONS] = {
+    LEVEL_PLUS_3DB,  LEVEL_PLUS_1POINT5DB,  LEVEL_ONE,       LEVEL_MINUS_4POINT5DB,
+    LEVEL_MINUS_3DB, LEVEL_MINUS_4POINT5DB, LEVEL_MINUS_6DB, LEVEL_ZERO
+};
+
+
+#define OFFSET(param) offsetof(AC3EncodeContext, options.param)
+#define AC3ENC_PARAM (AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
+
+static const AVOption options[] = {
+/* Metadata Options */
+{"per_frame_metadata", "Allow Changing Metadata Per-Frame", OFFSET(allow_per_frame_metadata), FF_OPT_TYPE_INT, 0, 0, 1, AC3ENC_PARAM},
+/* downmix levels */
+{"center_mixlev", "Center Mix Level", OFFSET(center_mix_level), FF_OPT_TYPE_FLOAT, LEVEL_MINUS_4POINT5DB, 0.0, 1.0, AC3ENC_PARAM},
+{"surround_mixlev", "Surround Mix Level", OFFSET(surround_mix_level), FF_OPT_TYPE_FLOAT, LEVEL_MINUS_6DB, 0.0, 1.0, AC3ENC_PARAM},
+/* audio production information */
+{"mixing_level", "Mixing Level", OFFSET(mixing_level), FF_OPT_TYPE_INT, -1, -1, 111, AC3ENC_PARAM},
+{"room_type", "Room Type", OFFSET(room_type), FF_OPT_TYPE_INT, -1, -1, 2, AC3ENC_PARAM, "room_type"},
+    {"notindicated", "Not Indicated (default)", 0, FF_OPT_TYPE_CONST, 0, INT_MIN, INT_MAX, AC3ENC_PARAM, "room_type"},
+    {"large",        "Large Room",              0, FF_OPT_TYPE_CONST, 1, INT_MIN, INT_MAX, AC3ENC_PARAM, "room_type"},
+    {"small",        "Small Room",              0, FF_OPT_TYPE_CONST, 2, INT_MIN, INT_MAX, AC3ENC_PARAM, "room_type"},
+/* other metadata options */
+{"copyright", "Copyright Bit", OFFSET(copyright), FF_OPT_TYPE_INT, 0, 0, 1, AC3ENC_PARAM},
+{"dialnorm", "Dialogue Level (dB)", OFFSET(dialogue_level), FF_OPT_TYPE_INT, -31, -31, -1, AC3ENC_PARAM},
+{"dsur_mode", "Dolby Surround Mode", OFFSET(dolby_surround_mode), FF_OPT_TYPE_INT, 0, 0, 2, AC3ENC_PARAM, "dsur_mode"},
+    {"notindicated", "Not Indicated (default)",    0, FF_OPT_TYPE_CONST, 0, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsur_mode"},
+    {"on",           "Dolby Surround Encoded",     0, FF_OPT_TYPE_CONST, 1, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsur_mode"},
+    {"off",          "Not Dolby Surround Encoded", 0, FF_OPT_TYPE_CONST, 2, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsur_mode"},
+{"original", "Original Bit Stream", OFFSET(original), FF_OPT_TYPE_INT, 1, 0, 1, AC3ENC_PARAM},
+/* extended bitstream information */
+{"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), FF_OPT_TYPE_INT, -1, -1, 2, AC3ENC_PARAM, "dmix_mode"},
+    {"notindicated", "Not Indicated (default)", 0, FF_OPT_TYPE_CONST, 0, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"},
+    {"ltrt", "Lt/Rt Downmix Preferred",         0, FF_OPT_TYPE_CONST, 1, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"},
+    {"loro", "Lo/Ro Downmix Preferred",         0, FF_OPT_TYPE_CONST, 2, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"},
+{"ltrt_cmixlev", "Lt/Rt Center Mix Level", OFFSET(ltrt_center_mix_level), FF_OPT_TYPE_FLOAT, -1.0, -1.0, 2.0, AC3ENC_PARAM},
+{"ltrt_surmixlev", "Lt/Rt Surround Mix Level", OFFSET(ltrt_surround_mix_level), FF_OPT_TYPE_FLOAT, -1.0, -1.0, 2.0, AC3ENC_PARAM},
+{"loro_cmixlev", "Lo/Ro Center Mix Level", OFFSET(loro_center_mix_level), FF_OPT_TYPE_FLOAT, -1.0, -1.0, 2.0, AC3ENC_PARAM},
+{"loro_surmixlev", "Lo/Ro Surround Mix Level", OFFSET(loro_surround_mix_level), FF_OPT_TYPE_FLOAT, -1.0, -1.0, 2.0, AC3ENC_PARAM},
+{"dsurex_mode", "Dolby Surround EX Mode", OFFSET(dolby_surround_ex_mode), FF_OPT_TYPE_INT, -1, -1, 2, AC3ENC_PARAM, "dsurex_mode"},
+    {"notindicated", "Not Indicated (default)",       0, FF_OPT_TYPE_CONST, 0, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"},
+    {"on",           "Dolby Surround EX Encoded",     0, FF_OPT_TYPE_CONST, 1, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"},
+    {"off",          "Not Dolby Surround EX Encoded", 0, FF_OPT_TYPE_CONST, 2, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"},
+{"dheadphone_mode", "Dolby Headphone Mode", OFFSET(dolby_headphone_mode), FF_OPT_TYPE_INT, -1, -1, 2, AC3ENC_PARAM, "dheadphone_mode"},
+    {"notindicated", "Not Indicated (default)",     0, FF_OPT_TYPE_CONST, 0, INT_MIN, INT_MAX, AC3ENC_PARAM, "dheadphone_mode"},
+    {"on",           "Dolby Headphone Encoded",     0, FF_OPT_TYPE_CONST, 1, INT_MIN, INT_MAX, AC3ENC_PARAM, "dheadphone_mode"},
+    {"off",          "Not Dolby Headphone Encoded", 0, FF_OPT_TYPE_CONST, 2, INT_MIN, INT_MAX, AC3ENC_PARAM, "dheadphone_mode"},
+{"ad_conv_type", "A/D Converter Type", OFFSET(ad_converter_type), FF_OPT_TYPE_INT, -1, -1, 1, AC3ENC_PARAM, "ad_conv_type"},
+    {"standard", "Standard (default)", 0, FF_OPT_TYPE_CONST, 0, INT_MIN, INT_MAX, AC3ENC_PARAM, "ad_conv_type"},
+    {"hdcd",     "HDCD",               0, FF_OPT_TYPE_CONST, 1, INT_MIN, INT_MAX, AC3ENC_PARAM, "ad_conv_type"},
+{NULL}
+};
+
+#if CONFIG_AC3ENC_FLOAT
+static AVClass ac3enc_class = { "AC-3 Encoder", av_default_item_name,
+                                options, LIBAVUTIL_VERSION_INT };
+#else
+static AVClass ac3enc_class = { "Fixed-Point AC-3 Encoder", av_default_item_name,
+                                options, LIBAVUTIL_VERSION_INT };
+#endif
+
+
 /* prototypes for functions in ac3enc_fixed.c and ac3enc_float.c */
 
 static av_cold void mdct_end(AC3MDCTContext *mdct);
@@ -167,7 +281,7 @@ static av_cold int mdct_init(AVCodecContext *avctx, AC3MDCTContext *mdct,
 static void mdct512(AC3MDCTContext *mdct, CoefType *out, SampleType *in);
 
 static void apply_window(DSPContext *dsp, SampleType *output, const SampleType *input,
-                         const SampleType *window, int n);
+                         const SampleType *window, unsigned int len);
 
 static int normalize_samples(AC3EncodeContext *s);
 
@@ -226,7 +340,7 @@ static void adjust_frame_size(AC3EncodeContext *s)
 
 /**
  * Deinterleave input samples.
- * Channels are reordered from FFmpeg's default order to AC-3 order.
+ * Channels are reordered from Libav's default order to AC-3 order.
  */
 static void deinterleave_input_samples(AC3EncodeContext *s,
                                        const SampleType *samples)
@@ -269,7 +383,7 @@ static void apply_mdct(AC3EncodeContext *s)
 
             apply_window(&s->dsp, s->windowed_samples, input_samples, s->mdct.window, AC3_WINDOW_SIZE);
 
-            block->exp_shift[ch] = normalize_samples(s);
+            block->coeff_shift[ch] = normalize_samples(s);
 
             mdct512(&s->mdct, block->mdct_coef[ch], s->windowed_samples);
         }
@@ -328,10 +442,10 @@ static void compute_rematrixing_strategy(AC3EncodeContext *s)
                 CoefType rt = block->mdct_coef[1][i];
                 CoefType md = lt + rt;
                 CoefType sd = lt - rt;
-                sum[0] += lt * lt;
-                sum[1] += rt * rt;
-                sum[2] += md * md;
-                sum[3] += sd * sd;
+                MAC_COEF(sum[0], lt, lt);
+                MAC_COEF(sum[1], rt, rt);
+                MAC_COEF(sum[2], md, md);
+                MAC_COEF(sum[3], sd, sd);
             }
 
             /* compare sums to determine if rematrixing will be used for this band */
@@ -416,18 +530,18 @@ static void extract_exponents(AC3EncodeContext *s)
             AC3Block *block = &s->blocks[blk];
             uint8_t *exp   = block->exp[ch];
             int32_t *coef = block->fixed_coef[ch];
-            int exp_shift  = block->exp_shift[ch];
             for (i = 0; i < AC3_MAX_COEFS; i++) {
                 int e;
                 int v = abs(coef[i]);
                 if (v == 0)
                     e = 24;
                 else {
-                    e = 23 - av_log2(v) + exp_shift;
+                    e = 23 - av_log2(v);
                     if (e >= 24) {
                         e = 24;
                         coef[i] = 0;
                     }
+                    av_assert2(e >= 0);
                 }
                 exp[i] = e;
             }
@@ -650,16 +764,19 @@ static void group_exponents(AC3EncodeContext *s)
                 exp1   = p[0];
                 p     += group_size;
                 delta0 = exp1 - exp0 + 2;
+                av_assert2(delta0 >= 0 && delta0 <= 4);
 
                 exp0   = exp1;
                 exp1   = p[0];
                 p     += group_size;
                 delta1 = exp1 - exp0 + 2;
+                av_assert2(delta1 >= 0 && delta1 <= 4);
 
                 exp0   = exp1;
                 exp1   = p[0];
                 p     += group_size;
                 delta2 = exp1 - exp0 + 2;
+                av_assert2(delta2 >= 0 && delta2 <= 4);
 
                 block->grouped_exp[ch][i] = ((delta0 * 5 + delta1) * 5) + delta2;
             }
@@ -783,9 +900,19 @@ static void bit_alloc_init(AC3EncodeContext *s)
  */
 static void count_frame_bits(AC3EncodeContext *s)
 {
+    AC3EncOptions *opt = &s->options;
     int blk, ch;
     int frame_bits = 0;
 
+    if (opt->audio_production_info)
+        frame_bits += 7;
+    if (s->bitstream_id == 6) {
+        if (opt->extended_bsi_1)
+            frame_bits += 14;
+        if (opt->extended_bsi_2)
+            frame_bits += 14;
+    }
+
     for (blk = 0; blk < AC3_MAX_BLOCKS; blk++) {
         /* stereo rematrixing */
         if (s->channel_mode == AC3_CHMODE_STEREO &&
@@ -947,6 +1074,7 @@ static int cbr_bit_allocation(AC3EncodeContext *s)
     int snr_offset, snr_incr;
 
     bits_left = 8 * s->frame_size - (s->frame_bits + s->exponent_bits);
+    av_assert2(bits_left >= 0);
 
     snr_offset = s->coarse_snr_offset << 4;
 
@@ -1092,17 +1220,7 @@ static int compute_bit_allocation(AC3EncodeContext *s)
  */
 static inline int sym_quant(int c, int e, int levels)
 {
-    int v;
-
-    if (c >= 0) {
-        v = (levels * (c << e)) >> 24;
-        v = (v + 1) >> 1;
-        v = (levels >> 1) + v;
-    } else {
-        v = (levels * ((-c) << e)) >> 24;
-        v = (v + 1) >> 1;
-        v = (levels >> 1) - v;
-    }
+    int v = ((((levels * c) >> (24 - e)) + 1) >> 1) + (levels >> 1);
     av_assert2(v >= 0 && v < levels);
     return v;
 }
@@ -1134,7 +1252,7 @@ static inline int asym_quant(int c, int e, int qbits)
  * Quantize a set of mantissas for a single channel in a single block.
  */
 static void quantize_mantissas_blk_ch(AC3EncodeContext *s, int32_t *fixed_coef,
-                                      int8_t exp_shift, uint8_t *exp,
+                                      uint8_t *exp,
                                       uint8_t *bap, uint16_t *qmant, int n)
 {
     int i;
@@ -1142,7 +1260,7 @@ static void quantize_mantissas_blk_ch(AC3EncodeContext *s, int32_t *fixed_coef,
     for (i = 0; i < n; i++) {
         int v;
         int c = fixed_coef[i];
-        int e = exp[i] - exp_shift;
+        int e = exp[i];
         int b = bap[i];
         switch (b) {
         case 0:
@@ -1238,7 +1356,7 @@ static void quantize_mantissas(AC3EncodeContext *s)
         s->qmant1_ptr = s->qmant2_ptr = s->qmant4_ptr = NULL;
 
         for (ch = 0; ch < s->channels; ch++) {
-            quantize_mantissas_blk_ch(s, block->fixed_coef[ch], block->exp_shift[ch],
+            quantize_mantissas_blk_ch(s, block->fixed_coef[ch],
                                       block->exp[ch], block->bap[ch],
                                       block->qmant[ch], s->nb_coefs[ch]);
         }
@@ -1251,6 +1369,8 @@ static void quantize_mantissas(AC3EncodeContext *s)
  */
 static void output_frame_header(AC3EncodeContext *s)
 {
+    AC3EncOptions *opt = &s->options;
+
     put_bits(&s->pb, 16, 0x0b77);   /* frame header */
     put_bits(&s->pb, 16, 0);        /* crc1: will be filled later */
     put_bits(&s->pb, 2,  s->bit_alloc.sr_code);
@@ -1259,20 +1379,43 @@ static void output_frame_header(AC3EncodeContext *s)
     put_bits(&s->pb, 3,  s->bitstream_mode);
     put_bits(&s->pb, 3,  s->channel_mode);
     if ((s->channel_mode & 0x01) && s->channel_mode != AC3_CHMODE_MONO)
-        put_bits(&s->pb, 2, 1);     /* XXX -4.5 dB */
+        put_bits(&s->pb, 2, s->center_mix_level);
     if (s->channel_mode & 0x04)
-        put_bits(&s->pb, 2, 1);     /* XXX -6 dB */
+        put_bits(&s->pb, 2, s->surround_mix_level);
     if (s->channel_mode == AC3_CHMODE_STEREO)
-        put_bits(&s->pb, 2, 0);     /* surround not indicated */
+        put_bits(&s->pb, 2, opt->dolby_surround_mode);
     put_bits(&s->pb, 1, s->lfe_on); /* LFE */
-    put_bits(&s->pb, 5, 31);        /* dialog norm: -31 db */
+    put_bits(&s->pb, 5, -opt->dialogue_level);
     put_bits(&s->pb, 1, 0);         /* no compression control word */
     put_bits(&s->pb, 1, 0);         /* no lang code */
-    put_bits(&s->pb, 1, 0);         /* no audio production info */
-    put_bits(&s->pb, 1, 0);         /* no copyright */
-    put_bits(&s->pb, 1, 1);         /* original bitstream */
+    put_bits(&s->pb, 1, opt->audio_production_info);
+    if (opt->audio_production_info) {
+        put_bits(&s->pb, 5, opt->mixing_level - 80);
+        put_bits(&s->pb, 2, opt->room_type);
+    }
+    put_bits(&s->pb, 1, opt->copyright);
+    put_bits(&s->pb, 1, opt->original);
+    if (s->bitstream_id == 6) {
+        /* alternate bit stream syntax */
+        put_bits(&s->pb, 1, opt->extended_bsi_1);
+        if (opt->extended_bsi_1) {
+            put_bits(&s->pb, 2, opt->preferred_stereo_downmix);
+            put_bits(&s->pb, 3, s->ltrt_center_mix_level);
+            put_bits(&s->pb, 3, s->ltrt_surround_mix_level);
+            put_bits(&s->pb, 3, s->loro_center_mix_level);
+            put_bits(&s->pb, 3, s->loro_surround_mix_level);
+        }
+        put_bits(&s->pb, 1, opt->extended_bsi_2);
+        if (opt->extended_bsi_2) {
+            put_bits(&s->pb, 2, opt->dolby_surround_ex_mode);
+            put_bits(&s->pb, 2, opt->dolby_headphone_mode);
+            put_bits(&s->pb, 1, opt->ad_converter_type);
+            put_bits(&s->pb, 9, 0);     /* xbsi2 and encinfo : reserved */
+        }
+    } else {
     put_bits(&s->pb, 1, 0);         /* no time code 1 */
     put_bits(&s->pb, 1, 0);         /* no time code 2 */
+    }
     put_bits(&s->pb, 1, 0);         /* no additional bit stream info */
 }
 
@@ -1438,6 +1581,7 @@ static void output_frame_end(AC3EncodeContext *s)
     frame_size_58 = ((s->frame_size >> 2) + (s->frame_size >> 4)) << 1;
 
     /* pad the remainder of the frame with zeros */
+    av_assert2(s->frame_size * 8 - put_bits_count(&s->pb) >= 18);
     flush_put_bits(&s->pb);
     frame = s->pb.buf;
     pad_bytes = s->frame_size - (put_bits_ptr(&s->pb) - frame) - 2;
@@ -1484,6 +1628,268 @@ static void output_frame(AC3EncodeContext *s, unsigned char *frame)
 }
 
 
+static void dprint_options(AVCodecContext *avctx)
+{
+#ifdef DEBUG
+    AC3EncodeContext *s = avctx->priv_data;
+    AC3EncOptions *opt = &s->options;
+    char strbuf[32];
+
+    switch (s->bitstream_id) {
+    case  6:  strncpy(strbuf, "AC-3 (alt syntax)", 32);      break;
+    case  8:  strncpy(strbuf, "AC-3 (standard)", 32);        break;
+    case  9:  strncpy(strbuf, "AC-3 (dnet half-rate)", 32);  break;
+    case 10:  strncpy(strbuf, "AC-3 (dnet quater-rate", 32); break;
+    default: snprintf(strbuf, 32, "ERROR");
+    }
+    av_dlog(avctx, "bitstream_id: %s (%d)\n", strbuf, s->bitstream_id);
+    av_dlog(avctx, "sample_fmt: %s\n", av_get_sample_fmt_name(avctx->sample_fmt));
+    av_get_channel_layout_string(strbuf, 32, s->channels, avctx->channel_layout);
+    av_dlog(avctx, "channel_layout: %s\n", strbuf);
+    av_dlog(avctx, "sample_rate: %d\n", s->sample_rate);
+    av_dlog(avctx, "bit_rate: %d\n", s->bit_rate);
+    if (s->cutoff)
+        av_dlog(avctx, "cutoff: %d\n", s->cutoff);
+
+    av_dlog(avctx, "per_frame_metadata: %s\n",
+            opt->allow_per_frame_metadata?"on":"off");
+    if (s->has_center)
+        av_dlog(avctx, "center_mixlev: %0.3f (%d)\n", opt->center_mix_level,
+                s->center_mix_level);
+    else
+        av_dlog(avctx, "center_mixlev: {not written}\n");
+    if (s->has_surround)
+        av_dlog(avctx, "surround_mixlev: %0.3f (%d)\n", opt->surround_mix_level,
+                s->surround_mix_level);
+    else
+        av_dlog(avctx, "surround_mixlev: {not written}\n");
+    if (opt->audio_production_info) {
+        av_dlog(avctx, "mixing_level: %ddB\n", opt->mixing_level);
+        switch (opt->room_type) {
+        case 0:  strncpy(strbuf, "notindicated", 32); break;
+        case 1:  strncpy(strbuf, "large", 32);        break;
+        case 2:  strncpy(strbuf, "small", 32);        break;
+        default: snprintf(strbuf, 32, "ERROR (%d)", opt->room_type);
+        }
+        av_dlog(avctx, "room_type: %s\n", strbuf);
+    } else {
+        av_dlog(avctx, "mixing_level: {not written}\n");
+        av_dlog(avctx, "room_type: {not written}\n");
+    }
+    av_dlog(avctx, "copyright: %s\n", opt->copyright?"on":"off");
+    av_dlog(avctx, "dialnorm: %ddB\n", opt->dialogue_level);
+    if (s->channel_mode == AC3_CHMODE_STEREO) {
+        switch (opt->dolby_surround_mode) {
+        case 0:  strncpy(strbuf, "notindicated", 32); break;
+        case 1:  strncpy(strbuf, "on", 32);           break;
+        case 2:  strncpy(strbuf, "off", 32);          break;
+        default: snprintf(strbuf, 32, "ERROR (%d)", opt->dolby_surround_mode);
+        }
+        av_dlog(avctx, "dsur_mode: %s\n", strbuf);
+    } else {
+        av_dlog(avctx, "dsur_mode: {not written}\n");
+    }
+    av_dlog(avctx, "original: %s\n", opt->original?"on":"off");
+
+    if (s->bitstream_id == 6) {
+        if (opt->extended_bsi_1) {
+            switch (opt->preferred_stereo_downmix) {
+            case 0:  strncpy(strbuf, "notindicated", 32); break;
+            case 1:  strncpy(strbuf, "ltrt", 32);         break;
+            case 2:  strncpy(strbuf, "loro", 32);         break;
+            default: snprintf(strbuf, 32, "ERROR (%d)", opt->preferred_stereo_downmix);
+            }
+            av_dlog(avctx, "dmix_mode: %s\n", strbuf);
+            av_dlog(avctx, "ltrt_cmixlev: %0.3f (%d)\n",
+                    opt->ltrt_center_mix_level, s->ltrt_center_mix_level);
+            av_dlog(avctx, "ltrt_surmixlev: %0.3f (%d)\n",
+                    opt->ltrt_surround_mix_level, s->ltrt_surround_mix_level);
+            av_dlog(avctx, "loro_cmixlev: %0.3f (%d)\n",
+                    opt->loro_center_mix_level, s->loro_center_mix_level);
+            av_dlog(avctx, "loro_surmixlev: %0.3f (%d)\n",
+                    opt->loro_surround_mix_level, s->loro_surround_mix_level);
+        } else {
+            av_dlog(avctx, "extended bitstream info 1: {not written}\n");
+        }
+        if (opt->extended_bsi_2) {
+            switch (opt->dolby_surround_ex_mode) {
+            case 0:  strncpy(strbuf, "notindicated", 32); break;
+            case 1:  strncpy(strbuf, "on", 32);           break;
+            case 2:  strncpy(strbuf, "off", 32);          break;
+            default: snprintf(strbuf, 32, "ERROR (%d)", opt->dolby_surround_ex_mode);
+            }
+            av_dlog(avctx, "dsurex_mode: %s\n", strbuf);
+            switch (opt->dolby_headphone_mode) {
+            case 0:  strncpy(strbuf, "notindicated", 32); break;
+            case 1:  strncpy(strbuf, "on", 32);           break;
+            case 2:  strncpy(strbuf, "off", 32);          break;
+            default: snprintf(strbuf, 32, "ERROR (%d)", opt->dolby_headphone_mode);
+            }
+            av_dlog(avctx, "dheadphone_mode: %s\n", strbuf);
+
+            switch (opt->ad_converter_type) {
+            case 0:  strncpy(strbuf, "standard", 32); break;
+            case 1:  strncpy(strbuf, "hdcd", 32);     break;
+            default: snprintf(strbuf, 32, "ERROR (%d)", opt->ad_converter_type);
+            }
+            av_dlog(avctx, "ad_conv_type: %s\n", strbuf);
+        } else {
+            av_dlog(avctx, "extended bitstream info 2: {not written}\n");
+        }
+    }
+#endif
+}
+
+
+#define FLT_OPTION_THRESHOLD 0.01
+
+static int validate_float_option(float v, const float *v_list, int v_list_size)
+{
+    int i;
+
+    for (i = 0; i < v_list_size; i++) {
+        if (v < (v_list[i] + FLT_OPTION_THRESHOLD) &&
+            v > (v_list[i] - FLT_OPTION_THRESHOLD))
+            break;
+    }
+    if (i == v_list_size)
+        return -1;
+
+    return i;
+}
+
+
+static void validate_mix_level(void *log_ctx, const char *opt_name,
+                               float *opt_param, const float *list,
+                               int list_size, int default_value, int min_value,
+                               int *ctx_param)
+{
+    int mixlev = validate_float_option(*opt_param, list, list_size);
+    if (mixlev < min_value) {
+        mixlev = default_value;
+        if (*opt_param >= 0.0) {
+            av_log(log_ctx, AV_LOG_WARNING, "requested %s is not valid. using "
+                   "default value: %0.3f\n", opt_name, list[mixlev]);
+        }
+    }
+    *opt_param = list[mixlev];
+    *ctx_param = mixlev;
+}
+
+
+/**
+ * Validate metadata options as set by AVOption system.
+ * These values can optionally be changed per-frame.
+ */
+static int validate_metadata(AVCodecContext *avctx)
+{
+    AC3EncodeContext *s = avctx->priv_data;
+    AC3EncOptions *opt = &s->options;
+
+    /* validate mixing levels */
+    if (s->has_center) {
+        validate_mix_level(avctx, "center_mix_level", &opt->center_mix_level,
+                           cmixlev_options, CMIXLEV_NUM_OPTIONS, 1, 0,
+                           &s->center_mix_level);
+    }
+    if (s->has_surround) {
+        validate_mix_level(avctx, "surround_mix_level", &opt->surround_mix_level,
+                           surmixlev_options, SURMIXLEV_NUM_OPTIONS, 1, 0,
+                           &s->surround_mix_level);
+    }
+
+    /* set audio production info flag */
+    if (opt->mixing_level >= 0 || opt->room_type >= 0) {
+        if (opt->mixing_level < 0) {
+            av_log(avctx, AV_LOG_ERROR, "mixing_level must be set if "
+                   "room_type is set\n");
+            return AVERROR(EINVAL);
+        }
+        if (opt->mixing_level < 80) {
+            av_log(avctx, AV_LOG_ERROR, "invalid mixing level. must be between "
+                   "80dB and 111dB\n");
+            return AVERROR(EINVAL);
+        }
+        /* default room type */
+        if (opt->room_type < 0)
+            opt->room_type = 0;
+        opt->audio_production_info = 1;
+    } else {
+        opt->audio_production_info = 0;
+    }
+
+    /* set extended bsi 1 flag */
+    if ((s->has_center || s->has_surround) &&
+        (opt->preferred_stereo_downmix >= 0 ||
+         opt->ltrt_center_mix_level   >= 0 ||
+         opt->ltrt_surround_mix_level >= 0 ||
+         opt->loro_center_mix_level   >= 0 ||
+         opt->loro_surround_mix_level >= 0)) {
+        /* default preferred stereo downmix */
+        if (opt->preferred_stereo_downmix < 0)
+            opt->preferred_stereo_downmix = 0;
+        /* validate Lt/Rt center mix level */
+        validate_mix_level(avctx, "ltrt_center_mix_level",
+                           &opt->ltrt_center_mix_level, extmixlev_options,
+                           EXTMIXLEV_NUM_OPTIONS, 5, 0,
+                           &s->ltrt_center_mix_level);
+        /* validate Lt/Rt surround mix level */
+        validate_mix_level(avctx, "ltrt_surround_mix_level",
+                           &opt->ltrt_surround_mix_level, extmixlev_options,
+                           EXTMIXLEV_NUM_OPTIONS, 6, 3,
+                           &s->ltrt_surround_mix_level);
+        /* validate Lo/Ro center mix level */
+        validate_mix_level(avctx, "loro_center_mix_level",
+                           &opt->loro_center_mix_level, extmixlev_options,
+                           EXTMIXLEV_NUM_OPTIONS, 5, 0,
+                           &s->loro_center_mix_level);
+        /* validate Lo/Ro surround mix level */
+        validate_mix_level(avctx, "loro_surround_mix_level",
+                           &opt->loro_surround_mix_level, extmixlev_options,
+                           EXTMIXLEV_NUM_OPTIONS, 6, 3,
+                           &s->loro_surround_mix_level);
+        opt->extended_bsi_1 = 1;
+    } else {
+        opt->extended_bsi_1 = 0;
+    }
+
+    /* set extended bsi 2 flag */
+    if (opt->dolby_surround_ex_mode >= 0 ||
+        opt->dolby_headphone_mode   >= 0 ||
+        opt->ad_converter_type      >= 0) {
+        /* default dolby surround ex mode */
+        if (opt->dolby_surround_ex_mode < 0)
+            opt->dolby_surround_ex_mode = 0;
+        /* default dolby headphone mode */
+        if (opt->dolby_headphone_mode < 0)
+            opt->dolby_headphone_mode = 0;
+        /* default A/D converter type */
+        if (opt->ad_converter_type < 0)
+            opt->ad_converter_type = 0;
+        opt->extended_bsi_2 = 1;
+    } else {
+        opt->extended_bsi_2 = 0;
+    }
+
+    /* set bitstream id for alternate bitstream syntax */
+    if (opt->extended_bsi_1 || opt->extended_bsi_2) {
+        if (s->bitstream_id > 8 && s->bitstream_id < 11) {
+            static int warn_once = 1;
+            if (warn_once) {
+                av_log(avctx, AV_LOG_WARNING, "alternate bitstream syntax is "
+                       "not compatible with reduced samplerates. writing of "
+                       "extended bitstream information will be disabled.\n");
+                warn_once = 0;
+            }
+        } else {
+            s->bitstream_id = 6;
+        }
+    }
+
+    return 0;
+}
+
+
 /**
  * Encode a single AC-3 frame.
  */
@@ -1494,6 +1900,12 @@ static int ac3_encode_frame(AVCodecContext *avctx, unsigned char *frame,
     const SampleType *samples = data;
     int ret;
 
+    if (s->options.allow_per_frame_metadata) {
+        ret = validate_metadata(avctx);
+        if (ret)
+            return ret;
+    }
+
     if (s->bit_alloc.sr_code == 1)
         adjust_frame_size(s);
 
@@ -1501,10 +1913,10 @@ static int ac3_encode_frame(AVCodecContext *avctx, unsigned char *frame,
 
     apply_mdct(s);
 
-    compute_rematrixing_strategy(s);
-
     scale_coefficients(s);
 
+    compute_rematrixing_strategy(s);
+
     apply_rematrixing(s);
 
     process_exponents(s);
@@ -1602,6 +2014,8 @@ static av_cold int set_channel_info(AC3EncodeContext *s, int channels,
     default:
         return AVERROR(EINVAL);
     }
+    s->has_center   = (s->channel_mode & 0x01) && s->channel_mode != AC3_CHMODE_MONO;
+    s->has_surround =  s->channel_mode & 0x04;
 
     s->channel_map  = ff_ac3_enc_channel_map[s->channel_mode][s->lfe_on];
     *channel_layout = ch_layout;
@@ -1640,6 +2054,7 @@ static av_cold int validate_options(AVCodecContext *avctx, AC3EncodeContext *s)
     s->sample_rate        = avctx->sample_rate;
     s->bit_alloc.sr_shift = i % 3;
     s->bit_alloc.sr_code  = i / 3;
+    s->bitstream_id       = 8 + s->bit_alloc.sr_shift;
 
     /* validate bit rate */
     for (i = 0; i < 19; i++) {
@@ -1662,6 +2077,22 @@ static av_cold int validate_options(AVCodecContext *avctx, AC3EncodeContext *s)
     if (s->cutoff > (s->sample_rate >> 1))
         s->cutoff = s->sample_rate >> 1;
 
+    /* validate audio service type / channels combination */
+    if ((avctx->audio_service_type == AV_AUDIO_SERVICE_TYPE_KARAOKE &&
+         avctx->channels == 1) ||
+        ((avctx->audio_service_type == AV_AUDIO_SERVICE_TYPE_COMMENTARY ||
+          avctx->audio_service_type == AV_AUDIO_SERVICE_TYPE_EMERGENCY  ||
+          avctx->audio_service_type == AV_AUDIO_SERVICE_TYPE_VOICE_OVER)
+         && avctx->channels > 1)) {
+        av_log(avctx, AV_LOG_ERROR, "invalid audio service type for the "
+                                    "specified number of channels\n");
+        return AVERROR(EINVAL);
+    }
+
+    ret = validate_metadata(avctx);
+    if (ret)
+        return ret;
+
     return 0;
 }
 
@@ -1803,8 +2234,9 @@ static av_cold int ac3_encode_init(AVCodecContext *avctx)
     if (ret)
         return ret;
 
-    s->bitstream_id   = 8 + s->bit_alloc.sr_shift;
-    s->bitstream_mode = 0; /* complete main audio service */
+    s->bitstream_mode = avctx->audio_service_type;
+    if (s->bitstream_mode == AV_AUDIO_SERVICE_TYPE_KARAOKE)
+        s->bitstream_mode = 0x7;
 
     s->frame_size_min  = 2 * ff_ac3_frame_size_tab[s->frame_size_code][s->bit_alloc.sr_code];
     s->bits_written    = 0;
@@ -1838,7 +2270,9 @@ static av_cold int ac3_encode_init(AVCodecContext *avctx)
     avctx->coded_frame= avcodec_alloc_frame();
 
     dsputil_init(&s->dsp, avctx);
-    ff_ac3dsp_init(&s->ac3dsp);
+    ff_ac3dsp_init(&s->ac3dsp, avctx->flags & CODEC_FLAG_BITEXACT);
+
+    dprint_options(avctx);
 
     return 0;
 init_fail: