]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/brstm.c
Merge commit '271ce76d317c5432e151216cf23f12b77ed6cb7e'
[ffmpeg] / libavformat / brstm.c
index 19a4a2a96be0c6bb6269351b671a7765f3f79fbb..291a4628bcf27c9bf33fb5689ebf58ad272e2810 100644 (file)
@@ -30,8 +30,12 @@ typedef struct BRSTMDemuxContext {
     uint32_t    current_block;
     uint32_t    samples_per_block;
     uint32_t    last_block_used_bytes;
+    uint32_t    last_block_size;
+    uint32_t    last_block_samples;
+    uint32_t    data_start;
     uint8_t     *table;
     uint8_t     *adpc;
+    int         little_endian;
 } BRSTMDemuxContext;
 
 static int probe(AVProbeData *p)
@@ -43,6 +47,16 @@ static int probe(AVProbeData *p)
     return 0;
 }
 
+static int probe_bfstm(AVProbeData *p)
+{
+    if ((AV_RL32(p->buf) == MKTAG('F','S','T','M') ||
+         AV_RL32(p->buf) == MKTAG('C','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;
@@ -53,14 +67,34 @@ static int read_close(AVFormatContext *s)
     return 0;
 }
 
+static av_always_inline unsigned int read16(AVFormatContext *s)
+{
+    BRSTMDemuxContext *b = s->priv_data;
+    if (b->little_endian)
+        return avio_rl16(s->pb);
+    else
+        return avio_rb16(s->pb);
+}
+
+static av_always_inline unsigned int read32(AVFormatContext *s)
+{
+    BRSTMDemuxContext *b = s->priv_data;
+    if (b->little_endian)
+        return avio_rl32(s->pb);
+    else
+        return avio_rb32(s->pb);
+}
+
 static int read_header(AVFormatContext *s)
 {
     BRSTMDemuxContext *b = s->priv_data;
     int bom, major, minor, codec, chunk;
-    int64_t pos, h1offset, toffset;
-    uint32_t size, start, asize;
+    int64_t h1offset, pos, toffset;
+    uint32_t size, asize, start = 0;
     AVStream *st;
     int ret = AVERROR_EOF;
+    int loop = 0;
+    int bfstm = !strcmp("bfstm", s->iformat->name);
 
     st = avformat_new_stream(s, NULL);
     if (!st)
@@ -74,31 +108,75 @@ static int read_header(AVFormatContext *s)
         av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom);
         return AVERROR_INVALIDDATA;
     }
-    if (bom == 0xFFFE) {
-        avpriv_request_sample(s, "little endian byte order");
-        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 (bom == 0xFFFE)
+        b->little_endian = 1;
 
-    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 (!bfstm) {
+        major = avio_r8(s->pb);
+        minor = avio_r8(s->pb);
+        avio_skip(s->pb, 4); // size of file
+        size = read16(s);
+        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 = read16(s); // 6
+
+        avio_skip(s->pb, 4); // Unknown constant 0x00030000
+        avio_skip(s->pb, 4); // size of file
+        section_count = read16(s);
+        avio_skip(s->pb, 2); // padding
+        for (i = 0; avio_tell(s->pb) < header_size
+                    && !(start && info_offset)
+                    && i < section_count; i++) {
+            uint16_t flag = read16(s);
+            avio_skip(s->pb, 2);
+            switch (flag) {
+            case 0x4000:
+                info_offset = read32(s);
+                info_size   = read32(s);
+                break;
+            case 0x4001:
+                avio_skip(s->pb, 4); // seek offset
+                avio_skip(s->pb, 4); // seek size
+                break;
+            case 0x4002:
+                start = read32(s) + 8;
+                avio_skip(s->pb, 4); //data_size = read32(s);
+                break;
+            case 0x4003:
+                avio_skip(s->pb, 4); // REGN offset
+                avio_skip(s->pb, 4); // REGN size
+                break;
+            }
+        }
+
+        if (!info_offset || !start)
+            return AVERROR_INVALIDDATA;
+
+        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;
+    }
+
+    size = read32(s);
+    if (size < 192)
         return AVERROR_INVALIDDATA;
     avio_skip(s->pb, 4); // unknown
-    h1offset = avio_rb32(s->pb);
+    h1offset = read32(s);
     if (h1offset > size)
         return AVERROR_INVALIDDATA;
     avio_skip(s->pb, 12);
-    toffset = avio_rb32(s->pb) + 16LL;
+    toffset = read32(s) + 16LL;
     if (toffset > size)
         return AVERROR_INVALIDDATA;
 
@@ -107,57 +185,77 @@ static int read_header(AVFormatContext *s)
 
     switch (codec) {
     case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR;    break;
-    case 1: codec = AV_CODEC_ID_PCM_S16BE_PLANAR; break;
-    case 2: codec = AV_CODEC_ID_ADPCM_THP;        break;
+    case 1: codec = b->little_endian ?
+                    AV_CODEC_ID_PCM_S16LE_PLANAR :
+                    AV_CODEC_ID_PCM_S16BE_PLANAR; break;
+    case 2: codec = b->little_endian ?
+                    AV_CODEC_ID_ADPCM_THP_LE :
+                    AV_CODEC_ID_ADPCM_THP;        break;
     default:
         avpriv_request_sample(s, "codec %d", codec);
         return AVERROR_PATCHWELCOME;
     }
 
-    avio_skip(s->pb, 1); // loop flag
+    loop = avio_r8(s->pb); // loop flag
     st->codec->codec_id = codec;
     st->codec->channels = avio_r8(s->pb);
     if (!st->codec->channels)
         return AVERROR_INVALIDDATA;
 
     avio_skip(s->pb, 1); // padding
-    st->codec->sample_rate = avio_rb16(s->pb);
+
+    st->codec->sample_rate = bfstm ? read32(s) : read16(s);
     if (!st->codec->sample_rate)
         return AVERROR_INVALIDDATA;
 
-    avio_skip(s->pb, 2); // padding
-    avio_skip(s->pb, 4); // loop start sample
+    if (!bfstm)
+        avio_skip(s->pb, 2); // padding
+
+    if (loop) {
+        if (av_dict_set_int(&s->metadata, "loop_start",
+                            av_rescale(read32(s), AV_TIME_BASE,
+                                       st->codec->sample_rate),
+                            0) < 0)
+            return AVERROR(ENOMEM);
+    } else {
+        avio_skip(s->pb, 4);
+    }
+
     st->start_time = 0;
-    st->duration = avio_rb32(s->pb);
+    st->duration = read32(s);
     avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
 
-    start = avio_rb32(s->pb);
+    if (!bfstm)
+        start = read32(s);
     b->current_block = 0;
-    b->block_count = avio_rb32(s->pb);
+    b->block_count = read32(s);
     if (b->block_count > UINT16_MAX) {
         av_log(s, AV_LOG_WARNING, "too many blocks: %u\n", b->block_count);
         return AVERROR_INVALIDDATA;
     }
 
-    b->block_size = avio_rb32(s->pb);
-    if (b->block_size > UINT16_MAX / st->codec->channels)
+    b->block_size = read32(s);
+    if (b->block_size > UINT32_MAX / st->codec->channels)
         return AVERROR_INVALIDDATA;
-    b->block_size *= st->codec->channels;
 
-    b->samples_per_block = avio_rb32(s->pb);
-    b->last_block_used_bytes = avio_rb32(s->pb);
-    if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels)
+    b->samples_per_block = read32(s);
+    b->last_block_used_bytes = read32(s);
+    b->last_block_samples = read32(s);
+    b->last_block_size = read32(s);
+    if (b->last_block_size > UINT32_MAX / st->codec->channels)
+        return AVERROR_INVALIDDATA;
+    if (b->last_block_used_bytes > b->last_block_size)
         return AVERROR_INVALIDDATA;
-    b->last_block_used_bytes *= st->codec->channels;
 
-    avio_skip(s->pb, 4); // last block samples
-    avio_skip(s->pb, 4); // last block size
 
-    if (codec == AV_CODEC_ID_ADPCM_THP) {
+    if (codec == AV_CODEC_ID_ADPCM_THP || codec == AV_CODEC_ID_ADPCM_THP_LE) {
         int ch;
 
         avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
-        toffset = avio_rb32(s->pb) + 16LL;
+        if (!bfstm)
+            toffset = read32(s) + 16LL;
+        else
+            toffset = toffset + read32(s) + st->codec->channels * 8 - 8;
         if (toffset > size)
             return AVERROR_INVALIDDATA;
 
@@ -171,7 +269,7 @@ static int read_header(AVFormatContext *s)
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
-            avio_skip(s->pb, 24);
+            avio_skip(s->pb, bfstm ? 14 : 24);
         }
     }
 
@@ -179,19 +277,22 @@ static int read_header(AVFormatContext *s)
         ret = AVERROR_INVALIDDATA;
         goto fail;
     }
+
     avio_skip(s->pb, size - (avio_tell(s->pb) - pos));
 
     while (!avio_feof(s->pb)) {
         chunk = avio_rl32(s->pb);
-        size  = avio_rb32(s->pb);
+        size  = read32(s);
         if (size < 8) {
             ret = AVERROR_INVALIDDATA;
             goto fail;
         }
         size -= 8;
         switch (chunk) {
+        case MKTAG('S','E','E','K'):
         case MKTAG('A','D','P','C'):
-            if (codec != AV_CODEC_ID_ADPCM_THP)
+            if (codec != AV_CODEC_ID_ADPCM_THP &&
+                codec != AV_CODEC_ID_ADPCM_THP_LE)
                 goto skip;
 
             asize = b->block_count * st->codec->channels * 4;
@@ -208,19 +309,36 @@ static int read_header(AVFormatContext *s)
                     ret = AVERROR(ENOMEM);
                     goto fail;
                 }
-                avio_read(s->pb, b->adpc, asize);
+                if (bfstm && codec != AV_CODEC_ID_ADPCM_THP_LE) {
+                    // Big-endian BFSTMs have little-endian SEEK tables
+                    // for some strange reason.
+                    int i;
+                    for (i = 0; i < asize; i += 2) {
+                        b->adpc[i+1] = avio_r8(s->pb);
+                        b->adpc[i]   = avio_r8(s->pb);
+                    }
+                } else {
+                    avio_read(s->pb, b->adpc, asize);
+                }
                 avio_skip(s->pb, size - asize);
             }
             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 ||
+                              codec == AV_CODEC_ID_ADPCM_THP_LE))) {
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
             avio_skip(s->pb, start - avio_tell(s->pb));
 
-            if (major != 1 || minor)
+            if (bfstm && (codec == AV_CODEC_ID_ADPCM_THP ||
+                          codec == AV_CODEC_ID_ADPCM_THP_LE))
+                avio_skip(s->pb, 24);
+
+            b->data_start = avio_tell(s->pb);
+
+            if (!bfstm && (major != 1 || minor))
                 avpriv_request_sample(s, "Version %d.%d", major, minor);
 
             return 0;
@@ -241,15 +359,25 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     AVCodecContext *codec = s->streams[0]->codec;
     BRSTMDemuxContext *b = s->priv_data;
-    uint32_t samples, size;
-    int ret;
+    uint32_t samples, size, skip = 0;
+    int ret, i;
 
     if (avio_feof(s->pb))
         return AVERROR_EOF;
     b->current_block++;
     if (b->current_block == b->block_count) {
         size    = b->last_block_used_bytes;
-        samples = size / (8 * codec->channels) * 14;
+        samples = b->last_block_samples;
+        skip    = b->last_block_size - b->last_block_used_bytes;
+
+        if (samples < size * 14 / 8) {
+            uint32_t adjusted_size = samples / 14 * 8;
+            if (samples % 14)
+                adjusted_size += (samples % 14 + 1) / 2 + 1;
+
+            skip += size - adjusted_size;
+            size = adjusted_size;
+        }
     } else if (b->current_block < b->block_count) {
         size    = b->block_size;
         samples = b->samples_per_block;
@@ -257,23 +385,36 @@ 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->codec_id == AV_CODEC_ID_ADPCM_THP_LE) {
         uint8_t *dst;
 
-        if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0)
+        if (av_new_packet(pkt, 8 + (32 + 4 + size) * codec->channels) < 0)
             return AVERROR(ENOMEM);
         dst = pkt->data;
-        bytestream_put_be32(&dst, size);
-        bytestream_put_be32(&dst, samples);
+        if (codec->codec_id == AV_CODEC_ID_ADPCM_THP_LE) {
+            bytestream_put_le32(&dst, size * codec->channels);
+            bytestream_put_le32(&dst, samples);
+        } else {
+            bytestream_put_be32(&dst, size * codec->channels);
+            bytestream_put_be32(&dst, samples);
+        }
         bytestream_put_buffer(&dst, b->table, 32 * codec->channels);
         bytestream_put_buffer(&dst, b->adpc + 4 * codec->channels *
                                     (b->current_block - 1), 4 * codec->channels);
 
-        ret = avio_read(s->pb, dst, size);
-        if (ret != size)
-            av_free_packet(pkt);
+        for (i = 0; i < codec->channels; i++) {
+            ret = avio_read(s->pb, dst, size);
+            dst += size;
+            avio_skip(s->pb, skip);
+            if (ret != size) {
+                av_free_packet(pkt);
+                break;
+            }
+        }
         pkt->duration = samples;
     } else {
+        size *= codec->channels;
         ret = av_get_packet(s->pb, pkt, size);
     }
 
@@ -285,6 +426,24 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
     return ret;
 }
 
+static int read_seek(AVFormatContext *s, int stream_index,
+                     int64_t timestamp, int flags)
+{
+    AVStream *st = s->streams[stream_index];
+    BRSTMDemuxContext *b = s->priv_data;
+    int64_t ret = 0;
+
+    timestamp /= b->samples_per_block;
+    ret = avio_seek(s->pb, b->data_start + timestamp * b->block_size *
+                           st->codec->channels, SEEK_SET);
+    if (ret < 0)
+        return ret;
+
+    b->current_block = timestamp;
+    ff_update_cur_dts(s, st, timestamp * b->samples_per_block);
+    return 0;
+}
+
 AVInputFormat ff_brstm_demuxer = {
     .name           = "brstm",
     .long_name      = NULL_IF_CONFIG_SMALL("BRSTM (Binary Revolution Stream)"),
@@ -293,5 +452,18 @@ AVInputFormat ff_brstm_demuxer = {
     .read_header    = read_header,
     .read_packet    = read_packet,
     .read_close     = read_close,
+    .read_seek      = read_seek,
     .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,
+    .read_seek      = read_seek,
+    .extensions     = "bfstm,bcstm",
+};