[vlc-devel] [PATCH] -- improving the art files user experience ....

brezhoneg1 brezhoneg1 at yahoo.fr
Tue Jan 29 02:41:18 CET 2013


Hi,

    Please, find attached a patch for review. This is a first draft to 
check if this is the right direction to go into.
    It was tested and it is fully functional.

Regards
Erwan

Le 28/01/2013 21:37, Rémi Denis-Courmont a écrit :
> Le lundi 28 janvier 2013 17:26:35, brezhoneg1 a écrit :
>>      So, I just duplicated fenrir's content-based detection code into vlc
>> core (src/misc/image.c) and __ALL__ art files would then be displayed
>> without any problem.
> Moving code from plugins into the core is not desirable. It bloats the LibVLC
> run-time. And obviously, duplicating code is not a very good idea either.
>
>>      So, the question, is : Would it be ok to move this content-based
>> detection at the vlc core (src/misc/image.c) so that it can be used
>> throughout vlc whenever a still image needs to be read ? Or, any other
>> means to benefit from this detection  ?
> IMHO, if some code needs to be moved, then the image code should be moved to
> the image plugin. The image handler interface is already modularized anyway.
>

-------------- next part --------------
>From f1f4cc41e0027f48ef40d5b1cd889924b1a437e5 Mon Sep 17 00:00:00 2001
From: Erwan Tulou <erwan10 at videolan.org>
Date: Tue, 29 Jan 2013 01:18:08 +0100
Subject: [PATCH] image: move the image module as a plugin and use
 content-based image detection

This patch does the following :
- move the core image module as a plugin (modules/demux/image2.c)
- create a image_helper.h file from fenrir's content detection code
- use this content detection in the new image plugin
- adapt the image demux to also use this helper
---
 include/vlc_image.h          |    6 +-
 modules/demux/Modules.am     |    4 +-
 modules/demux/image.c        |  301 +-----------------
 modules/demux/image2.c       |  719 ++++++++++++++++++++++++++++++++++++++++++
 modules/demux/image_helper.h |  334 ++++++++++++++++++++
 src/misc/image.c             |  657 +-------------------------------------
 6 files changed, 1080 insertions(+), 941 deletions(-)
 create mode 100644 modules/demux/image2.c
 create mode 100644 modules/demux/image_helper.h

diff --git a/include/vlc_image.h b/include/vlc_image.h
index 52bce1f..aae8db3 100644
--- a/include/vlc_image.h
+++ b/include/vlc_image.h
@@ -37,6 +37,11 @@ extern "C" {
 
 struct image_handler_t
 {
+    VLC_COMMON_MEMBERS
+
+    /* Module properties */
+    module_t    *p_module;
+
     picture_t * (*pf_read)      ( image_handler_t *, block_t *,
                                   video_format_t *, video_format_t * );
     picture_t * (*pf_read_url)  ( image_handler_t *, const char *,
@@ -53,7 +58,6 @@ struct image_handler_t
                                   video_format_t *, const char * );
 
     /* Private properties */
-    vlc_object_t *p_parent;
     decoder_t *p_dec;
     encoder_t *p_enc;
     filter_t  *p_filter;
diff --git a/modules/demux/Modules.am b/modules/demux/Modules.am
index 4a0817c..6ad5d79 100644
--- a/modules/demux/Modules.am
+++ b/modules/demux/Modules.am
@@ -29,7 +29,8 @@ SOURCES_smf = smf.c
 SOURCES_gme = gme.c dummy.cpp
 SOURCES_sid = sid.cpp
 SOURCES_dirac = dirac.c
-SOURCES_image = image.c mxpeg_helper.h
+SOURCES_image = image.c image_helper.h mxpeg_helper.h
+SOURCES_image2 = image2.c image_helper.h mxpeg_helper.h
 SOURCES_demux_stl = stl.c
 
 libasf_plugin_la_SOURCES = asf/asf.c asf/libasf.c asf/libasf.h asf/libasf_guid.h
@@ -174,6 +175,7 @@ libvlc_LTLIBRARIES += \
 	libwav_plugin.la \
 	libxa_plugin.la \
 	libimage_plugin.la \
+	libimage2_plugin.la \
 	libdemux_stl_plugin.la \
 	$(NULL)
 
diff --git a/modules/demux/image.c b/modules/demux/image.c
index 672e7ba..6c7397d 100644
--- a/modules/demux/image.c
+++ b/modules/demux/image.c
@@ -33,7 +33,7 @@
 #include <vlc_plugin.h>
 #include <vlc_demux.h>
 #include <vlc_image.h>
-#include "mxpeg_helper.h"
+#include "image_helper.h"
 
 /*****************************************************************************
  * Module descriptor
@@ -273,313 +273,26 @@ static int Control(demux_t *demux, int query, va_list args)
     }
 }
 
-static bool IsBmp(stream_t *s)
-{
-    const uint8_t *header;
-    if (stream_Peek(s, &header, 18) < 18)
-        return false;
-    if (memcmp(header, "BM", 2) &&
-        memcmp(header, "BA", 2) &&
-        memcmp(header, "CI", 2) &&
-        memcmp(header, "CP", 2) &&
-        memcmp(header, "IC", 2) &&
-        memcmp(header, "PT", 2))
-        return false;
-    uint32_t file_size   = GetDWLE(&header[2]);
-    uint32_t data_offset = GetDWLE(&header[10]);
-    uint32_t header_size = GetDWLE(&header[14]);
-    if (file_size != 14 && file_size != 14 + header_size &&
-        file_size <= data_offset)
-        return false;
-    if (data_offset < header_size + 14)
-        return false;
-    if (header_size != 12 && header_size < 40)
-        return false;
-    return true;
-}
-
-static bool IsPcx(stream_t *s)
-{
-    const uint8_t *header;
-    if (stream_Peek(s, &header, 66) < 66)
-        return false;
-    if (header[0] != 0x0A ||                        /* marker */
-        (header[1] != 0x00 && header[1] != 0x02 &&
-         header[1] != 0x03 && header[1] != 0x05) || /* version */
-        (header[2] != 0 && header[2] != 1) ||       /* encoding */
-        (header[3] != 1 && header[3] != 2 &&
-         header[3] != 4 && header[3] != 8) ||       /* bits per pixel per plane */
-        header[64] != 0 ||                          /* reserved */
-        header[65] == 0 || header[65] > 4)          /* plane count */
-        return false;
-    if (GetWLE(&header[4]) > GetWLE(&header[8]) ||  /* xmin vs xmax */
-        GetWLE(&header[6]) > GetWLE(&header[10]))   /* ymin vs ymax */
-        return false;
-    return true;
-}
-
-static bool IsLbm(stream_t *s)
-{
-    const uint8_t *header;
-    if (stream_Peek(s, &header, 12) < 12)
-        return false;
-    if (memcmp(&header[0], "FORM", 4) ||
-        GetDWBE(&header[4]) <= 4 ||
-        (memcmp(&header[8], "ILBM", 4) && memcmp(&header[8], "PBM ", 4)))
-        return false;
-    return true;
-}
-static bool IsPnmBlank(uint8_t v)
-{
-    return v == ' ' || v == '\t' || v == '\r' || v == '\n';
-}
-static bool IsPnm(stream_t *s)
-{
-    const uint8_t *header;
-    int size = stream_Peek(s, &header, 256);
-    if (size < 3)
-        return false;
-    if (header[0] != 'P' ||
-        header[1] < '1' || header[1] > '6' ||
-        !IsPnmBlank(header[2]))
-        return false;
-
-    int number_count = 0;
-    for (int i = 3, parsing_number = 0; i < size && number_count < 2; i++) {
-        if (IsPnmBlank(header[i])) {
-            if (parsing_number) {
-                parsing_number = 0;
-                number_count++;
-            }
-        } else {
-            if (header[i] < '0' || header[i] > '9')
-                break;
-            parsing_number = 1;
-        }
-    }
-    if (number_count < 2)
-        return false;
-    return true;
-}
-
-static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
-{
-    for (int i = *position; i + 1 < size; i++) {
-        if (data[i + 0] != 0xff || data[i + 1] == 0x00)
-            return 0xff;
-        if (data[i + 1] != 0xff) {
-            *position = i + 2;
-            return data[i + 1];
-        }
-    }
-    return 0xff;
-}
-static bool IsJfif(stream_t *s)
-{
-    const uint8_t *header;
-    int size = stream_Peek(s, &header, 256);
-    int position = 0;
-
-    if (FindJpegMarker(&position, header, size) != 0xd8)
-        return false;
-    if (FindJpegMarker(&position, header, size) != 0xe0)
-        return false;
-    position += 2;  /* Skip size */
-    if (position + 5 > size)
-        return false;
-    if (memcmp(&header[position], "JFIF\0", 5))
-        return false;
-    return true;
-}
-
-static bool IsSpiff(stream_t *s)
-{
-    const uint8_t *header;
-    if (stream_Peek(s, &header, 36) < 36) /* SPIFF header size */
-        return false;
-    if (header[0] != 0xff || header[1] != 0xd8 ||
-        header[2] != 0xff || header[3] != 0xe8)
-        return false;
-    if (memcmp(&header[6], "SPIFF\0", 6))
-        return false;
-    return true;
-}
-
-static bool IsExif(stream_t *s)
-{
-    const uint8_t *header;
-    int size = stream_Peek(s, &header, 256);
-    int position = 0;
-
-    if (FindJpegMarker(&position, header, size) != 0xd8)
-        return false;
-    if (FindJpegMarker(&position, header, size) != 0xe1)
-        return false;
-    position += 2;  /* Skip size */
-    if (position + 5 > size)
-        return false;
-    if (memcmp(&header[position], "Exif\0", 5))
-        return false;
-    return true;
-}
-
-static bool IsTarga(stream_t *s)
-{
-    /* The header is not enough to ensure proper detection, we need
-     * to have a look at the footer. But doing so can be slow. So
-     * try to avoid it when possible */
-    const uint8_t *header;
-    if (stream_Peek(s, &header, 18) < 18)   /* Targa fixed header */
-        return false;
-    if (header[1] > 1)                      /* Color Map Type */
-        return false;
-    if ((header[1] != 0 || header[3 + 4] != 0) &&
-        header[3 + 4] != 8  &&
-        header[3 + 4] != 15 && header[3 + 4] != 16 &&
-        header[3 + 4] != 24 && header[3 + 4] != 32)
-        return false;
-    if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */
-        return false;
-    if (GetWLE(&header[8 + 4]) <= 0 ||      /* Width */
-        GetWLE(&header[8 + 6]) <= 0)        /* Height */
-        return false;
-    if (header[8 + 8] != 8  &&
-        header[8 + 8] != 15 && header[8 + 8] != 16 &&
-        header[8 + 8] != 24 && header[8 + 8] != 32)
-        return false;
-    if (header[8 + 9] & 0xc0)               /* Reserved bits */
-        return false;
-
-    const int64_t size = stream_Size(s);
-    if (size <= 18 + 26)
-        return false;
-    bool can_seek;
-    if (stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek)
-        return false;
-
-    const int64_t position = stream_Tell(s);
-    if (stream_Seek(s, size - 26))
-        return false;
-
-    const uint8_t *footer;
-    bool is_targa = stream_Peek(s, &footer, 26) >= 26 &&
-                    !memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18);
-    stream_Seek(s, position);
-    return is_targa;
-}
-
-typedef struct {
-    vlc_fourcc_t  codec;
-    int           marker_size;
-    const uint8_t marker[14];
-    bool          (*detect)(stream_t *s);
-} image_format_t;
-
-#define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
-#define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
-static const image_format_t formats[] = {
-    { .codec = VLC_CODEC_XCF,
-      .marker_size = 9 + 4 + 1,
-      .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
-                  'f', 'i', 'l', 'e', '\0' }
-    },
-    { .codec = VLC_CODEC_XCF,
-      .marker_size = 9 + 4 + 1,
-      .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
-                  'v', '0', '0', '1', '\0' }
-    },
-    { .codec = VLC_CODEC_XCF,
-      .marker_size = 9 + 4 + 1,
-      .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
-                  'v', '0', '0', '2', '\0' }
-    },
-    { .codec = VLC_CODEC_PNG,
-      .marker_size = 8,
-      .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
-    },
-    { .codec = VLC_CODEC_GIF,
-      .marker_size = 6,
-      .marker = { 'G', 'I', 'F', '8', '7', 'a' }
-    },
-    { .codec = VLC_CODEC_GIF,
-      .marker_size = 6,
-      .marker = { 'G', 'I', 'F', '8', '9', 'a' }
-    },
-    /* XXX TIFF detection may be a bit weak */
-    { .codec = VLC_CODEC_TIFF,
-      .marker_size = 4,
-      .marker = { 'I', 'I', 0x2a, 0x00 },
-    },
-    { .codec = VLC_CODEC_TIFF,
-      .marker_size = 4,
-      .marker = { 'M', 'M', 0x00, 0x2a },
-    },
-    { .codec = VLC_CODEC_BMP,
-      .detect = IsBmp,
-    },
-    { .codec = VLC_CODEC_PCX,
-      .detect = IsPcx,
-    },
-    { .codec = VLC_CODEC_LBM,
-      .detect = IsLbm,
-    },
-    { .codec = VLC_CODEC_PNM,
-      .detect = IsPnm,
-    },
-    { .codec = VLC_CODEC_MXPEG,
-      .detect = IsMxpeg,
-    },
-    { .codec = VLC_CODEC_JPEG,
-      .detect = IsJfif,
-    },
-    { .codec = VLC_CODEC_JPEG,
-      .detect = IsSpiff,
-    },
-    { .codec = VLC_CODEC_JPEG,
-      .detect = IsExif,
-    },
-    { .codec = VLC_CODEC_TARGA,
-      .detect = IsTarga,
-    },
-    { .codec = 0 }
-};
-
 static int Open(vlc_object_t *object)
 {
     demux_t *demux = (demux_t*)object;
 
     /* Detect the image type */
-    const image_format_t *img;
-
-    const uint8_t *peek;
-    int peek_size = 0;
-    for (int i = 0; ; i++) {
-        img = &formats[i];
-        if (!img->codec)
-            return VLC_EGENERIC;
+    vlc_fourcc_t codec = Content2Fourcc(demux->s);
+    if( !codec )
+        return VLC_EGENERIC;
 
-        if (img->detect) {
-            if (img->detect(demux->s))
-                break;
-        } else {
-            if (peek_size < img->marker_size)
-                peek_size = stream_Peek(demux->s, &peek, img->marker_size);
-            if (peek_size >= img->marker_size &&
-                !memcmp(peek, img->marker, img->marker_size))
-                break;
-        }
-    }
     msg_Dbg(demux, "Detected image: %s",
-            vlc_fourcc_GetDescription(VIDEO_ES, img->codec));
+            vlc_fourcc_GetDescription(VIDEO_ES, codec));
 
-    if( img->codec == VLC_CODEC_MXPEG )
+    if( codec == VLC_CODEC_MXPEG )
     {
         return VLC_EGENERIC; //let avformat demux this file
     }
 
     /* Load and if selected decode */
     es_format_t fmt;
-    es_format_Init(&fmt, VIDEO_ES, img->codec);
+    es_format_Init(&fmt, VIDEO_ES, codec);
     fmt.video.i_chroma = fmt.i_codec;
 
     block_t *data = Load(demux);
diff --git a/modules/demux/image2.c b/modules/demux/image2.c
new file mode 100644
index 0000000..08c2dc2
--- /dev/null
+++ b/modules/demux/image2.c
@@ -0,0 +1,719 @@
+/*****************************************************************************
+ * image2.c : image plugin for reading/writing facilities
+ *****************************************************************************
+ * Copyright (C) 2004-2007 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Author: Gildas Bazin <gbazin at videolan.org>
+ *
+ * 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
+ * (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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * 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.
+ *****************************************************************************/
+
+/**
+ * \file
+ * This file contains the functions to handle the image_handler_t type
+ */
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_meta.h>
+#include <vlc_filter.h>
+#include <vlc_es.h>
+#include <vlc_image.h>
+#include <vlc_stream.h>
+#include <vlc_fs.h>
+#include <vlc_sout.h>
+#include <vlc_modules.h>
+#include <image_helper.h>
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_description(N_("Image facilities"))
+    set_capability("image provider", 10)
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+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 * );
+
+static picture_t *ImageConvert( image_handler_t *, picture_t *,
+                                video_format_t *, video_format_t * );
+static picture_t *ImageFilter( image_handler_t *, picture_t *,
+                               video_format_t *, const char *psz_module );
+
+static decoder_t *CreateDecoder( vlc_object_t *, video_format_t * );
+static void DeleteDecoder( decoder_t * );
+static encoder_t *CreateEncoder( vlc_object_t *, video_format_t *,
+                                 video_format_t * );
+static void DeleteEncoder( encoder_t * );
+static filter_t *CreateFilter( vlc_object_t *, es_format_t *,
+                               video_format_t *, const char * );
+static void DeleteFilter( filter_t * );
+
+static int Open(vlc_object_t *object)
+{
+    image_handler_t *p_image = (image_handler_t*)object;
+
+    p_image->pf_read = ImageRead;
+    p_image->pf_read_url = ImageReadUrl;
+    p_image->pf_write = ImageWrite;
+    p_image->pf_write_url = ImageWriteUrl;
+    p_image->pf_convert = ImageConvert;
+    p_image->pf_filter = ImageFilter;
+
+    return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *object)
+{
+    image_handler_t *p_image = (image_handler_t*)object;
+
+    if( p_image->p_dec ) DeleteDecoder( p_image->p_dec );
+    if( p_image->p_enc ) DeleteEncoder( p_image->p_enc );
+    if( p_image->p_filter ) DeleteFilter( p_image->p_filter );
+
+    p_image->p_dec = NULL;
+    p_image->p_enc = NULL;
+    p_image->p_filter = NULL;
+}
+
+/**
+ * Read an image
+ *
+ */
+
+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 )
+{
+    picture_t *p_pic = NULL, *p_tmp;
+
+    /* 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_fmt_in );
+        if( !p_image->p_dec ) return NULL;
+    }
+
+    p_block->i_pts = p_block->i_dts = mdate();
+    while( (p_tmp = p_image->p_dec->pf_decode_video( p_image->p_dec, &p_block ))
+             != NULL )
+    {
+        if( p_pic != NULL )
+            picture_Release( p_pic );
+        p_pic = p_tmp;
+    }
+
+    if( p_pic == NULL )
+    {
+        msg_Warn( p_image, "no image decoded" );
+        return 0;
+    }
+
+    if( !p_fmt_out->i_chroma )
+        p_fmt_out->i_chroma = p_image->p_dec->fmt_out.video.i_chroma;
+    if( !p_fmt_out->i_width && p_fmt_out->i_height )
+        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;
+
+    if( !p_fmt_out->i_height && p_fmt_out->i_width )
+        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;
+    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;
+
+    /* 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 ||
+            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 );
+            p_image->p_filter = 0;
+        }
+
+        /* Start a filter */
+        if( !p_image->p_filter )
+        {
+            p_image->p_filter =
+                CreateFilter( p_image, &p_image->p_dec->fmt_out,
+                              p_fmt_out, NULL );
+
+            if( !p_image->p_filter )
+            {
+                picture_Release( p_pic );
+                return NULL;
+            }
+        }
+        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;
+        }
+
+        p_pic = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
+        *p_fmt_out = p_image->p_filter->fmt_out.video;
+    }
+    else *p_fmt_out = p_image->p_dec->fmt_out.video;
+
+    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;
+    stream_t *p_stream = NULL;
+    int i_size;
+
+    p_stream = stream_UrlNew( p_image, psz_url );
+
+    if( !p_stream )
+    {
+        msg_Dbg( p_image, "could not open %s for reading",
+                 psz_url );
+        return NULL;
+    }
+
+    // first off, try an early chroma detection based on content
+    if( !p_fmt_in->i_chroma )
+    {
+        p_fmt_in->i_chroma = Content2Fourcc( p_stream );
+    }
+
+    i_size = stream_Size( p_stream );
+
+    p_block = block_Alloc( i_size );
+
+    stream_Read( p_stream, p_block->p_buffer, i_size );
+
+    if( !p_fmt_in->i_chroma )
+    {
+        char *psz_mime = NULL;
+        stream_Control( p_stream, STREAM_GET_CONTENT_TYPE, &psz_mime );
+        if( psz_mime )
+            p_fmt_in->i_chroma = image_Mime2Fourcc( psz_mime );
+        free( psz_mime );
+    }
+    stream_Delete( p_stream );
+
+    if( !p_fmt_in->i_chroma )
+    {
+        /* Try to guess format from file name */
+        p_fmt_in->i_chroma = image_Ext2Fourcc( psz_url );
+    }
+
+    p_pic = ImageRead( p_image, p_block, p_fmt_in, p_fmt_out );
+
+    return p_pic;
+}
+
+/**
+ * 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 )
+{
+    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_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 )
+    {
+        picture_t *p_tmp_pic;
+
+        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, &fmt_in,
+                              &p_image->p_enc->fmt_in.video, NULL );
+
+            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;
+        }
+
+        picture_Hold( p_pic );
+
+        p_tmp_pic =
+            p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
+
+        if( likely(p_tmp_pic != NULL) )
+        {
+            p_block = p_image->p_enc->pf_encode_video( p_image->p_enc,
+                                                       p_tmp_pic );
+            p_image->p_filter->pf_video_buffer_del( p_image->p_filter,
+                                                    p_tmp_pic );
+        }
+        else
+            p_block = NULL;
+    }
+    else
+    {
+        p_block = p_image->p_enc->pf_encode_video( p_image->p_enc, p_pic );
+    }
+
+    if( !p_block )
+    {
+        msg_Dbg( p_image, "no image encoded" );
+        return 0;
+    }
+
+    return p_block;
+}
+
+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 )
+{
+    block_t *p_block;
+    FILE *file;
+
+    if( !p_fmt_out->i_chroma )
+    {
+        /* Try to guess format from file name */
+        p_fmt_out->i_chroma = image_Ext2Fourcc( psz_url );
+    }
+
+    file = vlc_fopen( psz_url, "wb" );
+    if( !file )
+    {
+        msg_Err( p_image, "%s: %m", psz_url );
+        return VLC_EGENERIC;
+    }
+
+    p_block = ImageWrite( p_image, p_pic, p_fmt_in, p_fmt_out );
+
+    int err = 0;
+    if( p_block )
+    {
+        if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, file ) != 1 )
+            err = errno;
+        block_Release( p_block );
+    }
+
+    if( fclose( file ) && !err )
+        err = errno;
+
+    if( err )
+    {
+       errno = err;
+       msg_Err( p_image, "%s: %m", psz_url );
+    }
+
+    return err ? VLC_EGENERIC : VLC_SUCCESS;
+}
+
+/**
+ * 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;
+
+    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;
+    }
+
+    if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_fmt_in->i_chroma;
+    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;
+
+    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 );
+        p_image->p_filter = NULL;
+    }
+
+    /* 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, &fmt_in, p_fmt_out, NULL );
+
+        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;
+    }
+
+    picture_Hold( p_pic );
+
+    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 */
+        picture_Release( p_pif ); /* XXX: Better fix must be possible */
+        p_pif = p_image->p_filter->pf_video_buffer_new( p_image->p_filter );
+        if( p_pif )
+            picture_Copy( p_pif, p_pic );
+    }
+
+    return p_pif;
+}
+
+/**
+ * Filter an image with a psz_module filter
+ *
+ */
+
+static picture_t *ImageFilter( image_handler_t *p_image, picture_t *p_pic,
+                               video_format_t *p_fmt, const char *psz_module )
+{
+    /* Start a filter */
+    if( !p_image->p_filter )
+    {
+        es_format_t fmt;
+        es_format_Init( &fmt, VIDEO_ES, p_fmt->i_chroma );
+        fmt.video = *p_fmt;
+
+        p_image->p_filter =
+            CreateFilter( p_image, &fmt, &fmt.video, psz_module );
+
+        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;
+        p_image->p_filter->fmt_out.video = *p_fmt;
+    }
+
+    picture_Hold( p_pic );
+
+    return p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
+}
+
+static picture_t *video_new_buffer( decoder_t *p_dec )
+{
+    p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
+    return picture_NewFromFormat( &p_dec->fmt_out.video );
+}
+
+static void video_del_buffer( decoder_t *p_dec, picture_t *p_pic )
+{
+    (void)p_dec;
+    picture_Release( p_pic );
+}
+
+static void video_link_picture( decoder_t *p_dec, picture_t *p_pic )
+{
+    (void)p_dec;
+    picture_Hold( p_pic );
+}
+
+static void video_unlink_picture( decoder_t *p_dec, picture_t *p_pic )
+{
+    (void)p_dec;
+    picture_Release( p_pic );
+}
+
+static decoder_t *CreateDecoder( vlc_object_t *p_this, video_format_t *fmt )
+{
+    decoder_t *p_dec;
+
+    p_dec = vlc_object_create( p_this, sizeof( *p_dec ) );
+    if( p_dec == NULL )
+        return NULL;
+
+    p_dec->p_module = NULL;
+    es_format_Init( &p_dec->fmt_in, VIDEO_ES, fmt->i_chroma );
+    es_format_Init( &p_dec->fmt_out, VIDEO_ES, 0 );
+    p_dec->fmt_in.video = *fmt;
+    p_dec->b_pace_control = true;
+
+    p_dec->pf_vout_buffer_new = video_new_buffer;
+    p_dec->pf_vout_buffer_del = video_del_buffer;
+    p_dec->pf_picture_link    = video_link_picture;
+    p_dec->pf_picture_unlink  = video_unlink_picture;
+
+    /* Find a suitable decoder module */
+    p_dec->p_module = module_need( p_dec, "decoder", "$codec", false );
+    if( !p_dec->p_module )
+    {
+        msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'. "
+                 "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 )
+{
+    if( p_dec->p_module ) module_unneed( p_dec, p_dec->p_module );
+
+    es_format_Clean( &p_dec->fmt_in );
+    es_format_Clean( &p_dec->fmt_out );
+
+    if( p_dec->p_description )
+        vlc_meta_Delete( p_dec->p_description );
+
+    vlc_object_release( p_dec );
+    p_dec = NULL;
+}
+
+static encoder_t *CreateEncoder( vlc_object_t *p_this, video_format_t *fmt_in,
+                                 video_format_t *fmt_out )
+{
+    encoder_t *p_enc;
+
+    p_enc = sout_EncoderCreate( p_this );
+    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;
+    if( fmt_out->i_width > 0 && fmt_out->i_height > 0 )
+    {
+        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;
+        }
+    }
+    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;
+    }
+
+    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 */
+    p_enc->p_module = module_need( p_enc, "encoder", NULL, false );
+    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 )
+{
+    if( p_enc->p_module ) module_unneed( p_enc, p_enc->p_module );
+
+    es_format_Clean( &p_enc->fmt_in );
+    es_format_Clean( &p_enc->fmt_out );
+
+    vlc_object_release( p_enc );
+    p_enc = NULL;
+}
+
+static filter_t *CreateFilter( vlc_object_t *p_this, es_format_t *p_fmt_in,
+                               video_format_t *p_fmt_out,
+                               const char *psz_module )
+{
+    filter_t *p_filter;
+
+    p_filter = vlc_object_create( p_this, sizeof(filter_t) );
+    p_filter->pf_video_buffer_new =
+        (picture_t *(*)(filter_t *))video_new_buffer;
+    p_filter->pf_video_buffer_del =
+        (void (*)(filter_t *, picture_t *))video_del_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;
+    p_filter->p_module = module_need( p_filter, "video filter2",
+                                      psz_module, false );
+
+    if( !p_filter->p_module )
+    {
+        msg_Dbg( p_filter, "no video filter found" );
+        DeleteFilter( p_filter );
+        return NULL;
+    }
+
+    return p_filter;
+}
+
+static void DeleteFilter( filter_t * p_filter )
+{
+    if( p_filter->p_module ) module_unneed( p_filter, p_filter->p_module );
+
+    es_format_Clean( &p_filter->fmt_in );
+    es_format_Clean( &p_filter->fmt_out );
+
+    vlc_object_release( p_filter );
+}
diff --git a/modules/demux/image_helper.h b/modules/demux/image_helper.h
new file mode 100644
index 0000000..7652a7d
--- /dev/null
+++ b/modules/demux/image_helper.h
@@ -0,0 +1,334 @@
+/*****************************************************************************
+ * image_helper.h: Image helper
+ *****************************************************************************
+ * Copyright (C) 2010 Laurent Aimar
+ * $Id$
+ *
+ * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
+ *
+ * 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
+ * (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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_image.h>
+#include "mxpeg_helper.h"
+
+
+static bool IsBmp(stream_t *s)
+{
+    const uint8_t *header;
+    if (stream_Peek(s, &header, 18) < 18)
+        return false;
+    if (memcmp(header, "BM", 2) &&
+        memcmp(header, "BA", 2) &&
+        memcmp(header, "CI", 2) &&
+        memcmp(header, "CP", 2) &&
+        memcmp(header, "IC", 2) &&
+        memcmp(header, "PT", 2))
+        return false;
+    uint32_t file_size   = GetDWLE(&header[2]);
+    uint32_t data_offset = GetDWLE(&header[10]);
+    uint32_t header_size = GetDWLE(&header[14]);
+    if (file_size != 14 && file_size != 14 + header_size &&
+        file_size <= data_offset)
+        return false;
+    if (data_offset < header_size + 14)
+        return false;
+    if (header_size != 12 && header_size < 40)
+        return false;
+    return true;
+}
+
+static bool IsPcx(stream_t *s)
+{
+    const uint8_t *header;
+    if (stream_Peek(s, &header, 66) < 66)
+        return false;
+    if (header[0] != 0x0A ||                        /* marker */
+        (header[1] != 0x00 && header[1] != 0x02 &&
+         header[1] != 0x03 && header[1] != 0x05) || /* version */
+        (header[2] != 0 && header[2] != 1) ||       /* encoding */
+        (header[3] != 1 && header[3] != 2 &&
+         header[3] != 4 && header[3] != 8) ||       /* bits per pixel per plane */
+        header[64] != 0 ||                          /* reserved */
+        header[65] == 0 || header[65] > 4)          /* plane count */
+        return false;
+    if (GetWLE(&header[4]) > GetWLE(&header[8]) ||  /* xmin vs xmax */
+        GetWLE(&header[6]) > GetWLE(&header[10]))   /* ymin vs ymax */
+        return false;
+    return true;
+}
+
+static bool IsLbm(stream_t *s)
+{
+    const uint8_t *header;
+    if (stream_Peek(s, &header, 12) < 12)
+        return false;
+    if (memcmp(&header[0], "FORM", 4) ||
+        GetDWBE(&header[4]) <= 4 ||
+        (memcmp(&header[8], "ILBM", 4) && memcmp(&header[8], "PBM ", 4)))
+        return false;
+    return true;
+}
+static bool IsPnmBlank(uint8_t v)
+{
+    return v == ' ' || v == '\t' || v == '\r' || v == '\n';
+}
+static bool IsPnm(stream_t *s)
+{
+    const uint8_t *header;
+    int size = stream_Peek(s, &header, 256);
+    if (size < 3)
+        return false;
+    if (header[0] != 'P' ||
+        header[1] < '1' || header[1] > '6' ||
+        !IsPnmBlank(header[2]))
+        return false;
+
+    int number_count = 0;
+    for (int i = 3, parsing_number = 0; i < size && number_count < 2; i++) {
+        if (IsPnmBlank(header[i])) {
+            if (parsing_number) {
+                parsing_number = 0;
+                number_count++;
+            }
+        } else {
+            if (header[i] < '0' || header[i] > '9')
+                break;
+            parsing_number = 1;
+        }
+    }
+    if (number_count < 2)
+        return false;
+    return true;
+}
+
+static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
+{
+    for (int i = *position; i + 1 < size; i++) {
+        if (data[i + 0] != 0xff || data[i + 1] == 0x00)
+            return 0xff;
+        if (data[i + 1] != 0xff) {
+            *position = i + 2;
+            return data[i + 1];
+        }
+    }
+    return 0xff;
+}
+static bool IsJfif(stream_t *s)
+{
+    const uint8_t *header;
+    int size = stream_Peek(s, &header, 256);
+    int position = 0;
+
+    if (FindJpegMarker(&position, header, size) != 0xd8)
+        return false;
+    if (FindJpegMarker(&position, header, size) != 0xe0)
+        return false;
+    position += 2;  /* Skip size */
+    if (position + 5 > size)
+        return false;
+    if (memcmp(&header[position], "JFIF\0", 5))
+        return false;
+    return true;
+}
+
+static bool IsSpiff(stream_t *s)
+{
+    const uint8_t *header;
+    if (stream_Peek(s, &header, 36) < 36) /* SPIFF header size */
+        return false;
+    if (header[0] != 0xff || header[1] != 0xd8 ||
+        header[2] != 0xff || header[3] != 0xe8)
+        return false;
+    if (memcmp(&header[6], "SPIFF\0", 6))
+        return false;
+    return true;
+}
+
+static bool IsExif(stream_t *s)
+{
+    const uint8_t *header;
+    int size = stream_Peek(s, &header, 256);
+    int position = 0;
+
+    if (FindJpegMarker(&position, header, size) != 0xd8)
+        return false;
+    if (FindJpegMarker(&position, header, size) != 0xe1)
+        return false;
+    position += 2;  /* Skip size */
+    if (position + 5 > size)
+        return false;
+    if (memcmp(&header[position], "Exif\0", 5))
+        return false;
+    return true;
+}
+
+static bool IsTarga(stream_t *s)
+{
+    /* The header is not enough to ensure proper detection, we need
+     * to have a look at the footer. But doing so can be slow. So
+     * try to avoid it when possible */
+    const uint8_t *header;
+    if (stream_Peek(s, &header, 18) < 18)   /* Targa fixed header */
+        return false;
+    if (header[1] > 1)                      /* Color Map Type */
+        return false;
+    if ((header[1] != 0 || header[3 + 4] != 0) &&
+        header[3 + 4] != 8  &&
+        header[3 + 4] != 15 && header[3 + 4] != 16 &&
+        header[3 + 4] != 24 && header[3 + 4] != 32)
+        return false;
+    if ((header[2] > 3 && header[2] < 9) || header[2] > 11) /* Image Type */
+        return false;
+    if (GetWLE(&header[8 + 4]) <= 0 ||      /* Width */
+        GetWLE(&header[8 + 6]) <= 0)        /* Height */
+        return false;
+    if (header[8 + 8] != 8  &&
+        header[8 + 8] != 15 && header[8 + 8] != 16 &&
+        header[8 + 8] != 24 && header[8 + 8] != 32)
+        return false;
+    if (header[8 + 9] & 0xc0)               /* Reserved bits */
+        return false;
+
+    const int64_t size = stream_Size(s);
+    if (size <= 18 + 26)
+        return false;
+    bool can_seek;
+    if (stream_Control(s, STREAM_CAN_SEEK, &can_seek) || !can_seek)
+        return false;
+
+    const int64_t position = stream_Tell(s);
+    if (stream_Seek(s, size - 26))
+        return false;
+
+    const uint8_t *footer;
+    bool is_targa = stream_Peek(s, &footer, 26) >= 26 &&
+                    !memcmp(&footer[8], "TRUEVISION-XFILE.\x00", 18);
+    stream_Seek(s, position);
+    return is_targa;
+}
+
+typedef struct {
+    vlc_fourcc_t  codec;
+    int           marker_size;
+    const uint8_t marker[14];
+    bool          (*detect)(stream_t *s);
+} image_format_t;
+
+#define VLC_CODEC_XCF VLC_FOURCC('X', 'C', 'F', ' ')
+#define VLC_CODEC_LBM VLC_FOURCC('L', 'B', 'M', ' ')
+static const image_format_t formats[] = {
+    { .codec = VLC_CODEC_XCF,
+      .marker_size = 9 + 4 + 1,
+      .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
+                  'f', 'i', 'l', 'e', '\0' }
+    },
+    { .codec = VLC_CODEC_XCF,
+      .marker_size = 9 + 4 + 1,
+      .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
+                  'v', '0', '0', '1', '\0' }
+    },
+    { .codec = VLC_CODEC_XCF,
+      .marker_size = 9 + 4 + 1,
+      .marker = { 'g', 'i', 'm', 'p', ' ', 'x', 'c', 'f', ' ',
+                  'v', '0', '0', '2', '\0' }
+    },
+    { .codec = VLC_CODEC_PNG,
+      .marker_size = 8,
+      .marker = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }
+    },
+    { .codec = VLC_CODEC_GIF,
+      .marker_size = 6,
+      .marker = { 'G', 'I', 'F', '8', '7', 'a' }
+    },
+    { .codec = VLC_CODEC_GIF,
+      .marker_size = 6,
+      .marker = { 'G', 'I', 'F', '8', '9', 'a' }
+    },
+    /* XXX TIFF detection may be a bit weak */
+    { .codec = VLC_CODEC_TIFF,
+      .marker_size = 4,
+      .marker = { 'I', 'I', 0x2a, 0x00 },
+    },
+    { .codec = VLC_CODEC_TIFF,
+      .marker_size = 4,
+      .marker = { 'M', 'M', 0x00, 0x2a },
+    },
+    { .codec = VLC_CODEC_BMP,
+      .detect = IsBmp,
+    },
+    { .codec = VLC_CODEC_PCX,
+      .detect = IsPcx,
+    },
+    { .codec = VLC_CODEC_LBM,
+      .detect = IsLbm,
+    },
+    { .codec = VLC_CODEC_PNM,
+      .detect = IsPnm,
+    },
+    { .codec = VLC_CODEC_MXPEG,
+      .detect = IsMxpeg,
+    },
+    { .codec = VLC_CODEC_JPEG,
+      .detect = IsJfif,
+    },
+    { .codec = VLC_CODEC_JPEG,
+      .detect = IsSpiff,
+    },
+    { .codec = VLC_CODEC_JPEG,
+      .detect = IsExif,
+    },
+    { .codec = VLC_CODEC_TARGA,
+      .detect = IsTarga,
+    },
+    { .codec = 0 }
+};
+
+static vlc_fourcc_t Content2Fourcc( stream_t *s )
+{
+    /* Detect the image type */
+    const image_format_t *img;
+
+    const uint8_t *peek;
+    int peek_size = 0;
+    for (int i = 0; ; i++) {
+        img = &formats[i];
+        if (!img->codec)
+            return 0;
+
+        if (img->detect) {
+            if (img->detect(s))
+                break;
+        } else {
+            if (peek_size < img->marker_size)
+                peek_size = stream_Peek(s, &peek, img->marker_size);
+            if (peek_size >= img->marker_size &&
+                !memcmp(peek, img->marker, img->marker_size))
+                break;
+        }
+    }
+    msg_Dbg(s, "Detected image: %s",
+            vlc_fourcc_GetDescription(VIDEO_ES, img->codec));
+    return img->codec;
+}
diff --git a/src/misc/image.c b/src/misc/image.c
index 2ce3f9c..65aaf23 100644
--- a/src/misc/image.c
+++ b/src/misc/image.c
@@ -37,43 +37,10 @@
 #include <errno.h>
 
 #include <vlc_common.h>
-#include <vlc_codec.h>
-#include <vlc_meta.h>
-#include <vlc_filter.h>
-#include <vlc_es.h>
 #include <vlc_image.h>
-#include <vlc_stream.h>
-#include <vlc_fs.h>
-#include <vlc_sout.h>
 #include <libvlc.h>
 #include <vlc_modules.h>
 
-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 * );
-
-static picture_t *ImageConvert( image_handler_t *, picture_t *,
-                                video_format_t *, video_format_t * );
-static picture_t *ImageFilter( image_handler_t *, picture_t *,
-                               video_format_t *, const char *psz_module );
-
-static decoder_t *CreateDecoder( vlc_object_t *, video_format_t * );
-static void DeleteDecoder( decoder_t * );
-static encoder_t *CreateEncoder( vlc_object_t *, video_format_t *,
-                                 video_format_t * );
-static void DeleteEncoder( encoder_t * );
-static filter_t *CreateFilter( vlc_object_t *, es_format_t *,
-                               video_format_t *, const char * );
-static void DeleteFilter( filter_t * );
-
-vlc_fourcc_t image_Type2Fourcc( const char * );
-vlc_fourcc_t image_Ext2Fourcc( const char * );
-/*static const char *Fourcc2Ext( vlc_fourcc_t );*/
 
 #undef image_HandlerCreate
 /**
@@ -82,18 +49,19 @@ vlc_fourcc_t image_Ext2Fourcc( const char * );
  */
 image_handler_t *image_HandlerCreate( vlc_object_t *p_this )
 {
-    image_handler_t *p_image = calloc( 1, sizeof(image_handler_t) );
+    image_handler_t *p_image;
+
+    p_image = vlc_custom_create( p_this, sizeof( *p_image ), "image" );
     if( !p_image )
         return NULL;
 
-    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;
-    p_image->pf_convert = ImageConvert;
-    p_image->pf_filter = ImageFilter;
+    p_image->p_module = module_need( p_image, "image provider", NULL, false );
+    if( !p_image->p_module )
+    {
+        vlc_object_release( p_image );
+        msg_Err( p_this, "image provider not found" );
+        return NULL;
+    }
 
     return p_image;
 }
@@ -106,422 +74,8 @@ void image_HandlerDelete( image_handler_t *p_image )
 {
     if( !p_image ) return;
 
-    if( p_image->p_dec ) DeleteDecoder( p_image->p_dec );
-    if( p_image->p_enc ) DeleteEncoder( p_image->p_enc );
-    if( p_image->p_filter ) DeleteFilter( p_image->p_filter );
-
-    free( p_image );
-    p_image = NULL;
-}
-
-/**
- * Read an image
- *
- */
-
-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 )
-{
-    picture_t *p_pic = NULL, *p_tmp;
-
-    /* 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 );
-        if( !p_image->p_dec ) return NULL;
-    }
-
-    p_block->i_pts = p_block->i_dts = mdate();
-    while( (p_tmp = p_image->p_dec->pf_decode_video( p_image->p_dec, &p_block ))
-             != NULL )
-    {
-        if( p_pic != NULL )
-            picture_Release( p_pic );
-        p_pic = p_tmp;
-    }
-
-    if( p_pic == NULL )
-    {
-        msg_Warn( p_image->p_parent, "no image decoded" );
-        return 0;
-    }
-
-    if( !p_fmt_out->i_chroma )
-        p_fmt_out->i_chroma = p_image->p_dec->fmt_out.video.i_chroma;
-    if( !p_fmt_out->i_width && p_fmt_out->i_height )
-        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;
-
-    if( !p_fmt_out->i_height && p_fmt_out->i_width )
-        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;
-    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;
-
-    /* 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 ||
-            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 );
-            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,
-                              p_fmt_out, NULL );
-
-            if( !p_image->p_filter )
-            {
-                picture_Release( p_pic );
-                return NULL;
-            }
-        }
-        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;
-        }
-
-        p_pic = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
-        *p_fmt_out = p_image->p_filter->fmt_out.video;
-    }
-    else *p_fmt_out = p_image->p_dec->fmt_out.video;
-
-    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;
-    stream_t *p_stream = NULL;
-    int i_size;
-
-    p_stream = stream_UrlNew( p_image->p_parent, psz_url );
-
-    if( !p_stream )
-    {
-        msg_Dbg( p_image->p_parent, "could not open %s for reading",
-                 psz_url );
-        return NULL;
-    }
-
-    i_size = stream_Size( p_stream );
-
-    p_block = block_Alloc( i_size );
-
-    stream_Read( p_stream, p_block->p_buffer, i_size );
-
-    if( !p_fmt_in->i_chroma )
-    {
-        char *psz_mime = NULL;
-        stream_Control( p_stream, STREAM_GET_CONTENT_TYPE, &psz_mime );
-        if( psz_mime )
-            p_fmt_in->i_chroma = image_Mime2Fourcc( psz_mime );
-        free( psz_mime );
-    }
-    stream_Delete( p_stream );
-
-    if( !p_fmt_in->i_chroma )
-    {
-        /* Try to guess format from file name */
-        p_fmt_in->i_chroma = image_Ext2Fourcc( psz_url );
-    }
-
-    p_pic = ImageRead( p_image, p_block, p_fmt_in, p_fmt_out );
-
-    return p_pic;
-}
-
-/**
- * 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 )
-{
-    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 )
-    {
-        picture_t *p_tmp_pic;
-
-        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,
-                              &p_image->p_enc->fmt_in.video, NULL );
-
-            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;
-        }
-
-        picture_Hold( p_pic );
-
-        p_tmp_pic =
-            p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
-
-        if( likely(p_tmp_pic != NULL) )
-        {
-            p_block = p_image->p_enc->pf_encode_video( p_image->p_enc,
-                                                       p_tmp_pic );
-            p_image->p_filter->pf_video_buffer_del( p_image->p_filter,
-                                                    p_tmp_pic );
-        }
-        else
-            p_block = NULL;
-    }
-    else
-    {
-        p_block = p_image->p_enc->pf_encode_video( p_image->p_enc, p_pic );
-    }
-
-    if( !p_block )
-    {
-        msg_Dbg( p_image->p_parent, "no image encoded" );
-        return 0;
-    }
-
-    return p_block;
-}
-
-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 )
-{
-    block_t *p_block;
-    FILE *file;
-
-    if( !p_fmt_out->i_chroma )
-    {
-        /* Try to guess format from file name */
-        p_fmt_out->i_chroma = image_Ext2Fourcc( psz_url );
-    }
-
-    file = vlc_fopen( psz_url, "wb" );
-    if( !file )
-    {
-        msg_Err( p_image->p_parent, "%s: %m", psz_url );
-        return VLC_EGENERIC;
-    }
-
-    p_block = ImageWrite( p_image, p_pic, p_fmt_in, p_fmt_out );
-
-    int err = 0;
-    if( p_block )
-    {
-        if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, file ) != 1 )
-            err = errno;
-        block_Release( p_block );
-    }
-
-    if( fclose( file ) && !err )
-        err = errno;
-
-    if( err )
-    {
-       errno = err;
-       msg_Err( p_image->p_parent, "%s: %m", psz_url );
-    }
-
-    return err ? VLC_EGENERIC : VLC_SUCCESS;
-}
-
-/**
- * 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;
-
-    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;
-    }
-
-    if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_fmt_in->i_chroma;
-    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;
-
-    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 );
-        p_image->p_filter = NULL;
-    }
-
-    /* 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, p_fmt_out, NULL );
-
-        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;
-    }
-
-    picture_Hold( p_pic );
-
-    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 */
-        picture_Release( p_pif ); /* XXX: Better fix must be possible */
-        p_pif = p_image->p_filter->pf_video_buffer_new( p_image->p_filter );
-        if( p_pif )
-            picture_Copy( p_pif, p_pic );
-    }
-
-    return p_pif;
-}
-
-/**
- * Filter an image with a psz_module filter
- *
- */
-
-static picture_t *ImageFilter( image_handler_t *p_image, picture_t *p_pic,
-                               video_format_t *p_fmt, const char *psz_module )
-{
-    /* Start a filter */
-    if( !p_image->p_filter )
-    {
-        es_format_t fmt;
-        es_format_Init( &fmt, VIDEO_ES, p_fmt->i_chroma );
-        fmt.video = *p_fmt;
-
-        p_image->p_filter =
-            CreateFilter( p_image->p_parent, &fmt, &fmt.video, psz_module );
-
-        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;
-        p_image->p_filter->fmt_out.video = *p_fmt;
-    }
-
-    picture_Hold( p_pic );
-
-    return p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic );
+    module_unneed( p_image, p_image->p_module );
+    vlc_object_release( p_image );
 }
 
 /**
@@ -618,190 +172,3 @@ vlc_fourcc_t image_Mime2Fourcc( const char *psz_mime )
     return 0;
 }
 
-
-static picture_t *video_new_buffer( decoder_t *p_dec )
-{
-    p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
-    return picture_NewFromFormat( &p_dec->fmt_out.video );
-}
-
-static void video_del_buffer( decoder_t *p_dec, picture_t *p_pic )
-{
-    (void)p_dec;
-    picture_Release( p_pic );
-}
-
-static void video_link_picture( decoder_t *p_dec, picture_t *p_pic )
-{
-    (void)p_dec;
-    picture_Hold( p_pic );
-}
-
-static void video_unlink_picture( decoder_t *p_dec, picture_t *p_pic )
-{
-    (void)p_dec;
-    picture_Release( p_pic );
-}
-
-static decoder_t *CreateDecoder( vlc_object_t *p_this, video_format_t *fmt )
-{
-    decoder_t *p_dec;
-
-    p_dec = vlc_custom_create( p_this, sizeof( *p_dec ), "image decoder" );
-    if( p_dec == NULL )
-        return NULL;
-
-    p_dec->p_module = NULL;
-    es_format_Init( &p_dec->fmt_in, VIDEO_ES, fmt->i_chroma );
-    es_format_Init( &p_dec->fmt_out, VIDEO_ES, 0 );
-    p_dec->fmt_in.video = *fmt;
-    p_dec->b_pace_control = true;
-
-    p_dec->pf_vout_buffer_new = video_new_buffer;
-    p_dec->pf_vout_buffer_del = video_del_buffer;
-    p_dec->pf_picture_link    = video_link_picture;
-    p_dec->pf_picture_unlink  = video_unlink_picture;
-
-    /* Find a suitable decoder module */
-    p_dec->p_module = module_need( p_dec, "decoder", "$codec", false );
-    if( !p_dec->p_module )
-    {
-        msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'. "
-                 "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 )
-{
-    if( p_dec->p_module ) module_unneed( p_dec, p_dec->p_module );
-
-    es_format_Clean( &p_dec->fmt_in );
-    es_format_Clean( &p_dec->fmt_out );
-
-    if( p_dec->p_description )
-        vlc_meta_Delete( p_dec->p_description );
-
-    vlc_object_release( p_dec );
-    p_dec = NULL;
-}
-
-static encoder_t *CreateEncoder( vlc_object_t *p_this, video_format_t *fmt_in,
-                                 video_format_t *fmt_out )
-{
-    encoder_t *p_enc;
-
-    p_enc = sout_EncoderCreate( p_this );
-    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;
-    if( fmt_out->i_width > 0 && fmt_out->i_height > 0 )
-    {
-        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;
-        }
-    }
-    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;
-    }
-
-    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 */
-    p_enc->p_module = module_need( p_enc, "encoder", NULL, false );
-    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 )
-{
-    if( p_enc->p_module ) module_unneed( p_enc, p_enc->p_module );
-
-    es_format_Clean( &p_enc->fmt_in );
-    es_format_Clean( &p_enc->fmt_out );
-
-    vlc_object_release( p_enc );
-    p_enc = NULL;
-}
-
-static filter_t *CreateFilter( vlc_object_t *p_this, es_format_t *p_fmt_in,
-                               video_format_t *p_fmt_out,
-                               const char *psz_module )
-{
-    filter_t *p_filter;
-
-    p_filter = vlc_custom_create( p_this, sizeof(filter_t), "filter" );
-    p_filter->pf_video_buffer_new =
-        (picture_t *(*)(filter_t *))video_new_buffer;
-    p_filter->pf_video_buffer_del =
-        (void (*)(filter_t *, picture_t *))video_del_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;
-    p_filter->p_module = module_need( p_filter, "video filter2",
-                                      psz_module, false );
-
-    if( !p_filter->p_module )
-    {
-        msg_Dbg( p_filter, "no video filter found" );
-        DeleteFilter( p_filter );
-        return NULL;
-    }
-
-    return p_filter;
-}
-
-static void DeleteFilter( filter_t * p_filter )
-{
-    if( p_filter->p_module ) module_unneed( p_filter, p_filter->p_module );
-
-    es_format_Clean( &p_filter->fmt_in );
-    es_format_Clean( &p_filter->fmt_out );
-
-    vlc_object_release( p_filter );
-}
-- 
1.7.9.5



More information about the vlc-devel mailing list