lavf/brstm: add support for BFSTM files
authorRodger Combs <rodger.combs@gmail.com>
Wed, 17 Jun 2015 08:09:52 +0000 (03:09 -0500)
committerMichael Niedermayer <michaelni@gmx.at>
Wed, 17 Jun 2015 11:24:11 +0000 (13:24 +0200)
Previous version reviewed-by: Paul B Mahol <onemda@gmail.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Changelog
doc/general.texi
libavcodec/utils.c
libavformat/Makefile
libavformat/allformats.c
libavformat/brstm.c
libavformat/version.h

index 3853978..c93889a 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -3,6 +3,7 @@ releases are sorted from youngest to oldest.
 
 version <next>:
 - colorkey video filter
+- BFSTM demuxer
 
 
 version 2.7:
index ba79503..471a11b 100644 (file)
@@ -243,6 +243,8 @@ library:
     @tab Used in Z and Z95 games.
 @item Brute Force & Ignorance   @tab   @tab X
     @tab Used in the game Flash Traffic: City of Angels.
+@item BFSTM                     @tab   @tab X
+    @tab Audio format used on the Nintendo WiiU (based on BRSTM).
 @item BRSTM                     @tab   @tab X
     @tab Audio format used on the Nintendo Wii.
 @item BWF                       @tab X @tab X
index 558afeb..a444a5e 100644 (file)
@@ -3430,6 +3430,10 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes)
                 return (frame_bytes - 4) * 2 / ch;
             case AV_CODEC_ID_ADPCM_IMA_AMV:
                 return (frame_bytes - 8) * 2 / ch;
+            case AV_CODEC_ID_ADPCM_THP:
+                if (avctx->extradata)
+                    return frame_bytes * 14 / (8 * ch);
+                break;
             case AV_CODEC_ID_ADPCM_XA:
                 return (frame_bytes / 128) * 224 / ch;
             case AV_CODEC_ID_INTERPLAY_DPCM:
index b9169d9..993ec09 100644 (file)
@@ -105,6 +105,7 @@ OBJS-$(CONFIG_BIT_DEMUXER)               += bit.o
 OBJS-$(CONFIG_BIT_MUXER)                 += bit.o
 OBJS-$(CONFIG_BMV_DEMUXER)               += bmv.o
 OBJS-$(CONFIG_BOA_DEMUXER)               += boadec.o
+OBJS-$(CONFIG_BFSTM_DEMUXER)             += brstm.o
 OBJS-$(CONFIG_BRSTM_DEMUXER)             += brstm.o
 OBJS-$(CONFIG_C93_DEMUXER)               += c93.o vocdec.o voc.o
 OBJS-$(CONFIG_CAF_DEMUXER)               += cafdec.o caf.o mov.o mov_chan.o \
index 3a49650..306e0c9 100644 (file)
@@ -92,6 +92,7 @@ void av_register_all(void)
     REGISTER_DEMUXER (BINK,             bink);
     REGISTER_MUXDEMUX(BIT,              bit);
     REGISTER_DEMUXER (BMV,              bmv);
+    REGISTER_DEMUXER (BFSTM,            bfstm);
     REGISTER_DEMUXER (BRSTM,            brstm);
     REGISTER_DEMUXER (BOA,              boa);
     REGISTER_DEMUXER (C93,              c93);
index 19a4a2a..1eba943 100644 (file)
@@ -32,6 +32,7 @@ typedef struct BRSTMDemuxContext {
     uint32_t    last_block_used_bytes;
     uint8_t     *table;
     uint8_t     *adpc;
+    int         bfstm;
 } BRSTMDemuxContext;
 
 static int probe(AVProbeData *p)
@@ -43,6 +44,15 @@ static int probe(AVProbeData *p)
     return 0;
 }
 
+static int probe_bfstm(AVProbeData *p)
+{
+    if (AV_RL32(p->buf) == MKTAG('F','S','T','M') &&
+        (AV_RL16(p->buf + 4) == 0xFFFE ||
+         AV_RL16(p->buf + 4) == 0xFEFF))
+        return AVPROBE_SCORE_MAX / 3 * 2;
+    return 0;
+}
+
 static int read_close(AVFormatContext *s)
 {
     BRSTMDemuxContext *b = s->priv_data;
@@ -57,11 +67,13 @@ static int read_header(AVFormatContext *s)
 {
     BRSTMDemuxContext *b = s->priv_data;
     int bom, major, minor, codec, chunk;
-    int64_t pos, h1offset, toffset;
+    int64_t h1offset, pos, toffset, data_offset = 0;
     uint32_t size, start, asize;
     AVStream *st;
     int ret = AVERROR_EOF;
 
+    b->bfstm = !strcmp("bfstm", s->iformat->name);
+
     st = avformat_new_stream(s, NULL);
     if (!st)
         return AVERROR(ENOMEM);
@@ -79,19 +91,65 @@ static int read_header(AVFormatContext *s)
         return AVERROR_PATCHWELCOME;
     }
 
-    major = avio_r8(s->pb);
-    minor = avio_r8(s->pb);
-    avio_skip(s->pb, 4); // size of file
-    size = avio_rb16(s->pb);
-    if (size < 14)
-        return AVERROR_INVALIDDATA;
+    if (!b->bfstm) {
+        major = avio_r8(s->pb);
+        minor = avio_r8(s->pb);
+        avio_skip(s->pb, 4); // size of file
+        size = avio_rb16(s->pb);
+        if (size < 14)
+            return AVERROR_INVALIDDATA;
+
+        avio_skip(s->pb, size - 14);
+        pos = avio_tell(s->pb);
+        if (avio_rl32(s->pb) != MKTAG('H','E','A','D'))
+            return AVERROR_INVALIDDATA;
+    } else {
+        uint32_t info_offset = 0, info_size;
+        uint16_t section_count, header_size, i;
+
+        header_size = avio_rb16(s->pb); // 6
+
+        avio_skip(s->pb, 4); // Unknown constant 0x00030000
+        avio_skip(s->pb, 4); // size of file
+        section_count = avio_rb16(s->pb);
+        avio_skip(s->pb, 2); // padding
+        for (i = 0; avio_tell(s->pb) < header_size
+                    && !(data_offset && info_offset)
+                    && i < section_count; i++) {
+            uint32_t flag = avio_rb32(s->pb);
+            switch (flag) {
+            case 0x40000000:
+                info_offset = avio_rb32(s->pb);
+                info_size   = avio_rb32(s->pb);
+                break;
+            case 0x40010000:
+                avio_skip(s->pb, 4); // seek offset
+                avio_skip(s->pb, 4); // seek size
+                break;
+            case 0x40020000:
+                data_offset = avio_rb32(s->pb);
+                avio_skip(s->pb, 4); //data_size = avio_rb32(s->pb);
+                break;
+            case 0x40030000:
+                avio_skip(s->pb, 4); // REGN offset
+                avio_skip(s->pb, 4); // REGN size
+                break;
+            }
+        }
+
+        if (!info_offset || !data_offset)
+            return AVERROR_INVALIDDATA;
+
+        start = data_offset + 8;
+
+        avio_skip(s->pb, info_offset - avio_tell(s->pb));
+        pos = avio_tell(s->pb);
+        if (avio_rl32(s->pb) != MKTAG('I','N','F','O'))
+            return AVERROR_INVALIDDATA;
+    }
 
-    avio_skip(s->pb, size - 14);
-    pos = avio_tell(s->pb);
-    if (avio_rl32(s->pb) != MKTAG('H','E','A','D'))
-        return AVERROR_INVALIDDATA;
     size = avio_rb32(s->pb);
-    if (size < 256)
+    if (size < 192)
         return AVERROR_INVALIDDATA;
     avio_skip(s->pb, 4); // unknown
     h1offset = avio_rb32(s->pb);
@@ -121,17 +179,22 @@ static int read_header(AVFormatContext *s)
         return AVERROR_INVALIDDATA;
 
     avio_skip(s->pb, 1); // padding
+    if (b->bfstm)
+        avio_skip(s->pb, 2); // padding
+
     st->codec->sample_rate = avio_rb16(s->pb);
     if (!st->codec->sample_rate)
         return AVERROR_INVALIDDATA;
 
-    avio_skip(s->pb, 2); // padding
+    if (!b->bfstm)
+        avio_skip(s->pb, 2); // padding
     avio_skip(s->pb, 4); // loop start sample
     st->start_time = 0;
     st->duration = avio_rb32(s->pb);
     avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
 
-    start = avio_rb32(s->pb);
+    if (!b->bfstm)
+        start = avio_rb32(s->pb);
     b->current_block = 0;
     b->block_count = avio_rb32(s->pb);
     if (b->block_count > UINT16_MAX) {
@@ -157,7 +220,10 @@ static int read_header(AVFormatContext *s)
         int ch;
 
         avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
-        toffset = avio_rb32(s->pb) + 16LL;
+        if (!b->bfstm)
+            toffset = avio_rb32(s->pb) + 16LL;
+        else
+            toffset = toffset + avio_rb32(s->pb) + st->codec->channels * 8 - 8;
         if (toffset > size)
             return AVERROR_INVALIDDATA;
 
@@ -171,7 +237,15 @@ static int read_header(AVFormatContext *s)
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
-            avio_skip(s->pb, 24);
+            avio_skip(s->pb, b->bfstm ? 14 : 24);
+        }
+
+        if (b->bfstm) {
+            st->codec->extradata_size = 32 * st->codec->channels;
+            st->codec->extradata = av_malloc(st->codec->extradata_size);
+            if (!st->codec->extradata)
+                return AVERROR(ENOMEM);
+            memcpy(st->codec->extradata, b->table, st->codec->extradata_size);
         }
     }
 
@@ -179,7 +253,11 @@ static int read_header(AVFormatContext *s)
         ret = AVERROR_INVALIDDATA;
         goto fail;
     }
-    avio_skip(s->pb, size - (avio_tell(s->pb) - pos));
+
+    if (!b->bfstm)
+        avio_skip(s->pb, size - (avio_tell(s->pb) - pos));
+    else
+        avio_skip(s->pb, data_offset - avio_tell(s->pb));
 
     while (!avio_feof(s->pb)) {
         chunk = avio_rl32(s->pb);
@@ -214,13 +292,13 @@ static int read_header(AVFormatContext *s)
             break;
         case MKTAG('D','A','T','A'):
             if ((start < avio_tell(s->pb)) ||
-                (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP)) {
+                (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP && !b->bfstm)) {
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
             avio_skip(s->pb, start - avio_tell(s->pb));
 
-            if (major != 1 || minor)
+            if ((major != 1 || minor) && !b->bfstm)
                 avpriv_request_sample(s, "Version %d.%d", major, minor);
 
             return 0;
@@ -257,7 +335,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
         return AVERROR_EOF;
     }
 
-    if (codec->codec_id == AV_CODEC_ID_ADPCM_THP) {
+    if (codec->codec_id == AV_CODEC_ID_ADPCM_THP && !codec->extradata) {
         uint8_t *dst;
 
         if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0)
@@ -295,3 +373,14 @@ AVInputFormat ff_brstm_demuxer = {
     .read_close     = read_close,
     .extensions     = "brstm",
 };
+
+AVInputFormat ff_bfstm_demuxer = {
+    .name           = "bfstm",
+    .long_name      = NULL_IF_CONFIG_SMALL("BFSTM (Binary Cafe Stream)"),
+    .priv_data_size = sizeof(BRSTMDemuxContext),
+    .read_probe     = probe_bfstm,
+    .read_header    = read_header,
+    .read_packet    = read_packet,
+    .read_close     = read_close,
+    .extensions     = "bfstm",
+};
index c0920d5..99b7190 100644 (file)
@@ -30,7 +30,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVFORMAT_VERSION_MAJOR 56
-#define LIBAVFORMAT_VERSION_MINOR  36
+#define LIBAVFORMAT_VERSION_MINOR  37
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \