arduino-audio-tools
Loading...
Searching...
No Matches
Public Member Functions | Static Public Attributes | Protected Member Functions | Protected Attributes | List of all members
SourceSeekSampleTableStore< T > Class Template Reference

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>

Inheritance diagram for SourceSeekSampleTableStore< T >:
SampleTableStore< T >

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.
 
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
 
SeekableSourcep_source
 
uint8_t * raw_buf
 
int64_t start_offset = -1
 

Detailed Description

template<typename T>
class audio_tools::SourceSeekSampleTableStore< T >

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.

Constructor & Destructor Documentation

◆ SourceSeekSampleTableStore()

template<typename T >
SourceSeekSampleTableStore ( SeekableSource source,
size_t  onDiskSize,
T(*)(const uint8_t *)  decoder,
size_t  cacheEntries = 64 
)
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.

◆ ~SourceSeekSampleTableStore()

template<typename T >
~SourceSeekSampleTableStore ( )
inline

Member Function Documentation

◆ append()

template<typename T >
void append ( value)
inlineoverridevirtual

Appends one entry - call in file order, once per table entry.

Implements SampleTableStore< T >.

◆ clear()

template<typename T >
void clear ( )
inlineoverridevirtual

Resets to empty - call once per begin()/track.

Implements SampleTableStore< T >.

◆ get()

template<typename T >
T get ( size_t  index)
inlineoverridevirtual

Reads back entry 'index' - index must be < size().

Implements SampleTableStore< T >.

◆ refillCache()

template<typename T >
void refillCache ( size_t  index)
inlineprotected

◆ setNextEntryOffset()

template<typename T >
void setNextEntryOffset ( uint64_t  fileOffset)
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 >.

◆ setOnDiskEntrySize()

template<typename T >
void setOnDiskEntrySize ( size_t  bytes)
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 >.

◆ size()

template<typename T >
size_t size ( )
inlineoverridevirtual

Number of entries appended so far.

Implements SampleTableStore< T >.

Member Data Documentation

◆ cache

template<typename T >
T* cache
protected

◆ cache_capacity

template<typename T >
size_t cache_capacity
protected

◆ cache_len

template<typename T >
size_t cache_len = 0
protected

◆ cache_start

template<typename T >
size_t cache_start = 0
protected

◆ count

template<typename T >
size_t count = 0
protected

◆ decode

template<typename T >
T(* decode) (const uint8_t *)
protected

◆ kMaxOnDiskSize

template<typename T >
constexpr size_t kMaxOnDiskSize = 16
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.

◆ on_disk_size

template<typename T >
size_t on_disk_size
protected

◆ p_source

template<typename T >
SeekableSource* p_source
protected

◆ raw_buf

template<typename T >
uint8_t* raw_buf
protected

◆ start_offset

template<typename T >
int64_t start_offset = -1
protected

The documentation for this class was generated from the following file: