static int read_header(AVFormatContext *s) { BRSTMDemuxContext *b = s->priv_data; int bom, major, minor, codec, chunk; 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) return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; avio_skip(s->pb, 4); bom = avio_rb16(s->pb); if (bom != 0xFEFF && bom != 0xFFFE) { av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom); return AVERROR_INVALIDDATA; } if (bom == 0xFFFE) b->little_endian = 1; 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; 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 = read32(s); if (h1offset > size) return AVERROR_INVALIDDATA; avio_skip(s->pb, 12); toffset = read32(s) + 16LL; if (toffset > size) return AVERROR_INVALIDDATA; avio_skip(s->pb, pos + h1offset + 8 - avio_tell(s->pb)); codec = avio_r8(s->pb); switch (codec) { case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR; 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; } 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 = bfstm ? read32(s) : read16(s); if (!st->codec->sample_rate) return AVERROR_INVALIDDATA; 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 = read32(s); avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); if (!bfstm) start = read32(s); b->current_block = 0; 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 = read32(s); if (b->block_size > UINT32_MAX / st->codec->channels) return AVERROR_INVALIDDATA; 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; 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 (!bfstm) toffset = read32(s) + 16LL; else toffset = toffset + read32(s) + st->codec->channels * 8 - 8; if (toffset > size) return AVERROR_INVALIDDATA; avio_skip(s->pb, pos + toffset - avio_tell(s->pb)); b->table = av_mallocz(32 * st->codec->channels); if (!b->table) return AVERROR(ENOMEM); for (ch = 0; ch < st->codec->channels; ch++) { if (avio_read(s->pb, b->table + ch * 32, 32) != 32) { ret = AVERROR_INVALIDDATA; goto fail; } avio_skip(s->pb, bfstm ? 14 : 24); } } if (size < (avio_tell(s->pb) - pos)) { 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 = 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 && codec != AV_CODEC_ID_ADPCM_THP_LE) goto skip; asize = b->block_count * st->codec->channels * 4; if (size < asize) { ret = AVERROR_INVALIDDATA; goto fail; } if (b->adpc) { av_log(s, AV_LOG_WARNING, "skipping additional ADPC chunk\n"); goto skip; } else { b->adpc = av_mallocz(asize); if (!b->adpc) { ret = AVERROR(ENOMEM); goto fail; } 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 || codec == AV_CODEC_ID_ADPCM_THP_LE))) { ret = AVERROR_INVALIDDATA; goto fail; } avio_skip(s->pb, start - avio_tell(s->pb)); 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; default: av_log(s, AV_LOG_WARNING, "skipping unknown chunk: %X\n", chunk); skip: avio_skip(s->pb, size); } } fail: read_close(s); return ret; }