[vlc-devel] [PATCH 02/24] [codec/dirac] Add dirac encoding support using dirac-research (libdirac)

davidf+nntp at woaf.net davidf+nntp at woaf.net
Thu Oct 30 12:29:31 CET 2008


From: David Flynn <davidf at rd.bbc.co.uk>

libdirac is currently a higher quality (albeit much slower) encoder than
libschroedinger.  The intention is to port its algorithim to schro and
depricate the codebase.

This patch corrects previous attempts at doing an encoder module. It
takes into account:
 - input PTS
 - encoder options
 - choosing of suitable parameters from input frame parameters

Signed-off-by: David Flynn <davidf at rd.bbc.co.uk>
---
 modules/codec/dirac.c |  865 ++++++++++++++++++++++++++++++++-----------------
 1 files changed, 562 insertions(+), 303 deletions(-)

diff --git a/modules/codec/dirac.c b/modules/codec/dirac.c
index bb37518..2d7bca7 100644
--- a/modules/codec/dirac.c
+++ b/modules/codec/dirac.c
@@ -1,11 +1,16 @@
 /*****************************************************************************
- * dirac.c: Dirac decoder/encoder module making use of libdirac.
+ * dirac.c: Dirac encoder module making use of libdirac (dirac-research).
  *          (http://www.bbc.co.uk/rd/projects/dirac/index.shtml)
+ *          ##
+ *          ## NB, this is a temporary encoder only module until schroedinger
+ *          ## offers superior encoding quality than dirac-research
+ *          ##
  *****************************************************************************
- * Copyright (C) 1999-2001 the VideoLAN team
+ * Copyright (C) 2004-2008 the VideoLAN team
  * $Id$
  *
  * Authors: Gildas Bazin <gbazin at videolan.org>
+ * Rewritten: David Flynn <davidf at rd.bbc.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,293 +34,379 @@
 # include "config.h"
 #endif
 
+#include <limits.h>
+
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_codec.h>
 #include <vlc_sout.h>
 #include <vlc_vout.h>
 
-#include <libdirac_decoder/dirac_parser.h>
 #include <libdirac_encoder/dirac_encoder.h>
 
-/*****************************************************************************
- * decoder_sys_t : theora decoder descriptor
- *****************************************************************************/
-struct decoder_sys_t
-{
-    /*
-     * Dirac properties
-     */
-    dirac_decoder_t *p_dirac;
-};
+#ifndef DIRAC_RESEARCH_VERSION_ATLEAST
+# define DIRAC_RESEARCH_VERSION_ATLEAST(x,y,z) 0
+#endif
 
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
-static int        OpenDecoder  ( vlc_object_t * );
-static void       CloseDecoder ( vlc_object_t * );
-static picture_t *DecodeBlock  ( decoder_t *p_dec, block_t **pp_block );
-
 static int  OpenEncoder( vlc_object_t *p_this );
 static void CloseEncoder( vlc_object_t *p_this );
 static block_t *Encode( encoder_t *p_enc, picture_t *p_pict );
 
 #define ENC_CFG_PREFIX "sout-dirac-"
 
+#define ENC_QUALITY_FACTOR "quality"
+#define ENC_QUALITY_FACTOR_TEXT N_("quality factor (primarily for constant quality mode)")
+#define ENC_QUALITY_FACTOR_LONGTEXT N_("")
+
+#define ENC_TARGETRATE "bitrate"
+#define ENC_TARGETRATE_TEXT N_("target bit rate in kbps (setting this enables CBR mode)")
+#define ENC_TARGETRATE_LONGTEXT N_("")
+
+#define ENC_LOSSLESS "lossless"
+#define ENC_LOSSLESS_TEXT N_("enable lossless coding (ignores qf and tr)")
+#define ENC_LOSSLESS_LONGTEXT N_("")
+
+#define ENC_PREFILTER "prefilter"
+#define ENC_PREFILTER_TEXT N_("prefilter video")
+#define ENC_PREFILTER_LONGTEXT N_("")
+static const char *const enc_prefilter_list[] =
+  { "none", "cwm", "rectlp", "diaglp" };
+static const char *const enc_prefilter_list_text[] =
+  { N_("none"), N_("cwm"), N_("rectlp"), N_("diaglp") };
+
+#define ENC_PREFILTER_STRENGTH "prefilter-strength"
+#define ENC_PREFILTER_STRENGTH_TEXT N_("prefilter strength")
+#define ENC_PREFILTER_STRENGTH_LONGTEXT N_("")
+
+#define ENC_CHROMAFMT "chroma-fmt"
+#define ENC_CHROMAFMT_TEXT N_("Chroma format 444,422,420")
+#define ENC_CHROMAFMT_LONGTEXT N_("Picking chroma format will force a conversion of the video into that format")
+static const char *const enc_chromafmt_list[] =
+  { "420", "422", "444" };
+static const char *const enc_chromafmt_list_text[] =
+  { N_("420"), N_("422"), N_("444") };
+
+#define ENC_L1SEP "l1-sep"
+#define ENC_L1SEP_TEXT N_("Seperation between 'P-style' frames")
+#define ENC_L1SEP_LONGTEXT N_("")
+
+#define ENC_L1NUM "num-l1"
+#define ENC_L1NUM_TEXT N_("Ideal number of 'P-style' frames per GOP")
+#define ENC_L1NUM_LONGTEXT N_("")
+
+#define ENC_FIELD_CODING "field-coding"
+#define ENC_FIELD_CODING_TEXT N_("Picture coding mode (for interlaced material)")
+#define ENC_FIELD_CODING_LONGTEXT N_("Field coding is where interlaced fields are coded seperately as opposed to a pseudo-progressive frame")
+
+#define ENC_MCBLK_WIDTH "mc-blk-width"
+#define ENC_MCBLK_WIDTH_TEXT N_("Width of motion compensation blocks")
+#define ENC_MCBLK_WIDTH_LONGTEXT N_("")
+
+#define ENC_MCBLK_HEIGHT "mc-blk-height"
+#define ENC_MCBLK_HEIGHT_TEXT N_("Height of motion compensation blocks")
+#define ENC_MCBLK_HEIGHT_LONGTEXT N_("")
+
+/* also known as XBSEP */
+#define ENC_MCBLK_HOVERLAP "mc-blk-hoverlap"
+#define ENC_MCBLK_HOVERLAP_TEXT N_("Percentage horizontal overlap between adjacent motion blocks")
+#define ENC_MCBLK_HOVERLAP_LONGTEXT N_("")
+
+/* also known as YBSEP */
+#define ENC_MCBLK_VOVERLAP "mc-blk-voverlap"
+#define ENC_MCBLK_VOVERLAP_TEXT N_("Percentage vertical overlap between adjacent motion blocks")
+#define ENC_MCBLK_VOVERLAP_LONGTEXT N_("")
+
+/* advanced option only */
+#define ENC_MCBLK_XBLEN "mc-blk-xblen"
+#define ENC_MCBLK_XBLEN_TEXT N_("xblen")
+#define ENC_MCBLK_XBLEN_LONGTEXT N_("")
+
+/* advanded option only */
+#define ENC_MCBLK_YBLEN "mc-blk-yblen"
+#define ENC_MCBLK_YBLEN_TEXT N_("yblen")
+#define ENC_MCBLK_YBLEN_LONGTEXT N_("")
+
+#define ENC_MVPREC "mv-prec"
+#define ENC_MVPREC_TEXT N_("motion vector precision 0,1,2,3,4")
+#define ENC_MVPREC_LONGTEXT N_("")
+static const char *const enc_mvprec_list[] =
+  { "1", "1/2", "1/4", "1/8" };
+
+#define ENC_ME_FULLSEARCH "me-full-search"
+#define ENC_ME_FULLSEARCH_TEXT N_("Exhaustive motion estimation search")
+#define ENC_ME_FULLSEARCH_LONGTEXT N_("Perform an exhaustive motion vector search (Not recommended)")
+
+#define ENC_ME_COMBINED "me-combined"
+#define ENC_ME_COMBINED_TEXT N_("Use chroma in motion estimation")
+#define ENC_ME_COMBINED_LONGTEXT N_("")
+
+#define ENC_DWTINTRA "dwt-intra"
+#define ENC_DWTINTRA_TEXT N_("dwt filter to use on intra pictures")
+#define ENC_DWTINTRA_LONGTEXT N_("")
+
+#define ENC_DWTINTER "dwt-inter"
+#define ENC_DWTINTER_TEXT N_("dwt filter to use on motion compensated pictures")
+#define ENC_DWTINTER_LONGTEXT N_("")
+
+#define ENC_DWTDEPTH "dwt-depth"
+#define ENC_DWTDEPTH_TEXT N_("number of dwt iterations")
+#define ENC_DWTDEPTH_LONGTEXT N_("")
+
+#define ENC_MULTIQUANT "multi-quant"
+#define ENC_MULTIQUANT_TEXT N_("enable quantizer per codeblock mode")
+#define ENC_MULTIQUANT_LONGTEXT N_("")
+
+#define ENC_SPARTITION "spartition"
+#define ENC_SPARTITION_TEXT N_("enable spatial partitioning (codeblocks)")
+#define ENC_SPARTITION_LONGTEXT N_("")
+
+#define ENC_VLC "vlc"
+#define ENC_VLC_TEXT N_("disable arithmetic coding where possible")
+#define ENC_VLC_LONGTEXT N_("")
+
+/* visual modelling */
+
+#define ENC_CPD "cpd"
+#define ENC_CPD_TEXT N_("cycles-per-degree")
+#define ENC_CPD_LONGTEXT N_("")
+
 static const char *const ppsz_enc_options[] = {
-    "quality", NULL
+    ENC_QUALITY_FACTOR, ENC_TARGETRATE, ENC_LOSSLESS, ENC_PREFILTER,
+    ENC_CHROMAFMT, ENC_L1SEP, ENC_L1NUM, ENC_FIELD_CODING,
+    ENC_MCBLK_WIDTH, ENC_MCBLK_HEIGHT, ENC_MCBLK_HOVERLAP, ENC_MCBLK_VOVERLAP,
+    ENC_MVPREC, ENC_ME_FULLSEARCH, ENC_ME_COMBINED,
+    ENC_DWTINTRA, ENC_DWTINTER, ENC_DWTDEPTH,
+    ENC_MULTIQUANT, ENC_SPARTITION, ENC_VLC,
+    ENC_CPD,
+    NULL
 };
 
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
-#define ENC_QUALITY_TEXT N_("Encoding quality")
-#define ENC_QUALITY_LONGTEXT N_( \
-  "Quality of the encoding between 1.0 (low) and 10.0 (high)." )
 
-vlc_module_begin ()
+vlc_module_begin()
     set_category( CAT_INPUT )
     set_subcategory( SUBCAT_INPUT_VCODEC )
-    set_description( N_("Dirac video decoder") )
-    set_capability( "decoder", 100 )
-    set_callbacks( OpenDecoder, CloseDecoder )
-    add_shortcut( "dirac" )
-
-    add_submodule ()
-    set_description( N_("Dirac video encoder") )
+    set_description( N_("Dirac video encoder using dirac-research library") )
     set_capability( "encoder", 100 )
     set_callbacks( OpenEncoder, CloseEncoder )
-    add_float( ENC_CFG_PREFIX "quality", 7.0, NULL, ENC_QUALITY_TEXT,
-               ENC_QUALITY_LONGTEXT, false );
-
-vlc_module_end ()
 
-/*****************************************************************************
- * OpenDecoder: probe the decoder and return score
- *****************************************************************************/
-static int OpenDecoder( vlc_object_t *p_this )
-{
-    decoder_t *p_dec = (decoder_t*)p_this;
-    decoder_sys_t *p_sys;
-    dirac_decoder_t *p_dirac;
+    add_float( ENC_CFG_PREFIX ENC_QUALITY_FACTOR, -1.0, NULL,
+               ENC_QUALITY_FACTOR_TEXT, ENC_QUALITY_FACTOR_LONGTEXT, false );
+    change_float_range(0., 10.);
+
+    add_integer( ENC_CFG_PREFIX ENC_TARGETRATE, 0, NULL,
+                 ENC_TARGETRATE_TEXT, ENC_TARGETRATE_LONGTEXT, false );
+    change_integer_range(0, INT_MAX);
+
+    add_bool( ENC_CFG_PREFIX ENC_LOSSLESS, false, NULL,
+              ENC_LOSSLESS_TEXT, ENC_LOSSLESS_LONGTEXT, false );
+
+    add_string( ENC_CFG_PREFIX ENC_PREFILTER, "diaglp", NULL,
+                ENC_PREFILTER_TEXT, ENC_PREFILTER_LONGTEXT, false );
+    change_string_list( enc_prefilter_list, enc_prefilter_list_text, 0 );
+
+    add_integer( ENC_CFG_PREFIX ENC_PREFILTER_STRENGTH, 4, NULL,
+                 ENC_PREFILTER_STRENGTH_TEXT, ENC_PREFILTER_STRENGTH_LONGTEXT, false );
+    change_integer_range(0, 10);
+
+    add_string( ENC_CFG_PREFIX ENC_CHROMAFMT, "420", NULL,
+                ENC_CHROMAFMT_TEXT, ENC_CHROMAFMT_LONGTEXT, false );
+    change_string_list( enc_chromafmt_list, enc_chromafmt_list_text, 0 );
+
+    add_integer( ENC_CFG_PREFIX ENC_L1SEP, -1, NULL,
+                 ENC_L1SEP_TEXT, ENC_L1SEP_LONGTEXT, false );
+    change_integer_range(0, INT_MAX);
+
+    add_integer( ENC_CFG_PREFIX ENC_L1NUM, -1, NULL,
+                 ENC_L1NUM_TEXT, ENC_L1NUM_LONGTEXT, false );
+    change_integer_range(0, INT_MAX);
+
+    add_bool( ENC_CFG_PREFIX ENC_FIELD_CODING, true, NULL,
+              ENC_FIELD_CODING_TEXT, ENC_FIELD_CODING_LONGTEXT, false );
+
+    add_string( ENC_CFG_PREFIX ENC_MVPREC, "1/2", NULL,
+                ENC_MVPREC_TEXT, ENC_MVPREC_LONGTEXT, false );
+    change_string_list( enc_mvprec_list, enc_mvprec_list, 0 );
+
+    add_integer( ENC_CFG_PREFIX ENC_MCBLK_WIDTH, -1, NULL,
+                 ENC_MCBLK_WIDTH_TEXT, ENC_MCBLK_WIDTH_LONGTEXT, false );
+    //add_depricated_alias( ENC_CFG_PREFIX ENC_MCBLK_XBLEN );
+    change_integer_range(0, INT_MAX);
+
+    add_integer( ENC_CFG_PREFIX ENC_MCBLK_HEIGHT, -1, NULL,
+                 ENC_MCBLK_HEIGHT, ENC_MCBLK_HEIGHT_LONGTEXT, false );
+    //add_depricated_alias( ENC_CFG_PREFIX ENC_MCBLK_YBLEN );
+    change_integer_range(0, INT_MAX);
+
+    add_integer( ENC_CFG_PREFIX ENC_MCBLK_HOVERLAP, -1, NULL,
+                 ENC_MCBLK_HOVERLAP_TEXT, ENC_MCBLK_HOVERLAP_LONGTEXT, false );
+    change_integer_range(0, 100);
+
+    add_integer( ENC_CFG_PREFIX ENC_MCBLK_VOVERLAP, -1, NULL,
+                 ENC_MCBLK_VOVERLAP_TEXT, ENC_MCBLK_VOVERLAP_LONGTEXT, false );
+    change_integer_range(0, 100);
+
+    /* advanced option only */
+    add_integer( ENC_CFG_PREFIX ENC_MCBLK_XBLEN, -1, NULL,
+                 ENC_MCBLK_XBLEN_TEXT, ENC_MCBLK_XBLEN_LONGTEXT, true );
+    change_integer_range(0, INT_MAX);
+    /* advanced option only */
+    add_integer( ENC_CFG_PREFIX ENC_MCBLK_YBLEN, -1, NULL,
+                 ENC_MCBLK_YBLEN_TEXT, ENC_MCBLK_YBLEN_LONGTEXT, true );
+    change_integer_range(0, INT_MAX);
+
+    add_bool( ENC_CFG_PREFIX ENC_ME_FULLSEARCH, false, NULL,
+              ENC_ME_FULLSEARCH_TEXT, ENC_ME_FULLSEARCH_LONGTEXT, false );
+
+#if DIRAC_RESEARCH_VERSION_ATLEAST(1,0,1)
+    add_bool( ENC_CFG_PREFIX ENC_ME_COMBINED, true, NULL,
+              ENC_ME_COMBINED_TEXT, ENC_ME_COMBINED_LONGTEXT, false );
+#endif
 
-    if( p_dec->fmt_in.i_codec != VLC_FOURCC('d','r','a','c') )
-    {
-        return VLC_EGENERIC;
-    }
+    add_integer( ENC_CFG_PREFIX ENC_DWTINTRA, -1, NULL,
+                 ENC_DWTINTRA_TEXT, ENC_DWTINTRA_LONGTEXT, false );
+    change_integer_range(0, 6);
 
-    /* Initialise the dirac decoder */
-    if( !(p_dirac = dirac_decoder_init(0)) ) return VLC_EGENERIC;
+    add_integer( ENC_CFG_PREFIX ENC_DWTINTER, -1, NULL,
+                 ENC_DWTINTER_TEXT, ENC_DWTINTER_LONGTEXT, false );
+    change_integer_range(0, 6);
 
-    /* Allocate the memory needed to store the decoder's structure */
-    if( ( p_dec->p_sys = p_sys =
-          (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
-        return VLC_ENOMEM;
+    add_integer( ENC_CFG_PREFIX ENC_DWTDEPTH, -1, NULL,
+                 ENC_DWTDEPTH_TEXT, ENC_DWTDEPTH_LONGTEXT, false );
+    change_integer_range(0, 4);
 
-    p_sys->p_dirac = p_dirac;
+    add_bool( ENC_CFG_PREFIX ENC_MULTIQUANT, -1, NULL,
+              ENC_MULTIQUANT_TEXT, ENC_MULTIQUANT_LONGTEXT, false );
 
-    /* Set output properties */
-    p_dec->fmt_out.i_cat = VIDEO_ES;
-    p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0');
+    add_bool( ENC_CFG_PREFIX ENC_SPARTITION, -1, NULL,
+              ENC_SPARTITION_TEXT, ENC_SPARTITION_LONGTEXT, false );
 
-    /* Set callbacks */
-    p_dec->pf_decode_video = DecodeBlock;
+    add_bool( ENC_CFG_PREFIX ENC_VLC, false, NULL,
+              ENC_VLC_TEXT, ENC_VLC_LONGTEXT, false );
 
-    return VLC_SUCCESS;
-}
+    add_integer( ENC_CFG_PREFIX ENC_CPD, -1, NULL,
+                 ENC_CPD_TEXT, ENC_CPD_LONGTEXT, false );
+    change_integer_range(0, INT_MAX);
+vlc_module_end()
 
-static void FreeFrameBuffer( dirac_decoder_t *p_dirac )
+/*****************************************************************************
+ * picture_pts_t : store pts alongside picture number, not carried through
+ * encoder
+ *****************************************************************************/
+struct picture_pts_t
 {
-    if( p_dirac->fbuf )
-    {
-        int i;
-        for( i = 0; i < 3; i++ )
-        {
-            free( p_dirac->fbuf->buf[i] );
-            p_dirac->fbuf->buf[i] = 0;
-        }
-    }
-}
+   int i_empty;      /* entry is invalid */
+   uint32_t u_pnum;  /* dirac picture number */
+   mtime_t i_pts;    /* associated pts */
+};
 
 /*****************************************************************************
- * GetNewPicture: Get a new picture from the vout and copy the decoder output
+ * encoder_sys_t : dirac encoder descriptor
  *****************************************************************************/
-static picture_t *GetNewPicture( decoder_t *p_dec )
+#define ENC_BUFSIZE 1024*1024*10
+#define PTS_TLB_SIZE 16
+struct encoder_sys_t
 {
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    picture_t *p_pic;
-    int i_plane;
-
-    switch( p_sys->p_dirac->src_params.chroma )
-    {
-    case format420: p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0'); break;
-    case format422: p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','2'); break;
-    case format444: p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','4','4'); break;    // XXX 0.6 ?
-    default:
-        p_dec->fmt_out.i_codec = 0;
-        break;
-    }
-
-    p_dec->fmt_out.video.i_visible_width =
-    p_dec->fmt_out.video.i_width = p_sys->p_dirac->src_params.width;
-    p_dec->fmt_out.video.i_visible_height =
-    p_dec->fmt_out.video.i_height = p_sys->p_dirac->src_params.height;
-    p_dec->fmt_out.video.i_aspect = VOUT_ASPECT_FACTOR * 4 / 3;
-
-    p_dec->fmt_out.video.i_frame_rate =
-        p_sys->p_dirac->src_params.frame_rate.numerator;
-    p_dec->fmt_out.video.i_frame_rate_base =
-        p_sys->p_dirac->src_params.frame_rate.denominator;
-
-    /* Get a new picture */
-    p_pic = decoder_NewPicture( p_dec );
-
-    if( p_pic == NULL ) return NULL;
-    p_pic->b_progressive = !p_sys->p_dirac->src_params.source_sampling;
-    p_pic->b_top_field_first = p_sys->p_dirac->src_params.topfieldfirst;
-
-    p_pic->i_nb_fields = 2;
+    dirac_encoder_t *p_dirac;
+    dirac_encoder_context_t ctx;
+    bool b_auto_field_coding;
 
-    /* Copy picture stride by stride */
-    for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
-    {
-        int i_line, i_width, i_dst_stride;
-        uint8_t *p_src = p_sys->p_dirac->fbuf->buf[i_plane];
-        uint8_t *p_dst = p_pic->p[i_plane].p_pixels;
+    uint8_t *p_buffer_in;
+    int i_buffer_in;
+    uint32_t i_input_picnum;
 
-        i_width = p_pic->p[i_plane].i_visible_pitch;
-        i_dst_stride = p_pic->p[i_plane].i_pitch;
+    uint8_t p_buffer_out[ENC_BUFSIZE];
 
-        for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines; i_line++ )
-        {
-            vlc_memcpy( p_dst, p_src, i_width );
-            p_src += i_width;
-            p_dst += i_dst_stride;
-        }
-    }
+    struct picture_pts_t pts_tlb[PTS_TLB_SIZE];
+    mtime_t i_lastdts;
+    mtime_t i_pts_offset;
+};
 
-    return p_pic;
-}
+static struct {
+    unsigned int i_height;
+    int i_approx_fps;
+    VideoFormat i_vf;
+} dirac_format_guess[] = {
+    {120, 15, VIDEO_FORMAT_QSIF525},
+    {144, 12, VIDEO_FORMAT_QCIF},
+    {240, 15, VIDEO_FORMAT_SIF525},
+    {288, 12, VIDEO_FORMAT_CIF},
+    {480, 15, VIDEO_FORMAT_4SIF525},
+    {576, 12, VIDEO_FORMAT_4CIF},
+    {480, 30, VIDEO_FORMAT_SD_480I60},
+    {576, 25, VIDEO_FORMAT_SD_576I50},
+    {720, 60, VIDEO_FORMAT_HD_720P60},
+    {720, 50, VIDEO_FORMAT_HD_720P50},
+    {1080, 30, VIDEO_FORMAT_HD_1080I60},
+    {1080, 25, VIDEO_FORMAT_HD_1080I50},
+    {1080, 60, VIDEO_FORMAT_HD_1080P60},
+    {1080, 50, VIDEO_FORMAT_HD_1080P50},
+    {1080, 24, VIDEO_FORMAT_DIGI_CINEMA_2K24},
+    {2160, 24, VIDEO_FORMAT_DIGI_CINEMA_4K24},
+    {2160, 60, VIDEO_FORMAT_UHDTV_4K60},
+    {2160, 50, VIDEO_FORMAT_UHDTV_4K50},
+    {2160, 60, VIDEO_FORMAT_UHDTV_8K60},
+    {2160, 50, VIDEO_FORMAT_UHDTV_8K50},
+    {0, 0, 0},
+};
 
 /*****************************************************************************
- * CloseDecoder: decoder destruction
+ * ResetPTStlb: Purge all entries in @p_dec@'s PTS-tlb
  *****************************************************************************/
-static void CloseDecoder( vlc_object_t *p_this )
+static void ResetPTStlb( encoder_t *p_enc )
 {
-    decoder_t *p_dec = (decoder_t *)p_this;
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    FreeFrameBuffer( p_sys->p_dirac );
-    dirac_decoder_close( p_sys->p_dirac );
-    free( p_sys );
+    encoder_sys_t *p_sys = p_enc->p_sys;
+    for( int i=0; i<PTS_TLB_SIZE; i++) {
+        p_sys->pts_tlb[i].i_empty = 1;
+    }
 }
 
-/****************************************************************************
- * DecodeBlock: the whole thing
- ****************************************************************************
- * This function must be fed with complete frames.
- ****************************************************************************/
-static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
+/*****************************************************************************
+ * StorePicturePTS: Store the PTS value for a particular picture number
+ *****************************************************************************/
+static void StorePicturePTS( encoder_t *p_enc, uint32_t u_pnum, mtime_t i_pts )
 {
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    dirac_decoder_state_t state;
-    picture_t *p_pic;
-    block_t *p_block;
-
-    if( !pp_block || !*pp_block ) return NULL;
-
-    p_block = *pp_block;
-
-    while( 1 )
-    {
-        state = dirac_parse( p_sys->p_dirac );
-
-        switch( state )
-        {
-        case STATE_BUFFER:
-            if( !p_block->i_buffer )
-            {
-                block_Release( p_block );
-                return NULL;
-            }
-
-            msg_Dbg( p_dec, "STATE_BUFFER" );
-            dirac_buffer( p_sys->p_dirac, p_block->p_buffer,
-                          p_block->p_buffer + p_block->i_buffer );
-
-            p_block->i_buffer = 0;
-            break;
-
-        case STATE_SEQUENCE:
-        {
-            /* Initialize video output */
-            uint8_t *buf[3];
-
-            msg_Dbg( p_dec, "%dx%d, chroma %i, %f fps",
-                     p_sys->p_dirac->src_params.width,
-                     p_sys->p_dirac->src_params.height,
-                     p_sys->p_dirac->src_params.chroma,
-                     (float)p_sys->p_dirac->src_params.frame_rate.numerator/
-                     p_sys->p_dirac->src_params.frame_rate.denominator );
-
-            FreeFrameBuffer( p_sys->p_dirac );
-            buf[0] = malloc( p_sys->p_dirac->src_params.width *
-                             p_sys->p_dirac->src_params.height );
-            buf[1] = malloc( p_sys->p_dirac->src_params.chroma_width *
-                             p_sys->p_dirac->src_params.chroma_height );
-            buf[2] = malloc( p_sys->p_dirac->src_params.chroma_width *
-                             p_sys->p_dirac->src_params.chroma_height );
-
-            dirac_set_buf( p_sys->p_dirac, buf, NULL );
-            break;
-        }
-
-        case STATE_SEQUENCE_END:
-            msg_Dbg( p_dec, "SEQUENCE_END" );
-            FreeFrameBuffer( p_sys->p_dirac );
-            break;
-
-        case STATE_PICTURE_AVAIL:
-            msg_Dbg( p_dec, "PICTURE_AVAIL : frame_num=%d",
-                     p_sys->p_dirac->frame_num );
+    encoder_sys_t *p_sys = p_enc->p_sys;
 
-            /* Picture available for display */
-            p_pic = GetNewPicture( p_dec );
-            p_pic->date = p_block->i_pts > 0 ? p_block->i_pts : p_block->i_dts;
-            p_pic->b_force = 1; // HACK
-            return p_pic;
-            break;
+    for( int i=0; i<PTS_TLB_SIZE; i++ ) {
+        if( p_sys->pts_tlb[i].i_empty ) {
 
-        case STATE_INVALID:
-            msg_Dbg( p_dec, "STATE_INVALID" );
-            break;
+            p_sys->pts_tlb[i].u_pnum = u_pnum;
+            p_sys->pts_tlb[i].i_pts = i_pts;
+            p_sys->pts_tlb[i].i_empty = 0;
 
-        default:
-            break;
+            return;
         }
     }
 
-    /* Never reached */
-    return NULL;
+    msg_Err( p_enc, "Could not store PTS %"PRId64" for frame %u", i_pts, u_pnum );
 }
 
 /*****************************************************************************
- * encoder_sys_t : dirac encoder descriptor
+ * GetPicturePTS: Retrieve the PTS value for a particular picture number
  *****************************************************************************/
-#define ENC_BUFSIZE 1024*1024
-struct encoder_sys_t
+static mtime_t GetPicturePTS( encoder_t *p_enc, uint32_t u_pnum )
 {
-    /*
-     * Dirac properties
-     */
-    dirac_encoder_t *p_dirac;
-    dirac_encoder_context_t ctx;
+    encoder_sys_t *p_sys = p_enc->p_sys;
 
-    uint8_t *p_buffer_in;
-    int i_buffer_in;
+    for( int i=0; i<PTS_TLB_SIZE; i++ ) {
+        if( (!p_sys->pts_tlb[i].i_empty) &&
+            (p_sys->pts_tlb[i].u_pnum == u_pnum)) {
 
-    uint8_t p_buffer_out[ENC_BUFSIZE];
-};
+             p_sys->pts_tlb[i].i_empty = 1;
+             return p_sys->pts_tlb[i].i_pts;
+        }
+    }
+
+    msg_Err( p_enc, "Could not retrieve PTS for picture %u", u_pnum );
+    return 0;
+}
 
 /*****************************************************************************
  * OpenEncoder: probe the encoder and return score
@@ -325,7 +416,6 @@ static int OpenEncoder( vlc_object_t *p_this )
     encoder_t *p_enc = (encoder_t *)p_this;
     encoder_sys_t *p_sys = p_enc->p_sys;
     vlc_value_t val;
-    float f_quality;
 
     if( p_enc->fmt_out.i_codec != VLC_FOURCC('d','r','a','c') &&
         !p_enc->b_force )
@@ -337,49 +427,217 @@ static int OpenEncoder( vlc_object_t *p_this )
     if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL )
         return VLC_ENOMEM;
     memset( p_sys, 0, sizeof(encoder_sys_t) );
-    p_enc->p_sys = p_sys;
 
+    p_enc->p_sys = p_sys;
     p_enc->pf_encode_video = Encode;
-    p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0');
-    p_enc->fmt_in.video.i_bits_per_pixel = 12;
     p_enc->fmt_out.i_codec = VLC_FOURCC('d','r','a','c');
 
-    config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg );
+    ResetPTStlb( p_enc );
+
+    /* guess the video format based upon number of lines and picture height */
+    int i = 0;
+    VideoFormat guessed_video_fmt = VIDEO_FORMAT_CUSTOM;
+    do {
+        if( dirac_format_guess[i].i_height != p_enc->fmt_in.video.i_height )
+            continue;
+        int src_fps = p_enc->fmt_in.video.i_frame_rate / p_enc->fmt_in.video.i_frame_rate_base;
+        int delta_fps = abs( dirac_format_guess[i].i_approx_fps - src_fps );
+        if( delta_fps > 2 )
+            continue;
 
-    dirac_encoder_context_init( &p_sys->ctx, VIDEO_FORMAT_CUSTOM );
-    /* */
+        guessed_video_fmt = dirac_format_guess[i].i_vf;
+        break;
+    } while( dirac_format_guess[++i].i_height );
+
+    dirac_encoder_context_init( &p_sys->ctx, guessed_video_fmt );
+
+    /* constants set from the input video format */
     p_sys->ctx.src_params.width = p_enc->fmt_in.video.i_width;
     p_sys->ctx.src_params.height = p_enc->fmt_in.video.i_height;
-    p_sys->ctx.src_params.chroma = format420;
-    /* */
-    p_sys->ctx.src_params.frame_rate.numerator =
-        p_enc->fmt_in.video.i_frame_rate;
-    p_sys->ctx.src_params.frame_rate.denominator =
-        p_enc->fmt_in.video.i_frame_rate_base;
-    p_sys->ctx.src_params.source_sampling = 0;
-    p_sys->ctx.src_params.topfieldfirst = 0;
-
-    var_Get( p_enc, ENC_CFG_PREFIX "quality", &val );
-    f_quality = val.f_float;
-    if( f_quality > 10 ) f_quality = 10;
-    if( f_quality < 1 ) f_quality = 1;
-    p_sys->ctx.enc_params.qf = f_quality;
-
-    /* Initialise the encoder with the encoder context */
-    p_sys->p_dirac = dirac_encoder_init( &p_sys->ctx, 0 );
+    p_sys->ctx.src_params.frame_rate.numerator = p_enc->fmt_in.video.i_frame_rate;
+    p_sys->ctx.src_params.frame_rate.denominator = p_enc->fmt_in.video.i_frame_rate_base;
+    vlc_ureduce( (unsigned*) &p_sys->ctx.src_params.pix_asr.numerator,
+                 (unsigned*) &p_sys->ctx.src_params.pix_asr.denominator,
+                 p_enc->fmt_in.video.i_height * p_enc->fmt_in.video.i_aspect,
+                 p_enc->fmt_in.video.i_width * VOUT_ASPECT_FACTOR,
+                 0 );
+
+    config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg );
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_CHROMAFMT, &val );
+    if ( !val.psz_string ) {}
+    else if ( !strcmp( val.psz_string, "420" ) ) {
+        p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0');
+        p_enc->fmt_in.video.i_bits_per_pixel = 12;
+        p_sys->ctx.src_params.chroma = format420;
+        p_sys->i_buffer_in = p_enc->fmt_in.video.i_width * p_enc->fmt_in.video.i_height * 3 / 2;
+    }
+    else if ( !strcmp( val.psz_string, "422" ) ) {
+        p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','2');
+        p_enc->fmt_in.video.i_bits_per_pixel = 16;
+        p_sys->ctx.src_params.chroma = format422;
+        p_sys->i_buffer_in = p_enc->fmt_in.video.i_width * p_enc->fmt_in.video.i_height * 2;
+    }
+    else if ( !strcmp( val.psz_string, "444" ) ) {
+        p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','4','4');
+        p_enc->fmt_in.video.i_bits_per_pixel = 24;
+        p_sys->ctx.src_params.chroma = format444;
+        p_sys->i_buffer_in = p_enc->fmt_in.video.i_width * p_enc->fmt_in.video.i_height * 3;
+    }
+    if( val.psz_string )
+        free( val.psz_string );
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_QUALITY_FACTOR, &val );
+    if( val.f_float > 0 )
+        p_sys->ctx.enc_params.qf = val.f_float;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_TARGETRATE, &val );
+    p_sys->ctx.enc_params.trate = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_LOSSLESS, &val );
+    p_sys->ctx.enc_params.lossless = val.b_bool;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_PREFILTER, &val );
+    if ( !val.psz_string ) {}
+    else if ( !strcmp( val.psz_string, "none" ) ) {
+        p_sys->ctx.enc_params.prefilter = NO_PF;
+    }
+    else if ( !strcmp( val.psz_string, "cwm" ) ) {
+        p_sys->ctx.enc_params.prefilter = CWM;
+    }
+    else if ( !strcmp( val.psz_string, "rectlp" ) ) {
+        p_sys->ctx.enc_params.prefilter = RECTLP;
+    }
+    else if ( !strcmp( val.psz_string, "diaglp" ) ) {
+        p_sys->ctx.enc_params.prefilter = DIAGLP;
+    }
+    if( val.psz_string )
+        free( val.psz_string );
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_PREFILTER_STRENGTH, &val );
+    p_sys->ctx.enc_params.prefilter_strength = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_L1SEP, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.L1_sep = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_L1NUM, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.num_L1 = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_FIELD_CODING, &val );
+//        p_sys->ctx.enc_params.picture_coding_mode = val.b_bool;
+    p_sys->b_auto_field_coding = val.b_bool;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MVPREC, &val );
+    if ( !val.psz_string ) {}
+    else if ( !strcmp( val.psz_string, "1" ) ) {
+        p_sys->ctx.enc_params.mv_precision = MV_PRECISION_PIXEL;
+    }
+    else if ( !strcmp( val.psz_string, "1/2" ) ) {
+        p_sys->ctx.enc_params.mv_precision = MV_PRECISION_HALF_PIXEL;
+    }
+    else if ( !strcmp( val.psz_string, "1/4" ) ) {
+        p_sys->ctx.enc_params.mv_precision = MV_PRECISION_QUARTER_PIXEL;
+    }
+    else if ( !strcmp( val.psz_string, "1/8" ) ) {
+        p_sys->ctx.enc_params.mv_precision = MV_PRECISION_EIGHTH_PIXEL;
+    }
+    if( val.psz_string )
+        free( val.psz_string );
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MCBLK_WIDTH, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.xbsep = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MCBLK_HEIGHT, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.ybsep = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MCBLK_HOVERLAP, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.xblen = val.i_int / 0;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MCBLK_VOVERLAP, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.yblen = val.i_int / 0;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MCBLK_XBLEN, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.xblen = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MCBLK_YBLEN, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.yblen = val.i_int;
+
+#if 0
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_ME_FULLSEARCH, &val );
+    p_sys->ctx.enc_params.full_search = 1;
+        xxx needs x_range_me and y_range_me
+#endif
+
+#if DIRAC_RESEARCH_VERSION_ATLEAST(1,0,1)
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_ME_COMBINED, &val );
+    p_sys->ctx.enc_params.combined_me = val.b_bool;
+#endif
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_DWTINTRA, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.intra_wlt_filter = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_DWTINTER, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.inter_wlt_filter = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_DWTDEPTH, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.wlt_depth = val.i_int;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_MULTIQUANT, &val );
+    if( val.b_bool > -1 )
+        p_sys->ctx.enc_params.multi_quants = val.b_bool;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_SPARTITION, &val );
+    if( val.b_bool > -1 )
+        p_sys->ctx.enc_params.spatial_partition = val.b_bool;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_VLC, &val );
+    p_sys->ctx.enc_params.using_ac = !val.b_bool;
+
+    var_Get( p_enc, ENC_CFG_PREFIX ENC_CPD, &val );
+    if( val.i_int > -1 )
+        p_sys->ctx.enc_params.cpd = val.i_int;
 
     /* Set the buffer size for the encoded picture */
-    p_sys->i_buffer_in = p_enc->fmt_in.video.i_width *
-        p_enc->fmt_in.video.i_height * 3 / 2;
     p_sys->p_buffer_in = malloc( p_sys->i_buffer_in );
 
     return VLC_SUCCESS;
 }
 
+/* Attempt to find dirac picture number in an encapsulation unit */
+static uint32_t ReadDiracPictureNumber( block_t *p_block )
+{
+    uint32_t u_pos = 4;
+    /* protect against falling off the edge */
+    while ( u_pos + 13 < p_block->i_buffer ) {
+        /* find the picture startcode */
+        if ( p_block->p_buffer[u_pos] & 0x08 ) {
+            return GetDWBE( p_block->p_buffer + u_pos + 9 );
+        }
+        /* skip to the next dirac data unit */
+        uint32_t u_npo = GetDWBE( p_block->p_buffer + u_pos + 1 );
+        if (u_npo == 0)
+            u_npo = 13;
+        u_pos += u_npo;
+    }
+    return -1;
+}
+
+
 /****************************************************************************
  * Encode: the whole thing
  ****************************************************************************
- * This function spits out ogg packets.
+ * This function spits out encapsulation units.
  ****************************************************************************/
 static block_t *Encode( encoder_t *p_enc, picture_t *p_pic )
 {
@@ -388,7 +646,26 @@ static block_t *Encode( encoder_t *p_enc, picture_t *p_pic )
     int i_plane, i_line, i_width, i_src_stride;
     uint8_t *p_dst;
 
-    /* Copy input picture in encoder input buffer (stride by stride) */
+    /* we only know if the sequence is interlaced when the first
+     * picture arrives, so final setup is done here */
+    /* XXX todo, detect change of interlace */
+    p_sys->ctx.src_params.topfieldfirst = p_pic->b_top_field_first;
+    p_sys->ctx.src_params.source_sampling = !p_pic->b_progressive;
+
+    if( p_sys->b_auto_field_coding )
+        p_sys->ctx.enc_params.picture_coding_mode = !p_pic->b_progressive;
+
+    if( !p_sys->p_dirac )
+    {
+        /* Initialise the encoder with the encoder context */
+        p_sys->p_dirac = dirac_encoder_init( &p_sys->ctx, 0 );
+        p_sys->i_lastdts = p_pic->date;
+        p_sys->i_pts_offset = 0;
+    }
+
+    /* Copy input picture into encoder input buffer (stride by stride) */
+    /* Would be lovely to just pass the picture in, but there is noway for the
+     * library to free it */
     p_dst = p_sys->p_buffer_in;
     for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
     {
@@ -406,57 +683,40 @@ static block_t *Encode( encoder_t *p_enc, picture_t *p_pic )
 
     /* Load one frame of data into encoder */
     if( dirac_encoder_load( p_sys->p_dirac, p_sys->p_buffer_in,
-                            p_sys->i_buffer_in ) >= 0 )
+                            p_sys->i_buffer_in ) < 0 )
     {
-        dirac_encoder_state_t state;
+        msg_Dbg( p_enc, "dirac_encoder_load() error" );
+        return NULL;
+    }
 
-        msg_Dbg( p_enc, "dirac_encoder_load" );
+    StorePicturePTS( p_enc, p_sys->i_input_picnum, p_pic->date );
+    p_sys->i_input_picnum++;
 
-        /* Retrieve encoded frames from encoder */
-        do
-        {
-            p_sys->p_dirac->enc_buf.buffer = p_sys->p_buffer_out;
-            p_sys->p_dirac->enc_buf.size = ENC_BUFSIZE;
-            state = dirac_encoder_output( p_sys->p_dirac );
-            msg_Dbg( p_enc, "dirac_encoder_output: %i", state );
-            switch( state )
-            {
-            case ENC_STATE_AVAIL:
-                 // Encoded frame available in encoder->enc_buf
-                 // Encoded frame params available in enccoder->enc_fparams
-                 // Encoded frame stats available in enccoder->enc_fstats
-                 p_block = block_New( p_enc, p_sys->p_dirac->enc_buf.size );
-                 memcpy( p_block->p_buffer, p_sys->p_dirac->enc_buf.buffer,
-                         p_sys->p_dirac->enc_buf.size );
-                 p_block->i_dts = p_block->i_pts = p_pic->date;
-                 block_ChainAppend( &p_chain, p_block );
-
-                 break;
-            case ENC_STATE_BUFFER:
-                break;
-            case ENC_STATE_INVALID:
-            default:
-                break;
-            }
-            if( p_sys->p_dirac->decoded_frame_avail )
-            {
-                //locally decoded frame is available in
-                //encoder->dec_buf
-                //locally decoded frame parameters available
-                //in encoder->dec_fparams
-            }
-            if( p_sys->p_dirac->instr_data_avail )
-            {
-                //Instrumentation data (motion vectors etc.)
-                //available in encoder->instr
-            }
-
-        } while( state == ENC_STATE_AVAIL );
-    }
-    else
+    dirac_encoder_state_t state;
+    /* Retrieve encoded frames from encoder */
+    do
     {
-        msg_Dbg( p_enc, "dirac_encoder_load() error" );
-    }
+        p_sys->p_dirac->enc_buf.buffer = p_sys->p_buffer_out;
+        p_sys->p_dirac->enc_buf.size = ENC_BUFSIZE;
+        state = dirac_encoder_output( p_sys->p_dirac );
+        switch( state )
+        {
+        case ENC_STATE_AVAIL:
+             p_block = block_New( p_enc, p_sys->p_dirac->enc_buf.size );
+             memcpy( p_block->p_buffer, p_sys->p_dirac->enc_buf.buffer,
+                     p_sys->p_dirac->enc_buf.size );
+             /* todo dts */
+             p_block->i_pts = GetPicturePTS( p_enc, ReadDiracPictureNumber( p_block ));
+             block_ChainAppend( &p_chain, p_block );
+             break;
+
+        case ENC_STATE_BUFFER:
+            break;
+        case ENC_STATE_INVALID:
+        default:
+            break;
+        }
+    } while( state == ENC_STATE_AVAIL );
 
     return p_chain;
 }
@@ -469,12 +729,11 @@ static void CloseEncoder( vlc_object_t *p_this )
     encoder_t *p_enc = (encoder_t *)p_this;
     encoder_sys_t *p_sys = p_enc->p_sys;
 
-    msg_Dbg( p_enc, "resulting bit-rate: %lld bits/sec",
-             p_sys->p_dirac->enc_seqstats.bit_rate );
-
     /* Free the encoder resources */
-    dirac_encoder_close( p_sys->p_dirac );
- 
-    free( p_sys->p_buffer_in );
+    if( p_sys->p_dirac )
+        dirac_encoder_close( p_sys->p_dirac );
+
+    if( p_sys->p_buffer_in )
+        free( p_sys->p_buffer_in );
     free( p_sys );
 }
-- 
1.5.6.5




More information about the vlc-devel mailing list