Skip to content

Commit 8a6ccde

Browse files
committed
MultiStreamingDecoder using PrefixStream
1 parent 43ec90d commit 8a6ccde

File tree

1 file changed

+106
-12
lines changed

1 file changed

+106
-12
lines changed

src/AudioTools/AudioCodecs/StreamingDecoder.h

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include <new>
23
#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
34
#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
45
#include "AudioTools/CoreAudio/AudioMetaData/MimeDetector.h"
@@ -7,6 +8,99 @@
78

89
namespace audio_tools {
910

11+
/**
12+
* @brief Stream that combines prefix data with original stream
13+
*
14+
* Useful for format detection where some data needs to be preserved.
15+
*
16+
* @ingroup io
17+
* @author Phil Schatzmann
18+
* @copyright GPLv3
19+
*/
20+
class PrefixStream : public Stream {
21+
public:
22+
/// Default constructor - call setData() before use
23+
PrefixStream()
24+
: prefix_data_(nullptr), prefix_len_(0), prefix_pos_(0), original_stream_(nullptr) {}
25+
26+
/// Constructor with prefix data and original stream
27+
PrefixStream(const uint8_t* prefix_data, size_t prefix_len, Stream& original_stream)
28+
: prefix_data_(prefix_data), prefix_len_(prefix_len), prefix_pos_(0), original_stream_(&original_stream) {}
29+
30+
/// Sets the prefix data and original stream
31+
void setData(const uint8_t* prefix_data, size_t prefix_len, Stream& original_stream) {
32+
prefix_data_ = prefix_data;
33+
prefix_len_ = prefix_len;
34+
prefix_pos_ = 0;
35+
original_stream_ = &original_stream;
36+
}
37+
38+
/// Returns total bytes available (prefix + original stream)
39+
virtual int available() override {
40+
int remaining_prefix = prefix_len_ - prefix_pos_;
41+
if (original_stream_ != nullptr) {
42+
return remaining_prefix + original_stream_->available();
43+
}
44+
return remaining_prefix;
45+
}
46+
47+
/// Reads a single byte
48+
virtual int read() override {
49+
if (prefix_pos_ < prefix_len_) {
50+
return prefix_data_[prefix_pos_++];
51+
}
52+
if (original_stream_ != nullptr) {
53+
return original_stream_->read();
54+
}
55+
return -1;
56+
}
57+
58+
/// Peeks at next byte without consuming it
59+
virtual int peek() override {
60+
if (prefix_pos_ < prefix_len_) {
61+
return prefix_data_[prefix_pos_];
62+
}
63+
if (original_stream_ != nullptr) {
64+
return original_stream_->peek();
65+
}
66+
return -1;
67+
}
68+
69+
/// Reads multiple bytes into buffer
70+
virtual size_t readBytes(uint8_t* buffer, size_t length) override {
71+
size_t bytes_read = 0;
72+
73+
// First, read from prefix data
74+
if (prefix_pos_ < prefix_len_) {
75+
size_t prefix_available = prefix_len_ - prefix_pos_;
76+
size_t prefix_to_read = (length < prefix_available) ? length : prefix_available;
77+
memcpy(buffer, prefix_data_ + prefix_pos_, prefix_to_read);
78+
prefix_pos_ += prefix_to_read;
79+
bytes_read += prefix_to_read;
80+
buffer += prefix_to_read;
81+
length -= prefix_to_read;
82+
}
83+
84+
// Then read from original stream if more data is needed
85+
if (length > 0 && original_stream_ != nullptr) {
86+
bytes_read += original_stream_->readBytes(buffer, length);
87+
}
88+
89+
return bytes_read;
90+
}
91+
92+
/// Write operations not supported
93+
virtual size_t write(uint8_t) override { return 0; }
94+
virtual size_t write(const uint8_t*, size_t) override { return 0; }
95+
virtual void flush() override {}
96+
97+
private:
98+
const uint8_t* prefix_data_; ///< Prefix data buffer
99+
size_t prefix_len_; ///< Length of prefix data
100+
size_t prefix_pos_; ///< Current position in prefix
101+
Stream* original_stream_; ///< Original stream
102+
};
103+
10104
/**
11105
* @brief A Streaming Decoder where we provide both the input and output
12106
* as streams.
@@ -343,7 +437,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
343437
for (auto* adapter : adapters) {
344438
delete adapter;
345439
}
346-
adapters.clear();
440+
adapters.clear();
347441
}
348442

349443
/**
@@ -376,7 +470,7 @@ class MultiStreamingDecoder : public StreamingDecoder {
376470
actual_decoder.is_open = false;
377471
actual_decoder.decoder = nullptr;
378472
actual_decoder.mime = nullptr;
379-
is_first = true;
473+
is_first = true;
380474
}
381475

382476
/**
@@ -720,14 +814,15 @@ class MultiStreamingDecoder : public StreamingDecoder {
720814
Vector<StreamingDecoderAdapter*> adapters{
721815
0}; ///< Collection of internally created adapters
722816
MimeDetector mime_detector; ///< MIME type detection engine
723-
BufferedStream buffered_stream{0}; ///< Buffered stream for data preservation
724817
Vector<uint8_t> detection_buffer{0}; ///< Buffer for format detection data
725818
bool is_first = true; ///< Flag for first copy() call
726819
const char* selected_mime = nullptr; ///< MIME type that was selected
727820
MimeSource* p_mime_source =
728821
nullptr; ///< Optional MIME source for custom logic
729822
Stream *p_data_source = nullptr; ///< effective data source for decoder
730823

824+
PrefixStream prefix_stream; ///< Instance of prefix stream (uses placement new on prefix_stream_storage)
825+
731826
const char* toStr(const char* str){
732827
return str == nullptr ? "" : str;
733828
}
@@ -777,19 +872,13 @@ class MultiStreamingDecoder : public StreamingDecoder {
777872
p_data_source = p_input;
778873
} else {
779874
// Option 2: Auto-detect MIME type by analyzing stream content
780-
// Redirect the decoder to use the buffered stream
781-
// we use the buffered stream as input
782875
assert(p_input != nullptr);
783-
buffered_stream.setStream(*p_input);
784-
buffered_stream.resize(DEFAULT_BUFFER_SIZE);
785-
p_data_source = &buffered_stream;
786876

787-
// This requires reading a sample of data to identify the format
788-
789-
// Allocate buffer for MIME detection sample (80 bytes is typically sufficient
877+
// Read a sample of data for MIME detection
878+
// Allocate buffer for MIME detection sample (160 bytes is sufficient
790879
// for most audio format headers to be identified)
791880
detection_buffer.resize(160);
792-
size_t bytesRead = buffered_stream.peekBytes(detection_buffer.data(), detection_buffer.size());
881+
size_t bytesRead = p_input->readBytes(detection_buffer.data(), detection_buffer.size());
793882

794883
// If no data is available, we cannot proceed with detection
795884
if (bytesRead == 0) return false;
@@ -799,6 +888,11 @@ class MultiStreamingDecoder : public StreamingDecoder {
799888
mime_detector.write(detection_buffer.data(), bytesRead);
800889
mime = mime_detector.mime();
801890
LOGI("mime from detector: %s", toStr(mime));
891+
892+
// Create a prefix stream that combines the detection data with the original stream
893+
// This ensures the decoder receives the complete stream including the bytes used for detection
894+
prefix_stream.setData(detection_buffer.data(), bytesRead, *p_input);
895+
p_data_source = &prefix_stream;
802896
}
803897

804898
// Process the detected/provided MIME type

0 commit comments

Comments
 (0)