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