[FFmpeg-devel] [PATCH] Add Lame info tag to the Xing/Info header of mp3 files

Giovanni Motta giovanni.motta at gmail.com
Fri May 23 02:31:09 CEST 2014


Add Lame info tag (with delay, padding, replay gain, signal peak, etc..)
to the Xing/Info header of mp3 files.

A few notes/comments:

- Fate initially failed for lavf.mp3; this is expected.
  The test result has been updated with new CRC.

- The Lame info tag is generated by libmp3lame and passed to the mp3enc via extradata.

- To keep the size of the tag constant and simplify the code, vbr_scale is always added.

- The Lame string vendor in the tag is fixed length, so vendor is trimmed
  to 9 bytes and padded with 0x20 if shorter.
  Note: the current vendor string in ffmpeg is too long. The lame tags
  currently generated doesn't work and the delay added after the string is
  never parsed correctly.

- replay_gain and find_peak need a version of lame patched with
  libmp3lame/lame.c Revision 1.367 (patch tracker item #66): http://sourceforge.net/p/lame/patches/66/
  They have no effect otherwise.

- find_peak_sample only works if Lame is configured with --enable-decoder.
  It has no effect otherwise.

- Some fields in the Lame tag are not set because not accessible from
  the set/get API (preset and noise shaping, for example). I will bring this to
  the attention of the Lame developers and help there with any change if we
  decide to merge the patch.

Thanks

Giovanni



---
 libavcodec/libmp3lame.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++-
 libavformat/mp3enc.c    | 112 ++++++++++++++++-----
 tests/ref/lavf-fate/mp3 |   2 +-
 3 files changed, 344 insertions(+), 26 deletions(-)

diff --git a/libavcodec/libmp3lame.c b/libavcodec/libmp3lame.c
index a8c39cc..8f8b5ee 100644
--- a/libavcodec/libmp3lame.c
+++ b/libavcodec/libmp3lame.c
@@ -26,6 +26,7 @@

 #include <lame/lame.h>

+#include "libavutil/avstring.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
 #include "libavutil/float_dsp.h"
@@ -40,6 +41,9 @@

 #define BUFFER_SIZE (7200 + 2 * MPA_FRAME_SIZE + MPA_FRAME_SIZE / 4+1000) // FIXME: Buffer size to small? Adding 1000 to make up for it.

+/* size of the Lame Info tag added to the Xing/Info header */
+#define LAME_EXT_SIZE 40
+
 typedef struct LAMEContext {
     AVClass *class;
     AVCodecContext *avctx;
@@ -48,6 +52,8 @@ typedef struct LAMEContext {
     int buffer_index;
     int buffer_size;
     int reservoir;
+    int find_peak_sample;
+    int replay_gain;
     int joint_stereo;
     int abr;
     float *samples_flt[2];
@@ -93,6 +99,10 @@ static av_cold int mp3lame_encode_init(AVCodecContext *avctx)

     s->avctx = avctx;

+    /* extradata is used to store the Lame Info tag */
+    avctx->extradata = NULL;
+    avctx->extradata_size = 0;
+
     /* initialize LAME and get defaults */
     if ((s->gfp = lame_init()) == NULL)
         return AVERROR(ENOMEM);
@@ -131,6 +141,12 @@ static av_cold int mp3lame_encode_init(AVCodecContext *avctx)
     /* bit reservoir usage */
     lame_set_disable_reservoir(s->gfp, !s->reservoir);

+    /* find peak sample */
+    lame_set_decode_on_the_fly(s->gfp, s->find_peak_sample);
+
+    /* compute replay gain */
+    lame_set_findReplayGain(s->gfp, s->replay_gain);
+
     /* set specified parameters */
     if (lame_init_params(s->gfp) < 0) {
         ret = -1;
@@ -176,6 +192,226 @@ error:
                        s->buffer_size - s->buffer_index);                   \
 } while (0)

+/* flow and comments adapted from PutLameVBR() in Lame's VbrTag.c */
+static int GetLameInfoTag(lame_global_flags const *gfp, uint8_t * extradata)
+{
+    int nBytesWritten = 0;
+
+    int enc_delay = lame_get_encoder_delay(gfp);      // encoder delay
+    int enc_padding = lame_get_encoder_padding(gfp);  // encoder padding
+
+    /* recall: gfp->VBR_q is for example set by the switch -V */
+    /*         gfp->quality by -q, -h, -f, etc.               */
+    int nQuality = (100 - 10 * lame_get_VBR_q(gfp) - lame_get_quality(gfp));
+
+    const char *szVersion = get_lame_very_short_version();
+    uint8_t nVBR;
+    uint8_t nRevision = 0x00;
+    uint8_t nRevMethod;
+
+    /* numbering is different in vbr_mode vs. Lame tag */
+    uint8_t vbr_type_translator[] = { 1, 5, 3, 2, 4, 0, 3 };
+
+    uint8_t nLowpass =
+        (((lame_get_lowpassfreq(gfp) / 100.0) + .5) > 255 ?
+                255 : (lame_get_lowpassfreq(gfp) / 100.0) + .5);
+
+    uint32_t nPeakSignalAmplitude = 0;
+    uint16_t nRadioReplayGain = 0;
+    uint16_t nAudiophileReplayGain = 0;
+
+    uint8_t nNoiseShaping = 0;
+    uint8_t nStereoMode = 0;
+    int bNonOptimal = 0;
+    uint8_t nSourceFreq = 0;
+
+    uint8_t nMisc = 0;
+
+    size_t  nMusicLength = 0;
+    uint16_t nMusicCRC = 0;
+    uint16_t nTagCRC = 0;
+
+    /* psy model type: Gpsycho or NsPsytune */
+    unsigned char bExpNPsyTune = lame_get_exp_nspsytune(gfp) & 1;
+    unsigned char bSafeJoint = (lame_get_exp_nspsytune(gfp) & 2) != 0;
+
+    unsigned char bNoGapMore = 0;
+    unsigned char bNoGapPrevious = 0;
+
+    int nNoGapCount = lame_get_nogap_total(gfp);
+    int nNoGapCurr = lame_get_nogap_currentindex(gfp);
+
+    uint8_t nAthType = lame_get_ATHtype(gfp);  /* 4 bits */
+    int nInSampleRate = lame_get_in_samplerate(gfp);
+    uint8_t nFlags = 0;
+
+    /* if ABR, {store bitrate <= 255} else {store "-b"} */
+    int nABRBitrate;
+    switch (lame_get_VBR(gfp)) {
+    case vbr_abr:
+        nABRBitrate = lame_get_VBR_mean_bitrate_kbps(gfp);
+        break;
+    case vbr_off:
+        nABRBitrate = lame_get_brate(gfp);
+        break;
+    default:          // vbr modes
+        nABRBitrate = lame_get_VBR_mean_bitrate_kbps(gfp);
+    }
+
+    /* revision and vbr method */
+    if (lame_get_VBR(gfp) < sizeof(vbr_type_translator))
+        nVBR = vbr_type_translator[lame_get_VBR(gfp)];
+    else
+        nVBR = 0x00;    // unknown
+    nRevMethod = 0x10 * nRevision + nVBR;
+
+
+    /* ReplayGain */
+    if (lame_get_findReplayGain(gfp)) {
+        int nRadioGain = lame_get_RadioGain(gfp);
+        if (nRadioGain > 0x1FE)
+            nRadioGain = 0x1FE;
+        if (nRadioGain < -0x1FE)
+            nRadioGain = -0x1FE;
+
+        nRadioReplayGain = 0x2000;  // set name code
+        nRadioReplayGain |= 0xC00;  // set originator code to `determined automatically'
+
+        if (nRadioGain >= 0) {
+            nRadioReplayGain |= nRadioGain;  // set gain adjustment
+        } else {
+            nRadioReplayGain |= 0x200;       // set the sign bit
+            nRadioReplayGain |= -nRadioGain; // set gain adjustment
+        }
+    }
+
+    /* peak sample */
+    if (lame_get_decode_on_the_fly(gfp))
+        nPeakSignalAmplitude = abs((int) ((lame_get_PeakSample(gfp) / 32767.0) * pow(2, 23) + .5));
+
+    /* nogap */
+    if (nNoGapCount != -1) {
+        if (nNoGapCurr > 0)
+            bNoGapPrevious = 1;
+
+        if (nNoGapCurr < nNoGapCount - 1)
+            bNoGapMore = 1;
+    }
+
+    /* flags */
+    nFlags = nAthType + (bExpNPsyTune << 4)
+        + (bSafeJoint << 5)
+        + (bNoGapMore << 6)
+        + (bNoGapPrevious << 7);
+
+    if (nQuality < 0)
+        nQuality = 0;
+
+    /* stereo mode field... a bit ugly */
+    switch (lame_get_mode(gfp)) {
+    case MONO:
+        nStereoMode = 0;
+        break;
+    case STEREO:
+        nStereoMode = 1;
+        break;
+    case DUAL_CHANNEL:
+        nStereoMode = 2;
+        break;
+    case JOINT_STEREO:
+        if (lame_get_force_ms(gfp))
+            nStereoMode = 4;
+        else
+            nStereoMode = 3;
+        break;
+    case NOT_SET:
+        // FALLTHROUGH
+    default:
+        nStereoMode = 7;
+        break;
+    }
+
+    /* intensity stereo : nStereoMode = 6. IS is not implemented */
+    if (nInSampleRate <= 32000)
+        nSourceFreq = 0x00;
+    else if (nInSampleRate == 48000)
+        nSourceFreq = 0x02;
+    else if (nInSampleRate > 48000)
+        nSourceFreq = 0x03;
+    else
+        nSourceFreq = 0x01;   // default is 44100Hz
+
+    /* check if the user overrided the default LAME behaviour with some nasty options */
+    if ((lame_get_no_short_blocks(gfp) == 1) ||     // short blocks dispensed
+        (lame_get_force_short_blocks(gfp) == 1) ||  // short blocks forced
+        ((lame_get_lowpassfreq(gfp) == -1) && (lame_get_highpassfreq(gfp) == -1)) ||    // "-k"
+        (lame_get_scale_left(gfp) < lame_get_scale_right(gfp)) ||
+        (lame_get_scale_left(gfp) > lame_get_scale_right(gfp)) ||
+        (lame_get_disable_reservoir(gfp) && lame_get_brate(gfp) < 320) ||
+        lame_get_noATH(gfp) || lame_get_ATHonly(gfp) || (nAthType == 0) ||
+        (nInSampleRate <= 32000))
+        bNonOptimal = 1;
+
+    nMisc = nNoiseShaping + (nStereoMode << 2)
+        + (bNonOptimal << 5)
+        + (nSourceFreq << 6);
+
+    /* write all this information to extradata */
+    AV_WB32(&extradata[nBytesWritten], nQuality);
+    nBytesWritten += 4;
+
+    av_strlcpy((char *) &extradata[nBytesWritten], szVersion, 9);
+    nBytesWritten += 9;
+
+    extradata[nBytesWritten] = nRevMethod;
+    nBytesWritten++;
+
+    extradata[nBytesWritten] = nLowpass;
+    nBytesWritten++;
+
+    AV_WB32(&extradata[nBytesWritten], nPeakSignalAmplitude);
+    nBytesWritten += 4;
+
+    AV_WB16(&extradata[nBytesWritten], nRadioReplayGain);
+    nBytesWritten += 2;
+
+    AV_WB16(&extradata[nBytesWritten], nAudiophileReplayGain);
+    nBytesWritten += 2;
+
+    extradata[nBytesWritten] = nFlags;
+    nBytesWritten++;
+
+    if (nABRBitrate >= 255)
+        extradata[nBytesWritten] = 0xFF;
+    else
+        extradata[nBytesWritten] = nABRBitrate;
+    nBytesWritten++;
+
+    extradata[nBytesWritten] = enc_delay >> 4;
+    extradata[nBytesWritten + 1] = (enc_delay << 4) + (enc_padding >> 8);
+    extradata[nBytesWritten + 2] = enc_padding;
+    nBytesWritten += 3;
+
+    extradata[nBytesWritten] = nMisc;
+    nBytesWritten++;
+
+    extradata[nBytesWritten++] = 0;   // unused in rev0
+
+    AV_WB16(&extradata[nBytesWritten], 0);
+    nBytesWritten += 2;
+
+    AV_WB32(&extradata[nBytesWritten], (int) nMusicLength);
+    nBytesWritten += 4;
+
+    AV_WB16(&extradata[nBytesWritten], nMusicCRC);
+    nBytesWritten += 2;
+
+    AV_WB16(&extradata[nBytesWritten], nTagCRC);
+    nBytesWritten += 2;
+
+    return nBytesWritten;
+}
+
 static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
                                 const AVFrame *frame, int *got_packet_ptr)
 {
@@ -213,6 +449,18 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
     } else {
         lame_result = lame_encode_flush(s->gfp, s->buffer + s->buffer_index,
                                         s->buffer_size - s->buffer_index);
+        /* get Lame Info tag and store it in extradata */
+        if(avctx->extradata == NULL) {
+            avctx->extradata = av_malloc(LAME_EXT_SIZE);
+            avctx->extradata_size = GetLameInfoTag(s->gfp, avctx->extradata);
+            if (avctx->extradata_size != LAME_EXT_SIZE) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "error storing Lame info tag (%d bytes written instead of %d)\n",
+                       avctx->extradata_size, LAME_EXT_SIZE);
+            } else {
+                av_log(avctx, AV_LOG_DEBUG, "lame info tag succesfully stored\n");
+            }
+        }
     }
     if (lame_result < 0) {
         if (lame_result == -1) {
@@ -267,9 +515,11 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
 #define OFFSET(x) offsetof(LAMEContext, x)
 #define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
-    { "reservoir",    "use bit reservoir", OFFSET(reservoir),    AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AE },
-    { "joint_stereo", "use joint stereo",  OFFSET(joint_stereo), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AE },
-    { "abr",          "use ABR",           OFFSET(abr),          AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AE },
+    { "reservoir",        "use bit reservoir",   OFFSET(reservoir),        AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AE },
+    { "find_peak_sample", "find peak sample",    OFFSET(find_peak_sample), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AE },
+    { "replay_gain",      "compute replay gain", OFFSET(replay_gain),      AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AE },
+    { "joint_stereo",     "use joint stereo",    OFFSET(joint_stereo),     AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, AE },
+    { "abr",              "use ABR",             OFFSET(abr),              AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AE },
     { NULL },
 };

diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c
index 43f7656..fbb3303 100644
--- a/libavformat/mp3enc.c
+++ b/libavformat/mp3enc.c
@@ -77,8 +77,13 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf)

 #define XING_NUM_BAGS 400
 #define XING_TOC_SIZE 100
-// maximum size of the xing frame: offset/Xing/flags/frames/size/TOC
-#define XING_MAX_SIZE (32 + 4 + 4 + 4 + 4 + XING_TOC_SIZE)
+// maximum size of the xing frame: offset/Xing/flags/frames/size/TOC/vbr_scale
+#define XING_MAX_SIZE (32 + 4 + 4 + 4 + 4 + 4 + XING_TOC_SIZE)
+
+#define XING_FLAG_FRAMES    0x01
+#define XING_FLAG_SIZE      0x02
+#define XING_FLAG_TOC       0x04
+#define XING_FLAG_VBR_SCALE 0x08

 typedef struct MP3Context {
     const AVClass *class;
@@ -109,6 +114,15 @@ typedef struct MP3Context {

 static const uint8_t xing_offtbl[2][2] = {{32, 17}, {17, 9}};

+/* Bitrates used to determine the size of a Xing header (MPEG1 and MPEG2) in kbps */
+#define XING_BITRATE1 128
+#define XING_BITRATE2  64
+#define XING_BITRATE25 32
+
+/* Size in bytes of the Lame Info tag added to the Xing/Info header */
+#define LAME_EXT_SIZE 40
+#define LAME_EXT_VENDOR_LEN 9
+
 /*
  * Write an empty XING header and initialize respective data.
  */
@@ -125,22 +139,46 @@ static int mp3_write_xing(AVFormatContext *s)
     int xing_offset;
     int ver = 0;
     int bytes_needed;
-    const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT;
+    int kbps_header;

     if (!s->pb->seekable || !mp3->write_xing)
         return 0;

+    /*
+     * Xing VBR pretends to be a 48kbs layer III frame.  (at 44.1kHz).
+     * (at 48kHz they use 56kbs since 48kbs frame not big enough for
+     * table of contents)
+     * let's always embed Xing header inside a 64kbs layer III frame.
+     * this gives us enough room for a LAME version string too.
+     * size determined by sampling frequency (MPEG1)
+     * 32kHz:    216 bytes@ 48kbs    288 bytes@ 64kbs
+     * 44.1kHz:  156 bytes@ 48kbs    208 bytes@ 64kbs     (+1 if padding = 1)
+     * 48kHz:    144 bytes@ 48kbs    192 bytes@ 64kbs
+     *
+     * MPEG 2 values are the same since the framesize and samplerate
+     * are each reduced by a factor of 2.
+     */
     for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) {
         const uint16_t base_freq = avpriv_mpa_freq_tab[i];
-
-        if      (codec->sample_rate == base_freq)     ver = 0x3; // MPEG 1
-        else if (codec->sample_rate == base_freq / 2) ver = 0x2; // MPEG 2
-        else if (codec->sample_rate == base_freq / 4) ver = 0x0; // MPEG 2.5
-        else continue;
-
+        if (codec->sample_rate == base_freq) {
+            ver = 0x3; // MPEG 1
+            kbps_header = XING_BITRATE1;
+        } else if (codec->sample_rate == base_freq / 2) {
+            ver = 0x2; // MPEG 2
+            kbps_header = XING_BITRATE2;
+        } else if (codec->sample_rate == base_freq / 4) {
+            ver = 0x0; // MPEG 2.5
+            kbps_header = XING_BITRATE25;
+        } else {
+          continue;
+        }
         srate_idx = i;
         break;
     }
+
+    if (!mp3->has_variable_bitrate)
+        kbps_header = codec->bit_rate;
+
     if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) {
         av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing header.\n");
         return -1;
@@ -162,7 +200,7 @@ static int mp3_write_xing(AVFormatContext *s)

     for (bitrate_idx = 1; bitrate_idx < 15; bitrate_idx++) {
         int bit_rate = 1000 * avpriv_mpa_bitrate_tab[ver != 3][3 - 1][bitrate_idx];
-        int error    = FFABS(bit_rate - codec->bit_rate);
+        int error    = FFABS(bit_rate - kbps_header);

         if (error < best_bitrate_error) {
             best_bitrate_error = error;
@@ -179,14 +217,15 @@ static int mp3_write_xing(AVFormatContext *s)

         avpriv_mpegaudio_decode_header(&mpah, header);
         xing_offset=xing_offtbl[mpah.lsf == 1][mpah.nb_channels == 1];
-        bytes_needed = 4              // header
+        bytes_needed = 4        // header
                + xing_offset
                + 4              // xing tag
-               + 4              // frames/size/toc flags
+               + 4              // frames/size/toc/vbr_scale flags
                + 4              // frames
                + 4              // size
-               + XING_TOC_SIZE   // toc
-               + 24
+               + XING_TOC_SIZE  // toc
+               + 4              // vbr_scale
+               + LAME_EXT_SIZE  // lame extension
                ;

         if (bytes_needed <= mpah.frame_size)
@@ -200,7 +239,7 @@ static int mp3_write_xing(AVFormatContext *s)
     ffio_fill(s->pb, 0, xing_offset);
     mp3->xing_offset = avio_tell(s->pb);
     ffio_wfourcc(s->pb, "Xing");
-    avio_wb32(s->pb, 0x01 | 0x02 | 0x04);  // frames / size / TOC
+    avio_wb32(s->pb, XING_FLAG_FRAMES | XING_FLAG_SIZE | XING_FLAG_TOC | XING_FLAG_VBR_SCALE);  // frames / size / TOC / vbr_scale

     mp3->size = mpah.frame_size;
     mp3->want=1;
@@ -214,11 +253,10 @@ static int mp3_write_xing(AVFormatContext *s)
     for (i = 0; i < XING_TOC_SIZE; ++i)
         avio_w8(s->pb, (uint8_t)(255 * i / XING_TOC_SIZE));

-    for (i = 0; i < strlen(vendor); ++i)
-        avio_w8(s->pb, vendor[i]);
-    for (; i < 21; ++i)
+    avio_wb32(s->pb, 0);  // vbr_scale
+
+    for (i = 0; i < LAME_EXT_SIZE; ++i)
         avio_w8(s->pb, 0);
-    avio_wb24(s->pb, FFMAX(codec->delay - 528 - 1, 0)<<12);

     ffio_fill(s->pb, 0, mpah.frame_size - bytes_needed);

@@ -324,6 +362,8 @@ static int mp3_queue_flush(AVFormatContext *s)
 static void mp3_update_xing(AVFormatContext *s)
 {
     MP3Context  *mp3 = s->priv_data;
+    AVCodecContext   *codec = s->streams[mp3->audio_stream_idx]->codec;
+    const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT;
     int i;

     /* replace "Xing" identification string with "Info" for CBR files. */
@@ -344,6 +384,34 @@ static void mp3_update_xing(AVFormatContext *s)
         avio_w8(s->pb, FFMIN(seek_point, 255));
     }

+    /* add Lame Info tag. */
+    if (codec->extradata_size == LAME_EXT_SIZE) {
+      int64_t lame_tag_offset = mp3->xing_offset
+              + 4     // "Xing" or "Info"
+              + 4     // frames/size/toc/vbr_scale flags
+              + 4     // frame count
+              + 4     // stream size
+              + 100;  // toc
+
+      /* extradata starts with vbr_scale */
+      avio_seek(s->pb, lame_tag_offset, SEEK_SET);
+
+			/* copy the tag from extradata */
+      avio_write(s->pb, codec->extradata, codec->extradata_size);
+
+			/* replace vendor string */
+      avio_seek(s->pb, lame_tag_offset + 4, SEEK_SET);  // rewind and skip vbr_scale
+		  for (i = 0; i < FFMIN(strlen(vendor), LAME_EXT_VENDOR_LEN); ++i)
+		      avio_w8(s->pb, vendor[i]);
+		  for (; i < LAME_EXT_VENDOR_LEN; ++i)
+		      avio_w8(s->pb, 0x20);
+    } else if (codec->extradata_size != 0) {
+      av_log(s, AV_LOG_ERROR,
+          "lame info tag in extradata has incorrect size (%d vs. %d bytes)\n",
+          codec->extradata_size, LAME_EXT_SIZE);
+    }
+
+    avio_flush(s->pb);
     avio_seek(s->pb, 0, SEEK_END);
 }

@@ -363,7 +431,7 @@ static int mp3_write_trailer(struct AVFormatContext *s)
         avio_write(s->pb, buf, ID3v1_TAG_SIZE);
     }

-    if (mp3->xing_offset)
+    if (mp3->write_xing && mp3->xing_offset)
         mp3_update_xing(s);

     return 0;
@@ -384,7 +452,7 @@ static int query_codec(enum AVCodecID id, int std_compliance)
 AVOutputFormat ff_mp2_muxer = {
     .name              = "mp2",
     .long_name         = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"),
-    .mime_type         = "audio/mpeg",
+    .mime_type         = "audio/x-mpeg",
     .extensions        = "mp2,m2a,mpa",
     .audio_codec       = AV_CODEC_ID_MP2,
     .video_codec       = AV_CODEC_ID_NONE,
@@ -526,7 +594,7 @@ static int mp3_write_header(struct AVFormatContext *s)
 AVOutputFormat ff_mp3_muxer = {
     .name              = "mp3",
     .long_name         = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"),
-    .mime_type         = "audio/mpeg",
+    .mime_type         = "audio/x-mpeg",
     .extensions        = "mp3",
     .priv_data_size    = sizeof(MP3Context),
     .audio_codec       = AV_CODEC_ID_MP3,
diff --git a/tests/ref/lavf-fate/mp3 b/tests/ref/lavf-fate/mp3
index 6f201e0..5381e0e 100644
--- a/tests/ref/lavf-fate/mp3
+++ b/tests/ref/lavf-fate/mp3
@@ -1,3 +1,3 @@
-6bdea919dc6856d76ef2553698e2b0d3 *./tests/data/lavf-fate/lavf.mp3
+e062b13f521a06e5ee579a48600974a0 *./tests/data/lavf-fate/lavf.mp3
 96376 ./tests/data/lavf-fate/lavf.mp3
 ./tests/data/lavf-fate/lavf.mp3 CRC=0x6c9850fe
--
1.9.1.423.g4596e3a


More information about the ffmpeg-devel mailing list