The ds64 chunk holds the sizes that do not fit in 32 bits. It is the first
chunk of every RF64 file, straight after the 12-byte
header at offset 12, so a
reader has the real sizes before it meets the chunks that need them.
Fields
The chunk has the usual header, the ID ds64 and a 32-bit size of 28 + 12 ×
tableLength. Offsets below are from the start of its data. Numbers are
little-endian.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 8 | u64 | riffSize | the real size of the outer chunk: the file size minus 8 |
| 8 | 8 | u64 | dataSize | the real size of the data chunk |
| 16 | 8 | u64 | sampleCount | the real frame count, for a fact chunk whose own count is 0xFFFFFFFF |
| 24 | 4 | u32 | tableLength | the number of table entries that follow |
| 28 | 12 each | record | table | the sizes of other chunks too large for 32 bits |
The names are Tech 3306’s. The EBU’s library libbw64 calls the fields
bw64Size, dataSize, dummySize and
tableLength, and writes dummySize as 0. The explorer reports a
tableLength larger than the entries that fit in the chunk.
The table
Each entry gives the size of one chunk, other than the data chunk, whose own size is
0xFFFFFFFF:
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 4 | char[4] | chunkId | the chunk’s ID, such as axml |
| 4 | 8 | u64 | chunkSize | its real size |
Only very large metadata chunks need an entry, so the table is usually empty and the chunk is 28 bytes of data.
The size rule
A chunk whose 32-bit size is 0xFFFFFFFF takes its size from
ds64, as libbw64’s getChunkSize64 does it:
- the outer chunk takes
riffSize; - the
datachunk takesdataSize; - any other chunk takes the size in its table entry.
Any other 32-bit size is the real size. A small RF64 file may give its data chunk its
real 32-bit size; the explorer uses that size, and reports when dataSize
disagrees. A size of 0xFFFFFFFF for which ds64 has no value is
taken to run to the end of the file, and reported.
The same goes for the fact chunk’s frame count: when it is
0xFFFFFFFF, the real count is sampleCount. When it is not, trust
it: in one of the two RF64 files examined for this site, a SciPy test file,
sampleCount says 41,745 frames while the fact chunk says 4,410,
and the data holds 4,410.
An example
00000000 52 46 36 34 FF FF FF FF 57 41 56 45 64 73 36 34 RF64....WAVEds64
00000010 1C 00 00 00 48 F2 05 2A 01 00 00 00 00 F2 05 2A ....H..*.......*
00000020 01 00 00 00 80 7C 81 4A 00 00 00 00 00 00 00 00 .....|.J........
Here riffSize is 0x12A05F248, 5,000,000,072;
dataSize is 0x12A05F200, 5,000,000,000; sampleCount
is 0x4A817C80, 1,250,000,000 frames of 4 bytes; and the table is empty. The
format chunk follows at offset 48, then a data chunk whose size reads
FF FF FF FF. The RIFF size counts the 4 bytes of WAVE, the
36-byte ds64 chunk, the 24-byte format chunk, the data chunk’s 8-byte header
and the samples.
A complete small file, byte by byte, is on RF64 header and chunks.
The JUNK chunk trick
A recorder does not know in advance whether a file will pass 4 GB. So it writes an
ordinary WAV file, starting RIFF, with a 28-byte JUNK chunk
straight after the header: the size of an empty ds64’s data, 8 + 8 + 8 + 4
bytes. If the recording stays small, the file is an ordinary WAV file and readers skip
the filler. If it grows past 4 GB, the recorder changes RIFF to
RF64 and JUNK to ds64, fills in the 64-bit sizes
and sets the 32-bit ones to 0xFFFFFFFF, without moving any audio.
Ordinary WAV files laid out this way are met in practice: Apple’s own system sounds,
for example, have a 28-byte JUNK chunk first, as this trick would leave
them.