image.c 24.7 KB
Newer Older
1
2
3
/*****************************************************************************
 * image.c : wrapper for image reading/writing facilities
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2004-2007 VLC authors and VideoLAN
5
6
7
8
 * $Id$
 *
 * Author: Gildas Bazin <gbazin@videolan.org>
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
9
10
11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12
13
14
15
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
16
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
19
20
21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
27
28
29
30
31
 *****************************************************************************/

/**
 * \file
 * This file contains the functions to handle the image_handler_t type
 */

/*****************************************************************************
 * Preamble
 *****************************************************************************/
32

33
34
35
36
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

37
#include <errno.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38
#include <limits.h>
39

40
#include <vlc_common.h>
zorglub's avatar
zorglub committed
41
#include <vlc_codec.h>
42
#include <vlc_meta.h>
43
#include <vlc_filter.h>
zorglub's avatar
zorglub committed
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
53
54
55
56
57
58
59
60

static picture_t *ImageRead( image_handler_t *, block_t *,
                             video_format_t *, video_format_t * );
static picture_t *ImageReadUrl( image_handler_t *, const char *,
                                video_format_t *, video_format_t * );
static block_t *ImageWrite( image_handler_t *, picture_t *,
                            video_format_t *, video_format_t * );
static int ImageWriteUrl( image_handler_t *, picture_t *,
                          video_format_t *, video_format_t *, const char * );

61
62
63
static picture_t *ImageConvert( image_handler_t *, picture_t *,
                                video_format_t *, video_format_t * );

64
65
static decoder_t *CreateDecoder( vlc_object_t *, video_format_t * );
static void DeleteDecoder( decoder_t * );
66
67
68
static encoder_t *CreateEncoder( vlc_object_t *, video_format_t *,
                                 video_format_t * );
static void DeleteEncoder( encoder_t * );
69
static filter_t *CreateFilter( vlc_object_t *, es_format_t *,
70
                               video_format_t * );
71
72
static void DeleteFilter( filter_t * );

73
vlc_fourcc_t image_Type2Fourcc( const char * );
74
vlc_fourcc_t image_Ext2Fourcc( const char * );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
75
/*static const char *Fourcc2Ext( vlc_fourcc_t );*/
76

77
#undef image_HandlerCreate
78
79
80
81
/**
 * Create an image_handler_t instance
 *
 */
82
image_handler_t *image_HandlerCreate( vlc_object_t *p_this )
83
{
ivoire's avatar
ivoire committed
84
85
86
    image_handler_t *p_image = calloc( 1, sizeof(image_handler_t) );
    if( !p_image )
        return NULL;
87
88
89
90
91
92
93

    p_image->p_parent = p_this;

    p_image->pf_read = ImageRead;
    p_image->pf_read_url = ImageReadUrl;
    p_image->pf_write = ImageWrite;
    p_image->pf_write_url = ImageWriteUrl;
94
    p_image->pf_convert = ImageConvert;
95

96
97
    p_image->outfifo = picture_fifo_New();

98
99
100
101
102
103
104
105
106
107
108
109
    return p_image;
}

/**
 * Delete the image_handler_t instance
 *
 */
void image_HandlerDelete( image_handler_t *p_image )
{
    if( !p_image ) return;

    if( p_image->p_dec ) DeleteDecoder( p_image->p_dec );
110
    if( p_image->p_enc ) DeleteEncoder( p_image->p_enc );
111
112
    if( p_image->p_filter ) DeleteFilter( p_image->p_filter );

113
114
    picture_fifo_Delete( p_image->outfifo );

115
    free( p_image );
116
    p_image = NULL;
117
118
119
120
121
122
123
}

/**
 * Read an image
 *
 */

124
static int ImageQueueVideo( decoder_t *p_dec, picture_t *p_pic )
125
126
127
128
129
130
{
    image_handler_t *p_image = p_dec->p_queue_ctx;
    picture_fifo_Push( p_image->outfifo, p_pic );
    return 0;
}

131
132
133
134
static picture_t *ImageRead( image_handler_t *p_image, block_t *p_block,
                             video_format_t *p_fmt_in,
                             video_format_t *p_fmt_out )
{
135
    picture_t *p_pic = NULL;
136
137
138
139
140
141
142
143
144
145
146
147
148

    /* Check if we can reuse the current decoder */
    if( p_image->p_dec &&
        p_image->p_dec->fmt_in.i_codec != p_fmt_in->i_chroma )
    {
        DeleteDecoder( p_image->p_dec );
        p_image->p_dec = 0;
    }

    /* Start a decoder */
    if( !p_image->p_dec )
    {
        p_image->p_dec = CreateDecoder( p_image->p_parent, p_fmt_in );
149
150
151
152
153
        if( !p_image->p_dec )
        {
            block_Release(p_block);
            return NULL;
        }
154
155
156
157
158
159
160
161
162
        if( p_image->p_dec->fmt_out.i_cat != VIDEO_ES )
        {
            DeleteDecoder( p_image->p_dec );
            p_image->p_dec = NULL;
            block_Release(p_block);
            return NULL;
        }
        p_image->p_dec->pf_queue_video = ImageQueueVideo;
        p_image->p_dec->p_queue_ctx = p_image;
163
164
165
    }

    p_block->i_pts = p_block->i_dts = mdate();
166
167
    int ret = p_image->p_dec->pf_decode( p_image->p_dec, p_block );
    if( ret == VLCDEC_SUCCESS )
168
    {
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
        /* Drain */
        p_image->p_dec->pf_decode( p_image->p_dec, NULL );

        p_pic = picture_fifo_Pop( p_image->outfifo );

        unsigned lostcount = 0;
        picture_t *lostpic;
        while( ( lostpic = picture_fifo_Pop( p_image->outfifo ) ) != NULL )
        {
            picture_Release( lostpic );
            lostcount++;
        }
        if( lostcount > 0 )
            msg_Warn( p_image->p_parent, "Image decoder output more than one "
                      "picture (%d)", lostcount );
184
    }
185

186
    if( p_pic == NULL )
187
    {
188
        msg_Warn( p_image->p_parent, "no image decoded" );
189
190
191
192
193
        return 0;
    }

    if( !p_fmt_out->i_chroma )
        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 )
Laurent Aimar's avatar
Laurent Aimar committed
195
196
197
198
199
200
        p_fmt_out->i_width = (int64_t)p_image->p_dec->fmt_out.video.i_width *
                             p_image->p_dec->fmt_out.video.i_sar_num *
                             p_fmt_out->i_height /
                             p_image->p_dec->fmt_out.video.i_height /
                             p_image->p_dec->fmt_out.video.i_sar_den;

201
    if( !p_fmt_out->i_height && p_fmt_out->i_width )
Laurent Aimar's avatar
Laurent Aimar committed
202
203
204
205
206
        p_fmt_out->i_height = (int64_t)p_image->p_dec->fmt_out.video.i_height *
                              p_image->p_dec->fmt_out.video.i_sar_den *
                              p_fmt_out->i_width /
                              p_image->p_dec->fmt_out.video.i_width /
                              p_image->p_dec->fmt_out.video.i_sar_num;
207
208
209
210
    if( !p_fmt_out->i_width )
        p_fmt_out->i_width = p_image->p_dec->fmt_out.video.i_width;
    if( !p_fmt_out->i_height )
        p_fmt_out->i_height = p_image->p_dec->fmt_out.video.i_height;
211
212
213
214
    if( !p_fmt_out->i_visible_width )
        p_fmt_out->i_visible_width = p_fmt_out->i_width;
    if( !p_fmt_out->i_visible_height )
        p_fmt_out->i_visible_height = p_fmt_out->i_height;
215
216
217
218
219
220
221
222
223

    /* Check if we need chroma conversion or resizing */
    if( p_image->p_dec->fmt_out.video.i_chroma != p_fmt_out->i_chroma ||
        p_image->p_dec->fmt_out.video.i_width != p_fmt_out->i_width ||
        p_image->p_dec->fmt_out.video.i_height != p_fmt_out->i_height )
    {
        if( p_image->p_filter )
        if( p_image->p_filter->fmt_in.video.i_chroma !=
            p_image->p_dec->fmt_out.video.i_chroma ||
224
            p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma )
225
226
227
228
229
230
231
232
233
234
235
        {
            /* We need to restart a new filter */
            DeleteFilter( p_image->p_filter );
            p_image->p_filter = 0;
        }

        /* Start a filter */
        if( !p_image->p_filter )
        {
            p_image->p_filter =
                CreateFilter( p_image->p_parent, &p_image->p_dec->fmt_out,
236
                              p_fmt_out );
237
238
239

            if( !p_image->p_filter )
            {
240
                picture_Release( p_pic );
241
242
243
                return NULL;
            }
        }
244
245
246
247
248
249
250
251
        else
        {
            /* Filters should handle on-the-fly size changes */
            p_image->p_filter->fmt_in = p_image->p_dec->fmt_out;
            p_image->p_filter->fmt_out = p_image->p_dec->fmt_out;
            p_image->p_filter->fmt_out.i_codec = p_fmt_out->i_chroma;
            p_image->p_filter->fmt_out.video = *p_fmt_out;
        }
252
253

        p_pic = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
254
255
256
257
258
259
260
261

        video_format_Clean( p_fmt_out );
        video_format_Copy( p_fmt_out, &p_image->p_filter->fmt_out.video );
    }
    else
    {
        video_format_Clean( p_fmt_out );
        video_format_Copy( p_fmt_out, &p_image->p_dec->fmt_out.video );
262
263
264
265
266
267
268
269
270
271
272
    }

    return p_pic;
}

static picture_t *ImageReadUrl( image_handler_t *p_image, const char *psz_url,
                                video_format_t *p_fmt_in,
                                video_format_t *p_fmt_out )
{
    block_t *p_block;
    picture_t *p_pic;
273
    stream_t *p_stream = NULL;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
274
    uint64_t i_size;
275

276
    p_stream = vlc_stream_NewURL( p_image->p_parent, psz_url );
277

278
    if( !p_stream )
279
    {
280
281
282
        msg_Dbg( p_image->p_parent, "could not open %s for reading",
                 psz_url );
        return NULL;
283
284
    }

285
    if( vlc_stream_GetSize( p_stream, &i_size ) || i_size > SSIZE_MAX )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
286
287
288
289
    {
        msg_Dbg( p_image->p_parent, "could not read %s", psz_url );
        goto error;
    }
290

291
    p_block = vlc_stream_Block( p_stream, i_size );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
292
293
    if( p_block == NULL )
        goto error;
294
295
296

    if( !p_fmt_in->i_chroma )
    {
297
        char *psz_mime = stream_MimeType( p_stream );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
298
299
        if( psz_mime != NULL )
        {
300
            p_fmt_in->i_chroma = image_Mime2Fourcc( psz_mime );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301
302
            free( psz_mime );
        }
303
    }
304
    vlc_stream_Delete( p_stream );
305

306
307
308
    if( !p_fmt_in->i_chroma )
    {
        /* Try to guess format from file name */
309
        p_fmt_in->i_chroma = image_Ext2Fourcc( psz_url );
310
311
    }

312
313
314
    p_pic = ImageRead( p_image, p_block, p_fmt_in, p_fmt_out );

    return p_pic;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
315
error:
316
    vlc_stream_Delete( p_stream );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
317
    return NULL;
318
319
320
321
322
323
324
325
326
327
328
}

/**
 * Write an image
 *
 */

static block_t *ImageWrite( image_handler_t *p_image, picture_t *p_pic,
                            video_format_t *p_fmt_in,
                            video_format_t *p_fmt_out )
{
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
    block_t *p_block;

    /* Check if we can reuse the current encoder */
    if( p_image->p_enc &&
        ( p_image->p_enc->fmt_out.i_codec != p_fmt_out->i_chroma ||
          p_image->p_enc->fmt_out.video.i_width != p_fmt_out->i_width ||
          p_image->p_enc->fmt_out.video.i_height != p_fmt_out->i_height ) )
    {
        DeleteEncoder( p_image->p_enc );
        p_image->p_enc = 0;
    }

    /* Start an encoder */
    if( !p_image->p_enc )
    {
        p_image->p_enc = CreateEncoder( p_image->p_parent,
                                        p_fmt_in, p_fmt_out );
        if( !p_image->p_enc ) return NULL;
    }

    /* Check if we need chroma conversion or resizing */
    if( p_image->p_enc->fmt_in.video.i_chroma != p_fmt_in->i_chroma ||
        p_image->p_enc->fmt_in.video.i_width != p_fmt_in->i_width ||
        p_image->p_enc->fmt_in.video.i_height != p_fmt_in->i_height )
    {
354
        picture_t *p_tmp_pic;
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374

        if( p_image->p_filter )
        if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma ||
            p_image->p_filter->fmt_out.video.i_chroma !=
            p_image->p_enc->fmt_in.video.i_chroma )
        {
            /* We need to restart a new filter */
            DeleteFilter( p_image->p_filter );
            p_image->p_filter = 0;
        }

        /* Start a filter */
        if( !p_image->p_filter )
        {
            es_format_t fmt_in;
            es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma );
            fmt_in.video = *p_fmt_in;

            p_image->p_filter =
                CreateFilter( p_image->p_parent, &fmt_in,
375
                              &p_image->p_enc->fmt_in.video );
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

            if( !p_image->p_filter )
            {
                return NULL;
            }
        }
        else
        {
            /* Filters should handle on-the-fly size changes */
            p_image->p_filter->fmt_in.i_codec = p_fmt_in->i_chroma;
            p_image->p_filter->fmt_out.video = *p_fmt_in;
            p_image->p_filter->fmt_out.i_codec =p_image->p_enc->fmt_in.i_codec;
            p_image->p_filter->fmt_out.video = p_image->p_enc->fmt_in.video;
        }

dionoea's avatar
dionoea committed
391
        picture_Hold( p_pic );
392

393
394
        p_tmp_pic =
            p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
395

396
397
398
399
        if( likely(p_tmp_pic != NULL) )
        {
            p_block = p_image->p_enc->pf_encode_video( p_image->p_enc,
                                                       p_tmp_pic );
400
            picture_Release( p_tmp_pic );
401
402
403
        }
        else
            p_block = NULL;
404
405
406
407
408
    }
    else
    {
        p_block = p_image->p_enc->pf_encode_video( p_image->p_enc, p_pic );
    }
409
410
411
412
413
414
415
416

    if( !p_block )
    {
        msg_Dbg( p_image->p_parent, "no image encoded" );
        return 0;
    }

    return p_block;
417
418
419
420
421
422
}

static int ImageWriteUrl( image_handler_t *p_image, picture_t *p_pic,
                          video_format_t *p_fmt_in, video_format_t *p_fmt_out,
                          const char *psz_url )
{
423
424
425
426
427
428
    block_t *p_block;
    FILE *file;

    if( !p_fmt_out->i_chroma )
    {
        /* Try to guess format from file name */
429
        p_fmt_out->i_chroma = image_Ext2Fourcc( psz_url );
430
431
    }

432
    file = vlc_fopen( psz_url, "wb" );
433
434
    if( !file )
    {
435
        msg_Err( p_image->p_parent, "%s: %s", psz_url, vlc_strerror_c(errno) );
436
437
438
439
440
        return VLC_EGENERIC;
    }

    p_block = ImageWrite( p_image, p_pic, p_fmt_in, p_fmt_out );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
441
    int err = 0;
442
443
    if( p_block )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
444
445
        if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, file ) != 1 )
            err = errno;
446
447
448
        block_Release( p_block );
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
449
450
451
452
    if( fclose( file ) && !err )
        err = errno;

    if( err )
453
454
    {
       errno = err;
455
       msg_Err( p_image->p_parent, "%s: %s", psz_url, vlc_strerror_c(errno) );
456
    }
457

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
458
    return err ? VLC_EGENERIC : VLC_SUCCESS;
459
460
}

461
462
463
464
465
466
467
468
469
470
471
/**
 * Convert an image to a different format
 *
 */

static picture_t *ImageConvert( image_handler_t *p_image, picture_t *p_pic,
                                video_format_t *p_fmt_in,
                                video_format_t *p_fmt_out )
{
    picture_t *p_pif;

472
473
474
475
476
477
478
479
480
481
482
483
484
485
    if( !p_fmt_out->i_width && !p_fmt_out->i_height &&
        p_fmt_out->i_sar_num && p_fmt_out->i_sar_den &&
        p_fmt_out->i_sar_num * p_fmt_in->i_sar_den !=
        p_fmt_out->i_sar_den * p_fmt_in->i_sar_num )
    {
        p_fmt_out->i_width =
            p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den *
            p_fmt_in->i_width / p_fmt_in->i_sar_den / p_fmt_out->i_sar_num;
        p_fmt_out->i_visible_width =
            p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den *
            p_fmt_in->i_visible_width / p_fmt_in->i_sar_den /
            p_fmt_out->i_sar_num;
    }

486
    if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_fmt_in->i_chroma;
487
488
489
490
491
492
    if( !p_fmt_out->i_width )
        p_fmt_out->i_width = p_fmt_out->i_visible_width = p_fmt_in->i_width;
    if( !p_fmt_out->i_height )
        p_fmt_out->i_height = p_fmt_out->i_visible_height = p_fmt_in->i_height;
    if( !p_fmt_out->i_sar_num ) p_fmt_out->i_sar_num = p_fmt_in->i_sar_num;
    if( !p_fmt_out->i_sar_den ) p_fmt_out->i_sar_den = p_fmt_in->i_sar_den;
493
494
495
496
497
498
499

    if( p_image->p_filter )
    if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma ||
        p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma )
    {
        /* We need to restart a new filter */
        DeleteFilter( p_image->p_filter );
500
        p_image->p_filter = NULL;
501
502
503
504
505
506
507
508
509
510
    }

    /* Start a filter */
    if( !p_image->p_filter )
    {
        es_format_t fmt_in;
        es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma );
        fmt_in.video = *p_fmt_in;

        p_image->p_filter =
511
            CreateFilter( p_image->p_parent, &fmt_in, p_fmt_out );
512
513
514
515
516
517
518
519
520
521
522
523
524

        if( !p_image->p_filter )
        {
            return NULL;
        }
    }
    else
    {
        /* Filters should handle on-the-fly size changes */
        p_image->p_filter->fmt_in.video = *p_fmt_in;
        p_image->p_filter->fmt_out.video = *p_fmt_out;
    }

dionoea's avatar
dionoea committed
525
    picture_Hold( p_pic );
526

527
528
529
530
531
532
533
    p_pif = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );

    if( p_fmt_in->i_chroma == p_fmt_out->i_chroma &&
        p_fmt_in->i_width == p_fmt_out->i_width &&
        p_fmt_in->i_height == p_fmt_out->i_height )
    {
        /* Duplicate image */
534
        picture_Release( p_pif ); /* XXX: Better fix must be possible */
535
        p_pif = filter_NewPicture( p_image->p_filter );
536
537
        if( p_pif )
            picture_Copy( p_pif, p_pic );
538
539
540
541
542
    }

    return p_pif;
}

543
544
545
546
/**
 * Misc functions
 *
 */
547
static const struct
548
549
{
    vlc_fourcc_t i_codec;
Rafaël Carré's avatar
Rafaël Carré committed
550
    const char psz_ext[7];
551
552
553

} ext_table[] =
{
554
555
556
    { VLC_CODEC_JPEG,              "jpeg" },
    { VLC_CODEC_JPEG,              "jpg"  },
    { VLC_CODEC_JPEGLS,            "ljpg" },
Tristan Matthews's avatar
Tristan Matthews committed
557
    { VLC_CODEC_BPG,               "bpg" },
558
559
560
    { VLC_CODEC_PNG,               "png" },
    { VLC_CODEC_PGM,               "pgm" },
    { VLC_CODEC_PGMYUV,            "pgmyuv" },
561
562
    { VLC_FOURCC('p','b','m',' '), "pbm" },
    { VLC_FOURCC('p','a','m',' '), "pam" },
563
564
565
    { VLC_CODEC_TARGA,             "tga" },
    { VLC_CODEC_BMP,               "bmp" },
    { VLC_CODEC_PNM,               "pnm" },
566
567
    { VLC_FOURCC('x','p','m',' '), "xpm" },
    { VLC_FOURCC('x','c','f',' '), "xcf" },
568
569
    { VLC_CODEC_PCX,               "pcx" },
    { VLC_CODEC_GIF,               "gif" },
570
    { VLC_CODEC_SVG,               "svg" },
571
572
    { VLC_CODEC_TIFF,              "tif" },
    { VLC_CODEC_TIFF,              "tiff" },
573
    { VLC_FOURCC('l','b','m',' '), "lbm" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
574
    { VLC_CODEC_PPM,               "ppm" },
575
576
};

577
vlc_fourcc_t image_Type2Fourcc( const char *psz_type )
578
{
Rafaël Carré's avatar
Rafaël Carré committed
579
    for( unsigned i = 0; i < ARRAY_SIZE(ext_table); i++ )
dionoea's avatar
dionoea committed
580
581
        if( !strcasecmp( ext_table[i].psz_ext, psz_type ) )
            return ext_table[i].i_codec;
582
583
584
585

    return 0;
}

586
587
588
589
590
591
592
593
594
vlc_fourcc_t image_Ext2Fourcc( const char *psz_name )
{
    psz_name = strrchr( psz_name, '.' );
    if( !psz_name ) return 0;
    psz_name++;

    return image_Type2Fourcc( psz_name );
}

595
596
597
598
599
600
static const struct
{
    vlc_fourcc_t i_codec;
    const char *psz_mime;
} mime_table[] =
{
601
602
603
604
605
606
607
608
609
610
    { VLC_CODEC_BMP,               "image/bmp" },
    { VLC_CODEC_BMP,               "image/x-bmp" },
    { VLC_CODEC_BMP,               "image/x-bitmap" },
    { VLC_CODEC_BMP,               "image/x-ms-bmp" },
    { VLC_CODEC_PNM,               "image/x-portable-anymap" },
    { VLC_CODEC_PNM,               "image/x-portable-bitmap" },
    { VLC_CODEC_PNM,               "image/x-portable-graymap" },
    { VLC_CODEC_PNM,               "image/x-portable-pixmap" },
    { VLC_CODEC_GIF,               "image/gif" },
    { VLC_CODEC_JPEG,              "image/jpeg" },
Tristan Matthews's avatar
Tristan Matthews committed
611
    { VLC_CODEC_BPG,               "image/bpg" },
612
613
    { VLC_CODEC_PCX,               "image/pcx" },
    { VLC_CODEC_PNG,               "image/png" },
614
    { VLC_CODEC_SVG,               "image/svg+xml" },
615
    { VLC_CODEC_TIFF,              "image/tiff" },
616
    { VLC_CODEC_TARGA,             "image/x-tga" },
617
618
619
620
621
622
    { VLC_FOURCC('x','p','m',' '), "image/x-xpixmap" },
    { 0, NULL }
};

vlc_fourcc_t image_Mime2Fourcc( const char *psz_mime )
{
623
    for( int i = 0; mime_table[i].i_codec; i++ )
624
625
626
627
628
        if( !strcmp( psz_mime, mime_table[i].psz_mime ) )
            return mime_table[i].i_codec;
    return 0;
}

629
630
631
632
633
static int video_update_format( decoder_t *p_dec )
{
    p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
    return 0;
}
634

635
636
static picture_t *video_new_buffer( decoder_t *p_dec )
{
637
    return picture_NewFromFormat( &p_dec->fmt_out.video );
638
639
640
641
642
643
}

static decoder_t *CreateDecoder( vlc_object_t *p_this, video_format_t *fmt )
{
    decoder_t *p_dec;

644
    p_dec = vlc_custom_create( p_this, sizeof( *p_dec ), "image decoder" );
645
646
647
648
    if( p_dec == NULL )
        return NULL;

    p_dec->p_module = NULL;
649
650
    es_format_Init( &p_dec->fmt_in, VIDEO_ES, fmt->i_chroma );
    es_format_Init( &p_dec->fmt_out, VIDEO_ES, 0 );
651
    p_dec->fmt_in.video = *fmt;
652
    p_dec->b_frame_drop_allowed = false;
653

654
    p_dec->pf_vout_format_update = video_update_format;
655
656
657
    p_dec->pf_vout_buffer_new = video_new_buffer;

    /* Find a suitable decoder module */
658
    p_dec->p_module = module_need( p_dec, "decoder", "$codec", false );
659
660
    if( !p_dec->p_module )
    {
ivoire's avatar
ivoire committed
661
        msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'. "
662
663
664
665
666
667
668
669
670
671
672
673
                 "VLC probably does not support this image format.",
                 (char*)&p_dec->fmt_in.i_codec );

        DeleteDecoder( p_dec );
        return NULL;
    }

    return p_dec;
}

static void DeleteDecoder( decoder_t * p_dec )
{
674
    if( p_dec->p_module ) module_unneed( p_dec, p_dec->p_module );
675
676
677

    es_format_Clean( &p_dec->fmt_in );
    es_format_Clean( &p_dec->fmt_out );
678
679
680

    if( p_dec->p_description )
        vlc_meta_Delete( p_dec->p_description );
681

682
    vlc_object_release( p_dec );
683
    p_dec = NULL;
684
685
}

686
687
688
689
690
static encoder_t *CreateEncoder( vlc_object_t *p_this, video_format_t *fmt_in,
                                 video_format_t *fmt_out )
{
    encoder_t *p_enc;

691
    p_enc = sout_EncoderCreate( p_this );
692
693
694
695
696
697
    if( p_enc == NULL )
        return NULL;

    p_enc->p_module = NULL;
    es_format_Init( &p_enc->fmt_in, VIDEO_ES, fmt_in->i_chroma );
    p_enc->fmt_in.video = *fmt_in;
698

699
    if( p_enc->fmt_in.video.i_visible_width == 0 ||
700
701
702
        p_enc->fmt_in.video.i_visible_height == 0 ||
        p_enc->fmt_out.video.i_visible_width == 0 ||
        p_enc->fmt_out.video.i_visible_height == 0 )
703
704
    {
        if( fmt_out->i_width > 0 && fmt_out->i_height > 0 )
705
        {
706
707
708
709
710
711
712
713
714
715
716
717
718
719
            p_enc->fmt_in.video.i_width = fmt_out->i_width;
            p_enc->fmt_in.video.i_height = fmt_out->i_height;

            if( fmt_out->i_visible_width > 0 &&
                fmt_out->i_visible_height > 0 )
            {
                p_enc->fmt_in.video.i_visible_width = fmt_out->i_visible_width;
                p_enc->fmt_in.video.i_visible_height = fmt_out->i_visible_height;
            }
            else
            {
                p_enc->fmt_in.video.i_visible_width = fmt_out->i_width;
                p_enc->fmt_in.video.i_visible_height = fmt_out->i_height;
            }
720
        }
721
722
723
724
725
726
727
728
729
730
    } else if( fmt_out->i_sar_num && fmt_out->i_sar_den &&
               fmt_out->i_sar_num * fmt_in->i_sar_den !=
               fmt_out->i_sar_den * fmt_in->i_sar_num )
    {
        p_enc->fmt_in.video.i_width =
            fmt_in->i_sar_num * (int64_t)fmt_out->i_sar_den * fmt_in->i_width /
            fmt_in->i_sar_den / fmt_out->i_sar_num;
        p_enc->fmt_in.video.i_visible_width =
            fmt_in->i_sar_num * (int64_t)fmt_out->i_sar_den *
            fmt_in->i_visible_width / fmt_in->i_sar_den / fmt_out->i_sar_num;
731
    }
732

733
734
735
736
737
738
739
740
741
    p_enc->fmt_in.video.i_frame_rate = 25;
    p_enc->fmt_in.video.i_frame_rate_base = 1;

    es_format_Init( &p_enc->fmt_out, VIDEO_ES, fmt_out->i_chroma );
    p_enc->fmt_out.video = *fmt_out;
    p_enc->fmt_out.video.i_width = p_enc->fmt_in.video.i_width;
    p_enc->fmt_out.video.i_height = p_enc->fmt_in.video.i_height;

    /* Find a suitable decoder module */
742
    p_enc->p_module = module_need( p_enc, "encoder", NULL, false );
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
    if( !p_enc->p_module )
    {
        msg_Err( p_enc, "no suitable encoder module for fourcc `%4.4s'.\n"
                 "VLC probably does not support this image format.",
                 (char*)&p_enc->fmt_out.i_codec );

        DeleteEncoder( p_enc );
        return NULL;
    }
    p_enc->fmt_in.video.i_chroma = p_enc->fmt_in.i_codec;

    return p_enc;
}

static void DeleteEncoder( encoder_t * p_enc )
{
759
    if( p_enc->p_module ) module_unneed( p_enc, p_enc->p_module );
760
761
762
763

    es_format_Clean( &p_enc->fmt_in );
    es_format_Clean( &p_enc->fmt_out );

764
    vlc_object_release( p_enc );
765
    p_enc = NULL;
766
767
}

768
static filter_t *CreateFilter( vlc_object_t *p_this, es_format_t *p_fmt_in,
769
                               video_format_t *p_fmt_out )
770
771
772
{
    filter_t *p_filter;

773
    p_filter = vlc_custom_create( p_this, sizeof(filter_t), "filter" );
774
    p_filter->owner.video.buffer_new =
775
776
777
778
779
780
        (picture_t *(*)(filter_t *))video_new_buffer;

    p_filter->fmt_in = *p_fmt_in;
    p_filter->fmt_out = *p_fmt_in;
    p_filter->fmt_out.i_codec = p_fmt_out->i_chroma;
    p_filter->fmt_out.video = *p_fmt_out;
781
    p_filter->p_module = module_need( p_filter, "video converter", NULL, false );
782
783
784

    if( !p_filter->p_module )
    {
785
        msg_Dbg( p_filter, "no video converter found" );
786
787
788
789
790
791
792
793
794
        DeleteFilter( p_filter );
        return NULL;
    }

    return p_filter;
}

static void DeleteFilter( filter_t * p_filter )
{
795
    if( p_filter->p_module ) module_unneed( p_filter, p_filter->p_module );
796
797
798
799

    es_format_Clean( &p_filter->fmt_in );
    es_format_Clean( &p_filter->fmt_out );

800
    vlc_object_release( p_filter );
801
}