Skip to content

Commit fc3ace3

Browse files
committed
MultiDecoder DRAFT
1 parent af390d3 commit fc3ace3

File tree

4 files changed

+187
-44
lines changed

4 files changed

+187
-44
lines changed

src/AudioTools/AudioCodecs/AudioCodecs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@
2525
#include "AudioTools/AudioCodecs/CodecFloat.h"
2626
#include "AudioTools/AudioCodecs/CodecBase64.h"
2727
#include "AudioTools/AudioCodecs/DecoderFromStreaming.h"
28+
#include "AudioTools/AudioCodecs/MultiDecoder.h"
2829

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
#pragma once
3+
4+
#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
5+
#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
6+
#include "AudioTools/CoreAudio/MimeDetector.h"
7+
8+
namespace audio_tools {
9+
10+
/**
11+
* @brief Manage multiple decoders: the actual decoder is only opened when it
12+
* has been selected.
13+
* @ingroup codecs
14+
* @ingroup decoder
15+
* @author Phil Schatzmann
16+
* @copyright GPLv3
17+
*/
18+
class MultiDecoder : public AudioDecoder {
19+
//
20+
bool begin() {
21+
mime_detector.begin();
22+
is_first = true;
23+
return true;
24+
}
25+
26+
/// closes the actual decoder
27+
void end() {
28+
if (actual_decoder.decoder != nullptr && actual_decoder.is_open) {
29+
actual_decoder.decoder->end();
30+
}
31+
actual_decoder.is_open = false;
32+
actual_decoder.decoder = nullptr;
33+
actual_decoder.mime = nullptr;
34+
is_first = true;
35+
}
36+
37+
/// Adds a decoder that will be selected by it's mime type
38+
void addDecoder(AudioDecoder& decoder, const char* mime) {
39+
DecoderInfo info{mime, &decoder};
40+
decoders.push_back(info);
41+
}
42+
43+
// selects the actual decoder by mime type
44+
bool selectDecoder(const char* mime) {
45+
bool result = false;
46+
// do nothing if no change
47+
if (StrView(mime).equals(actual_decoder.mime)) {
48+
return true;
49+
}
50+
// close actual decoder
51+
end();
52+
53+
// find the corresponding decoder
54+
for (int j = 0; j < decoders.size(); j++) {
55+
DecoderInfo info = decoders[j];
56+
if (StrView(info.mime).equalsIgnoreCase(mime)) {
57+
LOGI("New decoder found for %d", info.mime);
58+
actual_decoder = info;
59+
actual_decoder.decoder->begin();
60+
result = true;
61+
}
62+
}
63+
return result;
64+
}
65+
66+
virtual size_t write(const uint8_t* data, size_t len) {
67+
if (is_first) {
68+
// select the decoder based on the detemined mime type
69+
mime_detector.write((uint8_t*)data, len);
70+
const char* mime = mime_detector.mime();
71+
if (mime != nullptr) {
72+
if (!selectDecoder(mime)) {
73+
LOGE("The decoder could not be found for %s", mime);
74+
}
75+
}
76+
is_first = false;
77+
}
78+
// check if we have a decoder
79+
if (actual_decoder.decoder == nullptr) return 0;
80+
// decode the data
81+
return actual_decoder.decoder->write(data, len);
82+
};
83+
84+
protected:
85+
struct DecoderInfo {
86+
const char* mime = nullptr;
87+
AudioDecoder* decoder = nullptr;
88+
bool is_open = false;
89+
} actual_decoder;
90+
Vector<DecoderInfo> decoders{0};
91+
MimeDetector mime_detector;
92+
bool is_first = true;
93+
};
94+
95+
} // namespace audio_tools
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#pragma once
2+
3+
namespace audio_tools {
4+
5+
/**
6+
* @brief Logic to detemine the mime type from the content. You can register
7+
* your own custom detection logic
8+
* @ingroup codecs
9+
* @ingroup decoder
10+
* @author Phil Schatzmann
11+
* @copyright GPLv3
12+
*/
13+
14+
class MimeDetector {
15+
public:
16+
bool begin() {
17+
is_first = true;
18+
return true;
19+
}
20+
21+
size_t write(uint8_t* data, size_t len) {
22+
determineMime(data, len);
23+
return len;
24+
}
25+
26+
/// Defines the mime detector
27+
void setMimeDetector(const char* (*mimeDetectCallback)(uint8_t* data,
28+
size_t len)) {
29+
this->mimeDetectCallback = mimeDetectCallback;
30+
}
31+
32+
/// Define the callback that will notify about mime changes
33+
void setMimeCallback(void (*callback)(const char*)) {
34+
TRACED();
35+
this->notifyMimeCallback = callback;
36+
}
37+
38+
/// Provides the actual mime type, that was determined from the first
39+
/// available data
40+
const char* mime() { return actual_mime; }
41+
42+
protected:
43+
bool is_first = false;
44+
const char* actual_mime = nullptr;
45+
void (*notifyMimeCallback)(const char* mime) = nullptr;
46+
const char* (*mimeDetectCallback)(uint8_t* data,
47+
size_t len) = defaultMimeDetector;
48+
49+
/// Update the mime type
50+
void determineMime(void* data, size_t len) {
51+
if (is_first) {
52+
actual_mime = mimeDetectCallback((uint8_t*)data, len);
53+
if (notifyMimeCallback != nullptr && actual_mime != nullptr) {
54+
notifyMimeCallback(actual_mime);
55+
}
56+
is_first = false;
57+
}
58+
}
59+
60+
/// Default logic which supports aac, mp3, wav and ogg
61+
static const char* defaultMimeDetector(uint8_t* data, size_t len) {
62+
const char* mime = nullptr;
63+
if (len > 4) {
64+
const uint8_t* start = (const uint8_t*)data;
65+
if (start[0] == 0xFF && start[1] == 0xF1) {
66+
mime = "audio/aac";
67+
} else if (memcmp(start, "ID3", 3) || start[0] == 0xFF ||
68+
start[0] == 0xFE) {
69+
mime = "audio/mpeg";
70+
} else if (memcmp(start, "RIFF", 4)) {
71+
mime = "audio/vnd.wave";
72+
} else if (memcmp(start, "OggS", 4)) {
73+
mime = "audio/ogg";
74+
}
75+
}
76+
return mime;
77+
}
78+
};
79+
80+
} // namespace audio_tools

src/AudioTools/CoreAudio/StreamCopy.h

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "AudioTools/CoreAudio/BaseConverter.h"
77
#include "AudioTools/CoreAudio/AudioLogger.h"
88
#include "AudioTools/CoreAudio/AudioStreams.h"
9+
#include "AudioTools/CoreAudio/MimeDetector.h"
910

1011
#define NOT_ENOUGH_MEMORY_MSG "Could not allocate enough memory: %d bytes"
1112

@@ -48,7 +49,7 @@ class StreamCopyT {
4849
/// (Re)starts the processing
4950
void begin(){
5051
TRACED();
51-
is_first = true;
52+
mime_detector.begin();
5253
resize(buffer_size);
5354
if (buffer){
5455
LOGI("buffer_size=%d",buffer_size);
@@ -153,7 +154,7 @@ class StreamCopyT {
153154
}
154155

155156
// determine mime
156-
determineMime(buffer.data(), bytes_to_read);
157+
mime_detector.write(buffer.data(), bytes_to_read);
157158

158159
// convert data
159160
if (p_converter!=nullptr) p_converter->convert((uint8_t*)buffer.data(), bytes_read );
@@ -256,13 +257,7 @@ class StreamCopyT {
256257

257258
/// Provides the actual mime type, that was determined from the first available data
258259
const char* mime() {
259-
return actual_mime;
260-
}
261-
262-
/// Define the callback that will notify about mime changes
263-
void setMimeCallback(void (*callback)(const char*)){
264-
TRACED();
265-
this->notifyMimeCallback = callback;
260+
return mime_detector.mime();
266261
}
267262

268263
/// Defines a callback that is notified with the wirtten data
@@ -352,9 +347,14 @@ class StreamCopyT {
352347
is_sync_audio_info = active;
353348
}
354349

350+
/// Define the callback that will notify about mime changes
351+
void setMimeCallback(void (*callback)(const char*)){
352+
mime_detector.setMimeCallback(callback);
353+
}
354+
355355
/// Defines the mime detector
356356
void setMimeDetector(const char* (*mimeDetectCallback)(uint8_t* data, size_t len)){
357-
this->mimeDetectCallback = mimeDetectCallback;
357+
mime_detector.setMimeDetector(mimeDetectCallback);
358358
}
359359

360360
protected:
@@ -364,14 +364,10 @@ class StreamCopyT {
364364
Vector<uint8_t> buffer{0};
365365
int buffer_size = DEFAULT_BUFFER_SIZE;
366366
void (*onWrite)(void*obj, void*buffer, size_t len) = nullptr;
367-
void (*notifyMimeCallback)(const char*mime) = nullptr;
368367
int (*availableCallback)(Stream*stream)=nullptr;
369-
const char* (*mimeDetectCallback)(uint8_t* data, size_t len) = defaultMimeDetector;
370368
void *onWriteObj = nullptr;
371-
bool is_first = false;
372369
bool check_available_for_write = false;
373370
bool check_available = true;
374-
const char* actual_mime = nullptr;
375371
int retryLimit = COPY_RETRY_LIMIT;
376372
int delay_on_no_data = COPY_DELAY_ON_NODATA;
377373
bool active = true;
@@ -382,7 +378,7 @@ class StreamCopyT {
382378
bool is_sync_audio_info = false;
383379
AudioInfoSupport *p_audio_info_support = nullptr;
384380
BaseConverter* p_converter = nullptr;
385-
381+
MimeDetector mime_detector;
386382

387383
void syncAudioInfo(){
388384
// synchronize audio info
@@ -431,35 +427,6 @@ class StreamCopyT {
431427
}
432428
return total;
433429
}
434-
435-
/// Update the mime type
436-
void determineMime(void* data, size_t len){
437-
if (is_first) {
438-
actual_mime = mimeDetectCallback((uint8_t*)data, len);
439-
if (notifyMimeCallback!=nullptr && actual_mime!=nullptr){
440-
notifyMimeCallback(actual_mime);
441-
}
442-
is_first = false;
443-
}
444-
}
445-
446-
static const char* defaultMimeDetector(uint8_t* data, size_t len){
447-
const char* mime = nullptr;
448-
if (len > 4) {
449-
const uint8_t *start = (const uint8_t *) data;
450-
if (start[0]==0xFF && start[1]==0xF1){
451-
mime = "audio/aac";
452-
} else if (memcmp(start,"ID3",3) || start[0]==0xFF || start[0]==0xFE ){
453-
mime = "audio/mpeg";
454-
} else if (memcmp(start,"RIFF",4)){
455-
mime = "audio/vnd.wave";
456-
} else if (memcmp(start,"OggS",4)){
457-
mime = "audio/ogg";
458-
}
459-
}
460-
return mime;
461-
}
462-
463430
};
464431

465432
/**

0 commit comments

Comments
 (0)