qt: playlist: use item title if available
[vlc.git] / modules / demux / image.c
blob59b8807ede6a161e18db245cb81ba8e671e201c9
1 /*****************************************************************************
2 * image.c: Image demuxer
3 *****************************************************************************
4 * Copyright (C) 2010 Laurent Aimar
6 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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.
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.
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 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <assert.h>
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"
39 /*****************************************************************************
40 * Module descriptor
41 *****************************************************************************/
42 static int Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
45 #define ID_TEXT N_("ES ID")
46 #define ID_LONGTEXT N_( \
47 "Set the ID of the elementary stream")
49 #define GROUP_TEXT N_("Group")
50 #define GROUP_LONGTEXT N_(\
51 "Set the group of the elementary stream")
53 #define DECODE_TEXT N_("Decode")
54 #define DECODE_LONGTEXT N_( \
55 "Decode at the demuxer stage")
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.")
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.")
67 #define FPS_TEXT N_("Frame rate")
68 #define FPS_LONGTEXT N_( \
69 "Frame rate of the elementary stream produced.")
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.")
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()
99 /*****************************************************************************
100 * Local prototypes
101 *****************************************************************************/
102 typedef struct
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;
113 static block_t *Load(demux_t *demux)
115 const unsigned max_size = 4096 * 4096 * 8;
116 uint64_t size;
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;
124 } else
125 size = max_size;
127 block_t *block = block_Alloc(size);
128 if (block == NULL)
129 return NULL;
131 ssize_t val = vlc_stream_Read(demux->s, block->p_buffer, size);
132 if (val < 0) {
133 block_Release(block);
134 return NULL;
137 block->i_buffer = val;
138 return block;
141 static block_t *Decode(demux_t *demux,
142 es_format_t *fmt, vlc_fourcc_t chroma, block_t *data)
144 image_handler_t *handler = image_HandlerCreate(demux);
145 if (!handler) {
146 block_Release(data);
147 return NULL;
150 video_format_t decoded;
151 video_format_Init(&decoded, chroma);
153 picture_t *image = image_Read(handler, data, fmt, &decoded);
154 image_HandlerDelete(handler);
156 if (!image)
157 return NULL;
159 es_format_Clean(fmt);
160 es_format_InitFromVideo(fmt, &decoded);
161 video_format_Clean(&decoded);
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;
167 data = block_Alloc(size);
168 if (!data) {
169 picture_Release(image);
170 return NULL;
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;
184 picture_Release(image);
185 return data;
188 static int Demux(demux_t *demux)
190 demux_sys_t *sys = demux->p_sys;
192 if (!sys->data)
193 return VLC_DEMUXER_EOF;
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;
208 } else {
209 deadline = 1 + pts_first;
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;
217 if (pts >= deadline)
218 return VLC_DEMUXER_SUCCESS;
220 block_t *data = block_Duplicate(sys->data);
221 if (!data)
222 return VLC_DEMUXER_EGENERIC;
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);
232 date_Increment(&sys->pts, 1);
236 static int Control(demux_t *demux, int query, va_list args)
238 demux_sys_t *sys = demux->p_sys;
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;
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;
259 case DEMUX_GET_TIME: {
260 *va_arg(args, vlc_tick_t *) = sys->pts_offset + date_Get(&sys->pts);
261 return VLC_SUCCESS;
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;
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;
277 case DEMUX_GET_LENGTH: {
278 *va_arg(args, vlc_tick_t *) = __MAX(sys->duration, 0);
279 return VLC_SUCCESS;
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;
286 case DEMUX_GET_META:
287 case DEMUX_HAS_UNSUPPORTED_META:
288 case DEMUX_GET_ATTACHMENTS:
289 return VLC_EGENERIC;
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 );
297 default:
298 return VLC_EGENERIC;
303 static bool IsBmp(stream_t *s)
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;
328 static bool IsPcx(stream_t *s)
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;
348 static bool IsLbm(stream_t *s)
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;
359 static bool IsPnmBlank(uint8_t v)
361 return v == ' ' || v == '\t' || v == '\r' || v == '\n';
363 static bool IsPnm(stream_t *s)
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;
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++;
381 } else {
382 if (header[i] < '0' || header[i] > '9')
383 break;
384 parsing_number = 1;
387 if (number_count < 2)
388 return false;
389 return true;
392 static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
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];
402 return 0xff;
404 static bool IsJfif(stream_t *s)
406 const uint8_t *header;
407 int size = vlc_stream_Peek(s, &header, 256);
408 int position = 0;
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;
422 static bool IsWebP(stream_t *s)
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;
436 static bool IsSpiff(stream_t *s)
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;
449 static bool IsExif(stream_t *s)
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;
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;
469 static bool FindSVGmarker(int *position, const uint8_t *data, const int size, const char *marker)
471 for( int i = *position; i < size; i++)
473 if (memcmp(&data[i], marker, strlen(marker)) == 0)
475 *position = i;
476 return true;
479 return false;
482 static bool IsSVG(stream_t *s)
484 if (s->psz_url == NULL)
485 return false;
487 char *ext = strstr(s->psz_url, ".svg");
488 if (!ext) return false;
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;
496 const char xml[] = "<?xml version=\"";
497 if (!FindSVGmarker(&position, header, size, xml))
498 return false;
499 if (position != 0)
500 return false;
502 const char endxml[] = ">\0";
503 if (!FindSVGmarker(&position, header, size, endxml))
504 return false;
505 if (position <= 15)
506 return false;
508 const char svg[] = "<svg";
509 if (!FindSVGmarker(&position, header, size, svg))
510 return false;
511 if (position < 19)
512 return false;
514 /* SVG Scalable Vector Graphics image */
516 /* NOTE: some SVG images have the mimetype set in a meta data section
517 * and some do not */
518 return true;
521 static bool IsTarga(stream_t *s)
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;
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;
555 const int64_t position = vlc_stream_Tell(s);
556 if (vlc_stream_Seek(s, size - 26))
557 return false;
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;
564 return vlc_stream_Seek(s, position) == 0;
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;
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' }
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' }
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' }
592 { .codec = VLC_CODEC_PNG,
593 .marker_size = 8,
594 .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
596 { .codec = VLC_CODEC_GIF,
597 .marker_size = 6,
598 .marker = { 'G', 'I', 'F', '8', '7', 'a' }
600 { .codec = VLC_CODEC_GIF,
601 .marker_size = 6,
602 .marker = { 'G', 'I', 'F', '8', '9', 'a' }
604 /* XXX TIFF detection may be a bit weak */
605 { .codec = VLC_CODEC_TIFF,
606 .marker_size = 4,
607 .marker = { 'I', 'I', 0x2a, 0x00 },
609 { .codec = VLC_CODEC_TIFF,
610 .marker_size = 4,
611 .marker = { 'M', 'M', 0x00, 0x2a },
613 { .codec = VLC_CODEC_BMP,
614 .detect = IsBmp,
616 { .codec = VLC_CODEC_PCX,
617 .detect = IsPcx,
619 { .codec = VLC_CODEC_LBM,
620 .detect = IsLbm,
622 { .codec = VLC_CODEC_PNM,
623 .detect = IsPnm,
625 { .codec = VLC_CODEC_MXPEG,
626 .detect = IsMxpeg,
628 { .codec = VLC_CODEC_JPEG,
629 .detect = IsJfif,
631 { .codec = VLC_CODEC_JPEG,
632 .detect = IsSpiff,
634 { .codec = VLC_CODEC_JPEG,
635 .detect = IsExif,
637 { .codec = VLC_CODEC_WEBP,
638 .detect = IsWebP,
640 { .codec = VLC_CODEC_BPG,
641 .marker_size = 4,
642 .marker = { 'B', 'P', 'G', 0xFB },
644 { .codec = VLC_CODEC_SVG,
645 .detect = IsSVG,
647 { .codec = VLC_CODEC_TARGA,
648 .detect = IsTarga,
652 static vlc_fourcc_t Detect(stream_t *s)
654 const uint8_t *peek;
655 size_t peek_size = 0;
657 for (size_t i = 0; i < ARRAY_SIZE(formats); i++) {
658 const image_format_t *img = &formats[i];
660 if (img->detect != NULL) {
661 if (img->detect(s))
662 return img->codec;
664 if (vlc_stream_Seek(s, 0))
665 return 0;
667 /* Seeking invalidates the current peek buffer */
668 peek_size = 0;
669 continue;
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;
679 assert(img->marker_size > 0); /* ensure peek is a valid pointer */
681 if (peek_size >= img->marker_size
682 && memcmp(peek, img->marker, img->marker_size) == 0)
683 return img->codec;
685 return 0;
688 static int Open(vlc_object_t *object)
690 demux_t *demux = (demux_t*)object;
692 /* Detect the image type */
693 vlc_fourcc_t codec = Detect(demux->s);
694 if (codec == 0)
695 return VLC_EGENERIC;
697 msg_Dbg(demux, "Detected image: %s",
698 vlc_fourcc_GetDescription(VIDEO_ES, codec));
700 if (codec == VLC_CODEC_MXPEG)
701 return VLC_EGENERIC; //let avformat demux this file
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;
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);
714 data = Decode(demux, &fmt, chroma, data);
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;
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");
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;
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);
751 es_format_Clean(&fmt);
753 demux->pf_demux = Demux;
754 demux->pf_control = Control;
755 demux->p_sys = sys;
756 return VLC_SUCCESS;
759 static void Close(vlc_object_t *object)
761 demux_t *demux = (demux_t*)object;
762 demux_sys_t *sys = demux->p_sys;
764 if (sys->data)
765 block_Release(sys->data);
766 free(sys);