|
arduino-audio-tools
|
Discards every value right after appending it, keeping only the file offset of the first entry - get() seeks back into the original MP4 source and re-reads the entry directly (entries are fixed-size and contiguous on disk, so entry N sits at start_offset + N*sizeof(T)), trading RAM for repeated small seeks. More...
#include <SampleTableStore.h>
Public Member Functions | |
| SourceSeekSampleTableStore (SeekableSource &source, size_t onDiskSize, T(*decoder)(const uint8_t *), size_t cacheEntries=64) | |
| ~SourceSeekSampleTableStore () | |
| void | append (T) override |
| Appends one entry - call in file order, once per table entry. | |
| void | clear () override |
| Resets to empty - call once per begin()/track. | |
| T | get (size_t index) override |
| Reads back entry 'index' - index must be < size(). | |
| void | setNextEntryOffset (uint64_t fileOffset) override |
| void | setOnDiskEntrySize (size_t bytes) override |
| size_t | size () override |
| Number of entries appended so far. | |
Static Public Attributes | |
| static constexpr size_t | kMaxOnDiskSize = 16 |
Protected Member Functions | |
| void | refillCache (size_t index) |
Protected Attributes | |
| T * | cache |
| size_t | cache_capacity |
| size_t | cache_len = 0 |
| size_t | cache_start = 0 |
| size_t | count = 0 |
| T(* | decode )(const uint8_t *) |
| size_t | on_disk_size |
| SeekableSource * | p_source |
| uint8_t * | raw_buf |
| int64_t | start_offset = -1 |
Discards every value right after appending it, keeping only the file offset of the first entry - get() seeks back into the original MP4 source and re-reads the entry directly (entries are fixed-size and contiguous on disk, so entry N sits at start_offset + N*sizeof(T)), trading RAM for repeated small seeks.
Needs the original source to (a) support seek()/read() (a local File, not a live network stream) and (b) stay open for the lifetime of playback, since entries are re-read from it on demand while consuming 'mdat' - long after 'moov' parsing has moved past them.
MP4's on-disk integers are big-endian, and the box handlers already decode them field-by-field (readU32()/readU64()) rather than via a plain memcpy - get() has to reproduce that exact decoding independently later, since it can't call back into the box handler at playback time. A raw byte-for-byte copy into T would silently read wrong values on any little-endian host (i.e. virtually everything). 'decoder' plus 'onDiskSize' let the caller supply the same decoding logic and on-disk width the corresponding box handler used - onDiskSize matters because it isn't always sizeof(T): e.g. chunk offsets are always decoded into a uint64_t in memory, but are 4 bytes on disk for 'stco' vs. 8 for 'co64' - see setOnDiskEntrySize().
get() is called once per sample, per table, during 'mdat' playback, and real playback access is overwhelmingly sequential (index, then index+1, then index+2, ...) since samples are consumed in roughly presentation order per track. A naive one-seek-one-read-per-get() (the original implementation) turns that into a syscall pair per single 4-8 byte value, which measurably slowed real playback down on a desktop filesystem - so get() keeps a small read-ahead window (cacheEntries decoded values, default 64) instead: a cache miss reads a whole block of consecutive entries in one seek+read and decodes them all at once, so a sequential scan pays that cost only once per 'cacheEntries' calls rather than once per call. Backward/random access still works (a miss just re-centers the window on the new index) but won't benefit from the caching the way sequential access does.
|
inline |
'source' must wrap the same open file DemuxerMP4 is (directly or indirectly) being fed from, positioned so that byte 0 of the file corresponds to the very first byte ever passed to DemuxerMP4::write() - entry offsets are computed relative to that. 'decoder' converts 'onDiskSize' raw big-endian bytes into a T value. 'cacheEntries' is the read-ahead window size - see the class comment.
|
inline |
|
inlineoverridevirtual |
Appends one entry - call in file order, once per table entry.
Implements SampleTableStore< T >.
|
inlineoverridevirtual |
Resets to empty - call once per begin()/track.
Implements SampleTableStore< T >.
|
inlineoverridevirtual |
Reads back entry 'index' - index must be < size().
Implements SampleTableStore< T >.
|
inlineprotected |
|
inlineoverridevirtual |
Only meaningful for SourceSeekSampleTableStore: the absolute byte offset (from the start of the original MP4 file) of the entry about to be appended next. Default no-op - other implementations ignore it, so callers can always call this unconditionally before append() without needing to know which implementation is active.
Reimplemented from SampleTableStore< T >.
|
inlineoverridevirtual |
Overrides the on-disk entry width assumed by get() - needed only for tables whose on-disk width can differ from sizeof(T) (chunk_offsets: 4 bytes for 'stco', 8 for 'co64', always stored as uint64_t). Safe to call any time before the first get(); has no effect after.
Reimplemented from SampleTableStore< T >.
|
inlineoverridevirtual |
Number of entries appended so far.
Implements SampleTableStore< T >.
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
staticconstexpr |
Upper bound on on-disk entry width this store can decode - matches the widest entry actually used (chunk offsets: 8 bytes for 'co64'; stsc/stts: 8 bytes each) with headroom.
|
protected |
|
protected |
|
protected |
|
protected |