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