Commit 1c2a95b8 authored by Anuradha Suraparaju's avatar Anuradha Suraparaju Committed by Jean-Baptiste Kempf
Browse files

Add Dirac encoding support to VLC using the Schroedinger library.


Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent 677f45de
......@@ -2967,7 +2967,7 @@ PKG_ENABLE_MODULES_VLC([DIRAC], [], [dirac >= 0.10.0], [dirac encoder], [auto])
dnl
dnl schroedinger decoder plugin (for dirac format video)
dnl
PKG_ENABLE_MODULES_VLC([SCHROEDINGER], [], [schroedinger-1.0 >= 1.0.6], [dirac decoder using schroedinger], [auto])
PKG_ENABLE_MODULES_VLC([SCHROEDINGER], [], [schroedinger-1.0 >= 1.0.10], [dirac decoder and encoder using schroedinger], [auto])
dnl
dnl PNG decoder module
......
......@@ -8,6 +8,7 @@
*
* Authors: Jonathan Rosser <jonathan.rosser@gmail.com>
* David Flynn <davidf at rd dot bbc.co.uk>
* Anuradha Suraparaju <asuraparaju at gmail dot com>
*
* 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
......@@ -31,6 +32,8 @@
# include "config.h"
#endif
#include <assert.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
......@@ -43,6 +46,27 @@
*****************************************************************************/
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_CODINGMODE "auto_coding_mode"
#define ENC_CODINGMODE_TEXT N_("Automate picture coding mode")
#define ENC_CODINGMODE_LONGTEXT N_("Use the input picture to determine how to" \
" code it - interlaced or progressive")
static const char **ppsz_enc_options = NULL;
vlc_module_begin ()
set_category( CAT_INPUT )
......@@ -51,6 +75,66 @@ vlc_module_begin ()
set_capability( "decoder", 200 )
set_callbacks( OpenDecoder, CloseDecoder )
add_shortcut( "schroedinger" )
add_submodule()
set_description( N_("Dirac video encoder using libschroedinger") )
set_capability( "encoder", 110 )
set_callbacks( OpenEncoder, CloseEncoder )
add_shortcut( "schroedinger", "schro" )
int i_numopts = schro_encoder_get_n_settings();
/* Allocate for schro encoder options + chroma format + coding mode
+ null */
ppsz_enc_options = (const char **)malloc( sizeof( char * )*( i_numopts + 3 ) );
if( unlikely( ppsz_enc_options == NULL ) )
return VLC_ENOMEM;
for( int i = 0; i < i_numopts; ++i ) {
const SchroEncoderSetting *p_setting = schro_encoder_get_setting_info( i );
bool b_advanced = ( strlen( p_setting->name ) > 6 && !strncmp( p_setting->name, "magic_ ", 6 ) );
char *p_cfg_setting_name = malloc( strlen( ENC_CFG_PREFIX ) + strlen( p_setting->name ) + 1 );
if( unlikely( p_cfg_setting_name == NULL ) )
return VLC_ENOMEM;
strcpy( p_cfg_setting_name, ENC_CFG_PREFIX );
strcat( p_cfg_setting_name, p_setting->name );
ppsz_enc_options[i] = strdup( p_setting->name );
switch( p_setting->type ) {
case SCHRO_ENCODER_SETTING_TYPE_BOOLEAN:
add_bool( p_cfg_setting_name, p_setting->default_value, p_setting->name, p_setting->name, b_advanced );
break;
case SCHRO_ENCODER_SETTING_TYPE_INT:
add_integer( p_cfg_setting_name, p_setting->default_value, p_setting->name, p_setting->name, b_advanced );
change_integer_range( p_setting->min, p_setting->max );
break;
case SCHRO_ENCODER_SETTING_TYPE_DOUBLE:
add_float( p_cfg_setting_name, p_setting->default_value, p_setting->name, p_setting->name, b_advanced );
change_float_range( p_setting->min, p_setting->max );
break;
case SCHRO_ENCODER_SETTING_TYPE_ENUM:
add_string( p_cfg_setting_name, p_setting->enum_list[(int)p_setting->default_value], p_setting->name, p_setting->name, b_advanced );
vlc_config_set( p_config, VLC_CONFIG_LIST, (int)(p_setting->max-p_setting->min+1), p_setting->enum_list, p_setting->enum_list, 0 );
break;
default:
break;
}
free( p_cfg_setting_name );
}
ppsz_enc_options[i_numopts] = strdup( ENC_CHROMAFMT );
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, 0 );
ppsz_enc_options[i_numopts+1] = strdup( ENC_CODINGMODE );
add_bool( ENC_CFG_PREFIX ENC_CODINGMODE, true,
ENC_CODINGMODE_TEXT, ENC_CODINGMODE_LONGTEXT, false )
ppsz_enc_options[i_numopts+2] = NULL;
vlc_module_end ()
/*****************************************************************************
......@@ -393,3 +477,663 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
}
}
/*****************************************************************************
* 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
{
bool b_empty; /* entry is invalid */
uint32_t u_pnum; /* dirac picture number */
mtime_t i_pts; /* associated pts */
};
/*****************************************************************************
* 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; 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].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, const SchroEncoderSetting *p_setting, const char *psz_value ) {
encoder_sys_t *p_sys = p_enc->p_sys;
if( psz_value && p_setting && p_setting->type == SCHRO_ENCODER_SETTING_TYPE_ENUM ) {
int i_index = -1;
int i_list_size = p_setting->max - p_setting->min + 1;
for( int i = 0; i < i_list_size; ++i ) {
if( strcmp( p_setting->enum_list[i], psz_value ) )
continue;
i_index = p_setting->min + i;
schro_encoder_setting_set_double( p_sys->p_schro, p_setting->name, i_index );
return true;
}
if( i_index == -1 ) {
msg_Err( p_enc, "Invalid %s: %s", p_setting->name, 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;
}
/*****************************************************************************
* 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;
int i_tmp;
float f_tmp;
bool b_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_height || !p_enc->fmt_in.video.i_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_width;
p_sys->p_format->height = p_enc->fmt_in.video.i_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 );
int i_numopts = schro_encoder_get_n_settings();
for( int i = 0; i < i_numopts; ++i ) {
const SchroEncoderSetting *p_setting = schro_encoder_get_setting_info( i );
char *p_cfg_setting_name = malloc( strlen( ENC_CFG_PREFIX ) + strlen( p_setting->name ) + 1 );
if( unlikely( p_cfg_setting_name == NULL ) )
return VLC_ENOMEM;
strcpy( p_cfg_setting_name, ENC_CFG_PREFIX );
strcat( p_cfg_setting_name, p_setting->name );
switch( p_setting->type ) {
case SCHRO_ENCODER_SETTING_TYPE_BOOLEAN:
schro_encoder_setting_set_double ( p_sys->p_schro, p_setting->name, var_GetBool( p_enc, p_cfg_setting_name ) );
break;
case SCHRO_ENCODER_SETTING_TYPE_INT:
i_tmp = var_GetInteger( p_enc, p_cfg_setting_name );
schro_encoder_setting_set_double( p_sys->p_schro, p_setting->name, i_tmp );
/* A kludge to set bitrate to the value in sout-transcode-vb */
if( !strcmp( p_setting->name, "bitrate" ) ) {
if( i_tmp == p_setting->default_value )
schro_encoder_setting_set_double( p_sys->p_schro, p_setting->name, p_enc->fmt_out.i_bitrate );
p_enc->fmt_out.i_bitrate = schro_encoder_setting_get_double( p_sys->p_schro, p_setting->name );
}
break;
case SCHRO_ENCODER_SETTING_TYPE_DOUBLE:
f_tmp = var_GetFloat( p_enc, p_cfg_setting_name );
if( f_tmp >= 0.0 )
schro_encoder_setting_set_double( p_sys->p_schro, p_setting->name, f_tmp );
break;
case SCHRO_ENCODER_SETTING_TYPE_ENUM:
psz_tmp = var_GetString( p_enc, p_cfg_setting_name );
if( !psz_tmp )
goto error;
else if( *psz_tmp != '\0' ) {
if( !SchroSetEnum( p_enc, p_setting, psz_tmp ) ) {
free( psz_tmp );
goto error;
}
}
free( psz_tmp );
break;
default:
break;
}
free( p_cfg_setting_name );
}
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 );
b_tmp = var_GetBool( p_enc, ENC_CFG_PREFIX ENC_CODINGMODE );
if( b_tmp == true )
p_sys->b_auto_field_coding = true;
else
p_sys->b_auto_field_coding = false;
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;