X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Fschroedinger.c;h=977afca0f274650fbd4e85d383de02785debcadd;hb=865d4b77957f3d16f667e2d62de5c423cc68928b;hp=3fc1582b8a901c15a6f76bf71b2fc7012ee9c442;hpb=35824733e305214dfada18dd6362cfa02a86dcd3;p=vlc diff --git a/modules/codec/schroedinger.c b/modules/codec/schroedinger.c index 3fc1582b8a..977afca0f2 100644 --- a/modules/codec/schroedinger.c +++ b/modules/codec/schroedinger.c @@ -3,39 +3,40 @@ * (http://www.bbc.co.uk/rd/projects/dirac/index.shtml) * (http://diracvideo.org) ***************************************************************************** - * Copyright (C) 2008 the VideoLAN team - * $Id$ + * Copyright (C) 2008-2011 VLC authors and VideoLAN * * Authors: Jonathan Rosser * David Flynn + * Anuradha Suraparaju * - * 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 - * the Free Software Foundation; either version 2 of the License, or + * 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 General Public License for more details. + * 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 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. + * 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 + #include #include #include -#include -#include #include @@ -44,32 +45,487 @@ *****************************************************************************/ static int OpenDecoder ( vlc_object_t * ); static void CloseDecoder ( vlc_object_t * ); +static int OpenEncoder ( vlc_object_t * ); +static void CloseEncoder ( vlc_object_t * ); + +#define ENC_CFG_PREFIX "sout-schro-" + +#define ENC_CHROMAFMT "chroma-fmt" +#define ENC_CHROMAFMT_TEXT N_("Chroma format") +#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_("4:2:0"), N_("4:2:2"), N_("4:4:4") }; + +#define ENC_RATE_CONTROL "rate-control" +#define ENC_RATE_CONTROL_TEXT N_("Rate control method") +#define ENC_RATE_CONTROL_LONGTEXT N_("Method used to encode the video sequence") + +static const char *enc_rate_control_list[] = { + "constant_noise_threshold", + "constant_bitrate", + "low_delay", + "lossless", + "constant_lambda", + "constant_error", + "constant_quality" +}; + +static const char *enc_rate_control_list_text[] = { + N_("Constant noise threshold mode"), + N_("Constant bitrate mode (CBR)"), + N_("Low Delay mode"), + N_("Lossless mode"), + N_("Constant lambda mode"), + N_("Constant error mode"), + N_("Constant quality mode") +}; + +#define ENC_GOP_STRUCTURE "gop-structure" +#define ENC_GOP_STRUCTURE_TEXT N_("GOP structure") +#define ENC_GOP_STRUCTURE_LONGTEXT N_("GOP structure used to encode the video sequence") + +static const char *enc_gop_structure_list[] = { + "adaptive", + "intra_only", + "backref", + "chained_backref", + "biref", + "chained_biref" +}; + +static const char *enc_gop_structure_list_text[] = { + N_("No fixed gop structure. A picture can be intra or inter and refer to previous or future pictures."), + N_("I-frame only sequence"), + N_("Inter pictures refere to previous pictures only"), + N_("Inter pictures refere to previous pictures only"), + N_("Inter pictures can refer to previous or future pictures"), + N_("Inter pictures can refer to previous or future pictures") +}; + +#define ENC_QUALITY "quality" +#define ENC_QUALITY_TEXT N_("Constant quality factor") +#define ENC_QUALITY_LONGTEXT N_("Quality factor to use in constant quality mode") + +#define ENC_NOISE_THRESHOLD "noise-threshold" +#define ENC_NOISE_THRESHOLD_TEXT N_("Noise Threshold") +#define ENC_NOISE_THRESHOLD_LONGTEXT N_("Noise threshold to use in constant noise threshold mode") + +#define ENC_BITRATE "bitrate" +#define ENC_BITRATE_TEXT N_("CBR bitrate (kbps)") +#define ENC_BITRATE_LONGTEXT N_("Target bitrate in kbps when encoding in constant bitrate mode") + +#define ENC_MAX_BITRATE "max-bitrate" +#define ENC_MAX_BITRATE_TEXT N_("Maximum bitrate (kbps)") +#define ENC_MAX_BITRATE_LONGTEXT N_("Maximum bitrate in kbps when encoding in constant bitrate mode") + +#define ENC_MIN_BITRATE "min-bitrate" +#define ENC_MIN_BITRATE_TEXT N_("Minimum bitrate (kbps)") +#define ENC_MIN_BITRATE_LONGTEXT N_("Minimum bitrate in kbps when encoding in constant bitrate mode") + +#define ENC_AU_DISTANCE "gop-length" +#define ENC_AU_DISTANCE_TEXT N_("GOP length") +#define ENC_AU_DISTANCE_LONGTEXT N_("Number of pictures between successive sequence headers i.e. length of the group of pictures") + + +#define ENC_PREFILTER "filtering" +#define ENC_PREFILTER_TEXT N_("Prefilter") +#define ENC_PREFILTER_LONGTEXT N_("Enable adaptive prefiltering") + +static const char *enc_filtering_list[] = { + "none", + "center_weighted_median", + "gaussian", + "add_noise", + "adaptive_gaussian", + "lowpass" +}; + +static const char *enc_filtering_list_text[] = { + N_("No pre-filtering"), + N_("Centre Weighted Median"), + N_("Gaussian Low Pass Filter"), + N_("Add Noise"), + N_("Gaussian Adaptive Low Pass Filter"), + N_("Low Pass Filter"), +}; + +#define ENC_PREFILTER_STRENGTH "filter-value" +#define ENC_PREFILTER_STRENGTH_TEXT N_("Amount of prefiltering") +#define ENC_PREFILTER_STRENGTH_LONGTEXT N_("Higher value implies more prefiltering") + +#define ENC_CODINGMODE "coding-mode" +#define ENC_CODINGMODE_TEXT N_("Picture coding mode") +#define ENC_CODINGMODE_LONGTEXT N_("Field coding is where interlaced fields are coded" \ + " separately as opposed to a pseudo-progressive frame") +static const char *const enc_codingmode_list[] = + { "auto", "progressive", "field" }; +static const char *const enc_codingmode_list_text[] = + { N_("auto - let encoder decide based upon input (Best)"), + N_("force coding frame as single picture"), + N_("force coding frame as separate interlaced fields"), + }; + +/* advanced option only */ +#define ENC_MCBLK_SIZE "motion-block-size" +#define ENC_MCBLK_SIZE_TEXT N_("Size of motion compensation blocks") + +static const char *enc_block_size_list[] = { + "automatic", + "small", + "medium", + "large" +}; +static const char *const enc_block_size_list_text[] = + { N_("automatic - let encoder decide based upon input (Best)"), + N_("small - use small motion compensation blocks"), + N_("medium - use medium motion compensation blocks"), + N_("large - use large motion compensation blocks"), + }; + +/* advanced option only */ +#define ENC_MCBLK_OVERLAP "motion-block-overlap" +#define ENC_MCBLK_OVERLAP_TEXT N_("Overlap of motion compensation blocks") + +static const char *enc_block_overlap_list[] = { + "automatic", + "none", + "partial", + "full" +}; +static const char *const enc_block_overlap_list_text[] = + { N_("automatic - let encoder decide based upon input (Best)"), + N_("none - Motion compensation blocks do not overlap"), + N_("partial - Motion compensation blocks only partially overlap"), + N_("full - Motion compensation blocks fully overlap"), + }; + + +#define ENC_MVPREC "mv-precision" +#define ENC_MVPREC_TEXT N_("Motion Vector precision") +#define ENC_MVPREC_LONGTEXT N_("Motion Vector precision in pels") +static const char *const enc_mvprec_list[] = + { "1", "1/2", "1/4", "1/8" }; + +/* advanced option only */ +#define ENC_ME_COMBINED "me-combined" +#define ENC_ME_COMBINED_TEXT N_("Three component motion estimation") +#define ENC_ME_COMBINED_LONGTEXT N_("Use chroma as part of the motion estimation process") + +#define ENC_DWTINTRA "intra-wavelet" +#define ENC_DWTINTRA_TEXT N_("Intra picture DWT filter") + +#define ENC_DWTINTER "inter-wavelet" +#define ENC_DWTINTER_TEXT N_("Inter picture DWT filter") + +static const char *enc_wavelet_list[] = { + "desl_dubuc_9_7", + "le_gall_5_3", + "desl_dubuc_13_7", + "haar_0", + "haar_1", + "fidelity", + "daub_9_7" +}; + +static const char *enc_wavelet_list_text[] = { + "Deslauriers-Dubuc (9,7)", + "LeGall (5,3)", + "Deslauriers-Dubuc (13,7)", + "Haar with no shift", + "Haar with single shift per level", + "Fidelity filter", + "Daubechies (9,7) integer approximation" +}; + +#define ENC_DWTDEPTH "transform-depth" +#define ENC_DWTDEPTH_TEXT N_("Number of DWT iterations") +#define ENC_DWTDEPTH_LONGTEXT N_("Also known as DWT levels") + + +/* advanced option only */ +#define ENC_MULTIQUANT "enable-multiquant" +#define ENC_MULTIQUANT_TEXT N_("Enable multiple quantizers") +#define ENC_MULTIQUANT_LONGTEXT N_("Enable multiple quantizers per subband (one per codeblock)") + +/* advanced option only */ +#define ENC_NOAC "enable-noarith" +#define ENC_NOAC_TEXT N_("Disable arithmetic coding") +#define ENC_NOAC_LONGTEXT N_("Use variable length codes instead, useful for very high bitrates") + +/* visual modelling */ +/* advanced option only */ +#define ENC_PWT "perceptual-weighting" +#define ENC_PWT_TEXT N_("perceptual weighting method") + +static const char *enc_perceptual_weighting_list[] = { + "none", + "ccir959", + "moo", + "manos_sakrison" +}; + +/* advanced option only */ +#define ENC_PDIST "perceptual-distance" +#define ENC_PDIST_TEXT N_("perceptual distance") +#define ENC_PDIST_LONGTEXT N_("perceptual distance to calculate perceptual weight") + +/* advanced option only */ +#define ENC_HSLICES "horiz-slices" +#define ENC_HSLICES_TEXT N_("Horizontal slices per frame") +#define ENC_HSLICES_LONGTEXT N_("Number of horizontal slices per frame in low delay mode") + +/* advanced option only */ +#define ENC_VSLICES "vert-slices" +#define ENC_VSLICES_TEXT N_("Vertical slices per frame") +#define ENC_VSLICES_LONGTEXT N_("Number of vertical slices per frame in low delay mode") + +/* advanced option only */ +#define ENC_SCBLK_SIZE "codeblock-size" +#define ENC_SCBLK_SIZE_TEXT N_("Size of code blocks in each subband") + +static const char *enc_codeblock_size_list[] = { + "automatic", + "small", + "medium", + "large", + "full" +}; +static const char *const enc_codeblock_size_list_text[] = + { N_("automatic - let encoder decide based upon input (Best)"), + N_("small - use small code blocks"), + N_("medium - use medium sized code blocks"), + N_("large - use large code blocks"), + N_("full - One code block per subband"), + }; + +/* advanced option only */ +#define ENC_ME_HIERARCHICAL "enable-hierarchical-me" +#define ENC_ME_HIERARCHICAL_TEXT N_("Enable hierarchical Motion Estimation") + +/* advanced option only */ +#define ENC_ME_DOWNSAMPLE_LEVELS "downsample-levels" +#define ENC_ME_DOWNSAMPLE_LEVELS_TEXT N_("Number of levels of downsampling") +#define ENC_ME_DOWNSAMPLE_LEVELS_LONGTEXT N_("Number of levels of downsampling in hierarchical motion estimation mode") + +/* advanced option only */ +#define ENC_ME_GLOBAL_MOTION "enable-global-me" +#define ENC_ME_GLOBAL_MOTION_TEXT N_("Enable Global Motion Estimation") + +/* advanced option only */ +#define ENC_ME_PHASECORR "enable-phasecorr-me" +#define ENC_ME_PHASECORR_TEXT N_("Enable Phase Correlation Estimation") + +/* advanced option only */ +#define ENC_SCD "enable-scd" +#define ENC_SCD_TEXT N_("Enable Scene Change Detection") + +/* advanced option only */ +#define ENC_FORCE_PROFILE "force-profile" +#define ENC_FORCE_PROFILE_TEXT N_("Force Profile") + +static const char *enc_profile_list[] = { + "auto", + "vc2_low_delay", + "vc2_simple", + "vc2_main", + "main" +}; + +static const char *const enc_profile_list_text[] = + { N_("automatic - let encoder decide based upon input (Best)"), + N_("VC2 Low Delay Profile"), + N_("VC2 Simple Profile"), + N_("VC2 Main Profile"), + N_("Main Profile"), + }; + +static const char *const ppsz_enc_options[] = { + ENC_RATE_CONTROL, ENC_GOP_STRUCTURE, ENC_QUALITY, ENC_NOISE_THRESHOLD, ENC_BITRATE, + ENC_MIN_BITRATE, ENC_MAX_BITRATE, ENC_AU_DISTANCE, ENC_CHROMAFMT, + ENC_PREFILTER, ENC_PREFILTER_STRENGTH, ENC_CODINGMODE, ENC_MCBLK_SIZE, + ENC_MCBLK_OVERLAP, ENC_MVPREC, ENC_ME_COMBINED, ENC_DWTINTRA, ENC_DWTINTER, + ENC_DWTDEPTH, ENC_MULTIQUANT, ENC_NOAC, ENC_PWT, ENC_PDIST, ENC_HSLICES, + ENC_VSLICES, ENC_SCBLK_SIZE, ENC_ME_HIERARCHICAL, ENC_ME_DOWNSAMPLE_LEVELS, + ENC_ME_GLOBAL_MOTION, ENC_ME_PHASECORR, ENC_SCD, ENC_FORCE_PROFILE, + NULL +}; + + +/* Module declaration */ + +vlc_module_begin () + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_VCODEC ) + set_shortname( "Schroedinger" ) + set_description( N_("Dirac video decoder using libschroedinger") ) + set_capability( "decoder", 200 ) + set_callbacks( OpenDecoder, CloseDecoder ) + add_shortcut( "schroedinger" ) + + /* encoder */ + add_submodule() + set_section( N_("Encoding") , NULL ) + set_description( N_("Dirac video encoder using libschroedinger") ) + set_capability( "encoder", 110 ) + set_callbacks( OpenEncoder, CloseEncoder ) + add_shortcut( "schroedinger", "schro" ) + + add_string( ENC_CFG_PREFIX ENC_RATE_CONTROL, NULL, + ENC_RATE_CONTROL_TEXT, ENC_RATE_CONTROL_LONGTEXT, false ) + change_string_list( enc_rate_control_list, enc_rate_control_list_text ) + + add_float( ENC_CFG_PREFIX ENC_QUALITY, -1., + ENC_QUALITY_TEXT, ENC_QUALITY_LONGTEXT, false ) + change_float_range(-1., 10.); + + add_float( ENC_CFG_PREFIX ENC_NOISE_THRESHOLD, -1., + ENC_NOISE_THRESHOLD_TEXT, ENC_NOISE_THRESHOLD_LONGTEXT, false ) + change_float_range(-1., 100.); + + add_integer( ENC_CFG_PREFIX ENC_BITRATE, -1, + ENC_BITRATE_TEXT, ENC_BITRATE_LONGTEXT, false ) + change_integer_range(-1, INT_MAX); + + add_integer( ENC_CFG_PREFIX ENC_MAX_BITRATE, -1, + ENC_MAX_BITRATE_TEXT, ENC_MAX_BITRATE_LONGTEXT, false ) + change_integer_range(-1, INT_MAX); + + add_integer( ENC_CFG_PREFIX ENC_MIN_BITRATE, -1, + ENC_MIN_BITRATE_TEXT, ENC_MIN_BITRATE_LONGTEXT, false ) + change_integer_range(-1, INT_MAX); + + add_string( ENC_CFG_PREFIX ENC_GOP_STRUCTURE, NULL, + ENC_GOP_STRUCTURE_TEXT, ENC_GOP_STRUCTURE_LONGTEXT, false ) + change_string_list( enc_gop_structure_list, enc_gop_structure_list_text ) + + add_integer( ENC_CFG_PREFIX ENC_AU_DISTANCE, -1, + ENC_AU_DISTANCE_TEXT, ENC_AU_DISTANCE_LONGTEXT, false ) + change_integer_range(-1, INT_MAX); + + add_string( ENC_CFG_PREFIX ENC_CHROMAFMT, "420", + ENC_CHROMAFMT_TEXT, ENC_CHROMAFMT_LONGTEXT, false ) + change_string_list( enc_chromafmt_list, enc_chromafmt_list_text ) + + add_string( ENC_CFG_PREFIX ENC_CODINGMODE, "auto", + ENC_CODINGMODE_TEXT, ENC_CODINGMODE_LONGTEXT, false ) + change_string_list( enc_codingmode_list, enc_codingmode_list_text ) + + add_string( ENC_CFG_PREFIX ENC_MVPREC, NULL, + ENC_MVPREC_TEXT, ENC_MVPREC_LONGTEXT, false ) + change_string_list( enc_mvprec_list, enc_mvprec_list ) + + /* advanced option only */ + add_string( ENC_CFG_PREFIX ENC_MCBLK_SIZE, NULL, + ENC_MCBLK_SIZE_TEXT, ENC_MCBLK_SIZE_TEXT, true ) + change_string_list( enc_block_size_list, enc_block_size_list_text ) + + + /* advanced option only */ + add_string( ENC_CFG_PREFIX ENC_MCBLK_OVERLAP, NULL, + ENC_MCBLK_OVERLAP_TEXT, ENC_MCBLK_OVERLAP_TEXT, true ) + change_string_list( enc_block_overlap_list, enc_block_overlap_list_text ) + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_ME_COMBINED, -1, + ENC_ME_COMBINED_TEXT, ENC_ME_COMBINED_LONGTEXT, true ) + change_integer_range(-1, 1 ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_ME_HIERARCHICAL, -1, + ENC_ME_HIERARCHICAL_TEXT, ENC_ME_HIERARCHICAL_TEXT, true ) + change_integer_range(-1, 1 ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_ME_DOWNSAMPLE_LEVELS, -1, + ENC_ME_DOWNSAMPLE_LEVELS_TEXT, ENC_ME_DOWNSAMPLE_LEVELS_LONGTEXT, true ) + change_integer_range(-1, 8 ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_ME_GLOBAL_MOTION, -1, + ENC_ME_GLOBAL_MOTION_TEXT, ENC_ME_GLOBAL_MOTION_TEXT, true ) + change_integer_range(-1, 1 ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_ME_PHASECORR, -1, + ENC_ME_PHASECORR_TEXT, ENC_ME_PHASECORR_TEXT, true ) + change_integer_range(-1, 1 ); + + add_string( ENC_CFG_PREFIX ENC_DWTINTRA, NULL, + ENC_DWTINTRA_TEXT, ENC_DWTINTRA_TEXT, false ) + change_string_list( enc_wavelet_list, enc_wavelet_list_text ) + + add_string( ENC_CFG_PREFIX ENC_DWTINTER, NULL, + ENC_DWTINTER_TEXT, ENC_DWTINTER_TEXT, false ) + change_string_list( enc_wavelet_list, enc_wavelet_list_text ) + + add_integer( ENC_CFG_PREFIX ENC_DWTDEPTH, -1, + ENC_DWTDEPTH_TEXT, ENC_DWTDEPTH_LONGTEXT, false ) + change_integer_range(-1, SCHRO_LIMIT_ENCODER_TRANSFORM_DEPTH ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_MULTIQUANT, -1, + ENC_MULTIQUANT_TEXT, ENC_MULTIQUANT_LONGTEXT, true ) + change_integer_range(-1, 1 ); + + /* advanced option only */ + add_string( ENC_CFG_PREFIX ENC_SCBLK_SIZE, NULL, + ENC_SCBLK_SIZE_TEXT, ENC_SCBLK_SIZE_TEXT, true ) + change_string_list( enc_codeblock_size_list, enc_codeblock_size_list_text ) + + add_string( ENC_CFG_PREFIX ENC_PREFILTER, NULL, + ENC_PREFILTER_TEXT, ENC_PREFILTER_LONGTEXT, false ) + change_string_list( enc_filtering_list, enc_filtering_list_text ) + + add_float( ENC_CFG_PREFIX ENC_PREFILTER_STRENGTH, -1., + ENC_PREFILTER_STRENGTH_TEXT, ENC_PREFILTER_STRENGTH_LONGTEXT, false ) + change_float_range(-1., 100.0); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_SCD, -1, + ENC_SCD_TEXT, ENC_SCD_TEXT, true ) + change_integer_range(-1, 1 ); + + /* advanced option only */ + add_string( ENC_CFG_PREFIX ENC_PWT, NULL, + ENC_PWT_TEXT, ENC_PWT_TEXT, true ) + change_string_list( enc_perceptual_weighting_list, enc_perceptual_weighting_list ) + + /* advanced option only */ + add_float( ENC_CFG_PREFIX ENC_PDIST, -1, + ENC_PDIST_TEXT, ENC_PDIST_LONGTEXT, true ) + change_float_range(-1., 100.); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_NOAC, -1, + ENC_NOAC_TEXT, ENC_NOAC_LONGTEXT, true ) + change_integer_range(-1, 1 ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_HSLICES, -1, + ENC_HSLICES_TEXT, ENC_HSLICES_LONGTEXT, true ) + change_integer_range(-1, INT_MAX ); + + /* advanced option only */ + add_integer( ENC_CFG_PREFIX ENC_VSLICES, -1, + ENC_VSLICES_TEXT, ENC_VSLICES_LONGTEXT, true ) + change_integer_range(-1, INT_MAX ); + + /* advanced option only */ + add_string( ENC_CFG_PREFIX ENC_FORCE_PROFILE, NULL, + ENC_FORCE_PROFILE_TEXT, ENC_FORCE_PROFILE_TEXT, true ) + change_string_list( enc_profile_list, enc_profile_list_text ) + +vlc_module_end () -vlc_module_begin(); - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_VCODEC ); - set_description( N_("Schroedinger video decoder") ); - set_capability( "decoder", 200 ); - set_callbacks( OpenDecoder, CloseDecoder ); - add_shortcut( "schroedinger" ); -vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ static picture_t *DecodeBlock ( decoder_t *p_dec, block_t **pp_block ); -/***************************************************************************** - * picture_pts_t : store pts alongside picture number, not carried through - * decoder - *****************************************************************************/ -struct picture_pts_t -{ - int i_empty; //not in use - uint32_t u_pnum; //picture number from dirac header - mtime_t i_pts; //pts for this picture -}; - struct picture_free_t { picture_t *p_pic; @@ -79,7 +535,6 @@ struct picture_free_t /***************************************************************************** * decoder_sys_t : Schroedinger decoder descriptor *****************************************************************************/ -#define PTS_TLB_SIZE 16 struct decoder_sys_t { /* @@ -89,23 +544,8 @@ struct decoder_sys_t mtime_t i_frame_pts_delta; SchroDecoder *p_schro; SchroVideoFormat *p_format; - struct picture_pts_t pts_tlb[PTS_TLB_SIZE]; - int i_ts_resync_hack; }; -//#define TRACE - -/***************************************************************************** - * ResetPTStlb: Purge all entries in @p_dec@'s PTS-tlb - *****************************************************************************/ -static void ResetPTStlb( decoder_t *p_dec ) -{ - decoder_sys_t *p_sys = p_dec->p_sys; - for( int i=0; ipts_tlb[i].i_empty = 1; - } -} - /***************************************************************************** * OpenDecoder: probe the decoder and return score *****************************************************************************/ @@ -115,7 +555,7 @@ static int OpenDecoder( vlc_object_t *p_this ) decoder_sys_t *p_sys; SchroDecoder *p_schro; - if( p_dec->fmt_in.i_codec != VLC_FOURCC('d','r','a','c') ) + if( p_dec->fmt_in.i_codec != VLC_CODEC_DIRAC ) { return VLC_EGENERIC; } @@ -139,15 +579,12 @@ static int OpenDecoder( vlc_object_t *p_this ) p_dec->p_sys = p_sys; p_sys->p_schro = p_schro; p_sys->p_format = NULL; - p_sys->i_lastpts = -1; + p_sys->i_lastpts = VLC_TS_INVALID; p_sys->i_frame_pts_delta = 0; - p_sys->i_ts_resync_hack = 0; - - ResetPTStlb(p_dec); /* Set output properties */ p_dec->fmt_out.i_cat = VIDEO_ES; - p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0'); + p_dec->fmt_out.i_codec = VLC_CODEC_I420; /* Set callbacks */ p_dec->pf_decode_video = DecodeBlock; @@ -161,37 +598,35 @@ static int OpenDecoder( vlc_object_t *p_this ) static void SetVideoFormat( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; - double f_aspect; p_sys->p_format = schro_decoder_get_video_format(p_sys->p_schro); if( p_sys->p_format == NULL ) return; - p_sys->i_frame_pts_delta = INT64_C(1000000) + p_sys->i_frame_pts_delta = CLOCK_FREQ * p_sys->p_format->frame_rate_denominator / p_sys->p_format->frame_rate_numerator; switch( p_sys->p_format->chroma_format ) { - case SCHRO_CHROMA_420: p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0'); break; - case SCHRO_CHROMA_422: p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','2'); break; - case SCHRO_CHROMA_444: p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','4','4'); break; + case SCHRO_CHROMA_420: p_dec->fmt_out.i_codec = VLC_CODEC_I420; break; + case SCHRO_CHROMA_422: p_dec->fmt_out.i_codec = VLC_CODEC_I422; break; + case SCHRO_CHROMA_444: p_dec->fmt_out.i_codec = VLC_CODEC_I444; break; default: p_dec->fmt_out.i_codec = 0; break; } - p_dec->fmt_out.video.i_visible_width = + p_dec->fmt_out.video.i_visible_width = p_sys->p_format->clean_width; + p_dec->fmt_out.video.i_x_offset = p_sys->p_format->left_offset; p_dec->fmt_out.video.i_width = p_sys->p_format->width; - p_dec->fmt_out.video.i_visible_height = + p_dec->fmt_out.video.i_visible_height = p_sys->p_format->clean_height; + p_dec->fmt_out.video.i_y_offset = p_sys->p_format->top_offset; p_dec->fmt_out.video.i_height = p_sys->p_format->height; /* aspect_ratio_[numerator|denominator] describes the pixel aspect ratio */ - f_aspect = (double) - ( p_sys->p_format->aspect_ratio_numerator * p_sys->p_format->width ) / - ( p_sys->p_format->aspect_ratio_denominator * p_sys->p_format->height); - - p_dec->fmt_out.video.i_aspect = VOUT_ASPECT_FACTOR * f_aspect; + p_dec->fmt_out.video.i_sar_num = p_sys->p_format->aspect_ratio_numerator; + p_dec->fmt_out.video.i_sar_den = p_sys->p_format->aspect_ratio_denominator; p_dec->fmt_out.video.i_frame_rate = p_sys->p_format->frame_rate_numerator; @@ -199,53 +634,6 @@ static void SetVideoFormat( decoder_t *p_dec ) p_sys->p_format->frame_rate_denominator; } -/***************************************************************************** - * StorePicturePTS: Store the PTS value for a particular picture number - *****************************************************************************/ -static void StorePicturePTS( decoder_t *p_dec, block_t *p_block, int i_pupos ) -{ - decoder_sys_t *p_sys = p_dec->p_sys; - uint32_t u_pnum; - mtime_t i_pts; - - u_pnum = GetDWBE( p_block->p_buffer + i_pupos + 13 ); - i_pts = p_block->i_pts > 0 ? p_block->i_pts : p_block->i_dts; - - for( int i=0; ipts_tlb[i].i_empty ) { - - 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; - - return; - } - } - - msg_Err( p_dec, "Could not store PTS %"PRId64" for picture %u", - i_pts, u_pnum ); -} - -/***************************************************************************** - * GetPicturePTS: Retrieve the PTS value for a particular picture number - *****************************************************************************/ -static mtime_t GetPicturePTS( decoder_t *p_dec, uint32_t u_pnum ) -{ - decoder_sys_t *p_sys = p_dec->p_sys; - - for( int i=0; ipts_tlb[i].i_empty) && - (p_sys->pts_tlb[i].u_pnum == u_pnum)) { - - p_sys->pts_tlb[i].i_empty = 1; - return p_sys->pts_tlb[i].i_pts; - } - } - - msg_Err( p_dec, "Could not retrieve PTS for picture %u", u_pnum ); - return 0; -} - /***************************************************************************** * SchroFrameFree: schro_frame callback to release the associated picture_t * When schro_decoder_reset() is called there will be pictures in the @@ -258,7 +646,7 @@ static void SchroFrameFree( SchroFrame *frame, void *priv) if( !p_free ) return; - p_free->p_dec->pf_vout_buffer_del( p_free->p_dec, p_free->p_pic ); + picture_Release( p_free->p_pic ); free(p_free); (void)frame; } @@ -276,7 +664,7 @@ static SchroFrame *CreateSchroFrameFromPic( decoder_t *p_dec ) if( !p_schroframe ) return NULL; - p_pic = p_dec->pf_vout_buffer_new( p_dec ); + p_pic = decoder_NewPicture( p_dec ); if( !p_pic ) return NULL; @@ -353,16 +741,8 @@ static void CloseDecoder( vlc_object_t *p_this ) /**************************************************************************** * DecodeBlock: the whole thing **************************************************************************** - * Blocks must start with a Dirac parse unit. - * Blocks must contain at least one Dirac parse unit. - * Blocks must end with a picture parse unit. - * Blocks must not contain more than one picture parse unit. - * If a block has a PTS signaled, it applies to the first picture in p_block - * - Schroedinger has no internal means to tag pictures with a PTS - * - In this case, the picture number is extracted and stored in a TLB - * When a picture is extracted from schro, it is looked up in the pts_tlb - * - If the picture was never tagged with a PTS, a new one is calculated - * based upon the frame rate and last output PTS. + * Blocks need not be Dirac dataunit aligned. + * If a block has a PTS signaled, it applies to the first picture at or after p_block * * If this function returns a picture (!NULL), it is called again and the * same block is resubmitted. To avoid this, set *pp_block to NULL; @@ -372,150 +752,59 @@ static void CloseDecoder( vlc_object_t *p_this ) static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; - int state; - SchroBuffer *p_schrobuffer; - SchroFrame *p_schroframe; - picture_t *p_pic; - block_t *p_block; - uint32_t u_pnum; if( !pp_block ) return NULL; - p_block = *pp_block; - - if ( p_block ) do { - /* prepare block for submission */ - - if (p_sys->i_ts_resync_hack && p_sys->i_ts_resync_hack--) - return NULL; - - if( !p_block->i_buffer ) { - msg_Err( p_dec, "block is of zero size" ); - break; - } + if ( *pp_block ) { + block_t *p_block = *pp_block; /* reset the decoder when seeking as the decode in progress is invalid */ /* discard the block as it is just a null magic block */ - if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_RESET" ); -#endif + if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) { schro_decoder_reset( p_sys->p_schro ); - ResetPTStlb( p_dec ); - - p_sys->i_lastpts = -1; - - /* The ts layer manages to corrupt the next packet we are to receive - * Since schro has no sync support, we need to drop it */ - p_sys->i_ts_resync_hack = 1; - + p_sys->i_lastpts = VLC_TS_INVALID; block_Release( p_block ); *pp_block = NULL; return NULL; } - /* Unsatisfactory, and will later be fixed in schro: - * - Schro can only handle a single Dirac parse unit at a time - * - Multiple parse units may exist in p_block - * - All mapping specs so far guarantee that p_block would - * not contain anything after a picture - * So, we can not give the whole block to schro, but piecemeal - */ - size_t i_bufused = 0; - while( schro_decoder_push_ready( p_sys->p_schro )) { - if( p_block->i_buffer - i_bufused < 13 ) { - *pp_block = NULL; - block_Release( p_block ); - msg_Err( p_dec, "not enough data left in block" ); - break; - } - - int b_bail = 0; - size_t i_pulen = GetDWBE( p_block->p_buffer + i_bufused + 5 ); - uint8_t *p_pu = p_block->p_buffer + i_bufused; - - if( 0 == i_pulen ) { - i_pulen = 13; - } - - /* blocks that do not start with the parse info prefix are invalid */ - if( p_pu[0] != 'B' || p_pu[1] != 'B' || - p_pu[2] != 'C' || p_pu[3] != 'D') - { - *pp_block = NULL; - block_Release( p_block ); - msg_Err( p_dec, "block does not start with dirac parse code" ); - break; - } - - if( i_bufused + i_pulen > p_block->i_buffer ) { - *pp_block = NULL; - block_Release( p_block ); - break; + SchroBuffer *p_schrobuffer; + p_schrobuffer = schro_buffer_new_with_data( p_block->p_buffer, p_block->i_buffer ); + p_schrobuffer->free = SchroBufferFree; + p_schrobuffer->priv = p_block; + if( p_block->i_pts > VLC_TS_INVALID ) { + mtime_t *p_pts = malloc( sizeof(*p_pts) ); + if( p_pts ) { + *p_pts = p_block->i_pts; + /* if this call fails, p_pts is freed automatically */ + p_schrobuffer->tag = schro_tag_new( p_pts, free ); } - - if( p_pu[4] & 0x08 ) - StorePicturePTS( p_dec, p_block, i_bufused ); - - p_schrobuffer = schro_buffer_new_with_data( p_pu, i_pulen ); - if( i_pulen + i_bufused < p_block->i_buffer ) { - /* don't let schro free this block, more data still in it */ - p_schrobuffer->free = 0; - } - else { - p_schrobuffer->free = SchroBufferFree; - p_schrobuffer->priv = p_block; - b_bail = 1; - } - -#ifdef TRACE - msg_Dbg( p_dec, "Inserting bytes into decoder len=%zu of %zu pts=%"PRId64, - i_pulen, p_block->i_buffer, p_block->i_pts); -#endif - /* this stops the same block being fed back into this function if - * we were on the next iteration of this loop to output a picture */ - *pp_block = NULL; - state = schro_decoder_push( p_sys->p_schro, p_schrobuffer ); - - /* DO NOT refer to p_block after this point, it may have been freed */ - - i_bufused += i_pulen; - - if( state == SCHRO_DECODER_FIRST_ACCESS_UNIT ) { -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_FIRST_ACCESS_UNIT"); -#endif - SetVideoFormat( p_dec ); - ResetPTStlb( p_dec ); - - p_schroframe = CreateSchroFrameFromPic( p_dec ); - if( p_schroframe ) { - schro_decoder_add_output_picture( p_sys->p_schro, p_schroframe); - } - } - - if( b_bail ) - break; } - } while( 0 ); + + /* this stops the same block being fed back into this function if + * we were on the next iteration of this loop to output a picture */ + *pp_block = NULL; + schro_decoder_autoparse_push( p_sys->p_schro, p_schrobuffer ); + /* DO NOT refer to p_block after this point, it may have been freed */ + } while( 1 ) { - state = schro_decoder_wait( p_sys->p_schro ); + SchroFrame *p_schroframe; + picture_t *p_pic; + int state = schro_decoder_autoparse_wait( p_sys->p_schro ); switch( state ) { + case SCHRO_DECODER_FIRST_ACCESS_UNIT: + SetVideoFormat( p_dec ); + break; + case SCHRO_DECODER_NEED_BITS: -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_NEED_BITS" ); -#endif return NULL; case SCHRO_DECODER_NEED_FRAME: -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_NEED_FRAME" ); -#endif p_schroframe = CreateSchroFrameFromPic( p_dec ); if( !p_schroframe ) @@ -527,44 +816,790 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) schro_decoder_add_output_picture( p_sys->p_schro, p_schroframe); break; - case SCHRO_DECODER_OK: - u_pnum = schro_decoder_get_picture_number( p_sys->p_schro ); + case SCHRO_DECODER_OK: { + SchroTag *p_tag = schro_decoder_get_picture_tag( p_sys->p_schro ); p_schroframe = schro_decoder_pull( p_sys->p_schro ); + if( !p_schroframe || !p_schroframe->priv ) + { + /* frame can't be one that was allocated by us + * -- no private data: discard */ + if( p_tag ) schro_tag_free( p_tag ); + if( p_schroframe ) schro_frame_unref( p_schroframe ); + break; + } p_pic = ((struct picture_free_t*) p_schroframe->priv)->p_pic; p_schroframe->priv = NULL; - schro_frame_unref( p_schroframe ); - /* solve presentation time stamp for picture. If this picture - * was not tagged with a pts when presented to decoder, interpolate - * one - * This means no need to set p_pic->b_force, as we have a pts on - * each picture */ - p_pic->date = GetPicturePTS( p_dec, u_pnum ); - if (p_sys->i_lastpts >= 0 && p_pic->date == 0) + if( p_tag ) + { + /* free is handled by schro_frame_unref */ + p_pic->date = *(mtime_t*) p_tag->value; + schro_tag_free( p_tag ); + } + else if( p_sys->i_lastpts > VLC_TS_INVALID ) + { + /* NB, this shouldn't happen since the packetizer does a + * very thorough job of inventing timestamps. The + * following is just a very rough fall back incase packetizer + * is missing. */ + /* maybe it would be better to set p_pic->b_force ? */ p_pic->date = p_sys->i_lastpts + p_sys->i_frame_pts_delta; + } p_sys->i_lastpts = p_pic->date; -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_OK num=%u date=%"PRId64, - u_pnum, p_pic->date); -#endif + schro_frame_unref( p_schroframe ); return p_pic; - + } case SCHRO_DECODER_EOS: -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_EOS"); -#endif - /* reset the decoder -- schro doesn't do this itself automatically */ - /* there are no more pictures in the output buffer at this point */ - schro_decoder_reset( p_sys->p_schro ); + /* NB, the new api will not emit _EOS, it handles the reset internally */ break; case SCHRO_DECODER_ERROR: -#ifdef TRACE - msg_Dbg( p_dec, "SCHRO_DECODER_ERROR"); -#endif + msg_Err( p_dec, "SCHRO_DECODER_ERROR"); + return NULL; + } + } +} + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static block_t *Encode( encoder_t *p_enc, picture_t *p_pict ); + + +/***************************************************************************** + * picture_pts_t : store pts alongside picture number, not carried through + * encoder + *****************************************************************************/ +struct picture_pts_t +{ + mtime_t i_pts; /* associated pts */ + uint32_t u_pnum; /* dirac picture number */ + bool b_empty; /* entry is invalid */ +}; + +/***************************************************************************** + * encoder_sys_t : Schroedinger encoder descriptor + *****************************************************************************/ +#define SCHRO_PTS_TLB_SIZE 256 +struct encoder_sys_t +{ + /* + * Schro properties + */ + SchroEncoder *p_schro; + SchroVideoFormat *p_format; + int started; + bool b_auto_field_coding; + + uint32_t i_input_picnum; + block_fifo_t *p_dts_fifo; + + block_t *p_chain; + + struct picture_pts_t pts_tlb[SCHRO_PTS_TLB_SIZE]; + mtime_t i_pts_offset; + mtime_t i_field_time; + + bool b_eos_signalled; + bool b_eos_pulled; +}; + +static struct +{ + unsigned int i_height; + int i_approx_fps; + SchroVideoFormatEnum i_vf; +} schro_format_guess[] = { + /* Important: Keep this list ordered in ascending picture height */ + {1, 0, SCHRO_VIDEO_FORMAT_CUSTOM}, + {120, 15, SCHRO_VIDEO_FORMAT_QSIF}, + {144, 12, SCHRO_VIDEO_FORMAT_QCIF}, + {240, 15, SCHRO_VIDEO_FORMAT_SIF}, + {288, 12, SCHRO_VIDEO_FORMAT_CIF}, + {480, 30, SCHRO_VIDEO_FORMAT_SD480I_60}, + {480, 15, SCHRO_VIDEO_FORMAT_4SIF}, + {576, 12, SCHRO_VIDEO_FORMAT_4CIF}, + {576, 25, SCHRO_VIDEO_FORMAT_SD576I_50}, + {720, 50, SCHRO_VIDEO_FORMAT_HD720P_50}, + {720, 60, SCHRO_VIDEO_FORMAT_HD720P_60}, + {1080, 24, SCHRO_VIDEO_FORMAT_DC2K_24}, + {1080, 25, SCHRO_VIDEO_FORMAT_HD1080I_50}, + {1080, 30, SCHRO_VIDEO_FORMAT_HD1080I_60}, + {1080, 50, SCHRO_VIDEO_FORMAT_HD1080P_50}, + {1080, 60, SCHRO_VIDEO_FORMAT_HD1080P_60}, + {2160, 24, SCHRO_VIDEO_FORMAT_DC4K_24}, + {0, 0, 0}, +}; + +/***************************************************************************** + * ResetPTStlb: Purge all entries in @p_enc@'s PTS-tlb + *****************************************************************************/ +static void ResetPTStlb( encoder_t *p_enc ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + for( int i = 0; i < SCHRO_PTS_TLB_SIZE; i++ ) + { + p_sys->pts_tlb[i].b_empty = true; + } +} + + +/***************************************************************************** + * 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 ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + + for( int i = 0; ipts_tlb[i].b_empty ) + { + p_sys->pts_tlb[i].u_pnum = u_pnum; + p_sys->pts_tlb[i].i_pts = i_pts; + p_sys->pts_tlb[i].b_empty = false; + + return; + } + } + + msg_Err( p_enc, "Could not store PTS %"PRId64" for frame %u", i_pts, u_pnum ); +} + +/***************************************************************************** + * GetPicturePTS: Retrieve the PTS value for a particular picture number + *****************************************************************************/ +static mtime_t GetPicturePTS( encoder_t *p_enc, uint32_t u_pnum ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + + for( int i = 0; i < SCHRO_PTS_TLB_SIZE; i++ ) + { + if( !p_sys->pts_tlb[i].b_empty && + p_sys->pts_tlb[i].u_pnum == u_pnum ) + { + p_sys->pts_tlb[i].b_empty = true; + return p_sys->pts_tlb[i].i_pts; + } + } + + msg_Err( p_enc, "Could not retrieve PTS for picture %u", u_pnum ); + return 0; +} + +static inline bool SchroSetEnum( const encoder_t *p_enc, int i_list_size, const char *list[], + const char *psz_name, const char *psz_name_text, const char *psz_value) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + if( list && psz_name_text && psz_name && psz_value ) { + for( int i = 0; i < i_list_size; ++i ) { + if( strcmp( list[i], psz_value ) ) + continue; + schro_encoder_setting_set_double( p_sys->p_schro, psz_name, i ); + return true; + } + msg_Err( p_enc, "Invalid %s: %s", psz_name_text, psz_value ); + } + return false; +} + +static bool SetEncChromaFormat( encoder_t *p_enc, uint32_t i_codec ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + + switch( i_codec ) { + case VLC_CODEC_I420: + p_enc->fmt_in.i_codec = i_codec; + p_enc->fmt_in.video.i_bits_per_pixel = 12; + p_sys->p_format->chroma_format = SCHRO_CHROMA_420; + break; + case VLC_CODEC_I422: + p_enc->fmt_in.i_codec = i_codec; + p_enc->fmt_in.video.i_bits_per_pixel = 16; + p_sys->p_format->chroma_format = SCHRO_CHROMA_422; + break; + case VLC_CODEC_I444: + p_enc->fmt_in.i_codec = i_codec; + p_enc->fmt_in.video.i_bits_per_pixel = 24; + p_sys->p_format->chroma_format = SCHRO_CHROMA_444; + break; + default: + return false; + } + + return true; +} + +#define SCHRO_SET_FLOAT(psz_name, pschro_name) \ + f_tmp = var_GetFloat( p_enc, ENC_CFG_PREFIX psz_name ); \ + if( f_tmp >= 0.0 ) \ + schro_encoder_setting_set_double( p_sys->p_schro, pschro_name, f_tmp ); + +#define SCHRO_SET_INTEGER(psz_name, pschro_name, ignore_val) \ + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX psz_name ); \ + if( i_tmp > ignore_val ) \ + schro_encoder_setting_set_double( p_sys->p_schro, pschro_name, i_tmp ); + +#define SCHRO_SET_ENUM(list, psz_name, psz_name_text, pschro_name) \ + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX psz_name ); \ + if( !psz_tmp ) \ + goto error; \ + else if ( *psz_tmp != '\0' ) { \ + int i_list_size = ARRAY_SIZE(list); \ + if( !SchroSetEnum( p_enc, i_list_size, list, pschro_name, psz_name_text, psz_tmp ) ) { \ + free( psz_tmp ); \ + goto error; \ + } \ + } \ + free( psz_tmp ); + +/***************************************************************************** + * OpenEncoder: probe the encoder and return score + *****************************************************************************/ +static int OpenEncoder( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys; + int i_tmp; + float f_tmp; + char *psz_tmp; + + if( p_enc->fmt_out.i_codec != VLC_CODEC_DIRAC && + !p_enc->b_force ) + { + return VLC_EGENERIC; + } + + if( !p_enc->fmt_in.video.i_frame_rate || !p_enc->fmt_in.video.i_frame_rate_base || + !p_enc->fmt_in.video.i_visible_height || !p_enc->fmt_in.video.i_visible_width ) + { + msg_Err( p_enc, "Framerate and picture dimensions must be non-zero" ); + return VLC_EGENERIC; + } + + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_sys = calloc( 1, sizeof( *p_sys ) ) ) == NULL ) + return VLC_ENOMEM; + + p_enc->p_sys = p_sys; + p_enc->pf_encode_video = Encode; + p_enc->fmt_out.i_codec = VLC_CODEC_DIRAC; + p_enc->fmt_out.i_cat = VIDEO_ES; + + if( ( p_sys->p_dts_fifo = block_FifoNew() ) == NULL ) + { + CloseEncoder( p_this ); + return VLC_ENOMEM; + } + + ResetPTStlb( p_enc ); + + /* guess the video format based upon number of lines and picture height */ + int i = 0; + SchroVideoFormatEnum guessed_video_fmt = SCHRO_VIDEO_FORMAT_CUSTOM; + /* Pick the dirac_video_format in this order of preference: + * 1. an exact match in frame height and an approximate fps match + * 2. the previous preset with a smaller number of lines. + */ + do + { + if( schro_format_guess[i].i_height > p_enc->fmt_in.video.i_height ) + { + guessed_video_fmt = schro_format_guess[i-1].i_vf; + break; + } + if( schro_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( schro_format_guess[i].i_approx_fps - src_fps ); + if( delta_fps > 2 ) + continue; + + guessed_video_fmt = schro_format_guess[i].i_vf; + break; + } while( schro_format_guess[++i].i_height ); + + schro_init(); + p_sys->p_schro = schro_encoder_new(); + if( !p_sys->p_schro ) { + msg_Err( p_enc, "Failed to initialize libschroedinger encoder" ); + return VLC_EGENERIC; + } + schro_encoder_set_packet_assembly( p_sys->p_schro, true ); + + if( !( p_sys->p_format = schro_encoder_get_video_format( p_sys->p_schro ) ) ) { + msg_Err( p_enc, "Failed to get Schroedigner video format" ); + schro_encoder_free( p_sys->p_schro ); + return VLC_EGENERIC; + } + + /* initialise the video format parameters to the guessed format */ + schro_video_format_set_std_video_format( p_sys->p_format, guessed_video_fmt ); + + /* constants set from the input video format */ + p_sys->p_format->width = p_enc->fmt_in.video.i_visible_width; + p_sys->p_format->height = p_enc->fmt_in.video.i_visible_height; + p_sys->p_format->frame_rate_numerator = p_enc->fmt_in.video.i_frame_rate; + p_sys->p_format->frame_rate_denominator = p_enc->fmt_in.video.i_frame_rate_base; + unsigned u_asr_num, u_asr_den; + vlc_ureduce( &u_asr_num, &u_asr_den, + p_enc->fmt_in.video.i_sar_num, + p_enc->fmt_in.video.i_sar_den, 0 ); + p_sys->p_format->aspect_ratio_numerator = u_asr_num; + p_sys->p_format->aspect_ratio_denominator = u_asr_den; + + config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg ); + + SCHRO_SET_ENUM(enc_rate_control_list, ENC_RATE_CONTROL, ENC_RATE_CONTROL_TEXT, "rate_control") + + SCHRO_SET_ENUM(enc_gop_structure_list, ENC_GOP_STRUCTURE, ENC_GOP_STRUCTURE_TEXT, "gop_structure") + + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_CHROMAFMT ); + if( !psz_tmp ) + goto error; + else { + uint32_t i_codec; + if( !strcmp( psz_tmp, "420" ) ) { + i_codec = VLC_CODEC_I420; + } + else if( !strcmp( psz_tmp, "422" ) ) { + i_codec = VLC_CODEC_I422; + } + else if( !strcmp( psz_tmp, "444" ) ) { + i_codec = VLC_CODEC_I444; + } + else { + msg_Err( p_enc, "Invalid chroma format: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + SetEncChromaFormat( p_enc, i_codec ); + } + free( psz_tmp ); + + SCHRO_SET_FLOAT(ENC_QUALITY, "quality") + + SCHRO_SET_FLOAT(ENC_NOISE_THRESHOLD, "noise_threshold") + + /* use bitrate from sout-transcode-vb in kbps */ + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_BITRATE ); + if( i_tmp > -1 ) + schro_encoder_setting_set_double( p_sys->p_schro, "bitrate", i_tmp * 1000 ); + else + schro_encoder_setting_set_double( p_sys->p_schro, "bitrate", p_enc->fmt_out.i_bitrate ); + + p_enc->fmt_out.i_bitrate = schro_encoder_setting_get_double( p_sys->p_schro, "bitrate" ); + + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MIN_BITRATE ); + if( i_tmp > -1 ) + schro_encoder_setting_set_double( p_sys->p_schro, "min_bitrate", i_tmp * 1000 ); + + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MAX_BITRATE ); + if( i_tmp > -1 ) + schro_encoder_setting_set_double( p_sys->p_schro, "max_bitrate", i_tmp * 1000 ); + + SCHRO_SET_INTEGER(ENC_AU_DISTANCE, "au_distance", -1) + + SCHRO_SET_ENUM(enc_filtering_list, ENC_PREFILTER, ENC_PREFILTER_TEXT, "filtering") + + SCHRO_SET_FLOAT(ENC_PREFILTER_STRENGTH, "filter_value") + + + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_CODINGMODE ); + if( !psz_tmp ) + goto error; + else if( !strcmp( psz_tmp, "auto" ) ) { + p_sys->b_auto_field_coding = true; + } + else if( !strcmp( psz_tmp, "progressive" ) ) { + p_sys->b_auto_field_coding = false; + schro_encoder_setting_set_double( p_sys->p_schro, "interlaced_coding", false); + } + else if( !strcmp( psz_tmp, "field" ) ) { + p_sys->b_auto_field_coding = false; + schro_encoder_setting_set_double( p_sys->p_schro, "interlaced_coding", true); + } + else { + msg_Err( p_enc, "Invalid codingmode: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + free( psz_tmp ); + + SCHRO_SET_ENUM(enc_block_size_list, ENC_MCBLK_SIZE, ENC_MCBLK_SIZE_TEXT, "motion_block_size") + + SCHRO_SET_ENUM(enc_block_overlap_list, ENC_MCBLK_OVERLAP, ENC_MCBLK_OVERLAP_TEXT, "motion_block_overlap") + + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_MVPREC ); + if( !psz_tmp ) + goto error; + else if( *psz_tmp != '\0') { + if( !strcmp( psz_tmp, "1" ) ) { + schro_encoder_setting_set_double( p_sys->p_schro, "mv_precision", 0 ); + } + else if( !strcmp( psz_tmp, "1/2" ) ) { + schro_encoder_setting_set_double( p_sys->p_schro, "mv_precision", 1 ); + } + else if( !strcmp( psz_tmp, "1/4" ) ) { + schro_encoder_setting_set_double( p_sys->p_schro, "mv_precision", 2 ); + } + else if( !strcmp( psz_tmp, "1/8" ) ) { + schro_encoder_setting_set_double( p_sys->p_schro, "mv_precision", 3 ); + } + else { + msg_Err( p_enc, "Invalid mv_precision: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + } + free( psz_tmp ); + + SCHRO_SET_INTEGER(ENC_ME_COMBINED, "enable_chroma_me", -1) + + SCHRO_SET_ENUM(enc_wavelet_list, ENC_DWTINTRA, ENC_DWTINTRA_TEXT, "intra_wavelet") + + SCHRO_SET_ENUM(enc_wavelet_list, ENC_DWTINTER, ENC_DWTINTER_TEXT, "inter_wavelet") + + SCHRO_SET_INTEGER(ENC_DWTDEPTH, "transform_depth", -1) + + SCHRO_SET_INTEGER(ENC_MULTIQUANT, "enable_multiquant", -1) + + SCHRO_SET_INTEGER(ENC_NOAC, "enable_noarith", -1) + + SCHRO_SET_ENUM(enc_perceptual_weighting_list, ENC_PWT, ENC_PWT_TEXT, "perceptual_weighting") + + SCHRO_SET_FLOAT(ENC_PDIST, "perceptual_distance") + + SCHRO_SET_INTEGER(ENC_HSLICES, "horiz_slices", -1) + + SCHRO_SET_INTEGER(ENC_VSLICES, "vert_slices", -1) + + SCHRO_SET_ENUM(enc_codeblock_size_list, ENC_SCBLK_SIZE, ENC_SCBLK_SIZE_TEXT, "codeblock_size") + + SCHRO_SET_INTEGER(ENC_ME_HIERARCHICAL, "enable_hierarchical_estimation", -1) + + SCHRO_SET_INTEGER(ENC_ME_DOWNSAMPLE_LEVELS, "downsample_levels", 1) + + SCHRO_SET_INTEGER(ENC_ME_GLOBAL_MOTION, "enable_global_motion", -1) + + SCHRO_SET_INTEGER(ENC_ME_PHASECORR, "enable_phasecorr_estimation", -1) + + SCHRO_SET_INTEGER(ENC_SCD, "enable_scene_change_detection", -1) + + SCHRO_SET_ENUM(enc_profile_list, ENC_FORCE_PROFILE, ENC_FORCE_PROFILE_TEXT, "force_profile") + + p_sys->started = 0; + + return VLC_SUCCESS; +error: + CloseEncoder( p_this ); + return VLC_EGENERIC; +} + + +struct enc_picture_free_t +{ + picture_t *p_pic; + encoder_t *p_enc; +}; + +/***************************************************************************** + * EncSchroFrameFree: schro_frame callback to release the associated picture_t + * When schro_encoder_reset() is called there will be pictures in the + * encoding pipeline that need to be released rather than displayed. + *****************************************************************************/ +static void EncSchroFrameFree( SchroFrame *frame, void *priv ) +{ + struct enc_picture_free_t *p_free = priv; + + if( !p_free ) + return; + + picture_Release( p_free->p_pic ); + free( p_free ); + (void)frame; +} + +/***************************************************************************** + * CreateSchroFrameFromPic: wrap a picture_t in a SchroFrame + *****************************************************************************/ +static SchroFrame *CreateSchroFrameFromInputPic( encoder_t *p_enc, picture_t *p_pic ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + SchroFrame *p_schroframe = schro_frame_new(); + struct enc_picture_free_t *p_free; + + if( !p_schroframe ) + return NULL; + + if( !p_pic ) + return NULL; + + p_schroframe->format = SCHRO_FRAME_FORMAT_U8_420; + if( p_sys->p_format->chroma_format == SCHRO_CHROMA_422 ) + { + p_schroframe->format = SCHRO_FRAME_FORMAT_U8_422; + } + else if( p_sys->p_format->chroma_format == SCHRO_CHROMA_444 ) + { + p_schroframe->format = SCHRO_FRAME_FORMAT_U8_444; + } + + p_schroframe->width = p_sys->p_format->width; + p_schroframe->height = p_sys->p_format->height; + + p_free = malloc( sizeof( *p_free ) ); + if( unlikely( p_free == NULL ) ) { + schro_frame_unref( p_schroframe ); + return NULL; + } + + p_free->p_pic = p_pic; + p_free->p_enc = p_enc; + schro_frame_set_free_callback( p_schroframe, EncSchroFrameFree, p_free ); + + for( int i=0; i<3; i++ ) + { + p_schroframe->components[i].width = p_pic->p[i].i_visible_pitch; + p_schroframe->components[i].stride = p_pic->p[i].i_pitch; + p_schroframe->components[i].height = p_pic->p[i].i_visible_lines; + p_schroframe->components[i].length = + p_pic->p[i].i_pitch * p_pic->p[i].i_lines; + p_schroframe->components[i].data = p_pic->p[i].p_pixels; + + if( i!=0 ) + { + p_schroframe->components[i].v_shift = + SCHRO_FRAME_FORMAT_V_SHIFT( p_schroframe->format ); + p_schroframe->components[i].h_shift = + SCHRO_FRAME_FORMAT_H_SHIFT( p_schroframe->format ); + } + } + + return p_schroframe; +} + +/* Attempt to find dirac picture number in an encapsulation unit */ +static int ReadDiracPictureNumber( uint32_t *p_picnum, 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 ) + { + *p_picnum = GetDWBE( p_block->p_buffer + u_pos + 9 ); + return 1; + } + /* skip to the next dirac data unit */ + uint32_t u_npo = GetDWBE( p_block->p_buffer + u_pos + 1 ); + assert( u_npo <= UINT32_MAX - u_pos ); + if( u_npo == 0 ) + u_npo = 13; + u_pos += u_npo; + } + return 0; +} + + +static block_t *Encode( encoder_t *p_enc, picture_t *p_pic ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + block_t *p_block, *p_output_chain = NULL; + SchroFrame *p_frame; + bool b_go = true; + + if( !p_pic ) { + if( !p_sys->started || p_sys->b_eos_pulled ) + return NULL; + + if( !p_sys->b_eos_signalled ) { + p_sys->b_eos_signalled = 1; + schro_encoder_end_of_stream( p_sys->p_schro ); + } + } else { + /* 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->p_format->interlaced = !p_pic->b_progressive; + p_sys->p_format->top_field_first = p_pic->b_top_field_first; + + if( p_sys->b_auto_field_coding ) + schro_encoder_setting_set_double( p_sys->p_schro, "interlaced_coding", !p_pic->b_progressive ); + } + + if( !p_sys->started ) { + date_t date; + + if( p_pic->format.i_chroma != p_enc->fmt_in.i_codec ) { + char chroma_in[5], chroma_out[5]; + vlc_fourcc_to_char( p_pic->format.i_chroma, chroma_in ); + chroma_in[4] = '\0'; + chroma_out[4] = '\0'; + vlc_fourcc_to_char( p_enc->fmt_in.i_codec, chroma_out ); + msg_Warn( p_enc, "Resetting chroma from %s to %s", chroma_out, chroma_in ); + if( !SetEncChromaFormat( p_enc, p_pic->format.i_chroma ) ) { + msg_Err( p_enc, "Could not reset chroma format to %s", chroma_in ); + return NULL; + } + } + + date_Init( &date, p_enc->fmt_in.video.i_frame_rate, p_enc->fmt_in.video.i_frame_rate_base ); + /* FIXME - Unlike dirac-research codec Schro doesn't have a function that returns the delay in pics yet. + * Use a default of 1 + */ + date_Increment( &date, 1 ); + p_sys->i_pts_offset = date_Get( &date ); + if( schro_encoder_setting_get_double( p_sys->p_schro, "interlaced_coding" ) > 0.0 ) { + date_Set( &date, 0 ); + date_Increment( &date, 1); + p_sys->i_field_time = date_Get( &date ) / 2; + } + + schro_video_format_set_std_signal_range( p_sys->p_format, SCHRO_SIGNAL_RANGE_8BIT_VIDEO ); + schro_encoder_set_video_format( p_sys->p_schro, p_sys->p_format ); + schro_encoder_start( p_sys->p_schro ); + p_sys->started = 1; + } + + if( !p_sys->b_eos_signalled ) { + /* create a schro frame from the input pic and load */ + /* Increase ref count by 1 so that the picture is not freed until + Schro finishes with it */ + picture_Hold( p_pic ); + + p_frame = CreateSchroFrameFromInputPic( p_enc, p_pic ); + if( !p_frame ) + return NULL; + schro_encoder_push_frame( p_sys->p_schro, p_frame ); + + + /* store pts in a lookaside buffer, so that the same pts may + * be used for the picture in coded order */ + StorePicturePTS( p_enc, p_sys->i_input_picnum, p_pic->date ); + p_sys->i_input_picnum++; + + /* store dts in a queue, so that they appear in order in + * coded order */ + p_block = block_Alloc( 1 ); + if( !p_block ) return NULL; + p_block->i_dts = p_pic->date - p_sys->i_pts_offset; + block_FifoPut( p_sys->p_dts_fifo, p_block ); + p_block = NULL; + + /* for field coding mode, insert an extra value into both the + * pts lookaside buffer and dts queue, offset to correspond + * to a one field delay. */ + if( schro_encoder_setting_get_double( p_sys->p_schro, "interlaced_coding" ) > 0.0 ) { + StorePicturePTS( p_enc, p_sys->i_input_picnum, p_pic->date + p_sys->i_field_time ); + p_sys->i_input_picnum++; + + p_block = block_Alloc( 1 ); + if( !p_block ) + return NULL; + p_block->i_dts = p_pic->date - p_sys->i_pts_offset + p_sys->i_field_time; + block_FifoPut( p_sys->p_dts_fifo, p_block ); + p_block = NULL; } } + + do + { + SchroStateEnum state; + state = schro_encoder_wait( p_sys->p_schro ); + switch( state ) + { + case SCHRO_STATE_NEED_FRAME: + b_go = false; + break; + case SCHRO_STATE_AGAIN: + break; + case SCHRO_STATE_END_OF_STREAM: + p_sys->b_eos_pulled = 1; + b_go = false; + break; + case SCHRO_STATE_HAVE_BUFFER: + { + SchroBuffer *p_enc_buf; + uint32_t u_pic_num; + int i_presentation_frame; + p_enc_buf = schro_encoder_pull( p_sys->p_schro, &i_presentation_frame ); + p_block = block_Alloc( p_enc_buf->length ); + if( !p_block ) + return NULL; + + memcpy( p_block->p_buffer, p_enc_buf->data, p_enc_buf->length ); + schro_buffer_unref( p_enc_buf ); + + /* Presence of a Sequence header indicates a seek point */ + if( 0 == p_block->p_buffer[4] ) + { + p_block->i_flags |= BLOCK_FLAG_TYPE_I; + + if( !p_enc->fmt_out.p_extra ) { + const uint8_t eos[] = { 'B','B','C','D',0x10,0,0,0,13,0,0,0,0 }; + uint32_t len = GetDWBE( p_block->p_buffer + 5 ); + /* if it hasn't been done so far, stash a copy of the + * sequence header for muxers such as ogg */ + /* The OggDirac spec advises that a Dirac EOS DataUnit + * is appended to the sequence header to allow guard + * against poor streaming servers */ + /* XXX, should this be done using the packetizer ? */ + + if( len > UINT32_MAX - sizeof( eos ) ) + return NULL; + + p_enc->fmt_out.p_extra = malloc( len + sizeof( eos ) ); + if( !p_enc->fmt_out.p_extra ) + return NULL; + memcpy( p_enc->fmt_out.p_extra, p_block->p_buffer, len ); + memcpy( (uint8_t*)p_enc->fmt_out.p_extra + len, eos, sizeof( eos ) ); + SetDWBE( (uint8_t*)p_enc->fmt_out.p_extra + len + sizeof(eos) - 4, len ); + p_enc->fmt_out.i_extra = len + sizeof( eos ); + } + } + + if( ReadDiracPictureNumber( &u_pic_num, p_block ) ) { + block_t *p_dts_block = block_FifoGet( p_sys->p_dts_fifo ); + p_block->i_dts = p_dts_block->i_dts; + p_block->i_pts = GetPicturePTS( p_enc, u_pic_num ); + block_Release( p_dts_block ); + block_ChainAppend( &p_output_chain, p_block ); + } else { + /* End of sequence */ + block_ChainAppend( &p_output_chain, p_block ); + } + break; + } + default: + break; + } + } while( b_go ); + + return p_output_chain; } +/***************************************************************************** + * CloseEncoder: Schro encoder destruction + *****************************************************************************/ +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; + + /* Free the encoder resources */ + if( p_sys->p_schro ) + schro_encoder_free( p_sys->p_schro ); + + free( p_sys->p_format ); + + if( p_sys->p_dts_fifo ) + block_FifoRelease( p_sys->p_dts_fifo ); + + block_ChainRelease( p_sys->p_chain ); + + free( p_sys ); +}