@@ -25,7 +25,7 @@ namespace audio_tools {
25
25
* @author Phil Schatzmann
26
26
* @copyright GPLv3
27
27
*/
28
- class StreamingDecoder : public AudioInfoSource {
28
+ class StreamingDecoder : public AudioInfoSource , public AudioInfoSupport {
29
29
public:
30
30
/* *
31
31
* @brief Starts the processing
@@ -153,8 +153,17 @@ class StreamingDecoder : public AudioInfoSource {
153
153
*/
154
154
virtual size_t readBytes (uint8_t * data, size_t len) = 0;
155
155
156
+ void setAudioInfo (AudioInfo newInfo) override {
157
+ TRACED ();
158
+ if (this ->info != newInfo) {
159
+ this ->info = newInfo;
160
+ notifyAudioChange (info);
161
+ }
162
+ }
163
+
156
164
Print* p_print = nullptr ; // /< Output stream for decoded PCM data
157
165
Stream* p_input = nullptr ; // /< Input stream for encoded audio data
166
+ AudioInfo info;
158
167
};
159
168
160
169
/* *
@@ -185,18 +194,24 @@ class StreamingDecoderAdapter : public StreamingDecoder {
185
194
StreamingDecoderAdapter (AudioDecoder& decoder, const char * mimeStr,
186
195
int copySize = DEFAULT_BUFFER_SIZE) {
187
196
p_decoder = &decoder;
197
+ p_decoder->addNotifyAudioChange (*this );
188
198
mime_str = mimeStr;
189
199
if (copySize > 0 ) resize (copySize);
190
200
}
191
201
192
202
/* *
193
203
* @brief Starts the processing
194
204
*
195
- * Initializes the wrapped decoder if an input stream is available .
205
+ * Initializes the wrapped decoder.
196
206
*
197
207
* @return true if initialization was successful, false otherwise
198
208
*/
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
+ }
200
215
201
216
/* *
202
217
* @brief Releases the reserved memory
@@ -244,7 +259,9 @@ class StreamingDecoderAdapter : public StreamingDecoder {
244
259
int read = readBytes (buffer.data (), buffer.size ());
245
260
int written = 0 ;
246
261
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;
248
265
}
249
266
250
267
/* *
@@ -394,7 +411,11 @@ class MultiStreamingDecoder : public StreamingDecoder {
394
411
*
395
412
* @param inStream The input stream containing encoded audio data
396
413
*/
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
+ }
398
419
399
420
/* *
400
421
* @brief Adds a decoder that will be selected by its MIME type
@@ -405,6 +426,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
405
426
* @param decoder The StreamingDecoder to register
406
427
*/
407
428
void addDecoder (StreamingDecoder& decoder) {
429
+ decoder.addNotifyAudioChange (*this );
408
430
const char * mime = decoder.mime ();
409
431
if (mime != nullptr ) {
410
432
DecoderInfo info{mime, &decoder};
@@ -425,6 +447,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
425
447
*/
426
448
void addDecoder (StreamingDecoder& decoder, const char * mime) {
427
449
if (mime != nullptr ) {
450
+ decoder.addNotifyAudioChange (*this );
428
451
DecoderInfo info{mime, &decoder};
429
452
decoders.push_back (info);
430
453
} else {
@@ -451,6 +474,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
451
474
int bufferSize = DEFAULT_BUFFER_SIZE) {
452
475
if (mime != nullptr ) {
453
476
// Create a StreamingDecoderAdapter to wrap the AudioDecoder
477
+ decoder.addNotifyAudioChange (*this );
454
478
auto adapter = new StreamingDecoderAdapter (decoder, mime, bufferSize);
455
479
adapters.push_back (adapter); // Store for cleanup
456
480
@@ -491,15 +515,6 @@ class MultiStreamingDecoder : public StreamingDecoder {
491
515
if (!selectDecoder ()) {
492
516
return false ;
493
517
}
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
-
503
518
is_first = false ;
504
519
}
505
520
@@ -521,10 +536,14 @@ class MultiStreamingDecoder : public StreamingDecoder {
521
536
* otherwise
522
537
*/
523
538
bool selectDecoder (const char * mime) {
539
+ TRACEI ();
524
540
bool result = false ;
525
541
526
542
// 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
+ }
528
547
529
548
// Optimization: Check if the requested MIME type is already active
530
549
// This avoids unnecessary decoder switching when the same format is detected
@@ -537,6 +556,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
537
556
// This ensures proper resource cleanup and state reset
538
557
if (actual_decoder.decoder != nullptr ) {
539
558
actual_decoder.decoder ->end ();
559
+ actual_decoder.is_open = false ; // Mark as inactive
540
560
}
541
561
542
562
// Search through all registered decoders to find one that handles this MIME type
@@ -546,7 +566,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
546
566
547
567
// Check if this decoder supports the detected MIME type
548
568
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) );
550
570
551
571
// Switch to the matching decoder
552
572
actual_decoder = info;
@@ -557,16 +577,16 @@ class MultiStreamingDecoder : public StreamingDecoder {
557
577
actual_decoder.decoder ->setOutput (*p_print);
558
578
}
559
579
560
- // Note: Input stream will be configured later with the buffered stream
561
- // that preserves the data used for MIME detection
562
-
563
580
// 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 );
564
584
if (actual_decoder.decoder ->begin ()) {
565
585
actual_decoder.is_open = true ;
566
- LOGI (" StreamingDecoder %s started" , actual_decoder.mime );
586
+ LOGI (" StreamingDecoder %s started" , toStr ( actual_decoder.mime ) );
567
587
} else {
568
588
// 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 ) );
570
590
return false ;
571
591
}
572
592
@@ -707,6 +727,11 @@ class MultiStreamingDecoder : public StreamingDecoder {
707
727
const char * selected_mime = nullptr ; // /< MIME type that was selected
708
728
MimeSource* p_mime_source =
709
729
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
+ }
710
735
711
736
/* *
712
737
* @brief Automatically detects MIME type and selects appropriate decoder
@@ -741,15 +766,21 @@ class MultiStreamingDecoder : public StreamingDecoder {
741
766
// This prevents re-detection on subsequent calls during the same stream
742
767
if (actual_decoder.decoder == nullptr ) {
743
768
const char * mime = nullptr ;
744
-
769
+ p_data_source = nullptr ;
770
+
745
771
// Two methods for MIME type determination: external source or auto-detection
746
- if (p_mime_source) {
772
+ if (p_mime_source != nullptr ) {
747
773
// Option 1: Use externally provided MIME source (e.g., from HTTP headers)
748
774
// This is more efficient as it avoids reading and analyzing stream data
749
775
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;
751
779
} else {
752
780
// 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
+
753
784
// This requires reading a sample of data to identify the format
754
785
755
786
// Allocate buffer for MIME detection sample (80 bytes is typically sufficient
@@ -764,22 +795,29 @@ class MultiStreamingDecoder : public StreamingDecoder {
764
795
// The detector examines file headers, magic numbers, etc.
765
796
mime_detector.write (detection_buffer.data (), bytesRead);
766
797
mime = mime_detector.mime ();
767
- LOGI (" mime from detector: %s" , mime);
798
+ LOGI (" mime from detector: %s" , toStr ( mime) );
768
799
}
769
800
770
801
// Process the detected/provided MIME type
771
802
if (mime != nullptr ) {
772
803
// Delegate to the overloaded selectDecoder(mime) method to find
773
804
// and initialize the appropriate decoder for this MIME type
774
805
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) );
776
807
return false ; // No registered decoder can handle this format
777
808
}
809
+ // define data source
810
+ assert (p_data_source!=nullptr );
811
+ actual_decoder.decoder ->setInput (*p_data_source);
778
812
} else {
779
813
// MIME detection failed - format is unknown or unsupported
780
814
LOGE (" Could not determine mime type" );
781
815
return false ;
782
816
}
817
+ } else {
818
+ LOGI (" Decoder already selected: %s" , toStr (actual_decoder.mime ));
819
+ assert (p_input != nullptr );
820
+ actual_decoder.decoder ->setInput (*p_input);
783
821
}
784
822
785
823
// Success: either decoder was already selected or selection completed successfully
0 commit comments