59b8807ede6a161e18db245cb81ba8e671e201c9
[vlc.git] / modules / demux / image.c
1 /*****************************************************************************
2  * image.c: Image demuxer
3  *****************************************************************************
4  * Copyright (C) 2010 Laurent Aimar
5  *
6  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_demux.h>
36 #include <vlc_image.h>
37 #include "mxpeg_helper.h"
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
44
45 #define ID_TEXT N_("ES ID")
46 #define ID_LONGTEXT N_( \
47     "Set the ID of the elementary stream")
48
49 #define GROUP_TEXT N_("Group")
50 #define GROUP_LONGTEXT N_(\
51     "Set the group of the elementary stream")
52
53 #define DECODE_TEXT N_("Decode")
54 #define DECODE_LONGTEXT N_( \
55     "Decode at the demuxer stage")
56
57 #define CHROMA_TEXT N_("Forced chroma")
58 #define CHROMA_LONGTEXT N_( \
59     "If non empty and image-decode is true, the image will be " \
60     "converted to the specified chroma.")
61
62 #define DURATION_TEXT N_("Duration in seconds")
63 #define DURATION_LONGTEXT N_( \
64     "Duration in seconds before simulating an end of file. " \
65     "A negative value means an unlimited play time.")
66
67 #define FPS_TEXT N_("Frame rate")
68 #define FPS_LONGTEXT N_( \
69     "Frame rate of the elementary stream produced.")
70
71 #define RT_TEXT N_("Real-time")
72 #define RT_LONGTEXT N_( \
73     "Use real-time mode suitable for being used as a master input and " \
74     "real-time input slaves.")
75
76 vlc_module_begin()
77     set_description(N_("Image demuxer"))
78     set_shortname(N_("Image"))
79     set_category(CAT_INPUT)
80     set_subcategory(SUBCAT_INPUT_DEMUX)
81     add_integer("image-id", -1, ID_TEXT, ID_LONGTEXT, true)
82         change_safe()
83     add_integer("image-group", 0, GROUP_TEXT, GROUP_LONGTEXT, true)
84         change_safe()
85     add_bool("image-decode", true, DECODE_TEXT, DECODE_LONGTEXT, true)
86         change_safe()
87     add_string("image-chroma", "", CHROMA_TEXT, CHROMA_LONGTEXT, true)
88         change_safe()
89     add_float("image-duration", 10, DURATION_TEXT, DURATION_LONGTEXT, false)
90         change_safe()
91     add_string("image-fps", "10/1", FPS_TEXT, FPS_LONGTEXT, true)
92         change_safe()
93     add_bool("image-realtime", false, RT_TEXT, RT_LONGTEXT, true)
94         change_safe()
95     set_capability("demux", 10)
96     set_callbacks(Open, Close)
97 vlc_module_end()
98
99 /*****************************************************************************
100  * Local prototypes
101  *****************************************************************************/
102 typedef struct
103 {
104     block_t     *data;
105     es_out_id_t *es;
106     vlc_tick_t  duration;
107     bool        is_realtime;
108     vlc_tick_t  pts_offset;
109     vlc_tick_t  pts_next;
110     date_t        pts;
111 } demux_sys_t;
112
113 static block_t *Load(demux_t *demux)
114 {
115     const unsigned max_size = 4096 * 4096 * 8;
116     uint64_t size;
117
118     if (vlc_stream_GetSize(demux->s, &size) == VLC_SUCCESS) {
119         if (size > max_size) {
120             msg_Err(demux, "image too large (%"PRIu64" > %u), rejected",
121                     size, max_size);
122             return NULL;
123         }
124     } else
125         size = max_size;
126
127     block_t *block = block_Alloc(size);
128     if (block == NULL)
129         return NULL;
130
131     ssize_t val = vlc_stream_Read(demux->s, block->p_buffer, size);
132     if (val < 0) {
133         block_Release(block);
134         return NULL;
135     }
136
137     block->i_buffer = val;
138     return block;
139 }
140
141 static block_t *Decode(demux_t *demux,
142                        es_format_t *fmt, vlc_fourcc_t chroma, block_t *data)
143 {
144     image_handler_t *handler = image_HandlerCreate(demux);
145     if (!handler) {
146         block_Release(data);
147         return NULL;
148     }
149
150     video_format_t decoded;
151     video_format_Init(&decoded, chroma);
152
153     picture_t *image = image_Read(handler, data, fmt, &decoded);
154     image_HandlerDelete(handler);
155
156     if (!image)
157         return NULL;
158
159     es_format_Clean(fmt);
160     es_format_InitFromVideo(fmt, &decoded);
161     video_format_Clean(&decoded);
162
163     size_t size = 0;
164     for (int i = 0; i < image->i_planes; i++)
165         size += image->p[i].i_pitch * image->p[i].i_lines;
166
167     data = block_Alloc(size);
168     if (!data) {
169         picture_Release(image);
170         return NULL;
171     }
172
173     size_t offset = 0;
174     for (int i = 0; i < image->i_planes; i++) {
175         const plane_t *src = &image->p[i];
176         for (int y = 0; y < src->i_visible_lines; y++) {
177             memcpy(&data->p_buffer[offset],
178                    &src->p_pixels[y * src->i_pitch],
179                    src->i_visible_pitch);
180             offset += src->i_visible_pitch;
181         }
182     }
183
184     picture_Release(image);
185     return data;
186 }
187
188 static int Demux(demux_t *demux)
189 {
190     demux_sys_t *sys = demux->p_sys;
191
192     if (!sys->data)
193         return VLC_DEMUXER_EOF;
194
195     vlc_tick_t deadline;
196     const vlc_tick_t pts_first = sys->pts_offset + date_Get(&sys->pts);
197     if (sys->pts_next != VLC_TICK_INVALID) {
198         deadline = sys->pts_next;
199     } else if (sys->is_realtime) {
200         deadline = vlc_tick_now();
201         const vlc_tick_t max_wait = VLC_TICK_FROM_MS(20);
202         if (deadline + max_wait < pts_first) {
203             es_out_SetPCR(demux->out, deadline);
204             /* That's ugly, but not yet easily fixable */
205             vlc_tick_wait(deadline + max_wait);
206             return VLC_DEMUXER_SUCCESS;
207         }
208     } else {
209         deadline = 1 + pts_first;
210     }
211
212     for (;;) {
213         const vlc_tick_t pts = sys->pts_offset + date_Get(&sys->pts);
214         if (sys->duration >= 0 && pts >= VLC_TICK_0 + sys->pts_offset + sys->duration)
215             return VLC_DEMUXER_EOF;
216
217         if (pts >= deadline)
218             return VLC_DEMUXER_SUCCESS;
219
220         block_t *data = block_Duplicate(sys->data);
221         if (!data)
222             return VLC_DEMUXER_EGENERIC;
223
224         data->i_dts =
225         data->i_pts = VLC_TICK_0 + pts;
226         es_out_SetPCR(demux->out, data->i_pts);
227         if(sys->es)
228             es_out_Send(demux->out, sys->es, data);
229         else
230             block_Release(data);
231
232         date_Increment(&sys->pts, 1);
233     }
234 }
235
236 static int Control(demux_t *demux, int query, va_list args)
237 {
238     demux_sys_t *sys = demux->p_sys;
239
240     switch (query) {
241     case DEMUX_CAN_SEEK:
242         *va_arg(args, bool *) = sys->duration >= 0 && !sys->is_realtime;
243         return VLC_SUCCESS;
244     case DEMUX_GET_POSITION: {
245         double *position = va_arg(args, double *);
246         if (sys->duration > 0)
247             *position = date_Get(&sys->pts) / (double)sys->duration;
248         else
249             *position = 0;
250         return VLC_SUCCESS;
251     }
252     case DEMUX_SET_POSITION: {
253         if (sys->duration < 0 || sys->is_realtime)
254             return VLC_EGENERIC;
255         double position = va_arg(args, double);
256         date_Set(&sys->pts, position * sys->duration);
257         return VLC_SUCCESS;
258     }
259     case DEMUX_GET_TIME: {
260         *va_arg(args, vlc_tick_t *) = sys->pts_offset + date_Get(&sys->pts);
261         return VLC_SUCCESS;
262     }
263     case DEMUX_SET_TIME: {
264         if (sys->duration < 0 || sys->is_realtime)
265             return VLC_EGENERIC;
266         vlc_tick_t time = va_arg(args, vlc_tick_t);
267         date_Set(&sys->pts, VLC_CLIP(time - sys->pts_offset, VLC_TICK_0, sys->duration));
268         return VLC_SUCCESS;
269     }
270     case DEMUX_SET_NEXT_DEMUX_TIME: {
271         vlc_tick_t pts_next = VLC_TICK_0 + va_arg(args, vlc_tick_t);
272         if (sys->pts_next == VLC_TICK_INVALID)
273             sys->pts_offset = pts_next - VLC_TICK_0;
274         sys->pts_next = pts_next;
275         return VLC_SUCCESS;
276     }
277     case DEMUX_GET_LENGTH: {
278         *va_arg(args, vlc_tick_t *) = __MAX(sys->duration, 0);
279         return VLC_SUCCESS;
280     }
281     case DEMUX_GET_FPS: {
282         double *fps = va_arg(args, double *);
283         *fps = (double)sys->pts.i_divider_num / sys->pts.i_divider_den;
284         return VLC_SUCCESS;
285     }
286     case DEMUX_GET_META:
287     case DEMUX_HAS_UNSUPPORTED_META:
288     case DEMUX_GET_ATTACHMENTS:
289         return VLC_EGENERIC;
290
291     case DEMUX_CAN_PAUSE:
292     case DEMUX_SET_PAUSE_STATE:
293     case DEMUX_CAN_CONTROL_PACE:
294     case DEMUX_GET_PTS_DELAY:
295         return demux_vaControlHelper( demux->s, 0, -1, 0, 1, query, args );
296
297     default:
298         return VLC_EGENERIC;
299
300     }
301 }
302
303 static bool IsBmp(stream_t *s)
304 {
305     const uint8_t *header;
306     if (vlc_stream_Peek(s, &header, 18) < 18)
307         return false;
308     if (memcmp(header, "BM", 2) &&
309         memcmp(header, "BA", 2) &&
310         memcmp(header, "CI", 2) &&
311         memcmp(header, "CP", 2) &&
312         memcmp(header, "IC", 2) &&
313         memcmp(header, "PT", 2))
314         return false;
315     uint32_t file_size   = GetDWLE(&header[2]);
316     uint32_t data_offset = GetDWLE(&header[10]);
317     uint32_t header_size = GetDWLE(&header[14]);
318     if (file_size != 14 && file_size != 14 + header_size &&
319         file_size <= data_offset)
320         return false;
321     if (data_offset < header_size + 14)
322         return false;
323     if (header_size != 12 && header_size < 40)
324         return false;
325     return true;
326 }
327
328 static bool IsPcx(stream_t *s)
329 {
330     const uint8_t *header;
331     if (vlc_stream_Peek(s, &header, 66) < 66)
332         return false;
333     if (header[0] != 0x0A ||                        /* marker */
334         (header[1] != 0x00 && header[1] != 0x02 &&
335          header[1] != 0x03 && header[1] != 0x05) || /* version */
336         (header[2] != 0 && header[2] != 1) ||       /* encoding */
337         (header[3] != 1 && header[3] != 2 &&
338          header[3] != 4 && header[3] != 8) ||       /* bits per pixel per plane */
339         header[64] != 0 ||                          /* reserved */
340         header[65] == 0 || header[65] > 4)          /* plane count */
341         return false;
342     if (GetWLE(&header[4]) > GetWLE(&header[8]) ||  /* xmin vs xmax */
343         GetWLE(&header[6]) > GetWLE(&header[10]))   /* ymin vs ymax */
344         return false;
345     return true;
346 }
347
348 static bool IsLbm(stream_t *s)
349 {
350     const uint8_t *header;
351     if (vlc_stream_Peek(s, &header, 12) < 12)
352         return false;
353     if (memcmp(&header[0], "FORM", 4) ||
354         GetDWBE(&header[4]) <= 4 ||
355         (memcmp(&header[8], "ILBM", 4) && memcmp(&header[8], "PBM ", 4)))
356         return false;
357     return true;
358 }
359 static bool IsPnmBlank(uint8_t v)
360 {
361     return v == ' ' || v == '\t' || v == '\r' || v == '\n';
362 }
363 static bool IsPnm(stream_t *s)
364 {
365     const uint8_t *header;
366     int size = vlc_stream_Peek(s, &header, 256);
367     if (size < 3)
368         return false;
369     if (header[0] != 'P' ||
370         header[1] < '1' || header[1] > '6' ||
371         !IsPnmBlank(header[2]))
372         return false;
373
374     int number_count = 0;
375     for (int i = 3, parsing_number = 0; i < size && number_count < 2; i++) {
376         if (IsPnmBlank(header[i])) {
377             if (parsing_number) {
378                 parsing_number = 0;
379                 number_count++;
380             }
381         } else {
382             if (header[i] < '0' || header[i] > '9')
383                 break;
384             parsing_number = 1;
385         }
386     }
387     if (number_count < 2)
388         return false;
389     return true;
390 }
391
392 static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
393 {
394     for (int i = *position; i + 1 < size; i++) {
395         if (data[i + 0] != 0xff || data[i + 1] == 0x00)
396             return 0xff;
397         if (data[i + 1] != 0xff) {
398             *position = i + 2;
399             return data[i + 1];
400         }
401     }
402     return 0xff;
403 }
404 static bool IsJfif(stream_t *s)
405 {
406     const uint8_t *header;
407     int size = vlc_stream_Peek(s, &header, 256);
408     int position = 0;
409
410     if (FindJpegMarker(&position, header, size) != 0xd8)
411         return false;
412     if (FindJpegMarker(&position, header, size) != 0xe0)
413         return false;
414     position += 2;  /* Skip size */
415     if (position + 5 > size)
416         return false;
417     if (memcmp(&header[position], "JFIF\0", 5))
418         return false;
419     return true;
420 }
421
422 static bool IsWebP(stream_t *s)
423 {
424     const uint8_t *header;
425     if (vlc_stream_Peek(s, &header, 20) < 20) /* WebP header size */
426         return false;
427     if (memcmp(&header[0], "RIFF", 4))
428         return false;
429     /* TODO: support other chunk types */
430     if (memcmp(&header[8], "WEBPVP8 ", 8))
431         return false;
432     /* skip headers */
433     return vlc_stream_Seek(s, 20) == 0;
434 }
435
436 static bool IsSpiff(stream_t *s)
437 {
438     const uint8_t *header;
439     if (vlc_stream_Peek(s, &header, 36) < 36) /* SPIFF header size */
440         return false;
441     if (header[0] != 0xff || header[1] != 0xd8 ||
442         header[2] != 0xff || header[3] != 0xe8)
443         return false;
444     if (memcmp(&header[6], "SPIFF\0", 6))
445         return false;
446     return true;
447 }
448
449 static bool IsExif(stream_t *s)
450 {
451     const uint8_t *header;
452     ssize_t size = vlc_stream_Peek(s, &header, 256);
453     if (size == -1)
454         return false;
455     int position = 0;
456
457     if (FindJpegMarker(&position, header, size) != 0xd8)
458         return false;
459     if (FindJpegMarker(&position, header, size) != 0xe1)
460         return false;
461     position += 2;  /* Skip size */
462     if (position + 5 > size)
463         return false;
464     if (memcmp(&header[position], "Exif\0", 5))
465         return false;
466     return true;
467 }
468
469 static bool FindSVGmarker(int *position, const uint8_t *data, const int size, const char *marker)
470 {
471     for( int i = *position; i < size; i++)
472     {
473         if (memcmp(&data[i], marker, strlen(marker)) == 0)
474         {
475             *position = i;
476             return true;
477         }
478     }
479     return false;
480 }
481
482 static bool IsSVG(stream_t *s)
483 {
484     if (s->psz_url == NULL)
485         return false;
486
487     char *ext = strstr(s->psz_url, ".svg");
488     if (!ext) return false;
489
490     const uint8_t *header;
491     ssize_t size = vlc_stream_Peek(s, &header, 4096);
492     if (size == -1)
493         return false;
494     int position = 0;
495
496     const char xml[] = "<?xml version=\"";
497     if (!FindSVGmarker(&position, header, size, xml))
498         return false;
499     if (position != 0)
500         return false;
501
502     const char endxml[] = ">\0";
503     if (!FindSVGmarker(&position, header, size, endxml))
504         return false;
505     if (position <= 15)
506         return false;
507
508     const char svg[] = "<svg";
509     if (!FindSVGmarker(&position, header, size, svg))
510         return false;
511     if (position < 19)
512         return false;
513
514     /* SVG Scalable Vector Graphics image */
515
516     /* NOTE: some SVG images have the mimetype set in a meta data section
517      * and some do not */
518     return true;
519 }
520
521 static bool IsTarga(stream_t *s)
522 {
523     /* The header is not enough to ensure proper detection, we need
524      * to have a look at the footer. But doing so can be slow. So
525      * try to avoid it when possible */
526     const uint8_t *header;
527     if (vlc_stream_Peek(s, &header, 18) < 18)   /* Targa fixed header */
528         return false;
529     if (header[1] > 1)                      /* Color Map Type */
530         return false;
531     if ((header[1] != 0 || header[3 + 4] != 0) &&
532         header[3 + 4] != 8  &&
533         header[3 + 4] != 15 && header[3 + 4] != 16 &&
534         header[3 + 4] != 24 && header[3 + 4] != 32)
535         return false;
536     if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */
537         return false;
538     if (GetWLE(&header[8 + 4]) <= 0 ||      /* Width */
539         GetWLE(&header[8 + 6]) <= 0)        /* Height */
540         return false;
541     if (header[8 + 8] != 8  &&
542         header[8 + 8] != 15 && header[8 + 8] != 16 &&
543         header[8 + 8] != 24 && header[8 + 8] != 32)
544         return false;
545     if (header[8 + 9] & 0xc0)               /* Reserved bits */
546         return false;
547
548     const int64_t size = stream_Size(s);
549     if (size <= 18 + 26)
550         return false;
551     bool can_seek;
552     if (vlc_stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek)
553         return false;
554
555     const int64_t position = vlc_stream_Tell(s);
556     if (vlc_stream_Seek(s, size - 26))
557         return false;
558
559     const uint8_t *footer;
560     if (vlc_stream_Peek(s, &footer, 26) < 26
561      || memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18))
562         return false;
563
564     return vlc_stream_Seek(s, position) == 0;
565 }
566
567 typedef struct {
568     vlc_fourcc_t  codec;
569     size_t        marker_size;
570     const uint8_t marker[14];
571     bool          (*detect)(stream_t *s);
572 } image_format_t;
573
574 #define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
575 #define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
576 static const image_format_t formats[] = {
577     { .codec = VLC_CODEC_XCF,
578       .marker_size = 9 + 4 + 1,
579       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
580                   'f', 'i', 'l', 'e', '\0' }
581     },
582     { .codec = VLC_CODEC_XCF,
583       .marker_size = 9 + 4 + 1,
584       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
585                   'v', '0', '0', '1', '\0' }
586     },
587     { .codec = VLC_CODEC_XCF,
588       .marker_size = 9 + 4 + 1,
589       .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
590                   'v', '0', '0', '2', '\0' }
591     },
592     { .codec = VLC_CODEC_PNG,
593       .marker_size = 8,
594       .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
595     },
596     { .codec = VLC_CODEC_GIF,
597       .marker_size = 6,
598       .marker = { 'G', 'I', 'F', '8', '7', 'a' }
599     },
600     { .codec = VLC_CODEC_GIF,
601       .marker_size = 6,
602       .marker = { 'G', 'I', 'F', '8', '9', 'a' }
603     },
604     /* XXX TIFF detection may be a bit weak */
605     { .codec = VLC_CODEC_TIFF,
606       .marker_size = 4,
607       .marker = { 'I', 'I', 0x2a, 0x00 },
608     },
609     { .codec = VLC_CODEC_TIFF,
610       .marker_size = 4,
611       .marker = { 'M', 'M', 0x00, 0x2a },
612     },
613     { .codec = VLC_CODEC_BMP,
614       .detect = IsBmp,
615     },
616     { .codec = VLC_CODEC_PCX,
617       .detect = IsPcx,
618     },
619     { .codec = VLC_CODEC_LBM,
620       .detect = IsLbm,
621     },
622     { .codec = VLC_CODEC_PNM,
623       .detect = IsPnm,
624     },
625     { .codec = VLC_CODEC_MXPEG,
626       .detect = IsMxpeg,
627     },
628     { .codec = VLC_CODEC_JPEG,
629       .detect = IsJfif,
630     },
631     { .codec = VLC_CODEC_JPEG,
632       .detect = IsSpiff,
633     },
634     { .codec = VLC_CODEC_JPEG,
635       .detect = IsExif,
636     },
637     { .codec = VLC_CODEC_WEBP,
638       .detect = IsWebP,
639     },
640     { .codec = VLC_CODEC_BPG,
641       .marker_size = 4,
642       .marker = { 'B', 'P', 'G', 0xFB },
643     },
644     { .codec = VLC_CODEC_SVG,
645       .detect = IsSVG,
646     },
647     { .codec = VLC_CODEC_TARGA,
648       .detect = IsTarga,
649     },
650 };
651
652 static vlc_fourcc_t Detect(stream_t *s)
653 {
654     const uint8_t *peek;
655     size_t peek_size = 0;
656
657     for (size_t i = 0; i < ARRAY_SIZE(formats); i++) {
658         const image_format_t *img = &formats[i];
659
660         if (img->detect != NULL) {
661             if (img->detect(s))
662                 return img->codec;
663
664             if (vlc_stream_Seek(s, 0))
665                return 0;
666
667             /* Seeking invalidates the current peek buffer */
668             peek_size = 0;
669             continue;
670         }
671
672         if (peek_size < img->marker_size) {
673             ssize_t val = vlc_stream_Peek(s, &peek, img->marker_size);
674             if (val < 0)
675                 continue;
676             peek_size = val;
677         }
678
679         assert(img->marker_size > 0); /* ensure peek is a valid pointer */
680
681         if (peek_size >= img->marker_size
682          && memcmp(peek, img->marker, img->marker_size) == 0)
683             return img->codec;
684     }
685     return 0;
686 }
687
688 static int Open(vlc_object_t *object)
689 {
690     demux_t *demux = (demux_t*)object;
691
692     /* Detect the image type */
693     vlc_fourcc_t codec = Detect(demux->s);
694     if (codec == 0)
695         return VLC_EGENERIC;
696
697     msg_Dbg(demux, "Detected image: %s",
698             vlc_fourcc_GetDescription(VIDEO_ES, codec));
699
700     if (codec == VLC_CODEC_MXPEG)
701         return VLC_EGENERIC; //let avformat demux this file
702
703     /* Load and if selected decode */
704     es_format_t fmt;
705     es_format_Init(&fmt, VIDEO_ES, codec);
706     fmt.video.i_chroma = fmt.i_codec;
707
708     block_t *data = Load(demux);
709     if (data && var_InheritBool(demux, "image-decode")) {
710         char *string = var_InheritString(demux, "image-chroma");
711         vlc_fourcc_t chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, string);
712         free(string);
713
714         data = Decode(demux, &fmt, chroma, data);
715     }
716     fmt.i_id    = var_InheritInteger(demux, "image-id");
717     fmt.i_group = var_InheritInteger(demux, "image-group");
718     if (var_InheritURational(demux,
719                              &fmt.video.i_frame_rate,
720                              &fmt.video.i_frame_rate_base,
721                              "image-fps") ||
722         fmt.video.i_frame_rate <= 0 || fmt.video.i_frame_rate_base <= 0) {
723         msg_Err(demux, "Invalid frame rate, using 10/1 instead");
724         fmt.video.i_frame_rate      = 10;
725         fmt.video.i_frame_rate_base = 1;
726     }
727
728     /* If loadind failed, we still continue to avoid mis-detection
729      * by other demuxers. */
730     if (!data)
731         msg_Err(demux, "Failed to load the image");
732
733     /* */
734     demux_sys_t *sys = malloc(sizeof(*sys));
735     if (!sys) {
736         if (data)
737             block_Release(data);
738         es_format_Clean(&fmt);
739         return VLC_ENOMEM;
740     }
741
742     sys->data        = data;
743     sys->es          = es_out_Add(demux->out, &fmt);
744     sys->duration    = vlc_tick_from_sec( var_InheritFloat(demux, "image-duration") );
745     sys->is_realtime = var_InheritBool(demux, "image-realtime");
746     sys->pts_offset  = sys->is_realtime ? vlc_tick_now() : 0;
747     sys->pts_next    = VLC_TICK_INVALID;
748     date_Init(&sys->pts, fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
749     date_Set(&sys->pts, VLC_TICK_0);
750
751     es_format_Clean(&fmt);
752
753     demux->pf_demux   = Demux;
754     demux->pf_control = Control;
755     demux->p_sys      = sys;
756     return VLC_SUCCESS;
757 }
758
759 static void Close(vlc_object_t *object)
760 {
761     demux_t     *demux = (demux_t*)object;
762     demux_sys_t *sys   = demux->p_sys;
763
764     if (sys->data)
765         block_Release(sys->data);
766     free(sys);
767 }
768