Skip to content

Commit 3ff7aaf

Browse files
committed
Streaming Decoder Corrections
1 parent 6102a8d commit 3ff7aaf

File tree

3 files changed

+86
-38
lines changed

3 files changed

+86
-38
lines changed

src/AudioTools/AudioCodecs/CodecMP3Helix.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,12 @@ class MP3DecoderHelix : public AudioDecoder {
7979

8080
/// Starts the processing
8181
bool begin() override {
82-
TRACED();
83-
if (mp3!=nullptr) {
84-
//mp3->setDelay(CODEC_DELAY_MS);
85-
mp3->begin();
86-
}
82+
TRACEI();
83+
if (mp3 == nullptr) {
84+
LOGE("Not enough memory for libhelix");
85+
return false;
86+
}
87+
mp3->begin();
8788
return true;
8889
}
8990

src/AudioTools/AudioCodecs/CodecVorbis.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace audio_tools {
1212

1313
#ifndef VARBIS_MAX_READ_SIZE
14-
# define VARBIS_MAX_READ_SIZE 256
14+
# define VARBIS_MAX_READ_SIZE 1024
1515
#endif
1616

1717
#define VORBIS_HEADER_OPEN_LIMIT 1024
@@ -72,9 +72,10 @@ class VorbisDecoder : public StreamingDecoder {
7272
// wait for data
7373
if (is_first){
7474
// wait for some data
75-
if(p_input->available()<VORBIS_HEADER_OPEN_LIMIT){
76-
delay(20);
77-
return false;
75+
if(p_input->available() < VORBIS_HEADER_OPEN_LIMIT){
76+
delay(500);
77+
// LOGW("Not enough data available: %d", p_input->available());
78+
// return false;
7879
}
7980
LOGI("available: %d", p_input->available());
8081
is_first = false;
@@ -113,26 +114,34 @@ class VorbisDecoder : public StreamingDecoder {
113114
} else {
114115
LOGE("copy: %ld - %s", result, readError(result));
115116
}
116-
117+
delay(delay_on_no_data_ms);
117118
return false;
118119
}
119120
}
120121

121122
/// Provides "audio/ogg"
122123
const char *mime() override { return "audio/vorbis+ogg"; }
123124

125+
/// Defines the delay when there is no data
126+
void setDelayOnNoData(size_t delay) { delay_on_no_data_ms = delay; }
127+
128+
/// Defines the default read size
129+
void setReadSize(size_t size) { max_read_size = size; }
130+
124131
protected:
125132
AudioInfo cfg;
126133
Vector<uint8_t> pcm;
127134
OggVorbis_File file;
128135
ov_callbacks callbacks;
129136
int bitstream;
137+
size_t delay_on_no_data_ms = 100;
138+
size_t max_read_size = VARBIS_MAX_READ_SIZE;
130139
bool active = false;
131140
bool is_first = true;
132141
bool is_ov_open = false;
133142

134143
bool ovOpen(){
135-
pcm.resize(VARBIS_MAX_READ_SIZE);
144+
pcm.resize(max_read_size);
136145
int rc = ov_open_callbacks(this, &file, nullptr, 0, callbacks);
137146
if (rc<0){
138147
LOGE("ov_open_callbacks: %d", rc);
@@ -152,7 +161,7 @@ class VorbisDecoder : public StreamingDecoder {
152161
}
153162

154163
virtual size_t readBytes(uint8_t *data, size_t len) override {
155-
size_t read_size = min(len,(size_t)VARBIS_MAX_READ_SIZE);
164+
size_t read_size = min(len,(size_t)max_read_size);
156165
size_t result = p_input->readBytes((uint8_t *)data, read_size);
157166
LOGD("readBytes: %zu",result);
158167
return result;

src/AudioTools/AudioCodecs/StreamingDecoder.h

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace audio_tools {
2525
* @author Phil Schatzmann
2626
* @copyright GPLv3
2727
*/
28-
class StreamingDecoder : public AudioInfoSource {
28+
class StreamingDecoder : public AudioInfoSource, public AudioInfoSupport {
2929
public:
3030
/**
3131
* @brief Starts the processing
@@ -153,8 +153,17 @@ class StreamingDecoder : public AudioInfoSource {
153153
*/
154154
virtual size_t readBytes(uint8_t* data, size_t len) = 0;
155155

156+
void setAudioInfo(AudioInfo newInfo) override {
157+
TRACED();
158+
if (this->info != newInfo) {
159+
this->info = newInfo;
160+
notifyAudioChange(info);
161+
}
162+
}
163+
156164
Print* p_print = nullptr; ///< Output stream for decoded PCM data
157165
Stream* p_input = nullptr; ///< Input stream for encoded audio data
166+
AudioInfo info;
158167
};
159168

160169
/**
@@ -185,18 +194,24 @@ class StreamingDecoderAdapter : public StreamingDecoder {
185194
StreamingDecoderAdapter(AudioDecoder& decoder, const char* mimeStr,
186195
int copySize = DEFAULT_BUFFER_SIZE) {
187196
p_decoder = &decoder;
197+
p_decoder->addNotifyAudioChange(*this);
188198
mime_str = mimeStr;
189199
if (copySize > 0) resize(copySize);
190200
}
191201

192202
/**
193203
* @brief Starts the processing
194204
*
195-
* Initializes the wrapped decoder if an input stream is available.
205+
* Initializes the wrapped decoder.
196206
*
197207
* @return true if initialization was successful, false otherwise
198208
*/
199-
bool begin() override { return p_input != nullptr && p_decoder->begin(); }
209+
bool begin() override {
210+
TRACED();
211+
if (p_decoder == nullptr) return false;
212+
if (p_input == nullptr) return false;
213+
return p_decoder->begin();
214+
}
200215

201216
/**
202217
* @brief Releases the reserved memory
@@ -244,7 +259,9 @@ class StreamingDecoderAdapter : public StreamingDecoder {
244259
int read = readBytes(buffer.data(), buffer.size());
245260
int written = 0;
246261
if (read > 0) written = p_decoder->write(&buffer[0], read);
247-
return written > 0;
262+
bool rc = written > 0;
263+
LOGI("copy: %s", rc ? "success" : "failure");
264+
return rc;
248265
}
249266

250267
/**
@@ -394,7 +411,11 @@ class MultiStreamingDecoder : public StreamingDecoder {
394411
*
395412
* @param inStream The input stream containing encoded audio data
396413
*/
397-
void setInput(Stream& inStream) { StreamingDecoder::setInput(inStream); }
414+
void setInput(Stream& inStream) {
415+
StreamingDecoder::setInput(inStream);
416+
// in some cases we use the buffered stream as input
417+
buffered_stream.setStream(inStream);
418+
}
398419

399420
/**
400421
* @brief Adds a decoder that will be selected by its MIME type
@@ -405,6 +426,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
405426
* @param decoder The StreamingDecoder to register
406427
*/
407428
void addDecoder(StreamingDecoder& decoder) {
429+
decoder.addNotifyAudioChange(*this);
408430
const char* mime = decoder.mime();
409431
if (mime != nullptr) {
410432
DecoderInfo info{mime, &decoder};
@@ -425,6 +447,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
425447
*/
426448
void addDecoder(StreamingDecoder& decoder, const char* mime) {
427449
if (mime != nullptr) {
450+
decoder.addNotifyAudioChange(*this);
428451
DecoderInfo info{mime, &decoder};
429452
decoders.push_back(info);
430453
} else {
@@ -451,6 +474,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
451474
int bufferSize = DEFAULT_BUFFER_SIZE) {
452475
if (mime != nullptr) {
453476
// Create a StreamingDecoderAdapter to wrap the AudioDecoder
477+
decoder.addNotifyAudioChange(*this);
454478
auto adapter = new StreamingDecoderAdapter(decoder, mime, bufferSize);
455479
adapters.push_back(adapter); // Store for cleanup
456480

@@ -491,15 +515,6 @@ class MultiStreamingDecoder : public StreamingDecoder {
491515
if (!selectDecoder()) {
492516
return false;
493517
}
494-
495-
// Set up buffered input stream that includes the detection data
496-
buffered_stream.setStream(*p_input);
497-
498-
// Redirect the decoder to use the buffered stream
499-
if (actual_decoder.decoder != nullptr) {
500-
actual_decoder.decoder->setInput(buffered_stream);
501-
}
502-
503518
is_first = false;
504519
}
505520

@@ -521,10 +536,14 @@ class MultiStreamingDecoder : public StreamingDecoder {
521536
* otherwise
522537
*/
523538
bool selectDecoder(const char* mime) {
539+
TRACEI();
524540
bool result = false;
525541

526542
// Guard against null MIME type - cannot proceed without valid MIME
527-
if (mime == nullptr) return false;
543+
if (mime == nullptr) {
544+
LOGE("mime is null");
545+
return false;
546+
}
528547

529548
// Optimization: Check if the requested MIME type is already active
530549
// This avoids unnecessary decoder switching when the same format is detected
@@ -537,6 +556,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
537556
// This ensures proper resource cleanup and state reset
538557
if (actual_decoder.decoder != nullptr) {
539558
actual_decoder.decoder->end();
559+
actual_decoder.is_open = false; // Mark as inactive
540560
}
541561

542562
// Search through all registered decoders to find one that handles this MIME type
@@ -546,7 +566,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
546566

547567
// Check if this decoder supports the detected MIME type
548568
if (StrView(info.mime).equals(mime)) {
549-
LOGI("Using StreamingDecoder for %s (%s)", info.mime, mime);
569+
LOGI("Using Decoder %s for %s", toStr(info.mime), toStr(mime));
550570

551571
// Switch to the matching decoder
552572
actual_decoder = info;
@@ -557,16 +577,16 @@ class MultiStreamingDecoder : public StreamingDecoder {
557577
actual_decoder.decoder->setOutput(*p_print);
558578
}
559579

560-
// Note: Input stream will be configured later with the buffered stream
561-
// that preserves the data used for MIME detection
562-
563580
// Initialize the selected decoder and mark it as active
581+
assert(p_data_source != nullptr);
582+
actual_decoder.decoder->setInput(*p_data_source);
583+
actual_decoder.decoder->addNotifyAudioChange(*this);
564584
if (actual_decoder.decoder->begin()) {
565585
actual_decoder.is_open = true;
566-
LOGI("StreamingDecoder %s started", actual_decoder.mime);
586+
LOGI("StreamingDecoder %s started", toStr(actual_decoder.mime));
567587
} else {
568588
// Decoder failed to start - this is a critical error
569-
LOGE("Failed to start StreamingDecoder %s", actual_decoder.mime);
589+
LOGE("Failed to start StreamingDecoder %s", toStr(actual_decoder.mime));
570590
return false;
571591
}
572592

@@ -707,6 +727,11 @@ class MultiStreamingDecoder : public StreamingDecoder {
707727
const char* selected_mime = nullptr; ///< MIME type that was selected
708728
MimeSource* p_mime_source =
709729
nullptr; ///< Optional MIME source for custom logic
730+
Stream *p_data_source = nullptr; ///< effective data source for decoder
731+
732+
const char* toStr(const char* str){
733+
return str == nullptr ? "" : str;
734+
}
710735

711736
/**
712737
* @brief Automatically detects MIME type and selects appropriate decoder
@@ -741,15 +766,21 @@ class MultiStreamingDecoder : public StreamingDecoder {
741766
// This prevents re-detection on subsequent calls during the same stream
742767
if (actual_decoder.decoder == nullptr) {
743768
const char* mime = nullptr;
744-
769+
p_data_source = nullptr;
770+
745771
// Two methods for MIME type determination: external source or auto-detection
746-
if (p_mime_source) {
772+
if (p_mime_source != nullptr) {
747773
// Option 1: Use externally provided MIME source (e.g., from HTTP headers)
748774
// This is more efficient as it avoids reading and analyzing stream data
749775
mime = p_mime_source->mime();
750-
LOGI("mime from source: %s", mime);
776+
LOGI("mime from source: %s", toStr(mime));
777+
assert(p_input != nullptr);
778+
p_data_source = p_input;
751779
} else {
752780
// Option 2: Auto-detect MIME type by analyzing stream content
781+
// Redirect the decoder to use the buffered stream
782+
p_data_source = &buffered_stream;
783+
753784
// This requires reading a sample of data to identify the format
754785

755786
// Allocate buffer for MIME detection sample (80 bytes is typically sufficient
@@ -764,22 +795,29 @@ class MultiStreamingDecoder : public StreamingDecoder {
764795
// The detector examines file headers, magic numbers, etc.
765796
mime_detector.write(detection_buffer.data(), bytesRead);
766797
mime = mime_detector.mime();
767-
LOGI("mime from detector: %s", mime);
798+
LOGI("mime from detector: %s", toStr(mime));
768799
}
769800

770801
// Process the detected/provided MIME type
771802
if (mime != nullptr) {
772803
// Delegate to the overloaded selectDecoder(mime) method to find
773804
// and initialize the appropriate decoder for this MIME type
774805
if (!selectDecoder(mime)) {
775-
LOGE("The decoder could not be found for %s", mime);
806+
LOGE("The decoder could not be selected for %s", toStr(mime));
776807
return false; // No registered decoder can handle this format
777808
}
809+
// define data source
810+
assert(p_data_source!=nullptr);
811+
actual_decoder.decoder->setInput(*p_data_source);
778812
} else {
779813
// MIME detection failed - format is unknown or unsupported
780814
LOGE("Could not determine mime type");
781815
return false;
782816
}
817+
} else {
818+
LOGI("Decoder already selected: %s", toStr(actual_decoder.mime));
819+
assert(p_input != nullptr);
820+
actual_decoder.decoder->setInput(*p_input);
783821
}
784822

785823
// Success: either decoder was already selected or selection completed successfully

0 commit comments

Comments
 (0)