1 /*****************************************************************************
2 * image.c: Image demuxer
3 *****************************************************************************
4 * Copyright (C) 2010 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_demux.h>
35 #include <vlc_image.h>
36 #include "mxpeg_helper.h"
38 /*****************************************************************************
40 *****************************************************************************/
41 static int Open (vlc_object_t
*);
42 static void Close(vlc_object_t
*);
44 #define ID_TEXT N_("ES ID")
45 #define ID_LONGTEXT N_( \
46 "Set the ID of the elementary stream")
48 #define GROUP_TEXT N_("Group")
49 #define GROUP_LONGTEXT N_(\
50 "Set the group of the elementary stream")
52 #define DECODE_TEXT N_("Decode")
53 #define DECODE_LONGTEXT N_( \
54 "Decode at the demuxer stage")
56 #define CHROMA_TEXT N_("Forced chroma")
57 #define CHROMA_LONGTEXT N_( \
58 "If non empty and image-decode is true, the image will be " \
59 "converted to the specified chroma.")
61 #define DURATION_TEXT N_("Duration in seconds")
62 #define DURATION_LONGTEXT N_( \
63 "Duration in seconds before simulating an end of file. " \
64 "A negative value means an unlimited play time.")
66 #define FPS_TEXT N_("Frame rate")
67 #define FPS_LONGTEXT N_( \
68 "Frame rate of the elementary stream produced.")
70 #define RT_TEXT N_("Real-time")
71 #define RT_LONGTEXT N_( \
72 "Use real-time mode suitable for being used as a master input and " \
73 "real-time input slaves.")
76 set_description(N_("Image demuxer"))
77 set_shortname(N_("Image"))
78 set_category(CAT_INPUT
)
79 set_subcategory(SUBCAT_INPUT_DEMUX
)
80 add_integer("image-id", -1, ID_TEXT
, ID_LONGTEXT
, true)
82 add_integer("image-group", 0, GROUP_TEXT
, GROUP_LONGTEXT
, true)
84 add_bool("image-decode", true, DECODE_TEXT
, DECODE_LONGTEXT
, true)
86 add_string("image-chroma", "", CHROMA_TEXT
, CHROMA_LONGTEXT
, true)
88 add_float("image-duration", 10, DURATION_TEXT
, DURATION_LONGTEXT
, false)
90 add_string("image-fps", "10/1", FPS_TEXT
, FPS_LONGTEXT
, true)
92 add_bool("image-realtime", false, RT_TEXT
, RT_LONGTEXT
, true)
94 set_capability("demux", 10)
95 set_callbacks(Open
, Close
)
98 /*****************************************************************************
100 *****************************************************************************/
112 static block_t
*Load(demux_t
*demux
)
114 const unsigned max_size
= 4096 * 4096 * 8;
117 if (vlc_stream_GetSize(demux
->s
, &size
) == VLC_SUCCESS
) {
118 if (size
> max_size
) {
119 msg_Err(demux
, "image too large (%"PRIu64
" > %u), rejected",
126 block_t
*block
= block_Alloc(size
);
130 ssize_t val
= vlc_stream_Read(demux
->s
, block
->p_buffer
, size
);
132 block_Release(block
);
136 block
->i_buffer
= val
;
140 static block_t
*Decode(demux_t
*demux
,
141 video_format_t
*fmt
, vlc_fourcc_t chroma
, block_t
*data
)
143 image_handler_t
*handler
= image_HandlerCreate(demux
);
149 video_format_t decoded
;
150 video_format_Init(&decoded
, chroma
);
152 picture_t
*image
= image_Read(handler
, data
, fmt
, &decoded
);
153 image_HandlerDelete(handler
);
158 video_format_Clean(fmt
);
162 for (int i
= 0; i
< image
->i_planes
; i
++)
163 size
+= image
->p
[i
].i_pitch
* image
->p
[i
].i_lines
;
165 data
= block_Alloc(size
);
167 picture_Release(image
);
172 for (int i
= 0; i
< image
->i_planes
; i
++) {
173 const plane_t
*src
= &image
->p
[i
];
174 for (int y
= 0; y
< src
->i_visible_lines
; y
++) {
175 memcpy(&data
->p_buffer
[offset
],
176 &src
->p_pixels
[y
* src
->i_pitch
],
177 src
->i_visible_pitch
);
178 offset
+= src
->i_visible_pitch
;
182 picture_Release(image
);
186 static int Demux(demux_t
*demux
)
188 demux_sys_t
*sys
= demux
->p_sys
;
191 return VLC_DEMUXER_EOF
;
194 const vlc_tick_t pts_first
= sys
->pts_offset
+ date_Get(&sys
->pts
);
195 if (sys
->pts_next
!= VLC_TICK_INVALID
) {
196 deadline
= sys
->pts_next
;
197 } else if (sys
->is_realtime
) {
198 deadline
= vlc_tick_now();
199 const vlc_tick_t max_wait
= VLC_TICK_FROM_MS(20);
200 if (deadline
+ max_wait
< pts_first
) {
201 es_out_SetPCR(demux
->out
, deadline
);
202 /* That's ugly, but not yet easily fixable */
203 vlc_tick_wait(deadline
+ max_wait
);
204 return VLC_DEMUXER_SUCCESS
;
207 deadline
= 1 + pts_first
;
211 const vlc_tick_t pts
= sys
->pts_offset
+ date_Get(&sys
->pts
);
212 if (sys
->duration
>= 0 && pts
>= VLC_TICK_0
+ sys
->pts_offset
+ sys
->duration
)
213 return VLC_DEMUXER_EOF
;
216 return VLC_DEMUXER_SUCCESS
;
218 block_t
*data
= block_Duplicate(sys
->data
);
220 return VLC_DEMUXER_EGENERIC
;
223 data
->i_pts
= VLC_TICK_0
+ pts
;
224 es_out_SetPCR(demux
->out
, data
->i_pts
);
226 es_out_Send(demux
->out
, sys
->es
, data
);
230 date_Increment(&sys
->pts
, 1);
234 static int Control(demux_t
*demux
, int query
, va_list args
)
236 demux_sys_t
*sys
= demux
->p_sys
;
240 *va_arg(args
, bool *) = sys
->duration
>= 0 && !sys
->is_realtime
;
242 case DEMUX_GET_POSITION
: {
243 double *position
= va_arg(args
, double *);
244 if (sys
->duration
> 0)
245 *position
= date_Get(&sys
->pts
) / (double)sys
->duration
;
250 case DEMUX_SET_POSITION
: {
251 if (sys
->duration
< 0 || sys
->is_realtime
)
253 double position
= va_arg(args
, double);
254 date_Set(&sys
->pts
, position
* sys
->duration
);
257 case DEMUX_GET_TIME
: {
258 int64_t *time
= va_arg(args
, int64_t *);
259 *time
= sys
->pts_offset
+ date_Get(&sys
->pts
);
262 case DEMUX_SET_TIME
: {
263 if (sys
->duration
< 0 || sys
->is_realtime
)
265 int64_t time
= va_arg(args
, int64_t);
266 date_Set(&sys
->pts
, VLC_CLIP(time
- sys
->pts_offset
, VLC_TICK_0
, sys
->duration
));
269 case DEMUX_SET_NEXT_DEMUX_TIME
: {
270 vlc_tick_t pts_next
= VLC_TICK_0
+ va_arg(args
, vlc_tick_t
);
271 if (sys
->pts_next
== VLC_TICK_INVALID
)
272 sys
->pts_offset
= pts_next
- VLC_TICK_0
;
273 sys
->pts_next
= pts_next
;
276 case DEMUX_GET_LENGTH
: {
277 int64_t *length
= va_arg(args
, int64_t *);
278 *length
= __MAX(sys
->duration
, 0);
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
;
287 case DEMUX_HAS_UNSUPPORTED_META
:
288 case DEMUX_GET_ATTACHMENTS
:
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
);
303 static bool IsBmp(stream_t
*s
)
305 const uint8_t *header
;
306 if (vlc_stream_Peek(s
, &header
, 18) < 18)
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))
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
)
321 if (data_offset
< header_size
+ 14)
323 if (header_size
!= 12 && header_size
< 40)
328 static bool IsPcx(stream_t
*s
)
330 const uint8_t *header
;
331 if (vlc_stream_Peek(s
, &header
, 66) < 66)
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 */
342 if (GetWLE(&header
[4]) > GetWLE(&header
[8]) || /* xmin vs xmax */
343 GetWLE(&header
[6]) > GetWLE(&header
[10])) /* ymin vs ymax */
348 static bool IsLbm(stream_t
*s
)
350 const uint8_t *header
;
351 if (vlc_stream_Peek(s
, &header
, 12) < 12)
353 if (memcmp(&header
[0], "FORM", 4) ||
354 GetDWBE(&header
[4]) <= 4 ||
355 (memcmp(&header
[8], "ILBM", 4) && memcmp(&header
[8], "PBM ", 4)))
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);
369 if (header
[0] != 'P' ||
370 header
[1] < '1' || header
[1] > '6' ||
371 !IsPnmBlank(header
[2]))
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
) {
382 if (header
[i
] < '0' || header
[i
] > '9')
387 if (number_count
< 2)
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)
397 if (data
[i
+ 1] != 0xff) {
404 static bool IsJfif(stream_t
*s
)
406 const uint8_t *header
;
407 int size
= vlc_stream_Peek(s
, &header
, 256);
410 if (FindJpegMarker(&position
, header
, size
) != 0xd8)
412 if (FindJpegMarker(&position
, header
, size
) != 0xe0)
414 position
+= 2; /* Skip size */
415 if (position
+ 5 > size
)
417 if (memcmp(&header
[position
], "JFIF\0", 5))
422 static bool IsWebP(stream_t
*s
)
424 const uint8_t *header
;
425 if (vlc_stream_Peek(s
, &header
, 20) < 20) /* WebP header size */
427 if (memcmp(&header
[0], "RIFF", 4))
429 /* TODO: support other chunk types */
430 if (memcmp(&header
[8], "WEBPVP8 ", 8))
433 vlc_stream_Seek(s
, 20);
437 static bool IsSpiff(stream_t
*s
)
439 const uint8_t *header
;
440 if (vlc_stream_Peek(s
, &header
, 36) < 36) /* SPIFF header size */
442 if (header
[0] != 0xff || header
[1] != 0xd8 ||
443 header
[2] != 0xff || header
[3] != 0xe8)
445 if (memcmp(&header
[6], "SPIFF\0", 6))
450 static bool IsExif(stream_t
*s
)
452 const uint8_t *header
;
453 ssize_t size
= vlc_stream_Peek(s
, &header
, 256);
458 if (FindJpegMarker(&position
, header
, size
) != 0xd8)
460 if (FindJpegMarker(&position
, header
, size
) != 0xe1)
462 position
+= 2; /* Skip size */
463 if (position
+ 5 > size
)
465 if (memcmp(&header
[position
], "Exif\0", 5))
470 static bool FindSVGmarker(int *position
, const uint8_t *data
, const int size
, const char *marker
)
472 for( int i
= *position
; i
< size
; i
++)
474 if (memcmp(&data
[i
], marker
, strlen(marker
)) == 0)
483 static bool IsSVG(stream_t
*s
)
485 if (s
->psz_url
== NULL
)
488 char *ext
= strstr(s
->psz_url
, ".svg");
489 if (!ext
) return false;
491 const uint8_t *header
;
492 ssize_t size
= vlc_stream_Peek(s
, &header
, 4096);
497 const char xml
[] = "<?xml version=\"";
498 if (!FindSVGmarker(&position
, header
, size
, xml
))
503 const char endxml
[] = ">\0";
504 if (!FindSVGmarker(&position
, header
, size
, endxml
))
509 const char svg
[] = "<svg";
510 if (!FindSVGmarker(&position
, header
, size
, svg
))
515 /* SVG Scalable Vector Graphics image */
517 /* NOTE: some SVG images have the mimetype set in a meta data section
522 static bool IsTarga(stream_t
*s
)
524 /* The header is not enough to ensure proper detection, we need
525 * to have a look at the footer. But doing so can be slow. So
526 * try to avoid it when possible */
527 const uint8_t *header
;
528 if (vlc_stream_Peek(s
, &header
, 18) < 18) /* Targa fixed header */
530 if (header
[1] > 1) /* Color Map Type */
532 if ((header
[1] != 0 || header
[3 + 4] != 0) &&
533 header
[3 + 4] != 8 &&
534 header
[3 + 4] != 15 && header
[3 + 4] != 16 &&
535 header
[3 + 4] != 24 && header
[3 + 4] != 32)
537 if ((header
[2] > 3 && header
[2] < 9) || header
[2] > 11) /* Image Type */
539 if (GetWLE(&header
[8 + 4]) <= 0 || /* Width */
540 GetWLE(&header
[8 + 6]) <= 0) /* Height */
542 if (header
[8 + 8] != 8 &&
543 header
[8 + 8] != 15 && header
[8 + 8] != 16 &&
544 header
[8 + 8] != 24 && header
[8 + 8] != 32)
546 if (header
[8 + 9] & 0xc0) /* Reserved bits */
549 const int64_t size
= stream_Size(s
);
553 if (vlc_stream_Control(s
, STREAM_CAN_SEEK
, &can_seek
) || !can_seek
)
556 const int64_t position
= vlc_stream_Tell(s
);
557 if (vlc_stream_Seek(s
, size
- 26))
560 const uint8_t *footer
;
561 bool is_targa
= vlc_stream_Peek(s
, &footer
, 26) >= 26 &&
562 !memcmp(&footer
[8], "TRUEVISION-XFILE.\x00", 18);
563 vlc_stream_Seek(s
, position
);
570 const uint8_t marker
[14];
571 bool (*detect
)(stream_t
*s
);
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
,
594 .marker
= { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
596 { .codec
= VLC_CODEC_GIF
,
598 .marker
= { 'G', 'I', 'F', '8', '7', 'a' }
600 { .codec
= VLC_CODEC_GIF
,
602 .marker
= { 'G', 'I', 'F', '8', '9', 'a' }
604 /* XXX TIFF detection may be a bit weak */
605 { .codec
= VLC_CODEC_TIFF
,
607 .marker
= { 'I', 'I', 0x2a, 0x00 },
609 { .codec
= VLC_CODEC_TIFF
,
611 .marker
= { 'M', 'M', 0x00, 0x2a },
613 { .codec
= VLC_CODEC_BMP
,
616 { .codec
= VLC_CODEC_PCX
,
619 { .codec
= VLC_CODEC_LBM
,
622 { .codec
= VLC_CODEC_PNM
,
625 { .codec
= VLC_CODEC_MXPEG
,
628 { .codec
= VLC_CODEC_JPEG
,
631 { .codec
= VLC_CODEC_JPEG
,
634 { .codec
= VLC_CODEC_JPEG
,
637 { .codec
= VLC_CODEC_WEBP
,
640 { .codec
= VLC_CODEC_BPG
,
642 .marker
= { 'B', 'P', 'G', 0xFB },
644 { .codec
= VLC_CODEC_SVG
,
647 { .codec
= VLC_CODEC_TARGA
,
653 static int Open(vlc_object_t
*object
)
655 demux_t
*demux
= (demux_t
*)object
;
657 /* Detect the image type */
658 const image_format_t
*img
;
661 ssize_t peek_size
= 0;
662 for (int i
= 0; ; i
++) {
668 if (img
->detect(demux
->s
))
670 /* detect callbacks can invalidate the current peek buffer */
673 if ((size_t) peek_size
< img
->marker_size
)
675 peek_size
= vlc_stream_Peek(demux
->s
, &peek
, img
->marker_size
);
679 if ((size_t) peek_size
>= img
->marker_size
&&
680 !memcmp(peek
, img
->marker
, img
->marker_size
))
684 msg_Dbg(demux
, "Detected image: %s",
685 vlc_fourcc_GetDescription(VIDEO_ES
, img
->codec
));
687 if( img
->codec
== VLC_CODEC_MXPEG
)
689 return VLC_EGENERIC
; //let avformat demux this file
692 /* Load and if selected decode */
694 es_format_Init(&fmt
, VIDEO_ES
, img
->codec
);
695 fmt
.video
.i_chroma
= fmt
.i_codec
;
697 block_t
*data
= Load(demux
);
698 if (data
&& var_InheritBool(demux
, "image-decode")) {
699 char *string
= var_InheritString(demux
, "image-chroma");
700 vlc_fourcc_t chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, string
);
703 data
= Decode(demux
, &fmt
.video
, chroma
, data
);
704 fmt
.i_codec
= fmt
.video
.i_chroma
;
706 fmt
.i_id
= var_InheritInteger(demux
, "image-id");
707 fmt
.i_group
= var_InheritInteger(demux
, "image-group");
708 if (var_InheritURational(demux
,
709 &fmt
.video
.i_frame_rate
,
710 &fmt
.video
.i_frame_rate_base
,
712 fmt
.video
.i_frame_rate
<= 0 || fmt
.video
.i_frame_rate_base
<= 0) {
713 msg_Err(demux
, "Invalid frame rate, using 10/1 instead");
714 fmt
.video
.i_frame_rate
= 10;
715 fmt
.video
.i_frame_rate_base
= 1;
718 /* If loadind failed, we still continue to avoid mis-detection
719 * by other demuxers. */
721 msg_Err(demux
, "Failed to load the image");
724 demux_sys_t
*sys
= malloc(sizeof(*sys
));
728 es_format_Clean(&fmt
);
733 sys
->es
= es_out_Add(demux
->out
, &fmt
);
734 sys
->duration
= vlc_tick_from_sec( var_InheritFloat(demux
, "image-duration") );
735 sys
->is_realtime
= var_InheritBool(demux
, "image-realtime");
736 sys
->pts_offset
= sys
->is_realtime
? vlc_tick_now() : 0;
737 sys
->pts_next
= VLC_TICK_INVALID
;
738 date_Init(&sys
->pts
, fmt
.video
.i_frame_rate
, fmt
.video
.i_frame_rate_base
);
739 date_Set(&sys
->pts
, VLC_TICK_0
);
741 es_format_Clean(&fmt
);
743 demux
->pf_demux
= Demux
;
744 demux
->pf_control
= Control
;
749 static void Close(vlc_object_t
*object
)
751 demux_t
*demux
= (demux_t
*)object
;
752 demux_sys_t
*sys
= demux
->p_sys
;
755 block_Release(sys
->data
);