The first byte of a DSS/DS2 file is the header size in 512-byte blocks. Olympus uses 2/3 (0x600); Grundig/Philips recorders (GR/PH9607, e.g. the Grundig Digta) use 6/7, with the extra blocks holding GR___ device-id records before the audio. Derive header_size from the first byte instead of hard-coding 0x600, and accept any version byte in the probe.
No codec change: the CELP frames are standard DS2-QP / DSS-SP. The same generalization in the hirparak/gaspardpetit Rust reference decodes a real GR/PH DS2-QP file bit-exact vs the licensed Olympus decoder (corr 1.0). Needs a GR/PH FATE sample before ffmpeg-devel submission. Signed-off-by: Guillain d'Erceville <[email protected]> --- Follow-up to the v2 DS2 series (still in review). Applies on top of it; independent of the empty-block / byte1 re-sync follow-up. Olympus files are unaffected (version 2/3 -> 0x600, unchanged). No FATE sample is included yet: the real GR/PH file I hit is a privileged legal dictation. A public Grundig .DSS (version 6) is attached to hirparak/dss-codec issue #11 and exercises the same generalization on the DSS-SP path; I can add it with a reference framecrc if preferred. The change mirrors byte-for-byte the same header_size = first_byte * 512 generalization already proven bit-exact against the Olympus reference in the Rust decoder. libavformat/ds2.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/libavformat/ds2.c b/libavformat/ds2.c index ee8208f..6147f21 100644 --- a/libavformat/ds2.c +++ b/libavformat/ds2.c @@ -50,6 +50,7 @@ #define DS2_COMMENT_SIZE 64 typedef struct DS2DemuxContext { + int header_size; /* first_byte * 512 (0x600 Olympus, 0xe00 Grundig/Philips) */ int format_type; /* DS2_FORMAT_SP or DS2_FORMAT_QP */ int counter; /* bytes remaining in current block payload */ int swap; /* SP byte-swap state */ @@ -61,7 +62,11 @@ typedef struct DS2DemuxContext { } DS2DemuxContext; static int ds2_probe(const AVProbeData *p) { - if (AV_RL32(p->buf) != MKTAG(0x3, 'd', 's', '2')) + /* First byte is the header size in 512-byte blocks (Olympus 2/3, + * Grundig/Philips 6/7); bytes 1..3 are the "ds2" tag. */ + if (p->buf_size < 4 || p->buf[1] != 'd' || p->buf[2] != 's' || p->buf[3] != '2') + return 0; + if (p->buf[0] < 2 || p->buf[0] > 16) return 0; return AVPROBE_SCORE_MAX; @@ -111,15 +116,16 @@ static int ds2_read_metadata_string(AVFormatContext *s, unsigned int offset, static int ds2_count_total_frames(AVFormatContext *s) { AVIOContext *pb = s->pb; + int header_size = ((DS2DemuxContext *)s->priv_data)->header_size; int64_t size = avio_size(pb); int blocks, i, total = 0; - if (size < DS2_HEADER_SIZE) + if (size < header_size) return AVERROR_INVALIDDATA; - blocks = (size - DS2_HEADER_SIZE) / DS2_BLOCK_SIZE; + blocks = (size - header_size) / DS2_BLOCK_SIZE; for (i = 0; i < blocks; i++) { - avio_seek(pb, DS2_HEADER_SIZE + (int64_t)i * DS2_BLOCK_SIZE + 2, SEEK_SET); + avio_seek(pb, header_size + (int64_t)i * DS2_BLOCK_SIZE + 2, SEEK_SET); total += avio_r8(pb); } @@ -130,10 +136,11 @@ static int ds2_find_next_nonempty_swap(AVFormatContext *s, int block_idx) { AVIOContext *pb = s->pb; int64_t fsize = avio_size(pb); int64_t pos = avio_tell(pb); + int header_size = ((DS2DemuxContext *)s->priv_data)->header_size; int bi; for (bi = block_idx + 1; ; bi++) { - int64_t bstart = DS2_HEADER_SIZE + (int64_t)bi * DS2_BLOCK_SIZE; + int64_t bstart = header_size + (int64_t)bi * DS2_BLOCK_SIZE; uint8_t hdr[DS2_AUDIO_BLOCK_HEADER_SIZE]; int ret; @@ -172,7 +179,7 @@ static int ds2_load_block(AVFormatContext *s) { blk_swap = hdr[0] >> 7; frame_count = hdr[2]; cont_size = FFMAX(0, 2 * hdr[1] + 2 * blk_swap - DS2_AUDIO_BLOCK_HEADER_SIZE); - block_idx = (block_pos - DS2_HEADER_SIZE) / DS2_BLOCK_SIZE; + block_idx = (block_pos - ctx->header_size) / DS2_BLOCK_SIZE; if (frame_count == 0) { ctx->counter = cont_size; @@ -199,6 +206,14 @@ static int ds2_read_header(AVFormatContext *s) { uint8_t block_header[DS2_AUDIO_BLOCK_HEADER_SIZE]; int ret, frame_count, cont_size, blk_swap, samples_per_frame; int64_t ret64; + int version; + + if ((ret64 = avio_seek(pb, 0, SEEK_SET)) < 0) + return (int)ret64; + version = avio_r8(pb); + if (version < 2 || version > 16) + return AVERROR_INVALIDDATA; + ctx->header_size = version * DS2_BLOCK_SIZE; st = avformat_new_stream(s, NULL); if (!st) @@ -223,7 +238,7 @@ static int ds2_read_header(AVFormatContext *s) { return ret; ctx->total_frames = ret; - if ((ret64 = avio_seek(pb, DS2_HEADER_SIZE, SEEK_SET)) < 0) + if ((ret64 = avio_seek(pb, ctx->header_size, SEEK_SET)) < 0) return (int)ret64; ret = avio_read(pb, block_header, DS2_AUDIO_BLOCK_HEADER_SIZE); @@ -260,8 +275,8 @@ static int ds2_read_header(AVFormatContext *s) { s->duration = av_rescale_q(nb_samples, (AVRational){1, st->codecpar->sample_rate}, AV_TIME_BASE_Q); - if (file_size > DS2_HEADER_SIZE && s->duration > 0) - s->bit_rate = (file_size - DS2_HEADER_SIZE) * 8LL * AV_TIME_BASE / s->duration; + if (file_size > ctx->header_size && s->duration > 0) + s->bit_rate = (file_size - ctx->header_size) * 8LL * AV_TIME_BASE / s->duration; else s->bit_rate = 8LL * frame_bytes * st->codecpar->sample_rate / samples_per_frame; @@ -449,7 +464,7 @@ static int ds2_read_seek(AVFormatContext *s, int stream_index, if (seekto < 0) seekto = 0; - seekto += DS2_HEADER_SIZE; + seekto += ctx->header_size; ret = avio_seek(s->pb, seekto, SEEK_SET); if (ret < 0) -- 2.39.5 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
