demux: mp4: avoid audio cuts on seek
[vlc.git] / modules / stream_filter / cache_read.c
blobed967760764b7d5388fecf6f460b63e98eeb4285
1 /*****************************************************************************
2 * cache_read.c
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_stream.h>
35 #include <vlc_interrupt.h>
37 // #define STREAM_DEBUG 1
40 * Complex scheme using mutliple track to avoid seeking
43 /* How many tracks we have, currently only used for stream mode */
44 #ifdef OPTIMIZE_MEMORY
45 # define STREAM_CACHE_TRACK 1
46 /* Max size of our cache 128Ko per track */
47 # define STREAM_CACHE_SIZE (STREAM_CACHE_TRACK*1024*128)
48 #else
49 # define STREAM_CACHE_TRACK 3
50 /* Max size of our cache 4Mo per track */
51 # define STREAM_CACHE_SIZE (4*STREAM_CACHE_TRACK*1024*1024)
52 #endif
54 /* How many data we try to prebuffer
55 * XXX it should be small to avoid useless latency but big enough for
56 * efficient demux probing */
57 #define STREAM_CACHE_PREBUFFER_SIZE (128)
59 /* Method:
60 * - We use ring buffers, only one if unseekable, all if seekable
61 * - Upon seek date current ring, then search if one ring match the pos,
62 * yes: switch to it, seek the access to match the end of the ring
63 * no: search the ring with i_end the closer to i_pos,
64 * if close enough, read data and use this ring
65 * else use the oldest ring, seek and use it.
67 * TODO: - with access non seekable: use all space available for only one ring, but
68 * we have to support seekable/non-seekable switch on the fly.
69 * - compute a good value for i_read_size
70 * - ?
72 #define STREAM_READ_ATONCE 1024
73 #define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)
75 typedef struct
77 mtime_t date;
79 uint64_t i_start;
80 uint64_t i_end;
82 uint8_t *p_buffer;
84 } stream_track_t;
86 struct stream_sys_t
88 uint64_t i_pos; /* Current reading offset */
90 unsigned i_offset; /* Buffer offset in the current track */
91 int i_tk; /* Current track */
92 stream_track_t tk[STREAM_CACHE_TRACK];
94 /* Global buffer */
95 uint8_t *p_buffer;
97 /* */
98 unsigned i_used; /* Used since last read */
99 unsigned i_read_size;
101 struct
103 /* Stat about reading data */
104 uint64_t i_read_count;
105 uint64_t i_bytes;
106 uint64_t i_read_time;
107 } stat;
110 static int AStreamRefillStream(stream_t *s)
112 stream_sys_t *sys = s->p_sys;
113 stream_track_t *tk = &sys->tk[sys->i_tk];
115 /* We read but won't increase i_start after initial start + offset */
116 int i_toread =
117 __MIN(sys->i_used, STREAM_CACHE_TRACK_SIZE -
118 (tk->i_end - tk->i_start - sys->i_offset));
120 if (i_toread <= 0) return VLC_SUCCESS; /* EOF */
122 #ifdef STREAM_DEBUG
123 msg_Dbg(s, "AStreamRefillStream: used=%d toread=%d",
124 sys->i_used, i_toread);
125 #endif
127 mtime_t start = mdate();
128 while (i_toread > 0)
130 int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE;
131 int i_read;
133 if (vlc_killed())
134 return VLC_EGENERIC;
136 i_read = __MIN(i_toread, STREAM_CACHE_TRACK_SIZE - i_off);
137 i_read = vlc_stream_Read(s->p_source, &tk->p_buffer[i_off], i_read);
139 /* msg_Dbg(s, "AStreamRefillStream: read=%d", i_read); */
140 if (i_read < 0)
142 continue;
144 else if (i_read == 0)
145 return VLC_SUCCESS;
147 /* Update end */
148 tk->i_end += i_read;
150 /* Windows of STREAM_CACHE_TRACK_SIZE */
151 if (tk->i_start + STREAM_CACHE_TRACK_SIZE < tk->i_end)
153 unsigned i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
155 tk->i_start += i_invalid;
156 sys->i_offset -= i_invalid;
159 i_toread -= i_read;
160 sys->i_used -= i_read;
162 sys->stat.i_bytes += i_read;
163 sys->stat.i_read_count++;
166 sys->stat.i_read_time += mdate() - start;
167 return VLC_SUCCESS;
170 static void AStreamPrebufferStream(stream_t *s)
172 stream_sys_t *sys = s->p_sys;
173 mtime_t start = mdate();
174 bool first = true;
176 msg_Dbg(s, "starting pre-buffering");
177 for (;;)
179 stream_track_t *tk = &sys->tk[sys->i_tk];
180 mtime_t now = mdate();
182 int i_read;
183 int i_buffered = tk->i_end - tk->i_start;
185 if (vlc_killed() || i_buffered >= STREAM_CACHE_PREBUFFER_SIZE)
187 int64_t i_byterate;
189 /* Update stat */
190 sys->stat.i_bytes = i_buffered;
191 sys->stat.i_read_time = now - start;
192 i_byterate = (CLOCK_FREQ * sys->stat.i_bytes) /
193 (sys->stat.i_read_time+1);
195 msg_Dbg(s, "pre-buffering done %"PRId64" bytes in %"PRId64"s - "
196 "%"PRId64" KiB/s", sys->stat.i_bytes,
197 sys->stat.i_read_time / CLOCK_FREQ, i_byterate / 1024);
198 break;
201 i_read = STREAM_CACHE_TRACK_SIZE - i_buffered;
202 i_read = __MIN((int)sys->i_read_size, i_read);
203 i_read = vlc_stream_Read(s->p_source, &tk->p_buffer[i_buffered],
204 i_read);
205 if (i_read < 0)
206 continue;
207 else if (i_read == 0)
208 break; /* EOF */
210 if (first)
212 msg_Dbg(s, "received first data after %"PRId64" ms",
213 (mdate() - start) / 1000);
214 first = false;
217 tk->i_end += i_read;
218 sys->stat.i_read_count++;
222 /****************************************************************************
223 * AStreamControlReset:
224 ****************************************************************************/
225 static void AStreamControlReset(stream_t *s)
227 stream_sys_t *sys = s->p_sys;
229 sys->i_pos = 0;
231 /* Setup our tracks */
232 sys->i_offset = 0;
233 sys->i_tk = 0;
234 sys->i_used = 0;
236 for (unsigned i = 0; i < STREAM_CACHE_TRACK; i++)
238 sys->tk[i].date = 0;
239 sys->tk[i].i_start = sys->i_pos;
240 sys->tk[i].i_end = sys->i_pos;
243 /* Do the prebuffering */
244 AStreamPrebufferStream(s);
247 static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
249 stream_sys_t *sys = s->p_sys;
250 stream_track_t *tk = &sys->tk[sys->i_tk];
252 if (tk->i_start >= tk->i_end)
253 return 0; /* EOF */
255 #ifdef STREAM_DEBUG
256 msg_Dbg(s, "AStreamReadStream: %zd pos=%"PRId64" tk=%d start=%"PRId64
257 " offset=%d end=%"PRId64, len, sys->i_pos, sys->i_tk,
258 tk->i_start, sys->i_offset, tk->i_end);
259 #endif
261 unsigned i_off = (tk->i_start + sys->i_offset) % STREAM_CACHE_TRACK_SIZE;
262 size_t i_current = __MIN(tk->i_end - tk->i_start - sys->i_offset,
263 STREAM_CACHE_TRACK_SIZE - i_off);
264 ssize_t i_copy = __MIN(i_current, len);
265 if (i_copy <= 0)
266 return 0; /* EOF */
268 /* Copy data */
269 /* msg_Dbg(s, "AStreamReadStream: copy %zd", i_copy); */
270 if (buf != NULL)
271 memcpy(buf, &tk->p_buffer[i_off], i_copy);
272 sys->i_offset += i_copy;
274 /* Update pos now */
275 sys->i_pos += i_copy;
277 /* */
278 sys->i_used += i_copy;
280 if (tk->i_end + i_copy <= tk->i_start + sys->i_offset + len)
282 const size_t i_read_requested = VLC_CLIP(len - i_copy,
283 STREAM_READ_ATONCE / 2,
284 STREAM_READ_ATONCE * 10);
285 if (sys->i_used < i_read_requested)
286 sys->i_used = i_read_requested;
288 AStreamRefillStream(s);
291 return i_copy;
294 static int AStreamSeekStream(stream_t *s, uint64_t i_pos)
296 stream_sys_t *sys = s->p_sys;
297 stream_track_t *p_current = &sys->tk[sys->i_tk];
299 if (p_current->i_start >= p_current->i_end && i_pos >= p_current->i_end)
300 return 0; /* EOF */
302 #ifdef STREAM_DEBUG
303 msg_Dbg(s, "AStreamSeekStream: to %"PRId64" pos=%"PRId64
304 " tk=%d start=%"PRId64" offset=%d end=%"PRId64,
305 i_pos, sys->i_pos, sys->i_tk, p_current->i_start,
306 sys->i_offset, p_current->i_end);
307 #endif
309 bool b_aseek;
310 vlc_stream_Control(s->p_source, STREAM_CAN_SEEK, &b_aseek);
311 if (!b_aseek && i_pos < p_current->i_start)
313 msg_Warn(s, "AStreamSeekStream: can't seek");
314 return VLC_EGENERIC;
317 bool b_afastseek;
318 vlc_stream_Control(s->p_source, STREAM_CAN_FASTSEEK, &b_afastseek);
320 /* FIXME compute seek cost (instead of static 'stupid' value) */
321 uint64_t i_skip_threshold;
322 if (b_aseek)
323 i_skip_threshold = b_afastseek ? 128 : 3 * sys->i_read_size;
324 else
325 i_skip_threshold = INT64_MAX;
327 /* Date the current track */
328 p_current->date = mdate();
330 /* Search a new track slot */
331 stream_track_t *tk = NULL;
332 int i_tk_idx = -1;
334 /* Prefer the current track */
335 if (p_current->i_start <= i_pos && i_pos <= p_current->i_end + i_skip_threshold)
337 tk = p_current;
338 i_tk_idx = sys->i_tk;
340 if (!tk)
342 /* Try to maximize already read data */
343 for (int i = 0; i < STREAM_CACHE_TRACK; i++)
345 stream_track_t *t = &sys->tk[i];
347 if (t->i_start > i_pos || i_pos > t->i_end)
348 continue;
350 if (!tk || tk->i_end < t->i_end)
352 tk = t;
353 i_tk_idx = i;
357 if (!tk)
359 /* Use the oldest unused */
360 for (int i = 0; i < STREAM_CACHE_TRACK; i++)
362 stream_track_t *t = &sys->tk[i];
364 if (!tk || tk->date > t->date)
366 tk = t;
367 i_tk_idx = i;
371 assert(i_tk_idx >= 0 && i_tk_idx < STREAM_CACHE_TRACK);
373 if (tk != p_current)
374 i_skip_threshold = 0;
375 if (tk->i_start <= i_pos && i_pos <= tk->i_end + i_skip_threshold)
377 #ifdef STREAM_DEBUG
378 msg_Err(s, "AStreamSeekStream: reusing %d start=%"PRId64
379 " end=%"PRId64"(%s)",
380 i_tk_idx, tk->i_start, tk->i_end,
381 tk != p_current ? "seek" : i_pos > tk->i_end ? "skip" : "noseek");
382 #endif
383 if (tk != p_current)
385 assert(b_aseek);
387 /* Seek at the end of the buffer
388 * TODO it is stupid to seek now, it would be better to delay it
390 if (vlc_stream_Seek(s->p_source, tk->i_end))
392 msg_Err(s, "AStreamSeekStream: hard seek failed");
393 return VLC_EGENERIC;
396 else if (i_pos > tk->i_end)
398 uint64_t i_skip = i_pos - tk->i_end;
399 while (i_skip > 0)
401 const int i_read_max = __MIN(10 * STREAM_READ_ATONCE, i_skip);
402 int i_read = 0;
403 if ((i_read = AStreamReadStream(s, NULL, i_read_max)) < 0)
405 msg_Err(s, "AStreamSeekStream: skip failed");
406 return VLC_EGENERIC;
407 } else if (i_read == 0)
408 return VLC_SUCCESS; /* EOF */
409 i_skip -= i_read_max;
413 else
415 #ifdef STREAM_DEBUG
416 msg_Err(s, "AStreamSeekStream: hard seek");
417 #endif
418 /* Nothing good, seek and choose oldest segment */
419 if (vlc_stream_Seek(s->p_source, i_pos))
421 msg_Err(s, "AStreamSeekStream: hard seek failed");
422 return VLC_EGENERIC;
425 tk->i_start = i_pos;
426 tk->i_end = i_pos;
428 sys->i_offset = i_pos - tk->i_start;
429 sys->i_tk = i_tk_idx;
430 sys->i_pos = i_pos;
432 /* If there is not enough data left in the track, refill */
433 /* TODO How to get a correct value for
434 * - refilling threshold
435 * - how much to refill
437 if (tk->i_end < tk->i_start + sys->i_offset + sys->i_read_size)
439 if (sys->i_used < STREAM_READ_ATONCE / 2)
440 sys->i_used = STREAM_READ_ATONCE / 2;
442 if (AStreamRefillStream(s))
443 return VLC_EGENERIC;
445 return VLC_SUCCESS;
448 /****************************************************************************
449 * AStreamControl:
450 ****************************************************************************/
451 static int AStreamControl(stream_t *s, int i_query, va_list args)
453 switch(i_query)
455 case STREAM_CAN_SEEK:
456 case STREAM_CAN_FASTSEEK:
457 case STREAM_CAN_PAUSE:
458 case STREAM_CAN_CONTROL_PACE:
459 case STREAM_IS_DIRECTORY:
460 case STREAM_GET_SIZE:
461 case STREAM_GET_PTS_DELAY:
462 case STREAM_GET_TITLE_INFO:
463 case STREAM_GET_TITLE:
464 case STREAM_GET_SEEKPOINT:
465 case STREAM_GET_META:
466 case STREAM_GET_CONTENT_TYPE:
467 case STREAM_GET_SIGNAL:
468 case STREAM_GET_TAGS:
469 case STREAM_SET_PAUSE_STATE:
470 case STREAM_SET_PRIVATE_ID_STATE:
471 case STREAM_SET_PRIVATE_ID_CA:
472 case STREAM_GET_PRIVATE_ID_STATE:
473 return vlc_stream_vaControl(s->p_source, i_query, args);
475 case STREAM_SET_TITLE:
476 case STREAM_SET_SEEKPOINT:
478 int ret = vlc_stream_vaControl(s->p_source, i_query, args);
479 if (ret == VLC_SUCCESS)
480 AStreamControlReset(s);
481 return ret;
484 case STREAM_SET_RECORD_STATE:
485 default:
486 msg_Err(s, "invalid vlc_stream_vaControl query=0x%x", i_query);
487 return VLC_EGENERIC;
489 return VLC_SUCCESS;
492 static int Open(vlc_object_t *obj)
494 stream_t *s = (stream_t *)obj;
496 stream_sys_t *sys = malloc(sizeof (*sys));
497 if (unlikely(sys == NULL))
498 return VLC_ENOMEM;
500 /* Common field */
501 sys->i_pos = 0;
503 /* Stats */
504 sys->stat.i_bytes = 0;
505 sys->stat.i_read_time = 0;
506 sys->stat.i_read_count = 0;
508 msg_Dbg(s, "Using stream method for AStream*");
510 /* Allocate/Setup our tracks */
511 sys->i_offset = 0;
512 sys->i_tk = 0;
513 sys->p_buffer = malloc(STREAM_CACHE_SIZE);
514 if (sys->p_buffer == NULL)
516 free(sys);
517 return VLC_ENOMEM;
520 sys->i_used = 0;
521 sys->i_read_size = STREAM_READ_ATONCE;
522 #if STREAM_READ_ATONCE < 256
523 # error "Invalid STREAM_READ_ATONCE value"
524 #endif
526 for (unsigned i = 0; i < STREAM_CACHE_TRACK; i++)
528 sys->tk[i].date = 0;
529 sys->tk[i].i_start = sys->i_pos;
530 sys->tk[i].i_end = sys->i_pos;
531 sys->tk[i].p_buffer = &sys->p_buffer[i * STREAM_CACHE_TRACK_SIZE];
534 s->p_sys = sys;
536 /* Do the prebuffering */
537 AStreamPrebufferStream(s);
539 if (sys->tk[sys->i_tk].i_end <= 0)
541 msg_Err(s, "cannot pre fill buffer");
542 free(sys->p_buffer);
543 free(sys);
544 return VLC_EGENERIC;
547 s->pf_read = AStreamReadStream;
548 s->pf_seek = AStreamSeekStream;
549 s->pf_control = AStreamControl;
550 return VLC_SUCCESS;
553 /****************************************************************************
554 * AStreamDestroy:
555 ****************************************************************************/
556 static void Close(vlc_object_t *obj)
558 stream_t *s = (stream_t *)obj;
559 stream_sys_t *sys = s->p_sys;
561 free(sys->p_buffer);
562 free(sys);
565 vlc_module_begin()
566 set_category(CAT_INPUT)
567 set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
568 set_capability("stream_filter", 0)
570 set_description(N_("Byte stream cache"))
571 set_callbacks(Open, Close)
572 vlc_module_end()