2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // Initial draft of my new cache system...
20 // Note it runs in 2 processes (using fork()), but doesn't require locking!!
21 // TODO: seeking, data consistency checking
23 #define READ_SLEEP_TIME 10
24 // These defines are used to reduce the cost of many successive
25 // seeks (e.g. when a file has no index) by spinning quickly at first.
26 #define INITIAL_FILL_USLEEP_TIME 1000
27 #define INITIAL_FILL_USLEEP_COUNT 10
28 #define FILL_USLEEP_TIME 50000
29 #define PREFILL_SLEEP_TIME 200
30 #define CONTROL_SLEEP_TIME 0
36 #include <sys/types.h>
40 #include <libavutil/common.h>
44 #include "osdep/shmem.h"
45 #include "osdep/timer.h"
46 #if defined(__MINGW32__)
48 static void ThreadProc( void *s
);
49 #elif defined(PTHREAD_CACHE)
51 static void *ThreadProc(void *s
);
54 #define FORKED_CACHE 1
57 #define FORKED_CACHE 0
68 unsigned char *buffer
; // base pointer of the allocated buffer memory
69 int buffer_size
; // size of the allocated buffer memory
70 int sector_size
; // size of a single sector (2048/2324)
71 int back_size
; // we should keep back_size amount of old bytes for backward seek
72 int fill_limit
; // we should fill buffer only if space>=fill_limit
73 int seek_limit
; // keep filling cache if distance is less that seek limit
76 off_t min_filepos
; // buffer contain only a part of the file, from min-max pos
78 off_t offset
; // filepos <-> bufferpos offset value (filepos of the buffer's first byte)
82 // int seek_lock; // 1 if we will seek/reset buffer, 2 if we are ready for cmd
83 // int fifo_flag; // 1 if we should use FIFO to notice cache about buffer reads.
87 volatile unsigned control_uint_arg
;
88 volatile double control_double_arg
;
89 volatile int control_res
;
90 volatile off_t control_new_pos
;
91 volatile double stream_time_length
;
92 volatile double stream_time_pos
;
95 static int min_fill
=0;
97 static void cache_wakeup(stream_t
*s
)
100 // signal process to wake up immediately
101 kill(s
->cache_pid
, SIGUSR1
);
105 static int cache_read(cache_vars_t
*s
, unsigned char *buf
, int size
)
109 int last_max
= s
->max_filepos
;
113 //printf("CACHE2_READ: 0x%X <= 0x%X <= 0x%X \n",s->min_filepos,s->read_filepos,s->max_filepos);
115 if(s
->read_filepos
>=s
->max_filepos
|| s
->read_filepos
<s
->min_filepos
){
118 if (s
->max_filepos
== last_max
) {
119 if (sleep_count
++ == 10)
120 mp_msg(MSGT_CACHE
, MSGL_WARN
, "Cache not filling, consider increasing -cache and/or -cache-min!\n");
122 last_max
= s
->max_filepos
;
125 // waiting for buffer fill...
126 if (stream_check_interrupt(READ_SLEEP_TIME
)) {
130 continue; // try again...
134 newb
=s
->max_filepos
-s
->read_filepos
; // new bytes in the buffer
135 if(newb
<min_fill
) min_fill
=newb
; // statistics...
137 // printf("*** newb: %d bytes ***\n",newb);
139 pos
=s
->read_filepos
- s
->offset
;
140 if(pos
<0) pos
+=s
->buffer_size
; else
141 if(pos
>=s
->buffer_size
) pos
-=s
->buffer_size
;
143 if(newb
>s
->buffer_size
-pos
) newb
=s
->buffer_size
-pos
; // handle wrap...
144 if(newb
>size
) newb
=size
;
147 if(s
->read_filepos
<s
->min_filepos
) mp_msg(MSGT_CACHE
,MSGL_ERR
,"Ehh. s->read_filepos<s->min_filepos !!! Report bug...\n");
149 // len=write(mem,newb)
150 //printf("Buffer read: %d bytes\n",newb);
151 memcpy(buf
,&s
->buffer
[pos
],newb
);
156 s
->read_filepos
+=len
;
164 static int cache_fill(cache_vars_t
*s
)
166 int back
,back2
,newb
,space
,len
,pos
;
167 off_t read
=s
->read_filepos
;
169 int wraparound_copy
= 0;
171 if(read
<s
->min_filepos
|| read
>s
->max_filepos
){
173 mp_msg(MSGT_CACHE
,MSGL_DBG2
,"Out of boundaries... seeking to 0x%"PRIX64
" \n",(int64_t)read
);
174 // drop cache contents only if seeking backward or too much fwd.
175 // This is also done for on-disk files, since it loses the backseek cache.
176 // That in turn can cause major bandwidth increase and performance
177 // issues with e.g. mov or badly interleaved files
178 if(read
<s
->min_filepos
|| read
>=s
->max_filepos
+s
->seek_limit
)
180 s
->offset
= // FIXME!?
181 s
->min_filepos
=s
->max_filepos
=read
; // drop cache content :(
182 if(s
->stream
->eof
) stream_reset(s
->stream
);
183 stream_seek_internal(s
->stream
,read
);
184 mp_msg(MSGT_CACHE
,MSGL_DBG2
,"Seek done. new pos: 0x%"PRIX64
" \n",(int64_t)stream_tell(s
->stream
));
188 // calc number of back-bytes:
189 back
=read
- s
->min_filepos
;
190 if(back
<0) back
=0; // strange...
191 if(back
>s
->back_size
) back
=s
->back_size
;
193 // calc number of new bytes:
194 newb
=s
->max_filepos
- read
;
195 if(newb
<0) newb
=0; // strange...
197 // calc free buffer space:
198 space
=s
->buffer_size
- (newb
+back
);
201 pos
=s
->max_filepos
- s
->offset
;
202 if(pos
>=s
->buffer_size
) pos
-=s
->buffer_size
; // wrap-around
204 if(space
<s
->fill_limit
){
205 // printf("Buffer is full (%d bytes free, limit: %d)\n",space,s->fill_limit);
206 return 0; // no fill...
209 // printf("### read=0x%X back=%d newb=%d space=%d pos=%d\n",read,back,newb,space,pos);
211 // try to avoid wrap-around. If not possible due to sector size
213 if(space
>s
->buffer_size
-pos
) {
214 if (s
->buffer_size
-pos
>= s
->sector_size
) {
215 space
=s
->buffer_size
-pos
;
217 space
= s
->sector_size
;
222 // limit one-time block size
223 read_chunk
= s
->stream
->read_chunk
;
224 if (!read_chunk
) read_chunk
= 4*s
->sector_size
;
225 space
= FFMIN(space
, read_chunk
);
228 // back+newb+space <= buffer_size
229 back2
=s
->buffer_size
-(space
+newb
); // max back size
230 if(s
->min_filepos
<(read
-back2
)) s
->min_filepos
=read
-back2
;
232 s
->min_filepos
=read
-back
; // avoid seeking-back to temp area...
235 if (wraparound_copy
) {
237 len
= stream_read_internal(s
->stream
, s
->stream
->buffer
, space
);
238 to_copy
= FFMIN(len
, s
->buffer_size
-pos
);
239 memcpy(s
->buffer
+ pos
, s
->stream
->buffer
, to_copy
);
240 memcpy(s
->buffer
, s
->stream
->buffer
+ to_copy
, len
- to_copy
);
242 len
= stream_read_internal(s
->stream
, &s
->buffer
[pos
], space
);
246 if(pos
+len
>=s
->buffer_size
){
248 s
->offset
+=s
->buffer_size
;
255 static int cache_execute_control(cache_vars_t
*s
) {
258 static unsigned last
;
259 int quit
= s
->control
== -2;
260 if (quit
|| !s
->stream
->control
) {
261 s
->stream_time_length
= 0;
262 s
->stream_time_pos
= MP_NOPTS_VALUE
;
263 s
->control_new_pos
= 0;
264 s
->control_res
= STREAM_UNSUPPORTED
;
268 if (GetTimerMS() - last
> 99) {
270 if (s
->stream
->control(s
->stream
, STREAM_CTRL_GET_TIME_LENGTH
, &len
) == STREAM_OK
)
271 s
->stream_time_length
= len
;
273 s
->stream_time_length
= 0;
274 if (s
->stream
->control(s
->stream
, STREAM_CTRL_GET_CURRENT_TIME
, &pos
) == STREAM_OK
)
275 s
->stream_time_pos
= pos
;
277 s
->stream_time_pos
= MP_NOPTS_VALUE
;
280 if (s
->control
== -1) return 1;
281 switch (s
->control
) {
282 case STREAM_CTRL_SEEK_TO_TIME
:
283 double_res
= s
->control_double_arg
;
284 case STREAM_CTRL_GET_CURRENT_TIME
:
285 case STREAM_CTRL_GET_ASPECT_RATIO
:
286 s
->control_res
= s
->stream
->control(s
->stream
, s
->control
, &double_res
);
287 s
->control_double_arg
= double_res
;
289 case STREAM_CTRL_SEEK_TO_CHAPTER
:
290 case STREAM_CTRL_SET_ANGLE
:
291 uint_res
= s
->control_uint_arg
;
292 case STREAM_CTRL_GET_NUM_CHAPTERS
:
293 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
294 case STREAM_CTRL_GET_NUM_ANGLES
:
295 case STREAM_CTRL_GET_ANGLE
:
296 s
->control_res
= s
->stream
->control(s
->stream
, s
->control
, &uint_res
);
297 s
->control_uint_arg
= uint_res
;
300 s
->control_res
= STREAM_UNSUPPORTED
;
303 s
->control_new_pos
= s
->stream
->pos
;
308 static void *shared_alloc(int size
) {
310 return shmem_alloc(size
);
316 static void shared_free(void *ptr
, int size
) {
318 shmem_free(ptr
, size
);
324 static cache_vars_t
* cache_init(int size
,int sector
){
326 cache_vars_t
* s
=shared_alloc(sizeof(cache_vars_t
));
327 if(s
==NULL
) return NULL
;
329 memset(s
,0,sizeof(cache_vars_t
));
334 s
->buffer_size
=num
*sector
;
335 s
->sector_size
=sector
;
336 s
->buffer
=shared_alloc(s
->buffer_size
);
338 if(s
->buffer
== NULL
){
339 shared_free(s
, sizeof(cache_vars_t
));
343 s
->fill_limit
=8*sector
;
344 s
->back_size
=s
->buffer_size
/2;
348 void cache_uninit(stream_t
*s
) {
349 cache_vars_t
* c
= s
->cache_data
;
352 cache_do_control(s
, -2, NULL
);
354 kill(s
->cache_pid
,SIGKILL
);
355 waitpid(s
->cache_pid
,NULL
,0);
360 shared_free(c
->buffer
, c
->buffer_size
);
363 shared_free(s
->cache_data
, sizeof(cache_vars_t
));
364 s
->cache_data
= NULL
;
368 static void exit_sighandler(int x
){
373 static void dummy_sighandler(int x
) {
378 * Main loop of the cache process or thread.
380 static void cache_mainloop(cache_vars_t
*s
) {
383 struct sigaction sa
= { .sa_handler
= SIG_IGN
};
384 sigaction(SIGUSR1
, &sa
, NULL
);
387 if (!cache_fill(s
)) {
389 // Let signal wake us up, we cannot leave this
390 // enabled since we do not handle EINTR in most places.
391 // This might need extra code to work on BSD.
392 sa
.sa_handler
= dummy_sighandler
;
393 sigaction(SIGUSR1
, &sa
, NULL
);
395 if (sleep_count
< INITIAL_FILL_USLEEP_COUNT
) {
397 usec_sleep(INITIAL_FILL_USLEEP_TIME
);
399 usec_sleep(FILL_USLEEP_TIME
); // idle
401 sa
.sa_handler
= SIG_IGN
;
402 sigaction(SIGUSR1
, &sa
, NULL
);
406 } while (cache_execute_control(s
));
409 int stream_enable_cache_percent(stream_t
*stream
, int stream_cache_size
,
410 float stream_cache_min_percent
, float stream_cache_seek_min_percent
)
412 return stream_enable_cache(stream
, stream_cache_size
* 1024,
413 stream_cache_size
* 1024 * (stream_cache_min_percent
/ 100.0),
414 stream_cache_size
* 1024 * (stream_cache_seek_min_percent
/ 100.0));
418 * \return 1 on success, 0 if the function was interrupted and -1 on error
420 int stream_enable_cache(stream_t
*stream
, int size
, int min
, int seek_limit
)
423 size
= stream
->cache_size
* 1024;
426 mp_tmsg(MSGT_NETWORK
,MSGL_INFO
,"Cache size set to %d KiB\n", size
/ 1024);
428 int ss
= stream
->sector_size
? stream
->sector_size
: STREAM_BUFFER_SIZE
;
432 s
=cache_init(size
,ss
);
433 if(s
== NULL
) return -1;
434 stream
->cache_data
=s
;
435 s
->stream
=stream
; // callback
436 s
->seek_limit
=seek_limit
;
439 //make sure that we won't wait from cache_fill
440 //more data than it is allowed to fill
441 if (s
->seek_limit
> s
->buffer_size
- s
->fill_limit
){
442 s
->seek_limit
= s
->buffer_size
- s
->fill_limit
;
444 if (min
> s
->buffer_size
- s
->fill_limit
) {
445 min
= s
->buffer_size
- s
->fill_limit
;
447 // to make sure we wait for the cache process/thread to be active
453 if((stream
->cache_pid
=fork())){
454 if ((pid_t
)stream
->cache_pid
== -1)
455 stream
->cache_pid
= 0;
458 stream_t
* stream2
=malloc(sizeof(stream_t
));
459 memcpy(stream2
,s
->stream
,sizeof(stream_t
));
461 #if defined(__MINGW32__)
462 stream
->cache_pid
= _beginthread( ThreadProc
, 0, s
);
466 pthread_create(&tid
, NULL
, ThreadProc
, s
);
467 stream
->cache_pid
= 1;
471 if (!stream
->cache_pid
) {
472 mp_msg(MSGT_CACHE
, MSGL_ERR
,
473 "Starting cache process/thread failed: %s.\n", strerror(errno
));
476 // wait until cache is filled at least prefill_init %
477 mp_msg(MSGT_CACHE
,MSGL_V
,"CACHE_PRE_INIT: %"PRId64
" [%"PRId64
"] %"PRId64
" pre:%d eof:%d \n",
478 (int64_t)s
->min_filepos
,(int64_t)s
->read_filepos
,(int64_t)s
->max_filepos
,min
,s
->eof
);
479 while(s
->read_filepos
<s
->min_filepos
|| s
->max_filepos
-s
->read_filepos
<min
){
480 mp_tmsg(MSGT_STATUSLINE
, MSGL_STATUS
, "\rCache fill: %5.2f%% (%"PRId64
" bytes) ",
481 100.0*(float)(s
->max_filepos
-s
->read_filepos
)/(float)(s
->buffer_size
),
482 (int64_t)s
->max_filepos
-s
->read_filepos
484 if(s
->eof
) break; // file is smaller than prefill size
485 if(stream_check_interrupt(PREFILL_SLEEP_TIME
)) {
490 stream
->cached
= true;
491 return 1; // parent exits
494 cache_uninit(stream
);
499 signal(SIGTERM
,exit_sighandler
); // kill
501 // make sure forked code never leaves this function
507 #if defined(__MINGW32__)
508 static void ThreadProc( void *s
){
513 static void *ThreadProc( void *s
){
520 int cache_stream_fill_buffer(stream_t
*s
){
523 if(!s
->cache_pid
) return stream_fill_buffer(s
);
525 if(s
->pos
!=((cache_vars_t
*)s
->cache_data
)->read_filepos
) mp_msg(MSGT_CACHE
,MSGL_ERR
,"!!! read_filepos differs!!! report this bug...\n");
526 sector_size
= ((cache_vars_t
*)s
->cache_data
)->sector_size
;
527 if (sector_size
> STREAM_MAX_SECTOR_SIZE
) {
528 mp_msg(MSGT_CACHE
, MSGL_ERR
, "Sector size %i larger than maximum %i\n", sector_size
, STREAM_MAX_SECTOR_SIZE
);
529 sector_size
= STREAM_MAX_SECTOR_SIZE
;
532 len
=cache_read(s
->cache_data
,s
->buffer
, sector_size
);
533 //printf("cache_stream_fill_buffer->read -> %d\n",len);
535 if(len
<=0){ s
->eof
=1; s
->buf_pos
=s
->buf_len
=0; return 0; }
540 // printf("[%d]",len);fflush(stdout);
542 stream_capture_do(s
);
547 int cache_fill_status(stream_t
*s
) {
549 if (!s
|| !s
->cache_data
)
552 return (cv
->max_filepos
-cv
->read_filepos
)/(cv
->buffer_size
/ 100);
555 int cache_stream_seek_long(stream_t
*stream
,off_t pos
){
558 if(!stream
->cache_pid
) return stream_seek_long(stream
,pos
);
560 s
=stream
->cache_data
;
563 mp_msg(MSGT_CACHE
,MSGL_DBG2
,"CACHE2_SEEK: 0x%"PRIX64
" <= 0x%"PRIX64
" (0x%"PRIX64
") <= 0x%"PRIX64
" \n",s
->min_filepos
,pos
,s
->read_filepos
,s
->max_filepos
);
565 newpos
=pos
/s
->sector_size
; newpos
*=s
->sector_size
; // align
566 stream
->pos
=s
->read_filepos
=newpos
;
568 cache_wakeup(stream
);
570 cache_stream_fill_buffer(stream
);
573 if(pos
>=0 && pos
<=stream
->buf_len
){
574 stream
->buf_pos
=pos
; // byte position in sector
578 // stream->buf_pos=stream->buf_len=0;
581 mp_msg(MSGT_CACHE
,MSGL_V
,"cache_stream_seek: WARNING! Can't seek to 0x%"PRIX64
" !\n",(int64_t)(pos
+newpos
));
585 int cache_do_control(stream_t
*stream
, int cmd
, void *arg
) {
587 cache_vars_t
* s
= stream
->cache_data
;
589 case STREAM_CTRL_SEEK_TO_TIME
:
590 s
->control_double_arg
= *(double *)arg
;
593 case STREAM_CTRL_SEEK_TO_CHAPTER
:
594 case STREAM_CTRL_SET_ANGLE
:
595 s
->control_uint_arg
= *(unsigned *)arg
;
598 // the core might call these every frame, so cache them...
599 case STREAM_CTRL_GET_TIME_LENGTH
:
600 *(double *)arg
= s
->stream_time_length
;
601 return s
->stream_time_length
? STREAM_OK
: STREAM_UNSUPPORTED
;
602 case STREAM_CTRL_GET_CURRENT_TIME
:
603 *(double *)arg
= s
->stream_time_pos
;
604 return s
->stream_time_pos
!= MP_NOPTS_VALUE
? STREAM_OK
: STREAM_UNSUPPORTED
;
605 case STREAM_CTRL_GET_NUM_CHAPTERS
:
606 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
607 case STREAM_CTRL_GET_ASPECT_RATIO
:
608 case STREAM_CTRL_GET_NUM_ANGLES
:
609 case STREAM_CTRL_GET_ANGLE
:
614 return STREAM_UNSUPPORTED
;
616 cache_wakeup(stream
);
617 while (s
->control
!= -1) {
618 if (sleep_count
++ == 1000)
619 mp_msg(MSGT_CACHE
, MSGL_WARN
, "Cache not responding!\n");
620 if (stream_check_interrupt(CONTROL_SLEEP_TIME
)) {
622 return STREAM_UNSUPPORTED
;
625 if (s
->control_res
!= STREAM_OK
)
626 return s
->control_res
;
628 case STREAM_CTRL_GET_TIME_LENGTH
:
629 case STREAM_CTRL_GET_CURRENT_TIME
:
630 case STREAM_CTRL_GET_ASPECT_RATIO
:
631 *(double *)arg
= s
->control_double_arg
;
633 case STREAM_CTRL_GET_NUM_CHAPTERS
:
634 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
635 case STREAM_CTRL_GET_NUM_ANGLES
:
636 case STREAM_CTRL_GET_ANGLE
:
637 *(unsigned *)arg
= s
->control_uint_arg
;
639 case STREAM_CTRL_SEEK_TO_CHAPTER
:
640 case STREAM_CTRL_SEEK_TO_TIME
:
641 case STREAM_CTRL_SET_ANGLE
:
642 stream
->pos
= s
->read_filepos
= s
->control_new_pos
;
645 return s
->control_res
;