1 /*****************************************************************************
2 * image.c : wrapper for image reading/writing facilities
3 *****************************************************************************
4 * Copyright (C) 2004-2007 VLC authors and VideoLAN
5 * $Id: ee51aa01e390ab4416c8fe0f928ea8f349de26d0 $
6 *
7 * Author: Gildas Bazin <gbazin@videolan.org>
8 *
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.
13 *
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.
18 *
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 *****************************************************************************/
23
24 /**
25 * \file
26 * This file contains the functions to handle the image_handler_t type
27 */
28
29 /*****************************************************************************
30 * Preamble
31 *****************************************************************************/
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <errno.h>
38 #include <limits.h>
39
40 #include <vlc_common.h>
41 #include <vlc_codec.h>
42 #include <vlc_meta.h>
43 #include <vlc_filter.h>
44 #include <vlc_es.h>
45 #include <vlc_image.h>
46 #include <vlc_stream.h>
47 #include <vlc_fs.h>
48 #include <vlc_sout.h>
49 #include <libvlc.h>
50 #include <vlc_modules.h>
51
52 static picture_t *ImageRead( image_handler_t *, block_t *,
53 const video_format_t *, video_format_t * );
54 static picture_t *ImageReadUrl( image_handler_t *, const char *,
55 video_format_t *, video_format_t * );
56 static block_t *ImageWrite( image_handler_t *, picture_t *,
57 const video_format_t *, const video_format_t * );
58 static int ImageWriteUrl( image_handler_t *, picture_t *,
59 const video_format_t *, video_format_t *, const char * );
60
61 static picture_t *ImageConvert( image_handler_t *, picture_t *,
62 const video_format_t *, video_format_t * );
63
64 static decoder_t *CreateDecoder( vlc_object_t *, const video_format_t * );
65 static void DeleteDecoder( decoder_t * );
66 static encoder_t *CreateEncoder( vlc_object_t *, const video_format_t *,
67 const video_format_t * );
68 static void DeleteEncoder( encoder_t * );
69 static filter_t *CreateFilter( vlc_object_t *, const es_format_t *,
70 const video_format_t * );
71 static void DeleteFilter( filter_t * );
72
73 vlc_fourcc_t image_Type2Fourcc( const char * );
74 vlc_fourcc_t image_Ext2Fourcc( const char * );
75 /*static const char *Fourcc2Ext( vlc_fourcc_t );*/
76
77 #undef image_HandlerCreate
78 /**
79 * Create an image_handler_t instance
80 *
81 */
image_HandlerCreate(vlc_object_t * p_this)82 image_handler_t *image_HandlerCreate( vlc_object_t *p_this )
83 {
84 image_handler_t *p_image = calloc( 1, sizeof(image_handler_t) );
85 if( !p_image )
86 return NULL;
87
88 p_image->p_parent = p_this;
89
90 p_image->pf_read = ImageRead;
91 p_image->pf_read_url = ImageReadUrl;
92 p_image->pf_write = ImageWrite;
93 p_image->pf_write_url = ImageWriteUrl;
94 p_image->pf_convert = ImageConvert;
95
96 p_image->outfifo = picture_fifo_New();
97
98 return p_image;
99 }
100
101 /**
102 * Delete the image_handler_t instance
103 *
104 */
image_HandlerDelete(image_handler_t * p_image)105 void image_HandlerDelete( image_handler_t *p_image )
106 {
107 if( !p_image ) return;
108
109 if( p_image->p_dec ) DeleteDecoder( p_image->p_dec );
110 if( p_image->p_enc ) DeleteEncoder( p_image->p_enc );
111 if( p_image->p_filter ) DeleteFilter( p_image->p_filter );
112
113 picture_fifo_Delete( p_image->outfifo );
114
115 free( p_image );
116 p_image = NULL;
117 }
118
119 /**
120 * Read an image
121 *
122 */
123
ImageQueueVideo(decoder_t * p_dec,picture_t * p_pic)124 static int ImageQueueVideo( decoder_t *p_dec, picture_t *p_pic )
125 {
126 image_handler_t *p_image = p_dec->p_queue_ctx;
127 picture_fifo_Push( p_image->outfifo, p_pic );
128 return 0;
129 }
130
ImageRead(image_handler_t * p_image,block_t * p_block,const video_format_t * p_fmt_in,video_format_t * p_fmt_out)131 static picture_t *ImageRead( image_handler_t *p_image, block_t *p_block,
132 const video_format_t *p_fmt_in,
133 video_format_t *p_fmt_out )
134 {
135 picture_t *p_pic = NULL;
136
137 /* Check if we can reuse the current decoder */
138 if( p_image->p_dec &&
139 p_image->p_dec->fmt_in.i_codec != p_fmt_in->i_chroma )
140 {
141 DeleteDecoder( p_image->p_dec );
142 p_image->p_dec = 0;
143 }
144
145 /* Start a decoder */
146 if( !p_image->p_dec )
147 {
148 p_image->p_dec = CreateDecoder( p_image->p_parent, p_fmt_in );
149 if( !p_image->p_dec )
150 {
151 block_Release(p_block);
152 return NULL;
153 }
154 if( p_image->p_dec->fmt_out.i_cat != VIDEO_ES )
155 {
156 DeleteDecoder( p_image->p_dec );
157 p_image->p_dec = NULL;
158 block_Release(p_block);
159 return NULL;
160 }
161 p_image->p_dec->pf_queue_video = ImageQueueVideo;
162 p_image->p_dec->p_queue_ctx = p_image;
163 }
164
165 p_block->i_pts = p_block->i_dts = mdate();
166 int ret = p_image->p_dec->pf_decode( p_image->p_dec, p_block );
167 if( ret == VLCDEC_SUCCESS )
168 {
169 /* Drain */
170 p_image->p_dec->pf_decode( p_image->p_dec, NULL );
171
172 p_pic = picture_fifo_Pop( p_image->outfifo );
173
174 unsigned lostcount = 0;
175 picture_t *lostpic;
176 while( ( lostpic = picture_fifo_Pop( p_image->outfifo ) ) != NULL )
177 {
178 picture_Release( lostpic );
179 lostcount++;
180 }
181 if( lostcount > 0 )
182 msg_Warn( p_image->p_parent, "Image decoder output more than one "
183 "picture (%d)", lostcount );
184 }
185
186 if( p_pic == NULL )
187 {
188 msg_Warn( p_image->p_parent, "no image decoded" );
189 return 0;
190 }
191
192 if( !p_fmt_out->i_chroma )
193 p_fmt_out->i_chroma = p_image->p_dec->fmt_out.video.i_chroma;
194 if( !p_fmt_out->i_width && p_fmt_out->i_height )
195 p_fmt_out->i_width = (int64_t)p_image->p_dec->fmt_out.video.i_width *
196 p_image->p_dec->fmt_out.video.i_sar_num *
197 p_fmt_out->i_height /
198 p_image->p_dec->fmt_out.video.i_height /
199 p_image->p_dec->fmt_out.video.i_sar_den;
200
201 if( !p_fmt_out->i_height && p_fmt_out->i_width )
202 p_fmt_out->i_height = (int64_t)p_image->p_dec->fmt_out.video.i_height *
203 p_image->p_dec->fmt_out.video.i_sar_den *
204 p_fmt_out->i_width /
205 p_image->p_dec->fmt_out.video.i_width /
206 p_image->p_dec->fmt_out.video.i_sar_num;
207 if( !p_fmt_out->i_width )
208 p_fmt_out->i_width = p_image->p_dec->fmt_out.video.i_width;
209 if( !p_fmt_out->i_height )
210 p_fmt_out->i_height = p_image->p_dec->fmt_out.video.i_height;
211 if( !p_fmt_out->i_visible_width )
212 p_fmt_out->i_visible_width = p_fmt_out->i_width;
213 if( !p_fmt_out->i_visible_height )
214 p_fmt_out->i_visible_height = p_fmt_out->i_height;
215 if( p_fmt_out->transfer == TRANSFER_FUNC_UNDEF )
216 p_fmt_out->transfer = p_image->p_dec->fmt_out.video.transfer;
217 if( p_fmt_out->primaries == COLOR_PRIMARIES_UNDEF )
218 p_fmt_out->primaries = p_image->p_dec->fmt_out.video.primaries;
219 if( p_fmt_out->space == COLOR_SPACE_UNDEF )
220 p_fmt_out->space = p_image->p_dec->fmt_out.video.space;
221
222 /* Check if we need chroma conversion or resizing */
223 if( p_image->p_dec->fmt_out.video.i_chroma != p_fmt_out->i_chroma ||
224 p_image->p_dec->fmt_out.video.i_width != p_fmt_out->i_width ||
225 p_image->p_dec->fmt_out.video.i_height != p_fmt_out->i_height )
226 {
227 if( p_image->p_filter )
228 if( p_image->p_filter->fmt_in.video.i_chroma !=
229 p_image->p_dec->fmt_out.video.i_chroma ||
230 p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma )
231 {
232 /* We need to restart a new filter */
233 DeleteFilter( p_image->p_filter );
234 p_image->p_filter = 0;
235 }
236
237 /* Start a filter */
238 if( !p_image->p_filter )
239 {
240 p_image->p_filter =
241 CreateFilter( p_image->p_parent, &p_image->p_dec->fmt_out,
242 p_fmt_out );
243
244 if( !p_image->p_filter )
245 {
246 picture_Release( p_pic );
247 return NULL;
248 }
249 }
250 else
251 {
252 /* Filters should handle on-the-fly size changes */
253 p_image->p_filter->fmt_in = p_image->p_dec->fmt_out;
254 p_image->p_filter->fmt_out = p_image->p_dec->fmt_out;
255 p_image->p_filter->fmt_out.i_codec = p_fmt_out->i_chroma;
256 p_image->p_filter->fmt_out.video = *p_fmt_out;
257 }
258
259 p_pic = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
260
261 video_format_Clean( p_fmt_out );
262 video_format_Copy( p_fmt_out, &p_image->p_filter->fmt_out.video );
263 }
264 else
265 {
266 video_format_Clean( p_fmt_out );
267 video_format_Copy( p_fmt_out, &p_image->p_dec->fmt_out.video );
268 }
269
270 return p_pic;
271 }
272
ImageReadUrl(image_handler_t * p_image,const char * psz_url,video_format_t * p_fmt_in,video_format_t * p_fmt_out)273 static picture_t *ImageReadUrl( image_handler_t *p_image, const char *psz_url,
274 video_format_t *p_fmt_in,
275 video_format_t *p_fmt_out )
276 {
277 block_t *p_block;
278 picture_t *p_pic;
279 stream_t *p_stream = NULL;
280 uint64_t i_size;
281
282 p_stream = vlc_stream_NewURL( p_image->p_parent, psz_url );
283
284 if( !p_stream )
285 {
286 msg_Dbg( p_image->p_parent, "could not open %s for reading",
287 psz_url );
288 return NULL;
289 }
290
291 if( vlc_stream_GetSize( p_stream, &i_size ) || i_size > SSIZE_MAX )
292 {
293 msg_Dbg( p_image->p_parent, "could not read %s", psz_url );
294 goto error;
295 }
296
297 p_block = vlc_stream_Block( p_stream, i_size );
298 if( p_block == NULL )
299 goto error;
300
301 if( !p_fmt_in->i_chroma )
302 {
303 char *psz_mime = stream_MimeType( p_stream );
304 if( psz_mime != NULL )
305 {
306 p_fmt_in->i_chroma = image_Mime2Fourcc( psz_mime );
307 free( psz_mime );
308 }
309 }
310 vlc_stream_Delete( p_stream );
311
312 if( !p_fmt_in->i_chroma )
313 {
314 /* Try to guess format from file name */
315 p_fmt_in->i_chroma = image_Ext2Fourcc( psz_url );
316 }
317
318 p_pic = ImageRead( p_image, p_block, p_fmt_in, p_fmt_out );
319
320 return p_pic;
321 error:
322 vlc_stream_Delete( p_stream );
323 return NULL;
324 }
325
326 /* FIXME: refactor by splitting video_format_IsSimilar() API */
BitMapFormatIsSimilar(const video_format_t * f1,const video_format_t * f2)327 static bool BitMapFormatIsSimilar( const video_format_t *f1,
328 const video_format_t *f2 )
329 {
330 if( f1->i_chroma == VLC_CODEC_RGB15 ||
331 f1->i_chroma == VLC_CODEC_RGB16 ||
332 f1->i_chroma == VLC_CODEC_RGB24 ||
333 f1->i_chroma == VLC_CODEC_RGB32 )
334 {
335 video_format_t v1 = *f1;
336 video_format_t v2 = *f2;
337
338 video_format_FixRgb( &v1 );
339 video_format_FixRgb( &v2 );
340
341 if( v1.i_rmask != v2.i_rmask ||
342 v1.i_gmask != v2.i_gmask ||
343 v1.i_bmask != v2.i_bmask )
344 return false;
345 }
346 return true;
347 }
348
349 /**
350 * Write an image
351 *
352 */
353
ImageWrite(image_handler_t * p_image,picture_t * p_pic,const video_format_t * p_fmt_in,const video_format_t * p_fmt_out)354 static block_t *ImageWrite( image_handler_t *p_image, picture_t *p_pic,
355 const video_format_t *p_fmt_in,
356 const video_format_t *p_fmt_out )
357 {
358 block_t *p_block;
359
360 /* Check if we can reuse the current encoder */
361 if( p_image->p_enc &&
362 ( p_image->p_enc->fmt_out.i_codec != p_fmt_out->i_chroma ||
363 p_image->p_enc->fmt_out.video.i_width != p_fmt_out->i_width ||
364 p_image->p_enc->fmt_out.video.i_height != p_fmt_out->i_height ) )
365 {
366 DeleteEncoder( p_image->p_enc );
367 p_image->p_enc = 0;
368 }
369
370 /* Start an encoder */
371 if( !p_image->p_enc )
372 {
373 p_image->p_enc = CreateEncoder( p_image->p_parent,
374 p_fmt_in, p_fmt_out );
375 if( !p_image->p_enc ) return NULL;
376 }
377
378 /* Check if we need chroma conversion or resizing */
379 if( p_image->p_enc->fmt_in.video.i_chroma != p_fmt_in->i_chroma ||
380 p_image->p_enc->fmt_in.video.i_width != p_fmt_in->i_width ||
381 p_image->p_enc->fmt_in.video.i_height != p_fmt_in->i_height ||
382 !BitMapFormatIsSimilar( &p_image->p_enc->fmt_in.video, p_fmt_in ) )
383 {
384 picture_t *p_tmp_pic;
385
386 if( p_image->p_filter )
387 if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma ||
388 p_image->p_filter->fmt_out.video.i_chroma !=
389 p_image->p_enc->fmt_in.video.i_chroma ||
390 !BitMapFormatIsSimilar( &p_image->p_filter->fmt_in.video, p_fmt_in ) )
391 {
392 /* We need to restart a new filter */
393 DeleteFilter( p_image->p_filter );
394 p_image->p_filter = 0;
395 }
396
397 /* Start a filter */
398 if( !p_image->p_filter )
399 {
400 es_format_t fmt_in;
401 es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma );
402 fmt_in.video = *p_fmt_in;
403
404 p_image->p_filter =
405 CreateFilter( p_image->p_parent, &fmt_in,
406 &p_image->p_enc->fmt_in.video );
407
408 if( !p_image->p_filter )
409 {
410 return NULL;
411 }
412 }
413 else
414 {
415 /* Filters should handle on-the-fly size changes */
416 p_image->p_filter->fmt_in.i_codec = p_fmt_in->i_chroma;
417 p_image->p_filter->fmt_out.video = *p_fmt_in;
418 p_image->p_filter->fmt_out.i_codec =p_image->p_enc->fmt_in.i_codec;
419 p_image->p_filter->fmt_out.video = p_image->p_enc->fmt_in.video;
420 }
421
422 picture_Hold( p_pic );
423
424 p_tmp_pic =
425 p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
426
427 if( likely(p_tmp_pic != NULL) )
428 {
429 p_block = p_image->p_enc->pf_encode_video( p_image->p_enc,
430 p_tmp_pic );
431 picture_Release( p_tmp_pic );
432 }
433 else
434 p_block = NULL;
435 }
436 else
437 {
438 p_block = p_image->p_enc->pf_encode_video( p_image->p_enc, p_pic );
439 }
440
441 if( !p_block )
442 {
443 msg_Dbg( p_image->p_parent, "no image encoded" );
444 return 0;
445 }
446
447 return p_block;
448 }
449
ImageWriteUrl(image_handler_t * p_image,picture_t * p_pic,const video_format_t * p_fmt_in,video_format_t * p_fmt_out,const char * psz_url)450 static int ImageWriteUrl( image_handler_t *p_image, picture_t *p_pic,
451 const video_format_t *p_fmt_in, video_format_t *p_fmt_out,
452 const char *psz_url )
453 {
454 block_t *p_block;
455 FILE *file;
456
457 if( !p_fmt_out->i_chroma )
458 {
459 /* Try to guess format from file name */
460 p_fmt_out->i_chroma = image_Ext2Fourcc( psz_url );
461 }
462
463 file = vlc_fopen( psz_url, "wb" );
464 if( !file )
465 {
466 msg_Err( p_image->p_parent, "%s: %s", psz_url, vlc_strerror_c(errno) );
467 return VLC_EGENERIC;
468 }
469
470 p_block = ImageWrite( p_image, p_pic, p_fmt_in, p_fmt_out );
471
472 int err = 0;
473 if( p_block )
474 {
475 if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, file ) != 1 )
476 err = errno;
477 block_Release( p_block );
478 }
479
480 if( fclose( file ) && !err )
481 err = errno;
482
483 if( err )
484 {
485 errno = err;
486 msg_Err( p_image->p_parent, "%s: %s", psz_url, vlc_strerror_c(errno) );
487 }
488
489 return err ? VLC_EGENERIC : VLC_SUCCESS;
490 }
491
492 /**
493 * Convert an image to a different format
494 *
495 */
496
ImageConvert(image_handler_t * p_image,picture_t * p_pic,const video_format_t * p_fmt_in,video_format_t * p_fmt_out)497 static picture_t *ImageConvert( image_handler_t *p_image, picture_t *p_pic,
498 const video_format_t *p_fmt_in,
499 video_format_t *p_fmt_out )
500 {
501 picture_t *p_pif;
502
503 if( !p_fmt_out->i_width && !p_fmt_out->i_height &&
504 p_fmt_out->i_sar_num && p_fmt_out->i_sar_den &&
505 p_fmt_out->i_sar_num * p_fmt_in->i_sar_den !=
506 p_fmt_out->i_sar_den * p_fmt_in->i_sar_num )
507 {
508 p_fmt_out->i_width =
509 p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den *
510 p_fmt_in->i_width / p_fmt_in->i_sar_den / p_fmt_out->i_sar_num;
511 p_fmt_out->i_visible_width =
512 p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den *
513 p_fmt_in->i_visible_width / p_fmt_in->i_sar_den /
514 p_fmt_out->i_sar_num;
515 }
516
517 if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_fmt_in->i_chroma;
518 if( !p_fmt_out->i_width )
519 p_fmt_out->i_width = p_fmt_out->i_visible_width = p_fmt_in->i_width;
520 if( !p_fmt_out->i_height )
521 p_fmt_out->i_height = p_fmt_out->i_visible_height = p_fmt_in->i_height;
522 if( !p_fmt_out->i_sar_num ) p_fmt_out->i_sar_num = p_fmt_in->i_sar_num;
523 if( !p_fmt_out->i_sar_den ) p_fmt_out->i_sar_den = p_fmt_in->i_sar_den;
524
525 if( p_image->p_filter )
526 if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma ||
527 p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma )
528 {
529 /* We need to restart a new filter */
530 DeleteFilter( p_image->p_filter );
531 p_image->p_filter = NULL;
532 }
533
534 /* Start a filter */
535 if( !p_image->p_filter )
536 {
537 es_format_t fmt_in;
538 es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma );
539 fmt_in.video = *p_fmt_in;
540
541 p_image->p_filter =
542 CreateFilter( p_image->p_parent, &fmt_in, p_fmt_out );
543
544 if( !p_image->p_filter )
545 {
546 return NULL;
547 }
548 }
549 else
550 {
551 /* Filters should handle on-the-fly size changes */
552 p_image->p_filter->fmt_in.video = *p_fmt_in;
553 p_image->p_filter->fmt_out.video = *p_fmt_out;
554 }
555
556 picture_Hold( p_pic );
557
558 p_pif = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
559
560 if( p_fmt_in->i_chroma == p_fmt_out->i_chroma &&
561 p_fmt_in->i_width == p_fmt_out->i_width &&
562 p_fmt_in->i_height == p_fmt_out->i_height )
563 {
564 /* Duplicate image */
565 picture_Release( p_pif ); /* XXX: Better fix must be possible */
566 p_pif = filter_NewPicture( p_image->p_filter );
567 if( p_pif )
568 picture_Copy( p_pif, p_pic );
569 }
570
571 return p_pif;
572 }
573
574 /**
575 * Misc functions
576 *
577 */
578 static const struct
579 {
580 vlc_fourcc_t i_codec;
581 const char psz_ext[7];
582
583 } ext_table[] =
584 {
585 { VLC_CODEC_JPEG, "jpeg" },
586 { VLC_CODEC_JPEG, "jpg" },
587 { VLC_CODEC_JPEGLS, "ljpg" },
588 { VLC_CODEC_BPG, "bpg" },
589 { VLC_CODEC_PNG, "png" },
590 { VLC_CODEC_PGM, "pgm" },
591 { VLC_CODEC_PGMYUV, "pgmyuv" },
592 { VLC_FOURCC('p','b','m',' '), "pbm" },
593 { VLC_FOURCC('p','a','m',' '), "pam" },
594 { VLC_CODEC_TARGA, "tga" },
595 { VLC_CODEC_BMP, "bmp" },
596 { VLC_CODEC_PNM, "pnm" },
597 { VLC_FOURCC('x','p','m',' '), "xpm" },
598 { VLC_FOURCC('x','c','f',' '), "xcf" },
599 { VLC_CODEC_PCX, "pcx" },
600 { VLC_CODEC_GIF, "gif" },
601 { VLC_CODEC_SVG, "svg" },
602 { VLC_CODEC_TIFF, "tif" },
603 { VLC_CODEC_TIFF, "tiff" },
604 { VLC_FOURCC('l','b','m',' '), "lbm" },
605 { VLC_CODEC_PPM, "ppm" },
606 };
607
image_Type2Fourcc(const char * psz_type)608 vlc_fourcc_t image_Type2Fourcc( const char *psz_type )
609 {
610 for( unsigned i = 0; i < ARRAY_SIZE(ext_table); i++ )
611 if( !strcasecmp( ext_table[i].psz_ext, psz_type ) )
612 return ext_table[i].i_codec;
613
614 return 0;
615 }
616
image_Ext2Fourcc(const char * psz_name)617 vlc_fourcc_t image_Ext2Fourcc( const char *psz_name )
618 {
619 psz_name = strrchr( psz_name, '.' );
620 if( !psz_name ) return 0;
621 psz_name++;
622
623 return image_Type2Fourcc( psz_name );
624 }
625
626 static const struct
627 {
628 vlc_fourcc_t i_codec;
629 const char *psz_mime;
630 } mime_table[] =
631 {
632 { VLC_CODEC_BMP, "image/bmp" },
633 { VLC_CODEC_BMP, "image/x-bmp" },
634 { VLC_CODEC_BMP, "image/x-bitmap" },
635 { VLC_CODEC_BMP, "image/x-ms-bmp" },
636 { VLC_CODEC_PNM, "image/x-portable-anymap" },
637 { VLC_CODEC_PNM, "image/x-portable-bitmap" },
638 { VLC_CODEC_PNM, "image/x-portable-graymap" },
639 { VLC_CODEC_PNM, "image/x-portable-pixmap" },
640 { VLC_CODEC_GIF, "image/gif" },
641 { VLC_CODEC_JPEG, "image/jpeg" },
642 { VLC_CODEC_BPG, "image/bpg" },
643 { VLC_CODEC_PCX, "image/pcx" },
644 { VLC_CODEC_PNG, "image/png" },
645 { VLC_CODEC_SVG, "image/svg+xml" },
646 { VLC_CODEC_TIFF, "image/tiff" },
647 { VLC_CODEC_TARGA, "image/x-tga" },
648 { VLC_FOURCC('x','p','m',' '), "image/x-xpixmap" },
649 { 0, NULL }
650 };
651
image_Mime2Fourcc(const char * psz_mime)652 vlc_fourcc_t image_Mime2Fourcc( const char *psz_mime )
653 {
654 for( int i = 0; mime_table[i].i_codec; i++ )
655 if( !strcmp( psz_mime, mime_table[i].psz_mime ) )
656 return mime_table[i].i_codec;
657 return 0;
658 }
659
video_update_format(decoder_t * p_dec)660 static int video_update_format( decoder_t *p_dec )
661 {
662 p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
663 return 0;
664 }
665
video_new_buffer(decoder_t * p_dec)666 static picture_t *video_new_buffer( decoder_t *p_dec )
667 {
668 return picture_NewFromFormat( &p_dec->fmt_out.video );
669 }
670
CreateDecoder(vlc_object_t * p_this,const video_format_t * fmt)671 static decoder_t *CreateDecoder( vlc_object_t *p_this, const video_format_t *fmt )
672 {
673 decoder_t *p_dec;
674
675 p_dec = vlc_custom_create( p_this, sizeof( *p_dec ), "image decoder" );
676 if( p_dec == NULL )
677 return NULL;
678
679 p_dec->p_module = NULL;
680 es_format_InitFromVideo( &p_dec->fmt_in, fmt );
681 es_format_Init( &p_dec->fmt_out, VIDEO_ES, 0 );
682 p_dec->b_frame_drop_allowed = false;
683
684 p_dec->pf_vout_format_update = video_update_format;
685 p_dec->pf_vout_buffer_new = video_new_buffer;
686
687 /* Find a suitable decoder module */
688 p_dec->p_module = module_need( p_dec, "video decoder", "$codec", false );
689 if( !p_dec->p_module )
690 {
691 msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'. "
692 "VLC probably does not support this image format.",
693 (char*)&p_dec->fmt_in.i_codec );
694
695 DeleteDecoder( p_dec );
696 return NULL;
697 }
698
699 return p_dec;
700 }
701
DeleteDecoder(decoder_t * p_dec)702 static void DeleteDecoder( decoder_t * p_dec )
703 {
704 if( p_dec->p_module ) module_unneed( p_dec, p_dec->p_module );
705
706 es_format_Clean( &p_dec->fmt_in );
707 es_format_Clean( &p_dec->fmt_out );
708
709 if( p_dec->p_description )
710 vlc_meta_Delete( p_dec->p_description );
711
712 vlc_object_release( p_dec );
713 p_dec = NULL;
714 }
715
CreateEncoder(vlc_object_t * p_this,const video_format_t * fmt_in,const video_format_t * fmt_out)716 static encoder_t *CreateEncoder( vlc_object_t *p_this, const video_format_t *fmt_in,
717 const video_format_t *fmt_out )
718 {
719 encoder_t *p_enc;
720
721 p_enc = sout_EncoderCreate( p_this );
722 if( p_enc == NULL )
723 return NULL;
724
725 p_enc->p_module = NULL;
726 es_format_InitFromVideo( &p_enc->fmt_in, fmt_in );
727
728 if( p_enc->fmt_in.video.i_visible_width == 0 ||
729 p_enc->fmt_in.video.i_visible_height == 0 ||
730 p_enc->fmt_out.video.i_visible_width == 0 ||
731 p_enc->fmt_out.video.i_visible_height == 0 )
732 {
733 if( fmt_out->i_width > 0 && fmt_out->i_height > 0 )
734 {
735 p_enc->fmt_in.video.i_width = fmt_out->i_width;
736 p_enc->fmt_in.video.i_height = fmt_out->i_height;
737
738 if( fmt_out->i_visible_width > 0 &&
739 fmt_out->i_visible_height > 0 )
740 {
741 p_enc->fmt_in.video.i_visible_width = fmt_out->i_visible_width;
742 p_enc->fmt_in.video.i_visible_height = fmt_out->i_visible_height;
743 }
744 else
745 {
746 p_enc->fmt_in.video.i_visible_width = fmt_out->i_width;
747 p_enc->fmt_in.video.i_visible_height = fmt_out->i_height;
748 }
749 }
750 } else if( fmt_out->i_sar_num && fmt_out->i_sar_den &&
751 fmt_out->i_sar_num * fmt_in->i_sar_den !=
752 fmt_out->i_sar_den * fmt_in->i_sar_num )
753 {
754 p_enc->fmt_in.video.i_width =
755 fmt_in->i_sar_num * (int64_t)fmt_out->i_sar_den * fmt_in->i_width /
756 fmt_in->i_sar_den / fmt_out->i_sar_num;
757 p_enc->fmt_in.video.i_visible_width =
758 fmt_in->i_sar_num * (int64_t)fmt_out->i_sar_den *
759 fmt_in->i_visible_width / fmt_in->i_sar_den / fmt_out->i_sar_num;
760 }
761
762 p_enc->fmt_in.video.i_frame_rate = 25;
763 p_enc->fmt_in.video.i_frame_rate_base = 1;
764
765 es_format_InitFromVideo( &p_enc->fmt_out, fmt_out );
766 p_enc->fmt_out.video.i_width = p_enc->fmt_in.video.i_width;
767 p_enc->fmt_out.video.i_height = p_enc->fmt_in.video.i_height;
768
769 /* Find a suitable decoder module */
770 p_enc->p_module = module_need( p_enc, "encoder", NULL, false );
771 if( !p_enc->p_module )
772 {
773 msg_Err( p_enc, "no suitable encoder module for fourcc `%4.4s'.\n"
774 "VLC probably does not support this image format.",
775 (char*)&p_enc->fmt_out.i_codec );
776
777 DeleteEncoder( p_enc );
778 return NULL;
779 }
780 p_enc->fmt_in.video.i_chroma = p_enc->fmt_in.i_codec;
781
782 return p_enc;
783 }
784
DeleteEncoder(encoder_t * p_enc)785 static void DeleteEncoder( encoder_t * p_enc )
786 {
787 if( p_enc->p_module ) module_unneed( p_enc, p_enc->p_module );
788
789 es_format_Clean( &p_enc->fmt_in );
790 es_format_Clean( &p_enc->fmt_out );
791
792 vlc_object_release( p_enc );
793 p_enc = NULL;
794 }
795
filter_new_picture(filter_t * p_filter)796 static picture_t *filter_new_picture( filter_t *p_filter )
797 {
798 return picture_NewFromFormat( &p_filter->fmt_out.video );
799 }
800
CreateFilter(vlc_object_t * p_this,const es_format_t * p_fmt_in,const video_format_t * p_fmt_out)801 static filter_t *CreateFilter( vlc_object_t *p_this, const es_format_t *p_fmt_in,
802 const video_format_t *p_fmt_out )
803 {
804 filter_t *p_filter;
805
806 p_filter = vlc_custom_create( p_this, sizeof(filter_t), "filter" );
807 p_filter->owner.video.buffer_new = filter_new_picture;
808
809 es_format_Copy( &p_filter->fmt_in, p_fmt_in );
810 es_format_Copy( &p_filter->fmt_out, p_fmt_in );
811 video_format_Copy( &p_filter->fmt_out.video, p_fmt_out );
812
813 /* whatever the input offset, write at offset 0 in the target image */
814 p_filter->fmt_out.video.i_x_offset = 0;
815 p_filter->fmt_out.video.i_y_offset = 0;
816
817 p_filter->fmt_out.i_codec = p_fmt_out->i_chroma;
818 p_filter->p_module = module_need( p_filter, "video converter", NULL, false );
819
820 if( !p_filter->p_module )
821 {
822 msg_Dbg( p_filter, "no video converter found" );
823 DeleteFilter( p_filter );
824 return NULL;
825 }
826
827 return p_filter;
828 }
829
DeleteFilter(filter_t * p_filter)830 static void DeleteFilter( filter_t * p_filter )
831 {
832 if( p_filter->p_module ) module_unneed( p_filter, p_filter->p_module );
833
834 es_format_Clean( &p_filter->fmt_in );
835 es_format_Clean( &p_filter->fmt_out );
836
837 vlc_object_release( p_filter );
838 }
839