]> git.sesse.net Git - vlc/blob - modules/demux/image.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / modules / demux / image.c
1 /*****************************************************************************
2  * image.c: Image demuxer
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_demux.h>
35 #include <vlc_image.h>
36
37 /*****************************************************************************
38  * Module descriptor
39  *****************************************************************************/
40 static int  Open (vlc_object_t *);
41 static void Close(vlc_object_t *);
42
43 #define ID_TEXT N_("ES ID")
44 #define ID_LONGTEXT N_( \
45     "Set the ID of the elementary stream")
46
47 #define GROUP_TEXT N_("Group")
48 #define GROUP_LONGTEXT N_(\
49     "Set the group of the elementary stream")
50
51 #define DECODE_TEXT N_("Decode")
52 #define DECODE_LONGTEXT N_( \
53     "Decode at the demuxer stage")
54
55 #define CHROMA_TEXT N_("Forced chroma")
56 #define CHROMA_LONGTEXT N_( \
57     "If non empty and image-decode is true, the image will be " \
58     "converted to the specified chroma.")
59
60 #define DURATION_TEXT N_("Duration in second")
61 #define DURATION_LONGTEXT N_( \
62     "Duration in second before simulating an end of file. " \
63     "A negative value means an unlimited play time.")
64
65 #define FPS_TEXT N_("Frame rate")
66 #define FPS_LONGTEXT N_( \
67     "Frame rate of the elementary stream produced.")
68
69 #define RT_TEXT N_("Real-time")
70 #define RT_LONGTEXT N_( \
71     "Use real-time mode suitable for being used as a master input and " \
72     "real-time input slaves.")
73
74 vlc_module_begin()
75     set_description(N_("Image demuxer"))
76     set_category(CAT_INPUT)
77     set_subcategory(SUBCAT_INPUT_DEMUX)
78     add_integer("image-id", -1, NULL, ID_TEXT, ID_LONGTEXT, true)
79         change_safe()
80     add_integer("image-group", 0, NULL, GROUP_TEXT, GROUP_LONGTEXT, true)
81         change_safe()
82     add_bool("image-decode", true, NULL, DECODE_TEXT, DECODE_LONGTEXT, true)
83         change_safe()
84     add_string("image-chroma", "", NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
85         change_safe()
86     add_float("image-duration", 10, NULL, DURATION_TEXT, DURATION_LONGTEXT, true)
87         change_safe()
88     add_string("image-fps", "10/1", NULL, FPS_TEXT, FPS_LONGTEXT, true)
89         change_safe()
90     add_bool("image-realtime", false, NULL, RT_TEXT, RT_LONGTEXT, true)
91         change_safe()
92     set_capability("demux", 10)
93     set_callbacks(Open, Close)
94 vlc_module_end()
95
96 /*****************************************************************************
97  * Local prototypes
98  *****************************************************************************/
99 struct demux_sys_t
100 {
101     block_t     *data;
102     es_out_id_t *es;
103     mtime_t     duration;
104     bool        is_realtime;
105     mtime_t     pts_origin;
106     mtime_t     pts_next;
107         date_t          pts;
108 };
109
110 static block_t *Load(demux_t *demux)
111 {
112     const int     max_size = 4096 * 4096 * 8;
113     const int64_t size = stream_Size(demux->s);
114     if (size < 0 || size > max_size) {
115         msg_Err(demux, "Rejecting image based on its size (%"PRId64" > %d)", size, max_size);
116         return NULL;
117     }
118
119     if (size > 0)
120         return stream_Block(demux->s, size);
121     /* TODO */
122     return NULL;
123 }
124
125 static block_t *Decode(demux_t *demux,
126                        video_format_t *fmt, vlc_fourcc_t chroma, block_t *data)
127 {
128     image_handler_t *handler = image_HandlerCreate(demux);
129     if (!handler) {
130         block_Release(data);
131         return NULL;
132     }
133
134     video_format_t decoded;
135     video_format_Init(&decoded, chroma);
136
137     picture_t *image = image_Read(handler, data, fmt, &decoded);
138     image_HandlerDelete(handler);
139
140     if (!image)
141         return NULL;
142
143     video_format_Clean(fmt);
144     *fmt = decoded;
145
146     size_t size = 0;
147     for (int i = 0; i < image->i_planes; i++)
148         size += image->p[i].i_visible_pitch *
149                 image->p[i].i_visible_lines;
150
151     data = block_New(demux, size);
152     if (!data) {
153         picture_Release(image);
154         return NULL;
155     }
156
157     size_t offset = 0;
158     for (int i = 0; i < image->i_planes; i++) {
159         const plane_t *src = &image->p[i];
160         for (int y = 0; y < src->i_visible_lines; y++) {
161             memcpy(&data->p_buffer[offset],
162                    &src->p_pixels[y * src->i_pitch],
163                    src->i_visible_pitch);
164             offset += src->i_visible_pitch;
165         }
166     }
167
168     picture_Release(image);
169     return data;
170 }
171
172 static int Demux(demux_t *demux)
173 {
174     demux_sys_t *sys = demux->p_sys;
175
176     if (!sys->data)
177         return 0;
178
179     mtime_t deadline;
180     const mtime_t pts_first = sys->pts_origin + date_Get(&sys->pts);
181     if (sys->pts_next > VLC_TS_INVALID) {
182         deadline = sys->pts_next;
183     } else if (sys->is_realtime) {
184         deadline = mdate();
185         const mtime_t max_wait = CLOCK_FREQ / 50;
186         if (deadline + max_wait < pts_first) {
187             es_out_Control(demux->out, ES_OUT_SET_PCR, deadline);
188             /* That's ugly, but not yet easily fixable */
189             mwait(deadline + max_wait);
190             return 1;
191         }
192     } else {
193         deadline = 1 + pts_first;
194     }
195
196     for (;;) {
197         const mtime_t pts = sys->pts_origin + date_Get(&sys->pts);
198         if (sys->duration >= 0 && pts >= sys->pts_origin + sys->duration)
199             return 0;
200
201         if (pts >= deadline)
202             return 1;
203
204         block_t *data = block_Duplicate(sys->data);
205         if (!data)
206             return -1;
207
208         data->i_dts =
209         data->i_pts = VLC_TS_0 + pts;
210         es_out_Control(demux->out, ES_OUT_SET_PCR, data->i_pts);
211         es_out_Send(demux->out, sys->es, data);
212
213         date_Increment(&sys->pts, 1);
214     }
215 }
216
217 static int Control(demux_t *demux, int query, va_list args)
218 {
219     demux_sys_t *sys = demux->p_sys;
220
221         switch (query) {
222     case DEMUX_GET_POSITION: {
223         double *position = va_arg(args, double *);
224         if (sys->duration > 0)
225             *position = date_Get(&sys->pts) / (double)sys->duration;
226         else
227             *position = 0;
228         return VLC_SUCCESS;
229     }
230     case DEMUX_SET_POSITION: {
231         if (sys->duration < 0 || sys->is_realtime)
232             return VLC_EGENERIC;
233         double position = va_arg(args, double);
234         date_Set(&sys->pts, position * sys->duration);
235         return VLC_SUCCESS;
236     }
237     case DEMUX_GET_TIME: {
238         int64_t *time = va_arg(args, int64_t *);
239         *time = sys->pts_origin + date_Get(&sys->pts);
240         return VLC_SUCCESS;
241     }
242     case DEMUX_SET_TIME: {
243         if (sys->duration < 0 || sys->is_realtime)
244             return VLC_EGENERIC;
245         int64_t time = va_arg(args, int64_t);
246         date_Set(&sys->pts, __MIN(__MAX(time - sys->pts_origin, 0),
247                                   sys->duration));
248         return VLC_SUCCESS;
249     }
250     case DEMUX_SET_NEXT_DEMUX_TIME: {
251         int64_t pts_next = VLC_TS_0 + va_arg(args, int64_t);
252         if (sys->pts_next <= VLC_TS_INVALID)
253             sys->pts_origin = pts_next;
254         sys->pts_next = pts_next;
255         return VLC_SUCCESS;
256     }
257     case DEMUX_GET_LENGTH: {
258         int64_t *length = va_arg(args, int64_t *);
259         *length = __MAX(sys->duration, 0);
260         return VLC_SUCCESS;
261     }
262     case DEMUX_GET_FPS: {
263         double *fps = va_arg(args, double *);
264         *fps = (double)sys->pts.i_divider_num / sys->pts.i_divider_den;
265         return VLC_SUCCESS;
266     }
267     case DEMUX_GET_META:
268     case DEMUX_HAS_UNSUPPORTED_META:
269     case DEMUX_GET_ATTACHMENTS:
270         default:
271                 return VLC_EGENERIC;
272         }
273 }
274
275 static bool IsBmp(stream_t *s)
276 {
277     const uint8_t *header;
278     if (stream_Peek(s, &header, 18) < 18)
279         return false;
280     if (memcmp(header, "BM", 2) &&
281         memcmp(header, "BA", 2) &&
282         memcmp(header, "CI", 2) &&
283         memcmp(header, "CP", 2) &&
284         memcmp(header, "IC", 2) &&
285         memcmp(header, "PT", 2))
286         return false;
287     uint32_t file_size   = GetDWLE(&header[2]);
288     uint32_t data_offset = GetDWLE(&header[10]);
289     uint32_t header_size = GetDWLE(&header[14]);
290     if (file_size != 14 && file_size != 14 + header_size &&
291         file_size <= data_offset)
292         return false;
293     if (data_offset < header_size + 14)
294         return false;
295     if (header_size != 12 && header_size < 40)
296         return false;
297     return true;
298 }
299
300 static bool IsPcx(stream_t *s)
301 {
302     const uint8_t *header;
303     if (stream_Peek(s, &header, 66) < 66)
304         return false;
305     if (header[0] != 0x0A ||                        /* marker */
306         (header[1] != 0x00 && header[1] != 0x02 &&
307          header[1] != 0x03 && header[1] != 0x05) || /* version */
308         (header[2] != 0 && header[2] != 1) ||       /* encoding */
309         (header[3] != 1 && header[3] != 2 &&
310          header[3] != 4 && header[3] != 8) ||       /* bits per pixel per plane */
311         header[64] != 0 ||                          /* reserved */
312         header[65] == 0 || header[65] > 4)          /* plane count */
313         return false;
314     if (GetWLE(&header[4]) > GetWLE(&header[8]) ||  /* xmin vs xmax */
315         GetWLE(&header[6]) > GetWLE(&header[10]))   /* ymin vs ymax */
316         return false;
317     return true;
318 }
319
320 static bool IsLbm(stream_t *s)
321 {
322     const uint8_t *header;
323     if (stream_Peek(s, &header, 12) < 12)
324         return false;
325     if (memcmp(&header[0], "FORM", 4) ||
326         GetDWBE(&header[4]) <= 4 ||
327         (memcmp(&header[8], "ILBM", 4) && memcmp(&header[8], "PBM ", 4)))
328         return false;
329     return true;
330 }
331 static bool IsPnmBlank(uint8_t v)
332 {
333     return v == ' ' || v == '\t' || v == '\r' || v == '\n';
334 }
335 static bool IsPnm(stream_t *s)
336 {
337     const uint8_t *header;
338     int size = stream_Peek(s, &header, 256);
339     if (size < 3)
340         return false;
341     if (header[0] != 'P' ||
342         header[1] < '1' || header[1] > '6' ||
343         !IsPnmBlank(header[2]))
344         return false;
345
346     int number_count = 0;
347     for (int i = 3, parsing_number = 0; i < size && number_count < 2; i++) {
348         if (IsPnmBlank(header[i])) {
349             if (parsing_number) {
350                 parsing_number = 0;
351                 number_count++;
352             }
353         } else {
354             if (header[i] < '0' || header[i] > '9')
355                 break;
356             parsing_number = 1;
357         }
358     }
359     if (number_count < 2)
360         return false;
361     return true;
362 }
363
364 static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
365 {
366     for (int i = *position; i + 1 < size; i++) {
367         if (data[i + 0] != 0xff || data[i + 1] == 0x00)
368             return 0xff;
369         if (data[i + 1] != 0xff) {
370             *position = i + 2;
371             return data[i + 1];
372         }
373     }
374     return 0xff;
375 }
376 static bool IsJfif(stream_t *s)
377 {
378     const uint8_t *header;
379     int size = stream_Peek(s, &header, 256);
380     int position = 0;
381
382     if (FindJpegMarker(&position, header, size) != 0xd8)
383         return false;
384     if (FindJpegMarker(&position, header, size) != 0xe0)
385         return false;
386     position += 2;  /* Skip size */
387     if (position + 5 > size)
388         return false;
389     if (memcmp(&header[position], "JFIF\0", 5))
390         return false;
391     return true;
392 }
393
394 static bool IsSpiff(stream_t *s)
395 {
396     const uint8_t *header;
397     if (stream_Peek(s, &header, 36) < 36) /* SPIFF header size */
398         return false;
399     if (header[0] != 0xff || header[1] != 0xd8 ||
400         header[2] != 0xff || header[3] != 0xe8)
401         return false;
402     if (memcmp(&header[6], "SPIFF\0", 6))
403         return false;
404     return true;
405 }
406
407 typedef struct {
408     vlc_fourcc_t  codec;
409     int           marker_size;
410     const uint8_t marker[14];
411     bool          (*detect)(stream_t *s);
412 } image_format_t;
413
414 #define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
415 #define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
416 static const image_format_t formats[] = {
417     { .codec = VLC_CODEC_XCF,
418       .marker_size = 9 + 4 + 1,
419       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
420                   'f', 'i', 'l', 'e', '\0' }
421     },
422     { .codec = VLC_CODEC_XCF,
423       .marker_size = 9 + 4 + 1,
424       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
425                   'v', '0', '0', '1', '\0' }
426     },
427     { .codec = VLC_CODEC_XCF,
428       .marker_size = 9 + 4 + 1,
429       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
430                   'v', '0', '0', '2', '\0' }
431     },
432     { .codec = VLC_CODEC_PNG,
433       .marker_size = 8,
434       .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
435     },
436     { .codec = VLC_CODEC_GIF,
437       .marker_size = 6,
438       .marker = { 'G', 'I', 'F', '8', '7', 'a' }
439     },
440     { .codec = VLC_CODEC_GIF,
441       .marker_size = 6,
442       .marker = { 'G', 'I', 'F', '8', '9', 'a' }
443     },
444     /* XXX TIFF detection may be a bit weak */
445     { .codec = VLC_CODEC_TIFF,
446       .marker_size = 4,
447       .marker = { 'I', 'I', 0x2a, 0x00 },
448     },
449     { .codec = VLC_CODEC_TIFF,
450       .marker_size = 4,
451       .marker = { 'M', 'M', 0x00, 0x2a },
452     },
453     { .codec = VLC_CODEC_BMP,
454       .detect = IsBmp,
455     },
456     { .codec = VLC_CODEC_PCX,
457       .detect = IsPcx,
458     },
459     { .codec = VLC_CODEC_LBM,
460       .detect = IsLbm,
461     },
462     { .codec = VLC_CODEC_PNM,
463       .detect = IsPnm,
464     },
465     { .codec = VLC_CODEC_JPEG,
466       .detect = IsJfif,
467     },
468     { .codec = VLC_CODEC_JPEG,
469       .detect = IsSpiff,
470     },
471     { .codec = 0 }
472 };
473
474 static int Open(vlc_object_t *object)
475 {
476     demux_t *demux = (demux_t*)object;
477
478     /* Detect the image type */
479     const image_format_t *img;
480
481     const uint8_t *peek;
482     int peek_size = 0;
483     for (int i = 0; ; i++) {
484         img = &formats[i];
485         if (!img->codec)
486             return VLC_EGENERIC;
487
488         if (img->detect) {
489             if (img->detect(demux->s))
490                 break;
491         } else {
492             if (peek_size < img->marker_size)
493                 peek_size = stream_Peek(demux->s, &peek, img->marker_size);
494             if (peek_size >= img->marker_size &&
495                 !memcmp(peek, img->marker, img->marker_size))
496                 break;
497         }
498     }
499     msg_Dbg(demux, "Detected image: %s",
500             vlc_fourcc_GetDescription(VIDEO_ES, img->codec));
501
502     /* Load and if selected decode */
503     es_format_t fmt;
504     es_format_Init(&fmt, VIDEO_ES, img->codec);
505     fmt.video.i_chroma = fmt.i_codec;
506
507     block_t *data = Load(demux);
508     if (data && var_InheritBool(demux, "image-decode")) {
509         char *string = var_InheritString(demux, "image-chroma");
510         vlc_fourcc_t chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, string);
511         free(string);
512
513         data = Decode(demux, &fmt.video, chroma, data);
514         fmt.i_codec = fmt.video.i_chroma;
515     }
516     fmt.i_id    = var_InheritInteger(demux, "image-id");
517     fmt.i_group = var_InheritInteger(demux, "image-group");
518     if (var_InheritURational(demux,
519                              &fmt.video.i_frame_rate,
520                              &fmt.video.i_frame_rate_base,
521                              "image-fps") ||
522         fmt.video.i_frame_rate <= 0 || fmt.video.i_frame_rate_base <= 0) {
523         msg_Err(demux, "Invalid frame rate, using 10/1 instead");
524         fmt.video.i_frame_rate      = 10;
525         fmt.video.i_frame_rate_base = 1;
526     }
527
528     /* If loadind failed, we still continue to avoid mis-detection
529      * by other demuxers. */
530     if (!data)
531         msg_Err(demux, "Failed to load the image");
532
533     /* */
534     demux_sys_t *sys = malloc(sizeof(*sys));
535     if (!sys) {
536         if (data)
537             block_Release(data);
538         es_format_Clean(&fmt);
539         return VLC_ENOMEM;
540     }
541
542     sys->data        = data;
543     sys->es          = es_out_Add(demux->out, &fmt);
544     sys->duration    = CLOCK_FREQ * var_InheritFloat(demux, "image-duration");
545     sys->is_realtime = var_InheritBool(demux, "image-realtime");
546     sys->pts_origin  = sys->is_realtime ? mdate() : 0;
547     sys->pts_next    = VLC_TS_INVALID;
548     date_Init(&sys->pts, fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
549     date_Set(&sys->pts, 0);
550
551     es_format_Clean(&fmt);
552
553     demux->pf_demux   = Demux;
554     demux->pf_control = Control;
555     demux->p_sys      = sys;
556     return VLC_SUCCESS;
557 }
558
559 static void Close(vlc_object_t *object)
560 {
561     demux_t     *demux = (demux_t*)object;
562     demux_sys_t *sys   = demux->p_sys;
563
564     if (sys->data)
565         block_Release(sys->data);
566     free(sys);
567 }
568