Skip to content

Commit 317eedf

Browse files
committed
MP4 Parser: Simplified API
1 parent 06863ab commit 317eedf

File tree

17 files changed

+980
-853
lines changed

17 files changed

+980
-853
lines changed

src/AudioTools/AudioCodecs/AudioCodecsBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ class AudioDecoder : public AudioWriter, public AudioInfoSource {
2828
void setAudioInfo(AudioInfo from) override {
2929
TRACED();
3030
if (info != from) {
31+
info = from;
3132
notifyAudioChange(from);
3233
}
33-
info = from;
3434
}
3535
/// Defines where the decoded result is written to
3636
virtual void setOutput(AudioStream &out_stream) {

src/AudioTools/AudioCodecs/ContainerM4A.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ class ContainerM4A : public ContainerDecoder {
3737
* @param out_stream Output AudioStream.
3838
*/
3939
void setOutput(Print& out_stream) override {
40-
if (p_decoder != nullptr) p_decoder->setOutput(out_stream);
40+
if (p_decoder != nullptr && p_decoder->getOutput()!=&out_stream) {
41+
p_decoder->setOutput(out_stream);
42+
}
4143
ContainerDecoder::setOutput(out_stream);
4244
}
4345

@@ -115,6 +117,10 @@ class ContainerM4A : public ContainerDecoder {
115117
return true;
116118
}
117119

120+
M4AAudioDemuxer& getDemuxer() {
121+
return demux;
122+
}
123+
118124
protected:
119125
bool is_active = false; ///< True if demuxer is active.
120126
bool is_magic_cookie_processed =
@@ -135,7 +141,7 @@ class ContainerM4A : public ContainerDecoder {
135141
return;
136142
}
137143
MultiDecoder& dec = *(self->p_decoder);
138-
const char* current_mime = dec.selectedMime();
144+
const char* old_mime = dec.selectedMime();
139145

140146
// select decoder based on mime type
141147
if (!dec.selectDecoder(frame.mime)) {
@@ -158,9 +164,10 @@ class ContainerM4A : public ContainerDecoder {
158164
}
159165
// write encoded data to decoder
160166
dec.write(frame.data, frame.size);
167+
168+
// restore previous decoder
169+
dec.selectDecoder(old_mime);
161170

162-
// restore the original mime type
163-
dec.selectDecoder(current_mime);
164171
}
165172
};
166173

src/AudioTools/AudioCodecs/M4AAudioDemuxer.h

Lines changed: 32 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class M4AAudioDemuxer : public M4ACommonDemuxer {
2828
* @brief Initializes the demuxer and resets state.
2929
*/
3030
bool begin() {
31-
codec = Codec::Unknown;
32-
alacMagicCookie.clear();
31+
audio_config.codec = Codec::Unknown;
32+
audio_config.alacMagicCookie.clear();
3333
resize(default_size);
3434

3535
stsz_processed = false;
@@ -58,51 +58,32 @@ class M4AAudioDemuxer : public M4ACommonDemuxer {
5858
* @brief Returns the ALAC magic cookie (codec config).
5959
* @return Reference to the ALAC magic cookie vector.
6060
*/
61-
Vector<uint8_t>& getALACMagicCookie() { return alacMagicCookie; }
61+
Vector<uint8_t>& getALACMagicCookie() { return audio_config.alacMagicCookie; }
6262

6363
/**
6464
* @brief Sets a reference pointer for callbacks.
6565
* @param ref Reference pointer.
6666
*/
6767
void setReference(void* ref) { this->ref = ref; }
6868

69-
/**
70-
* @brief Resizes the internal buffer.
71-
* @param size New buffer size.
72-
*/
73-
void resize(int size) {
74-
default_size = size;
75-
if (buffer.size() < size) {
76-
buffer.resize(size);
77-
}
69+
void copyFrom(M4ACommonDemuxer& source) {
70+
audio_config = source.getM4AAudioConfig();
7871
}
7972

8073
protected:
81-
SingleBuffer<uint8_t> buffer; ///< Buffer for incremental data.
82-
void* ref = nullptr; ///< Reference pointer for callbacks.
83-
size_t default_size = 2 * 1024; ///< Default buffer size.
74+
void* ref = nullptr; ///< Reference pointer for callbacks.
8475

8576
/**
8677
* @brief Setup all parser callbacks
8778
*/
88-
virtual void setupParser() {
79+
void setupParser() override {
8980
// global box data callback to get sizes
9081
parser.setReference(this);
91-
parser.setCallback(boxDataSetupCallback);
9282

93-
// incremental data callback
94-
parser.setIncrementalDataCallback(incrementalBoxDataCallback);
95-
96-
// Register a specific incremental data callback for mdat
97-
parser.setIncrementalDataCallback(
98-
"mdat",
99-
[](MP4Parser::Box& box, const uint8_t* data, size_t len, bool is_final,
100-
void* ref) {
101-
auto* self = static_cast<M4AAudioDemuxer*>(ref);
102-
LOGI("*Box: %s, size: %u bytes", box.type, (unsigned)len);
103-
self->sampleExtractor.write(data, len, is_final);
104-
},
105-
false);
83+
// parsing for content of stsd (Sample Description Box)
84+
parser.setCallback("stsd", [](MP4Parser::Box& box, void* ref) {
85+
static_cast<M4AAudioDemuxer*>(ref)->onStsd(box);
86+
});
10687

10788
// parsing for content of stsd (Sample Description Box)
10889
parser.setCallback("esds", [](MP4Parser::Box& box, void* ref) {
@@ -114,107 +95,37 @@ class M4AAudioDemuxer : public M4ACommonDemuxer {
11495
parser.setCallback("alac", [](MP4Parser::Box& box, void* ref) {
11596
static_cast<M4AAudioDemuxer*>(ref)->onAlac(box);
11697
});
98+
99+
// mdat
117100
parser.setCallback(
118101
"mdat",
119102
[](MP4Parser::Box& box, void* ref) {
120103
M4AAudioDemuxer& self = *static_cast<M4AAudioDemuxer*>(ref);
121104
// mdat must not be buffered
122-
LOGI("Box: %s, size: %u bytes", box.type, (unsigned)box.size);
123-
self.sampleExtractor.setMaxSize(box.size);
105+
LOGI("#%d Box: %s, size: %u of %u bytes", (unsigned) box.seq, box.type,(unsigned) box.available, (unsigned)box.size);
106+
if (box.seq == 0) self.sampleExtractor.setMaxSize(box.size);
107+
size_t written = self.sampleExtractor.write(box.data, box.available, box.is_complete);
108+
assert(written == box.available);
124109
},
125110
false); // 'false' prevents the generic callback from being executed
126-
}
127-
128-
/**
129-
* @brief Checks if a box type is relevant for audio demuxing.
130-
* @param type Box type string.
131-
* @return True if relevant, false otherwise.
132-
*/
133-
static bool isRelevantBox(const char* type) {
134-
// Check if the box is relevant for audio demuxing
135-
return (StrView(type) == "stsd" || StrView(type) == "stsz" ||
136-
StrView(type) == "stco");
137-
}
138-
139-
/**
140-
* @brief Callback for box data setup. If we contain data we add
141-
* it to the buffer. If there is no data we set up the buffer to
142-
* receive incremental data.
143-
* @param box MP4 box.
144-
* @param ref Reference pointer.
145-
*/
146-
static void boxDataSetupCallback(MP4Parser::Box& box, void* ref) {
147-
M4AAudioDemuxer& self = *static_cast<M4AAudioDemuxer*>(ref);
148-
149-
bool is_relevant = isRelevantBox(box.type);
150-
if (is_relevant) {
151-
LOGI("Box: %s, size: %u bytes", box.type, (unsigned)box.size);
152-
if (box.data_size == 0) {
153-
// setup for incremental processing
154-
self.resize(box.size);
155-
self.buffer.clear();
156-
} else {
157-
// we have the complete box data
158-
self.processBox(box);
159-
}
160-
}
161-
}
162111

163-
/**
164-
* @brief Callback for incremental box data.
165-
* @param box MP4 box.
166-
* @param data Pointer to data.
167-
* @param len Length of data.
168-
* @param is_final True if this is the last chunk.
169-
* @param ref Reference pointer.
170-
*/
171-
static void incrementalBoxDataCallback(MP4Parser::Box& box,
172-
const uint8_t* data, size_t len,
173-
bool is_final, void* ref) {
174-
M4AAudioDemuxer& self = *static_cast<M4AAudioDemuxer*>(ref);
175-
176-
// mdat is now handled by its specific incremental callback, so remove logic
177-
// here
178-
179-
// only process relevant boxes
180-
if (!isRelevantBox(box.type)) return;
181-
182-
LOGI("*Box: %s, size: %u bytes", box.type, (unsigned)len);
183-
184-
// others fill buffer incrementally
185-
if (len > 0) {
186-
size_t written = self.buffer.writeArray(data, len);
187-
if (written != len) {
188-
LOGE("Failed to write all data to buffer, written: %zu, expected: %zu",
189-
written, len);
190-
}
191-
}
192-
193-
// on last chunk, call the specific box handler
194-
if (is_final) {
195-
MP4Parser::Box complete_box = box;
196-
complete_box.data = self.buffer.data();
197-
complete_box.data_size = self.buffer.size();
198-
self.processBox(complete_box);
199-
// The buffer might be quite large, so we resize it to the default size
200-
self.resize(self.default_size);
201-
}
202-
}
112+
// stsz
113+
parser.setCallback(
114+
"stsz",
115+
[](MP4Parser::Box& box, void* ref) {
116+
M4AAudioDemuxer& self = *static_cast<M4AAudioDemuxer*>(ref);
117+
self.onStsz(box);
118+
},
119+
false); // 'false' prevents the generic callback from being executed
203120

204-
/**
205-
* @brief Processes a parsed MP4 box.
206-
* @param box MP4 box.
207-
*/
208-
void processBox(MP4Parser::Box& box) {
209-
if (StrView(box.type) == "stsd") {
210-
onStsd(box);
211-
} else if (StrView(box.type) == "stsz") {
212-
onStsz(box);
213-
} else if (StrView(box.type) == "stco") {
214-
// onStco(box); // currently not supported
215-
}
121+
// parser.setCallback(
122+
// "stco",
123+
// [](MP4Parser::Box& box, void* ref) {
124+
// M4AAudioDemuxer& self = *static_cast<M4AAudioDemuxer*>(ref);
125+
// self.onStco(box);
126+
// },
127+
// false); // 'false' prevents the generic callback from being executed
216128
}
217-
218129
};
219130

220131
} // namespace audio_tools

0 commit comments

Comments
 (0)