Contribs: update libgcrypt to 1.7.3
[vlc.git] / modules / stream_filter / cache_block.c
blobb41d8b23adba207c0c52167846e7c090331a2afe
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 if (buf == NULL)
364 { /* seek if possible, else use plain old read and discard */
365 bool b_aseek;
367 vlc_stream_Control(s->p_source, STREAM_CAN_SEEK, &b_aseek);
368 if (b_aseek)
369 return AStreamSeekBlock(s, sys->i_pos + len) ? 0 : len;
372 ssize_t i_current = sys->p_current->i_buffer - sys->i_offset;
373 size_t i_copy = VLC_CLIP((size_t)i_current, 0, len);
375 /* Copy data */
376 if (buf != NULL)
377 memcpy(buf, &sys->p_current->p_buffer[sys->i_offset], i_copy);
379 sys->i_offset += i_copy;
380 if (sys->i_offset >= sys->p_current->i_buffer)
381 { /* Current block is now empty, switch to next */
382 sys->i_offset = 0;
383 sys->p_current = sys->p_current->p_next;
385 /* Get a new block if needed */
386 if (sys->p_current == NULL)
387 AStreamRefillBlock(s);
391 * we should not signal end-of-file if we have not exhausted
392 * the blocks we know about, as such we should try again if that
393 * is the case. i_copy == 0 just means that the processed block does
394 * not contain data at the offset that we want, not EOF.
397 if( i_copy == 0 && sys->p_current )
398 return AStreamReadBlock( s, buf, len );
400 sys->i_pos += i_copy;
401 return i_copy;
404 /****************************************************************************
405 * AStreamControl:
406 ****************************************************************************/
407 static int AStreamControl(stream_t *s, int i_query, va_list args)
409 switch(i_query)
411 case STREAM_CAN_SEEK:
412 case STREAM_CAN_FASTSEEK:
413 case STREAM_CAN_PAUSE:
414 case STREAM_CAN_CONTROL_PACE:
415 case STREAM_IS_DIRECTORY:
416 case STREAM_GET_SIZE:
417 case STREAM_GET_PTS_DELAY:
418 case STREAM_GET_TITLE_INFO:
419 case STREAM_GET_TITLE:
420 case STREAM_GET_SEEKPOINT:
421 case STREAM_GET_META:
422 case STREAM_GET_CONTENT_TYPE:
423 case STREAM_GET_SIGNAL:
424 case STREAM_SET_PAUSE_STATE:
425 case STREAM_SET_PRIVATE_ID_STATE:
426 case STREAM_SET_PRIVATE_ID_CA:
427 case STREAM_GET_PRIVATE_ID_STATE:
428 return vlc_stream_vaControl(s->p_source, i_query, args);
430 case STREAM_SET_TITLE:
431 case STREAM_SET_SEEKPOINT:
433 int ret = vlc_stream_vaControl(s->p_source, i_query, args);
434 if (ret == VLC_SUCCESS)
435 AStreamControlReset(s);
436 return ret;
439 case STREAM_SET_RECORD_STATE:
440 default:
441 msg_Err(s, "invalid vlc_stream_vaControl query=0x%x", i_query);
442 return VLC_EGENERIC;
444 return VLC_SUCCESS;
447 static int Open(vlc_object_t *obj)
449 stream_t *s = (stream_t *)obj;
451 stream_sys_t *sys = malloc(sizeof (*sys));
452 if (unlikely(sys == NULL))
453 return VLC_ENOMEM;
455 /* Common field */
456 sys->i_pos = 0;
458 /* Stats */
459 sys->stat.i_bytes = 0;
460 sys->stat.i_read_time = 0;
461 sys->stat.i_read_count = 0;
463 msg_Dbg(s, "Using block method for AStream*");
465 /* Init all fields of sys->block */
466 sys->i_start = sys->i_pos;
467 sys->i_offset = 0;
468 sys->p_current = NULL;
469 sys->i_size = 0;
470 sys->p_first = NULL;
471 sys->pp_last = &sys->p_first;
473 s->p_sys = sys;
474 /* Do the prebuffering */
475 AStreamPrebufferBlock(s);
477 if (sys->i_size <= 0)
479 msg_Err(s, "cannot pre fill buffer");
480 free(sys);
481 return VLC_EGENERIC;
484 s->pf_read = AStreamReadBlock;
485 s->pf_seek = AStreamSeekBlock;
486 s->pf_control = AStreamControl;
487 return VLC_SUCCESS;
490 /****************************************************************************
491 * AStreamDestroy:
492 ****************************************************************************/
493 static void Close(vlc_object_t *obj)
495 stream_t *s = (stream_t *)obj;
496 stream_sys_t *sys = s->p_sys;
498 block_ChainRelease(sys->p_first);
499 free(sys);
502 vlc_module_begin()
503 set_category(CAT_INPUT)
504 set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
505 set_capability("stream_filter", 0)
507 set_description(N_("Block stream cache"))
508 set_callbacks(Open, Close)
509 vlc_module_end()