demux: mp4: avoid audio cuts on seek
[vlc.git] / modules / stream_filter / cache_block.c
blob85c10203e13490bf937051deb9d69effafb6f8ec
1 /*****************************************************************************
2 * cache_block.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 /* TODO:
38 * - tune the 2 methods (block/stream)
39 * - compute cost for seek
40 * - improve stream mode seeking with closest segments
41 * - ...
44 /*
45 * One linked list of data read
48 /* How many tracks we have, currently only used for stream mode */
49 #ifdef OPTIMIZE_MEMORY
50 /* Max size of our cache 128KiB per stream */
51 # define STREAM_CACHE_SIZE (1024*128)
52 #else
53 /* Max size of our cache 48MiB per stream */
54 # define STREAM_CACHE_SIZE (4*12*1024*1024)
55 #endif
57 /* How many data we try to prebuffer
58 * XXX it should be small to avoid useless latency but big enough for
59 * efficient demux probing */
60 #define STREAM_CACHE_PREBUFFER_SIZE (128)
62 /* Method: Simple, for pf_block.
63 * We get blocks and put them in the linked list.
64 * We release blocks once the total size is bigger than STREAM_CACHE_SIZE
67 struct stream_sys_t
69 uint64_t i_pos; /* Current reading offset */
71 uint64_t i_start; /* Offset of block for p_first */
72 uint64_t i_offset; /* Offset for data in p_current */
73 block_t *p_current; /* Current block */
75 uint64_t i_size; /* Total amount of data in the list */
76 block_t *p_first;
77 block_t **pp_last;
79 struct
81 /* Stat about reading data */
82 uint64_t i_read_count;
83 uint64_t i_bytes;
84 uint64_t i_read_time;
85 } stat;
88 static int AStreamRefillBlock(stream_t *s)
90 stream_sys_t *sys = s->p_sys;
92 /* Release data */
93 while (sys->i_size >= STREAM_CACHE_SIZE &&
94 sys->p_first != sys->p_current)
96 block_t *b = sys->p_first;
98 sys->i_start += b->i_buffer;
99 sys->i_size -= b->i_buffer;
100 sys->p_first = b->p_next;
102 block_Release(b);
104 if (sys->i_size >= STREAM_CACHE_SIZE &&
105 sys->p_current == sys->p_first &&
106 sys->p_current->p_next) /* At least 2 packets */
108 /* Enough data, don't read more */
109 return VLC_SUCCESS;
112 /* Now read a new block */
113 const mtime_t start = mdate();
114 block_t *b;
116 for (;;)
118 if (vlc_killed())
119 return VLC_EGENERIC;
121 /* Fetch a block */
122 if ((b = vlc_stream_ReadBlock(s->p_source)))
123 break;
124 if (vlc_stream_Eof(s->p_source))
125 return VLC_EGENERIC;
128 sys->stat.i_read_time += mdate() - start;
129 while (b)
131 /* Append the block */
132 sys->i_size += b->i_buffer;
133 *sys->pp_last = b;
134 sys->pp_last = &b->p_next;
136 /* Fix p_current */
137 if (sys->p_current == NULL)
138 sys->p_current = b;
140 /* Update stat */
141 sys->stat.i_bytes += b->i_buffer;
142 sys->stat.i_read_count++;
144 b = b->p_next;
146 return VLC_SUCCESS;
149 static void AStreamPrebufferBlock(stream_t *s)
151 stream_sys_t *sys = s->p_sys;
152 mtime_t start = mdate();
153 bool first = true;
155 msg_Dbg(s, "starting pre-buffering");
156 for (;;)
158 const int64_t now = mdate();
160 if (vlc_killed() || sys->i_size > STREAM_CACHE_PREBUFFER_SIZE)
162 int64_t i_byterate;
164 /* Update stat */
165 sys->stat.i_bytes = sys->i_size;
166 sys->stat.i_read_time = now - start;
167 i_byterate = (CLOCK_FREQ * sys->stat.i_bytes) /
168 (sys->stat.i_read_time + 1);
170 msg_Dbg(s, "prebuffering done %"PRId64" bytes in %"PRId64"s - "
171 "%"PRId64" KiB/s",
172 sys->stat.i_bytes,
173 sys->stat.i_read_time / CLOCK_FREQ,
174 i_byterate / 1024);
175 break;
178 /* Fetch a block */
179 block_t *b = vlc_stream_ReadBlock(s->p_source);
180 if (b == NULL)
182 if (vlc_stream_Eof(s->p_source))
183 break;
184 continue;
187 while (b)
189 /* Append the block */
190 sys->i_size += b->i_buffer;
191 *sys->pp_last = b;
192 sys->pp_last = &b->p_next;
194 sys->stat.i_read_count++;
195 b = b->p_next;
198 if (first)
200 msg_Dbg(s, "received first data after %"PRId64" ms",
201 (mdate() - start) / 1000);
202 first = false;
206 sys->p_current = sys->p_first;
209 /****************************************************************************
210 * AStreamControlReset:
211 ****************************************************************************/
212 static void AStreamControlReset(stream_t *s)
214 stream_sys_t *sys = s->p_sys;
216 sys->i_pos = 0;
218 block_ChainRelease(sys->p_first);
220 /* Init all fields of sys->block */
221 sys->i_start = 0;
222 sys->i_offset = 0;
223 sys->p_current = NULL;
224 sys->i_size = 0;
225 sys->p_first = NULL;
226 sys->pp_last = &sys->p_first;
228 /* Do the prebuffering */
229 AStreamPrebufferBlock(s);
232 static int AStreamSeekBlock(stream_t *s, uint64_t i_pos)
234 stream_sys_t *sys = s->p_sys;
235 int64_t i_offset = i_pos - sys->i_start;
236 bool b_seek;
238 /* We already have thoses data, just update p_current/i_offset */
239 if (i_offset >= 0 && (uint64_t)i_offset < sys->i_size)
241 block_t *b = sys->p_first;
242 int i_current = 0;
244 while (i_current + b->i_buffer < (uint64_t)i_offset)
246 i_current += b->i_buffer;
247 b = b->p_next;
250 sys->p_current = b;
251 sys->i_offset = i_offset - i_current;
253 sys->i_pos = i_pos;
255 return VLC_SUCCESS;
258 /* We may need to seek or to read data */
259 if (i_offset < 0)
261 bool b_aseek;
262 vlc_stream_Control(s->p_source, STREAM_CAN_SEEK, &b_aseek);
264 if (!b_aseek)
266 msg_Err(s, "backward seeking impossible (access not seekable)");
267 return VLC_EGENERIC;
270 b_seek = true;
272 else
274 bool b_aseek, b_aseekfast;
276 vlc_stream_Control(s->p_source, STREAM_CAN_SEEK, &b_aseek);
277 vlc_stream_Control(s->p_source, STREAM_CAN_FASTSEEK, &b_aseekfast);
279 if (!b_aseek)
281 b_seek = false;
282 msg_Warn(s, "%"PRId64" bytes need to be skipped "
283 "(access non seekable)", i_offset - sys->i_size);
285 else
287 int64_t i_skip = i_offset - sys->i_size;
289 /* Avg bytes per packets */
290 int i_avg = sys->stat.i_bytes / sys->stat.i_read_count;
291 /* TODO compute a seek cost instead of fixed threshold */
292 int i_th = b_aseekfast ? 1 : 5;
294 if (i_skip <= i_th * i_avg &&
295 i_skip < STREAM_CACHE_SIZE)
296 b_seek = false;
297 else
298 b_seek = true;
300 msg_Dbg(s, "b_seek=%d th*avg=%d skip=%"PRId64,
301 b_seek, i_th*i_avg, i_skip);
305 if (b_seek)
307 /* Do the access seek */
308 if (vlc_stream_Seek(s->p_source, i_pos)) return VLC_EGENERIC;
310 /* Release data */
311 block_ChainRelease(sys->p_first);
313 /* Reinit */
314 sys->i_start = sys->i_pos = i_pos;
315 sys->i_offset = 0;
316 sys->p_current = NULL;
317 sys->i_size = 0;
318 sys->p_first = NULL;
319 sys->pp_last = &sys->p_first;
321 /* Refill a block */
322 if (AStreamRefillBlock(s))
323 return VLC_EGENERIC;
325 return VLC_SUCCESS;
327 else
331 while (sys->p_current &&
332 sys->i_pos + sys->p_current->i_buffer - sys->i_offset <= i_pos)
334 sys->i_pos += sys->p_current->i_buffer - sys->i_offset;
335 sys->p_current = sys->p_current->p_next;
336 sys->i_offset = 0;
338 if (!sys->p_current && AStreamRefillBlock(s))
340 if (sys->i_pos != i_pos)
341 return VLC_EGENERIC;
344 while (sys->i_start + sys->i_size < i_pos);
346 sys->i_offset += i_pos - sys->i_pos;
347 sys->i_pos = i_pos;
349 return VLC_SUCCESS;
352 return VLC_EGENERIC;
355 static ssize_t AStreamReadBlock(stream_t *s, void *buf, size_t len)
357 stream_sys_t *sys = s->p_sys;
359 /* It means EOF */
360 if (sys->p_current == NULL)
361 return 0;
363 ssize_t i_current = sys->p_current->i_buffer - sys->i_offset;
364 size_t i_copy = VLC_CLIP((size_t)i_current, 0, len);
366 /* Copy data */
367 memcpy(buf, &sys->p_current->p_buffer[sys->i_offset], i_copy);
369 sys->i_offset += i_copy;
370 if (sys->i_offset >= sys->p_current->i_buffer)
371 { /* Current block is now empty, switch to next */
372 sys->i_offset = 0;
373 sys->p_current = sys->p_current->p_next;
375 /* Get a new block if needed */
376 if (sys->p_current == NULL)
377 AStreamRefillBlock(s);
381 * we should not signal end-of-file if we have not exhausted
382 * the blocks we know about, as such we should try again if that
383 * is the case. i_copy == 0 just means that the processed block does
384 * not contain data at the offset that we want, not EOF.
387 if( i_copy == 0 && sys->p_current )
388 return AStreamReadBlock( s, buf, len );
390 sys->i_pos += i_copy;
391 return i_copy;
394 /****************************************************************************
395 * AStreamControl:
396 ****************************************************************************/
397 static int AStreamControl(stream_t *s, int i_query, va_list args)
399 switch(i_query)
401 case STREAM_CAN_SEEK:
402 case STREAM_CAN_FASTSEEK:
403 case STREAM_CAN_PAUSE:
404 case STREAM_CAN_CONTROL_PACE:
405 case STREAM_IS_DIRECTORY:
406 case STREAM_GET_SIZE:
407 case STREAM_GET_PTS_DELAY:
408 case STREAM_GET_TITLE_INFO:
409 case STREAM_GET_TITLE:
410 case STREAM_GET_SEEKPOINT:
411 case STREAM_GET_META:
412 case STREAM_GET_CONTENT_TYPE:
413 case STREAM_GET_SIGNAL:
414 case STREAM_GET_TAGS:
415 case STREAM_SET_PAUSE_STATE:
416 case STREAM_SET_PRIVATE_ID_STATE:
417 case STREAM_SET_PRIVATE_ID_CA:
418 case STREAM_GET_PRIVATE_ID_STATE:
419 return vlc_stream_vaControl(s->p_source, i_query, args);
421 case STREAM_SET_TITLE:
422 case STREAM_SET_SEEKPOINT:
424 int ret = vlc_stream_vaControl(s->p_source, i_query, args);
425 if (ret == VLC_SUCCESS)
426 AStreamControlReset(s);
427 return ret;
430 case STREAM_SET_RECORD_STATE:
431 default:
432 msg_Err(s, "invalid vlc_stream_vaControl query=0x%x", i_query);
433 return VLC_EGENERIC;
435 return VLC_SUCCESS;
438 static int Open(vlc_object_t *obj)
440 stream_t *s = (stream_t *)obj;
442 stream_sys_t *sys = malloc(sizeof (*sys));
443 if (unlikely(sys == NULL))
444 return VLC_ENOMEM;
446 /* Common field */
447 sys->i_pos = 0;
449 /* Stats */
450 sys->stat.i_bytes = 0;
451 sys->stat.i_read_time = 0;
452 sys->stat.i_read_count = 0;
454 msg_Dbg(s, "Using block method for AStream*");
456 /* Init all fields of sys->block */
457 sys->i_start = sys->i_pos;
458 sys->i_offset = 0;
459 sys->p_current = NULL;
460 sys->i_size = 0;
461 sys->p_first = NULL;
462 sys->pp_last = &sys->p_first;
464 s->p_sys = sys;
465 /* Do the prebuffering */
466 AStreamPrebufferBlock(s);
468 if (sys->i_size <= 0)
470 msg_Err(s, "cannot pre fill buffer");
471 free(sys);
472 return VLC_EGENERIC;
475 s->pf_read = AStreamReadBlock;
476 s->pf_seek = AStreamSeekBlock;
477 s->pf_control = AStreamControl;
478 return VLC_SUCCESS;
481 /****************************************************************************
482 * AStreamDestroy:
483 ****************************************************************************/
484 static void Close(vlc_object_t *obj)
486 stream_t *s = (stream_t *)obj;
487 stream_sys_t *sys = s->p_sys;
489 block_ChainRelease(sys->p_first);
490 free(sys);
493 vlc_module_begin()
494 set_category(CAT_INPUT)
495 set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
496 set_capability("stream_filter", 0)
498 set_description(N_("Block stream cache"))
499 set_callbacks(Open, Close)
500 vlc_module_end()