1 /*
2  * Copyright (C) 2012-2019 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 
21 /* Torsten Jager <t.jager@gmx.de>
22    The idea is: present a virtual directory filled with images.
23    Create on demand in memory what the user actually tries to access. */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 
36 #define LOG_MODULE "input_test"
37 #define LOG_VERBOSE
38 /*
39 #define LOG
40 */
41 
42 #include <xine/xine_internal.h>
43 #include <xine/xineutils.h>
44 #include <xine/compat.h>
45 #include <xine/input_plugin.h>
46 #include <xine/video_out.h>
47 
48 #include "input_helper.h"
49 
50 /* describe tests here */
51 static const char * const test_names[] = {
52   "test://",
53   "test://color_circle.bmp",
54   "test://rgb_levels.bmp",
55   "test://saturation_levels.bmp",
56   "test://uv_square.bmp",
57   "test://y_resolution.bmp",
58   "test://color_circle.y4m",
59   "test://rgb_levels.y4m",
60   "test://saturation_levels.y4m",
61   "test://uv_square.y4m",
62   "test://y_resolution.y4m",
63   "test://rgb_levels_fullrange.y4m",
64   NULL
65 };
66 static const char test_type[]          = {2, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 2};
67 static const char test_is_yuv[]        = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1};
68 static const char test_is_mpeg_range[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0};
69 
70 #define TEST_FILES ((sizeof (test_names) / sizeof (char *)) - 1)
71 
72 typedef struct {
73   input_class_t     input_class;
74   xine_t           *xine;
75   xine_mrl_t       *mrls[TEST_FILES], m[TEST_FILES];
76 } test_input_class_t;
77 
78 typedef struct {
79   input_plugin_t    input_plugin;
80   xine_stream_t    *stream;
81 
82   unsigned char    *buf, *bmp_head, *y4m_head, *y4m_frame;
83   off_t             filesize, filepos, headsize, framesize;
84   int               bufsize, index;
85 } test_input_plugin_t;
86 
87 /* TJ. the generator code - actually a cut down version of my "testvideo" project */
88 /* It also reminisces good old Amiga coding style - strictly integer math, no     */
89 /* unnecessary memory allocations ...                                             */
90 
put32le(unsigned int v,unsigned char * p)91 static void put32le (unsigned int v, unsigned char *p) {
92   p[0] = v;
93   p[1] = v >> 8;
94   p[2] = v >> 16;
95   p[3] = v >> 24;
96 }
97 
98 /* square root */
isqr(unsigned int v)99 static unsigned int isqr (unsigned int v) {
100   unsigned int a, b = v, c = 0, e = 0;
101   if (v == 0) return (0);
102   while (b) {b >>= 2; c++;}
103   a = 1 << (c - 1);
104   c = 1 << c;
105   while (a + 1 < c) {
106     b = (a + c) >> 1;
107     e = b * b;
108     if (e <= v) a = b; else c = b;
109   }
110   return (a + (c * c - v < v - e ? 1 : 0));
111 }
112 
113 /* arcus tangens 0..24*255 */
iatan(int x,int y)114 static int iatan (int x, int y) {
115   int a, b, v = 0;
116   if ((x == 0) && (y == 0)) return (0);
117   /* mirror to first half quadrant */
118   if (y < 0) {v = -24 * 255; y = -y;}
119   if (x < 0) {v = -12 * 255 - v; x = -x;}
120   if (x < y) {v = -6 * 255 - v; a = x; b = y;} else {a = y; b = x;}
121   /* cubic interpolation within 32 bit */
122   v += (1027072 * a / b - 718 * a * a / b * 255 / b
123     - 237 * a * a / b * a / b * 255 / b) >> 10;
124   return (v < 0 ? -v : v);
125 }
126 
127 /* absolute angle difference 0..180*255 */
adiff(int a,int b)128 static int adiff (int a, int b) {
129   int d = a > b ? a - b : b - a;
130   return (d < 12 * 255 ? d : 24 * 255 - d);
131 }
132 
133 /* gimmick #2588: the XINE logo ;-) */
render_parallelogram(unsigned char * buf,int buf_width,int buf_height,unsigned int gray,int x,int y,int width,int height,int slant,int sc)134 static void render_parallelogram (unsigned char *buf, int buf_width, int buf_height, unsigned int gray,
135   int x, int y, int width, int height, int slant, int sc) {
136   int i, o;
137   int pitch = (3 * buf_width + 3) & ~3;
138   if (height < 2) return;
139   /* slant compensation */
140   if (sc) {
141     i = (width * slant + height / 2) / height;
142     width = isqr (width * width + i * i);
143   }
144   /* 3 bytes per pixel */
145   width *= 3;
146   /* OK now render */
147   height--;
148   for (i = 0; i <= height; i++) {
149     o = (buf_height - 1 - y - i) * pitch + 3 * (x + (slant * i + height / 2) / height);
150     memset (buf + o, gray, width);
151   }
152 }
153 
render_turn(unsigned char * buf,int buf_width,int buf_height,unsigned int gray,int x,int y,int size,int quad)154 static void render_turn (unsigned char *buf, int buf_width, int buf_height, unsigned int gray,
155   int x, int y, int size, int quad) {
156   int cx = quad & 1 ? 0 : size;
157   int cy = quad & 2 ? 0 : size;
158   int i, j, d, e;
159   int _min = size * size, _max = 4 * _min;
160   unsigned char *p;
161   int pitch = (3 * buf_width + 3) & ~3;
162   for (i = 0; i < size; i++) {
163     for (j = 0; j < size; j++) {
164       d = 2 * (i - cy) + 1;
165       d *= d;
166       e = 2 * (j - cx) + 1;
167       e *= e;
168       d += e;
169       if ((d < _min) || (d > _max)) continue;
170       p = buf + (buf_height - 1 - y - i) * pitch + 3 * (x + j);
171       *p++ = gray;
172       *p++ = gray;
173       *p = gray;
174     }
175   }
176 }
177 
render_xine_logo(unsigned char * buf,int buf_width,int buf_height,unsigned int gray)178 static void render_xine_logo (unsigned char *buf, int buf_width, int buf_height, unsigned int gray) {
179   int height = buf_height / 30 >= 10 ? buf_height / 30 : 10;
180   int width = height / 4;
181   int top = buf_height - 2 * height;
182   int left = buf_width - 4 * height - 3 * width;
183   /* safety */
184   if ((top < 0) || (left < 0)) return;
185   /* X */
186   render_parallelogram (buf, buf_width, buf_height, gray,
187     left, top, width, height, height - width, 1);
188   render_parallelogram (buf, buf_width, buf_height, gray,
189     left + height - width, top, width, height, width - height, 1);
190   left += height + width / 2;
191   /* I */
192   render_parallelogram (buf, buf_width, buf_height, gray,
193     left, top, width, height, 0, 0);
194   left += 3 * width / 2;
195   /* N */
196   render_parallelogram (buf, buf_width, buf_height, gray,
197     left, top, width, height, 0, 0);
198   render_parallelogram (buf, buf_width, buf_height, gray,
199     left + width, top, height - 3 * width, width, 0, 0);
200   render_turn (buf, buf_width, buf_height, gray,
201     left + height - 2 * width, top, 2 * width, 1);
202   render_parallelogram (buf, buf_width, buf_height, gray,
203     left + height - width, top + 2 * width, width, height - 2 * width, 0, 0);
204   left += height + width / 2;
205   /* E */
206   render_turn (buf, buf_width, buf_height, gray,
207     left, top, 2 * width, 0);
208   render_parallelogram (buf, buf_width, buf_height, gray,
209     left + 2 * width, top, height - 2 * width, width, 0, 0);
210   render_parallelogram (buf, buf_width, buf_height, gray,
211     left, top + 2 * width, width, height - 4 * width, 0, 0);
212   render_parallelogram (buf, buf_width, buf_height, gray,
213     left + width, top + (height - width) / 2, height - width, width, 0, 0);
214   render_turn (buf, buf_width, buf_height, gray,
215     left, top + height - 2 * width, 2 * width, 2);
216   render_parallelogram (buf, buf_width, buf_height, gray,
217     left + 2 * width, top + height - width, height - 2  * width, width, 0, 0);
218 }
219 
test_make(test_input_plugin_t * this)220 static int test_make (test_input_plugin_t * this) {
221   int width, height, x, y, cx, cy, d, r, dx, dy, a, red, green, blue;
222   int type, yuv, mpeg;
223   int pad, pitch;
224   int angle = 0, hdtv = 0, gray = 0;
225   unsigned char *p;
226 
227   /* mode */
228   type = test_type[this->index];
229   yuv  = test_is_yuv[this->index];
230   mpeg = test_is_mpeg_range[this->index];
231 
232   /* dimensions */
233   width = 320;
234   if (this->stream && this->stream->video_out) {
235     if (_x_lock_port_rewiring(this->stream->xine, 0)) {
236       if (this->stream->video_out) {
237         x = this->stream->video_out->get_property (this->stream->video_out,
238                                                    VO_PROP_WINDOW_WIDTH);
239         if (x > width) width = x;
240       }
241       _x_unlock_port_rewiring(this->stream->xine);
242     }
243   }
244   if (width > 1920) width = 1920;
245   width &= ~1;
246   height = width * 9 / 16;
247   height &= ~1;
248 
249   /* BMP rows must be n * 4 bytes long */
250   pitch = (width * 3 + 3) & ~3;
251   pad = pitch - width * 3;
252 
253   /* (re)allocate buffer */
254   a = 54 + pitch * height;
255   if (yuv) {
256     if (height >= 720) hdtv = 1;
257     a += 88 + width * height * 3 / 2;
258   }
259   if (this->buf && (a != this->bufsize)) {
260     free (this->buf);
261     this->buf = NULL;
262   }
263   if (!this->buf) {
264     this->buf = malloc (a);
265     if (!this->buf) return (1);
266     this->bufsize = a;
267   }
268 
269   /* make file heads */
270   p = this->buf;
271   this->bmp_head = p;
272   this->filesize = 54 + pitch * height;
273   if (yuv) {
274     p += 54 + pitch * height;
275     this->y4m_head = p;
276     /* use inofficial extension to announce color matrix here ;-) */
277     this->headsize = sprintf (p,
278       "YUV4MPEG2 W%d H%d F25:1 Ip A0:0 C420mpeg2 XYSCSS=420MPEG2 XXINE_CM=%d\n",
279       width, height, (hdtv ? 2 : 10) | !mpeg);
280     p += 82;
281     this->y4m_frame = p;
282     memcpy (p, "FRAME\n", 6);
283     this->framesize = 6 + width * height * 3 / 2;
284     this->filesize = this->headsize + 10 * 25 * this->framesize;
285   }
286   this->filepos = 0;
287   p = this->bmp_head;
288   memset (p, 0, 54);
289   p[0] = 'B';
290   p[1] = 'M';
291   put32le (54 + pitch * height, p + 2); /* file size */
292   put32le (54, p + 10); /* header size */
293   put32le (40, p + 14); /* ?? */
294   put32le (width, p + 18);
295   put32le (height, p + 22);
296   p[26] = 1; /* ?? */
297   p[28] = 24; /* depth */
298   put32le (pitch * height, p + 34); /* bitmap size */
299   put32le (2835, p + 38); /* ?? */
300   put32le (2835, p + 42); /* ?? */
301   p += 54;
302 
303   /* generate RGB image */
304   switch (type) {
305 
306     case 1:
307       /* color circle test */
308       cx = width >> 1;
309       cy = height >> 1;
310       r = width < height ? (width * 98) / 100 : (height * 98) / 100;
311       for (y = 0; y < height; y++) {
312         for (x = 0; x < width; x++) {
313           dx = ((x - cx) << 1) + 1;
314           dy = ((y - cy) << 1) + 1;
315           d = isqr (dx * dx + dy * dy);
316           if (d > r) red = green = blue = 128; else {
317             a = (iatan (dx, dy) + angle) % (24 * 255);
318             red = 8 * 255 - adiff (a, 0);
319             red = red < 0 ? 0 : (red > 4 * 255 ? 4 * 255 : red);
320             red = red * d / (4 * r);
321             green = 8 * 255 - adiff (a, 8 * 255);
322             green = green < 0 ? 0 : (green > 4 * 255 ? 4 * 255 : green);
323             green = green * d / (4 * r);
324             blue = 8 * 255 - adiff (a, 16 * 255);
325             blue = blue < 0 ? 0 : (blue > 4 * 255 ? 4 * 255 : blue);
326             blue = blue * d / (4 * r);
327           }
328           *p++ = blue;
329           *p++ = green;
330           *p++ = red;
331         }
332         for (x = pad; x; x--) *p++ = 0;
333       }
334     break;
335 
336     case 2:
337       /* sweep bars */
338       dx = (((width + 9) / 18) + 1) & ~1;
339       dy = (((height + 10) / 20) + 1) & ~1;
340       cx = (width / 2 - 8 * dx) & ~1;
341       cy = (height / 2 - 8 * dy) & ~1;
342       /* bottom gray */
343       d = cy * pitch;
344       memset (p, 127, d);
345       p += d;
346       /* color bars */
347       for (y = 0; y < 16; y++) {
348         /* make 1 line */
349         unsigned char *q = p;
350         for (x = 0; x < width; x++) {
351           d = x - cx;
352           if ((d < 0) || (d >= 16 * dx)) red = green = blue = 127;
353           else {
354             a = (y + 1) & 2 ? 17 * (15 - d / dx) : 255 - 16 * d / dx;
355             red = y & 4 ? a : 0;
356             green = y & 8 ? a : 0;
357             blue = y & 2 ? a : 0;
358           }
359           *p++ = blue;
360           *p++ = green;
361           *p++ = red;
362         }
363         for (x = pad; x; x--) *p++ = 0;
364         /* duplicate it further */
365         for (d = 1; d < dy; d++) {
366           memcpy (p, q, pitch);
367           p += pitch;
368         }
369       }
370       /* top gray */
371       memset (p, 127, (height - cy - 16 * dy) * pitch);
372     break;
373 
374     case 3: {
375       /* sweep bars, saturation */
376       int g[] = {0, 29, 76, 105, 150, 179, 226, 255, 0, 18, 54, 73, 182, 201, 237, 255};
377       dx = (((width + 9) / 18) + 1) & ~1;
378       dy = (((height + 10) / 20) + 1) & ~1;
379       cx = (width / 2 - 8 * dx) & ~1;
380       cy = (height / 2 - 8 * dy) & ~1;
381       /* bottom gray */
382       d = cy * pitch;
383       memset (p, 127, d);
384       p += d;
385       /* color bars */
386       for (y = 0; y < 16; y++) {
387         /* make 1 line */
388         unsigned char *q = p;
389         for (x = 0; x < width; x++) {
390           d = x - cx;
391           if ((d < 0) || (d >= 16 * dx)) red = green = blue = 127;
392           else {
393             a = (y + 1) & 2 ? 17 * (15 - d / dx) : 255 - 16 * d / dx;
394             r = (255 - a) * g[y / 2 + 8 * hdtv];
395             red = ((y & 4 ? 255 : 0) * a + r) / 255;
396             green = ((y & 8 ? 255 : 0) * a + r) / 255;
397             blue = ((y & 2 ? 255 : 0) * a + r) / 255;
398           }
399           *p++ = blue;
400           *p++ = green;
401           *p++ = red;
402         }
403         for (x = pad; x; x--) *p++ = 0;
404         /* duplicate it further */
405         for (d = 1; d < dy; d++) {
406           memcpy (p, q, pitch);
407           p += pitch;
408         }
409       }
410       /* top gray */
411       memset (p, 127, (height - cy - 16 * dy) * pitch);
412     } break;
413 
414     case 4: {
415       /* UV square */
416       int m1 = hdtv ?  51603 :  45941;
417       int m2 = hdtv ?  -6138 : -11277;
418       int m3 = hdtv ? -15339 : -23401;
419       int m4 = hdtv ?  60804 :  58065;
420       r = width < height ? width : height;
421       r = (49 << 9) * r / 100;
422       for (y = 0; y < height; y++) {
423         for (x = 0; x < width; x++) {
424           int min, max, u, v;
425           u = (x << 1) - width + 1;
426           v = (y << 1) - height + 1;
427           min = max = red = m1 * v;
428           green = m2 * u + m3 * v;
429           if (green < min) min = green; else if (green > max) max = green;
430           blue = m4 * u;
431           if (blue < min) min = blue; else if (blue > max) max = blue;
432           d = (256 * r + (r >> 1)) + min - max - 1;
433           if (d < 0) red = green = blue = 127;
434           else {
435             if (gray == 255) min -= d - (r >> 1);
436             else min -= d / 255 * gray - (r >> 1);
437             red = (red - min) / r;
438             green = (green - min) / r;
439             blue = (blue - min) / r;
440           }
441           *p++ = blue;
442           *p++ = green;
443           *p++ = red;
444         }
445         for (x = pad; x; x--) *p++ = 0;
446       }
447     } break;
448 
449     case 5:
450       /* resolution pattern */
451       dx = (width / 10);
452       dy = (height / 7);
453       for (y = 0; y < height; y++) {
454         for (x = 0; x < width; x++) {
455           if ((x < dx) || (x >= 9 * dx)) red = 127;
456           else {
457             d = x / dx;
458             switch (y / dy) {
459               case 1: red = ((x / d) ^ (y / d)) & 1 ? 0 : 255; break;
460               case 3: red = (x / d) & 1 ? 0 : 255; break;
461               case 5: red = (y / d) & 1 ? 0 : 255; break;
462               default: red = 127;
463             }
464           }
465           *p++ = red;
466           *p++ = red;
467           *p++ = red;
468         }
469         for (x = pad; x; x--) *p++ = 0;
470       }
471     break;
472   }
473 
474   /* add logo */
475   render_xine_logo (this->bmp_head + 54, width, height, 150);
476 
477   /* convert to YUV */
478   if (yuv) {
479     int fb, fr, yb, yr, yg, yo, ubvr, vb, ur, ug, vg;
480     int i, _yb[256], _yr[256], _yg[256];
481     int _ubvr[1024], _vb[1024], _ur[1024], _ug[1024], _vg[1024];
482     unsigned char *p2, *q, *q2;
483     #define SSHIFT 17
484     #define SFACTOR (1 << SSHIFT)
485     fb = hdtv ? 722 : 1140;
486     fr = hdtv ? 2126 : 2990;
487     if (mpeg) {
488       yg = (SFACTOR * 219 + 127) / 255;
489       yo = SFACTOR * 16 + SFACTOR / 2;
490       ubvr = (SFACTOR * 112 + 127) / 255;
491     } else {
492       yg = SFACTOR;
493       yo = SFACTOR / 2;
494       ubvr = (SFACTOR * 127 + 127) / 255;
495     }
496     yb = (yg * fb + 5000) / 10000;
497     yr = (yg * fr + 5000) / 10000;
498     yg -= yb + yr;
499     for (i = 0; i < 256; i++) {
500       _yb[i] = yb * i;
501       _yr[i] = yr * i;
502       _yg[i] = yg * i + yo;
503     }
504     ur = (ubvr * fr + fb / 2 - 5000) / (fb - 10000);
505     ug = -ur - ubvr;
506     vb = (ubvr * fb + fr / 2 - 5000) / (fr - 10000);
507     vg = -vb - ubvr;
508     for (i = 0; i < 1024; i++) {
509       _ubvr[i] = ubvr * i + 4 * (SFACTOR * 128 + SFACTOR / 2);
510       _ur[i] = ur * i;
511       _ug[i] = ug * i;
512       _vb[i] = vb * i;
513       _vg[i] = vg * i;
514     }
515     q = this->y4m_frame + 6;
516     for (y = height - 1; y >= 0; y--) {
517       p = this->bmp_head + 54 + y * pitch;
518       for (x = width; x; x--) {
519         *q++ = (_yb[p[0]] + _yg[p[1]] + _yr[p[2]]) >> SSHIFT;
520         p += 3;
521       }
522     }
523     q2 = q + width * height / 4;
524     for (y = height - 2; y >= 0; y -= 2) {
525       p = this->bmp_head + 54 + y * pitch;
526       p2 = p + pitch;
527       for (x = width / 2; x; x--) {
528         blue  = (unsigned int)*p++ + *p2++;
529         green = (unsigned int)*p++ + *p2++;
530         red   = (unsigned int)*p++ + *p2++;
531         blue  += (unsigned int)*p++ + *p2++;
532         green += (unsigned int)*p++ + *p2++;
533         red   += (unsigned int)*p++ + *p2++;
534         *q++  = (_ubvr[blue] + _ug[green] + _ur[red]) >> (SSHIFT + 2);
535         *q2++ = (_ubvr[red] + _vg[green] + _vb[blue]) >> (SSHIFT + 2);
536       }
537     }
538   }
539 
540   /* add readable title */
541   {
542     char *test_titles[5] = {
543       _("Colour Circle"),
544       _("RGB Levels"),
545       _("Saturation Levels"),
546       _("UV Square"),
547       _("Luminance Resolution")
548     };
549     char *temp;
550 
551     temp = _x_asprintf ("%s [%s%s]", test_titles[type - 1],
552       yuv ? (mpeg ? "" : "full swing ") : "",
553       yuv ? (hdtv ? "ITU-R 709 YUV" : " ITU-R 601 YUV") : "RGB");
554     _x_meta_info_set (this->stream, XINE_META_INFO_TITLE, temp);
555     free(temp);
556   }
557 
558   return (1);
559 }
560 
561 /* instance functions */
562 
test_plugin_read(input_plugin_t * this_gen,void * buf,off_t len)563 static off_t test_plugin_read (input_plugin_t *this_gen, void *buf, off_t len) {
564   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
565 
566   if (!this->buf || (len < 0) || !buf) return -1;
567   if (len > this->filesize - this->filepos) len = this->filesize - this->filepos;
568 
569   if (test_is_yuv[this->index]) {
570     unsigned char *p = this->y4m_frame, *q = buf;
571     off_t l = len, d;
572     d = this->headsize - this->filepos;
573     if (d > 0) {
574       xine_fast_memcpy (q, this->y4m_head + this->filepos, d);
575       q += d;
576       this->filepos += d;
577       l -= d;
578       d = this->framesize;
579     } else {
580       d = (this->filepos - this->headsize) % this->framesize;
581       p += d;
582       d = this->framesize - d;
583     }
584     while (l > 0) {
585       if (d > l) d = l;
586       xine_fast_memcpy (q, p, d);
587       p = this->y4m_frame;
588       q += d;
589       this->filepos += d;
590       l -= d;
591       d = this->framesize;
592     }
593   } else {
594     xine_fast_memcpy (buf, this->bmp_head + this->filepos, len);
595     this->filepos += len;
596   }
597   return len;
598 }
599 
test_plugin_seek(input_plugin_t * this_gen,off_t offset,int origin)600 static off_t test_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
601   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
602 
603   offset = _x_input_translate_seek(offset, origin, this->filepos, this->filesize);
604 
605   if (offset >= 0) {
606     this->filepos = offset;
607   }
608 
609   return offset;
610 }
611 
test_plugin_get_current_pos(input_plugin_t * this_gen)612 static off_t test_plugin_get_current_pos (input_plugin_t *this_gen) {
613   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
614 
615   return this->filepos;
616 }
617 
test_plugin_get_length(input_plugin_t * this_gen)618 static off_t test_plugin_get_length (input_plugin_t *this_gen) {
619   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
620 
621   return this->filesize;
622 }
623 
test_plugin_get_mrl(input_plugin_t * this_gen)624 static const char *test_plugin_get_mrl (input_plugin_t *this_gen) {
625   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
626 
627   return test_names[this->index];
628 }
629 
test_plugin_dispose(input_plugin_t * this_gen)630 static void test_plugin_dispose (input_plugin_t *this_gen ) {
631   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
632 
633   if (this->buf) free (this->buf);
634   free (this);
635 }
636 
test_plugin_open(input_plugin_t * this_gen)637 static int test_plugin_open (input_plugin_t *this_gen ) {
638   test_input_plugin_t *this = (test_input_plugin_t *) this_gen;
639 
640   return test_make (this);
641 }
642 
test_class_get_instance(input_class_t * cls_gen,xine_stream_t * stream,const char * data)643 static input_plugin_t *test_class_get_instance (input_class_t *cls_gen,
644   xine_stream_t *stream, const char *data) {
645   test_input_plugin_t *this;
646   unsigned int i;
647 
648   for (i = 0; i < TEST_FILES; i++) {
649     if (!strcasecmp (data, test_names[i])) break;
650   }
651   if (i == TEST_FILES) return NULL;
652 
653   this = (test_input_plugin_t *) calloc(1, sizeof (test_input_plugin_t));
654   if (!this)
655     return NULL;
656 
657   this->stream = stream;
658   this->index = i;
659 
660   this->input_plugin.open               = test_plugin_open;
661   this->input_plugin.get_capabilities   = _x_input_get_capabilities_seekable;
662   this->input_plugin.read               = test_plugin_read;
663   this->input_plugin.read_block         = _x_input_default_read_block;
664   this->input_plugin.seek               = test_plugin_seek;
665   this->input_plugin.get_current_pos    = test_plugin_get_current_pos;
666   this->input_plugin.get_length         = test_plugin_get_length;
667   this->input_plugin.get_blocksize      = _x_input_default_get_blocksize;
668   this->input_plugin.get_mrl            = test_plugin_get_mrl;
669   this->input_plugin.get_optional_data  = _x_input_default_get_optional_data;
670   this->input_plugin.dispose            = test_plugin_dispose;
671   this->input_plugin.input_class        = cls_gen;
672 
673   return &this->input_plugin;
674 }
675 
676 
677 /*
678  * plugin class functions
679  */
680 
test_get_autoplay_list(input_class_t * this_gen,int * num_files)681 static const char * const * test_get_autoplay_list (input_class_t *this_gen, int *num_files) {
682   (void)this_gen;
683   if (num_files) *num_files = TEST_FILES - 1;
684   return test_names + 1;
685 }
686 
test_class_get_dir(input_class_t * this_gen,const char * filename,int * nFiles)687 static xine_mrl_t **test_class_get_dir (input_class_t *this_gen, const char *filename,
688   int *nFiles) {
689   test_input_class_t *this = (test_input_class_t *) this_gen;
690   unsigned int i;
691   xine_mrl_t *m;
692 
693   (void)filename;
694   if (!this->mrls[0]) {
695     for (i = 0; i < TEST_FILES - 1; i++) {
696       m = &this->m[i];
697       this->mrls[i] = m;
698 
699       m->origin = (char *)test_names[0];
700       m->mrl = (char *)test_names[i + 1];
701       m->link = NULL;
702       m->type = mrl_file | mrl_file_normal;
703       m->size = 54 + 1024 * 576 * 3; /* for true size, call test_plugin_get_length () */
704     }
705 
706     this->mrls[i] = NULL;
707   }
708 
709   if (nFiles) *nFiles = TEST_FILES - 1;
710 
711   return this->mrls;
712 }
713 
test_class_dispose(input_class_t * this_gen)714 static void test_class_dispose (input_class_t *this_gen) {
715   free (this_gen);
716 }
717 
test_init_plugin(xine_t * xine,const void * data)718 static void *test_init_plugin (xine_t *xine, const void *data) {
719   test_input_class_t *this;
720 
721   (void)data;
722   this = (test_input_class_t *) calloc(1, sizeof (test_input_class_t));
723   if (!this)
724     return NULL;
725 
726   this->xine   = xine;
727 
728   this->input_class.get_instance       = test_class_get_instance;
729   this->input_class.identifier         = "test";
730   this->input_class.description        = N_("test card input plugin");
731   this->input_class.get_dir            = test_class_get_dir;
732   this->input_class.get_autoplay_list  = test_get_autoplay_list;
733   this->input_class.dispose            = test_class_dispose;
734   this->input_class.eject_media        = NULL;
735 
736   return this;
737 }
738 
739 static const input_info_t input_info_test = {
740   .priority = 110
741 };
742 
743 /*
744  * exported plugin catalog entry
745  */
746 
747 #define INPUT_TEST_CATALOG  { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 18, "TEST", XINE_VERSION_CODE, &input_info_test, test_init_plugin }
748 
749 #ifndef XINE_MAKE_BUILTINS
750 const plugin_info_t xine_plugin_info[] EXPORTED = {
751   /* type, API, "name", version, special_info, init_function */
752   INPUT_TEST_CATALOG,
753   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
754 };
755 #endif
756 
757