diff --git a/Changelog b/Changelog index cfe3e93768..a5c6b821a0 100644 --- a/Changelog +++ b/Changelog @@ -3,7 +3,7 @@ releases are sorted from youngest to oldest. version : - colorkey video filter -- BFSTM demuxer +- BFSTM/BCSTM demuxer - little-endian ADPCM_THP decoder diff --git a/libavformat/brstm.c b/libavformat/brstm.c index 1eba943e51..15e63b8170 100644 --- a/libavformat/brstm.c +++ b/libavformat/brstm.c @@ -33,6 +33,7 @@ typedef struct BRSTMDemuxContext { uint8_t *table; uint8_t *adpc; int bfstm; + int little_endian; } BRSTMDemuxContext; static int probe(AVProbeData *p) @@ -46,7 +47,8 @@ static int probe(AVProbeData *p) static int probe_bfstm(AVProbeData *p) { - if (AV_RL32(p->buf) == MKTAG('F','S','T','M') && + 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; @@ -63,6 +65,24 @@ 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; @@ -86,16 +106,15 @@ 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; - } + + if (bom == 0xFFFE) + b->little_endian = 1; 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); + size = read16(s); if (size < 14) return AVERROR_INVALIDDATA; @@ -107,30 +126,31 @@ static int read_header(AVFormatContext *s) uint32_t info_offset = 0, info_size; uint16_t section_count, header_size, i; - header_size = avio_rb16(s->pb); // 6 + header_size = read16(s); // 6 avio_skip(s->pb, 4); // Unknown constant 0x00030000 avio_skip(s->pb, 4); // size of file - section_count = avio_rb16(s->pb); + section_count = read16(s); 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); + uint16_t flag = read16(s); + avio_skip(s->pb, 2); switch (flag) { - case 0x40000000: - info_offset = avio_rb32(s->pb); - info_size = avio_rb32(s->pb); + case 0x4000: + info_offset = read32(s); + info_size = read32(s); break; - case 0x40010000: + case 0x4001: 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); + case 0x4002: + data_offset = read32(s); + avio_skip(s->pb, 4); //data_size = read32(s); break; - case 0x40030000: + case 0x4003: avio_skip(s->pb, 4); // REGN offset avio_skip(s->pb, 4); // REGN size break; @@ -148,15 +168,15 @@ static int read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - size = avio_rb32(s->pb); + 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; @@ -166,7 +186,9 @@ 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 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; @@ -179,10 +201,8 @@ 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); + st->codec->sample_rate = b->bfstm ? read32(s) : read16(s); if (!st->codec->sample_rate) return AVERROR_INVALIDDATA; @@ -190,25 +210,25 @@ static int read_header(AVFormatContext *s) avio_skip(s->pb, 2); // padding avio_skip(s->pb, 4); // loop start sample 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); if (!b->bfstm) - start = avio_rb32(s->pb); + 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); + b->block_size = read32(s); if (b->block_size > UINT16_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); + b->samples_per_block = read32(s); + b->last_block_used_bytes = read32(s); if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels) return AVERROR_INVALIDDATA; b->last_block_used_bytes *= st->codec->channels; @@ -216,14 +236,14 @@ static int read_header(AVFormatContext *s) 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)); if (!b->bfstm) - toffset = avio_rb32(s->pb) + 16LL; + toffset = read32(s) + 16LL; else - toffset = toffset + avio_rb32(s->pb) + st->codec->channels * 8 - 8; + toffset = toffset + read32(s) + st->codec->channels * 8 - 8; if (toffset > size) return AVERROR_INVALIDDATA; @@ -261,7 +281,7 @@ static int read_header(AVFormatContext *s) 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; @@ -269,7 +289,8 @@ static int read_header(AVFormatContext *s) size -= 8; switch (chunk) { 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; @@ -292,7 +313,9 @@ 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->bfstm)) { + (!b->adpc && (codec == AV_CODEC_ID_ADPCM_THP || + codec == AV_CODEC_ID_ADPCM_THP_LE) + && !b->bfstm)) { ret = AVERROR_INVALIDDATA; goto fail; } @@ -335,7 +358,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } - if (codec->codec_id == AV_CODEC_ID_ADPCM_THP && !codec->extradata) { + if ((codec->codec_id == AV_CODEC_ID_ADPCM_THP || + codec->codec_id == AV_CODEC_ID_ADPCM_THP_LE) && !codec->extradata) { uint8_t *dst; if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0) @@ -382,5 +406,5 @@ AVInputFormat ff_bfstm_demuxer = { .read_header = read_header, .read_packet = read_packet, .read_close = read_close, - .extensions = "bfstm", + .extensions = "bfstm,bcstm", };