X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Fdirac.c;h=8505bd2822206cb30b1de8f36490b2f69d00eb42;hb=72fa5a9d8e8f5a1bbe0d83ffd1f30b3265a531c7;hp=42d5d71d540259fad2da3b6b190926c186d689c4;hpb=94bf62da4aa90b593f908983774ac3901e8e5de4;p=vlc diff --git a/modules/codec/dirac.c b/modules/codec/dirac.c index 42d5d71d54..8505bd2822 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 + * Rewritten: David Flynn * * 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 @@ -25,378 +30,804 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include #include #include #include -#include #include -/***************************************************************************** - * 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-" -static const char *ppsz_enc_options[] = { - "quality", NULL +#define ENC_QUALITY_FACTOR "quality" +#define ENC_QUALITY_FACTOR_TEXT N_("Constant quality factor") +#define ENC_QUALITY_FACTOR_LONGTEXT N_("If bitrate=0, use this value for constant quality") + +#define ENC_TARGETRATE "bitrate" +#define ENC_TARGETRATE_TEXT N_("CBR bitrate (kbps)") +#define ENC_TARGETRATE_LONGTEXT N_("A value > 0 enables constant bitrate mode") + +#define ENC_LOSSLESS "lossless" +#define ENC_LOSSLESS_TEXT N_("Enable lossless coding") +#define ENC_LOSSLESS_LONGTEXT N_("Lossless coding ignores bitrate and quality settings, " \ + "allowing for perfect reproduction of the original") + +#define ENC_PREFILTER "prefilter" +#define ENC_PREFILTER_TEXT N_("Prefilter") +#define ENC_PREFILTER_LONGTEXT N_("Enable adaptive prefiltering") +static const char *const enc_prefilter_list[] = + { "none", "cwm", "rectlp", "diaglp" }; +static const char *const enc_prefilter_list_text[] = + { N_("none"), N_("Centre Weighted Median"), + N_("Rectangular Linear Phase"), N_("Diagonal Linear Phase") }; + +#define ENC_PREFILTER_STRENGTH "prefilter-strength" +#define ENC_PREFILTER_STRENGTH_TEXT N_("Amount of prefiltering") +#define ENC_PREFILTER_STRENGTH_LONGTEXT N_("Higher value implies more prefiltering") + +#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_L1SEP "l1-sep" +#define ENC_L1SEP_TEXT N_("Distance between 'P' frames") +#define ENC_L1SEP_LONGTEXT ENC_L1SEP_TEXT + +#define ENC_L1NUM "num-l1" +#define ENC_L1NUM_TEXT N_("Number of 'P' frames per GOP") +#define ENC_L1NUM_LONGTEXT ENC_L1NUM_TEXT + +#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" \ + " seperately 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 seperate interlaced fields"), + }; + +#define ENC_MCBLK_WIDTH "mc-blk-width" +#define ENC_MCBLK_WIDTH_TEXT N_("Width of motion compensation blocks") +#define ENC_MCBLK_WIDTH_LONGTEXT "" + +#define ENC_MCBLK_HEIGHT "mc-blk-height" +#define ENC_MCBLK_HEIGHT_TEXT N_("Height of motion compensation blocks") +#define ENC_MCBLK_HEIGHT_LONGTEXT "" + +/* also known as XBSEP and YBSEP */ +#define ENC_MCBLK_OVERLAP "mc-blk-overlap" +#define ENC_MCBLK_OVERLAP_TEXT N_("Block overlap (%)") +#define ENC_MCBLK_OVERLAP_LONGTEXT N_("Amount that each motion block should " \ + "be overlapped by its neighbours") + +/* advanced option only */ +#define ENC_MCBLK_XBLEN "mc-blk-xblen" +#define ENC_MCBLK_XBLEN_TEXT N_("xblen") +#define ENC_MCBLK_XBLEN_LONGTEXT N_("Total horizontal block length including overlaps") + +/* advanded option only */ +#define ENC_MCBLK_YBLEN "mc-blk-yblen" +#define ENC_MCBLK_YBLEN_TEXT N_("yblen") +#define ENC_MCBLK_YBLEN_LONGTEXT N_("Total vertical block length including overlaps") + +#define ENC_MVPREC "mv-prec" +#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" }; + +#define ENC_ME_SIMPLESEARCH "me-simple-search" +#define ENC_ME_SIMPLESEARCH_TEXT N_("Simple ME search area x:y") +#define ENC_ME_SIMPLESEARCH_LONGTEXT N_("(Not recommended) Perform a simple (non hierarchical " \ + "block matching motion vector search with search range " \ + "of +/-x, +/-y") + +#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 "dwt-intra" +#define ENC_DWTINTRA_TEXT N_("Intra picture DWT filter") +#define ENC_DWTINTRA_LONGTEXT ENC_DWTINTRA_TEXT + +#define ENC_DWTINTER "dwt-inter" +#define ENC_DWTINTER_TEXT N_("Inter picture DWT filter") +#define ENC_DWTINTER_LONGTEXT ENC_DWTINTER_TEXT + +#define ENC_DWTDEPTH "dwt-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 "multi-quant" +#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_SPARTITION "spartition" +#define ENC_SPARTITION_TEXT N_("Enable spatial partitioning") +#define ENC_SPARTITION_LONGTEXT ENC_SPARTITION_TEXT + +#define ENC_NOAC "noac" +#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_CPD "cpd" +#define ENC_CPD_TEXT N_("cycles per degree") +#define ENC_CPD_LONGTEXT ENC_CPD_TEXT + +static const char *const ppsz_enc_options[] = { + ENC_QUALITY_FACTOR, ENC_TARGETRATE, ENC_LOSSLESS, ENC_PREFILTER, ENC_PREFILTER_STRENGTH, + ENC_CHROMAFMT, ENC_L1SEP, ENC_L1NUM, ENC_CODINGMODE, + ENC_MCBLK_WIDTH, ENC_MCBLK_HEIGHT, ENC_MCBLK_OVERLAP, + ENC_MVPREC, ENC_ME_SIMPLESEARCH, ENC_ME_COMBINED, + ENC_DWTINTRA, ENC_DWTINTER, ENC_DWTDEPTH, + ENC_MULTIQUANT, ENC_SPARTITION, ENC_NOAC, + 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(); - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_VCODEC ); - set_description( _("Dirac video decoder") ); - set_capability( "decoder", 100 ); - set_callbacks( OpenDecoder, CloseDecoder ); - add_shortcut( "dirac" ); - - add_submodule(); - set_description( _("Dirac video encoder") ); - set_capability( "encoder", 100 ); - set_callbacks( OpenEncoder, CloseEncoder ); - add_float( ENC_CFG_PREFIX "quality", 7.0, NULL, ENC_QUALITY_TEXT, - ENC_QUALITY_LONGTEXT, VLC_FALSE ); - -vlc_module_end(); + +vlc_module_begin() + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_VCODEC ) + set_description( N_("Dirac video encoder using dirac-research library") ) + set_capability( "encoder", 100 ) + set_callbacks( OpenEncoder, CloseEncoder ) + + add_float( ENC_CFG_PREFIX ENC_QUALITY_FACTOR, 5.5, NULL, + ENC_QUALITY_FACTOR_TEXT, ENC_QUALITY_FACTOR_LONGTEXT, false ) + change_float_range(0., 10.); + + add_integer( ENC_CFG_PREFIX ENC_TARGETRATE, -1, NULL, + ENC_TARGETRATE_TEXT, ENC_TARGETRATE_LONGTEXT, false ) + change_integer_range(-1, 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, 1, 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(-1, INT_MAX); + + add_integer( ENC_CFG_PREFIX ENC_L1NUM, -1, NULL, + ENC_L1NUM_TEXT, ENC_L1NUM_LONGTEXT, false ) + change_integer_range(-1, INT_MAX); + + add_string( ENC_CFG_PREFIX ENC_CODINGMODE, "auto", NULL, + ENC_CODINGMODE_TEXT, ENC_CODINGMODE_LONGTEXT, false ) + change_string_list( enc_codingmode_list, enc_codingmode_list_text, 0 ); + + 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_deprecated_alias( ENC_CFG_PREFIX ENC_MCBLK_XBLEN ); + change_integer_range(-1, INT_MAX); + + add_integer( ENC_CFG_PREFIX ENC_MCBLK_HEIGHT, -1, NULL, + ENC_MCBLK_HEIGHT, ENC_MCBLK_HEIGHT_LONGTEXT, false ) + add_deprecated_alias( ENC_CFG_PREFIX ENC_MCBLK_YBLEN ); + change_integer_range(-1, INT_MAX); + + add_integer( ENC_CFG_PREFIX ENC_MCBLK_OVERLAP, -1, NULL, + ENC_MCBLK_OVERLAP_TEXT, ENC_MCBLK_OVERLAP_LONGTEXT, false ) + change_integer_range(-1, 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(-1, 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(-1, INT_MAX); + + add_string( ENC_CFG_PREFIX ENC_ME_SIMPLESEARCH, "", NULL, + ENC_ME_SIMPLESEARCH_TEXT, ENC_ME_SIMPLESEARCH_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 + + add_integer( ENC_CFG_PREFIX ENC_DWTINTRA, -1, NULL, + ENC_DWTINTRA_TEXT, ENC_DWTINTRA_LONGTEXT, false ) + change_integer_range(-1, 6); + + add_integer( ENC_CFG_PREFIX ENC_DWTINTER, -1, NULL, + ENC_DWTINTER_TEXT, ENC_DWTINTER_LONGTEXT, false ) + change_integer_range(-1, 6); + + add_integer( ENC_CFG_PREFIX ENC_DWTDEPTH, -1, NULL, + ENC_DWTDEPTH_TEXT, ENC_DWTDEPTH_LONGTEXT, false ) + change_integer_range(-1, 4); + + /* advanced option only */ + /* NB, unforunately vlc doesn't have a concept of 'dont care' */ + add_integer( ENC_CFG_PREFIX ENC_MULTIQUANT, -1, NULL, + ENC_MULTIQUANT_TEXT, ENC_MULTIQUANT_LONGTEXT, true ) + change_integer_range(-1, 1); + + /* advanced option only */ + /* NB, unforunately vlc doesn't have a concept of 'dont care' */ + add_integer( ENC_CFG_PREFIX ENC_SPARTITION, -1, NULL, + ENC_SPARTITION_TEXT, ENC_SPARTITION_LONGTEXT, true ) + change_integer_range(-1, 1); + + add_bool( ENC_CFG_PREFIX ENC_NOAC, false, NULL, + ENC_NOAC_TEXT, ENC_NOAC_LONGTEXT, false ) + + /* advanced option only */ + add_float( ENC_CFG_PREFIX ENC_CPD, -1, NULL, + ENC_CPD_TEXT, ENC_CPD_LONGTEXT, true ) + change_integer_range(-1, INT_MAX); +vlc_module_end() /***************************************************************************** - * OpenDecoder: probe the decoder and return score + * picture_pts_t : store pts alongside picture number, not carried through + * encoder *****************************************************************************/ -static int OpenDecoder( vlc_object_t *p_this ) +struct picture_pts_t { - decoder_t *p_dec = (decoder_t*)p_this; - decoder_sys_t *p_sys; - dirac_decoder_t *p_dirac; + bool b_empty; /* entry is invalid */ + uint32_t u_pnum; /* dirac picture number */ + mtime_t i_pts; /* associated pts */ +}; - if( p_dec->fmt_in.i_codec != VLC_FOURCC('d','r','a','c') ) - { - return VLC_EGENERIC; - } +/***************************************************************************** + * encoder_sys_t : dirac encoder descriptor + *****************************************************************************/ +#define PTS_TLB_SIZE 256 +struct encoder_sys_t +{ + dirac_encoder_t *p_dirac; + dirac_encoder_context_t ctx; + bool b_auto_field_coding; - /* Initialise the dirac decoder */ - if( !(p_dirac = dirac_decoder_init(0)) ) return VLC_EGENERIC; + uint8_t *p_buffer_in; + int i_buffer_in; + uint32_t i_input_picnum; + block_fifo_t *p_dts_fifo; - /* 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 ) + int i_buffer_out; + uint8_t *p_buffer_out; + block_t *p_chain; + + struct picture_pts_t pts_tlb[PTS_TLB_SIZE]; + mtime_t i_pts_offset; + mtime_t i_field_time; +}; + +static struct +{ + unsigned int i_height; + int i_approx_fps; + VideoFormat i_vf; +} dirac_format_guess[] = { + /* Important: Keep this list ordered in ascending picture height */ + {1, 0, VIDEO_FORMAT_CUSTOM}, + {120, 15, VIDEO_FORMAT_QSIF525}, + {144, 12, VIDEO_FORMAT_QCIF}, + {240, 15, VIDEO_FORMAT_SIF525}, + {288, 12, VIDEO_FORMAT_CIF}, + {480, 30, VIDEO_FORMAT_SD_480I60}, + {480, 15, VIDEO_FORMAT_4SIF525}, + {576, 12, VIDEO_FORMAT_4CIF}, + {576, 25, VIDEO_FORMAT_SD_576I50}, + {720, 50, VIDEO_FORMAT_HD_720P50}, + {720, 60, VIDEO_FORMAT_HD_720P60}, + {1080, 24, VIDEO_FORMAT_DIGI_CINEMA_2K24}, + {1080, 25, VIDEO_FORMAT_HD_1080I50}, + {1080, 30, VIDEO_FORMAT_HD_1080I60}, + {1080, 50, VIDEO_FORMAT_HD_1080P50}, + {1080, 60, VIDEO_FORMAT_HD_1080P60}, + {2160, 24, VIDEO_FORMAT_DIGI_CINEMA_4K24}, + {2160, 50, VIDEO_FORMAT_UHDTV_4K50}, + {2160, 60, VIDEO_FORMAT_UHDTV_4K60}, + {3840, 50, VIDEO_FORMAT_UHDTV_8K50}, + {3840, 60, VIDEO_FORMAT_UHDTV_8K60}, + {0, 0, 0}, +}; + +/***************************************************************************** + * ResetPTStlb: Purge all entries in @p_dec@'s PTS-tlb + *****************************************************************************/ +static void ResetPTStlb( encoder_t *p_enc ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + for( int i=0; ipts_tlb[i].b_empty = true; } +} - p_sys->p_dirac = p_dirac; +/***************************************************************************** + * 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; - /* Set output properties */ - p_dec->fmt_out.i_cat = VIDEO_ES; - p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0'); + 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; - /* Set callbacks */ - p_dec->pf_decode_video = DecodeBlock; + return; + } + } - return VLC_SUCCESS; + msg_Err( p_enc, "Could not store PTS %"PRId64" for frame %u", i_pts, u_pnum ); } -static void FreeFrameBuffer( dirac_decoder_t *p_dirac ) +/***************************************************************************** + * GetPicturePTS: Retrieve the PTS value for a particular picture number + *****************************************************************************/ +static mtime_t GetPicturePTS( encoder_t *p_enc, uint32_t u_pnum ) { - if( p_dirac->fbuf ) + 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 ) { - if( p_dirac->fbuf->buf[i] ) free( p_dirac->fbuf->buf[i] ); - p_dirac->fbuf->buf[i] = 0; + 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; } /***************************************************************************** - * GetNewPicture: Get a new picture from the vout and copy the decoder output + * OpenEncoder: probe the encoder and return score *****************************************************************************/ -static picture_t *GetNewPicture( decoder_t *p_dec ) +static int OpenEncoder( vlc_object_t *p_this ) { - decoder_sys_t *p_sys = p_dec->p_sys; - picture_t *p_pic; - int i_plane; + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys = p_enc->p_sys; + int i_tmp; + float f_tmp; + char *psz_tmp; - switch( p_sys->p_dirac->src_params.chroma ) + if( p_enc->fmt_out.i_codec != VLC_FOURCC('d','r','a','c') && + !p_enc->b_force ) { - 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; + return VLC_EGENERIC; } - 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 = p_dec->pf_vout_buffer_new( p_dec ); + if( !p_enc->fmt_in.video.i_frame_rate || !p_enc->fmt_in.video.i_frame_rate_base || + !p_enc->fmt_in.video.i_height || !p_enc->fmt_in.video.i_width ) + { + msg_Err( p_enc, "Framerate and picture dimensions must be non-zero" ); + return VLC_EGENERIC; + } - if( p_pic == NULL ) return NULL; - p_pic->b_progressive = !p_sys->p_dirac->src_params.interlace; - p_pic->b_top_field_first = p_sys->p_dirac->src_params.topfieldfirst; + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_sys = calloc( 1, sizeof(*p_sys) ) ) == NULL ) + return VLC_ENOMEM; - p_pic->i_nb_fields = 2; + p_enc->p_sys = p_sys; + p_enc->pf_encode_video = Encode; + p_enc->fmt_out.i_codec = VLC_FOURCC('d','r','a','c'); + p_enc->fmt_out.i_cat = VIDEO_ES; - /* Copy picture stride by stride */ - for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) + if( ( p_sys->p_dts_fifo = block_FifoNew() ) == NULL ) { - 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; + CloseEncoder( p_this ); + return VLC_ENOMEM; + } - i_width = p_pic->p[i_plane].i_visible_pitch; - i_dst_stride = p_pic->p[i_plane].i_pitch; + ResetPTStlb( p_enc ); - for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines; i_line++ ) + /* guess the video format based upon number of lines and picture height */ + int i = 0; + VideoFormat guessed_video_fmt = 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( dirac_format_guess[i].i_height > p_enc->fmt_in.video.i_height ) { - p_dec->p_libvlc->pf_memcpy( p_dst, p_src, i_width ); - p_src += i_width; - p_dst += i_dst_stride; + guessed_video_fmt = dirac_format_guess[i-1].i_vf; + break; } - } + 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; + + guessed_video_fmt = dirac_format_guess[i].i_vf; + break; + } while( dirac_format_guess[++i].i_height ); - return p_pic; -} + dirac_encoder_context_init( &p_sys->ctx, guessed_video_fmt ); -/***************************************************************************** - * CloseDecoder: decoder destruction - *****************************************************************************/ -static void CloseDecoder( vlc_object_t *p_this ) -{ - decoder_t *p_dec = (decoder_t *)p_this; - decoder_sys_t *p_sys = p_dec->p_sys; + /* 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.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; + unsigned u_asr_num, u_asr_den; + vlc_ureduce( &u_asr_num, &u_asr_den, + 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 ); + p_sys->ctx.src_params.pix_asr.numerator = u_asr_num; + p_sys->ctx.src_params.pix_asr.denominator = u_asr_den; - FreeFrameBuffer( p_sys->p_dirac ); - dirac_decoder_close( p_sys->p_dirac ); - free( p_sys ); -} + config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg ); -/**************************************************************************** - * DecodeBlock: the whole thing - **************************************************************************** - * This function must be fed with complete frames. - ****************************************************************************/ -static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) -{ - decoder_sys_t *p_sys = p_dec->p_sys; - dirac_decoder_state_t state; - picture_t *p_pic; - block_t *p_block; + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_CHROMAFMT ); + if( !psz_tmp ) + goto error; + else if( !strcmp( psz_tmp, "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( psz_tmp, "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( psz_tmp, "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; + } + else { + msg_Err( p_enc, "Invalid chroma format: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + free( psz_tmp ); - if( !pp_block || !*pp_block ) return NULL; + p_sys->ctx.enc_params.qf = var_GetFloat( p_enc, ENC_CFG_PREFIX ENC_QUALITY_FACTOR ); - p_block = *pp_block; + /* use bitrate from sout-transcode-vb in kbps */ + p_sys->ctx.enc_params.trate = p_enc->fmt_out.i_bitrate / 1000; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_TARGETRATE ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.trate = i_tmp; + p_enc->fmt_out.i_bitrate = p_sys->ctx.enc_params.trate * 1000; - while( 1 ) - { - state = dirac_parse( p_sys->p_dirac ); + p_sys->ctx.enc_params.lossless = var_GetBool( p_enc, ENC_CFG_PREFIX ENC_LOSSLESS ); - switch( state ) - { - case STATE_BUFFER: - if( !p_block->i_buffer ) - { - block_Release( p_block ); - return NULL; - } + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_PREFILTER ); + if( !psz_tmp ) + goto error; + else if( !strcmp( psz_tmp, "none" ) ) { + p_sys->ctx.enc_params.prefilter = NO_PF; + } + else if( !strcmp( psz_tmp, "cwm" ) ) { + p_sys->ctx.enc_params.prefilter = CWM; + } + else if( !strcmp( psz_tmp, "rectlp" ) ) { + p_sys->ctx.enc_params.prefilter = RECTLP; + } + else if( !strcmp( psz_tmp, "diaglp" ) ) { + p_sys->ctx.enc_params.prefilter = DIAGLP; + } + else { + msg_Err( p_enc, "Invalid prefilter: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + free( psz_tmp ); - 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_sys->ctx.enc_params.prefilter_strength = + var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_PREFILTER_STRENGTH ); - p_block->i_buffer = 0; - break; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_L1SEP ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.L1_sep = i_tmp; - 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; - } + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_L1NUM ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.num_L1 = i_tmp; - case STATE_SEQUENCE_END: - msg_Dbg( p_dec, "SEQUENCE_END" ); - FreeFrameBuffer( p_sys->p_dirac ); - break; + 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; + p_sys->ctx.enc_params.picture_coding_mode = 0; + } + else if( !strcmp( psz_tmp, "field" ) ) { + p_sys->b_auto_field_coding = false; + p_sys->ctx.enc_params.picture_coding_mode = 1; + } + else { + msg_Err( p_enc, "Invalid codingmode: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + free( psz_tmp ); - case STATE_PICTURE_START: - msg_Dbg( p_dec, "PICTURE_START: frame_type=%i frame_num=%d", - p_sys->p_dirac->frame_params.ftype, - p_sys->p_dirac->frame_params.fnum ); - break; + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_MVPREC ); + if( !psz_tmp ) + goto error; + else if( !strcmp( psz_tmp, "1" ) ) { + p_sys->ctx.enc_params.mv_precision = MV_PRECISION_PIXEL; + } + else if( !strcmp( psz_tmp, "1/2" ) ) { + p_sys->ctx.enc_params.mv_precision = MV_PRECISION_HALF_PIXEL; + } + else if( !strcmp( psz_tmp, "1/4" ) ) { + p_sys->ctx.enc_params.mv_precision = MV_PRECISION_QUARTER_PIXEL; + } + else if( !strcmp( psz_tmp, "1/8" ) ) { + p_sys->ctx.enc_params.mv_precision = MV_PRECISION_EIGHTH_PIXEL; + } + else { + msg_Err( p_enc, "Invalid mv-prec: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + free( psz_tmp ); - case STATE_PICTURE_AVAIL: - msg_Dbg( p_dec, "PICTURE_AVAI : frame_type=%i frame_num=%d", - p_sys->p_dirac->frame_params.ftype, - p_sys->p_dirac->frame_params.fnum ); + /* + * {x,y}b{len,sep} must be multiples of 4 + */ + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MCBLK_WIDTH ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.xbsep = i_tmp / 4 * 4; + + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MCBLK_HEIGHT ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.ybsep = i_tmp / 4 * 4; + + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MCBLK_OVERLAP ); + if( i_tmp > -1 ) { + p_sys->ctx.enc_params.xblen = p_sys->ctx.enc_params.xbsep * (100 + i_tmp) / 400 * 4; + p_sys->ctx.enc_params.yblen = p_sys->ctx.enc_params.ybsep * (100 + i_tmp) / 400 * 4; + } - /* 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; + /* + * {x,y}blen >= {x,y}bsep + * {x,y}blen <= 2* {x,y}bsep + */ + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MCBLK_XBLEN ); + if( i_tmp > -1 ) { + int xblen = __MAX( i_tmp, p_sys->ctx.enc_params.xbsep ); + xblen = __MIN( xblen, 2 * p_sys->ctx.enc_params.xbsep ); + p_sys->ctx.enc_params.xblen = xblen; + } - case STATE_INVALID: - msg_Dbg( p_dec, "STATE_INVALID" ); - break; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MCBLK_YBLEN ); + if( i_tmp > -1 ) { + int yblen = __MAX( i_tmp, p_sys->ctx.enc_params.ybsep ); + yblen = __MIN( yblen, 2 * p_sys->ctx.enc_params.ybsep ); + p_sys->ctx.enc_params.yblen = yblen; + } - default: - break; + psz_tmp = var_GetString( p_enc, ENC_CFG_PREFIX ENC_ME_SIMPLESEARCH ); + if( !psz_tmp ) + goto error; + if( *psz_tmp != '\0' ) { + /* of the form [0-9]+:[0-9]+ */ + char *psz_start = psz_tmp; + char *psz_end = psz_tmp; + p_sys->ctx.enc_params.x_range_me = strtol(psz_start, &psz_end, 10); + if( *psz_end != ':' || psz_end == psz_start ) { + msg_Err( p_enc, "Invalid simple search range: %s", psz_tmp ); + free( psz_tmp ); + goto error; } + psz_start = ++psz_end; + p_sys->ctx.enc_params.y_range_me = strtol(psz_start, &psz_end, 10); + if( *psz_end != '\0' || psz_end == psz_start ) { + msg_Err( p_enc, "Invalid simple search range: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + if( p_sys->ctx.enc_params.x_range_me < 0 || + p_sys->ctx.enc_params.y_range_me < 0 ) + { + msg_Err( p_enc, "Invalid negative simple search range: %s", psz_tmp ); + free( psz_tmp ); + goto error; + } + p_sys->ctx.enc_params.full_search = 1; } + free( psz_tmp ); - /* Never reached */ - return NULL; -} +#if DIRAC_RESEARCH_VERSION_ATLEAST(1,0,1) + p_sys->ctx.enc_params.combined_me = var_GetBool( p_enc, ENC_CFG_PREFIX ENC_ME_COMBINED ); +#endif -/***************************************************************************** - * encoder_sys_t : dirac encoder descriptor - *****************************************************************************/ -#define ENC_BUFSIZE 1024*1024 -struct encoder_sys_t -{ - /* - * Dirac properties - */ - dirac_encoder_t *p_dirac; - dirac_encoder_context_t ctx; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_DWTINTRA ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.intra_wlt_filter = i_tmp; - uint8_t *p_buffer_in; - int i_buffer_in; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_DWTINTER ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.inter_wlt_filter = i_tmp; - uint8_t p_buffer_out[ENC_BUFSIZE]; -}; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_DWTDEPTH ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.wlt_depth = i_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 = p_enc->p_sys; - vlc_value_t val; - float f_quality; + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_MULTIQUANT ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.multi_quants = i_tmp; - if( p_enc->fmt_out.i_codec != VLC_FOURCC('d','r','a','c') && - !p_enc->b_force ) + i_tmp = var_GetInteger( p_enc, ENC_CFG_PREFIX ENC_SPARTITION ); + if( i_tmp > -1 ) + p_sys->ctx.enc_params.spatial_partition = i_tmp; + + p_sys->ctx.enc_params.using_ac = !var_GetBool( p_enc, ENC_CFG_PREFIX ENC_NOAC ); + + f_tmp = var_GetFloat( p_enc, ENC_CFG_PREFIX ENC_CPD ); + if( f_tmp > -1 ) + p_sys->ctx.enc_params.cpd = f_tmp; + + /* Allocate the buffer for inputing frames into the encoder */ + if( ( p_sys->p_buffer_in = malloc( p_sys->i_buffer_in ) ) == NULL ) { - return VLC_EGENERIC; + CloseEncoder( p_this ); + return VLC_ENOMEM; } - /* Allocate the memory needed to store the decoder's structure */ - if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) + /* Set up output buffer */ + /* Unfortunately it isn't possible to determine if the buffer + * is too small (and then reallocate it) */ + p_sys->i_buffer_out = 4096 + p_sys->i_buffer_in; + if( ( p_sys->p_buffer_out = malloc( p_sys->i_buffer_out ) ) == NULL ) { - msg_Err( p_enc, "out of memory" ); - return VLC_EGENERIC; + CloseEncoder( p_this ); + return VLC_ENOMEM; } - memset( p_sys, 0, sizeof(encoder_sys_t) ); - 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 ); - - dirac_encoder_context_init( &p_sys->ctx, VIDEO_FORMAT_CUSTOM ); - /* */ - 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.interlace = 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 ); - - /* 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; +error: + CloseEncoder( p_this ); + return VLC_EGENERIC; +} + +/* 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; } /**************************************************************************** * 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 ) { encoder_sys_t *p_sys = p_enc->p_sys; - block_t *p_block, *p_chain = NULL; + block_t *p_block, *p_output_chain = NULL; 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 ) + { + date_t date; + /* Initialise the encoder with the encoder context */ + p_sys->p_dirac = dirac_encoder_init( &p_sys->ctx, 0 ); + if( !p_sys->p_dirac ) + { + msg_Err( p_enc, "Failed to initialize dirac encoder" ); + p_enc->b_error = 1; + return NULL; + } + date_Init( &date, p_enc->fmt_in.video.i_frame_rate, p_enc->fmt_in.video.i_frame_rate_base ); +#if DIRAC_RESEARCH_VERSION_ATLEAST(1,0,2) + int i_delayinpics = dirac_encoder_ptsoffset( p_sys->p_dirac ); + i_delayinpics /= p_sys->ctx.enc_params.picture_coding_mode + 1; + date_Increment( &date, i_delayinpics ); +#else + date_Increment( &date, 1 ); +#endif + p_sys->i_pts_offset = date_Get( &date ); + + /* picture_coding_mode = 1 == FIELD_CODING, two pictures are produced + * for each frame input. Calculate time between fields for offsetting + * the second field later. */ + if( 1 == p_sys->ctx.enc_params.picture_coding_mode ) + { + date_Set( &date, 0 ); + date_Increment( &date, 1 ); + p_sys->i_field_time = date_Get( &date ) / 2; + } + } + + /* 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,7 +837,7 @@ static block_t *Encode( encoder_t *p_enc, picture_t *p_pic ) for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines; i_line++ ) { - p_enc->p_libvlc->pf_memcpy( p_dst, p_src, i_width ); + vlc_memcpy( p_dst, p_src, i_width ); p_dst += i_width; p_src += i_src_stride; } @@ -414,59 +845,132 @@ 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; + } + + /* 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++; - msg_Dbg( p_enc, "dirac_encoder_load" ); + /* store dts in a queue, so that they appear in order in + * coded order */ + p_block = block_New( p_enc, 1 ); + if( !p_block ) + { + p_enc->b_error = 1; + 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( 1 == p_sys->ctx.enc_params.picture_coding_mode ) + { + StorePicturePTS( p_enc, p_sys->i_input_picnum, p_pic->date + p_sys->i_field_time ); + p_sys->i_input_picnum++; - /* Retrieve encoded frames from encoder */ - do + p_block = block_New( p_enc, 1 ); + if( !p_block ) { - 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 ) + p_enc->b_error = 1; + 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; + } + + dirac_encoder_state_t state; + /* 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 = p_sys->i_buffer_out; + state = dirac_encoder_output( p_sys->p_dirac ); + switch( state ) + { + case ENC_STATE_AVAIL: { + uint32_t pic_num; + + /* extract data from encoder temporary buffer. */ + p_block = block_New( p_enc, p_sys->p_dirac->enc_buf.size ); + if( !p_block ) { - 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; + p_enc->b_error = 1; + return NULL; } - if( p_sys->p_dirac->decoded_frame_avail ) + memcpy( p_block->p_buffer, p_sys->p_dirac->enc_buf.buffer, + p_sys->p_dirac->enc_buf.size ); + + /* if some flags were set for a previous block, prevent + * them from getting lost */ + if( p_sys->p_chain ) + p_block->i_flags |= p_sys->p_chain->i_flags; + + /* store all extracted blocks in a chain and gather up when an + * entire encapsulation unit is avaliable (ends with a picture) */ + block_ChainAppend( &p_sys->p_chain, p_block ); + + /* Presence of a Sequence header indicates a seek point */ + if( 0 == p_block->p_buffer[4] ) { - //locally decoded frame is available in - //encoder->dec_buf - //locally decoded frame parameters available - //in encoder->dec_fparams + 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 ? */ + p_enc->fmt_out.p_extra = malloc( len + sizeof(eos) ); + if( !p_enc->fmt_out.p_extra ) + { + p_enc->b_error = 1; + 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 + 10, len ); + p_enc->fmt_out.i_extra = len + sizeof(eos); + } } - if( p_sys->p_dirac->instr_data_avail ) + + if( ReadDiracPictureNumber( &pic_num, p_block ) ) { - //Instrumentation data (motion vectors etc.) - //available in encoder->instr + /* Finding a picture terminates an ecapsulation unit, gather + * all data and output; use the next dts value queued up + * and find correct pts in the tlb */ + p_block = block_FifoGet( p_sys->p_dts_fifo ); + p_sys->p_chain->i_dts = p_block->i_dts; + p_sys->p_chain->i_pts = GetPicturePTS( p_enc, pic_num ); + block_Release( p_block ); + block_ChainAppend( &p_output_chain, block_ChainGather( p_sys->p_chain ) ); + p_sys->p_chain = NULL; + } else { + p_block = NULL; + } + break; } - } while( state == ENC_STATE_AVAIL ); - } - else - { - msg_Dbg( p_enc, "dirac_encoder_load() error" ); - } + case ENC_STATE_BUFFER: + break; + case ENC_STATE_INVALID: + default: + break; + } + } while( state == ENC_STATE_AVAIL ); - return p_chain; + return p_output_chain; } /***************************************************************************** @@ -477,12 +981,16 @@ 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: %i bits/sec", - p_sys->p_dirac->enc_seqstats.bit_rate ); - /* Free the encoder resources */ - dirac_encoder_close( p_sys->p_dirac ); - + if( p_sys->p_dirac ) + dirac_encoder_close( p_sys->p_dirac ); + free( p_sys->p_buffer_in ); + free( p_sys->p_buffer_out ); + + if( p_sys->p_dts_fifo ) + block_FifoRelease( p_sys->p_dts_fifo ); + block_ChainRelease( p_sys->p_chain ); + free( p_sys ); }