[FFmpeg-devel] [PATCH 3/4] Copy Lame tag to extradata
Giovanni Motta
giovanni.motta at gmail.com
Wed May 28 03:19:49 CEST 2014
Fixes ticket #3577
---
libavcodec/libmp3lame.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 207 insertions(+)
diff --git a/libavcodec/libmp3lame.c b/libavcodec/libmp3lame.c
index a7274f4..4b1fc38 100644
--- a/libavcodec/libmp3lame.c
+++ b/libavcodec/libmp3lame.c
@@ -26,6 +26,8 @@
#include <lame/lame.h>
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/float_dsp.h"
@@ -34,12 +36,16 @@
#include "libavutil/opt.h"
#include "avcodec.h"
#include "audio_frame_queue.h"
+#include "bytestream.h"
#include "internal.h"
#include "mpegaudio.h"
#include "mpegaudiodecheader.h"
#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 tag added to the Xing/Info header */
+#define LAME_EXT_SIZE 40
+
typedef struct LAMEContext {
AVClass *class;
AVCodecContext *avctx;
@@ -195,6 +201,200 @@ 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 extradata_size)
+{
+ uint8_t *extradata_ptr = extradata;
+
+ 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;
+
+ int nABRBitrate;
+
+ /* Lame tag is fixed size */
+ if (extradata_size < LAME_EXT_SIZE)
+ return 0;
+
+ /* if ABR, {store bitrate <= 255} else {store "-b"} */
+ 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 LAME_EXT_SIZE bytes to extradata */
+ bytestream_put_be32(&extradata_ptr, nQuality);
+ bytestream_put_buffer(&extradata_ptr, szVersion, 9);
+ bytestream_put_byte(&extradata_ptr, nRevMethod);
+ bytestream_put_byte(&extradata_ptr, nLowpass);
+ bytestream_put_be32(&extradata_ptr, nPeakSignalAmplitude);
+ bytestream_put_be16(&extradata_ptr, nRadioReplayGain);
+ bytestream_put_be16(&extradata_ptr, nAudiophileReplayGain);
+ bytestream_put_byte(&extradata_ptr, nFlags);
+ bytestream_put_byte(&extradata_ptr, (nABRBitrate >= 255)? 0xFF: nABRBitrate);
+ bytestream_put_byte(&extradata_ptr, enc_delay >> 4);
+ bytestream_put_byte(&extradata_ptr, (enc_delay << 4) + (enc_padding >> 8));
+ bytestream_put_byte(&extradata_ptr, enc_padding);
+ bytestream_put_byte(&extradata_ptr, nMisc);
+ bytestream_put_byte(&extradata_ptr, 0x00); // unused in rev0
+ bytestream_put_be16(&extradata_ptr, 0x0000);
+ bytestream_put_be32(&extradata_ptr, (int) nMusicLength);
+ bytestream_put_be16(&extradata_ptr, nMusicCRC);
+ bytestream_put_be16(&extradata_ptr, nTagCRC);
+
+ av_assert1(extradata_ptr - extradata == LAME_EXT_SIZE);
+
+ return extradata_ptr - extradata;
+}
+
static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet_ptr)
{
@@ -232,6 +432,13 @@ 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);
+ avctx->extradata = av_malloc(LAME_EXT_SIZE);
+ avctx->extradata_size = GetLameInfoTag(s->gfp, avctx->extradata, LAME_EXT_SIZE);
+ /* Lame tag is fixed size; we expect exactly LAME_EXT_SIZE bytes */
+ if (avctx->extradata_size != LAME_EXT_SIZE) {
+ av_log(avctx, AV_LOG_ERROR, "error storing Lame info tag\n");
+ avctx->extradata_size = 0;
+ }
}
if (lame_result < 0) {
if (lame_result == -1) {
--
1.9.1.423.g4596e3a
More information about the ffmpeg-devel
mailing list