Add replaygain side data type and code for parsing replaygain tags.
authorAnton Khirnov <anton@khirnov.net>
Wed, 19 Feb 2014 14:29:06 +0000 (15:29 +0100)
committerAnton Khirnov <anton@khirnov.net>
Mon, 24 Mar 2014 05:07:51 +0000 (06:07 +0100)
doc/APIchanges
libavcodec/avcodec.h
libavcodec/version.h
libavformat/replaygain.c [new file with mode: 0644]
libavformat/replaygain.h [new file with mode: 0644]
libavutil/Makefile
libavutil/frame.h
libavutil/replaygain.h [new file with mode: 0644]
libavutil/version.h

index 120ba83..afeec8c 100644 (file)
@@ -13,6 +13,13 @@ libavutil:     2013-12-xx
 
 API changes, most recent first:
 
+2014-02-xx - xxxxxxx - lavu 53.07.0 - frame.h, replaygain.h
+  Add AV_FRAME_DATA_REPLAYGAIN for exporting replaygain tags.
+  Add a new header replaygain.h with the AVReplayGain struct.
+
+2014-02-xx - xxxxxxx - lavc 55.36.0 - avcodec.h
+  Add AV_PKT_DATA_REPLAYGAIN for exporting replaygain tags.
+
 2014-02-xx - xxxxxxx - lavf 55.13.0 - avformat.h
   Add AVStream.side_data and AVStream.nb_side_data for exporting stream-global
   side data (e.g. replaygain tags, video rotation)
index a870a2b..4fda36e 100644 (file)
@@ -923,6 +923,12 @@ enum AVPacketSideDataType {
      * @endcode
      */
     AV_PKT_DATA_H263_MB_INFO,
+
+    /**
+     * This side data should be associated with an audio stream and contains
+     * ReplayGain information in form of the AVReplayGain struct.
+     */
+    AV_PKT_DATA_REPLAYGAIN,
 };
 
 typedef struct AVPacketSideData {
index d2f80ad..47166f7 100644 (file)
@@ -29,7 +29,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR 55
-#define LIBAVCODEC_VERSION_MINOR 35
+#define LIBAVCODEC_VERSION_MINOR 36
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/replaygain.c b/libavformat/replaygain.c
new file mode 100644 (file)
index 0000000..cf4dbf8
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * This file is part of Libav.
+ *
+ * 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.
+ *
+ * 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * replaygain tags parsing
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libavutil/avstring.h"
+#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/mem.h"
+#include "libavutil/replaygain.h"
+
+#include "avformat.h"
+#include "replaygain.h"
+
+static int32_t parse_gain(const char *gain)
+{
+    char *fraction;
+    int  scale = 10000;
+    int32_t mb = 0;
+    int db;
+
+    if (!gain)
+        return INT32_MIN;
+
+    gain += strspn(gain, " \t");
+
+    db = strtol(gain, &fraction, 0);
+    if (*fraction++ == '.') {
+        while (av_isdigit(*fraction) && scale) {
+            mb += scale * (*fraction - '0');
+            scale /= 10;
+            fraction++;
+        }
+    }
+
+    if (abs(db) > (INT32_MAX - mb) / 100000)
+        return INT32_MIN;
+
+    return db * 100000 + FFSIGN(db) * mb;
+}
+
+static uint32_t parse_peak(const uint8_t *peak)
+{
+    int64_t val = 0;
+    int64_t scale = 1;
+
+    if (!peak)
+        return 0;
+
+    peak += strspn(peak, " \t");
+
+    if (peak[0] == '1' && peak[1] == '.')
+        return UINT32_MAX;
+    else if (!(peak[0] == '0' && peak[1] == '.'))
+        return 0;
+
+    peak += 2;
+
+    while (av_isdigit(*peak)) {
+        int digit = *peak - '0';
+
+        if (scale > INT64_MAX / 10)
+            break;
+
+        val    = 10 * val + digit;
+        scale *= 10;
+
+        peak++;
+    }
+
+    return av_rescale(val, UINT32_MAX, scale);
+}
+
+static int replaygain_export(AVStream *st,
+                             const uint8_t *track_gain, const uint8_t *track_peak,
+                             const uint8_t *album_gain, const uint8_t *album_peak)
+{
+    AVPacketSideData *sd, *tmp;
+    AVReplayGain *replaygain;
+    uint8_t *data;
+    int32_t tg, ag;
+    uint32_t tp, ap;
+
+    tg = parse_gain(track_gain);
+    ag = parse_gain(album_gain);
+    tp = parse_peak(track_peak);
+    ap = parse_peak(album_peak);
+
+    if (tg == INT32_MIN && ag == INT32_MIN)
+        return 0;
+
+    replaygain = av_mallocz(sizeof(*replaygain));
+    if (!replaygain)
+        return AVERROR(ENOMEM);
+
+    tmp = av_realloc_array(st->side_data, st->nb_side_data + 1, sizeof(*tmp));
+    if (!tmp) {
+        av_freep(&replaygain);
+        return AVERROR(ENOMEM);
+    }
+    st->side_data = tmp;
+    st->nb_side_data++;
+
+    sd = &st->side_data[st->nb_side_data - 1];
+    sd->type = AV_PKT_DATA_REPLAYGAIN;
+    sd->data = (uint8_t*)replaygain;
+    sd->size = sizeof(*replaygain);
+
+    replaygain->track_gain = tg;
+    replaygain->track_peak = tp;
+    replaygain->album_gain = ag;
+    replaygain->album_peak = ap;
+
+    return 0;
+}
+
+int ff_replaygain_export(AVStream *st, AVDictionary *metadata)
+{
+    const AVDictionaryEntry *tg, *tp, *ag, *ap;
+
+    tg = av_dict_get(metadata, "REPLAYGAIN_TRACK_GAIN", NULL, 0);
+    tp = av_dict_get(metadata, "REPLAYGAIN_TRACK_PEAK", NULL, 0);
+    ag = av_dict_get(metadata, "REPLAYGAIN_ALBUM_GAIN", NULL, 0);
+    ap = av_dict_get(metadata, "REPLAYGAIN_ALBUM_PEAK", NULL, 0);
+
+    return replaygain_export(st,
+                             tg ? tg->value : NULL,
+                             tp ? tp->value : NULL,
+                             ag ? ag->value : NULL,
+                             ap ? ap->value : NULL);
+}
diff --git a/libavformat/replaygain.h b/libavformat/replaygain.h
new file mode 100644 (file)
index 0000000..641b7a2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * This file is part of Libav.
+ *
+ * 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.
+ *
+ * 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_REPLAYGAIN_H
+#define AVFORMAT_REPLAYGAIN_H
+
+#include "libavutil/dict.h"
+
+#include "avformat.h"
+
+/**
+ * Parse replaygain tags and export them as per-stream side data.
+ */
+int ff_replaygain_export(AVStream *st, AVDictionary *metadata);
+
+#endif /* AVFORMAT_REPLAYGAIN_H */
index 5869e67..d5c1636 100644 (file)
@@ -39,6 +39,7 @@ HEADERS = adler32.h                                                     \
           pixdesc.h                                                     \
           pixfmt.h                                                      \
           random_seed.h                                                 \
+          replaygain.h                                                  \
           rational.h                                                    \
           samplefmt.h                                                   \
           sha.h                                                         \
index 6b168e2..e9bc6ae 100644 (file)
@@ -69,6 +69,10 @@ enum AVFrameSideDataType {
      * The data is the AVDownmixInfo struct defined in libavutil/downmix_info.h.
      */
     AV_FRAME_DATA_DOWNMIX_INFO,
+    /**
+     * ReplayGain information in the form of the AVReplayGain struct.
+     */
+    AV_FRAME_DATA_REPLAYGAIN,
 };
 
 typedef struct AVFrameSideData {
diff --git a/libavutil/replaygain.h b/libavutil/replaygain.h
new file mode 100644 (file)
index 0000000..8447703
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ * This file is part of Libav.
+ *
+ * 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.
+ *
+ * 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_REPLAYGAIN_H
+#define AVUTIL_REPLAYGAIN_H
+
+#include <stdint.h>
+
+/**
+ * ReplayGain information (see
+ * http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification).
+ * The size of this struct is a part of the public ABI.
+ */
+typedef struct AVReplayGain {
+    /**
+     * Track replay gain in microbels (divide by 100000 to get the value in dB).
+     * Should be set to INT32_MIN when unknown.
+     */
+    int32_t track_gain;
+    /**
+     * Peak track amplitude, with UINT32_MAX representing full scale. 0 when
+     * unknown.
+     */
+    uint32_t track_peak;
+    /**
+     * Same as track_gain, but for the whole album.
+     */
+    int32_t album_gain;
+    /**
+     * Same as track_peak, but for the whole album,
+     */
+    uint32_t album_peak;
+} AVReplayGain;
+
+#endif /* AVUTIL_REPLAYGAIN_H */
index 36070b2..d680979 100644 (file)
@@ -54,7 +54,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR 53
-#define LIBAVUTIL_VERSION_MINOR  6
+#define LIBAVUTIL_VERSION_MINOR  7
 #define LIBAVUTIL_VERSION_MICRO  0
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \