Merge remote branch 'mplayer/master'
[mplayer/glamo.git] / stream / cache2.c
blob4c9bdf8aec735ef09c9bae16932a4539f0f82ac2
1 #include "config.h"
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
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <signal.h>
16 #include <sys/types.h>
17 #include <unistd.h>
19 #include "osdep/shmem.h"
20 #include "osdep/timer.h"
21 #if defined(__MINGW32__)
22 #include <windows.h>
23 static void ThreadProc( void *s );
24 #elif defined(__OS2__)
25 #define INCL_DOS
26 #include <os2.h>
27 static void ThreadProc( void *s );
28 #elif defined(PTHREAD_CACHE)
29 #include <pthread.h>
30 static void *ThreadProc(void *s);
31 #else
32 #include <sys/wait.h>
33 #endif
35 #include "mp_msg.h"
36 #include "help_mp.h"
38 #include "stream.h"
39 #include "cache2.h"
41 typedef struct {
42 // constats:
43 unsigned char *buffer; // base pointer of the alllocated buffer memory
44 int buffer_size; // size of the alllocated buffer memory
45 int sector_size; // size of a single sector (2048/2324)
46 int back_size; // we should keep back_size amount of old bytes for backward seek
47 int fill_limit; // we should fill buffer only if space>=fill_limit
48 int seek_limit; // keep filling cache if distanse is less that seek limit
49 // filler's pointers:
50 int eof;
51 off_t min_filepos; // buffer contain only a part of the file, from min-max pos
52 off_t max_filepos;
53 off_t offset; // filepos <-> bufferpos offset value (filepos of the buffer's first byte)
54 // reader's pointers:
55 off_t read_filepos;
56 // commands/locking:
57 // int seek_lock; // 1 if we will seek/reset buffer, 2 if we are ready for cmd
58 // int fifo_flag; // 1 if we should use FIFO to notice cache about buffer reads.
59 // callback
60 stream_t* stream;
61 volatile int control;
62 volatile unsigned control_uint_arg;
63 volatile double control_double_arg;
64 volatile int control_res;
65 volatile off_t control_new_pos;
66 volatile double stream_time_length;
67 } cache_vars_t;
69 static int min_fill=0;
71 int cache_fill_status=0;
73 static void cache_stats(cache_vars_t* s){
74 int newb=s->max_filepos-s->read_filepos; // new bytes in the buffer
75 mp_msg(MSGT_CACHE,MSGL_INFO,"0x%06X [0x%06X] 0x%06X ",(int)s->min_filepos,(int)s->read_filepos,(int)s->max_filepos);
76 mp_msg(MSGT_CACHE,MSGL_INFO,"%3d %% (%3d%%)\n",100*newb/s->buffer_size,100*min_fill/s->buffer_size);
79 static int cache_read(cache_vars_t* s,unsigned char* buf,int size){
80 int total=0;
81 while(size>0){
82 int pos,newb,len;
84 //printf("CACHE2_READ: 0x%X <= 0x%X <= 0x%X \n",s->min_filepos,s->read_filepos,s->max_filepos);
86 if(s->read_filepos>=s->max_filepos || s->read_filepos<s->min_filepos){
87 // eof?
88 if(s->eof) break;
89 // waiting for buffer fill...
90 usec_sleep(READ_USLEEP_TIME); // 10ms
91 continue; // try again...
94 newb=s->max_filepos-s->read_filepos; // new bytes in the buffer
95 if(newb<min_fill) min_fill=newb; // statistics...
97 // printf("*** newb: %d bytes ***\n",newb);
99 pos=s->read_filepos - s->offset;
100 if(pos<0) pos+=s->buffer_size; else
101 if(pos>=s->buffer_size) pos-=s->buffer_size;
103 if(newb>s->buffer_size-pos) newb=s->buffer_size-pos; // handle wrap...
104 if(newb>size) newb=size;
106 // check:
107 if(s->read_filepos<s->min_filepos) mp_msg(MSGT_CACHE,MSGL_ERR,"Ehh. s->read_filepos<s->min_filepos !!! Report bug...\n");
109 // len=write(mem,newb)
110 //printf("Buffer read: %d bytes\n",newb);
111 memcpy(buf,&s->buffer[pos],newb);
112 buf+=newb;
113 len=newb;
114 // ...
116 s->read_filepos+=len;
117 size-=len;
118 total+=len;
121 cache_fill_status=(s->max_filepos-s->read_filepos)/(s->buffer_size / 100);
122 return total;
125 static int cache_fill(cache_vars_t* s){
126 int back,back2,newb,space,len,pos;
127 off_t read=s->read_filepos;
129 if(read<s->min_filepos || read>s->max_filepos){
130 // seek...
131 mp_msg(MSGT_CACHE,MSGL_DBG2,"Out of boundaries... seeking to 0x%"PRIX64" \n",(int64_t)read);
132 // streaming: drop cache contents only if seeking backward or too much fwd:
133 if(s->stream->type!=STREAMTYPE_STREAM ||
134 read<s->min_filepos || read>=s->max_filepos+s->seek_limit)
136 s->offset= // FIXME!?
137 s->min_filepos=s->max_filepos=read; // drop cache content :(
138 if(s->stream->eof) stream_reset(s->stream);
139 stream_seek(s->stream,read);
140 mp_msg(MSGT_CACHE,MSGL_DBG2,"Seek done. new pos: 0x%"PRIX64" \n",(int64_t)stream_tell(s->stream));
144 // calc number of back-bytes:
145 back=read - s->min_filepos;
146 if(back<0) back=0; // strange...
147 if(back>s->back_size) back=s->back_size;
149 // calc number of new bytes:
150 newb=s->max_filepos - read;
151 if(newb<0) newb=0; // strange...
153 // calc free buffer space:
154 space=s->buffer_size - (newb+back);
156 // calc bufferpos:
157 pos=s->max_filepos - s->offset;
158 if(pos>=s->buffer_size) pos-=s->buffer_size; // wrap-around
160 if(space<s->fill_limit){
161 // printf("Buffer is full (%d bytes free, limit: %d)\n",space,s->fill_limit);
162 return 0; // no fill...
165 // printf("### read=0x%X back=%d newb=%d space=%d pos=%d\n",read,back,newb,space,pos);
167 // reduce space if needed:
168 if(space>s->buffer_size-pos) space=s->buffer_size-pos;
170 // if(space>32768) space=32768; // limit one-time block size
171 if(space>4*s->sector_size) space=4*s->sector_size;
173 // if(s->seek_lock) return 0; // FIXME
175 #if 1
176 // back+newb+space <= buffer_size
177 back2=s->buffer_size-(space+newb); // max back size
178 if(s->min_filepos<(read-back2)) s->min_filepos=read-back2;
179 #else
180 s->min_filepos=read-back; // avoid seeking-back to temp area...
181 #endif
183 // ....
184 //printf("Buffer fill: %d bytes of %d\n",space,s->buffer_size);
185 //len=stream_fill_buffer(s->stream);
186 //memcpy(&s->buffer[pos],s->stream->buffer,len); // avoid this extra copy!
187 // ....
188 len=stream_read(s->stream,&s->buffer[pos],space);
189 if(!len) s->eof=1;
191 s->max_filepos+=len;
192 if(pos+len>=s->buffer_size){
193 // wrap...
194 s->offset+=s->buffer_size;
197 return len;
201 static int cache_execute_control(cache_vars_t *s) {
202 int res = 1;
203 static unsigned last;
204 if (!s->stream->control) {
205 s->stream_time_length = 0;
206 s->control_new_pos = 0;
207 s->control_res = STREAM_UNSUPPORTED;
208 s->control = -1;
209 return res;
211 if (GetTimerMS() - last > 99) {
212 double len;
213 if (s->stream->control(s->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) == STREAM_OK)
214 s->stream_time_length = len;
215 else
216 s->stream_time_length = 0;
217 last = GetTimerMS();
219 if (s->control == -1) return res;
220 switch (s->control) {
221 case STREAM_CTRL_GET_CURRENT_TIME:
222 case STREAM_CTRL_SEEK_TO_TIME:
223 case STREAM_CTRL_GET_ASPECT_RATIO:
224 s->control_res = s->stream->control(s->stream, s->control, &s->control_double_arg);
225 break;
226 case STREAM_CTRL_SEEK_TO_CHAPTER:
227 case STREAM_CTRL_GET_NUM_CHAPTERS:
228 case STREAM_CTRL_GET_CURRENT_CHAPTER:
229 case STREAM_CTRL_GET_NUM_ANGLES:
230 case STREAM_CTRL_GET_ANGLE:
231 case STREAM_CTRL_SET_ANGLE:
232 s->control_res = s->stream->control(s->stream, s->control, &s->control_uint_arg);
233 break;
234 case -2:
235 res = 0;
236 default:
237 s->control_res = STREAM_UNSUPPORTED;
238 break;
240 s->control_new_pos = s->stream->pos;
241 s->control = -1;
242 return res;
245 static cache_vars_t* cache_init(int size,int sector){
246 int num;
247 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
248 cache_vars_t* s=shmem_alloc(sizeof(cache_vars_t));
249 #else
250 cache_vars_t* s=malloc(sizeof(cache_vars_t));
251 #endif
252 if(s==NULL) return NULL;
254 memset(s,0,sizeof(cache_vars_t));
255 num=size/sector;
256 if(num < 16){
257 num = 16;
258 }//32kb min_size
259 s->buffer_size=num*sector;
260 s->sector_size=sector;
261 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
262 s->buffer=shmem_alloc(s->buffer_size);
263 #else
264 s->buffer=malloc(s->buffer_size);
265 #endif
267 if(s->buffer == NULL){
268 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
269 shmem_free(s,sizeof(cache_vars_t));
270 #else
271 free(s);
272 #endif
273 return NULL;
276 s->fill_limit=8*sector;
277 s->back_size=s->buffer_size/2;
278 return s;
281 void cache_uninit(stream_t *s) {
282 cache_vars_t* c = s->cache_data;
283 if(!s->cache_pid) return;
284 #if defined(__MINGW32__) || defined(PTHREAD_CACHE) || defined(__OS2__)
285 cache_do_control(s, -2, NULL);
286 #else
287 kill(s->cache_pid,SIGKILL);
288 waitpid(s->cache_pid,NULL,0);
289 #endif
290 if(!c) return;
291 #if defined(__MINGW32__) || defined(PTHREAD_CACHE) || defined(__OS2__)
292 free(c->stream);
293 free(c->buffer);
294 free(s->cache_data);
295 #else
296 shmem_free(c->buffer,c->buffer_size);
297 shmem_free(s->cache_data,sizeof(cache_vars_t));
298 #endif
301 static void exit_sighandler(int x){
302 // close stream
303 exit(0);
306 int stream_enable_cache(stream_t *stream,int size,int min,int seek_limit){
307 int ss = stream->sector_size ? stream->sector_size : STREAM_BUFFER_SIZE;
308 cache_vars_t* s;
310 if (stream->flags & STREAM_NON_CACHEABLE) {
311 mp_msg(MSGT_CACHE,MSGL_STATUS,"\rThis stream is non-cacheable\n");
312 return 1;
315 s=cache_init(size,ss);
316 if(s == NULL) return 0;
317 stream->cache_data=s;
318 s->stream=stream; // callback
319 s->seek_limit=seek_limit;
322 //make sure that we won't wait from cache_fill
323 //more data than it is alowed to fill
324 if (s->seek_limit > s->buffer_size - s->fill_limit ){
325 s->seek_limit = s->buffer_size - s->fill_limit;
327 if (min > s->buffer_size - s->fill_limit) {
328 min = s->buffer_size - s->fill_limit;
331 #if !defined(__MINGW32__) && !defined(PTHREAD_CACHE) && !defined(__OS2__)
332 if((stream->cache_pid=fork())){
333 #else
335 stream_t* stream2=malloc(sizeof(stream_t));
336 memcpy(stream2,s->stream,sizeof(stream_t));
337 s->stream=stream2;
338 #if defined(__MINGW32__)
339 stream->cache_pid = _beginthread( ThreadProc, 0, s );
340 #elif defined(__OS2__)
341 stream->cache_pid = _beginthread( ThreadProc, NULL, 256 * 1024, s );
342 #else
344 pthread_t tid;
345 pthread_create(&tid, NULL, ThreadProc, s);
346 stream->cache_pid = 1;
348 #endif
349 #endif
350 // wait until cache is filled at least prefill_init %
351 mp_msg(MSGT_CACHE,MSGL_V,"CACHE_PRE_INIT: %"PRId64" [%"PRId64"] %"PRId64" pre:%d eof:%d \n",
352 (int64_t)s->min_filepos,(int64_t)s->read_filepos,(int64_t)s->max_filepos,min,s->eof);
353 while(s->read_filepos<s->min_filepos || s->max_filepos-s->read_filepos<min){
354 mp_tmsg(MSGT_CACHE,MSGL_STATUS,"\rCache fill: %5.2f%% (%"PRId64" bytes) ",
355 100.0*(float)(s->max_filepos-s->read_filepos)/(float)(s->buffer_size),
356 (int64_t)s->max_filepos-s->read_filepos
358 if(s->eof) break; // file is smaller than prefill size
359 if(stream_check_interrupt(PREFILL_SLEEP_TIME))
360 return 0;
362 mp_msg(MSGT_CACHE,MSGL_STATUS,"\n");
363 return 1; // parent exits
366 #if defined(__MINGW32__) || defined(PTHREAD_CACHE) || defined(__OS2__)
368 #ifdef PTHREAD_CACHE
369 static void *ThreadProc( void *s ){
370 #else
371 static void ThreadProc( void *s ){
372 #endif
373 #endif
375 // cache thread mainloop:
376 signal(SIGTERM,exit_sighandler); // kill
377 do {
378 if(!cache_fill(s)){
379 usec_sleep(FILL_USLEEP_TIME); // idle
381 // cache_stats(s->cache_data);
382 } while (cache_execute_control(s));
383 #if defined(__MINGW32__) || defined(__OS2__)
384 _endthread();
385 #endif
386 #ifdef PTHREAD_CACHE
387 return NULL;
388 #endif
391 int cache_stream_fill_buffer(stream_t *s){
392 int len;
393 if(s->eof){ s->buf_pos=s->buf_len=0; return 0; }
394 if(!s->cache_pid) return stream_fill_buffer(s);
396 // cache_stats(s->cache_data);
398 if(s->pos!=((cache_vars_t*)s->cache_data)->read_filepos) mp_msg(MSGT_CACHE,MSGL_ERR,"!!! read_filepos differs!!! report this bug...\n");
400 len=cache_read(s->cache_data,s->buffer, ((cache_vars_t*)s->cache_data)->sector_size);
401 //printf("cache_stream_fill_buffer->read -> %d\n",len);
403 if(len<=0){ s->eof=1; s->buf_pos=s->buf_len=0; return 0; }
404 s->buf_pos=0;
405 s->buf_len=len;
406 s->pos+=len;
407 // printf("[%d]",len);fflush(stdout);
408 return len;
412 int cache_stream_seek_long(stream_t *stream,off_t pos){
413 cache_vars_t* s;
414 off_t newpos;
415 if(!stream->cache_pid) return stream_seek_long(stream,pos);
417 s=stream->cache_data;
418 // s->seek_lock=1;
420 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);
422 newpos=pos/s->sector_size; newpos*=s->sector_size; // align
423 stream->pos=s->read_filepos=newpos;
424 s->eof=0; // !!!!!!!
426 cache_stream_fill_buffer(stream);
428 pos-=newpos;
429 if(pos>=0 && pos<=stream->buf_len){
430 stream->buf_pos=pos; // byte position in sector
431 return 1;
434 // stream->buf_pos=stream->buf_len=0;
435 // return 1;
437 mp_msg(MSGT_CACHE,MSGL_V,"cache_stream_seek: WARNING! Can't seek to 0x%"PRIX64" !\n",(int64_t)(pos+newpos));
438 return 0;
441 int cache_do_control(stream_t *stream, int cmd, void *arg) {
442 cache_vars_t* s = stream->cache_data;
443 switch (cmd) {
444 case STREAM_CTRL_SEEK_TO_TIME:
445 s->control_double_arg = *(double *)arg;
446 s->control = cmd;
447 break;
448 case STREAM_CTRL_SEEK_TO_CHAPTER:
449 case STREAM_CTRL_SET_ANGLE:
450 s->control_uint_arg = *(unsigned *)arg;
451 s->control = cmd;
452 break;
453 // the core might call these every frame, they are too slow for this...
454 case STREAM_CTRL_GET_TIME_LENGTH:
455 // case STREAM_CTRL_GET_CURRENT_TIME:
456 *(double *)arg = s->stream_time_length;
457 return s->stream_time_length ? STREAM_OK : STREAM_UNSUPPORTED;
458 case STREAM_CTRL_GET_NUM_CHAPTERS:
459 case STREAM_CTRL_GET_CURRENT_CHAPTER:
460 case STREAM_CTRL_GET_ASPECT_RATIO:
461 case STREAM_CTRL_GET_NUM_ANGLES:
462 case STREAM_CTRL_GET_ANGLE:
463 case -2:
464 s->control = cmd;
465 break;
466 default:
467 return STREAM_UNSUPPORTED;
469 while (s->control != -1)
470 usec_sleep(CONTROL_SLEEP_TIME);
471 switch (cmd) {
472 case STREAM_CTRL_GET_TIME_LENGTH:
473 case STREAM_CTRL_GET_CURRENT_TIME:
474 case STREAM_CTRL_GET_ASPECT_RATIO:
475 *(double *)arg = s->control_double_arg;
476 break;
477 case STREAM_CTRL_GET_NUM_CHAPTERS:
478 case STREAM_CTRL_GET_CURRENT_CHAPTER:
479 case STREAM_CTRL_GET_NUM_ANGLES:
480 case STREAM_CTRL_GET_ANGLE:
481 *(unsigned *)arg = s->control_uint_arg;
482 break;
483 case STREAM_CTRL_SEEK_TO_CHAPTER:
484 case STREAM_CTRL_SEEK_TO_TIME:
485 case STREAM_CTRL_SET_ANGLE:
486 stream->pos = s->read_filepos = s->control_new_pos;
487 break;
489 return s->control_res;