|
arduino-audio-tools
|
DemuxerMP4 extracts both the audio and (H.264) video track from a general, interleaved MP4/ISO-BMFF stream - unlike M4AAudioDemuxer/ M4AAudioFileDemuxer, which only handle a single (audio) track. More...
#include <ContainerMP4.h>
Classes | |
| struct | StscEntry |
| sample-to-chunk table entry (stsc) More... | |
| struct | SttsEntry |
| struct | Track |
| One track (audio or video) as found in 'moov'. More... | |
Public Types | |
| using | Codec = M4ACommonDemuxer::Codec |
Public Member Functions | |
| DemuxerMP4 () | |
| DemuxerMP4 (Print &video_out, Print &audio_out) | |
| DemuxerMP4 (SeekableSource &seekSource) | |
| DemuxerMP4 (SpoolStorageFactory &spoolFactory) | |
| DemuxerMP4 (VideoOutput &video_out, Print &audio_out) | |
| ~DemuxerMP4 () | |
| virtual void | addNotifyAudioChange (AudioInfoSupport &bi) |
| Adds target to be notified about audio changes. | |
| Vector< uint8_t > & | audioALACMagicCookie () |
| AudioInfo | audioInfo () override |
| provides the actual input AudioInfo | |
| virtual AudioInfo | audioInfoOut () |
| bool | begin () override |
| virtual bool | begin (AudioInfo info) override |
| virtual void | clearNotifyAudioChange () |
| Deletes all change notify subscriptions. | |
| void | end () override |
| AudioInfoFormat | getAudioInfo () override |
| Print * | getOutput () |
| VideoInfo | getVideoInfo () override |
| bool | isNotifyActive () |
| Checks if the automatic AudioInfo update is active. | |
| bool | isValid (const uint8_t *data, size_t len) override |
| const char * | mime () override |
| const char * | mimeVideo () override |
| Provides the container mime. | |
| operator bool () override | |
| bool | quickStart () |
| virtual bool | removeNotifyAudioChange (AudioInfoSupport &bi) |
| Removes a target in order not to be notified about audio changes. | |
| void | setAudioInfo (AudioInfo from) override |
| for most decoders this is not needed | |
| virtual bool | setCodecConfig (const uint8_t *data, size_t len) |
| Some decoders need e.g. a magic cookie to provide the relevant info for decoding. | |
| void | setNotifyActive (bool flag) |
| Deactivate/Reactivate automatic AudioInfo updates: (default is active) | |
| virtual void | setOutput (AudioOutput &out_stream) |
| Defines where the decoded result is written to. | |
| virtual void | setOutput (AudioStream &out_stream) |
| Defines where the decoded result is written to. | |
| void | setOutput (Print &out) override |
| void | setOutputAudio (Print &out) override |
| void | setOutputVideo (Print &out) override |
| void | setOutputVideo (VideoOutput &out) override |
| void | setSeekSource (SeekableSource &seekSource) |
| void | setSpoolStorageFactory (SpoolStorageFactory &spoolFactory) |
| VideoInfo | videoInfo () override |
| size_t | write (const uint8_t *data, size_t len) override |
Public Attributes | |
| int | id |
| custom id to be used by application | |
Protected Member Functions | |
| void | advanceChunkIfNeeded () |
| bool | beginBoxAccum (const MP4Parser::Box &box) |
| uint64_t | boxPayloadFileOffset () |
| void | configureSpoolStores (Track &t) |
| void | dispatchAudio (Track &t, const uint8_t *data, size_t size, bool isFirst) |
| void | dispatchSample (Track &t, const uint8_t *data, size_t size, bool isFirst) |
| void | dispatchVideo (Track &t, const uint8_t *data, size_t size, bool isFirst) |
| void | endBoxAccum () |
| Call once a box_accum-using handler has fully processed a complete box. | |
| void | feed (const uint8_t *data, size_t len) |
| void | flushVideo () |
| void | freeTracks () |
| void | notifyAudioChange (AudioInfo info) |
| void | onAc3 (const MP4Parser::Box &box) |
| void | onAlac (const MP4Parser::Box &box) |
| void | onAvc1 (const MP4Parser::Box &box) |
| void | onAvcC (const MP4Parser::Box &box) |
| void | onEsds (const MP4Parser::Box &box) |
| void | onHdlr (MP4Parser::Box &box) |
| void | onHevc (const MP4Parser::Box &box) |
| void | onMdat (MP4Parser::Box &box) |
| void | onMdhd (MP4Parser::Box &box) |
| void | onMp4a (const MP4Parser::Box &box) |
| void | onStco (MP4Parser::Box &box, bool is64) |
| void | onStsc (MP4Parser::Box &box) |
| void | onStsd (MP4Parser::Box &box) |
| void | onStsz (MP4Parser::Box &box) |
| void | onStts (MP4Parser::Box &box) |
| void | onTrak () |
| uint32_t | samplesInChunk (Track &t, uint32_t chunkIndex0) |
| void | sendWavHeader () |
| void | setSendWavHeader (bool flag) override |
| void | setupAudioInfo (AudioFormat format, const uint8_t *entryData, size_t entrySize) |
| void | setupParser () |
| void | writeBlocking (Print *out, uint8_t *data, size_t len) |
| void | writeVideo (uint8_t *data, size_t size) |
Static Protected Member Functions | |
| static StscEntry | readStscEntry (const uint8_t *p) |
| static SttsEntry | readSttsEntry (const uint8_t *p) |
| static uint16_t | readU16 (const uint8_t *p) |
| static uint32_t | readU32 (const uint8_t *p) |
| static uint64_t | readU32Widened (const uint8_t *p) |
| static uint64_t | readU64 (const uint8_t *p) |
| static void | writeAdtsHeader (uint8_t *adts, int aacProfile, int sampleRateIdx, int channelCfg, int frameLen) |
DemuxerMP4 extracts both the audio and (H.264) video track from a general, interleaved MP4/ISO-BMFF stream - unlike M4AAudioDemuxer/ M4AAudioFileDemuxer, which only handle a single (audio) track.
This is a streaming (forward-only) demuxer: it does not need a seekable source, so it works directly over e.g. an HTTP download (URLStream) or an SD file read sequentially with StreamCopy - the same way DemuxerAVI is used. It relies on the file being "faststart" muxed (moov before mdat, e.g. ffmpeg ... -movflags +faststart) - the same requirement MP4Parser and the M4A demuxers already document.
How the interleaving is resolved without seeking: once 'moov' has been fully parsed, every track's stsz (sample sizes) + stsc (sample-to-chunk)
| using Codec = M4ACommonDemuxer::Codec |
|
inline |
This class only demuxes - it does not decode audio itself. Point setOutputAudio() at an EncodedAudioStream (wrapping whatever AudioDecoder matches the track's codec - AAC/ALAC) if you need the audio track decoded; the raw payload (ADTS-wrapped for AAC, raw for ALAC) is written through as-is otherwise. For ALAC, the decoder also needs the magic cookie exposed via audioALACMagicCookie() - configure your decoder with it (e.g. AudioDecoder::setCodecConfig()) once it's available (after the audio track's 'stsd' has been parsed, i.e. once getAudioInfo().format != AudioFormat::UNKNOWN). Point setOutputVideo() at a Print (e.g. a VideoOutput) to receive the demuxed H.264 Annex-B video track; leave unset to ignore video.
|
inline |
|
inline |
|
inline |
'seekSource' wraps the same open file this instance will be fed from (e.g. via FileSeekableSource<File>(myFile)) - when set, newly discovered tracks' sample tables (stsz/stco/stsc/stts) are backed by SourceSeekSampleTableStore instead of the RAM-backed default, trading the ~4.5MB (for a feature-length file) RAM cost for small repeated seeks back into 'seekSource' during playback. Must stay valid and open for the lifetime of playback. See setSeekSource().
|
inline |
'spoolFactory' provides a fresh SpoolStorage for each of a track's four sample tables as tracks are discovered - when set, backs new tracks' sample tables with SpoolFileSampleTableStore instead of the RAM-backed default. Unlike the SeekableSource overload above, this needs no seekability in the original MP4 source at all (it writes its own local copy as data streams past), at the cost of real disk I/O for both the writes during 'moov' parsing and the seek+reads during 'mdat' playback. See setSpoolStorageFactory().
|
inline |
|
inlinevirtualinherited |
Adds target to be notified about audio changes.
Reimplemented in CodecNOP, EncodedAudioOutput, EncodedAudioStream, AACDecoderFDK, DecoderBasic, CodecChain, MP3DecoderHelix, MP3DecoderMAD, OggContainerDecoder, RTSPClient< TcpClient, UdpSocket >, Pipeline, and Pipeline::ModifyingStreamAdapter.
|
inlineprotected |
Picks whichever track's next not-yet-consumed chunk comes first in the file (by chunk_offset) and makes it the active chunk - an incremental two-way merge instead of precomputing a whole-file schedule: each track's own chunk_offsets are already ascending (a muxer writes each track's chunks in file order), so the merged cross-track order falls out of comparing just the next pending chunk_offset per track, in O(1) per call instead of materializing and sorting every chunk from every track upfront.
|
inline |
ALAC magic cookie (the 'alac' box payload, without its own size/type prefix) needed to configure an external ALAC decoder - empty unless getAudioInfo().format == AudioFormat::ALAC.
|
inlineoverridevirtualinherited |
provides the actual input AudioInfo
Implements AudioInfoSupport.
Reimplemented in CodecNOP, DecoderBasic, DSFDecoder, MP3DecoderHelix, MP3DecoderMAD, OpusAudioDecoder, OpusMultiStreamAudioDecoder, VorbisDecoder, WAVDecoder, WavIMADecoder, OggContainerDecoder, and VorbisDecoder.
|
inlinevirtualinherited |
provides the actual output AudioInfo: this is usually the same as audioInfo() unless we use a transforming stream
Reimplemented in MP3EncoderShine, PureDataStream, PWMAudioOutput< PWMDriverT >, ChannelFormatConverterStreamT< T >, ChannelFormatConverterStream, NumberFormatConverterStreamT< TFrom, TTo >, NumberFormatConverterStream, FormatConverterStream, Pipeline, ResampleStream, ResampleStreamT< TInterpolator >, and SupportedRatesStream.
|
inlineoverridevirtual |
Reimplemented from AudioDecoder.
|
inlineoverridevirtualinherited |
Reimplemented from AudioWriter.
Reimplemented in DecoderBasic, and OggContainerDecoder.
|
inlineprotected |
Call at the top of every box_accum-using handler. Returns true the first time it's called for a given box (i.e. right when accumulation starts), false on continuations - callers that need to (re)initialize per-box state (e.g. onStsz's stsz_header_pending) should key off that.
|
inlineprotected |
Absolute file offset of the first byte of this box's payload (box_accum's content), i.e. right after the box's own 8-byte size+type header - used, plus each table's own sub-header size on top, to tell a SourceSeekSampleTableStore where a table's first entry lives on disk.
Deliberately based on box_start_file_offset (captured once, at the box's first delivery - see beginBoxAccum()) rather than computed from total_bytes_received/box_accum.available() at entry-iteration time: MP4Parser advances its own internal fileOffset to the box's end as soon as an incremental box starts (see startIncrementalBox() in MP4Parser.h), before the box is actually fully delivered - a formula relying on "bytes received so far" at the time entries are iterated (which only happens on the box's last delivery) silently used the wrong file position for any box large enough to span multiple write() calls (which every sample table in a feature-length file is) - confirmed by hex-dumping the file at the computed offset and finding it landed inside 'mdat', nowhere near the actual table.
|
inlinevirtualinherited |
Deletes all change notify subscriptions.
Reimplemented in RTSPClient< TcpClient, UdpSocket >.
|
inlineprotected |
Swaps a track's four sample tables for spool-backed stores, one SpoolStorage per table obtained from p_spool_factory. Guarded so it only ever runs once per track even if onHdlr() somehow saw its kind change or got called again.
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineoverridevirtual |
Reimplemented from AudioDecoder.
|
inlineprotected |
Call once a box_accum-using handler has fully processed a complete box.
|
inlineprotected |
Feeds raw mdat bytes; accumulates until the current sample is complete, dispatches it, and moves on - possibly switching tracks at chunk boundaries per the incremental cross-track merge (see advanceChunkIfNeeded()).
|
inlineprotected |
|
inlineprotected |
|
inlineoverridevirtual |
Common audio info (sample_rate/channels/bits_per_sample), extended with the parsed codec format tag - the same AudioInfoFormat type is also provided by DemuxerAVI. Named getAudioInfo() rather than audioInfo() because AudioDecoder already declares a virtual AudioInfo audioInfo() (returning the plain, unextended type by value) - by-value virtual returns can't be covariantly widened in C++, so reusing that name for a subclass-returning version isn't possible (it would be a hard "invalid covariant return type" error, not silent hiding).
Implements Demuxer.
|
inlineinherited |
|
inlineoverridevirtual |
Common video info (width/height/format/frame_size/total_file_size), analogous to audioInfo() - the same VideoInfo type is also provided by DemuxerAVI. format is VideoFormat::H264 (the only video codec this class demuxes) once the video track's 'stsd' has been parsed, UNKNOWN before that or for an unsupported codec (e.g. HEVC). frame_size is always 0 (H264 frame size varies) - kept for symmetry with DemuxerAVI, whose raw pixel formats do have a fixed per-frame size. total_file_size has no declared-upfront equivalent in MP4 (no single header field states it, unlike AVI's RIFF header), so it reports the number of bytes received via write() so far instead - only equal to the true total once the whole stream has been fed in.
Implements Demuxer.
|
inlineinherited |
Checks if the automatic AudioInfo update is active.
|
inlineoverridevirtual |
True if data starts with an ISO base media 'ftyp' box (4-byte box size, then "ftyp" at offset 4) - the standard start of an MP4/M4A file - see Demuxer::isValid().
Reimplemented from Demuxer.
|
inlineoverridevirtual |
Provides the audio mime (AudioFormat::UNKNOWN/nullptr until the audio track's 'stsd' has been parsed - see getAudioInfo())
Implements Demuxer.
|
inlineoverridevirtual |
Provides the container mime.
Implements Demuxer.
|
inlineprotectedinherited |
|
inlineprotected |
AC-3 frames are self-synchronizing (each frame starts with its own 0x0B77 sync word and carries its own sample rate/channel info), so - unlike onMp4a/onAlac - there is no child config box to parse here.
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
'mdhd' (in 'mdia', scoped by the enclosing 'trak'): captures this track's own timescale (ticks/second), needed together with 'stts' to compute each sample's scheduled presentation time.
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
|
inlineprotected |
'stts' (time-to-sample): run-length list of {sample_count, sample_delta} - see getVideoInfo(), which derives fps from the first entry's sample_delta.
|
inlineprotected |
|
inlineoverridevirtual |
Implements AudioWriter.
|
inline |
Speeds up startup for a "non-faststart" MP4 (moov written after mdat) by locating and parsing 'moov' directly via seek()/readBytes() on the configured SeekableSource (setSeekSource()), instead of streaming through 'mdat' first to reach it. Only box headers (8 bytes each) are read - the payload is skipped via seek() - so this costs a handful of tiny reads regardless of 'mdat's size.
Call after begin(), before feeding data via write() - or just let FileSeekableSource call it automatically (its default behavior).
On success, leaves the SeekableSource positioned at 'mdat', ready for the normal write()-loop to resume there. Returns false (no side effects) if already faststart, no SeekableSource is configured, or a box form this scan doesn't understand (64-bit or "extends to EOF" size) is encountered.
|
inlinestaticprotected |
|
inlinestaticprotected |
|
inlinestaticprotected |
|
inlinestaticprotected |
|
inlinestaticprotected |
|
inlinestaticprotected |
|
inlinevirtualinherited |
Removes a target in order not to be notified about audio changes.
Reimplemented in RTSPClient< TcpClient, UdpSocket >.
|
inlineprotected |
Number of samples in the given (0-based) chunk index, per 'stsc'. Keeps a per-track cursor rather than rescanning 'stsc' from the start every call - correct as long as chunkIndex0 is non-decreasing across calls for a given track, which holds for the sequential per-chunk consumption in advanceChunkIfNeeded()/feed().
|
inlineprotected |
Synthesizes a valid WAV header (via the shared WAVHeader writer) from the parsed AudioInfo and sends it to p_output_audio, once, before any real audio payload - so a WAVDecoder-based output can bootstrap itself from it. Length is written as streamed/unknown, since the track's length is not known upfront while demuxing. Only ever relevant for a raw-PCM ('lpcm') audio track - not currently produced by this class (AAC/ALAC are the only codecs parsed), but kept symmetric with DemuxerAVI for when PCM tracks are added.
|
inlineoverridevirtualinherited |
for most decoders this is not needed
Implements AudioWriter.
Reimplemented in DecoderALAC, CodecChain, DSFDecoder, DecoderL8, OpusAudioDecoder, OpusMultiStreamAudioDecoder, CodecNOP, G729Decoder, and G7xxDecoder.
|
inlinevirtualinherited |
Some decoders need e.g. a magic cookie to provide the relevant info for decoding.
Reimplemented in DecoderALAC, and MultiDecoder.
|
inlineinherited |
Deactivate/Reactivate automatic AudioInfo updates: (default is active)
|
inlinevirtualinherited |
Defines where the decoded result is written to.
Reimplemented in ADTSDecoder, CodecChain, MTSDecoder, MTSDecoderTSDemux, and MetaDataFilterDecoder.
|
inlinevirtualinherited |
Defines where the decoded result is written to.
Reimplemented in ADTSDecoder, CodecChain, MTSDecoder, MTSDecoderTSDemux, and MetaDataFilterDecoder.
|
inlineoverridevirtual |
Satisfies the AudioWriter/AudioDecoder interface - needed so EncodedAudioOutput/EncodedAudioStream's polymorphic AudioDecoder* wiring (which calls setOutput() through that base class pointer) still reaches the audio output correctly; delegates to setOutputAudio().
Reimplemented from AudioDecoder.
|
inlineoverridevirtual |
Defines the audio output stream - e.g. an EncodedAudioStream wrapping an AudioDecoder that matches the audio track's codec, or any other Print if you want the raw payload as-is.
Implements Demuxer.
|
inlineoverridevirtual |
Defines the video output - e.g. a VideoOutput implementation, or any other Print if you want the raw payload as-is.
Implements Demuxer.
|
inlineoverridevirtual |
Defines the video output - the VideoOutput-specific counterpart of setOutputVideo(Print&), for a target like PacedVideoOutput/ OutputTinyGPU/OutputOpenCV that implements VideoOutput but not Print (the two are unrelated types - see VideoFrameMeter's own comment). Part of the base interface (unlike other concrete-class-specific extras) so code holding only a Demuxer& (e.g. VideoPlayer) can wire it to an arbitrary VideoOutput& without knowing the concrete container format - every current demuxer already implemented this identically before it was pulled up into the interface.
Implements Demuxer.
|
inline |
See the SeekableSource constructor overload above - can also be called any time before begin(), instead of via the constructor.
|
inlineoverrideprotectedvirtual |
Overrides the automatic decision of whether a synthetic WAV header is sent to the audio output before any audio payload. By default this is decided automatically from the parsed codec: on whenever isWavFormat() of the parsed format is true (raw PCM), off otherwise (AAC/ALAC, which have their own decoder and don't expect a WAV header). Call this to force it either way instead.
Implements Demuxer.
|
inline |
See the SpoolStorageFactory constructor overload above - can also be called any time before begin(), instead of via the constructor.
|
inlineprotected |
Populates the shared AudioInfo (channels/sample_rate/format/mime) from an AudioSampleEntry's fixed 28-byte header (ISO/IEC 14496-12 12.2.3): channelcount at rel-offset 16-17, samplerate (16.16 fixed point, upper 16 bits = Hz) at rel-offset 24-27 - the same layout for any audio sample entry (mp4a, alac, ...), so this is codec-agnostic.
|
inlineprotected |
|
inlineoverridevirtualinherited |
Fulfills VideoInfoSource::videoInfo(), so any Demuxer can be passed directly to e.g. OutputOpenCV::setVideoInfoSource() - note the reported 'format' is the container's codec (e.g. H264), not necessarily what ends up written to a raw-picture VideoOutput/Print (that depends on whatever decoder sits between the demuxer and the output); consumers that care about the decoded format should still set it explicitly and rely on this only for width/height.
Implements VideoInfoSource.
|
inlineoverridevirtual |
Partial-write contract (like AVIDecoder::write()): MP4Parser's own internal buffer has finite capacity, so a single call may accept fewer bytes than requested - the caller is expected to call write() again with the remainder, exactly like the underlying Print contract.
Implements AudioWriter.
|
inlinestaticprotected |
|
inlineprotectedinherited |
|
inlineprotected |
|
protected |
Codec format tag of the (first) audio track - AudioFormat::UNKNOWN until its 'stsd' has been parsed. Kept separately from AudioInfo (see AudioInfoFormat) since AudioInfo itself has no format field.
|
protected |
|
protected |
|
protected |
Absolute file offset of the current box_accum-accumulating box's own header - see beginBoxAccum().
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
track currently being parsed (scoped by 'trak')
|
protected |
|
inherited |
custom id to be used by application
|
protectedinherited |
|
protected |
|
protectedinherited |
|
protected |
|
protected |
|
protected |
scratch buffer for Annex-B conversion
|
protectedinherited |
|
protected |
first audio track found
|
protected |
|
protectedinherited |
|
protected |
Set via setSeekSource()/the SeekableSource constructor - when set, onTrak() backs new tracks' sample tables with SourceSeekSampleTableStore instead of the RAM-backed default.
|
protected |
Set via setSpoolStorageFactory()/the SpoolStorageFactory constructor
|
protected |
|
protected |
|
protected |
first video track found
|
protected |
|
protected |
Set by quickStart() on success, to 'mdat's declared size. -1 (the begin() default) means no boundary active - see write().
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
Bytes received via write() so far this stream - MP4 has no declared total-file-size field to report instead (see getVideoInfo()).