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