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
;
194 const mtime_t pts_first
= sys
->pts_origin
+ date_Get(&sys
->pts
);
195 if (sys
->pts_next
> VLC_TS_INVALID
) {
196 deadline
= sys
->pts_next
;
197 } else if (sys
->is_realtime
) {
199 const mtime_t max_wait
= CLOCK_FREQ
/ 50;
200 if (deadline
+ max_wait
< pts_first
) {
201 es_out_SetPCR(demux
->out
, deadline
);
202 /* That's ugly, but not yet easily fixable */
203 mwait(deadline
+ max_wait
);
207 deadline
= 1 + pts_first
;
211 const mtime_t pts
= sys
->pts_origin
+ date_Get(&sys
->pts
);
212 if (sys
->duration
>= 0 && pts
>= sys
->pts_origin
+ sys
->duration
)
218 block_t
*data
= block_Duplicate(sys
->data
);
223 data
->i_pts
= VLC_TS_0
+ pts
;
224 es_out_SetPCR(demux
->out
, data
->i_pts
);
225 es_out_Send(demux
->out
, sys
->es
, data
);
227 date_Increment(&sys
->pts
, 1);
231 static int Control(demux_t
*demux
, int query
, va_list args
)
233 demux_sys_t
*sys
= demux
->p_sys
;
237 *va_arg(args
, bool *) = sys
->duration
>= 0 && !sys
->is_realtime
;
239 case DEMUX_GET_POSITION
: {
240 double *position
= va_arg(args
, double *);
241 if (sys
->duration
> 0)
242 *position
= date_Get(&sys
->pts
) / (double)sys
->duration
;
247 case DEMUX_SET_POSITION
: {
248 if (sys
->duration
< 0 || sys
->is_realtime
)
250 double position
= va_arg(args
, double);
251 date_Set(&sys
->pts
, position
* sys
->duration
);
254 case DEMUX_GET_TIME
: {
255 int64_t *time
= va_arg(args
, int64_t *);
256 *time
= sys
->pts_origin
+ date_Get(&sys
->pts
);
259 case DEMUX_SET_TIME
: {
260 if (sys
->duration
< 0 || sys
->is_realtime
)
262 int64_t time
= va_arg(args
, int64_t);
263 date_Set(&sys
->pts
, VLC_CLIP(time
- sys
->pts_origin
, 0, sys
->duration
));
266 case DEMUX_SET_NEXT_DEMUX_TIME
: {
267 int64_t pts_next
= VLC_TS_0
+ va_arg(args
, int64_t);
268 if (sys
->pts_next
<= VLC_TS_INVALID
)
269 sys
->pts_origin
= pts_next
;
270 sys
->pts_next
= pts_next
;
273 case DEMUX_GET_LENGTH
: {
274 int64_t *length
= va_arg(args
, int64_t *);
275 *length
= __MAX(sys
->duration
, 0);
278 case DEMUX_GET_FPS
: {
279 double *fps
= va_arg(args
, double *);
280 *fps
= (double)sys
->pts
.i_divider_num
/ sys
->pts
.i_divider_den
;
284 case DEMUX_HAS_UNSUPPORTED_META
:
285 case DEMUX_GET_ATTACHMENTS
:
288 case DEMUX_CAN_PAUSE
:
289 case DEMUX_SET_PAUSE_STATE
:
290 case DEMUX_CAN_CONTROL_PACE
:
291 case DEMUX_GET_PTS_DELAY
:
292 return demux_vaControlHelper( demux
->s
, 0, -1, 0, 1, query
, args
);
300 static bool IsBmp(stream_t
*s
)
302 const uint8_t *header
;
303 if (vlc_stream_Peek(s
, &header
, 18) < 18)
305 if (memcmp(header
, "BM", 2) &&
306 memcmp(header
, "BA", 2) &&
307 memcmp(header
, "CI", 2) &&
308 memcmp(header
, "CP", 2) &&
309 memcmp(header
, "IC", 2) &&
310 memcmp(header
, "PT", 2))
312 uint32_t file_size
= GetDWLE(&header
[2]);
313 uint32_t data_offset
= GetDWLE(&header
[10]);
314 uint32_t header_size
= GetDWLE(&header
[14]);
315 if (file_size
!= 14 && file_size
!= 14 + header_size
&&
316 file_size
<= data_offset
)
318 if (data_offset
< header_size
+ 14)
320 if (header_size
!= 12 && header_size
< 40)
325 static bool IsPcx(stream_t
*s
)
327 const uint8_t *header
;
328 if (vlc_stream_Peek(s
, &header
, 66) < 66)
330 if (header
[0] != 0x0A || /* marker */
331 (header
[1] != 0x00 && header
[1] != 0x02 &&
332 header
[1] != 0x03 && header
[1] != 0x05) || /* version */
333 (header
[2] != 0 && header
[2] != 1) || /* encoding */
334 (header
[3] != 1 && header
[3] != 2 &&
335 header
[3] != 4 && header
[3] != 8) || /* bits per pixel per plane */
336 header
[64] != 0 || /* reserved */
337 header
[65] == 0 || header
[65] > 4) /* plane count */
339 if (GetWLE(&header
[4]) > GetWLE(&header
[8]) || /* xmin vs xmax */
340 GetWLE(&header
[6]) > GetWLE(&header
[10])) /* ymin vs ymax */
345 static bool IsLbm(stream_t
*s
)
347 const uint8_t *header
;
348 if (vlc_stream_Peek(s
, &header
, 12) < 12)
350 if (memcmp(&header
[0], "FORM", 4) ||
351 GetDWBE(&header
[4]) <= 4 ||
352 (memcmp(&header
[8], "ILBM", 4) && memcmp(&header
[8], "PBM ", 4)))
356 static bool IsPnmBlank(uint8_t v
)
358 return v
== ' ' || v
== '\t' || v
== '\r' || v
== '\n';
360 static bool IsPnm(stream_t
*s
)
362 const uint8_t *header
;
363 int size
= vlc_stream_Peek(s
, &header
, 256);
366 if (header
[0] != 'P' ||
367 header
[1] < '1' || header
[1] > '6' ||
368 !IsPnmBlank(header
[2]))
371 int number_count
= 0;
372 for (int i
= 3, parsing_number
= 0; i
< size
&& number_count
< 2; i
++) {
373 if (IsPnmBlank(header
[i
])) {
374 if (parsing_number
) {
379 if (header
[i
] < '0' || header
[i
] > '9')
384 if (number_count
< 2)
389 static uint8_t FindJpegMarker(int *position
, const uint8_t *data
, int size
)
391 for (int i
= *position
; i
+ 1 < size
; i
++) {
392 if (data
[i
+ 0] != 0xff || data
[i
+ 1] == 0x00)
394 if (data
[i
+ 1] != 0xff) {
401 static bool IsJfif(stream_t
*s
)
403 const uint8_t *header
;
404 int size
= vlc_stream_Peek(s
, &header
, 256);
407 if (FindJpegMarker(&position
, header
, size
) != 0xd8)
409 if (FindJpegMarker(&position
, header
, size
) != 0xe0)
411 position
+= 2; /* Skip size */
412 if (position
+ 5 > size
)
414 if (memcmp(&header
[position
], "JFIF\0", 5))
419 static bool IsWebP(stream_t
*s
)
421 const uint8_t *header
;
422 if (vlc_stream_Peek(s
, &header
, 20) < 20) /* WebP header size */
424 if (memcmp(&header
[0], "RIFF", 4))
426 /* TODO: support other chunk types */
427 if (memcmp(&header
[8], "WEBPVP8 ", 8))
430 vlc_stream_Seek(s
, 20);
434 static bool IsSpiff(stream_t
*s
)
436 const uint8_t *header
;
437 if (vlc_stream_Peek(s
, &header
, 36) < 36) /* SPIFF header size */
439 if (header
[0] != 0xff || header
[1] != 0xd8 ||
440 header
[2] != 0xff || header
[3] != 0xe8)
442 if (memcmp(&header
[6], "SPIFF\0", 6))
447 static bool IsExif(stream_t
*s
)
449 const uint8_t *header
;
450 ssize_t size
= vlc_stream_Peek(s
, &header
, 256);
455 if (FindJpegMarker(&position
, header
, size
) != 0xd8)
457 if (FindJpegMarker(&position
, header
, size
) != 0xe1)
459 position
+= 2; /* Skip size */
460 if (position
+ 5 > size
)
462 if (memcmp(&header
[position
], "Exif\0", 5))
467 static bool FindSVGmarker(int *position
, const uint8_t *data
, const int size
, const char *marker
)
469 for( int i
= *position
; i
< size
; i
++)
471 if (memcmp(&data
[i
], marker
, strlen(marker
)) == 0)
480 static bool IsSVG(stream_t
*s
)
482 if (s
->psz_url
== NULL
)
485 char *ext
= strstr(s
->psz_url
, ".svg");
486 if (!ext
) return false;
488 const uint8_t *header
;
489 ssize_t size
= vlc_stream_Peek(s
, &header
, 4096);
494 const char xml
[] = "<?xml version=\"";
495 if (!FindSVGmarker(&position
, header
, size
, xml
))
500 const char endxml
[] = ">\0";
501 if (!FindSVGmarker(&position
, header
, size
, endxml
))
506 const char svg
[] = "<svg";
507 if (!FindSVGmarker(&position
, header
, size
, svg
))
512 /* SVG Scalable Vector Graphics image */
514 /* NOTE: some SVG images have the mimetype set in a meta data section
519 static bool IsTarga(stream_t
*s
)
521 /* The header is not enough to ensure proper detection, we need
522 * to have a look at the footer. But doing so can be slow. So
523 * try to avoid it when possible */
524 const uint8_t *header
;
525 if (vlc_stream_Peek(s
, &header
, 18) < 18) /* Targa fixed header */
527 if (header
[1] > 1) /* Color Map Type */
529 if ((header
[1] != 0 || header
[3 + 4] != 0) &&
530 header
[3 + 4] != 8 &&
531 header
[3 + 4] != 15 && header
[3 + 4] != 16 &&
532 header
[3 + 4] != 24 && header
[3 + 4] != 32)
534 if ((header
[2] > 3 && header
[2] < 9) || header
[2] > 11) /* Image Type */
536 if (GetWLE(&header
[8 + 4]) <= 0 || /* Width */
537 GetWLE(&header
[8 + 6]) <= 0) /* Height */
539 if (header
[8 + 8] != 8 &&
540 header
[8 + 8] != 15 && header
[8 + 8] != 16 &&
541 header
[8 + 8] != 24 && header
[8 + 8] != 32)
543 if (header
[8 + 9] & 0xc0) /* Reserved bits */
546 const int64_t size
= stream_Size(s
);
550 if (vlc_stream_Control(s
, STREAM_CAN_SEEK
, &can_seek
) || !can_seek
)
553 const int64_t position
= vlc_stream_Tell(s
);
554 if (vlc_stream_Seek(s
, size
- 26))
557 const uint8_t *footer
;
558 bool is_targa
= vlc_stream_Peek(s
, &footer
, 26) >= 26 &&
559 !memcmp(&footer
[8], "TRUEVISION-XFILE.\x00", 18);
560 vlc_stream_Seek(s
, position
);
567 const uint8_t marker
[14];
568 bool (*detect
)(stream_t
*s
);
571 #define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
572 #define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
573 static const image_format_t formats
[] = {
574 { .codec
= VLC_CODEC_XCF
,
575 .marker_size
= 9 + 4 + 1,
576 .marker
= { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
577 'f', 'i', 'l', 'e', '\0' }
579 { .codec
= VLC_CODEC_XCF
,
580 .marker_size
= 9 + 4 + 1,
581 .marker
= { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
582 'v', '0', '0', '1', '\0' }
584 { .codec
= VLC_CODEC_XCF
,
585 .marker_size
= 9 + 4 + 1,
586 .marker
= { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
587 'v', '0', '0', '2', '\0' }
589 { .codec
= VLC_CODEC_PNG
,
591 .marker
= { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
593 { .codec
= VLC_CODEC_GIF
,
595 .marker
= { 'G', 'I', 'F', '8', '7', 'a' }
597 { .codec
= VLC_CODEC_GIF
,
599 .marker
= { 'G', 'I', 'F', '8', '9', 'a' }
601 /* XXX TIFF detection may be a bit weak */
602 { .codec
= VLC_CODEC_TIFF
,
604 .marker
= { 'I', 'I', 0x2a, 0x00 },
606 { .codec
= VLC_CODEC_TIFF
,
608 .marker
= { 'M', 'M', 0x00, 0x2a },
610 { .codec
= VLC_CODEC_BMP
,
613 { .codec
= VLC_CODEC_PCX
,
616 { .codec
= VLC_CODEC_LBM
,
619 { .codec
= VLC_CODEC_PNM
,
622 { .codec
= VLC_CODEC_MXPEG
,
625 { .codec
= VLC_CODEC_JPEG
,
628 { .codec
= VLC_CODEC_JPEG
,
631 { .codec
= VLC_CODEC_JPEG
,
634 { .codec
= VLC_CODEC_WEBP
,
637 { .codec
= VLC_CODEC_BPG
,
639 .marker
= { 'B', 'P', 'G', 0xFB },
641 { .codec
= VLC_CODEC_SVG
,
644 { .codec
= VLC_CODEC_TARGA
,
650 static int Open(vlc_object_t
*object
)
652 demux_t
*demux
= (demux_t
*)object
;
654 /* Detect the image type */
655 const image_format_t
*img
;
658 ssize_t peek_size
= 0;
659 for (int i
= 0; ; i
++) {
665 if (img
->detect(demux
->s
))
667 /* detect callbacks can invalidate the current peek buffer */
670 if ((size_t) peek_size
< img
->marker_size
)
672 peek_size
= vlc_stream_Peek(demux
->s
, &peek
, img
->marker_size
);
676 if ((size_t) peek_size
>= img
->marker_size
&&
677 !memcmp(peek
, img
->marker
, img
->marker_size
))
681 msg_Dbg(demux
, "Detected image: %s",
682 vlc_fourcc_GetDescription(VIDEO_ES
, img
->codec
));
684 if( img
->codec
== VLC_CODEC_MXPEG
)
686 return VLC_EGENERIC
; //let avformat demux this file
689 /* Load and if selected decode */
691 es_format_Init(&fmt
, VIDEO_ES
, img
->codec
);
692 fmt
.video
.i_chroma
= fmt
.i_codec
;
694 block_t
*data
= Load(demux
);
695 if (data
&& var_InheritBool(demux
, "image-decode")) {
696 char *string
= var_InheritString(demux
, "image-chroma");
697 vlc_fourcc_t chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, string
);
700 data
= Decode(demux
, &fmt
.video
, chroma
, data
);
701 fmt
.i_codec
= fmt
.video
.i_chroma
;
703 fmt
.i_id
= var_InheritInteger(demux
, "image-id");
704 fmt
.i_group
= var_InheritInteger(demux
, "image-group");
705 if (var_InheritURational(demux
,
706 &fmt
.video
.i_frame_rate
,
707 &fmt
.video
.i_frame_rate_base
,
709 fmt
.video
.i_frame_rate
<= 0 || fmt
.video
.i_frame_rate_base
<= 0) {
710 msg_Err(demux
, "Invalid frame rate, using 10/1 instead");
711 fmt
.video
.i_frame_rate
= 10;
712 fmt
.video
.i_frame_rate_base
= 1;
715 /* If loadind failed, we still continue to avoid mis-detection
716 * by other demuxers. */
718 msg_Err(demux
, "Failed to load the image");
721 demux_sys_t
*sys
= malloc(sizeof(*sys
));
725 es_format_Clean(&fmt
);
730 sys
->es
= es_out_Add(demux
->out
, &fmt
);
731 sys
->duration
= CLOCK_FREQ
* var_InheritFloat(demux
, "image-duration");
732 sys
->is_realtime
= var_InheritBool(demux
, "image-realtime");
733 sys
->pts_origin
= sys
->is_realtime
? mdate() : 0;
734 sys
->pts_next
= VLC_TS_INVALID
;
735 date_Init(&sys
->pts
, fmt
.video
.i_frame_rate
, fmt
.video
.i_frame_rate_base
);
736 date_Set(&sys
->pts
, 0);
738 es_format_Clean(&fmt
);
740 demux
->pf_demux
= Demux
;
741 demux
->pf_control
= Control
;
746 static void Close(vlc_object_t
*object
)
748 demux_t
*demux
= (demux_t
*)object
;
749 demux_sys_t
*sys
= demux
->p_sys
;
752 block_Release(sys
->data
);