3 // Initial draft of my new cache system...
4 // Note it runs in 2 processes (using fork()), but doesn't requires locking!!
5 // TODO: seeking, data consistency checking
7 #define READ_USLEEP_TIME 10000
8 #define FILL_USLEEP_TIME 50000
9 #define PREFILL_SLEEP_TIME 200
10 #define CONTROL_SLEEP_TIME 0
16 #include <sys/types.h>
19 #include "osdep/shmem.h"
20 #include "osdep/timer.h"
21 #if defined(__MINGW32__)
23 static void ThreadProc( void *s
);
24 #elif defined(__OS2__)
27 static void ThreadProc( void *s
);
28 #elif defined(PTHREAD_CACHE)
30 static void *ThreadProc(void *s
);
42 int stream_fill_buffer(stream_t
*s
);
43 int stream_seek_long(stream_t
*s
,off_t pos
);
47 unsigned char *buffer
; // base pointer of the alllocated buffer memory
48 int buffer_size
; // size of the alllocated buffer memory
49 int sector_size
; // size of a single sector (2048/2324)
50 int back_size
; // we should keep back_size amount of old bytes for backward seek
51 int fill_limit
; // we should fill buffer only if space>=fill_limit
52 int seek_limit
; // keep filling cache if distanse is less that seek limit
55 off_t min_filepos
; // buffer contain only a part of the file, from min-max pos
57 off_t offset
; // filepos <-> bufferpos offset value (filepos of the buffer's first byte)
61 // int seek_lock; // 1 if we will seek/reset buffer, 2 if we are ready for cmd
62 // int fifo_flag; // 1 if we should use FIFO to notice cache about buffer reads.
66 volatile unsigned control_uint_arg
;
67 volatile double control_double_arg
;
68 volatile int control_res
;
69 volatile off_t control_new_pos
;
70 volatile double stream_time_length
;
73 static int min_fill
=0;
75 int cache_fill_status
=0;
77 void cache_stats(cache_vars_t
* s
){
78 int newb
=s
->max_filepos
-s
->read_filepos
; // new bytes in the buffer
79 mp_msg(MSGT_CACHE
,MSGL_INFO
,"0x%06X [0x%06X] 0x%06X ",(int)s
->min_filepos
,(int)s
->read_filepos
,(int)s
->max_filepos
);
80 mp_msg(MSGT_CACHE
,MSGL_INFO
,"%3d %% (%3d%%)\n",100*newb
/s
->buffer_size
,100*min_fill
/s
->buffer_size
);
83 int cache_read(cache_vars_t
* s
,unsigned char* buf
,int size
){
88 //printf("CACHE2_READ: 0x%X <= 0x%X <= 0x%X \n",s->min_filepos,s->read_filepos,s->max_filepos);
90 if(s
->read_filepos
>=s
->max_filepos
|| s
->read_filepos
<s
->min_filepos
){
93 // waiting for buffer fill...
94 usec_sleep(READ_USLEEP_TIME
); // 10ms
95 continue; // try again...
98 newb
=s
->max_filepos
-s
->read_filepos
; // new bytes in the buffer
99 if(newb
<min_fill
) min_fill
=newb
; // statistics...
101 // printf("*** newb: %d bytes ***\n",newb);
103 pos
=s
->read_filepos
- s
->offset
;
104 if(pos
<0) pos
+=s
->buffer_size
; else
105 if(pos
>=s
->buffer_size
) pos
-=s
->buffer_size
;
107 if(newb
>s
->buffer_size
-pos
) newb
=s
->buffer_size
-pos
; // handle wrap...
108 if(newb
>size
) newb
=size
;
111 if(s
->read_filepos
<s
->min_filepos
) mp_msg(MSGT_CACHE
,MSGL_ERR
,"Ehh. s->read_filepos<s->min_filepos !!! Report bug...\n");
113 // len=write(mem,newb)
114 //printf("Buffer read: %d bytes\n",newb);
115 memcpy(buf
,&s
->buffer
[pos
],newb
);
120 s
->read_filepos
+=len
;
125 cache_fill_status
=(s
->max_filepos
-s
->read_filepos
)/(s
->buffer_size
/ 100);
129 int cache_fill(cache_vars_t
* s
){
130 int back
,back2
,newb
,space
,len
,pos
;
131 off_t read
=s
->read_filepos
;
133 if(read
<s
->min_filepos
|| read
>s
->max_filepos
){
135 mp_msg(MSGT_CACHE
,MSGL_DBG2
,"Out of boundaries... seeking to 0x%"PRIX64
" \n",(int64_t)read
);
136 // streaming: drop cache contents only if seeking backward or too much fwd:
137 if(s
->stream
->type
!=STREAMTYPE_STREAM
||
138 read
<s
->min_filepos
|| read
>=s
->max_filepos
+s
->seek_limit
)
140 s
->offset
= // FIXME!?
141 s
->min_filepos
=s
->max_filepos
=read
; // drop cache content :(
142 if(s
->stream
->eof
) stream_reset(s
->stream
);
143 stream_seek(s
->stream
,read
);
144 mp_msg(MSGT_CACHE
,MSGL_DBG2
,"Seek done. new pos: 0x%"PRIX64
" \n",(int64_t)stream_tell(s
->stream
));
148 // calc number of back-bytes:
149 back
=read
- s
->min_filepos
;
150 if(back
<0) back
=0; // strange...
151 if(back
>s
->back_size
) back
=s
->back_size
;
153 // calc number of new bytes:
154 newb
=s
->max_filepos
- read
;
155 if(newb
<0) newb
=0; // strange...
157 // calc free buffer space:
158 space
=s
->buffer_size
- (newb
+back
);
161 pos
=s
->max_filepos
- s
->offset
;
162 if(pos
>=s
->buffer_size
) pos
-=s
->buffer_size
; // wrap-around
164 if(space
<s
->fill_limit
){
165 // printf("Buffer is full (%d bytes free, limit: %d)\n",space,s->fill_limit);
166 return 0; // no fill...
169 // printf("### read=0x%X back=%d newb=%d space=%d pos=%d\n",read,back,newb,space,pos);
171 // reduce space if needed:
172 if(space
>s
->buffer_size
-pos
) space
=s
->buffer_size
-pos
;
174 // if(space>32768) space=32768; // limit one-time block size
175 if(space
>4*s
->sector_size
) space
=4*s
->sector_size
;
177 // if(s->seek_lock) return 0; // FIXME
180 // back+newb+space <= buffer_size
181 back2
=s
->buffer_size
-(space
+newb
); // max back size
182 if(s
->min_filepos
<(read
-back2
)) s
->min_filepos
=read
-back2
;
184 s
->min_filepos
=read
-back
; // avoid seeking-back to temp area...
188 //printf("Buffer fill: %d bytes of %d\n",space,s->buffer_size);
189 //len=stream_fill_buffer(s->stream);
190 //memcpy(&s->buffer[pos],s->stream->buffer,len); // avoid this extra copy!
192 len
=stream_read(s
->stream
,&s
->buffer
[pos
],space
);
196 if(pos
+len
>=s
->buffer_size
){
198 s
->offset
+=s
->buffer_size
;
205 static int cache_execute_control(cache_vars_t
*s
) {
207 static unsigned last
;
208 if (!s
->stream
->control
) {
209 s
->stream_time_length
= 0;
210 s
->control_new_pos
= 0;
211 s
->control_res
= STREAM_UNSUPPORTED
;
215 if (GetTimerMS() - last
> 99) {
217 if (s
->stream
->control(s
->stream
, STREAM_CTRL_GET_TIME_LENGTH
, &len
) == STREAM_OK
)
218 s
->stream_time_length
= len
;
220 s
->stream_time_length
= 0;
223 if (s
->control
== -1) return res
;
224 switch (s
->control
) {
225 case STREAM_CTRL_GET_CURRENT_TIME
:
226 case STREAM_CTRL_SEEK_TO_TIME
:
227 case STREAM_CTRL_GET_ASPECT_RATIO
:
228 s
->control_res
= s
->stream
->control(s
->stream
, s
->control
, &s
->control_double_arg
);
230 case STREAM_CTRL_SEEK_TO_CHAPTER
:
231 case STREAM_CTRL_GET_NUM_CHAPTERS
:
232 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
233 case STREAM_CTRL_GET_NUM_ANGLES
:
234 case STREAM_CTRL_GET_ANGLE
:
235 case STREAM_CTRL_SET_ANGLE
:
236 s
->control_res
= s
->stream
->control(s
->stream
, s
->control
, &s
->control_uint_arg
);
241 s
->control_res
= STREAM_UNSUPPORTED
;
244 s
->control_new_pos
= s
->stream
->pos
;
249 cache_vars_t
* cache_init(int size
,int sector
){
251 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
252 cache_vars_t
* s
=shmem_alloc(sizeof(cache_vars_t
));
254 cache_vars_t
* s
=malloc(sizeof(cache_vars_t
));
256 if(s
==NULL
) return NULL
;
258 memset(s
,0,sizeof(cache_vars_t
));
263 s
->buffer_size
=num
*sector
;
264 s
->sector_size
=sector
;
265 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
266 s
->buffer
=shmem_alloc(s
->buffer_size
);
268 s
->buffer
=malloc(s
->buffer_size
);
271 if(s
->buffer
== NULL
){
272 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
273 shmem_free(s
,sizeof(cache_vars_t
));
280 s
->fill_limit
=8*sector
;
281 s
->back_size
=s
->buffer_size
/2;
285 void cache_uninit(stream_t
*s
) {
286 cache_vars_t
* c
= s
->cache_data
;
287 if(!s
->cache_pid
) return;
288 #if defined(__MINGW32__) || defined(PTHREAD_CACHE) || defined(__OS2__)
289 cache_do_control(s
, -2, NULL
);
291 kill(s
->cache_pid
,SIGKILL
);
292 waitpid(s
->cache_pid
,NULL
,0);
295 #if defined(__MINGW32__) || defined(PTHREAD_CACHE) || defined(__OS2__)
300 shmem_free(c
->buffer
,c
->buffer_size
);
301 shmem_free(s
->cache_data
,sizeof(cache_vars_t
));
305 static void exit_sighandler(int x
){
310 int stream_enable_cache(stream_t
*stream
,int size
,int min
,int seek_limit
){
311 int ss
= stream
->sector_size
? stream
->sector_size
: STREAM_BUFFER_SIZE
;
314 if (stream
->type
==STREAMTYPE_STREAM
&& stream
->fd
< 0) {
315 // The stream has no 'fd' behind it, so is non-cacheable
316 mp_msg(MSGT_CACHE
,MSGL_STATUS
,"\rThis stream is non-cacheable\n");
320 s
=cache_init(size
,ss
);
321 if(s
== NULL
) return 0;
322 stream
->cache_data
=s
;
323 s
->stream
=stream
; // callback
324 s
->seek_limit
=seek_limit
;
327 //make sure that we won't wait from cache_fill
328 //more data than it is alowed to fill
329 if (s
->seek_limit
> s
->buffer_size
- s
->fill_limit
){
330 s
->seek_limit
= s
->buffer_size
- s
->fill_limit
;
332 if (min
> s
->buffer_size
- s
->fill_limit
) {
333 min
= s
->buffer_size
- s
->fill_limit
;
336 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
337 if((stream
->cache_pid
=fork())){
340 stream_t
* stream2
=malloc(sizeof(stream_t
));
341 memcpy(stream2
,s
->stream
,sizeof(stream_t
));
343 #if defined(__MINGW32__)
344 stream
->cache_pid
= _beginthread( ThreadProc
, 0, s
);
345 #elif defined(__OS2__)
346 stream
->cache_pid
= _beginthread( ThreadProc
, NULL
, 256 * 1024, s
);
350 pthread_create(&tid
, NULL
, ThreadProc
, s
);
351 stream
->cache_pid
= 1;
355 // wait until cache is filled at least prefill_init %
356 mp_msg(MSGT_CACHE
,MSGL_V
,"CACHE_PRE_INIT: %"PRId64
" [%"PRId64
"] %"PRId64
" pre:%d eof:%d \n",
357 (int64_t)s
->min_filepos
,(int64_t)s
->read_filepos
,(int64_t)s
->max_filepos
,min
,s
->eof
);
358 while(s
->read_filepos
<s
->min_filepos
|| s
->max_filepos
-s
->read_filepos
<min
){
359 mp_msg(MSGT_CACHE
,MSGL_STATUS
,MSGTR_CacheFill
,
360 100.0*(float)(s
->max_filepos
-s
->read_filepos
)/(float)(s
->buffer_size
),
361 (int64_t)s
->max_filepos
-s
->read_filepos
363 if(s
->eof
) break; // file is smaller than prefill size
364 if(stream_check_interrupt(PREFILL_SLEEP_TIME
))
367 mp_msg(MSGT_CACHE
,MSGL_STATUS
,"\n");
368 return 1; // parent exits
371 #if defined(__MINGW32__) || defined(PTHREAD_CACHE) || defined(__OS2__)
374 static void *ThreadProc( void *s
){
376 static void ThreadProc( void *s
){
381 use_gui
= 0; // mp_msg may not use gui stuff in forked code
383 // cache thread mainloop:
384 signal(SIGTERM
,exit_sighandler
); // kill
387 usec_sleep(FILL_USLEEP_TIME
); // idle
389 // cache_stats(s->cache_data);
390 } while (cache_execute_control(s
));
391 #if defined(__MINGW32__) || defined(__OS2__)
399 int cache_stream_fill_buffer(stream_t
*s
){
401 if(s
->eof
){ s
->buf_pos
=s
->buf_len
=0; return 0; }
402 if(!s
->cache_pid
) return stream_fill_buffer(s
);
404 // cache_stats(s->cache_data);
406 if(s
->pos
!=((cache_vars_t
*)s
->cache_data
)->read_filepos
) mp_msg(MSGT_CACHE
,MSGL_ERR
,"!!! read_filepos differs!!! report this bug...\n");
408 len
=cache_read(s
->cache_data
,s
->buffer
, ((cache_vars_t
*)s
->cache_data
)->sector_size
);
409 //printf("cache_stream_fill_buffer->read -> %d\n",len);
411 if(len
<=0){ s
->eof
=1; s
->buf_pos
=s
->buf_len
=0; return 0; }
415 // printf("[%d]",len);fflush(stdout);
420 int cache_stream_seek_long(stream_t
*stream
,off_t pos
){
423 if(!stream
->cache_pid
) return stream_seek_long(stream
,pos
);
425 s
=stream
->cache_data
;
428 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
);
430 newpos
=pos
/s
->sector_size
; newpos
*=s
->sector_size
; // align
431 stream
->pos
=s
->read_filepos
=newpos
;
434 cache_stream_fill_buffer(stream
);
437 if(pos
>=0 && pos
<=stream
->buf_len
){
438 stream
->buf_pos
=pos
; // byte position in sector
442 // stream->buf_pos=stream->buf_len=0;
445 mp_msg(MSGT_CACHE
,MSGL_V
,"cache_stream_seek: WARNING! Can't seek to 0x%"PRIX64
" !\n",(int64_t)(pos
+newpos
));
449 int cache_do_control(stream_t
*stream
, int cmd
, void *arg
) {
450 cache_vars_t
* s
= stream
->cache_data
;
452 case STREAM_CTRL_SEEK_TO_TIME
:
453 s
->control_double_arg
= *(double *)arg
;
456 case STREAM_CTRL_SEEK_TO_CHAPTER
:
457 case STREAM_CTRL_SET_ANGLE
:
458 s
->control_uint_arg
= *(unsigned *)arg
;
461 // the core might call these every frame, they are too slow for this...
462 case STREAM_CTRL_GET_TIME_LENGTH
:
463 // case STREAM_CTRL_GET_CURRENT_TIME:
464 *(double *)arg
= s
->stream_time_length
;
465 return s
->stream_time_length
? STREAM_OK
: STREAM_UNSUPPORTED
;
466 case STREAM_CTRL_GET_NUM_CHAPTERS
:
467 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
468 case STREAM_CTRL_GET_ASPECT_RATIO
:
469 case STREAM_CTRL_GET_NUM_ANGLES
:
470 case STREAM_CTRL_GET_ANGLE
:
475 return STREAM_UNSUPPORTED
;
477 while (s
->control
!= -1)
478 usec_sleep(CONTROL_SLEEP_TIME
);
480 case STREAM_CTRL_GET_TIME_LENGTH
:
481 case STREAM_CTRL_GET_CURRENT_TIME
:
482 case STREAM_CTRL_GET_ASPECT_RATIO
:
483 *(double *)arg
= s
->control_double_arg
;
485 case STREAM_CTRL_GET_NUM_CHAPTERS
:
486 case STREAM_CTRL_GET_CURRENT_CHAPTER
:
487 case STREAM_CTRL_GET_NUM_ANGLES
:
488 case STREAM_CTRL_GET_ANGLE
:
489 *(unsigned *)arg
= s
->control_uint_arg
;
491 case STREAM_CTRL_SEEK_TO_CHAPTER
:
492 case STREAM_CTRL_SEEK_TO_TIME
:
493 case STREAM_CTRL_SET_ANGLE
:
494 stream
->pos
= s
->read_filepos
= s
->control_new_pos
;
497 return s
->control_res
;