1 /*
2  * Copyright (C) 2003-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 /*
22  * image dummy demultiplexer
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdlib.h>
34 
35 #define LOG_MODULE "demux_image"
36 #define LOG_VERBOSE
37 /*
38 #define LOG
39 */
40 
41 #include <xine/xine_internal.h>
42 #include <xine/xineutils.h>
43 #include "bswap.h"
44 #include <xine/demux.h>
45 
46 #define IMAGE_HEADER_LEN 4
47 
48 typedef struct demux_image_s {
49   demux_plugin_t        demux_plugin;
50 
51   xine_stream_t        *stream;
52   fifo_buffer_t        *video_fifo;
53   input_plugin_t       *input;
54   int                   status;
55   int                   buf_type;
56   int                   bytes_left;
57 } demux_image_t ;
58 
59 
_probe(xine_t * xine,const uint8_t * header)60 static uint32_t _probe (xine_t *xine, const uint8_t *header) {
61   if (memcmp (header, "GIF", 3) == 0) { /* GIF */
62     if (_x_decoder_available (xine, BUF_VIDEO_IMAGE))
63       return BUF_VIDEO_IMAGE;
64   } else if (memcmp (header, "BM", 2) == 0) { /* BMP */
65     if (_x_decoder_available (xine, BUF_VIDEO_IMAGE))
66       return BUF_VIDEO_IMAGE;
67   } else if (memcmp (header, "\x89PNG", 4) == 0) { /* PNG */
68     if (_x_decoder_available (xine, BUF_VIDEO_PNG))
69       return BUF_VIDEO_PNG;
70   } else if (memcmp (header, "\xff\xd8", 2) == 0) { /* JPEG */
71     if (_x_decoder_available (xine, BUF_VIDEO_JPEG))
72       return BUF_VIDEO_JPEG;
73   }
74   return 0;
75 }
76 
77 
demux_image_get_status(demux_plugin_t * this_gen)78 static int demux_image_get_status (demux_plugin_t *this_gen) {
79   demux_image_t *this = (demux_image_t *) this_gen;
80 
81   return this->status;
82 }
83 
demux_image_next(demux_plugin_t * this_gen,int decoder_flags)84 static int demux_image_next (demux_plugin_t *this_gen, int decoder_flags) {
85   demux_image_t *this = (demux_image_t *) this_gen;
86   buf_element_t *buf = this->video_fifo->buffer_pool_size_alloc (this->video_fifo, this->bytes_left);
87 
88   buf->content = buf->mem;
89   buf->decoder_flags = decoder_flags;
90 
91   buf->size = this->input->read (this->input, (char *)buf->mem, buf->max_size);
92 
93   this->bytes_left -= buf->size;
94   if (this->bytes_left < 0)
95     this->bytes_left = 0;
96 
97   if (buf->size <= 0) {
98     buf->size = 0;
99     buf->decoder_flags |= BUF_FLAG_FRAME_END;
100     this->status = DEMUX_FINISHED;
101   } else {
102     if (!this->buf_type) {
103       this->buf_type = _probe (this->stream->xine, buf->content);
104       if (!this->buf_type) {
105         /* allow forcing any file to generic image decoders */
106         this->buf_type = BUF_VIDEO_IMAGE;
107       }
108     }
109     this->status = DEMUX_OK;
110   }
111 
112   buf->type = this->buf_type;
113 
114   this->video_fifo->put (this->video_fifo, buf);
115 
116   return this->status;
117 }
118 
demux_image_send_chunk(demux_plugin_t * this_gen)119 static int demux_image_send_chunk (demux_plugin_t *this_gen) {
120   return demux_image_next(this_gen, 0);
121 }
122 
demux_image_send_headers(demux_plugin_t * this_gen)123 static void demux_image_send_headers (demux_plugin_t *this_gen) {
124   demux_image_t *this = (demux_image_t *) this_gen;
125 
126   this->video_fifo  = this->stream->video_fifo;
127 
128   _x_demux_control_start(this->stream);
129 
130   if (this->input->seek (this->input, 0, SEEK_SET) != 0) {
131     this->status = DEMUX_FINISHED;
132     return;
133   }
134   this->bytes_left = this->input->get_length (this->input);
135   if (this->bytes_left < 0)
136     this->bytes_left = 0;
137 
138   /* we can send everything here. this makes image decoder a lot easier */
139   while (demux_image_next(this_gen, BUF_FLAG_PREVIEW) == DEMUX_OK);
140 
141   this->status = DEMUX_OK;
142 
143   _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 1);
144   _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 0);
145 }
146 
demux_image_seek(demux_plugin_t * this_gen,off_t start_pos,int start_time,int playing)147 static int demux_image_seek (demux_plugin_t *this_gen,
148 			    off_t start_pos, int start_time, int playing) {
149 
150   demux_image_t *this = (demux_image_t *) this_gen;
151 
152   (void)start_pos;
153   (void)start_time;
154   (void)playing;
155   /* delay finished event for presentation mode.
156    * -1 => wait forever
157    * 0  => do not wait
158    * xx => wait xx/10 seconds
159    */
160   xine_set_param (this->stream, XINE_PARAM_DELAY_FINISHED_EVENT, -1);
161 
162   return this->status;
163 }
164 
demux_image_get_stream_length(demux_plugin_t * this_gen)165 static int demux_image_get_stream_length (demux_plugin_t *this_gen) {
166   /* demux_image_t *this = (demux_image_t *) this_gen;  */
167 
168   (void)this_gen;
169   return 0;
170 }
171 
demux_image_get_capabilities(demux_plugin_t * this_gen)172 static uint32_t demux_image_get_capabilities(demux_plugin_t *this_gen) {
173   (void)this_gen;
174   return DEMUX_CAP_NOCAP;
175 }
176 
demux_image_get_optional_data(demux_plugin_t * this_gen,void * data,int data_type)177 static int demux_image_get_optional_data(demux_plugin_t *this_gen,
178 					void *data, int data_type) {
179   (void)this_gen;
180   (void)data;
181   (void)data_type;
182   return DEMUX_OPTIONAL_UNSUPPORTED;
183 }
184 
open_plugin(demux_class_t * class_gen,xine_stream_t * stream,input_plugin_t * input)185 static demux_plugin_t *open_plugin (demux_class_t *class_gen,
186 				    xine_stream_t *stream,
187 				    input_plugin_t *input) {
188 
189   demux_image_t *this;
190   int buf_type = 0;
191 
192   switch (stream->content_detection_method) {
193 
194   case METHOD_BY_CONTENT: {
195     uint8_t header[IMAGE_HEADER_LEN];
196     if (_x_demux_read_header(input, header, IMAGE_HEADER_LEN) != IMAGE_HEADER_LEN) {
197       return NULL;
198     }
199     buf_type = _probe (stream->xine, header);
200     if (buf_type)
201       break;
202     return NULL;
203   }
204   break;
205 
206   case METHOD_BY_MRL:
207   case METHOD_EXPLICIT:
208   break;
209 
210   default:
211     return NULL;
212   }
213 
214   lprintf ("input accepted.\n");
215   /*
216    * if we reach this point, the input has been accepted.
217    */
218 
219   this = calloc(1, sizeof(demux_image_t));
220   if (!this)
221     return NULL;
222 
223   this->stream = stream;
224   this->input  = input;
225 
226   this->demux_plugin.send_headers      = demux_image_send_headers;
227   this->demux_plugin.send_chunk        = demux_image_send_chunk;
228   this->demux_plugin.seek              = demux_image_seek;
229   this->demux_plugin.dispose           = default_demux_plugin_dispose;
230   this->demux_plugin.get_status        = demux_image_get_status;
231   this->demux_plugin.get_stream_length = demux_image_get_stream_length;
232   this->demux_plugin.get_capabilities  = demux_image_get_capabilities;
233   this->demux_plugin.get_optional_data = demux_image_get_optional_data;
234   this->demux_plugin.demux_class       = class_gen;
235 
236   this->status = DEMUX_FINISHED;
237   this->buf_type = buf_type;
238 
239   lprintf("opened\n");
240   return &this->demux_plugin;
241 }
242 
243 /*
244  * image demuxer class
245  */
init_class(xine_t * xine,const void * data)246 static void *init_class (xine_t *xine, const void *data) {
247 
248   (void)xine;
249   (void)data;
250 
251   static const demux_class_t demux_image_class = {
252     .open_plugin     = open_plugin,
253     .description     = N_("image demux plugin"),
254     .identifier      = "imagedmx",
255     .mimetypes       = NULL,
256     /* NOTE: a leading, trailing, or double space would add the empty extension "" to the list. Avoid that.
257      * FIXME: this frozen at build time. */
258     .extensions      = ""
259 #if defined(HAVE_GDK_PIXBUF) || defined(HAVE_IMAGEMAGICK)
260         "bmp gif jpg jpeg png"
261 #else
262 #  if defined(HAVE_LIBJPEG)
263         "jpg jpeg"
264 #  endif
265 #  if defined(HAVE_LIBJPEG) && defined(HAVE_LIBPNG)
266         " "
267 #  endif
268 #  if defined(HAVE_LIBPNG)
269         "png"
270 #  endif
271 #endif
272     ,
273     .dispose         = NULL,
274   };
275 
276   return (void *)&demux_image_class;
277 }
278 
279 /*
280  * exported plugin catalog entry
281  */
282 static const demuxer_info_t demux_info_image = {
283   .priority = 11,
284 };
285 
286 const plugin_info_t xine_plugin_info[] EXPORTED = {
287   /* type, API, "name", version, special_info, init_function */
288   { PLUGIN_DEMUX, 27, "image", XINE_VERSION_CODE, &demux_info_image, init_class },
289   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
290 };
291 
292