1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
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 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_stream.h>
35 #include <vlc_interrupt.h>
38 * - tune the 2 methods (block/stream)
39 * - compute cost for seek
40 * - improve stream mode seeking with closest segments
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)
53 /* Max size of our cache 48MiB per stream */
54 # define STREAM_CACHE_SIZE (4*12*1024*1024)
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
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 */
81 /* Stat about reading data */
82 uint64_t i_read_count
;
88 static int AStreamRefillBlock(stream_t
*s
)
90 stream_sys_t
*sys
= s
->p_sys
;
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
;
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 */
112 /* Now read a new block */
113 const mtime_t start
= mdate();
122 if ((b
= vlc_stream_ReadBlock(s
->p_source
)))
124 if (vlc_stream_Eof(s
->p_source
))
128 sys
->stat
.i_read_time
+= mdate() - start
;
131 /* Append the block */
132 sys
->i_size
+= b
->i_buffer
;
134 sys
->pp_last
= &b
->p_next
;
137 if (sys
->p_current
== NULL
)
141 sys
->stat
.i_bytes
+= b
->i_buffer
;
142 sys
->stat
.i_read_count
++;
149 static void AStreamPrebufferBlock(stream_t
*s
)
151 stream_sys_t
*sys
= s
->p_sys
;
152 mtime_t start
= mdate();
155 msg_Dbg(s
, "starting pre-buffering");
158 const int64_t now
= mdate();
160 if (vlc_killed() || sys
->i_size
> STREAM_CACHE_PREBUFFER_SIZE
)
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 - "
173 sys
->stat
.i_read_time
/ CLOCK_FREQ
,
179 block_t
*b
= vlc_stream_ReadBlock(s
->p_source
);
182 if (vlc_stream_Eof(s
->p_source
))
189 /* Append the block */
190 sys
->i_size
+= b
->i_buffer
;
192 sys
->pp_last
= &b
->p_next
;
194 sys
->stat
.i_read_count
++;
200 msg_Dbg(s
, "received first data after %"PRId64
" ms",
201 (mdate() - start
) / 1000);
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
;
218 block_ChainRelease(sys
->p_first
);
220 /* Init all fields of sys->block */
223 sys
->p_current
= 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
;
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
;
244 while (i_current
+ b
->i_buffer
< (uint64_t)i_offset
)
246 i_current
+= b
->i_buffer
;
251 sys
->i_offset
= i_offset
- i_current
;
258 /* We may need to seek or to read data */
262 vlc_stream_Control(s
->p_source
, STREAM_CAN_SEEK
, &b_aseek
);
266 msg_Err(s
, "backward seeking impossible (access not seekable)");
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
);
282 msg_Warn(s
, "%"PRId64
" bytes need to be skipped "
283 "(access non seekable)", i_offset
- sys
->i_size
);
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
)
300 msg_Dbg(s
, "b_seek=%d th*avg=%d skip=%"PRId64
,
301 b_seek
, i_th
*i_avg
, i_skip
);
307 /* Do the access seek */
308 if (vlc_stream_Seek(s
->p_source
, i_pos
)) return VLC_EGENERIC
;
311 block_ChainRelease(sys
->p_first
);
314 sys
->i_start
= sys
->i_pos
= i_pos
;
316 sys
->p_current
= NULL
;
319 sys
->pp_last
= &sys
->p_first
;
322 if (AStreamRefillBlock(s
))
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
;
338 if (!sys
->p_current
&& AStreamRefillBlock(s
))
340 if (sys
->i_pos
!= i_pos
)
344 while (sys
->i_start
+ sys
->i_size
< i_pos
);
346 sys
->i_offset
+= i_pos
- sys
->i_pos
;
355 static ssize_t
AStreamReadBlock(stream_t
*s
, void *buf
, size_t len
)
357 stream_sys_t
*sys
= s
->p_sys
;
360 if (sys
->p_current
== NULL
)
364 { /* seek if possible, else use plain old read and discard */
367 vlc_stream_Control(s
->p_source
, STREAM_CAN_SEEK
, &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
);
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 */
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
;
404 /****************************************************************************
406 ****************************************************************************/
407 static int AStreamControl(stream_t
*s
, int i_query
, va_list args
)
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
);
439 case STREAM_SET_RECORD_STATE
:
441 msg_Err(s
, "invalid vlc_stream_vaControl query=0x%x", i_query
);
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
))
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
;
468 sys
->p_current
= NULL
;
471 sys
->pp_last
= &sys
->p_first
;
474 /* Do the prebuffering */
475 AStreamPrebufferBlock(s
);
477 if (sys
->i_size
<= 0)
479 msg_Err(s
, "cannot pre fill buffer");
484 s
->pf_read
= AStreamReadBlock
;
485 s
->pf_seek
= AStreamSeekBlock
;
486 s
->pf_control
= AStreamControl
;
490 /****************************************************************************
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
);
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
)