arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
M4AAudioFileDemuxer.h
1#pragma once
2
3#include <Arduino.h>
4#include <SD.h>
5
6#include "M4AAudioDemuxer.h"
7
8namespace audio_tools {
9
26 public:
28 using M4ACommonDemuxer::FrameCallback;
29
34
42
49 bool setDecoder(MultiDecoder& decoder) {
50 this->p_decoder = &decoder;
51 if (decoder.getOutput() == nullptr) {
52 LOGE("No output defined for MultiDecoder");
53 return false;
54 }
55 setCallback([&decoder](const Frame& frame, void* /*ref*/) {
56 LOGI("Decoding frame: %s with %d bytes", frame.mime, (int)frame.size);
57 if (!decoder.selectDecoder(frame.mime)) {
58 LOGE("Failed to select decoder for %s", frame.mime);
59 return;
60 }
61 decoder.write(frame.data, frame.size);
62 });
63 return true;
64 }
65
70 void setCallback(FrameCallback cb) override { frame_callback = cb; }
71
76 void setSamplesBufferSize(int size) {
77 stsz_bufsize = size / 4;
79 }
80
86 bool begin(File& file) {
87 M4ACommonDemuxer::begin();
88 this->p_file = &file;
89 if (!file) return false;
90 parser.begin();
91 end();
93 if (!parseFile()) return false;
94 if (!readStszHeader()) return false;
95 if (!checkMdat()) return false;
96 mdat_sample_pos = mdat_offset + mdat_pos;
97
98 return true;
99 }
100
104 void end() {
105 audio_config.codec = M4ACommonDemuxer::Codec::Unknown;
106 audio_config.alacMagicCookie.clear();
107 // resize(default_size);
108 sample_index = 0;
109 sample_count = 0;
110 mdat_pos = 0;
111 stsd_processed = false;
112 mdat_offset = 0;
113 mdat_size = 0;
114 stsz_offset = 0;
115 stsz_size = 0;
116 mdat_pos = 0;
118 }
119
126 bool copy() {
127 if (!p_file || sample_index >= sample_count) return false;
128 size_t currentSize = getNextSampleSize();
129 if (currentSize == 0) return false;
130 if (!p_file->seek(mdat_sample_pos)) return false;
131 if (buffer.size() < currentSize) buffer.resize(currentSize);
132 size_t bytesRead = p_file->read(buffer.data(), currentSize);
133 if (bytesRead != currentSize) return false;
134 buffer.setWritePos(bytesRead);
135 executeCallback(currentSize, buffer);
136 mdat_sample_pos += currentSize;
137 return true;
138 }
139
141 operator bool() { return sample_count > 0 && sample_index < sample_count; }
142
143 uint32_t sampleIndex() const { return sample_index; }
144
145 uint32_t size() const { return sample_count; }
146
147 uint32_t getMdatOffset() const { return mdat_offset; }
148
153 uint32_t getNextSampleSize() {
154 assert(p_file != nullptr);
155 if (sample_index >= sample_count) return 0;
156 uint32_t currentSize = 0;
157 if (fixed_sample_size) {
158 currentSize = fixed_sample_size;
159 } else {
160 // if buffer is empty, fill it again
161 if (stsz_buf.isEmpty()) {
162 uint64_t pos = stsz_offset + 20 + sample_index * 4;
163 if (!p_file->seek(pos)) return false;
164 stsz_buf.clear();
165 size_t read_bytes = p_file->read(
166 reinterpret_cast<uint8_t*>(stsz_buf.data()), stsz_bufsize * 4);
167 stsz_buf.setWritePos(read_bytes / 4);
168 if (stsz_buf.isEmpty()) return 0;
169 }
170 // provide next size
171 uint32_t val = 0;
172 if (!stsz_buf.read(val)) return 0;
173 currentSize = readU32(val);
174 }
175 sample_index++;
176 return currentSize;
177 }
178
192 void beginSampleSizeAccess(File* filePtr, uint32_t sampleCount,
193 uint32_t stszOffset) {
194 p_file = filePtr;
195 sample_index = 0;
196 sample_count = sampleCount;
197 stsz_offset = stszOffset;
198 }
199
208 bool parseFile() {
209 uint8_t buffer[1024];
210 p_file->seek(0);
211 while (p_file->available()) {
212 int to_read = min(sizeof(buffer), parser.availableForWrite());
213 size_t len = p_file->read(buffer, to_read);
214 parser.write(buffer, len);
215 // stop if we have all the data
216 if (stsd_processed && mdat_offset && stsz_offset) return true;
217 }
218 return false;
219 }
220
221 protected:
222 File* p_file = nullptr;
223 uint64_t mdat_offset = 0;
224 uint64_t mdat_size = 0;
225 uint64_t stsz_offset = 0;
226 uint64_t stsz_size = 0;
227 uint32_t sample_index = 0;
228 uint64_t mdat_pos = 0;
230 int stsz_bufsize = 256;
233 uint32_t fixed_sample_size = 0;
235 uint64_t mdat_sample_pos = 0;
239 void setupParser() override {
240 parser.setReference(this);
241
242 // Callback for ESDS box (AAC config)
244 "esds",
245 [](MP4Parser::Box& box, void* ref) {
246 static_cast<M4AAudioFileDemuxer*>(ref)->onEsds(box);
247 },
248 false);
249
250 // Callback for MP4A box (AAC sample entry)
252 "mp4a",
253 [](MP4Parser::Box& box, void* ref) {
254 static_cast<M4AAudioFileDemuxer*>(ref)->onMp4a(box);
255 },
256 false);
257
258 // Callback for ALAC box (ALAC sample entry)
260 "alac",
261 [](MP4Parser::Box& box, void* ref) {
262 static_cast<M4AAudioFileDemuxer*>(ref)->onAlac(box);
263 },
264 false);
265
266 // Callback for STSZ box (sample sizes)
268 "stsz",
269 [](MP4Parser::Box& box, void* ref) {
270 auto* self = static_cast<M4AAudioFileDemuxer*>(ref);
271 if (box.seq == 0) {
272 self->stsz_offset = box.file_offset;
273 self->stsz_size = box.size;
274 }
275 },
276 false);
277
278 // Callback for MDAT box (media data)
280 "mdat",
281 [](MP4Parser::Box& box, void* ref) {
282 auto* self = static_cast<M4AAudioFileDemuxer*>(ref);
283 if (box.seq == 0) {
284 self->mdat_offset = box.file_offset + 8; // skip box header
285 self->mdat_size = box.size;
286 }
287 },
288 false);
289
290 // Callback for STSD box (sample description)
292 "stsd",
293 [](MP4Parser::Box& box, void* ref) {
294 auto* self = static_cast<M4AAudioFileDemuxer*>(ref);
295 self->onStsd(box); // for aac and alac
296 self->stsd_processed = true;
297 },
298 false);
299 }
300
307 Frame frame = sampleExtractor.getFrame(size, buffer);
308 if (frame_callback)
309 frame_callback(frame, nullptr);
310 else
311 LOGW("No frame callback defined");
312 }
313
320 if (!p_file || stsz_offset == 0) return false;
321 uint8_t buffer[20];
322 if (!p_file->seek(stsz_offset)) return false;
323 if (p_file->read(buffer, 20) != 20) return false;
324 if (!checkType(buffer, "stsz", 4)) return false;
325 uint8_t* cont = buffer + 8;
326 fixed_sample_size = readU32(cont + 4);
327 sample_count = readU32(cont + 8);
328 stsz_processed = true;
329 return true;
330 }
331
332 bool checkMdat() {
333 p_file->seek(mdat_offset - 8);
334 uint8_t buffer[8];
335 if (p_file->read(buffer, 8) != 8) return false;
336 return checkType(buffer, "mdat", 4);
337 }
338};
339
340} // namespace audio_tools
void clear()
same as reset
Definition Buffers.h:95
Demuxer for M4A/MP4 files to extract audio data using an Arduino File. This class locates the mdat an...
Definition M4AAudioFileDemuxer.h:25
uint64_t mdat_size
Size of mdat box payload.
Definition M4AAudioFileDemuxer.h:224
uint32_t sample_index
Current sample index.
Definition M4AAudioFileDemuxer.h:227
uint64_t mdat_offset
Offset of mdat box payload.
Definition M4AAudioFileDemuxer.h:223
void setupParser() override
Sets up the MP4 parser and registers box callbacks.
Definition M4AAudioFileDemuxer.h:239
void setSamplesBufferSize(int size)
Sets the size of the samples buffer (in bytes).
Definition M4AAudioFileDemuxer.h:76
M4AAudioFileDemuxer()
Default constructor. Sets up parser callbacks.
Definition M4AAudioFileDemuxer.h:33
bool begin(File &file)
Open and parse the given file.
Definition M4AAudioFileDemuxer.h:86
uint64_t stsz_size
Size of stsz box.
Definition M4AAudioFileDemuxer.h:226
M4AAudioFileDemuxer(MultiDecoder &decoder)
Constructor with decoder.
Definition M4AAudioFileDemuxer.h:39
uint32_t getNextSampleSize()
Provides the next sample size (= frame size) from the stsz box queue.
Definition M4AAudioFileDemuxer.h:153
bool readStszHeader()
Reads the stsz header (sample count and fixed sample size) from the file.
Definition M4AAudioFileDemuxer.h:319
bool copy()
Copies the next audio frame from the file using the sample size table and mdat offset....
Definition M4AAudioFileDemuxer.h:126
void beginSampleSizeAccess(File *filePtr, uint32_t sampleCount, uint32_t stszOffset)
Initializes the demuxer for reading sample sizes from the stsz box.
Definition M4AAudioFileDemuxer.h:192
File * p_file
Pointer to the open file.
Definition M4AAudioFileDemuxer.h:222
bool parseFile()
Parses the file and feeds data to the parser until we have all the necessary data: 1) stsd box proces...
Definition M4AAudioFileDemuxer.h:208
bool setDecoder(MultiDecoder &decoder)
Sets the decoder to use for audio frames. Please note that calls setCallback() to register the decode...
Definition M4AAudioFileDemuxer.h:49
uint64_t mdat_pos
Current position in mdat box.
Definition M4AAudioFileDemuxer.h:228
MultiDecoder * p_decoder
Pointer to decoder.
Definition M4AAudioFileDemuxer.h:234
uint64_t stsz_offset
Offset of stsz box.
Definition M4AAudioFileDemuxer.h:225
SingleBuffer< uint8_t > buffer
Buffer for sample data.
Definition M4AAudioFileDemuxer.h:229
void end()
End demuxing and reset state.
Definition M4AAudioFileDemuxer.h:104
SingleBuffer< uint32_t > stsz_buf
Buffer for stsz sample sizes.
Definition M4AAudioFileDemuxer.h:231
void executeCallback(size_t size, SingleBuffer< uint8_t > &buffer)
Executes the callback for a completed frame.
Definition M4AAudioFileDemuxer.h:306
void setCallback(FrameCallback cb) override
Sets the callback for extracted audio frames.
Definition M4AAudioFileDemuxer.h:70
uint32_t fixed_sample_size
Fixed sample size (if nonzero)
Definition M4AAudioFileDemuxer.h:233
int stsz_bufsize
Number of sample sizes to buffer.
Definition M4AAudioFileDemuxer.h:230
Frame getFrame(size_t size, SingleBuffer< uint8_t > &buffer)
Constructs a Frame object for the current codec.
Definition M4ACommonDemuxer.h:243
Abstract base class for M4A/MP4 demuxers. Provides shared functionality for both file-based and strea...
Definition M4ACommonDemuxer.h:23
MP4Parser parser
Underlying MP4 parser.
Definition M4ACommonDemuxer.h:437
uint32_t sample_count
Number of samples in stsz.
Definition M4ACommonDemuxer.h:443
void onEsds(const MP4Parser::Box &box)
Handles the esds (Elementary Stream Descriptor) box.
Definition M4ACommonDemuxer.h:530
SampleExtractor sampleExtractor
Extractor for audio samples.
Definition M4ACommonDemuxer.h:435
void onAlac(const MP4Parser::Box &box)
Handles the alac box.
Definition M4ACommonDemuxer.h:585
void onMp4a(const MP4Parser::Box &box)
Handles the mp4a box.
Definition M4ACommonDemuxer.h:507
static uint32_t readU32(const uint8_t *p)
Reads a 32-bit big-endian unsigned integer from a buffer.
Definition M4ACommonDemuxer.h:453
bool stsz_processed
Marks the stsz table as processed.
Definition M4ACommonDemuxer.h:438
bool checkType(uint8_t *buffer, const char *type, int offset)
Checks if the buffer at the given offset matches the specified type.
Definition M4ACommonDemuxer.h:476
bool begin()
Initializes the parser.
Definition MP4Parser.h:107
void setCallback(BoxCallback cb)
Defines the generic callback for all boxes.
Definition MP4Parser.h:75
int availableForWrite()
Returns the available space for writing.
Definition MP4Parser.h:151
void setReference(void *ref)
Defines an optional reference. By default it is the parser itself.
Definition MP4Parser.h:69
size_t write(const uint8_t *data, size_t len)
Provide the data to the parser (in chunks if needed).
Definition MP4Parser.h:130
Manage multiple decoders: the actual decoder is only opened when it has been selected....
Definition MultiDecoder.h:21
bool selectDecoder(const char *mime)
Definition MultiDecoder.h:75
bool begin() override
Enables the automatic mime type determination.
Definition MultiDecoder.h:29
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:172
void setWritePos(int pos)
Updates the actual available data size.
Definition Buffers.h:304
bool read(T &result) override
reads a single value
Definition Buffers.h:211
bool resize(int size)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:292
T * data()
Provides address of actual data.
Definition Buffers.h:271
Arduino File support using std::fstream.
Definition VFSFile.h:33
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Definition M4ACommonDemuxer.h:27
Vector< uint8_t > alacMagicCookie
ALAC codec config.
Definition M4ACommonDemuxer.h:39
Codec codec
Current codec.
Definition M4ACommonDemuxer.h:35
Represents an individual box in the MP4 file.
Definition MP4Parser.h:33
uint64_t file_offset
File offset where box starts.
Definition MP4Parser.h:46
size_t size
Size of payload including subboxes (not including header)
Definition MP4Parser.h:43
size_t seq
Sequence number for the box per id.
Definition MP4Parser.h:38