Skip to content

Commit 4617605

Browse files
committed
MP4 comments
1 parent 6c0cb30 commit 4617605

File tree

2 files changed

+247
-67
lines changed

2 files changed

+247
-67
lines changed

src/AudioTools/AudioCodecs/MP4Parser.h

Lines changed: 150 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,66 +16,86 @@ namespace audio_tools {
1616
* @brief MP4Parser is a class that parses MP4 files and extracts boxes (atoms).
1717
* It provides a callback mechanism to process each box as it is parsed. You can
1818
* define specific callbacks for individual box types or use a generic callback
19-
* for the undefined boxes: By default is just prints the box information to
19+
* for the undefined boxes: By default it just prints the box information to
2020
* Serial.
2121
* If a container box contains data, it will be processed recursively and if it
2222
* contains data itself, it will be reported in a second callback call.
23-
* @Note This parser expects that the buffer size is largen then the biggest
23+
* @note This parser expects that the buffer size is larger than the biggest
2424
* box!
2525
* @ingroup codecs
2626
* @author Phil Schatzmann
2727
*/
28-
2928
class MP4Parser {
3029
public:
31-
/// An individual box in the MP4 file
30+
/**
31+
* @brief Represents an individual box in the MP4 file.
32+
*/
3233
struct Box {
33-
friend class MP4Parser; // Allow MP4Parser to access private members
34-
friend class MP4ParserExt; // Allow MP4Parser to access private members
35-
size_t id = 0;
36-
char type[5]; // 4-character box type
37-
const uint8_t* data = nullptr; // Pointer to box payload (not including header)
38-
size_t data_size = 0;
39-
size_t size = 0; // Size of payload (not including header)
40-
int level = 0; // Nesting depth
41-
uint64_t offset = 0; // File offset where box starts
42-
bool is_complete = false;
43-
bool is_container = false;
44-
45-
private:
46-
// SingleBuffer<uint8_t> data_buffer; // Buffer to hold box data if needed
34+
friend class MP4Parser; ///< Allow MP4Parser to access private members
35+
friend class MP4ParserExt; ///< Allow MP4ParserExt to access private members
36+
size_t id = 0; ///< Unique box ID
37+
char type[5]; ///< 4-character box type (null-terminated)
38+
const uint8_t* data = nullptr; ///< Pointer to box payload (not including header)
39+
size_t data_size = 0; ///< Size of payload (not including header)
40+
size_t size = 0; ///< Size of payload (not including header)
41+
int level = 0; ///< Nesting depth
42+
uint64_t offset = 0; ///< File offset where box starts
43+
bool is_complete = false; ///< True if the box data is complete
44+
bool is_container = false; ///< True if the box is a container
4745
};
4846

4947
using BoxCallback = std::function<void(Box&, void* ref)>;
5048

51-
/// Type specific callbacks
49+
/**
50+
* @brief Structure for type-specific callbacks.
51+
*/
5252
struct CallbackEntry {
53-
char type[5]; // 4-character box type
54-
BoxCallback cb; // Callback function
53+
char type[5]; ///< 4-character box type
54+
BoxCallback cb; ///< Callback function
55+
bool callGeneric = true; ///< If true, also call the generic callback after this one
5556
};
5657

57-
/// Defines an optional reference. By default it is the parser itself
58+
/**
59+
* @brief Defines an optional reference. By default it is the parser itself.
60+
* @param ref Pointer to reference object.
61+
*/
5862
void setReference(void* ref) { this->ref = ref; }
5963

60-
/// Defines the generic callback for all boxes
64+
/**
65+
* @brief Defines the generic callback for all boxes.
66+
* @param cb Callback function for all boxes.
67+
*/
6168
void setCallback(BoxCallback cb) { callback = cb; }
6269

63-
/// Defines a specific callback for a box type
64-
void setCallback(const char* type, BoxCallback cb) {
70+
/**
71+
* @brief Defines a specific callback for a box type.
72+
* @param type 4-character box type (e.g. "moov", "mdat").
73+
* @param cb Callback function for this box type.
74+
* @param callGeneric If true, the generic callback will also be called after the type-specific callback.
75+
*/
76+
void setCallback(const char* type, BoxCallback cb, bool callGeneric = true) {
6577
CallbackEntry entry;
6678
strncpy(entry.type, type, 4);
6779
entry.type[4] = '\0'; // Ensure null-termination
6880
entry.cb = cb;
81+
entry.callGeneric = callGeneric;
6982
callbacks.push_back(entry);
7083
};
7184

72-
/// Defines a specific buffer size
85+
/**
86+
* @brief Defines a specific buffer size.
87+
* @param size Buffer size in bytes.
88+
* @return true if the buffer was resized successfully.
89+
*/
7390
bool resize(size_t size) {
7491
buffer.resize(size);
7592
return buffer.size() == size;
7693
}
7794

78-
/// Initializes the parser
95+
/**
96+
* @brief Initializes the parser.
97+
* @return true on success.
98+
*/
7999
bool begin() {
80100
buffer.clear();
81101
if (buffer.size() == 0) buffer.resize(1024);
@@ -92,29 +112,52 @@ class MP4Parser {
92112
return true;
93113
}
94114

95-
/// Provide the data to the parser (in chunks if needed)
115+
/**
116+
* @brief Provide the data to the parser (in chunks if needed).
117+
* @param data Pointer to input data.
118+
* @param len Length of input data.
119+
* @return Number of bytes written to the buffer.
120+
*/
96121
size_t write(const uint8_t* data, size_t len) {
97122
if (is_error) return len; // If an error occurred, skip writing
98123
size_t result = buffer.writeArray(data, len);
99124
parse();
100125
return result;
101126
}
102127

103-
/// Provide the data to the parser (in chunks if needed)
128+
/**
129+
* @brief Provide the data to the parser (in chunks if needed).
130+
* @param data Pointer to input data (char*).
131+
* @param len Length of input data.
132+
* @return Number of bytes written to the buffer.
133+
*/
104134
size_t write(const char* data, size_t len) {
105135
return write(reinterpret_cast<const uint8_t*>(data), len);
106136
}
107137

138+
/**
139+
* @brief Returns the available space for writing.
140+
* @return Number of bytes available for writing.
141+
*/
108142
int availableForWrite() { return buffer.availableForWrite(); }
109143

110-
/// Adds a box name that will be interpreted as container
144+
/**
145+
* @brief Adds a box name that will be interpreted as a container.
146+
* @param name Name of the container box.
147+
* @param start Offset of child boxes (default 0).
148+
*/
111149
void addContainer(const char* name, int start = 0) {
112150
ContainerInfo info;
113151
info.name = name;
114152
info.start = start; // offset of child boxes
115153
}
116154

117-
/// trigger separate parsing (and callbacks) on the indicated string
155+
/**
156+
* @brief Trigger separate parsing (and callbacks) on the indicated string.
157+
* @param str Pointer to the string data.
158+
* @param len Length of the string data.
159+
* @return Number of bytes parsed.
160+
*/
118161
int parseString(const uint8_t* str, int len) {
119162
char type[5];
120163
int idx = 0;
@@ -136,25 +179,36 @@ class MP4Parser {
136179
}
137180

138181
protected:
139-
BoxCallback callback = defaultCallback;
140-
Vector<CallbackEntry> callbacks;
141-
SingleBuffer<uint8_t> buffer;
142-
Vector<size_t> levelStack;
143-
size_t parseOffset = 0;
144-
uint64_t fileOffset = 0;
145-
void* ref = this;
146-
Box box;
147-
bool is_error = false;
182+
BoxCallback callback = defaultCallback; ///< Generic callback for all boxes
183+
Vector<CallbackEntry> callbacks; ///< List of type-specific callbacks
184+
SingleBuffer<uint8_t> buffer; ///< Buffer for incoming data
185+
Vector<size_t> levelStack; ///< Stack for container box levels
186+
size_t parseOffset = 0; ///< Current parse offset in buffer
187+
uint64_t fileOffset = 0; ///< Current file offset
188+
void* ref = this; ///< Reference pointer for callbacks
189+
Box box; ///< Current box being processed
190+
bool is_error = false; ///< True if an error occurred
191+
192+
/**
193+
* @brief Structure for container box information.
194+
*/
148195
struct ContainerInfo {
149-
const char* name = nullptr;
150-
int start = 0;
196+
const char* name = nullptr; ///< Name of the container box
197+
int start = 0; ///< Offset of child boxes
151198
};
152-
Vector<ContainerInfo> containers;
199+
Vector<ContainerInfo> containers; ///< List of container box info
153200

154-
/// Returns the current file offset (absolute position in file)
201+
/**
202+
* @brief Returns the current file offset (absolute position in file).
203+
* @return Current file offset.
204+
*/
155205
uint64_t currentFileOffset() { return fileOffset + parseOffset; }
156206

157-
// Default callback that prints box information to Serial
207+
/**
208+
* @brief Default callback that prints box information to Serial.
209+
* @param box The box being processed.
210+
* @param ref Optional reference pointer.
211+
*/
158212
static void defaultCallback(const Box& box, void* ref) {
159213
char space[box.level * 2 + 1];
160214
memset(space, ' ', box.level * 2);
@@ -172,13 +226,27 @@ class MP4Parser {
172226
#endif
173227
}
174228

229+
/**
230+
* @brief Reads a 32-bit big-endian unsigned integer from a buffer.
231+
* @param p Pointer to buffer.
232+
* @return 32-bit unsigned integer.
233+
*/
175234
static uint32_t readU32(const uint8_t* p) {
176235
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
177236
}
178237

238+
/**
239+
* @brief Reads a 64-bit big-endian unsigned integer from a buffer.
240+
* @param p Pointer to buffer.
241+
* @return 64-bit unsigned integer.
242+
*/
179243
static uint64_t readU64(const uint8_t* p) {
180244
return ((uint64_t)readU32(p) << 32) | readU32(p + 4);
181245
}
246+
247+
/**
248+
* @brief Main parsing loop. Handles parsing of boxes in the buffer.
249+
*/
182250
virtual void parse() {
183251
while (true) {
184252
size_t bufferSize = buffer.available();
@@ -253,6 +321,9 @@ class MP4Parser {
253321
}
254322
}
255323

324+
/**
325+
* @brief Pops levels from the stack if we've passed their bounds.
326+
*/
256327
void popLevels() {
257328
// Pop levels if we've passed their bounds (absolute file offset)
258329
while (!levelStack.empty() &&
@@ -261,18 +332,30 @@ class MP4Parser {
261332
}
262333
}
263334

335+
/**
336+
* @brief Processes the callback for a box.
337+
* Calls the type-specific callback if present, and the generic callback if allowed.
338+
* @param box The box being processed.
339+
*/
264340
void processCallback(Box& box) {
265341
bool is_called = false;
342+
bool call_generic = true;
266343
for (const auto& entry : callbacks) {
267344
if (strncmp(entry.type, box.type, 4) == 0) {
268345
entry.cb(box, ref);
269346
is_called = true;
347+
if (!entry.callGeneric) call_generic = false;
270348
}
271349
}
272-
/// call generic callback
273-
if (!is_called) callback(box, ref);
350+
/// call generic callback if allowed
351+
if ((!is_called || call_generic) && callback) callback(box, ref);
274352
}
275353

354+
/**
355+
* @brief Checks if a box type is a container box.
356+
* @param type Box type string.
357+
* @return true if container box, false otherwise.
358+
*/
276359
bool isContainerBox(const char* type) {
277360
// fill with default values if nothing has been defined
278361
if (containers.empty()) {
@@ -294,27 +377,47 @@ class MP4Parser {
294377
return false;
295378
}
296379

380+
/**
381+
* @brief Gets the start offset for a subcontainer.
382+
* @param type Box type string.
383+
* @return Offset of the subcontainer.
384+
*/
297385
int getSubcontainerStart(const char* type) {
298386
for (auto& cont : containers) {
299387
if (StrView(type) == cont.name) return cont.start;
300388
}
301389
return 0;
302390
}
303391

392+
/**
393+
* @brief Checks if a box type is a persisted box.
394+
* @param type Box type string.
395+
* @return true if persisted, false otherwise.
396+
*/
304397
bool isPersistedBox(const char* type) const {
305398
static const char* persisted[] = {"stsz"};
306399
for (const char* p : persisted)
307400
if (StrView(type) == p) return true;
308401
return false;
309402
}
310403

404+
/**
405+
* @brief Checks if a type string is a valid 4-character box type.
406+
* @param type Pointer to type string.
407+
* @param offset Offset in the string.
408+
* @return true if valid, false otherwise.
409+
*/
311410
bool isValidType(const char* type, int offset=0) const {
312411
// Check if the type is a valid 4-character string
313412
return (type != nullptr &&
314413
isalnum(type[offset]) && isalnum(type[offset+1]) &&
315414
isalnum(type[offset+2]) && isalnum(type[offset+3]));
316415
}
317416

417+
/**
418+
* @brief Checks and adjusts the parse offset for valid box types.
419+
* @return Adjusted parse offset.
420+
*/
318421
size_t checkParseOffset() {
319422
size_t current = parseOffset;
320423
const char* type = (char*)(buffer.data() + parseOffset + 4);

0 commit comments

Comments
 (0)