1
1
#pragma once
2
- #include " AudioToolsConfig.h"
3
2
#include " AudioTools/AudioCodecs/AudioCodecsBase.h"
3
+ #include " AudioToolsConfig.h"
4
4
#include " vorbis-tremor.h"
5
5
6
6
// #include "AudioTools/AudioCodecs/ContainerOgg.h"
7
7
// #include "ivorbiscodec.h"
8
8
// #include "ivorbisfile.h"
9
9
10
-
11
10
namespace audio_tools {
12
11
13
12
#ifndef VARBIS_MAX_READ_SIZE
14
- # define VARBIS_MAX_READ_SIZE 1024
13
+ #define VARBIS_MAX_READ_SIZE 1024
15
14
#endif
16
15
17
16
#define VORBIS_HEADER_OPEN_LIMIT 1024
@@ -25,7 +24,7 @@ namespace audio_tools {
25
24
* @copyright GPLv3
26
25
*/
27
26
class VorbisDecoder : public StreamingDecoder {
28
- public:
27
+ public:
29
28
VorbisDecoder () = default ;
30
29
31
30
// / Destroy the VorbisDecoder object
@@ -39,27 +38,41 @@ class VorbisDecoder : public StreamingDecoder {
39
38
bool begin () override {
40
39
LOGI (" begin" );
41
40
41
+ // Ensure we start with clean state
42
+ if (active) {
43
+ LOGW (" Decoder already active, calling end() first" );
44
+ end ();
45
+ }
46
+
42
47
callbacks.read_func = read_func;
43
48
callbacks.seek_func = seek_func;
44
49
callbacks.close_func = nullptr ;
45
50
callbacks.tell_func = tell_func;
46
51
47
- if (p_input->available ()>=VORBIS_HEADER_OPEN_LIMIT){
48
- ovOpen ();
52
+ assert (p_input != nullptr );
53
+ if (p_input->available () < VORBIS_HEADER_OPEN_LIMIT) {
54
+ delay (delay_wait_for_data_ms);
49
55
}
56
+ LOGI (" available: %d" , p_input->available ());
50
57
51
- active = true ;
52
- is_first = true ;
53
- return true ;
58
+ is_ov_open = ovOpen ();
59
+ LOGI (" ovOpen result: %d" , is_ov_open);
60
+
61
+ active = is_ov_open;
62
+ return is_ov_open;
54
63
}
55
64
56
65
// / Releases the reserved memory
57
66
void end () override {
58
67
LOGI (" end" );
68
+ if (is_ov_open && active) {
69
+ ov_clear (&file);
70
+ LOGI (" ov_clear completed" );
71
+ }
59
72
is_ov_open = false ;
60
73
is_first = true ;
61
- if (active) ov_clear (&file);
62
74
active = false ;
75
+ pcm.clear (); // Free the PCM buffer
63
76
}
64
77
65
78
// / Provides the last available MP3FrameInfo
@@ -69,50 +82,62 @@ class VorbisDecoder : public StreamingDecoder {
69
82
virtual operator bool () override { return active; }
70
83
71
84
virtual bool copy () override {
72
- // wait for data
73
- if (is_first){
74
- // wait for some data
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;
79
- }
80
- LOGI (" available: %d" , p_input->available ());
81
- is_first = false ;
82
- }
85
+ TRACED ();
83
86
84
87
// open if not already done
85
- if (!is_ov_open){
88
+ if (!is_ov_open) {
86
89
if (!ovOpen ()) {
87
- LOGE (" not open" );
90
+ LOGE (" Failed to open Vorbis stream " );
88
91
return false ;
89
92
}
90
93
}
91
94
92
- if (pcm.data ()==nullptr ){
93
- LOGE (" Not enough memory" );
95
+ // Defensive checks before calling Vorbis functions
96
+ if (pcm.data () == nullptr ) {
97
+ LOGE (" PCM buffer is null - memory allocation failed" );
98
+ return false ;
99
+ }
100
+
101
+ if (pcm.size () == 0 ) {
102
+ LOGE (" PCM buffer size is 0" );
103
+ return false ;
104
+ }
105
+
106
+ // Additional sanity check for the file structure
107
+ if (!active) {
108
+ LOGE (" Decoder is not active" );
94
109
return false ;
95
110
}
96
111
97
- // convert to pcm
112
+ LOGD (" ov_read: buffer size %d" , pcm.size ());
113
+ bitstream = 0 ;
114
+
115
+ // Call ov_read with additional error checking
98
116
long result = ov_read (&file, (char *)pcm.data (), pcm.size (), &bitstream);
99
- LOGI (" copy: %ld" , result);
117
+ LOGI (" copy result: %d" , (int )result);
118
+
100
119
if (result > 0 ) {
101
120
AudioInfo current = currentInfo ();
102
121
if (current != cfg) {
103
122
cfg = current;
104
123
cfg.logInfo ();
105
124
notifyAudioChange (cfg);
106
125
}
107
- p_print->write (pcm.data (), result);
126
+
127
+ if (p_print != nullptr ) {
128
+ p_print->write (pcm.data (), result);
129
+ } else {
130
+ LOGE (" Output stream is null" );
131
+ return false ;
132
+ }
108
133
delay (1 );
109
134
return true ;
110
135
} else {
111
- if (result== 0 || result== -3 ){
136
+ if (result == 0 || result == -3 ) {
112
137
// data interruption
113
- LOGD (" copy: %ld - %s" , result, readError (result));
138
+ LOGD (" copy: %d - %s" , ( int ) result, readError (result));
114
139
} else {
115
- LOGE (" copy: %ld - %s" , result, readError (result));
140
+ LOGE (" copy: %d - %s" , ( int ) result, readError (result));
116
141
}
117
142
delay (delay_on_no_data_ms);
118
143
return false ;
@@ -123,54 +148,83 @@ class VorbisDecoder : public StreamingDecoder {
123
148
const char *mime () override { return " audio/vorbis+ogg" ; }
124
149
125
150
// / Defines the delay when there is no data
126
- void setDelayOnNoData (size_t delay) { delay_on_no_data_ms = delay; }
151
+ void setDelayOnNoData (size_t delay) { delay_on_no_data_ms = delay; }
152
+
153
+ // / Defines the delay to wait if there is not enough data to open the decoder
154
+ void setWaitForData (size_t wait) { delay_wait_for_data_ms = wait; }
127
155
128
156
// / Defines the default read size
129
- void setReadSize (size_t size) { max_read_size = size; }
157
+ void setReadSize (size_t size) {
158
+ max_read_size = size;
159
+ // Ensure we don't set an unreasonably large size
160
+ if (max_read_size > 8192 ) {
161
+ LOGW (" Read size %zu is very large, consider smaller buffer" ,
162
+ max_read_size);
163
+ }
164
+ }
130
165
131
- protected:
166
+ protected:
132
167
AudioInfo cfg;
133
- Vector<uint8_t > pcm;
168
+ Vector<uint8_t > pcm{ 0 } ;
134
169
OggVorbis_File file;
135
170
ov_callbacks callbacks;
136
- int bitstream;
171
+ int bitstream = 0 ;
137
172
size_t delay_on_no_data_ms = 100 ;
173
+ size_t delay_wait_for_data_ms = 500 ;
138
174
size_t max_read_size = VARBIS_MAX_READ_SIZE;
139
175
bool active = false ;
140
176
bool is_first = true ;
141
177
bool is_ov_open = false ;
142
178
143
- bool ovOpen (){
179
+ bool ovOpen () {
144
180
pcm.resize (max_read_size);
181
+ checkMemory (true );
145
182
int rc = ov_open_callbacks (this , &file, nullptr , 0 , callbacks);
146
- if (rc< 0 ) {
147
- LOGE (" ov_open_callbacks: %d " , rc);
183
+ if (rc < 0 ) {
184
+ LOGE (" ov_open_callbacks failed with error %d: %s " , rc, getOpenError (rc) );
148
185
} else {
186
+ LOGI (" ov_open_callbacks succeeded" );
149
187
is_ov_open = true ;
150
188
}
189
+ checkMemory (true );
151
190
return is_ov_open;
152
191
}
153
192
154
193
AudioInfo currentInfo () {
155
194
AudioInfo result;
195
+ if (!is_ov_open) {
196
+ LOGE (" Cannot get audio info - stream not open" );
197
+ return result;
198
+ }
199
+
156
200
vorbis_info *info = ov_info (&file, -1 );
201
+ if (info == nullptr ) {
202
+ LOGE (" ov_info returned null pointer" );
203
+ return result;
204
+ }
205
+
157
206
result.sample_rate = info->rate ;
158
207
result.channels = info->channels ;
159
208
result.bits_per_sample = 16 ;
209
+
210
+ LOGD (" Audio info - rate: %d, channels: %d" , info->rate , info->channels );
160
211
return result;
161
212
}
162
213
163
214
virtual size_t readBytes (uint8_t *data, size_t len) override {
164
- size_t read_size = min (len,(size_t )max_read_size);
215
+ size_t read_size = min (len, (size_t )max_read_size);
165
216
size_t result = p_input->readBytes ((uint8_t *)data, read_size);
166
- LOGD (" readBytes: %zu" ,result);
217
+ LOGD (" readBytes: %zu" , result);
167
218
return result;
168
219
}
169
220
170
221
static size_t read_func (void *ptr, size_t size, size_t nmemb,
171
222
void *datasource) {
172
223
VorbisDecoder *self = (VorbisDecoder *)datasource;
173
- return self->readBytes ((uint8_t *)ptr, size * nmemb);
224
+ assert (datasource != nullptr );
225
+ size_t result = self->readBytes ((uint8_t *)ptr, size * nmemb);
226
+ LOGD (" read_func: %d -> %d" , size * nmemb, (int )result);
227
+ return result;
174
228
}
175
229
176
230
static int seek_func (void *datasource, ogg_int64_t offset, int whence) {
@@ -189,22 +243,54 @@ class VorbisDecoder : public StreamingDecoder {
189
243
// return 0;
190
244
// }
191
245
192
- const char * readError (long error){
193
- if (error>= 0 ) {
246
+ const char * readError (long error) {
247
+ if (error >= 0 ) {
194
248
return " OK" ;
195
249
}
196
- switch (error){
250
+ switch (error) {
197
251
case OV_HOLE:
198
252
return " Interruption in the data" ;
199
253
case OV_EBADLINK:
200
- return " Invalid stream section " ;
254
+ return " Invalid stream section" ;
255
+ case OV_EREAD:
256
+ return " Read error" ;
257
+ case OV_EFAULT:
258
+ return " Internal fault" ;
259
+ case OV_EIMPL:
260
+ return " Unimplemented feature" ;
201
261
case OV_EINVAL:
202
- return " Invalid header" ;
262
+ return " Invalid argument" ;
263
+ case OV_ENOTVORBIS:
264
+ return " Not a Vorbis file" ;
265
+ case OV_EBADHEADER:
266
+ return " Invalid Vorbis header" ;
267
+ case OV_EVERSION:
268
+ return " Vorbis version mismatch" ;
269
+ case OV_ENOSEEK:
270
+ return " Stream not seekable" ;
203
271
default :
204
- return " N/A " ;
272
+ return " Unknown error " ;
205
273
}
206
274
}
207
275
276
+ const char *getOpenError (int error) {
277
+ switch (error) {
278
+ case 0 :
279
+ return " Success" ;
280
+ case OV_EREAD:
281
+ return " Read from media error" ;
282
+ case OV_ENOTVORBIS:
283
+ return " Not Vorbis data" ;
284
+ case OV_EVERSION:
285
+ return " Vorbis version mismatch" ;
286
+ case OV_EBADHEADER:
287
+ return " Invalid Vorbis bitstream header" ;
288
+ case OV_EFAULT:
289
+ return " Internal logic fault" ;
290
+ default :
291
+ return " Unknown open error" ;
292
+ }
293
+ }
208
294
};
209
295
210
- } // namespace audio_tools
296
+ } // namespace audio_tools
0 commit comments