lavf: add support for libgme
authorwm4 <nfxjfg@googlemail.com>
Fri, 28 Jun 2013 08:36:00 +0000 (10:36 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Tue, 2 Jul 2013 16:52:45 +0000 (18:52 +0200)
Reviewed-by: Stefano Sabatini <stefasab@gmail.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Changelog
configure
doc/demuxers.texi
libavformat/Makefile
libavformat/allformats.c
libavformat/libgme.c [new file with mode: 0644]
libavformat/version.h

index be3cf4c..2cf2805 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -68,6 +68,7 @@ version <next>:
 - WavPack encoding through libwavpack
 - rotate filter
 - spp filter ported from libmpcodecs
+- libgme support
 
 
 version 1.2:
index dbb6672..dd6e951 100755 (executable)
--- a/configure
+++ b/configure
@@ -202,6 +202,7 @@ External library support:
   --enable-libfdk-aac      enable AAC encoding via libfdk-aac [no]
   --enable-libflite        enable flite (voice synthesis) support via libflite [no]
   --enable-libfreetype     enable libfreetype [no]
+  --enable-libgme          enable Game Music Emu via libgme [no]
   --enable-libgsm          enable GSM de/encoding via libgsm [no]
   --enable-libiec61883     enable iec61883 via libiec61883 [no]
   --enable-libilbc         enable iLBC de/encoding via libilbc [no]
@@ -1167,6 +1168,7 @@ EXTERNAL_LIBRARY_LIST="
     libfdk_aac
     libflite
     libfreetype
+    libgme
     libgsm
     libiec61883
     libilbc
@@ -1974,6 +1976,7 @@ libfaac_encoder_deps="libfaac"
 libfaac_encoder_select="audio_frame_queue"
 libfdk_aac_encoder_deps="libfdk_aac"
 libfdk_aac_encoder_select="audio_frame_queue"
+libgme_demuxer_deps="libgme"
 libgsm_decoder_deps="libgsm"
 libgsm_encoder_deps="libgsm"
 libgsm_ms_decoder_deps="libgsm"
@@ -4131,6 +4134,7 @@ enabled libfdk_aac        && require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen
 flite_libs="-lflite_cmu_time_awb -lflite_cmu_us_awb -lflite_cmu_us_kal -lflite_cmu_us_kal16 -lflite_cmu_us_rms -lflite_cmu_us_slt -lflite_usenglish -lflite_cmulex -lflite"
 enabled libflite          && require2 libflite "flite/flite.h" flite_init $flite_libs
 enabled libfreetype       && require_pkg_config freetype2 "ft2build.h freetype/freetype.h" FT_Init_FreeType
+enabled libgme            && require  libgme gme/gme.h gme_new_emu -lgme
 enabled libgsm            && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do
                                    check_lib "${gsm_hdr}" gsm_create -lgsm && break;
                                done || die "ERROR: libgsm not found"; }
index 9cfa954..1a4d739 100644 (file)
@@ -103,6 +103,19 @@ probed and 0 otherwise.
 
 @end table
 
+@section libgme
+
+The Game Music Emu library is a collection of video game music file emulators.
+
+See @url{http://code.google.com/p/game-music-emu/} for more information.
+
+Some files have multiple tracks. The demuxer will pick the first track by
+default. The @option{track_index} option can be used to select a different
+track. Track indexes start at 0. The demuxer exports the number of tracks as
+@var{tracks} meta data entry.
+
+For very large files, the @option{max_size} option may have to be adjusted.
+
 @section libquvi
 
 Play media from Internet services using the quvi project.
index 20bf186..d037661 100644 (file)
@@ -412,6 +412,7 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER)        += yuv4mpeg.o
 OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER)      += yuv4mpeg.o
 
 # external libraries
+OBJS-$(CONFIG_LIBGME_DEMUXER)            += libgme.o
 OBJS-$(CONFIG_LIBMODPLUG_DEMUXER)        += libmodplug.o
 OBJS-$(CONFIG_LIBNUT_DEMUXER)            += libnut.o
 OBJS-$(CONFIG_LIBNUT_MUXER)              += libnut.o
index d4a2d27..6bba812 100644 (file)
@@ -337,6 +337,7 @@ void av_register_all(void)
     REGISTER_PROTOCOL(UDP,              udp);
 
     /* external libraries */
+    REGISTER_DEMUXER (LIBGME,           libgme);
     REGISTER_DEMUXER (LIBMODPLUG,       libmodplug);
     REGISTER_MUXDEMUX(LIBNUT,           libnut);
     REGISTER_DEMUXER (LIBQUVI,          libquvi);
diff --git a/libavformat/libgme.c b/libavformat/libgme.c
new file mode 100644 (file)
index 0000000..1ae63dc
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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
+ */
+
+/**
+* @file
+* libgme demuxer
+*/
+
+#include <gme/gme.h>
+#include "libavutil/avstring.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "avformat.h"
+#include "internal.h"
+
+typedef struct GMEContext {
+    const AVClass *class;
+    Music_Emu *music_emu;
+    gme_info_t *info;   ///< selected track
+
+    /* options */
+    int track_index;
+    int sample_rate;
+    int64_t max_size;
+} GMEContext;
+
+#define OFFSET(x) offsetof(GMEContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+#define D AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    {"track_index", "set track that should be played",        OFFSET(track_index), AV_OPT_TYPE_INT,   {.i64 = 0},                0,    INT_MAX,  A|D},
+    {"sample_rate", "set sample rate",                        OFFSET(sample_rate), AV_OPT_TYPE_INT,   {.i64 = 44100},            1000, 999999,   A|D},
+    {"max_size",    "set max file size supported (in bytes)", OFFSET(max_size),    AV_OPT_TYPE_INT64, {.i64 = 50 * 1024 * 1024}, 0,    SIZE_MAX, A|D},
+    {NULL}
+};
+
+static void add_meta(AVFormatContext *s, const char *name, const char *value)
+{
+    if (value && value[0])
+        av_dict_set(&s->metadata, name, value, 0);
+}
+
+static int load_metadata(AVFormatContext *s)
+{
+    GMEContext *gme = s->priv_data;
+    gme_info_t *info = gme->info;
+    char buf[30];
+
+    add_meta(s, "system",       info->system);
+    add_meta(s, "game",         info->game);
+    add_meta(s, "song",         info->song);
+    add_meta(s, "author",       info->author);
+    add_meta(s, "copyright",    info->copyright);
+    add_meta(s, "comment",      info->comment);
+    add_meta(s, "dumper",       info->dumper);
+
+    snprintf(buf, sizeof(buf), "%d", (int)gme_track_count(gme->music_emu));
+    add_meta(s, "tracks", buf);
+
+    return 0;
+}
+
+#define AUDIO_PKT_SIZE 512
+
+static int read_header_gme(AVFormatContext *s)
+{
+    AVStream *st;
+    AVIOContext *pb = s->pb;
+    GMEContext *gme = s->priv_data;
+    int64_t sz = avio_size(pb);
+    char *buf;
+    char dummy;
+
+    if (sz < 0) {
+        av_log(s, AV_LOG_WARNING, "Could not determine file size\n");
+        sz = gme->max_size;
+    } else if (gme->max_size && sz > gme->max_size) {
+        sz = gme->max_size;
+    }
+
+    buf = av_malloc(sz);
+    if (!buf)
+        return AVERROR(ENOMEM);
+    sz = avio_read(pb, buf, sz);
+
+    // Data left means our buffer (the max_size option) is too small
+    if (avio_read(pb, &dummy, 1) == 1) {
+        av_log(s, AV_LOG_ERROR, "File size is larger than max_size option "
+               "value %"PRIi64", consider increasing the max_size option\n",
+               gme->max_size);
+        return AVERROR_BUFFER_TOO_SMALL;
+    }
+
+    if (gme_open_data(buf, sz, &gme->music_emu, gme->sample_rate)) {
+        av_freep(&buf);
+        return AVERROR_INVALIDDATA;
+    }
+    av_freep(&buf);
+
+    if (gme_track_info(gme->music_emu, &gme->info, gme->track_index))
+        return AVERROR_STREAM_NOT_FOUND;
+
+    if (gme_start_track(gme->music_emu, gme->track_index))
+        return AVERROR_UNKNOWN;
+
+    load_metadata(s);
+
+    st = avformat_new_stream(s, NULL);
+    if (!st)
+        return AVERROR(ENOMEM);
+    avpriv_set_pts_info(st, 64, 1, 1000);
+    if (st->duration > 0)
+        st->duration = gme->info->length;
+    st->codec->codec_type  = AVMEDIA_TYPE_AUDIO;
+    st->codec->codec_id    = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE);
+    st->codec->channels    = 2;
+    st->codec->sample_rate = gme->sample_rate;
+
+    return 0;
+}
+
+static int read_packet_gme(AVFormatContext *s, AVPacket *pkt)
+{
+    GMEContext *gme = s->priv_data;
+    int n_samples = AUDIO_PKT_SIZE / 2;
+    int ret;
+
+    if (gme_track_ended(gme->music_emu))
+        return AVERROR_EOF;
+
+    if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0)
+        return ret;
+
+    if (gme_play(gme->music_emu, n_samples, (short *)pkt->data))
+        return AVERROR_EXTERNAL;
+    pkt->size = AUDIO_PKT_SIZE;
+
+    return 0;
+}
+
+static int read_close_gme(AVFormatContext *s)
+{
+    GMEContext *gme = s->priv_data;
+    gme_free_info(gme->info);
+    gme_delete(gme->music_emu);
+    return 0;
+}
+
+static int read_seek_gme(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
+{
+    GMEContext *gme = s->priv_data;
+    if (!gme_seek(gme->music_emu, (int)ts))
+        return AVERROR_EXTERNAL;
+    return 0;
+}
+
+static int probe_gme(AVProbeData *p)
+{
+    // Reads 4 bytes - returns "" if unknown format.
+    if (gme_identify_header(p->buf)[0]) {
+        if (p->buf_size < 16384)
+            return AVPROBE_SCORE_MAX / 4 + 1;
+        else
+            return AVPROBE_SCORE_MAX / 2;
+    }
+    return 0;
+}
+
+static const AVClass class_gme = {
+    .class_name = "Game Music Emu demuxer",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_libgme_demuxer = {
+    .name           = "libgme",
+    .long_name      = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"),
+    .priv_data_size = sizeof(GMEContext),
+    .read_probe     = probe_gme,
+    .read_header    = read_header_gme,
+    .read_packet    = read_packet_gme,
+    .read_close     = read_close_gme,
+    .read_seek      = read_seek_gme,
+    .priv_class     = &class_gme,
+};
index 3614c44..fcf53d8 100644 (file)
@@ -30,8 +30,8 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 55
-#define LIBAVFORMAT_VERSION_MINOR 10
-#define LIBAVFORMAT_VERSION_MICRO 101
+#define LIBAVFORMAT_VERSION_MINOR 11
+#define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \