/src/mpg123/src/libmpg123/readers.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* TODO: Check all read calls (in loops, especially!) for return value 0 (EOF)! */ |
2 | | /* Check if get_fileinfo should read ID3 info or not, seems a bit out of place here. */ |
3 | | /* #define EXTRA_DEBUG */ |
4 | | /* |
5 | | readers.c: reading input data |
6 | | |
7 | | copyright ?-2023 by the mpg123 project - free software under the terms of the LGPL 2.1 |
8 | | see COPYING and AUTHORS files in distribution or http://mpg123.org |
9 | | initially written by Michael Hipp |
10 | | */ |
11 | | |
12 | | #include "mpg123lib_intern.h" |
13 | | |
14 | | #include "../common/debug.h" |
15 | | |
16 | | static int stream_init(mpg123_handle *fr); |
17 | | static int64_t get_fileinfo(mpg123_handle *); |
18 | | |
19 | | static ptrdiff_t plain_fullread(mpg123_handle *fr,unsigned char *buf, ptrdiff_t count); |
20 | | |
21 | | #ifndef NO_FEEDER |
22 | | /* Bufferchain methods. */ |
23 | | static void bc_init(struct bufferchain *bc); |
24 | | static void bc_reset(struct bufferchain *bc); |
25 | | static int bc_append(struct bufferchain *bc, ptrdiff_t size); |
26 | | #if 0 |
27 | | static void bc_drop(struct bufferchain *bc); |
28 | | #endif |
29 | | static int bc_add(struct bufferchain *bc, const unsigned char *data, ptrdiff_t size); |
30 | | static ptrdiff_t bc_give(struct bufferchain *bc, unsigned char *out, ptrdiff_t size); |
31 | | static ptrdiff_t bc_skip(struct bufferchain *bc, ptrdiff_t count); |
32 | | static ptrdiff_t bc_seekback(struct bufferchain *bc, ptrdiff_t count); |
33 | | static void bc_forget(struct bufferchain *bc); |
34 | | #endif |
35 | | |
36 | | // This is only for streams, so READER_HANDLEIO must be set. |
37 | | static ptrdiff_t fdread(mpg123_handle *fr, void *buf, size_t count) |
38 | 0 | { |
39 | 0 | if((fr->rdat.flags & READER_HANDLEIO) && fr->rdat.r_read64) |
40 | 0 | { |
41 | 0 | size_t got = 0; |
42 | 0 | int ret = fr->rdat.r_read64(fr->rdat.iohandle, buf, count, &got); |
43 | 0 | if(ret<0) |
44 | 0 | { |
45 | 0 | if(NOQUIET) merror("error reading %zu bytes", count); |
46 | 0 | return -1; |
47 | 0 | } |
48 | 0 | if(VERBOSE3) mdebug("read %zu bytes of %zu", got, count); |
49 | | // Stupid handling, but at least some handling of never-occuring case. |
50 | 0 | return (ptrdiff_t)(got > PTRDIFF_MAX ? PTRDIFF_MAX : got); |
51 | 0 | } |
52 | 0 | if(NOQUIET) error("no reader setup"); |
53 | 0 | return -1; |
54 | 0 | } |
55 | | |
56 | | static int64_t fdseek(mpg123_handle *fr, int64_t offset, int whence) |
57 | 0 | { |
58 | 0 | if((fr->rdat.flags & READER_HANDLEIO) && fr->rdat.r_lseek64) |
59 | 0 | return (fr->rdat.flags & READER_NOSEEK) |
60 | 0 | ? -1 |
61 | 0 | : fr->rdat.r_lseek64(fr->rdat.iohandle, offset, whence); |
62 | 0 | if(NOQUIET) error("no reader setup"); |
63 | 0 | return -1; |
64 | 0 | } |
65 | | |
66 | | #ifndef NO_ICY |
67 | | /* stream based operation with icy meta data*/ |
68 | | static ptrdiff_t icy_fullread(mpg123_handle *fr, unsigned char *buf, ptrdiff_t count) |
69 | 0 | { |
70 | 0 | ptrdiff_t ret,cnt; |
71 | 0 | cnt = 0; |
72 | 0 | if(fr->rdat.flags & READER_SEEKABLE) |
73 | 0 | { |
74 | 0 | if(NOQUIET) error("mpg123 programmer error: I don't do ICY on seekable streams."); |
75 | 0 | return -1; |
76 | 0 | } |
77 | | /* |
78 | | There used to be a check for expected file end here (length value or ID3 flag). |
79 | | This is not needed: |
80 | | 1. EOF is indicated by fdread returning zero bytes anyway. |
81 | | 2. We get false positives of EOF for either files that grew or |
82 | | 3. ... files that have ID3v1 tags in between (stream with intro). |
83 | | */ |
84 | | |
85 | 0 | while(cnt < count) |
86 | 0 | { |
87 | | /* all icy code is inside this if block, everything else is the plain fullread we know */ |
88 | | /* debug1("read: %li left", (long) count-cnt); */ |
89 | 0 | if(fr->icy.next < count-cnt) |
90 | 0 | { |
91 | 0 | unsigned char temp_buff; |
92 | 0 | size_t meta_size; |
93 | 0 | ptrdiff_t cut_pos; |
94 | | |
95 | | /* we are near icy-metaint boundary, read up to the boundary */ |
96 | 0 | if(fr->icy.next > 0) |
97 | 0 | { |
98 | 0 | cut_pos = fr->icy.next; |
99 | 0 | ret = fdread(fr,buf+cnt,cut_pos); |
100 | 0 | if(ret < 1) |
101 | 0 | { |
102 | 0 | if(ret == 0) break; /* Just EOF. */ |
103 | 0 | if(NOQUIET) error("icy boundary read"); |
104 | |
|
105 | 0 | return READER_ERROR; |
106 | 0 | } |
107 | | |
108 | 0 | if(!(fr->rdat.flags & READER_BUFFERED)) |
109 | 0 | SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX); |
110 | 0 | cnt += ret; |
111 | 0 | fr->icy.next -= ret; |
112 | 0 | if(fr->icy.next > 0) |
113 | 0 | { |
114 | 0 | debug1("another try... still %li left", (long)fr->icy.next); |
115 | 0 | continue; |
116 | 0 | } |
117 | 0 | } |
118 | | /* now off to read icy data */ |
119 | | |
120 | | /* one byte icy-meta size (must be multiplied by 16 to get icy-meta length) */ |
121 | | |
122 | 0 | ret = fdread(fr,&temp_buff,1); /* Getting one single byte hast to suceed. */ |
123 | 0 | if(ret < 0){ if(NOQUIET) error("reading icy size"); return READER_ERROR; } |
124 | 0 | if(ret == 0) break; |
125 | | |
126 | 0 | debug2("got meta-size byte: %u, at filepos %li", temp_buff, (long)fr->rdat.filepos ); |
127 | 0 | if(!(fr->rdat.flags & READER_BUFFERED)) |
128 | 0 | SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX); /* 1... */ |
129 | |
|
130 | 0 | if((meta_size = ((size_t) temp_buff) * 16)) |
131 | 0 | { |
132 | | /* we have got some metadata */ |
133 | 0 | char *meta_buff; |
134 | | /* TODO: Get rid of this malloc ... perhaps hooking into the reader buffer pool? */ |
135 | 0 | meta_buff = malloc(meta_size+1); |
136 | 0 | if(meta_buff != NULL) |
137 | 0 | { |
138 | 0 | ptrdiff_t left = meta_size; |
139 | 0 | while(left > 0) |
140 | 0 | { |
141 | 0 | ret = fdread(fr,meta_buff+meta_size-left,left); |
142 | | /* 0 is error here, too... there _must_ be the ICY data, the server promised! */ |
143 | 0 | if(ret < 1){ if(NOQUIET) error("reading icy-meta"); return READER_ERROR; } |
144 | 0 | left -= ret; |
145 | 0 | } |
146 | 0 | meta_buff[meta_size] = 0; /* string paranoia */ |
147 | 0 | if(!(fr->rdat.flags & READER_BUFFERED)) |
148 | 0 | SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX); |
149 | |
|
150 | 0 | if(fr->icy.data) free(fr->icy.data); |
151 | 0 | fr->icy.data = meta_buff; |
152 | 0 | fr->metaflags |= MPG123_NEW_ICY; |
153 | 0 | debug2("icy-meta: %s size: %d bytes", fr->icy.data, (int)meta_size); |
154 | 0 | } |
155 | 0 | else |
156 | 0 | { |
157 | 0 | if(NOQUIET) error1("cannot allocate memory for meta_buff (%lu bytes) ... trying to skip the metadata!", (unsigned long)meta_size); |
158 | 0 | fr->rd->skip_bytes(fr, meta_size); |
159 | 0 | } |
160 | 0 | } |
161 | 0 | fr->icy.next = fr->icy.interval; |
162 | 0 | } |
163 | 0 | else |
164 | 0 | { |
165 | 0 | ret = plain_fullread(fr, buf+cnt, count-cnt); |
166 | 0 | if(ret < 0){ if(NOQUIET) error1("reading the rest of %li", (long)(count-cnt)); return READER_ERROR; } |
167 | 0 | if(ret == 0) break; |
168 | | |
169 | 0 | cnt += ret; |
170 | 0 | fr->icy.next -= ret; |
171 | 0 | } |
172 | 0 | } |
173 | | /* debug1("done reading, got %li", (long)cnt); */ |
174 | 0 | return cnt; |
175 | 0 | } |
176 | | #else |
177 | | #define icy_fullread NULL |
178 | | #endif /* NO_ICY */ |
179 | | |
180 | | /* stream based operation */ |
181 | | static ptrdiff_t plain_fullread(mpg123_handle *fr,unsigned char *buf, ptrdiff_t count) |
182 | 0 | { |
183 | 0 | ptrdiff_t ret,cnt=0; |
184 | |
|
185 | | #ifdef EXTRA_DEBUG |
186 | | mdebug("plain fullread of %td", count); |
187 | | #endif |
188 | | /* |
189 | | There used to be a check for expected file end here (length value or ID3 flag). |
190 | | This is not needed: |
191 | | 1. EOF is indicated by fdread returning zero bytes anyway. |
192 | | 2. We get false positives of EOF for either files that grew or |
193 | | 3. ... files that have ID3v1 tags in between (stream with intro). |
194 | | */ |
195 | 0 | while(cnt < count) |
196 | 0 | { |
197 | 0 | ret = fdread(fr,buf+cnt,count-cnt); |
198 | 0 | if(ret < 0) return READER_ERROR; |
199 | 0 | if(ret == 0) break; |
200 | 0 | if(!(fr->rdat.flags & READER_BUFFERED)) |
201 | 0 | SATURATE_ADD(fr->rdat.filepos, ret, INT64_MAX); |
202 | 0 | cnt += ret; |
203 | 0 | } |
204 | 0 | return cnt; |
205 | 0 | } |
206 | | |
207 | | static int64_t stream_lseek(mpg123_handle *fr, int64_t pos, int whence) |
208 | 0 | { |
209 | 0 | int64_t ret = fdseek(fr, pos, whence); |
210 | 0 | if (ret >= 0) |
211 | 0 | fr->rdat.filepos = ret; |
212 | 0 | else |
213 | 0 | { |
214 | 0 | fr->err = MPG123_LSEEK_FAILED; |
215 | 0 | ret = READER_ERROR; /* not the original value */ |
216 | 0 | } |
217 | 0 | return ret; |
218 | 0 | } |
219 | | |
220 | | static void stream_close(mpg123_handle *fr) |
221 | 0 | { |
222 | 0 | #ifndef NO_FEEDER |
223 | 0 | if(fr->rdat.flags & READER_BUFFERED) bc_reset(&fr->rdat.buffer); |
224 | 0 | #endif |
225 | 0 | if(fr->rdat.flags & READER_HANDLEIO) |
226 | 0 | { |
227 | 0 | if(fr->rdat.cleanup_handle != NULL) |
228 | 0 | fr->rdat.cleanup_handle(fr->rdat.iohandle); |
229 | |
|
230 | 0 | fr->rdat.iohandle = NULL; |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | static int stream_seek_frame(mpg123_handle *fr, int64_t newframe) |
235 | 0 | { |
236 | 0 | debug2("seek_frame to %"PRIi64" (from %"PRIi64")", newframe, fr->num); |
237 | | /* Seekable streams can go backwards and jump forwards. |
238 | | Non-seekable streams still can go forward, just not jump. */ |
239 | 0 | if((fr->rdat.flags & READER_SEEKABLE) || (newframe >= fr->num)) |
240 | 0 | { |
241 | 0 | int64_t preframe; /* a leading frame we jump to */ |
242 | 0 | int64_t seek_to; /* the byte offset we want to reach */ |
243 | 0 | int64_t to_skip; /* bytes to skip to get there (can be negative) */ |
244 | | /* |
245 | | now seek to nearest leading index position and read from there until newframe is reached. |
246 | | We use skip_bytes, which handles seekable and non-seekable streams |
247 | | (the latter only for positive offset, which we ensured before entering here). |
248 | | */ |
249 | 0 | seek_to = INT123_frame_index_find(fr, newframe, &preframe); |
250 | | /* No need to seek to index position if we are closer already. |
251 | | But I am picky about fr->num == newframe, play safe by reading the frame again. |
252 | | If you think that's stupid, don't call a seek to the current frame. */ |
253 | 0 | if(fr->num >= newframe || fr->num < preframe) |
254 | 0 | { |
255 | 0 | to_skip = seek_to - fr->rd->tell(fr); |
256 | 0 | if(fr->rd->skip_bytes(fr, to_skip) != seek_to) |
257 | 0 | return READER_ERROR; |
258 | | |
259 | 0 | debug2("going to %lu; just got %lu", (long unsigned)newframe, (long unsigned)preframe); |
260 | 0 | fr->num = preframe-1; /* Watch out! I am going to read preframe... fr->num should indicate the frame before! */ |
261 | 0 | } |
262 | 0 | while(fr->num < newframe) |
263 | 0 | { |
264 | | /* try to be non-fatal now... frameNum only gets advanced on success anyway */ |
265 | 0 | if(!INT123_read_frame(fr)) break; |
266 | 0 | } |
267 | | /* Now the wanted frame should be ready for decoding. */ |
268 | 0 | debug1("arrived at %"PRIi64, fr->num); |
269 | |
|
270 | 0 | return MPG123_OK; |
271 | 0 | } |
272 | 0 | else |
273 | 0 | { |
274 | 0 | fr->err = MPG123_NO_SEEK; |
275 | 0 | return READER_ERROR; /* invalid, no seek happened */ |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | /* return FALSE on error, TRUE on success, READER_MORE on occasion */ |
280 | | static int generic_head_read(mpg123_handle *fr,unsigned long *newhead) |
281 | 0 | { |
282 | 0 | unsigned char hbuf[4]; |
283 | 0 | int ret = fr->rd->fullread(fr,hbuf,4); |
284 | 0 | if(ret == READER_MORE) return ret; |
285 | 0 | if(ret != 4) return FALSE; |
286 | | |
287 | 0 | *newhead = ((unsigned long) hbuf[0] << 24) | |
288 | 0 | ((unsigned long) hbuf[1] << 16) | |
289 | 0 | ((unsigned long) hbuf[2] << 8) | |
290 | 0 | (unsigned long) hbuf[3]; |
291 | |
|
292 | 0 | return TRUE; |
293 | 0 | } |
294 | | |
295 | | /* return FALSE on error, TRUE on success, READER_MORE on occasion */ |
296 | | static int generic_head_shift(mpg123_handle *fr,unsigned long *head) |
297 | 0 | { |
298 | 0 | unsigned char hbuf; |
299 | 0 | int ret = fr->rd->fullread(fr,&hbuf,1); |
300 | 0 | if(ret == READER_MORE) return ret; |
301 | 0 | if(ret != 1) return FALSE; |
302 | | |
303 | 0 | *head <<= 8; |
304 | 0 | *head |= hbuf; |
305 | 0 | *head &= 0xffffffff; |
306 | 0 | return TRUE; |
307 | 0 | } |
308 | | |
309 | | /* returns reached position... negative ones are bad... */ |
310 | | static int64_t stream_skip_bytes(mpg123_handle *fr, int64_t len) |
311 | 0 | { |
312 | 0 | if(fr->rdat.flags & READER_SEEKABLE) |
313 | 0 | { |
314 | 0 | int64_t ret = stream_lseek(fr, len, SEEK_CUR); |
315 | 0 | return (ret < 0) ? READER_ERROR : ret; |
316 | 0 | } |
317 | 0 | else if(len >= 0) |
318 | 0 | { |
319 | 0 | unsigned char buf[1024]; /* ThOr: Compaq cxx complained and it makes sense to me... or should one do a cast? What for? */ |
320 | 0 | ptrdiff_t ret; |
321 | 0 | while (len > 0) |
322 | 0 | { |
323 | 0 | ptrdiff_t num = len < (ptrdiff_t)sizeof(buf) ? (ptrdiff_t)len : (ptrdiff_t)sizeof(buf); |
324 | 0 | ret = fr->rd->fullread(fr, buf, num); |
325 | 0 | if (ret < 0) return ret; |
326 | 0 | else if(ret == 0) break; /* EOF... an error? interface defined to tell the actual position... */ |
327 | 0 | len -= ret; |
328 | 0 | } |
329 | 0 | return fr->rd->tell(fr); |
330 | 0 | } |
331 | 0 | #ifndef NO_FEEDER |
332 | 0 | else if(fr->rdat.flags & READER_BUFFERED) |
333 | 0 | { /* Perhaps we _can_ go a bit back. */ |
334 | 0 | if(fr->rdat.buffer.pos >= -len) |
335 | 0 | { |
336 | 0 | fr->rdat.buffer.pos += len; |
337 | 0 | return fr->rd->tell(fr); |
338 | 0 | } |
339 | 0 | else |
340 | 0 | { |
341 | 0 | fr->err = MPG123_NO_SEEK; |
342 | 0 | return READER_ERROR; |
343 | 0 | } |
344 | 0 | } |
345 | 0 | #endif |
346 | 0 | else |
347 | 0 | { |
348 | 0 | fr->err = MPG123_NO_SEEK; |
349 | 0 | return READER_ERROR; |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | /* Return 0 on success... */ |
354 | | static int stream_back_bytes(mpg123_handle *fr, int64_t bytes) |
355 | 0 | { |
356 | 0 | int64_t want = fr->rd->tell(fr)-bytes; |
357 | 0 | if(want < 0) return READER_ERROR; |
358 | 0 | if(stream_skip_bytes(fr,-bytes) != want) return READER_ERROR; |
359 | | |
360 | 0 | return 0; |
361 | 0 | } |
362 | | |
363 | | |
364 | | /* returns size on success... otherwise an error code < 0 */ |
365 | | static int generic_read_frame_body(mpg123_handle *fr,unsigned char *buf, int size) |
366 | 0 | { |
367 | 0 | ptrdiff_t l; |
368 | 0 | l=fr->rd->fullread(fr,buf,size); |
369 | 0 | return (l >= 0 && l<size) ? READER_ERROR : (int)l; |
370 | 0 | } |
371 | | |
372 | | static int64_t generic_tell(mpg123_handle *fr) |
373 | 0 | { |
374 | 0 | #ifndef NO_FEEDER |
375 | 0 | if(fr->rdat.flags & READER_BUFFERED) |
376 | 0 | { |
377 | 0 | fr->rdat.filepos = fr->rdat.buffer.fileoff; |
378 | 0 | SATURATE_ADD(fr->rdat.filepos, fr->rdat.buffer.pos, INT64_MAX); |
379 | 0 | } |
380 | 0 | #endif |
381 | |
|
382 | 0 | return fr->rdat.filepos; |
383 | 0 | } |
384 | | |
385 | | /* This does not (fully) work for non-seekable streams... You have to check for that flag, pal! */ |
386 | | static void stream_rewind(mpg123_handle *fr) |
387 | 0 | { |
388 | 0 | if(fr->rdat.flags & READER_SEEKABLE) |
389 | 0 | { |
390 | 0 | fr->rdat.filepos = stream_lseek(fr,0,SEEK_SET); |
391 | 0 | #ifndef NO_FEEDER |
392 | 0 | fr->rdat.buffer.fileoff = fr->rdat.filepos; |
393 | 0 | #endif |
394 | 0 | } |
395 | 0 | #ifndef NO_FEEDER |
396 | 0 | if(fr->rdat.flags & READER_BUFFERED) |
397 | 0 | { |
398 | 0 | fr->rdat.buffer.pos = 0; |
399 | 0 | fr->rdat.buffer.firstpos = 0; |
400 | 0 | fr->rdat.filepos = fr->rdat.buffer.fileoff; |
401 | 0 | } |
402 | 0 | #endif |
403 | 0 | } |
404 | | |
405 | | /* |
406 | | * returns length of a file (if filept points to a file) |
407 | | * reads the last 128 bytes information into buffer |
408 | | * ... that is not totally safe... |
409 | | */ |
410 | | static int64_t get_fileinfo(mpg123_handle *fr) |
411 | 0 | { |
412 | 0 | int64_t len; |
413 | |
|
414 | 0 | if((len=fdseek(fr,0,SEEK_END)) < 0) |
415 | 0 | { |
416 | 0 | debug("cannot seek to end"); |
417 | 0 | return -1; |
418 | 0 | } else if(len >= 128) |
419 | 0 | { |
420 | 0 | if(fdseek(fr,-128,SEEK_END) < 0) |
421 | 0 | { |
422 | 0 | debug("cannot seek to END-128"); |
423 | 0 | return -1; |
424 | 0 | } |
425 | 0 | if(fr->rd->fullread(fr,(unsigned char *)fr->id3buf,128) != 128) |
426 | 0 | { |
427 | 0 | debug("cannot read ID3v1?!"); |
428 | 0 | return -1; |
429 | 0 | } |
430 | 0 | if(!strncmp((char*)fr->id3buf,"TAG",3)) len -= 128; |
431 | 0 | } else |
432 | 0 | { |
433 | 0 | debug("stream too short for ID3"); |
434 | 0 | } |
435 | | |
436 | 0 | if(fdseek(fr,0,SEEK_SET) < 0) |
437 | 0 | { |
438 | 0 | debug("cannot seek back"); |
439 | 0 | return -1; |
440 | 0 | } |
441 | 0 | fr->rdat.filepos = 0; // un-do our seeking here |
442 | |
|
443 | 0 | debug1("returning length: %"PRIi64, len); |
444 | 0 | return len; |
445 | 0 | } |
446 | | |
447 | | #ifndef NO_FEEDER |
448 | | /* Methods for the buffer chain, mainly used for feed reader, but not just that. */ |
449 | | |
450 | | |
451 | | static struct buffy* buffy_new(ptrdiff_t size, ptrdiff_t minsize) |
452 | 0 | { |
453 | 0 | struct buffy *newbuf; |
454 | 0 | if(size > PTRDIFF_MAX) |
455 | 0 | return NULL; |
456 | 0 | newbuf = malloc(sizeof(struct buffy)); |
457 | 0 | if(newbuf == NULL) return NULL; |
458 | | |
459 | 0 | newbuf->realsize = size > minsize ? size : minsize; |
460 | 0 | newbuf->data = malloc((size_t)newbuf->realsize); |
461 | 0 | if(newbuf->data == NULL) |
462 | 0 | { |
463 | 0 | free(newbuf); |
464 | 0 | return NULL; |
465 | 0 | } |
466 | 0 | newbuf->size = 0; |
467 | 0 | newbuf->next = NULL; |
468 | 0 | return newbuf; |
469 | 0 | } |
470 | | |
471 | | static void buffy_del(struct buffy* buf) |
472 | 0 | { |
473 | 0 | if(buf) |
474 | 0 | { |
475 | 0 | free(buf->data); |
476 | 0 | free(buf); |
477 | 0 | } |
478 | 0 | } |
479 | | |
480 | | /* Delete this buffy and all following buffies. */ |
481 | | static void buffy_del_chain(struct buffy* buf) |
482 | 0 | { |
483 | 0 | while(buf) |
484 | 0 | { |
485 | 0 | struct buffy* next = buf->next; |
486 | 0 | buffy_del(buf); |
487 | 0 | buf = next; |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | void INT123_bc_prepare(struct bufferchain *bc, size_t pool_size, size_t bufblock) |
492 | 0 | { |
493 | 0 | INT123_bc_poolsize(bc, pool_size, bufblock); |
494 | 0 | bc->pool = NULL; |
495 | 0 | bc->pool_fill = 0; |
496 | 0 | bc_init(bc); /* Ensure that members are zeroed for read-only use. */ |
497 | 0 | } |
498 | | |
499 | | size_t INT123_bc_fill(struct bufferchain *bc) |
500 | 0 | { |
501 | 0 | return (size_t)(bc->size - bc->pos); |
502 | 0 | } |
503 | | |
504 | | void INT123_bc_poolsize(struct bufferchain *bc, size_t pool_size, size_t bufblock) |
505 | 0 | { |
506 | 0 | bc->pool_size = pool_size; |
507 | 0 | bc->bufblock = bufblock; |
508 | 0 | } |
509 | | |
510 | | void INT123_bc_cleanup(struct bufferchain *bc) |
511 | 0 | { |
512 | 0 | buffy_del_chain(bc->pool); |
513 | 0 | bc->pool = NULL; |
514 | 0 | bc->pool_fill = 0; |
515 | 0 | } |
516 | | |
517 | | /* Fetch a buffer from the pool (if possible) or create one. */ |
518 | | static struct buffy* bc_alloc(struct bufferchain *bc, size_t size) |
519 | 0 | { |
520 | | /* Easy route: Just try the first available buffer. |
521 | | Size does not matter, it's only a hint for creation of new buffers. */ |
522 | 0 | if(bc->pool) |
523 | 0 | { |
524 | 0 | struct buffy *buf = bc->pool; |
525 | 0 | bc->pool = buf->next; |
526 | 0 | buf->next = NULL; /* That shall be set to a sensible value later. */ |
527 | 0 | buf->size = 0; |
528 | 0 | --bc->pool_fill; |
529 | 0 | debug2("bc_alloc: picked %p from pool (fill now %zu)", (void*)buf, bc->pool_fill); |
530 | 0 | return buf; |
531 | 0 | } |
532 | 0 | else return buffy_new(size, bc->bufblock); |
533 | 0 | } |
534 | | |
535 | | /* Either stuff the buffer back into the pool or free it for good. */ |
536 | | static void bc_free(struct bufferchain *bc, struct buffy* buf) |
537 | 0 | { |
538 | 0 | if(!buf) return; |
539 | | |
540 | 0 | if(bc->pool_fill < bc->pool_size) |
541 | 0 | { |
542 | 0 | buf->next = bc->pool; |
543 | 0 | bc->pool = buf; |
544 | 0 | ++bc->pool_fill; |
545 | 0 | } |
546 | 0 | else buffy_del(buf); |
547 | 0 | } |
548 | | |
549 | | /* Make the buffer count in the pool match the pool size. */ |
550 | | static int bc_fill_pool(struct bufferchain *bc) |
551 | 0 | { |
552 | | /* Remove superfluous ones. */ |
553 | 0 | while(bc->pool_fill > bc->pool_size) |
554 | 0 | { |
555 | | /* Lazyness: Just work on the front. */ |
556 | 0 | struct buffy* buf = bc->pool; |
557 | 0 | bc->pool = buf->next; |
558 | 0 | buffy_del(buf); |
559 | 0 | --bc->pool_fill; |
560 | 0 | } |
561 | | |
562 | | /* Add missing ones. */ |
563 | 0 | while(bc->pool_fill < bc->pool_size) |
564 | 0 | { |
565 | | /* Again, just work on the front. */ |
566 | 0 | struct buffy* buf; |
567 | 0 | buf = buffy_new(0, bc->bufblock); /* Use default block size. */ |
568 | 0 | if(!buf) return -1; |
569 | | |
570 | 0 | buf->next = bc->pool; |
571 | 0 | bc->pool = buf; |
572 | 0 | ++bc->pool_fill; |
573 | 0 | } |
574 | | |
575 | 0 | return 0; |
576 | 0 | } |
577 | | |
578 | | |
579 | | static void bc_init(struct bufferchain *bc) |
580 | 0 | { |
581 | 0 | bc->first = NULL; |
582 | 0 | bc->last = bc->first; |
583 | 0 | bc->size = 0; |
584 | 0 | bc->pos = 0; |
585 | 0 | bc->firstpos = 0; |
586 | 0 | bc->fileoff = 0; |
587 | 0 | } |
588 | | |
589 | | static void bc_reset(struct bufferchain *bc) |
590 | 0 | { |
591 | | /* Free current chain, possibly stuffing back into the pool. */ |
592 | 0 | while(bc->first) |
593 | 0 | { |
594 | 0 | struct buffy* buf = bc->first; |
595 | 0 | bc->first = buf->next; |
596 | 0 | bc_free(bc, buf); |
597 | 0 | } |
598 | 0 | bc_fill_pool(bc); /* Ignoring an error here... */ |
599 | 0 | bc_init(bc); |
600 | 0 | } |
601 | | |
602 | | /* Create a new buffy at the end to be filled. */ |
603 | | static int bc_append(struct bufferchain *bc, ptrdiff_t size) |
604 | 0 | { |
605 | 0 | struct buffy *newbuf; |
606 | 0 | if(size < 1) return -1; |
607 | | |
608 | 0 | newbuf = bc_alloc(bc, size); |
609 | 0 | if(newbuf == NULL) return -2; |
610 | | |
611 | 0 | if(bc->last != NULL) bc->last->next = newbuf; |
612 | 0 | else if(bc->first == NULL) bc->first = newbuf; |
613 | |
|
614 | 0 | bc->last = newbuf; |
615 | 0 | debug3("bc_append: new last buffer %p with %td B (really %td)", (void*)bc->last, bc->last->size, bc->last->realsize); |
616 | 0 | return 0; |
617 | 0 | } |
618 | | |
619 | | /* Append a new buffer and copy content to it. */ |
620 | | static int bc_add(struct bufferchain *bc, const unsigned char *data, ptrdiff_t size) |
621 | 0 | { |
622 | 0 | int ret = 0; |
623 | 0 | ptrdiff_t part = 0; |
624 | 0 | if((size_t)(PTRDIFF_MAX - bc->size) < size) |
625 | 0 | return -1; |
626 | 0 | debug2("bc_add: adding %zu bytes at %"PRIi64, size, (int64_t)(bc->fileoff+bc->size)); |
627 | 0 | if(size >=4) debug4("first bytes: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); |
628 | |
|
629 | 0 | while(size > 0) |
630 | 0 | { |
631 | | /* Try to fill up the last buffer block. */ |
632 | 0 | if(bc->last != NULL && bc->last->size < bc->last->realsize) |
633 | 0 | { |
634 | 0 | part = bc->last->realsize - bc->last->size; |
635 | 0 | if(part > size) part = size; |
636 | |
|
637 | 0 | debug2("bc_add: adding %td B to existing block %p", part, (void*)bc->last); |
638 | 0 | memcpy(bc->last->data+bc->last->size, data, part); |
639 | 0 | bc->last->size += part; |
640 | 0 | size -= part; |
641 | 0 | bc->size += part; |
642 | 0 | data += part; |
643 | 0 | } |
644 | | |
645 | | /* If there is still data left, put it into a new buffer block. */ |
646 | 0 | if(size > 0 && (ret = bc_append(bc, size)) != 0) |
647 | 0 | break; |
648 | 0 | } |
649 | |
|
650 | 0 | return ret; |
651 | 0 | } |
652 | | |
653 | | /* Common handler for "You want more than I can give." situation. */ |
654 | | static ptrdiff_t bc_need_more(struct bufferchain *bc, ptrdiff_t size) |
655 | 0 | { |
656 | 0 | debug3("hit end, back to beginning (%td - %td < %td)", bc->size, bc->pos, size); |
657 | | /* go back to firstpos, undo the previous reads */ |
658 | 0 | bc->pos = bc->firstpos; |
659 | 0 | return READER_MORE; |
660 | 0 | } |
661 | | |
662 | | /* Give some data, advancing position but not forgetting yet. */ |
663 | | static ptrdiff_t bc_give(struct bufferchain *bc, unsigned char *out, ptrdiff_t size) |
664 | 0 | { |
665 | 0 | struct buffy *b = bc->first; |
666 | 0 | ptrdiff_t gotcount = 0; |
667 | 0 | ptrdiff_t offset = 0; |
668 | 0 | if(bc->size - bc->pos < size) return bc_need_more(bc, size); |
669 | | |
670 | | /* find the current buffer */ |
671 | 0 | while(b != NULL && (offset + b->size) <= bc->pos) |
672 | 0 | { |
673 | 0 | offset += b->size; |
674 | 0 | b = b->next; |
675 | 0 | } |
676 | | /* now start copying from there */ |
677 | 0 | while(gotcount < size && (b != NULL)) |
678 | 0 | { |
679 | 0 | ptrdiff_t loff = bc->pos - offset; |
680 | 0 | ptrdiff_t chunk = size - gotcount; /* amount of bytes to get from here... */ |
681 | 0 | if(chunk > b->size - loff) chunk = b->size - loff; |
682 | |
|
683 | | #ifdef EXTRA_DEBUG |
684 | | debug3("copying %liB from %p+%li",(long)chunk, b->data, (long)loff); |
685 | | #endif |
686 | |
|
687 | 0 | memcpy(out+gotcount, b->data+loff, chunk); |
688 | 0 | gotcount += chunk; |
689 | 0 | bc->pos += chunk; |
690 | 0 | offset += b->size; |
691 | 0 | b = b->next; |
692 | 0 | } |
693 | | #ifdef EXTRA_DEBUG |
694 | | debug2("got %li bytes, pos advanced to %li", (long)gotcount, (long)bc->pos); |
695 | | #endif |
696 | |
|
697 | 0 | return gotcount; |
698 | 0 | } |
699 | | |
700 | | /* Skip some bytes and return the new position. |
701 | | The buffers are still there, just the read pointer is moved! */ |
702 | | static ptrdiff_t bc_skip(struct bufferchain *bc, ptrdiff_t count) |
703 | 0 | { |
704 | 0 | if(count >= 0) |
705 | 0 | { |
706 | 0 | if(bc->size - bc->pos < count) return bc_need_more(bc, count); |
707 | 0 | else return bc->pos += count; |
708 | 0 | } |
709 | 0 | else return READER_ERROR; |
710 | 0 | } |
711 | | |
712 | | static ptrdiff_t bc_seekback(struct bufferchain *bc, ptrdiff_t count) |
713 | 0 | { |
714 | 0 | if(count >= 0 && count <= bc->pos) return bc->pos -= count; |
715 | 0 | else return READER_ERROR; |
716 | 0 | } |
717 | | |
718 | | /* Throw away buffies that we passed. */ |
719 | | static void bc_forget(struct bufferchain *bc) |
720 | 0 | { |
721 | 0 | struct buffy *b = bc->first; |
722 | | /* free all buffers that are def'n'tly outdated */ |
723 | | /* we have buffers until filepos... delete all buffers fully below it */ |
724 | 0 | if(b) debug2("bc_forget: block %td pos %td", b->size, bc->pos); |
725 | 0 | else debug("forget with nothing there!"); |
726 | |
|
727 | 0 | while(b != NULL && bc->pos >= b->size) |
728 | 0 | { |
729 | 0 | struct buffy *n = b->next; /* != NULL or this is indeed the end and the last cycle anyway */ |
730 | 0 | if(n == NULL) bc->last = NULL; /* Going to delete the last buffy... */ |
731 | 0 | bc->fileoff += b->size; |
732 | 0 | bc->pos -= b->size; |
733 | 0 | bc->size -= b->size; |
734 | |
|
735 | 0 | debug5("bc_forget: forgot %p with %td, pos=%td, size=%td, fileoff=%td" |
736 | 0 | , (void*)b->data, b->size, bc->pos, bc->size, bc->fileoff); |
737 | |
|
738 | 0 | bc_free(bc, b); |
739 | 0 | b = n; |
740 | 0 | } |
741 | 0 | bc->first = b; |
742 | 0 | bc->firstpos = bc->pos; |
743 | 0 | } |
744 | | |
745 | | /* reader for input via manually provided buffers */ |
746 | | |
747 | | static int feed_init(mpg123_handle *fr) |
748 | 0 | { |
749 | 0 | bc_init(&fr->rdat.buffer); |
750 | 0 | bc_fill_pool(&fr->rdat.buffer); |
751 | 0 | fr->rdat.filelen = 0; |
752 | 0 | fr->rdat.filepos = 0; |
753 | 0 | fr->rdat.flags |= READER_BUFFERED; |
754 | 0 | return 0; |
755 | 0 | } |
756 | | |
757 | | /* externally called function, returns 0 on success, -1 on error */ |
758 | | // External API uses size_t, we use signed ptrdiff_t internally. Overflow |
759 | | // is a theoretical possibility. |
760 | | int INT123_feed_more(mpg123_handle *fr, const unsigned char *in, size_t count) |
761 | 0 | { |
762 | 0 | int ret = 0; |
763 | 0 | if(VERBOSE3) debug("INT123_feed_more"); |
764 | 0 | if(count > PTRDIFF_MAX) |
765 | 0 | return READER_ERROR; |
766 | 0 | if((ret = bc_add(&fr->rdat.buffer, in, (ptrdiff_t)count)) != 0) |
767 | 0 | { |
768 | 0 | ret = READER_ERROR; |
769 | 0 | if(NOQUIET) error1("Failed to add buffer, return: %i", ret); |
770 | 0 | } |
771 | 0 | else /* Not talking about filelen... that stays at 0. */ |
772 | | |
773 | 0 | if(VERBOSE3) debug3("INT123_feed_more: %p %luB bufsize=%lu", fr->rdat.buffer.last->data, |
774 | 0 | (unsigned long)fr->rdat.buffer.last->size, (unsigned long)fr->rdat.buffer.size); |
775 | 0 | return ret; |
776 | 0 | } |
777 | | |
778 | | static ptrdiff_t feed_read(mpg123_handle *fr, unsigned char *out, ptrdiff_t count) |
779 | 0 | { |
780 | 0 | ptrdiff_t gotcount = bc_give(&fr->rdat.buffer, out, count); |
781 | 0 | if(gotcount >= 0 && gotcount != count) return READER_ERROR; |
782 | 0 | else return gotcount; |
783 | 0 | } |
784 | | |
785 | | /* returns reached position... negative ones are bad... */ |
786 | | static int64_t feed_skip_bytes(mpg123_handle *fr, int64_t len) |
787 | 0 | { |
788 | | /* This is either the new buffer offset or some negative error value. */ |
789 | 0 | int64_t res = bc_skip(&fr->rdat.buffer, (ptrdiff_t)len); |
790 | 0 | if(res < 0) return res; |
791 | | |
792 | 0 | return fr->rdat.buffer.fileoff+res; |
793 | 0 | } |
794 | | |
795 | | static int feed_back_bytes(mpg123_handle *fr, int64_t bytes) |
796 | 0 | { |
797 | 0 | if(bytes >=0) |
798 | 0 | return bc_seekback(&fr->rdat.buffer, (ptrdiff_t)bytes) >= 0 ? 0 : READER_ERROR; |
799 | 0 | else |
800 | 0 | return feed_skip_bytes(fr, -bytes) >= 0 ? 0 : READER_ERROR; |
801 | 0 | } |
802 | | |
803 | 0 | static int feed_seek_frame(mpg123_handle *fr, int64_t num){ return READER_ERROR; } |
804 | | |
805 | | /* Not just for feed reader, also for self-feeding buffered reader. */ |
806 | | static void buffered_forget(mpg123_handle *fr) |
807 | 0 | { |
808 | 0 | bc_forget(&fr->rdat.buffer); |
809 | 0 | fr->rdat.filepos = fr->rdat.buffer.fileoff; |
810 | 0 | SATURATE_ADD(fr->rdat.filepos, fr->rdat.buffer.pos, INT64_MAX); |
811 | 0 | } |
812 | | |
813 | | int64_t INT123_feed_set_pos(mpg123_handle *fr, int64_t pos) |
814 | 0 | { |
815 | 0 | struct bufferchain *bc = &fr->rdat.buffer; |
816 | 0 | if(pos >= bc->fileoff && pos-bc->fileoff < bc->size) |
817 | 0 | { /* We have the position! */ |
818 | 0 | bc->pos = (ptrdiff_t)(pos - bc->fileoff); |
819 | 0 | debug1("INT123_feed_set_pos inside, next feed from %"PRIi64, (int64_t)(bc->fileoff+bc->size)); |
820 | 0 | return bc->fileoff+bc->size; /* Next input after end of buffer... */ |
821 | 0 | } |
822 | 0 | else |
823 | 0 | { /* I expect to get the specific position on next feed. Forget what I have now. */ |
824 | 0 | bc_reset(bc); |
825 | 0 | bc->fileoff = pos; |
826 | 0 | debug1("INT123_feed_set_pos outside, buffer reset, next feed from %"PRIi64, pos); |
827 | 0 | return pos; /* Next input from exactly that position. */ |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | | /* The specific stuff for buffered stream reader. */ |
832 | | |
833 | | static ptrdiff_t buffered_fullread(mpg123_handle *fr, unsigned char *out, ptrdiff_t count |
834 | | , ptrdiff_t (*fullread)(mpg123_handle *, unsigned char *, ptrdiff_t)) |
835 | 0 | { |
836 | 0 | struct bufferchain *bc = &fr->rdat.buffer; |
837 | 0 | ptrdiff_t gotcount; |
838 | 0 | if(VERBOSE3) |
839 | 0 | mdebug("buffered_fullread: want %zd", count); |
840 | 0 | if(bc->size - bc->pos < count) |
841 | 0 | { /* Add more stuff to buffer. If hitting end of file, adjust count. */ |
842 | 0 | unsigned char readbuf[4096]; |
843 | 0 | ptrdiff_t need = count - (bc->size-bc->pos); |
844 | 0 | while(need>0) |
845 | 0 | { |
846 | 0 | int ret; |
847 | 0 | ptrdiff_t got = fullread(fr, readbuf, sizeof(readbuf)); |
848 | 0 | if(got < 0) |
849 | 0 | { |
850 | 0 | if(NOQUIET) error("buffer reading"); |
851 | 0 | return READER_ERROR; |
852 | 0 | } |
853 | | |
854 | 0 | if(VERBOSE3) |
855 | 0 | debug1("buffered_fullread: buffering %td bytes from stream (if > 0)", got); |
856 | 0 | if(got > 0 && (ret=bc_add(bc, readbuf, got)) != 0) |
857 | 0 | { |
858 | 0 | if(NOQUIET) error1("unable to add to chain, return: %i", ret); |
859 | 0 | return READER_ERROR; |
860 | 0 | } |
861 | | |
862 | 0 | need -= got; /* May underflow here... */ |
863 | 0 | if(got < sizeof(readbuf)) /* That naturally catches got == 0, too. */ |
864 | 0 | { |
865 | 0 | if(VERBOSE3) fprintf(stderr, "Note: Input data end.\n"); |
866 | 0 | break; /* End. */ |
867 | 0 | } |
868 | 0 | } |
869 | 0 | if(bc->size - bc->pos < count) |
870 | 0 | count = bc->size - bc->pos; /* We want only what we got. */ |
871 | 0 | } |
872 | 0 | gotcount = bc_give(bc, out, count); |
873 | 0 | if(VERBOSE3) |
874 | 0 | mdebug("buffered_fullread: got %td", gotcount); |
875 | 0 | if(gotcount != count){ if(NOQUIET) error("gotcount != count"); return READER_ERROR; } |
876 | 0 | else return gotcount; |
877 | 0 | } |
878 | | |
879 | | static ptrdiff_t buffered_plain_fullread(mpg123_handle *fr, unsigned char *out, ptrdiff_t count) |
880 | 0 | { |
881 | 0 | return buffered_fullread(fr, out, count, plain_fullread); |
882 | 0 | } |
883 | | |
884 | | static ptrdiff_t buffered_icy_fullread(mpg123_handle *fr, unsigned char *out, ptrdiff_t count) |
885 | 0 | { |
886 | 0 | return buffered_fullread(fr, out, count, icy_fullread); |
887 | 0 | } |
888 | | |
889 | | #else |
890 | | int INT123_feed_more(mpg123_handle *fr, const unsigned char *in, size_t count) |
891 | | { |
892 | | fr->err = MPG123_MISSING_FEATURE; |
893 | | return -1; |
894 | | } |
895 | | int64_t INT123_feed_set_pos(mpg123_handle *fr, int64_t pos) |
896 | | { |
897 | | fr->err = MPG123_MISSING_FEATURE; |
898 | | return -1; |
899 | | } |
900 | | #endif /* NO_FEEDER */ |
901 | | |
902 | | /***************************************************************** |
903 | | * read frame helper |
904 | | */ |
905 | | |
906 | 0 | #define bugger_off { mh->err = MPG123_NO_READER; return MPG123_ERR; } Unexecuted instantiation: readers.c:bad_init Unexecuted instantiation: readers.c:bad_fullread Unexecuted instantiation: readers.c:bad_head_read Unexecuted instantiation: readers.c:bad_head_shift Unexecuted instantiation: readers.c:bad_skip_bytes Unexecuted instantiation: readers.c:bad_read_frame_body Unexecuted instantiation: readers.c:bad_back_bytes Unexecuted instantiation: readers.c:bad_seek_frame Unexecuted instantiation: readers.c:bad_tell |
907 | | static int bad_init(mpg123_handle *mh) bugger_off |
908 | 0 | static void bad_close(mpg123_handle *mh){} |
909 | | static ptrdiff_t bad_fullread(mpg123_handle *mh, unsigned char *data, ptrdiff_t count) bugger_off |
910 | | static int bad_head_read(mpg123_handle *mh, unsigned long *newhead) bugger_off |
911 | | static int bad_head_shift(mpg123_handle *mh, unsigned long *head) bugger_off |
912 | | static int64_t bad_skip_bytes(mpg123_handle *mh, int64_t len) bugger_off |
913 | | static int bad_read_frame_body(mpg123_handle *mh, unsigned char *data, int size) bugger_off |
914 | | static int bad_back_bytes(mpg123_handle *mh, int64_t bytes) bugger_off |
915 | | static int bad_seek_frame(mpg123_handle *mh, int64_t num) bugger_off |
916 | | static int64_t bad_tell(mpg123_handle *mh) bugger_off |
917 | 0 | static void bad_rewind(mpg123_handle *mh){} |
918 | | #undef bugger_off |
919 | | |
920 | 0 | #define READER_STREAM 0 |
921 | 0 | #define READER_ICY_STREAM 1 |
922 | 0 | #define READER_FEED 2 |
923 | 0 | #define READER_BUF_STREAM 3 |
924 | 0 | #define READER_BUF_ICY_STREAM 4 |
925 | | static struct reader readers[] = |
926 | | { |
927 | | { /* READER_STREAM */ |
928 | | stream_init, |
929 | | stream_close, |
930 | | plain_fullread, |
931 | | generic_head_read, |
932 | | generic_head_shift, |
933 | | stream_skip_bytes, |
934 | | generic_read_frame_body, |
935 | | stream_back_bytes, |
936 | | stream_seek_frame, |
937 | | generic_tell, |
938 | | stream_rewind, |
939 | | NULL |
940 | | } , |
941 | | { /* READER_ICY_STREAM */ |
942 | | stream_init, |
943 | | stream_close, |
944 | | icy_fullread, |
945 | | generic_head_read, |
946 | | generic_head_shift, |
947 | | stream_skip_bytes, |
948 | | generic_read_frame_body, |
949 | | stream_back_bytes, |
950 | | stream_seek_frame, |
951 | | generic_tell, |
952 | | stream_rewind, |
953 | | NULL |
954 | | }, |
955 | | #ifdef NO_FEEDER |
956 | | #define feed_init NULL |
957 | | #define feed_read NULL |
958 | | #define buffered_fullread NULL |
959 | | #define buffered_plain_fullread NULL |
960 | | #define buffered_icy_fullread NULL |
961 | | #define feed_seek_frame NULL |
962 | | #define feed_back_bytes NULL |
963 | | #define feed_skip_bytes NULL |
964 | | #define buffered_forget NULL |
965 | | #endif |
966 | | { /* READER_FEED */ |
967 | | feed_init, |
968 | | stream_close, |
969 | | feed_read, |
970 | | generic_head_read, |
971 | | generic_head_shift, |
972 | | feed_skip_bytes, |
973 | | generic_read_frame_body, |
974 | | feed_back_bytes, |
975 | | feed_seek_frame, |
976 | | generic_tell, |
977 | | stream_rewind, |
978 | | buffered_forget |
979 | | }, |
980 | | { /* READER_BUF_STREAM */ |
981 | | stream_init, |
982 | | stream_close, |
983 | | buffered_plain_fullread, |
984 | | generic_head_read, |
985 | | generic_head_shift, |
986 | | stream_skip_bytes, |
987 | | generic_read_frame_body, |
988 | | stream_back_bytes, |
989 | | stream_seek_frame, |
990 | | generic_tell, |
991 | | stream_rewind, |
992 | | buffered_forget |
993 | | } , |
994 | | { /* READER_BUF_ICY_STREAM */ |
995 | | stream_init, |
996 | | stream_close, |
997 | | buffered_icy_fullread, |
998 | | generic_head_read, |
999 | | generic_head_shift, |
1000 | | stream_skip_bytes, |
1001 | | generic_read_frame_body, |
1002 | | stream_back_bytes, |
1003 | | stream_seek_frame, |
1004 | | generic_tell, |
1005 | | stream_rewind, |
1006 | | buffered_forget |
1007 | | }, |
1008 | | }; |
1009 | | |
1010 | | static struct reader bad_reader = |
1011 | | { |
1012 | | bad_init, |
1013 | | bad_close, |
1014 | | bad_fullread, |
1015 | | bad_head_read, |
1016 | | bad_head_shift, |
1017 | | bad_skip_bytes, |
1018 | | bad_read_frame_body, |
1019 | | bad_back_bytes, |
1020 | | bad_seek_frame, |
1021 | | bad_tell, |
1022 | | bad_rewind, |
1023 | | NULL |
1024 | | }; |
1025 | | |
1026 | | static int stream_init(mpg123_handle *fr) |
1027 | 0 | { |
1028 | 0 | #ifndef NO_ICY |
1029 | | /* ICY streams of any sort shall not be seekable. */ |
1030 | 0 | if(fr->p.icy_interval > 0) fr->rdat.flags |= READER_NOSEEK; |
1031 | 0 | #endif |
1032 | |
|
1033 | 0 | fr->rdat.filepos = 0; |
1034 | 0 | fr->rdat.filelen = fr->p.flags & MPG123_NO_PEEK_END ? -1 : get_fileinfo(fr); |
1035 | 0 | if(fr->p.flags & MPG123_FORCE_SEEKABLE) |
1036 | 0 | { |
1037 | 0 | fr->rdat.flags |= READER_SEEKABLE; |
1038 | 0 | fr->rdat.flags &= ~READER_NOSEEK; |
1039 | 0 | } |
1040 | | /* |
1041 | | Don't enable seeking on ICY streams, just plain normal files. |
1042 | | This check is necessary since the client can enforce ICY parsing on files that would otherwise be seekable. |
1043 | | It is a task for the future to make the ICY parsing safe with seeks ... or not. |
1044 | | */ |
1045 | 0 | if(fr->rdat.filelen >= 0) |
1046 | 0 | { |
1047 | 0 | debug("seekable stream"); |
1048 | 0 | fr->rdat.flags |= READER_SEEKABLE; |
1049 | 0 | if(!strncmp((char*)fr->id3buf,"TAG",3)) |
1050 | 0 | { |
1051 | 0 | fr->rdat.flags |= READER_ID3TAG; |
1052 | 0 | fr->metaflags |= MPG123_NEW_ID3; |
1053 | 0 | } |
1054 | 0 | } |
1055 | | /* Switch reader to a buffered one, if allowed. */ |
1056 | 0 | else if(fr->p.flags & MPG123_SEEKBUFFER) |
1057 | 0 | { |
1058 | | #ifdef NO_FEEDER |
1059 | | if(NOQUIET) |
1060 | | error("Buffered readers not supported in this build."); |
1061 | | fr->err = MPG123_MISSING_FEATURE; |
1062 | | return -1; |
1063 | | #else |
1064 | 0 | if (fr->rd == &readers[READER_STREAM]) |
1065 | 0 | { |
1066 | 0 | debug("switching to buffered stream reader"); |
1067 | 0 | fr->rd = &readers[READER_BUF_STREAM]; |
1068 | 0 | } |
1069 | 0 | #ifndef NO_ICY |
1070 | 0 | else if(fr->rd == &readers[READER_ICY_STREAM]) |
1071 | 0 | { |
1072 | 0 | debug("switching to buffered ICY stream reader"); |
1073 | 0 | fr->rd = &readers[READER_BUF_ICY_STREAM]; |
1074 | 0 | } |
1075 | 0 | #endif |
1076 | 0 | else |
1077 | 0 | { |
1078 | 0 | if(NOQUIET) error("mpg123 Programmer's fault: invalid reader"); |
1079 | 0 | return -1; |
1080 | 0 | } |
1081 | 0 | bc_init(&fr->rdat.buffer); |
1082 | 0 | fr->rdat.filelen = 0; /* We carry the offset, but never know how big the stream is. */ |
1083 | 0 | fr->rdat.flags |= READER_BUFFERED; |
1084 | 0 | #endif /* NO_ICY */ |
1085 | 0 | } |
1086 | 0 | return 0; |
1087 | 0 | } |
1088 | | |
1089 | | |
1090 | | void INT123_open_bad(mpg123_handle *mh) |
1091 | 0 | { |
1092 | 0 | debug("INT123_open_bad"); |
1093 | 0 | #ifndef NO_ICY |
1094 | 0 | INT123_clear_icy(&mh->icy); |
1095 | 0 | #endif |
1096 | 0 | mh->rd = &bad_reader; |
1097 | 0 | mh->rdat.flags = 0; |
1098 | 0 | #ifndef NO_FEEDER |
1099 | 0 | bc_init(&mh->rdat.buffer); |
1100 | 0 | #endif |
1101 | 0 | mh->rdat.filelen = -1; |
1102 | 0 | } |
1103 | | |
1104 | | int INT123_open_feed(mpg123_handle *fr) |
1105 | 0 | { |
1106 | 0 | debug("feed reader"); |
1107 | | #ifdef NO_FEEDER |
1108 | | if(NOQUIET) |
1109 | | error("Buffered readers not supported in this build."); |
1110 | | fr->err = MPG123_MISSING_FEATURE; |
1111 | | return -1; |
1112 | | #else |
1113 | 0 | #ifndef NO_ICY |
1114 | 0 | if(fr->p.icy_interval > 0) |
1115 | 0 | { |
1116 | 0 | if(NOQUIET) error("Feed reader cannot do ICY parsing!"); |
1117 | |
|
1118 | 0 | return -1; |
1119 | 0 | } |
1120 | 0 | INT123_clear_icy(&fr->icy); |
1121 | 0 | #endif |
1122 | 0 | fr->rd = &readers[READER_FEED]; |
1123 | 0 | fr->rdat.flags = 0; |
1124 | 0 | if(fr->rd->init(fr) < 0) return -1; |
1125 | | |
1126 | 0 | debug("feed reader init successful"); |
1127 | 0 | return 0; |
1128 | 0 | #endif /* NO_FEEDER */ |
1129 | 0 | } |
1130 | | |
1131 | | /* Final code common to open_stream and INT123_open_stream_handle. */ |
1132 | | int INT123_open_stream_handle(mpg123_handle *fr, void *iohandle) |
1133 | 0 | { |
1134 | 0 | INT123_clear_icy(&fr->icy); /* can be done inside frame_clear ...? */ |
1135 | 0 | fr->rdat.filelen = -1; |
1136 | 0 | fr->rdat.iohandle = iohandle; |
1137 | 0 | fr->rdat.flags = 0; |
1138 | 0 | fr->rdat.flags |= READER_HANDLEIO; |
1139 | |
|
1140 | 0 | #ifndef NO_ICY |
1141 | 0 | if(fr->p.icy_interval > 0) |
1142 | 0 | { |
1143 | 0 | debug("ICY reader"); |
1144 | 0 | fr->icy.interval = fr->p.icy_interval; |
1145 | 0 | fr->icy.next = fr->icy.interval; |
1146 | 0 | fr->rd = &readers[READER_ICY_STREAM]; |
1147 | 0 | } |
1148 | 0 | else |
1149 | 0 | #endif |
1150 | 0 | { |
1151 | 0 | fr->rd = &readers[READER_STREAM]; |
1152 | 0 | debug("stream reader"); |
1153 | 0 | } |
1154 | |
|
1155 | 0 | if(fr->rd->init(fr) < 0) return -1; |
1156 | | |
1157 | 0 | return MPG123_OK; |
1158 | 0 | } |