demux: asf: don't use demux_t for packetsys
[vlc.git] / modules / stream_filter / cache_read.c
blob046c1d9279e17becccdc92854ee0f5e153c3c7e5
1 /*****************************************************************************
2 * cache_read.c
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <string.h>
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_stream.h>
34 #include <vlc_interrupt.h>
36 // #define STREAM_DEBUG 1
39 * Complex scheme using mutliple track to avoid seeking
42 /* How many tracks we have, currently only used for stream mode */
43 #ifdef OPTIMIZE_MEMORY
44 # define STREAM_CACHE_TRACK 1
45 /* Max size of our cache 128Ko per track */
46 # define STREAM_CACHE_SIZE (STREAM_CACHE_TRACK*1024*128)
47 #else
48 # define STREAM_CACHE_TRACK 3
49 /* Max size of our cache 4Mo per track */
50 # define STREAM_CACHE_SIZE (4*STREAM_CACHE_TRACK*1024*1024)
51 #endif
53 /* How many data we try to prebuffer
54 * XXX it should be small to avoid useless latency but big enough for
55 * efficient demux probing */
56 #define STREAM_CACHE_PREBUFFER_SIZE (128)
58 /* Method:
59 * - We use ring buffers, only one if unseekable, all if seekable
60 * - Upon seek date current ring, then search if one ring match the pos,
61 * yes: switch to it, seek the access to match the end of the ring
62 * no: search the ring with i_end the closer to i_pos,
63 * if close enough, read data and use this ring
64 * else use the oldest ring, seek and use it.
66 * TODO: - with access non seekable: use all space available for only one ring, but
67 * we have to support seekable/non-seekable switch on the fly.
68 * - compute a good value for i_read_size
69 * - ?
71 #define STREAM_READ_ATONCE 1024
72 #define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)
74 typedef struct
76 vlc_tick_t date;
78 uint64_t i_start;
79 uint64_t i_end;
81 uint8_t *p_buffer;
83 } stream_track_t;
85 typedef struct
87 uint64_t i_pos; /* Current reading offset */
89 unsigned i_offset; /* Buffer offset in the current track */
90 int i_tk; /* Current track */
91 stream_track_t tk[STREAM_CACHE_TRACK];
93 /* Global buffer */
94 uint8_t *p_buffer;
96 /* */
97 unsigned i_used; /* Used since last read */
98 unsigned i_read_size;
100 struct
102 /* Stat about reading data */
103 uint64_t i_read_count;
104 uint64_t i_bytes;
105 vlc_tick_t i_read_time;
106 } stat;
107 } stream_sys_t;
109 static int AStreamRefillStream(stream_t *s)
111 stream_sys_t *sys = s->p_sys;
112 stream_track_t *tk = &sys->tk[sys->i_tk];
114 /* We read but won't increase i_start after initial start + offset */
115 int i_toread =
116 __MIN(sys->i_used, STREAM_CACHE_TRACK_SIZE -
117 (tk->i_end - tk->i_start - sys->i_offset));
119 if (i_toread <= 0) return VLC_SUCCESS; /* EOF */
121 #ifdef STREAM_DEBUG
122 msg_Dbg(s, "AStreamRefillStream: used=%d toread=%d",
123 sys->i_used, i_toread);
124 #endif
126 vlc_tick_t start = vlc_tick_now();
127 while (i_toread > 0)
129 int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE;
130 int i_read;
132 if (vlc_killed())
133 return VLC_EGENERIC;
135 i_read = __MIN(i_toread, STREAM_CACHE_TRACK_SIZE - i_off);
136 i_read = vlc_stream_Read(s->s, &tk->p_buffer[i_off], i_read);
138 /* msg_Dbg(s, "AStreamRefillStream: read=%d", i_read); */
139 if (i_read < 0)
141 continue;
143 else if (i_read == 0)
144 return VLC_SUCCESS;
146 /* Update end */
147 tk->i_end += i_read;
149 /* Windows of STREAM_CACHE_TRACK_SIZE */
150 if (tk->i_start + STREAM_CACHE_TRACK_SIZE < tk->i_end)
152 unsigned i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
154 tk->i_start += i_invalid;
155 sys->i_offset -= i_invalid;
158 i_toread -= i_read;
159 sys->i_used -= i_read;
161 sys->stat.i_bytes += i_read;
162 sys->stat.i_read_count++;
165 sys->stat.i_read_time += vlc_tick_now() - start;
166 return VLC_SUCCESS;
169 static void AStreamPrebufferStream(stream_t *s)
171 stream_sys_t *sys = s->p_sys;
172 vlc_tick_t start = vlc_tick_now();
173 bool first = true;
175 msg_Dbg(s, "starting pre-buffering");
176 for (;;)
178 stream_track_t *tk = &sys->tk[sys->i_tk];
179 vlc_tick_t now = vlc_tick_now();
181 int i_read;
182 int i_buffered = tk->i_end - tk->i_start;
184 if (vlc_killed() || i_buffered >= STREAM_CACHE_PREBUFFER_SIZE)
186 int64_t i_byterate;
188 /* Update stat */
189 sys->stat.i_bytes = i_buffered;
190 sys->stat.i_read_time = now - start;
191 i_byterate = (CLOCK_FREQ * sys->stat.i_bytes) /
192 (sys->stat.i_read_time+1);
194 msg_Dbg(s, "pre-buffering done %"PRId64" bytes in %"PRId64"s - "
195 "%"PRId64" KiB/s", sys->stat.i_bytes,
196 SEC_FROM_VLC_TICK(sys->stat.i_read_time), i_byterate / 1024);
197 break;
200 i_read = STREAM_CACHE_TRACK_SIZE - i_buffered;
201 i_read = __MIN((int)sys->i_read_size, i_read);
202 i_read = vlc_stream_Read(s->s, &tk->p_buffer[i_buffered], i_read);
203 if (i_read < 0)
204 continue;
205 else if (i_read == 0)
206 break; /* EOF */
208 if (first)
210 msg_Dbg(s, "received first data after %"PRId64" ms",
211 MS_FROM_VLC_TICK(vlc_tick_now() - start));
212 first = false;
215 tk->i_end += i_read;
216 sys->stat.i_read_count++;
220 /****************************************************************************
221 * AStreamControlReset:
222 ****************************************************************************/
223 static void AStreamControlReset(stream_t *s)
225 stream_sys_t *sys = s->p_sys;
227 sys->i_pos = 0;
229 /* Setup our tracks */
230 sys->i_offset = 0;
231 sys->i_tk = 0;
232 sys->i_used = 0;
234 for (unsigned i = 0; i < STREAM_CACHE_TRACK; i++)
236 sys->tk[i].date = 0;
237 sys->tk[i].i_start = sys->i_pos;
238 sys->tk[i].i_end = sys->i_pos;
241 /* Do the prebuffering */
242 AStreamPrebufferStream(s);
245 static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
247 stream_sys_t *sys = s->p_sys;
248 stream_track_t *tk = &sys->tk[sys->i_tk];
250 if (tk->i_start >= tk->i_end)
251 return 0; /* EOF */
253 #ifdef STREAM_DEBUG
254 msg_Dbg(s, "AStreamReadStream: %zd pos=%"PRId64" tk=%d start=%"PRId64
255 " offset=%d end=%"PRId64, len, sys->i_pos, sys->i_tk,
256 tk->i_start, sys->i_offset, tk->i_end);
257 #endif
259 unsigned i_off = (tk->i_start + sys->i_offset) % STREAM_CACHE_TRACK_SIZE;
260 size_t i_current = __MIN(tk->i_end - tk->i_start - sys->i_offset,
261 STREAM_CACHE_TRACK_SIZE - i_off);
262 ssize_t i_copy = __MIN(i_current, len);
263 if (i_copy <= 0)
264 return 0; /* EOF */
266 /* Copy data */
267 /* msg_Dbg(s, "AStreamReadStream: copy %zd", i_copy); */
268 if (buf != NULL)
269 memcpy(buf, &tk->p_buffer[i_off], i_copy);
270 sys->i_offset += i_copy;
272 /* Update pos now */
273 sys->i_pos += i_copy;
275 /* */
276 sys->i_used += i_copy;
278 if (tk->i_end + i_copy <= tk->i_start + sys->i_offset + len)
280 const size_t i_read_requested = VLC_CLIP(len - i_copy,
281 STREAM_READ_ATONCE / 2,
282 STREAM_READ_ATONCE * 10);
283 if (sys->i_used < i_read_requested)
284 sys->i_used = i_read_requested;
286 AStreamRefillStream(s);
289 return i_copy;
292 static int AStreamSeekStream(stream_t *s, uint64_t i_pos)
294 stream_sys_t *sys = s->p_sys;
295 stream_track_t *p_current = &sys->tk[sys->i_tk];
297 if (p_current->i_start >= p_current->i_end && i_pos >= p_current->i_end)
298 return 0; /* EOF */
300 #ifdef STREAM_DEBUG
301 msg_Dbg(s, "AStreamSeekStream: to %"PRId64" pos=%"PRId64
302 " tk=%d start=%"PRId64" offset=%d end=%"PRId64,
303 i_pos, sys->i_pos, sys->i_tk, p_current->i_start,
304 sys->i_offset, p_current->i_end);
305 #endif
307 bool b_aseek;
308 vlc_stream_Control(s->s, STREAM_CAN_SEEK, &b_aseek);
309 if (!b_aseek && i_pos < p_current->i_start)
311 msg_Warn(s, "AStreamSeekStream: can't seek");
312 return VLC_EGENERIC;
315 bool b_afastseek;
316 vlc_stream_Control(s->s, STREAM_CAN_FASTSEEK, &b_afastseek);
318 /* FIXME compute seek cost (instead of static 'stupid' value) */
319 uint64_t i_skip_threshold;
320 if (b_aseek)
321 i_skip_threshold = b_afastseek ? 128 : 3 * sys->i_read_size;
322 else
323 i_skip_threshold = INT64_MAX;
325 /* Date the current track */
326 p_current->date = vlc_tick_now();
328 /* Search a new track slot */
329 stream_track_t *tk = NULL;
330 int i_tk_idx = -1;
332 /* Prefer the current track */
333 if (p_current->i_start <= i_pos && i_pos <= p_current->i_end + i_skip_threshold)
335 tk = p_current;
336 i_tk_idx = sys->i_tk;
338 if (!tk)
340 /* Try to maximize already read data */
341 for (int i = 0; i < STREAM_CACHE_TRACK; i++)
343 stream_track_t *t = &sys->tk[i];
345 if (t->i_start > i_pos || i_pos > t->i_end)
346 continue;
348 if (!tk || tk->i_end < t->i_end)
350 tk = t;
351 i_tk_idx = i;
355 if (!tk)
357 /* Use the oldest unused */
358 for (int i = 0; i < STREAM_CACHE_TRACK; i++)
360 stream_track_t *t = &sys->tk[i];
362 if (!tk || tk->date > t->date)
364 tk = t;
365 i_tk_idx = i;
369 assert(i_tk_idx >= 0 && i_tk_idx < STREAM_CACHE_TRACK);
371 if (tk != p_current)
372 i_skip_threshold = 0;
373 if (tk->i_start <= i_pos && i_pos <= tk->i_end + i_skip_threshold)
375 #ifdef STREAM_DEBUG
376 msg_Err(s, "AStreamSeekStream: reusing %d start=%"PRId64
377 " end=%"PRId64"(%s)",
378 i_tk_idx, tk->i_start, tk->i_end,
379 tk != p_current ? "seek" : i_pos > tk->i_end ? "skip" : "noseek");
380 #endif
381 if (tk != p_current)
383 assert(b_aseek);
385 /* Seek at the end of the buffer
386 * TODO it is stupid to seek now, it would be better to delay it
388 if (vlc_stream_Seek(s->s, tk->i_end))
390 msg_Err(s, "AStreamSeekStream: hard seek failed");
391 return VLC_EGENERIC;
394 else if (i_pos > tk->i_end)
396 uint64_t i_skip = i_pos - tk->i_end;
397 while (i_skip > 0)
399 const int i_read_max = __MIN(10 * STREAM_READ_ATONCE, i_skip);
400 int i_read = 0;
401 if ((i_read = AStreamReadStream(s, NULL, i_read_max)) < 0)
403 msg_Err(s, "AStreamSeekStream: skip failed");
404 return VLC_EGENERIC;
405 } else if (i_read == 0)
406 return VLC_SUCCESS; /* EOF */
407 i_skip -= i_read_max;
411 else
413 #ifdef STREAM_DEBUG
414 msg_Err(s, "AStreamSeekStream: hard seek");
415 #endif
416 /* Nothing good, seek and choose oldest segment */
417 if (vlc_stream_Seek(s->s, i_pos))
419 msg_Err(s, "AStreamSeekStream: hard seek failed");
420 return VLC_EGENERIC;
423 tk->i_start = i_pos;
424 tk->i_end = i_pos;
426 sys->i_offset = i_pos - tk->i_start;
427 sys->i_tk = i_tk_idx;
428 sys->i_pos = i_pos;
430 /* If there is not enough data left in the track, refill */
431 /* TODO How to get a correct value for
432 * - refilling threshold
433 * - how much to refill
435 if (tk->i_end < tk->i_start + sys->i_offset + sys->i_read_size)
437 if (sys->i_used < STREAM_READ_ATONCE / 2)
438 sys->i_used = STREAM_READ_ATONCE / 2;
440 if (AStreamRefillStream(s))
441 return VLC_EGENERIC;
443 return VLC_SUCCESS;
446 /****************************************************************************
447 * AStreamControl:
448 ****************************************************************************/
449 static int AStreamControl(stream_t *s, int i_query, va_list args)
451 switch(i_query)
453 case STREAM_CAN_SEEK:
454 case STREAM_CAN_FASTSEEK:
455 case STREAM_CAN_PAUSE:
456 case STREAM_CAN_CONTROL_PACE:
457 case STREAM_GET_SIZE:
458 case STREAM_GET_PTS_DELAY:
459 case STREAM_GET_TITLE_INFO:
460 case STREAM_GET_TITLE:
461 case STREAM_GET_SEEKPOINT:
462 case STREAM_GET_META:
463 case STREAM_GET_CONTENT_TYPE:
464 case STREAM_GET_SIGNAL:
465 case STREAM_GET_TAGS:
466 case STREAM_GET_TYPE:
467 case STREAM_SET_PAUSE_STATE:
468 case STREAM_SET_PRIVATE_ID_STATE:
469 case STREAM_SET_PRIVATE_ID_CA:
470 case STREAM_GET_PRIVATE_ID_STATE:
471 return vlc_stream_vaControl(s->s, i_query, args);
473 case STREAM_SET_TITLE:
474 case STREAM_SET_SEEKPOINT:
476 int ret = vlc_stream_vaControl(s->s, i_query, args);
477 if (ret == VLC_SUCCESS)
478 AStreamControlReset(s);
479 return ret;
482 case STREAM_SET_RECORD_STATE:
483 default:
484 msg_Err(s, "invalid vlc_stream_vaControl query=0x%x", i_query);
485 return VLC_EGENERIC;
487 return VLC_SUCCESS;
490 static int Open(vlc_object_t *obj)
492 stream_t *s = (stream_t *)obj;
494 if (s->s->pf_read == NULL)
495 return VLC_EGENERIC;
497 stream_sys_t *sys = malloc(sizeof (*sys));
498 if (unlikely(sys == NULL))
499 return VLC_ENOMEM;
501 /* Common field */
502 sys->i_pos = 0;
504 /* Stats */
505 sys->stat.i_bytes = 0;
506 sys->stat.i_read_time = 0;
507 sys->stat.i_read_count = 0;
509 msg_Dbg(s, "Using stream method for AStream*");
511 /* Allocate/Setup our tracks */
512 sys->i_offset = 0;
513 sys->i_tk = 0;
514 sys->p_buffer = malloc(STREAM_CACHE_SIZE);
515 if (sys->p_buffer == NULL)
517 free(sys);
518 return VLC_ENOMEM;
521 sys->i_used = 0;
522 sys->i_read_size = STREAM_READ_ATONCE;
523 #if STREAM_READ_ATONCE < 256
524 # error "Invalid STREAM_READ_ATONCE value"
525 #endif
527 for (unsigned i = 0; i < STREAM_CACHE_TRACK; i++)
529 sys->tk[i].date = 0;
530 sys->tk[i].i_start = sys->i_pos;
531 sys->tk[i].i_end = sys->i_pos;
532 sys->tk[i].p_buffer = &sys->p_buffer[i * STREAM_CACHE_TRACK_SIZE];
535 s->p_sys = sys;
537 /* Do the prebuffering */
538 AStreamPrebufferStream(s);
540 if (sys->tk[sys->i_tk].i_end <= 0)
542 msg_Err(s, "cannot pre fill buffer");
543 free(sys->p_buffer);
544 free(sys);
545 return VLC_EGENERIC;
548 s->pf_read = AStreamReadStream;
549 s->pf_seek = AStreamSeekStream;
550 s->pf_control = AStreamControl;
551 return VLC_SUCCESS;
554 /****************************************************************************
555 * AStreamDestroy:
556 ****************************************************************************/
557 static void Close(vlc_object_t *obj)
559 stream_t *s = (stream_t *)obj;
560 stream_sys_t *sys = s->p_sys;
562 free(sys->p_buffer);
563 free(sys);
566 vlc_module_begin()
567 set_category(CAT_INPUT)
568 set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
569 set_capability("stream_filter", 0)
570 add_shortcut("cache")
572 set_description(N_("Byte stream cache"))
573 set_callbacks(Open, Close)
574 vlc_module_end()