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
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.
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.
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 *****************************************************************************/
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 int max_size = 4096 * 4096 * 8;
115 const int64_t size = stream_Size(demux->s);
116 if (size < 0 || size > max_size) {
117 msg_Err(demux, "Rejecting image based on its size (%"PRId64" > %d)", size, max_size);
122 return stream_Block(demux->s, size);
127 static block_t *Decode(demux_t *demux,
128 video_format_t *fmt, vlc_fourcc_t chroma, block_t *data)
130 image_handler_t *handler = image_HandlerCreate(demux);
136 video_format_t decoded;
137 video_format_Init(&decoded, chroma);
139 picture_t *image = image_Read(handler, data, fmt, &decoded);
140 image_HandlerDelete(handler);
145 video_format_Clean(fmt);
149 for (int i = 0; i < image->i_planes; i++)
150 size += image->p[i].i_visible_pitch *
151 image->p[i].i_visible_lines;
153 data = block_New(demux, size);
155 picture_Release(image);
160 for (int i = 0; i < image->i_planes; i++) {
161 const plane_t *src = &image->p[i];
162 for (int y = 0; y < src->i_visible_lines; y++) {
163 memcpy(&data->p_buffer[offset],
164 &src->p_pixels[y * src->i_pitch],
165 src->i_visible_pitch);
166 offset += src->i_visible_pitch;
170 picture_Release(image);
174 static int Demux(demux_t *demux)
176 demux_sys_t *sys = demux->p_sys;
182 const mtime_t pts_first = sys->pts_origin + date_Get(&sys->pts);
183 if (sys->pts_next > VLC_TS_INVALID) {
184 deadline = sys->pts_next;
185 } else if (sys->is_realtime) {
187 const mtime_t max_wait = CLOCK_FREQ / 50;
188 if (deadline + max_wait < pts_first) {
189 es_out_Control(demux->out, ES_OUT_SET_PCR, deadline);
190 /* That's ugly, but not yet easily fixable */
191 mwait(deadline + max_wait);
195 deadline = 1 + pts_first;
199 const mtime_t pts = sys->pts_origin + date_Get(&sys->pts);
200 if (sys->duration >= 0 && pts >= sys->pts_origin + sys->duration)
206 block_t *data = block_Duplicate(sys->data);
211 data->i_pts = VLC_TS_0 + pts;
212 es_out_Control(demux->out, ES_OUT_SET_PCR, data->i_pts);
213 es_out_Send(demux->out, sys->es, data);
215 date_Increment(&sys->pts, 1);
219 static int Control(demux_t *demux, int query, va_list args)
221 demux_sys_t *sys = demux->p_sys;
224 case DEMUX_GET_POSITION: {
225 double *position = va_arg(args, double *);
226 if (sys->duration > 0)
227 *position = date_Get(&sys->pts) / (double)sys->duration;
232 case DEMUX_SET_POSITION: {
233 if (sys->duration < 0 || sys->is_realtime)
235 double position = va_arg(args, double);
236 date_Set(&sys->pts, position * sys->duration);
239 case DEMUX_GET_TIME: {
240 int64_t *time = va_arg(args, int64_t *);
241 *time = sys->pts_origin + date_Get(&sys->pts);
244 case DEMUX_SET_TIME: {
245 if (sys->duration < 0 || sys->is_realtime)
247 int64_t time = va_arg(args, int64_t);
248 date_Set(&sys->pts, VLC_CLIP(time - sys->pts_origin, 0, sys->duration));
251 case DEMUX_SET_NEXT_DEMUX_TIME: {
252 int64_t pts_next = VLC_TS_0 + va_arg(args, int64_t);
253 if (sys->pts_next <= VLC_TS_INVALID)
254 sys->pts_origin = pts_next;
255 sys->pts_next = pts_next;
258 case DEMUX_GET_LENGTH: {
259 int64_t *length = va_arg(args, int64_t *);
260 *length = __MAX(sys->duration, 0);
263 case DEMUX_GET_FPS: {
264 double *fps = va_arg(args, double *);
265 *fps = (double)sys->pts.i_divider_num / sys->pts.i_divider_den;
269 case DEMUX_HAS_UNSUPPORTED_META:
270 case DEMUX_GET_ATTACHMENTS:
276 static bool IsBmp(stream_t *s)
278 const uint8_t *header;
279 if (stream_Peek(s, &header, 18) < 18)
281 if (memcmp(header, "BM", 2) &&
282 memcmp(header, "BA", 2) &&
283 memcmp(header, "CI", 2) &&
284 memcmp(header, "CP", 2) &&
285 memcmp(header, "IC", 2) &&
286 memcmp(header, "PT", 2))
288 uint32_t file_size = GetDWLE(&header[2]);
289 uint32_t data_offset = GetDWLE(&header[10]);
290 uint32_t header_size = GetDWLE(&header[14]);
291 if (file_size != 14 && file_size != 14 + header_size &&
292 file_size <= data_offset)
294 if (data_offset < header_size + 14)
296 if (header_size != 12 && header_size < 40)
301 static bool IsPcx(stream_t *s)
303 const uint8_t *header;
304 if (stream_Peek(s, &header, 66) < 66)
306 if (header[0] != 0x0A || /* marker */
307 (header[1] != 0x00 && header[1] != 0x02 &&
308 header[1] != 0x03 && header[1] != 0x05) || /* version */
309 (header[2] != 0 && header[2] != 1) || /* encoding */
310 (header[3] != 1 && header[3] != 2 &&
311 header[3] != 4 && header[3] != 8) || /* bits per pixel per plane */
312 header[64] != 0 || /* reserved */
313 header[65] == 0 || header[65] > 4) /* plane count */
315 if (GetWLE(&header[4]) > GetWLE(&header[8]) || /* xmin vs xmax */
316 GetWLE(&header[6]) > GetWLE(&header[10])) /* ymin vs ymax */
321 static bool IsLbm(stream_t *s)
323 const uint8_t *header;
324 if (stream_Peek(s, &header, 12) < 12)
326 if (memcmp(&header[0], "FORM", 4) ||
327 GetDWBE(&header[4]) <= 4 ||
328 (memcmp(&header[8], "ILBM", 4) && memcmp(&header[8], "PBM ", 4)))
332 static bool IsPnmBlank(uint8_t v)
334 return v == ' ' || v == '\t' || v == '\r' || v == '\n';
336 static bool IsPnm(stream_t *s)
338 const uint8_t *header;
339 int size = stream_Peek(s, &header, 256);
342 if (header[0] != 'P' ||
343 header[1] < '1' || header[1] > '6' ||
344 !IsPnmBlank(header[2]))
347 int number_count = 0;
348 for (int i = 3, parsing_number = 0; i < size && number_count < 2; i++) {
349 if (IsPnmBlank(header[i])) {
350 if (parsing_number) {
355 if (header[i] < '0' || header[i] > '9')
360 if (number_count < 2)
365 static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
367 for (int i = *position; i + 1 < size; i++) {
368 if (data[i + 0] != 0xff || data[i + 1] == 0x00)
370 if (data[i + 1] != 0xff) {
377 static bool IsJfif(stream_t *s)
379 const uint8_t *header;
380 int size = stream_Peek(s, &header, 256);
383 if (FindJpegMarker(&position, header, size) != 0xd8)
385 if (FindJpegMarker(&position, header, size) != 0xe0)
387 position += 2; /* Skip size */
388 if (position + 5 > size)
390 if (memcmp(&header[position], "JFIF\0", 5))
395 static bool IsSpiff(stream_t *s)
397 const uint8_t *header;
398 if (stream_Peek(s, &header, 36) < 36) /* SPIFF header size */
400 if (header[0] != 0xff || header[1] != 0xd8 ||
401 header[2] != 0xff || header[3] != 0xe8)
403 if (memcmp(&header[6], "SPIFF\0", 6))
408 static bool IsExif(stream_t *s)
410 const uint8_t *header;
411 int size = stream_Peek(s, &header, 256);
414 if (FindJpegMarker(&position, header, size) != 0xd8)
416 if (FindJpegMarker(&position, header, size) != 0xe1)
418 position += 2; /* Skip size */
419 if (position + 5 > size)
421 if (memcmp(&header[position], "Exif\0", 5))
426 static bool IsTarga(stream_t *s)
428 /* The header is not enough to ensure proper detection, we need
429 * to have a look at the footer. But doing so can be slow. So
430 * try to avoid it when possible */
431 const uint8_t *header;
432 if (stream_Peek(s, &header, 18) < 18) /* Targa fixed header */
434 if (header[1] > 1) /* Color Map Type */
436 if ((header[1] != 0 || header[3 + 4] != 0) &&
437 header[3 + 4] != 8 &&
438 header[3 + 4] != 15 && header[3 + 4] != 16 &&
439 header[3 + 4] != 24 && header[3 + 4] != 32)
441 if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */
443 if (GetWLE(&header[8 + 4]) <= 0 || /* Width */
444 GetWLE(&header[8 + 6]) <= 0) /* Height */
446 if (header[8 + 8] != 8 &&
447 header[8 + 8] != 15 && header[8 + 8] != 16 &&
448 header[8 + 8] != 24 && header[8 + 8] != 32)
450 if (header[8 + 9] & 0xc0) /* Reserved bits */
453 const int64_t size = stream_Size(s);
457 if (stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek)
460 const int64_t position = stream_Tell(s);
461 if (stream_Seek(s, size - 26))
464 const uint8_t *footer;
465 bool is_targa = stream_Peek(s, &footer, 26) >= 26 &&
466 !memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18);
467 stream_Seek(s, position);
474 const uint8_t marker[14];
475 bool (*detect)(stream_t *s);
478 #define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
479 #define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
480 static const image_format_t formats[] = {
481 { .codec = VLC_CODEC_XCF,
482 .marker_size = 9 + 4 + 1,
483 .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
484 'f', 'i', 'l', 'e', '\0' }
486 { .codec = VLC_CODEC_XCF,
487 .marker_size = 9 + 4 + 1,
488 .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
489 'v', '0', '0', '1', '\0' }
491 { .codec = VLC_CODEC_XCF,
492 .marker_size = 9 + 4 + 1,
493 .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
494 'v', '0', '0', '2', '\0' }
496 { .codec = VLC_CODEC_PNG,
498 .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
500 { .codec = VLC_CODEC_GIF,
502 .marker = { 'G', 'I', 'F', '8', '7', 'a' }
504 { .codec = VLC_CODEC_GIF,
506 .marker = { 'G', 'I', 'F', '8', '9', 'a' }
508 /* XXX TIFF detection may be a bit weak */
509 { .codec = VLC_CODEC_TIFF,
511 .marker = { 'I', 'I', 0x2a, 0x00 },
513 { .codec = VLC_CODEC_TIFF,
515 .marker = { 'M', 'M', 0x00, 0x2a },
517 { .codec = VLC_CODEC_BMP,
520 { .codec = VLC_CODEC_PCX,
523 { .codec = VLC_CODEC_LBM,
526 { .codec = VLC_CODEC_PNM,
529 { .codec = VLC_CODEC_MXPEG,
532 { .codec = VLC_CODEC_JPEG,
535 { .codec = VLC_CODEC_JPEG,
538 { .codec = VLC_CODEC_JPEG,
541 { .codec = VLC_CODEC_TARGA,
547 static int Open(vlc_object_t *object)
549 demux_t *demux = (demux_t*)object;
551 /* Detect the image type */
552 const image_format_t *img;
556 for (int i = 0; ; i++) {
562 if (img->detect(demux->s))
565 if (peek_size < img->marker_size)
566 peek_size = stream_Peek(demux->s, &peek, img->marker_size);
567 if (peek_size >= img->marker_size &&
568 !memcmp(peek, img->marker, img->marker_size))
572 msg_Dbg(demux, "Detected image: %s",
573 vlc_fourcc_GetDescription(VIDEO_ES, img->codec));
575 if( img->codec == VLC_CODEC_MXPEG )
577 return VLC_EGENERIC; //let avformat demux this file
580 /* Load and if selected decode */
582 es_format_Init(&fmt, VIDEO_ES, img->codec);
583 fmt.video.i_chroma = fmt.i_codec;
585 block_t *data = Load(demux);
586 if (data && var_InheritBool(demux, "image-decode")) {
587 char *string = var_InheritString(demux, "image-chroma");
588 vlc_fourcc_t chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, string);
591 data = Decode(demux, &fmt.video, chroma, data);
592 fmt.i_codec = fmt.video.i_chroma;
594 fmt.i_id = var_InheritInteger(demux, "image-id");
595 fmt.i_group = var_InheritInteger(demux, "image-group");
596 if (var_InheritURational(demux,
597 &fmt.video.i_frame_rate,
598 &fmt.video.i_frame_rate_base,
600 fmt.video.i_frame_rate <= 0 || fmt.video.i_frame_rate_base <= 0) {
601 msg_Err(demux, "Invalid frame rate, using 10/1 instead");
602 fmt.video.i_frame_rate = 10;
603 fmt.video.i_frame_rate_base = 1;
606 /* If loadind failed, we still continue to avoid mis-detection
607 * by other demuxers. */
609 msg_Err(demux, "Failed to load the image");
612 demux_sys_t *sys = malloc(sizeof(*sys));
616 es_format_Clean(&fmt);
621 sys->es = es_out_Add(demux->out, &fmt);
622 sys->duration = CLOCK_FREQ * var_InheritFloat(demux, "image-duration");
623 sys->is_realtime = var_InheritBool(demux, "image-realtime");
624 sys->pts_origin = sys->is_realtime ? mdate() : 0;
625 sys->pts_next = VLC_TS_INVALID;
626 date_Init(&sys->pts, fmt.video.i_frame_rate, fmt.video.i_frame_rate_base);
627 date_Set(&sys->pts, 0);
629 es_format_Clean(&fmt);
631 demux->pf_demux = Demux;
632 demux->pf_control = Control;
637 static void Close(vlc_object_t *object)
639 demux_t *demux = (demux_t*)object;
640 demux_sys_t *sys = demux->p_sys;
643 block_Release(sys->data);