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]

Reply via email to