1 /*
2  * Copyright (C) 2000-2018 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * Buffered Input Plugin (request optimizer).
21  *
22  * The goal of this input plugin is to reduce
23  * the number of calls to the real input plugin.
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #define LOG_MODULE "input_cache"
31 #define LOG_VERBOSE
32 /*
33 #define LOG
34 */
35 
36 #include <xine/xine_internal.h>
37 #include "xine_private.h"
38 
39 #define DEFAULT_BUFFER_SIZE 8192
40 
41 typedef struct {
42   input_plugin_t    input_plugin;      /* inherited structure */
43 
44   input_plugin_t   *main_input_plugin; /* original input plugin */
45   xine_stream_t    *stream;
46 
47   char             *buf;
48   size_t            buf_size;          /* allocated size */
49   int               buf_len;           /* data size */
50   int               buf_pos;
51 
52   int               is_clone;
53 
54   /* Statistics */
55   int               read_call;
56   int               main_read_call;
57   int               seek_call;
58   int               main_seek_call;
59 
60 } cache_input_plugin_t;
61 
62 
63 /*
64  * read data from input plugin and write it into file
65  */
cache_plugin_read(input_plugin_t * this_gen,void * buf_gen,off_t len)66 static off_t cache_plugin_read(input_plugin_t *this_gen, void *buf_gen, off_t len) {
67   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
68   char *buf = (char *)buf_gen;
69 
70   lprintf("cache_plugin_read: len=%"PRId64"\n", len);
71   this->read_call++;
72 
73   if (len <= 0) {
74     _x_assert(len >= 0);
75     return len;
76   }
77 
78   /* optimized for common cases */
79   if (len <= (this->buf_len - this->buf_pos)) {
80     /* all bytes are in the buffer */
81     switch (len) {
82 #if defined(__i386__) || defined(__x86_64__)
83       /* These are restricted to x86 and amd64. Some other architectures don't
84        * handle unaligned accesses in the same way, quite possibly requiring
85        * extra code over and above simple byte copies.
86        */
87       case 8:
88         *((uint64_t *)buf) = *(uint64_t *)(&(this->buf[this->buf_pos]));
89         break;
90       case 7:
91         buf[6] = (char)this->buf[this->buf_pos + 6];
92         /* fallthru */
93       case 6:
94         *((uint32_t *)buf) = *(uint32_t *)(&(this->buf[this->buf_pos]));
95         *((uint16_t *)&buf[4]) = *(uint16_t *)(&(this->buf[this->buf_pos + 4]));
96         break;
97       case 5:
98         buf[4] = (char)this->buf[this->buf_pos + 4];
99         /* fallthru */
100       case 4:
101         *((uint32_t *)buf) = *(uint32_t *)(&(this->buf[this->buf_pos]));
102         break;
103       case 3:
104         buf[2] = (char)this->buf[this->buf_pos + 2];
105         /* fallthru */
106       case 2:
107         *((uint16_t *)buf) = *(uint16_t *)(&(this->buf[this->buf_pos]));
108         break;
109 #endif
110       case 1:
111         *buf = (char)this->buf[this->buf_pos];
112         break;
113       default:
114         xine_fast_memcpy(buf, this->buf + this->buf_pos, len);
115     }
116     this->buf_pos += len;
117     return len;
118   }
119 
120   {
121     off_t read_len = 0;
122     int in_buf_len;
123 
124     /* copy internal buffer bytes */
125     in_buf_len = this->buf_len - this->buf_pos;
126     if (in_buf_len > 0) {
127       xine_fast_memcpy(buf, this->buf + this->buf_pos, in_buf_len);
128       len -= in_buf_len;
129       read_len = in_buf_len;
130     }
131     this->buf_len = 0;
132     this->buf_pos = 0;
133 
134     /* read the rest */
135     if (len < (off_t)this->buf_size) {
136       /* readahead bytes */
137       do {
138         int main_read;
139         this->main_read_call++;
140         main_read = this->main_input_plugin->read (this->main_input_plugin,
141           this->buf + this->buf_len, this->buf_size - this->buf_len);
142         if (main_read == 0) { /* EOF */
143           len = this->buf_len;
144           break;
145         }
146         if (main_read < 0) /* read error: report return value to caller */
147           return main_read;
148         this->buf_len += main_read;
149       } while (this->buf_len < (int)len);
150       if (len) {
151         xine_fast_memcpy (buf + read_len, this->buf, len);
152         this->buf_pos = len;
153         read_len += len;
154       }
155       return read_len;
156     }
157 
158     do {
159       /* direct read */
160       off_t main_read;
161       this->main_read_call++;
162       main_read = this->main_input_plugin->read (this->main_input_plugin, buf + read_len, len);
163       if (main_read == 0) /* EOF */
164         break;
165       if (main_read < 0) /* read error: report return value to caller */
166         return main_read;
167       read_len += main_read;
168       len -= main_read;
169     } while (len > 0);
170     return read_len;
171   }
172 }
173 
174 /*
175  * open should never be called
176  */
cache_plugin_open(input_plugin_t * this_gen)177 static int cache_plugin_open(input_plugin_t *this_gen) {
178   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
179 
180   xine_log(this->stream->xine, XINE_LOG_MSG,
181 	   _(LOG_MODULE": open() function should never be called\n"));
182   return 0;
183 }
184 
cache_plugin_get_capabilities(input_plugin_t * this_gen)185 static uint32_t cache_plugin_get_capabilities(input_plugin_t *this_gen) {
186   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
187 
188   return this->main_input_plugin->get_capabilities(this->main_input_plugin);
189 }
190 
cache_plugin_read_block(input_plugin_t * this_gen,fifo_buffer_t * fifo,off_t todo)191 static buf_element_t *cache_plugin_read_block(input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo) {
192   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
193   buf_element_t *buf;
194   int in_buf_len;
195 
196   in_buf_len = this->buf_len - this->buf_pos;
197   if (in_buf_len > 0) {
198     off_t read_len;
199 
200     /* hmmm, the demuxer mixes read and read_block */
201     buf = fifo->buffer_pool_alloc (fifo);
202     if (buf) {
203       buf->type = BUF_DEMUX_BLOCK;
204 
205       _x_assert(todo <= buf->max_size);
206       read_len = cache_plugin_read (this_gen, buf->content, todo);
207       buf->size = read_len;
208     }
209   } else {
210     buf = this->main_input_plugin->read_block(this->main_input_plugin, fifo, todo);
211     this->read_call++;
212     this->main_read_call++;
213   }
214   return buf;
215 }
216 
cache_plugin_seek(input_plugin_t * this_gen,off_t offset,int origin)217 static off_t cache_plugin_seek(input_plugin_t *this_gen, off_t offset, int origin) {
218   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
219   off_t cur_pos;
220   off_t rel_offset;
221   off_t new_buf_pos;
222 
223   lprintf("offset: %"PRId64", origin: %d\n", offset, origin);
224   this->seek_call++;
225 
226   if( !this->buf_len ) {
227     cur_pos = this->main_input_plugin->seek(this->main_input_plugin, offset, origin);
228     this->main_seek_call++;
229   } else {
230     cur_pos = this->main_input_plugin->get_current_pos(this->main_input_plugin);
231     if( cur_pos >= (this->buf_len - this->buf_pos) )
232       cur_pos -= (this->buf_len - this->buf_pos);
233     else
234       cur_pos = 0;
235 
236     switch (origin) {
237     case SEEK_CUR:
238       rel_offset = offset;
239       break;
240 
241     case SEEK_SET:
242       rel_offset = offset - cur_pos;
243       break;
244 
245     default:
246       /* invalid origin - main input should know better */
247       cur_pos = this->main_input_plugin->seek(this->main_input_plugin, offset, origin);
248       this->buf_len = this->buf_pos = 0;
249       this->main_seek_call++;
250       return cur_pos;
251     }
252 
253     new_buf_pos = (off_t)this->buf_pos + rel_offset;
254     lprintf("buf_len: %d, rel_offset=%"PRId64", new_buf_pos=%"PRId64"\n",
255 	  this->buf_len, rel_offset, new_buf_pos);
256 
257     if ((new_buf_pos < 0) || (new_buf_pos >= this->buf_len)) {
258       if( origin == SEEK_SET )
259         cur_pos = this->main_input_plugin->seek(this->main_input_plugin, offset, origin);
260       else
261         cur_pos = this->main_input_plugin->seek(this->main_input_plugin,
262                   offset - (this->buf_len - this->buf_pos), origin);
263       this->buf_len = this->buf_pos = 0;
264       this->main_seek_call++;
265     } else {
266       this->buf_pos = (int)new_buf_pos;
267       cur_pos += rel_offset;
268     }
269   }
270   return cur_pos;
271 }
272 
cache_plugin_seek_time(input_plugin_t * this_gen,int time_offset,int origin)273 static off_t cache_plugin_seek_time(input_plugin_t *this_gen, int time_offset, int origin) {
274   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
275   off_t cur_pos;
276 
277   lprintf("time_offset: %d, origin: %d\n", time_offset, origin);
278   this->seek_call++;
279 
280   cur_pos = this->main_input_plugin->seek_time(this->main_input_plugin, time_offset, origin);
281   this->buf_len = this->buf_pos = 0;
282   this->main_seek_call++;
283   return cur_pos;
284 }
285 
cache_plugin_get_current_pos(input_plugin_t * this_gen)286 static off_t cache_plugin_get_current_pos(input_plugin_t *this_gen) {
287   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
288   off_t cur_pos;
289 
290   cur_pos = this->main_input_plugin->get_current_pos(this->main_input_plugin);
291   if( this->buf_len ) {
292     if( cur_pos >= (this->buf_len - this->buf_pos) )
293       cur_pos -= (this->buf_len - this->buf_pos);
294     else
295       cur_pos = 0;
296   }
297 
298   return cur_pos;
299 }
300 
cache_plugin_get_current_time(input_plugin_t * this_gen)301 static int cache_plugin_get_current_time(input_plugin_t *this_gen) {
302   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
303   int cur_time;
304 
305   cur_time = this->main_input_plugin->get_current_time(this->main_input_plugin);
306 
307   return cur_time;
308 }
309 
cache_plugin_get_length(input_plugin_t * this_gen)310 static off_t cache_plugin_get_length (input_plugin_t *this_gen) {
311   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
312 
313   return this->main_input_plugin->get_length(this->main_input_plugin);
314 }
315 
cache_plugin_get_blocksize(input_plugin_t * this_gen)316 static uint32_t cache_plugin_get_blocksize(input_plugin_t *this_gen) {
317   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
318 
319   return this->main_input_plugin->get_blocksize(this->main_input_plugin);
320 }
321 
cache_plugin_get_mrl(input_plugin_t * this_gen)322 static const char* cache_plugin_get_mrl (input_plugin_t *this_gen) {
323   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
324 
325   return this->main_input_plugin->get_mrl(this->main_input_plugin);
326 }
327 
328 /*
329  * dispose main input plugin and self
330  */
cache_plugin_dispose(input_plugin_t * this_gen)331 static void cache_plugin_dispose(input_plugin_t *this_gen) {
332   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
333 
334   lprintf("cache_plugin_dispose\n");
335 
336   xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
337 	  LOG_MODULE": read calls: %d, main input read calls: %d\n", this->read_call, this->main_read_call);
338   xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG,
339 	  LOG_MODULE": seek_calls: %d, main input seek calls: %d\n", this->seek_call, this->main_seek_call);
340 
341   if (this->is_clone)
342     this->main_input_plugin->dispose (this->main_input_plugin);
343   else
344     _x_free_input_plugin (this->stream, this->main_input_plugin);
345 
346   _x_freep(&this->buf);
347   free(this);
348 }
349 
350 
351 /*
352  * create self instance,
353  */
354 static int cache_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type);
355 
cache_plugin_new(xine_stream_t * stream,input_plugin_t * main_plugin)356 static input_plugin_t *cache_plugin_new (xine_stream_t *stream, input_plugin_t *main_plugin) {
357   cache_input_plugin_t *this;
358 
359   /* check given input plugin */
360   if (!main_plugin) {
361     xine_log(stream->xine, XINE_LOG_MSG, _(LOG_MODULE": input plugin not defined!\n"));
362     return NULL;
363   }
364 
365   lprintf("mrl: %s\n", main_plugin->get_mrl(main_plugin));
366 
367   this = calloc(1, sizeof(cache_input_plugin_t));
368   if (!this)
369     return NULL;
370 
371   this->main_input_plugin = main_plugin;
372   this->stream            = stream;
373 
374   this->input_plugin.open                = cache_plugin_open;
375   this->input_plugin.get_capabilities    = cache_plugin_get_capabilities;
376   this->input_plugin.read                = cache_plugin_read;
377   this->input_plugin.read_block          = cache_plugin_read_block;
378   this->input_plugin.seek                = cache_plugin_seek;
379   if(this->main_input_plugin->seek_time)
380     this->input_plugin.seek_time         = cache_plugin_seek_time;
381   this->input_plugin.get_current_pos     = cache_plugin_get_current_pos;
382   if(this->main_input_plugin->get_current_time)
383     this->input_plugin.get_current_time  = cache_plugin_get_current_time;
384   this->input_plugin.get_length          = cache_plugin_get_length;
385   this->input_plugin.get_blocksize       = cache_plugin_get_blocksize;
386   this->input_plugin.get_mrl             = cache_plugin_get_mrl;
387   this->input_plugin.get_optional_data   = cache_plugin_get_optional_data;
388   this->input_plugin.dispose             = cache_plugin_dispose;
389   this->input_plugin.input_class         = main_plugin->input_class;
390 
391   /* use main input block size */
392   this->buf_size = this->main_input_plugin->get_blocksize(this->main_input_plugin);
393   if (this->buf_size < DEFAULT_BUFFER_SIZE) {
394     this->buf_size = DEFAULT_BUFFER_SIZE;
395   }
396 
397   this->buf = calloc(1, this->buf_size);
398   if (!this->buf) {
399     free (this);
400     return NULL;
401   }
402 
403   return &this->input_plugin;
404 }
405 
_x_cache_plugin_get_instance(xine_stream_t * stream)406 input_plugin_t *_x_cache_plugin_get_instance (xine_stream_t *stream) {
407   input_plugin_t *main_plugin = stream->input_plugin;
408 
409   return cache_plugin_new (stream, main_plugin);
410 }
411 
cache_plugin_get_optional_data(input_plugin_t * this_gen,void * data,int data_type)412 static int cache_plugin_get_optional_data (input_plugin_t *this_gen,
413 					  void *data, int data_type) {
414   cache_input_plugin_t *this = (cache_input_plugin_t *)this_gen;
415 
416   if (data_type == INPUT_OPTIONAL_DATA_CLONE) {
417     input_plugin_t *new_main = NULL, *new_cache = NULL;
418     if (!data)
419       return INPUT_OPTIONAL_UNSUPPORTED;
420     if (!(this->main_input_plugin->get_capabilities (this->main_input_plugin) & INPUT_CAP_CLONE))
421       return INPUT_OPTIONAL_UNSUPPORTED;
422     if (this->main_input_plugin->get_optional_data (this->main_input_plugin,
423       &new_main, INPUT_OPTIONAL_DATA_CLONE) != INPUT_OPTIONAL_SUCCESS)
424       return INPUT_OPTIONAL_UNSUPPORTED;
425     if (!new_main)
426       return INPUT_OPTIONAL_UNSUPPORTED;
427     new_cache = cache_plugin_new (this->stream, new_main);
428     if (!new_cache) {
429       input_plugin_t **q = data;
430       *q = new_main;
431       return INPUT_OPTIONAL_SUCCESS;
432     }
433     this = (cache_input_plugin_t *)new_cache;
434     this->is_clone = 1;
435     {
436       input_plugin_t **q = data;
437       *q = new_cache;
438     }
439     return INPUT_OPTIONAL_SUCCESS;
440   }
441 
442   return this->main_input_plugin->get_optional_data(
443     this->main_input_plugin, data, data_type);
444 }
445