Commit 3e1c59ce authored by Jean-Paul Saman's avatar Jean-Paul Saman

transcode: rewrite transcode_audio_process

parent 15ce6c62
/***************************************************************************** /*****************************************************************************
* transcode.c: transcoding stream output module * transcode.c: transcoding stream output module
***************************************************************************** *****************************************************************************
* Copyright (C) 2003-2008 the VideoLAN team * Copyright (C) 2003-2009 the VideoLAN team
* $Id$ * $Id$
* *
* Authors: Laurent Aimar <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@videolan.org> * Gildas Bazin <gbazin@videolan.org>
* Jean-Paul Saman <jpsaman #_at_# m2x dot nl> * Jean-Paul Saman <jean-paul.saman at m2x dot nl>
* Antoine Cellerier <dionoea at videolan dot org> * Antoine Cellerier <dionoea at videolan dot org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -254,7 +254,7 @@ static int Del ( sout_stream_t *, sout_stream_id_t * ); ...@@ -254,7 +254,7 @@ static int Del ( sout_stream_t *, sout_stream_id_t * );
static int Send( sout_stream_t *, sout_stream_id_t *, block_t* ); static int Send( sout_stream_t *, sout_stream_id_t *, block_t* );
static int transcode_audio_new ( sout_stream_t *, sout_stream_id_t * ); static int transcode_audio_new ( sout_stream_t *, sout_stream_id_t * );
static void transcode_audio_close ( sout_stream_id_t * ); static void transcode_audio_close ( sout_stream_t *, sout_stream_id_t * );
static int transcode_audio_process( sout_stream_t *, sout_stream_id_t *, static int transcode_audio_process( sout_stream_t *, sout_stream_id_t *,
block_t *, block_t ** ); block_t *, block_t ** );
...@@ -286,7 +286,9 @@ static void transcode_osd_close ( sout_stream_t *, sout_stream_id_t * ); ...@@ -286,7 +286,9 @@ static void transcode_osd_close ( sout_stream_t *, sout_stream_id_t * );
static int transcode_osd_process( sout_stream_t *, sout_stream_id_t *, static int transcode_osd_process( sout_stream_t *, sout_stream_id_t *,
block_t *, block_t ** ); block_t *, block_t ** );
static void* EncoderThread( vlc_object_t * p_this ); static void* VideoEncoderThread( vlc_object_t * p_this );
static void* AudioThread( vlc_object_t * p_this );
static const int pi_channels_maps[6] = static const int pi_channels_maps[6] =
{ {
...@@ -305,29 +307,27 @@ static const int pi_channels_maps[6] = ...@@ -305,29 +307,27 @@ static const int pi_channels_maps[6] =
#define ENC_FRAMERATE (25 * 1000 + .5) #define ENC_FRAMERATE (25 * 1000 + .5)
#define ENC_FRAMERATE_BASE 1000 #define ENC_FRAMERATE_BASE 1000
struct sout_stream_sys_t typedef struct sout_stream_video_t sout_stream_video_t;
typedef struct sout_stream_audio_t sout_stream_audio_t;
struct sout_stream_video_t
{ {
VLC_COMMON_MEMBERS VLC_COMMON_MEMBERS
sout_stream_t *p_out; sout_stream_id_t *id;
sout_stream_id_t *id_video; #if 0
picture_fifo_t *pic_empty;
picture_fifo_t *pic_fifo;
block_fifo_t *fifo_in;
block_fifo_t *fifo_out;
#endif
block_t *p_buffers; block_t *p_buffers;
vlc_mutex_t lock_out; vlc_mutex_t lock_out;
vlc_cond_t cond; vlc_cond_t cond;
picture_t * pp_pics[PICTURE_RING_SIZE]; picture_t * pp_pics[PICTURE_RING_SIZE];
int i_first_pic, i_last_pic; int i_first_pic, i_last_pic;
/* Audio */
vlc_fourcc_t i_acodec; /* codec audio (0 if not transcode) */
char *psz_aenc;
config_chain_t *p_audio_cfg;
uint32_t i_sample_rate;
uint32_t i_channels;
int i_abitrate;
char *psz_af2;
/* Video */
vlc_fourcc_t i_vcodec; /* codec video (0 if not transcode) */ vlc_fourcc_t i_vcodec; /* codec video (0 if not transcode) */
char *psz_venc; char *psz_venc;
config_chain_t *p_video_cfg; config_chain_t *p_video_cfg;
...@@ -336,15 +336,49 @@ struct sout_stream_sys_t ...@@ -336,15 +336,49 @@ struct sout_stream_sys_t
double f_fps; double f_fps;
unsigned int i_width, i_maxwidth; unsigned int i_width, i_maxwidth;
unsigned int i_height, i_maxheight; unsigned int i_height, i_maxheight;
bool b_deinterlace; bool b_deinterlace;
char *psz_deinterlace; char *psz_deinterlace;
config_chain_t *p_deinterlace_cfg; config_chain_t *p_deinterlace_cfg;
int i_threads; int i_threads;
bool b_high_priority;
bool b_hurry_up;
char *psz_vf2; char *psz_vf2;
sout_stream_sys_t *p_sys;
};
struct sout_stream_audio_t
{
VLC_COMMON_MEMBERS
sout_stream_id_t *id;
block_fifo_t *fifo_in;
block_fifo_t *fifo_out;
vlc_fourcc_t i_acodec; /* codec audio (0 if not transcode) */
char *psz_aenc;
config_chain_t *p_audio_cfg;
uint32_t i_sample_rate;
uint32_t i_channels;
int i_abitrate;
char *psz_af2;
sout_stream_sys_t *p_sys;
};
struct sout_stream_sys_t
{
sout_stream_t *p_out;
/* Video */
sout_stream_video_t *p_video;
/* Audio */
sout_stream_audio_t *p_audio;
/* SPU */ /* SPU */
vlc_fourcc_t i_scodec; /* codec spu (0 if not transcode) */ vlc_fourcc_t i_scodec; /* codec spu (0 if not transcode) */
char *psz_senc; char *psz_senc;
...@@ -358,6 +392,10 @@ struct sout_stream_sys_t ...@@ -358,6 +392,10 @@ struct sout_stream_sys_t
config_chain_t *p_osd_cfg; config_chain_t *p_osd_cfg;
bool b_osd; /* true when osd es is registered */ bool b_osd; /* true when osd es is registered */
/* Misc. */
bool b_high_priority;
bool b_hurry_up;
/* Sync */ /* Sync */
bool b_master_sync; bool b_master_sync;
mtime_t i_master_drift; mtime_t i_master_drift;
...@@ -368,6 +406,7 @@ struct decoder_owner_sys_t ...@@ -368,6 +406,7 @@ struct decoder_owner_sys_t
picture_t *pp_pics[PICTURE_RING_SIZE]; picture_t *pp_pics[PICTURE_RING_SIZE];
sout_stream_sys_t *p_sys; sout_stream_sys_t *p_sys;
}; };
struct filter_owner_sys_t struct filter_owner_sys_t
{ {
picture_t *pp_pics[PICTURE_RING_SIZE]; picture_t *pp_pics[PICTURE_RING_SIZE];
...@@ -381,18 +420,39 @@ static int Open( vlc_object_t *p_this ) ...@@ -381,18 +420,39 @@ static int Open( vlc_object_t *p_this )
{ {
sout_stream_t *p_stream = (sout_stream_t*)p_this; sout_stream_t *p_stream = (sout_stream_t*)p_this;
sout_stream_sys_t *p_sys; sout_stream_sys_t *p_sys;
sout_stream_audio_t *p_audio;
sout_stream_video_t *p_video;
vlc_value_t val; vlc_value_t val;
p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) ); p_sys = (sout_stream_sys_t *)calloc( 1, sizeof( sout_stream_sys_t ) );
if( !p_sys ) return VLC_ENOMEM;
p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next ); p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
if( !p_sys->p_out ) if( !p_sys->p_out )
{ {
msg_Err( p_stream, "cannot create chain" ); msg_Err( p_stream, "cannot create chain" );
vlc_object_release( p_sys ); free( p_sys );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
p_sys->p_audio = p_audio = vlc_object_create( p_this, sizeof( sout_stream_audio_t ) );
if( !p_audio )
{
free( p_sys );
return VLC_ENOMEM;
}
p_sys->p_video = p_video = vlc_object_create( p_this, sizeof( sout_stream_video_t ) );
if( !p_video )
{
vlc_object_release( p_audio );
free( p_sys );
return VLC_ENOMEM;
}
p_video->p_sys = p_sys;
p_audio->p_sys = p_sys;
p_sys->i_master_drift = 0; p_sys->i_master_drift = 0;
config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
...@@ -400,143 +460,145 @@ static int Open( vlc_object_t *p_this ) ...@@ -400,143 +460,145 @@ static int Open( vlc_object_t *p_this )
/* Audio transcoding parameters */ /* Audio transcoding parameters */
var_Get( p_stream, SOUT_CFG_PREFIX "aenc", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "aenc", &val );
p_sys->psz_aenc = NULL; p_audio->psz_aenc = NULL;
p_sys->p_audio_cfg = NULL; p_audio->p_audio_cfg = NULL;
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
{ {
char *psz_next; char *psz_next;
psz_next = config_ChainCreate( &p_sys->psz_aenc, &p_sys->p_audio_cfg, psz_next = config_ChainCreate( &p_audio->psz_aenc, &p_audio->p_audio_cfg,
val.psz_string ); val.psz_string );
free( psz_next ); free( psz_next );
} }
free( val.psz_string ); free( val.psz_string );
var_Get( p_stream, SOUT_CFG_PREFIX "acodec", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "acodec", &val );
p_sys->i_acodec = 0; p_audio->i_acodec = 0;
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
{ {
char fcc[4] = " "; char fcc[4] = " ";
memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) ); memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
p_sys->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] ); p_audio->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
} }
free( val.psz_string ); free( val.psz_string );
var_Get( p_stream, SOUT_CFG_PREFIX "ab", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "ab", &val );
p_sys->i_abitrate = val.i_int; p_audio->i_abitrate = val.i_int;
if( p_sys->i_abitrate < 4000 ) p_sys->i_abitrate *= 1000; if( p_audio->i_abitrate < 4000 )
p_audio->i_abitrate *= 1000;
var_Get( p_stream, SOUT_CFG_PREFIX "samplerate", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "samplerate", &val );
p_sys->i_sample_rate = val.i_int; p_audio->i_sample_rate = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "channels", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "channels", &val );
p_sys->i_channels = val.i_int; p_audio->i_channels = val.i_int;
if( p_sys->i_acodec ) if( p_audio->i_acodec )
{ {
if( p_sys->i_acodec == VLC_FOURCC('m','p','3',0) && if( p_audio->i_acodec == VLC_FOURCC('m','p','3',0) &&
p_sys->i_channels > 2 ) p_audio->i_channels > 2 )
{ {
msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2", msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
p_sys->i_channels ); p_audio->i_channels );
p_sys->i_channels = 2; p_audio->i_channels = 2;
} }
msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s", msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s",
(char *)&p_sys->i_acodec, p_sys->i_sample_rate, (char *)&p_audio->i_acodec, p_audio->i_sample_rate,
p_sys->i_channels, p_sys->i_abitrate / 1000 ); p_audio->i_channels, p_audio->i_abitrate / 1000 );
} }
var_Get( p_stream, SOUT_CFG_PREFIX "afilter", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "afilter", &val );
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
p_sys->psz_af2 = val.psz_string; p_audio->psz_af2 = val.psz_string;
else else
{ {
free( val.psz_string ); free( val.psz_string );
p_sys->psz_af2 = NULL; p_audio->psz_af2 = NULL;
} }
/* Video transcoding parameters */ /* Video transcoding parameters */
var_Get( p_stream, SOUT_CFG_PREFIX "venc", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "venc", &val );
p_sys->psz_venc = NULL; p_video->psz_venc = NULL;
p_sys->p_video_cfg = NULL; p_video->p_video_cfg = NULL;
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
{ {
char *psz_next; char *psz_next;
psz_next = config_ChainCreate( &p_sys->psz_venc, &p_sys->p_video_cfg, psz_next = config_ChainCreate( &p_video->psz_venc,
val.psz_string ); &p_video->p_video_cfg, val.psz_string );
free( psz_next ); free( psz_next );
} }
free( val.psz_string ); free( val.psz_string );
var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val );
p_sys->i_vcodec = 0; p_video->i_vcodec = 0;
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
{ {
char fcc[4] = " "; char fcc[4] = " ";
memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) ); memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
p_sys->i_vcodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] ); p_video->i_vcodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
} }
free( val.psz_string ); free( val.psz_string );
var_Get( p_stream, SOUT_CFG_PREFIX "vb", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "vb", &val );
p_sys->i_vbitrate = val.i_int; p_video->i_vbitrate = val.i_int;
if( p_sys->i_vbitrate < 16000 ) p_sys->i_vbitrate *= 1000; if( p_video->i_vbitrate < 16000 )
p_video->i_vbitrate *= 1000;
var_Get( p_stream, SOUT_CFG_PREFIX "scale", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "scale", &val );
p_sys->f_scale = val.f_float; p_video->f_scale = val.f_float;
var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val );
p_sys->f_fps = val.f_float; p_video->f_fps = val.f_float;
var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val );
p_sys->b_hurry_up = val.b_bool; p_sys->b_hurry_up = val.b_bool;
var_Get( p_stream, SOUT_CFG_PREFIX "width", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
p_sys->i_width = val.i_int; p_video->i_width = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "height", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
p_sys->i_height = val.i_int; p_video->i_height = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "maxwidth", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "maxwidth", &val );
p_sys->i_maxwidth = val.i_int; p_video->i_maxwidth = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "maxheight", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "maxheight", &val );
p_sys->i_maxheight = val.i_int; p_video->i_maxheight = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val );
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
p_sys->psz_vf2 = val.psz_string; p_video->psz_vf2 = val.psz_string;
else else
{ {
free( val.psz_string ); free( val.psz_string );
p_sys->psz_vf2 = NULL; p_video->psz_vf2 = NULL;
} }
var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val );
p_sys->b_deinterlace = val.b_bool; p_video->b_deinterlace = val.b_bool;
var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace-module", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace-module", &val );
p_sys->psz_deinterlace = NULL; p_video->psz_deinterlace = NULL;
p_sys->p_deinterlace_cfg = NULL; p_video->p_deinterlace_cfg = NULL;
if( val.psz_string && *val.psz_string ) if( val.psz_string && *val.psz_string )
{ {
char *psz_next; char *psz_next;
psz_next = config_ChainCreate( &p_sys->psz_deinterlace, psz_next = config_ChainCreate( &p_video->psz_deinterlace,
&p_sys->p_deinterlace_cfg, &p_video->p_deinterlace_cfg,
val.psz_string ); val.psz_string );
free( psz_next ); free( psz_next );
} }
free( val.psz_string ); free( val.psz_string );
var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
p_sys->i_threads = val.i_int; p_video->i_threads = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val );
p_sys->b_high_priority = val.b_bool; p_sys->b_high_priority = val.b_bool;
if( p_sys->i_vcodec ) if( p_video->i_vcodec )
{ {
msg_Dbg( p_stream, "codec video=%4.4s %dx%d scaling: %f %dkb/s", msg_Dbg( p_stream, "codec video=%4.4s %dx%d scaling: %f %dkb/s",
(char *)&p_sys->i_vcodec, p_sys->i_width, p_sys->i_height, (char *)&p_video->i_vcodec, p_video->i_width, p_video->i_height,
p_sys->f_scale, p_sys->i_vbitrate / 1000 ); p_video->f_scale, p_video->i_vbitrate / 1000 );
} }
/* Subpictures transcoding parameters */ /* Subpictures transcoding parameters */
...@@ -622,7 +684,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -622,7 +684,7 @@ static int Open( vlc_object_t *p_this )
/* Audio settings */ /* Audio settings */
var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
p_sys->b_master_sync = val.b_bool; p_sys->b_master_sync = val.b_bool;
if( p_sys->f_fps > 0 ) p_sys->b_master_sync = true; if( p_video->f_fps > 0 ) p_sys->b_master_sync = true;
p_stream->pf_add = Add; p_stream->pf_add = Add;
p_stream->pf_del = Del; p_stream->pf_del = Del;
...@@ -639,21 +701,23 @@ static void Close( vlc_object_t * p_this ) ...@@ -639,21 +701,23 @@ static void Close( vlc_object_t * p_this )
{ {
sout_stream_t *p_stream = (sout_stream_t*)p_this; sout_stream_t *p_stream = (sout_stream_t*)p_this;
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_audio_t *p_audio = p_sys->p_audio;
sout_stream_video_t *p_video = p_sys->p_video;
sout_StreamDelete( p_sys->p_out ); sout_StreamDelete( p_sys->p_out );
free( p_sys->psz_af2 ); free( p_audio->psz_af2 );
config_ChainDestroy( p_sys->p_audio_cfg ); config_ChainDestroy( p_audio->p_audio_cfg );
free( p_sys->psz_aenc ); free( p_audio->psz_aenc );
free( p_sys->psz_vf2 ); free( p_video->psz_vf2 );
config_ChainDestroy( p_sys->p_video_cfg ); config_ChainDestroy( p_video->p_video_cfg );
free( p_sys->psz_venc ); free( p_video->psz_venc );
config_ChainDestroy( p_sys->p_deinterlace_cfg ); config_ChainDestroy( p_video->p_deinterlace_cfg );
free( p_sys->psz_deinterlace ); free( p_video->psz_deinterlace );
config_ChainDestroy( p_sys->p_spu_cfg ); config_ChainDestroy( p_sys->p_spu_cfg );
free( p_sys->psz_senc ); free( p_sys->psz_senc );
...@@ -663,7 +727,9 @@ static void Close( vlc_object_t * p_this ) ...@@ -663,7 +727,9 @@ static void Close( vlc_object_t * p_this )
config_ChainDestroy( p_sys->p_osd_cfg ); config_ChainDestroy( p_sys->p_osd_cfg );
free( p_sys->psz_osdenc ); free( p_sys->psz_osdenc );
vlc_object_release( p_sys ); vlc_object_release( p_audio );
vlc_object_release( p_video );
free( p_sys );
} }
struct sout_stream_id_t struct sout_stream_id_t
...@@ -691,6 +757,8 @@ struct sout_stream_id_t ...@@ -691,6 +757,8 @@ struct sout_stream_id_t
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt ) static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_audio_t *p_audio = p_sys->p_audio;
sout_stream_video_t *p_video = p_sys->p_video;
sout_stream_id_t *id; sout_stream_id_t *id;
id = calloc( 1, sizeof( sout_stream_id_t ) ); id = calloc( 1, sizeof( sout_stream_id_t ) );
...@@ -724,21 +792,22 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt ) ...@@ -724,21 +792,22 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
if( p_fmt->psz_language ) if( p_fmt->psz_language )
id->p_encoder->fmt_out.psz_language = strdup( p_fmt->psz_language ); id->p_encoder->fmt_out.psz_language = strdup( p_fmt->psz_language );
if( p_fmt->i_cat == AUDIO_ES && (p_sys->i_acodec || p_sys->psz_aenc) ) if( p_fmt->i_cat == AUDIO_ES &&
(p_audio->i_acodec || p_audio->psz_aenc) )
{ {
msg_Dbg( p_stream, msg_Dbg( p_stream,
"creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'", "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
(char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec ); (char*)&p_fmt->i_codec, (char*)&p_audio->i_acodec );
/* Complete destination format */ /* Complete destination format */
id->p_encoder->fmt_out.i_codec = p_sys->i_acodec; id->p_encoder->fmt_out.i_codec = p_audio->i_acodec;
id->p_encoder->fmt_out.audio.i_rate = p_sys->i_sample_rate > 0 ? id->p_encoder->fmt_out.audio.i_rate = p_audio->i_sample_rate > 0 ?
p_sys->i_sample_rate : p_fmt->audio.i_rate; p_audio->i_sample_rate : p_fmt->audio.i_rate;
id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate; id->p_encoder->fmt_out.i_bitrate = p_audio->i_abitrate;
id->p_encoder->fmt_out.audio.i_bitspersample = id->p_encoder->fmt_out.audio.i_bitspersample =
p_fmt->audio.i_bitspersample; p_fmt->audio.i_bitspersample;
id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ? id->p_encoder->fmt_out.audio.i_channels = p_audio->i_channels > 0 ?
p_sys->i_channels : p_fmt->audio.i_channels; p_audio->i_channels : p_fmt->audio.i_channels;
/* Sanity check for audio channels */ /* Sanity check for audio channels */
id->p_encoder->fmt_out.audio.i_channels = id->p_encoder->fmt_out.audio.i_channels =
__MIN( id->p_encoder->fmt_out.audio.i_channels, __MIN( id->p_encoder->fmt_out.audio.i_channels,
...@@ -770,24 +839,24 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt ) ...@@ -770,24 +839,24 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
if( !id->id ) if( !id->id )
{ {
transcode_audio_close( id ); transcode_audio_close( p_stream, id );
goto error; goto error;
} }
date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 ); date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 );
} }
else if( p_fmt->i_cat == VIDEO_ES && else if( p_fmt->i_cat == VIDEO_ES &&
(p_sys->i_vcodec != 0 || p_sys->psz_venc) ) (p_video->i_vcodec != 0 || p_video->psz_venc) )
{ {
msg_Dbg( p_stream, msg_Dbg( p_stream,
"creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'", "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
(char*)&p_fmt->i_codec, (char*)&p_sys->i_vcodec ); (char*)&p_fmt->i_codec, (char*)&p_video->i_vcodec );
/* Complete destination format */ /* Complete destination format */
id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec; id->p_encoder->fmt_out.i_codec = p_video->i_vcodec;
id->p_encoder->fmt_out.video.i_width = p_sys->i_width & ~1; id->p_encoder->fmt_out.video.i_width = p_video->i_width & ~1;
id->p_encoder->fmt_out.video.i_height = p_sys->i_height & ~1; id->p_encoder->fmt_out.video.i_height = p_video->i_height & ~1;
id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate; id->p_encoder->fmt_out.i_bitrate = p_video->i_vbitrate;
/* Build decoder -> filter -> encoder chain */ /* Build decoder -> filter -> encoder chain */
if( transcode_video_new( p_stream, id ) ) if( transcode_video_new( p_stream, id ) )
...@@ -800,10 +869,10 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt ) ...@@ -800,10 +869,10 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
* all the characteristics of the decoded stream yet */ * all the characteristics of the decoded stream yet */
id->b_transcode = true; id->b_transcode = true;
if( p_sys->f_fps > 0 ) if( p_video->f_fps > 0 )
{ {
id->p_encoder->fmt_out.video.i_frame_rate = id->p_encoder->fmt_out.video.i_frame_rate =
(p_sys->f_fps * 1000) + 0.5; (p_video->f_fps * 1000) + 0.5;
id->p_encoder->fmt_out.video.i_frame_rate_base = id->p_encoder->fmt_out.video.i_frame_rate_base =
ENC_FRAMERATE_BASE; ENC_FRAMERATE_BASE;
} }
...@@ -909,7 +978,7 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id ) ...@@ -909,7 +978,7 @@ static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
switch( id->p_decoder->fmt_in.i_cat ) switch( id->p_decoder->fmt_in.i_cat )
{ {
case AUDIO_ES: case AUDIO_ES:
transcode_audio_close( id ); transcode_audio_close( p_stream, id );
break; break;
case VIDEO_ES: case VIDEO_ES:
transcode_video_close( p_stream, id ); transcode_video_close( p_stream, id );
...@@ -1064,6 +1133,7 @@ static bool transcode_audio_filter_needed( const es_format_t *p_fmt1, const es_f ...@@ -1064,6 +1133,7 @@ static bool transcode_audio_filter_needed( const es_format_t *p_fmt1, const es_f
return true; return true;
return false; return false;
} }
static int transcode_audio_filter_chain_build( sout_stream_t *p_stream, filter_chain_t *p_chain, static int transcode_audio_filter_chain_build( sout_stream_t *p_stream, filter_chain_t *p_chain,
const es_format_t *p_dst, const es_format_t *p_src ) const es_format_t *p_dst, const es_format_t *p_src )
{ {
...@@ -1159,16 +1229,17 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1159,16 +1229,17 @@ static int transcode_audio_new( sout_stream_t *p_stream,
sout_stream_id_t *id ) sout_stream_id_t *id )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_audio_t *p_audio = p_sys->p_audio;
es_format_t fmt_last; es_format_t fmt_last;
int i_priority;
/* /*
* Open decoder * Open decoder
*/ */
/* Initialization of decoder structures */ /* Initialization of decoder structures */
id->p_decoder->fmt_out = id->p_decoder->fmt_in; id->p_decoder->fmt_out = id->p_decoder->fmt_in;
id->p_decoder->fmt_out.i_extra = 0; id->p_decoder->fmt_out.i_extra = 0;
id->p_decoder->fmt_out.p_extra = 0; id->p_decoder->fmt_out.p_extra = NULL;
id->p_decoder->pf_decode_audio = NULL; id->p_decoder->pf_decode_audio = NULL;
id->p_decoder->pf_aout_buffer_new = audio_new_buffer; id->p_decoder->pf_aout_buffer_new = audio_new_buffer;
id->p_decoder->pf_aout_buffer_del = audio_del_buffer; id->p_decoder->pf_aout_buffer_del = audio_del_buffer;
...@@ -1183,7 +1254,9 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1183,7 +1254,9 @@ static int transcode_audio_new( sout_stream_t *p_stream,
} }
id->p_decoder->fmt_out.audio.i_bitspersample = id->p_decoder->fmt_out.audio.i_bitspersample =
aout_BitsPerSample( id->p_decoder->fmt_out.i_codec ); aout_BitsPerSample( id->p_decoder->fmt_out.i_codec );
fmt_last = id->p_decoder->fmt_out; fmt_last = id->p_decoder->fmt_out;
/* Fix AAC SBR changing number of channels and sampling rate */ /* Fix AAC SBR changing number of channels and sampling rate */
if( !(id->p_decoder->fmt_in.i_codec == VLC_FOURCC('m','p','4','a') && if( !(id->p_decoder->fmt_in.i_codec == VLC_FOURCC('m','p','4','a') &&
fmt_last.audio.i_rate != id->p_encoder->fmt_in.audio.i_rate && fmt_last.audio.i_rate != id->p_encoder->fmt_in.audio.i_rate &&
...@@ -1193,7 +1266,6 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1193,7 +1266,6 @@ static int transcode_audio_new( sout_stream_t *p_stream,
/* /*
* Open encoder * Open encoder
*/ */
/* Initialization of encoder format structures */ /* Initialization of encoder format structures */
es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat, es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat,
id->p_decoder->fmt_out.i_codec ); id->p_decoder->fmt_out.i_codec );
...@@ -1209,24 +1281,48 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1209,24 +1281,48 @@ static int transcode_audio_new( sout_stream_t *p_stream,
id->p_encoder->fmt_in.audio.i_bitspersample = id->p_encoder->fmt_in.audio.i_bitspersample =
aout_BitsPerSample( id->p_encoder->fmt_in.i_codec ); aout_BitsPerSample( id->p_encoder->fmt_in.i_codec );
id->p_encoder->p_cfg = p_stream->p_sys->p_audio_cfg; id->p_encoder->p_cfg = p_audio->p_audio_cfg;
id->p_encoder->p_module = id->p_encoder->p_module =
module_need( id->p_encoder, "encoder", p_sys->psz_aenc, true ); module_need( id->p_encoder, "encoder", p_audio->psz_aenc, true );
if( !id->p_encoder->p_module ) if( !id->p_encoder->p_module )
{ {
msg_Err( p_stream, "cannot find audio encoder (module:%s fourcc:%4.4s)", msg_Err( p_stream, "cannot find audio encoder (module:%s fourcc:%4.4s)",
p_sys->psz_aenc ? p_sys->psz_aenc : "any", p_audio->psz_aenc ? p_audio->psz_aenc : "any",
(char *)&p_sys->i_acodec ); (char *)&p_audio->i_acodec );
module_unneed( id->p_decoder, id->p_decoder->p_module );
id->p_decoder->p_module = NULL;
return VLC_EGENERIC; return VLC_EGENERIC;
} }
id->p_encoder->fmt_in.audio.i_format = id->p_encoder->fmt_in.i_codec; id->p_encoder->fmt_in.audio.i_format = id->p_encoder->fmt_in.i_codec;
id->p_encoder->fmt_in.audio.i_bitspersample = id->p_encoder->fmt_in.audio.i_bitspersample =
aout_BitsPerSample( id->p_encoder->fmt_in.i_codec ); aout_BitsPerSample( id->p_encoder->fmt_in.i_codec );
i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT :
VLC_THREAD_PRIORITY_AUDIO;
p_audio->id = id;
p_audio->b_die = p_audio->b_error = 0;
p_audio->fifo_in = block_FifoNew();
p_audio->fifo_out = block_FifoNew();
if( !p_audio->fifo_in || !p_audio->fifo_out )
{
module_unneed( id->p_decoder, id->p_decoder->p_module );
id->p_decoder->p_module = NULL;
free( id->p_decoder->p_owner );
if( p_audio->fifo_in )
block_FifoRelease( p_audio->fifo_in );
return VLC_ENOMEM;
}
if( vlc_thread_create( p_audio, "audio", AudioThread, i_priority ) )
{
msg_Err( p_stream, "cannot spawn encoder thread" );
module_unneed( id->p_decoder, id->p_decoder->p_module );
id->p_decoder->p_module = NULL;
free( id->p_decoder->p_owner );
block_FifoRelease( p_audio->fifo_in );
block_FifoRelease( p_audio->fifo_out );
return VLC_EGENERIC;
}
/* Load user specified audio filters */ /* Load user specified audio filters */
if( p_sys->psz_af2 ) if( p_audio->psz_af2 )
{ {
es_format_t fmt_fl32 = fmt_last; es_format_t fmt_fl32 = fmt_last;
fmt_fl32.i_codec = fmt_fl32.i_codec =
...@@ -1235,7 +1331,11 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1235,7 +1331,11 @@ static int transcode_audio_new( sout_stream_t *p_stream,
if( transcode_audio_filter_chain_build( p_stream, id->p_uf_chain, if( transcode_audio_filter_chain_build( p_stream, id->p_uf_chain,
&fmt_fl32, &fmt_last ) ) &fmt_fl32, &fmt_last ) )
{ {
transcode_audio_close( id ); vlc_object_kill( p_audio );
block_FifoWake( p_audio->fifo_in );
block_FifoWake( p_audio->fifo_out );
vlc_thread_join( p_audio );
transcode_audio_close( p_stream, id );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
fmt_last = fmt_fl32; fmt_last = fmt_fl32;
...@@ -1243,7 +1343,7 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1243,7 +1343,7 @@ static int transcode_audio_new( sout_stream_t *p_stream,
id->p_uf_chain = filter_chain_New( p_stream, "audio filter2", false, id->p_uf_chain = filter_chain_New( p_stream, "audio filter2", false,
transcode_audio_filter_allocation_init, NULL, NULL ); transcode_audio_filter_allocation_init, NULL, NULL );
filter_chain_Reset( id->p_uf_chain, &fmt_last, &fmt_fl32 ); filter_chain_Reset( id->p_uf_chain, &fmt_last, &fmt_fl32 );
if( filter_chain_AppendFromString( id->p_uf_chain, p_sys->psz_af2 ) > 0 ) if( filter_chain_AppendFromString( id->p_uf_chain, p_audio->psz_af2 ) > 0 )
fmt_last = *filter_chain_GetFmtOut( id->p_uf_chain ); fmt_last = *filter_chain_GetFmtOut( id->p_uf_chain );
} }
...@@ -1255,7 +1355,7 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1255,7 +1355,7 @@ static int transcode_audio_new( sout_stream_t *p_stream,
if( transcode_audio_filter_chain_build( p_stream, id->p_f_chain, if( transcode_audio_filter_chain_build( p_stream, id->p_f_chain,
&id->p_encoder->fmt_in, &fmt_last ) ) &id->p_encoder->fmt_in, &fmt_last ) )
{ {
transcode_audio_close( id ); transcode_audio_close( p_stream, id );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
fmt_last = id->p_encoder->fmt_in; fmt_last = id->p_encoder->fmt_in;
...@@ -1267,10 +1367,17 @@ static int transcode_audio_new( sout_stream_t *p_stream, ...@@ -1267,10 +1367,17 @@ static int transcode_audio_new( sout_stream_t *p_stream,
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static void transcode_audio_close( sout_stream_id_t *id ) static void transcode_audio_close( sout_stream_t *p_stream, sout_stream_id_t *id )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys;
audio_timer_close( id->p_encoder ); audio_timer_close( id->p_encoder );
vlc_object_kill( p_sys->p_audio );
block_FifoWake( p_sys->p_audio->fifo_in );
block_FifoWake( p_sys->p_audio->fifo_out );
vlc_thread_join( p_sys->p_audio );
/* Close decoder */ /* Close decoder */
if( id->p_decoder->p_module ) if( id->p_decoder->p_module )
module_unneed( id->p_decoder, id->p_decoder->p_module ); module_unneed( id->p_decoder, id->p_decoder->p_module );
...@@ -1290,6 +1397,10 @@ static void transcode_audio_close( sout_stream_id_t *id ) ...@@ -1290,6 +1397,10 @@ static void transcode_audio_close( sout_stream_id_t *id )
filter_chain_Delete( id->p_uf_chain ); filter_chain_Delete( id->p_uf_chain );
if( id->p_f_chain ) if( id->p_f_chain )
filter_chain_Delete( id->p_f_chain ); filter_chain_Delete( id->p_f_chain );
/* Release fifo's */
block_FifoRelease( p_sys->p_audio->fifo_in );
block_FifoRelease( p_sys->p_audio->fifo_out );
} }
static int transcode_audio_process( sout_stream_t *p_stream, static int transcode_audio_process( sout_stream_t *p_stream,
...@@ -1297,64 +1408,106 @@ static int transcode_audio_process( sout_stream_t *p_stream, ...@@ -1297,64 +1408,106 @@ static int transcode_audio_process( sout_stream_t *p_stream,
block_t *in, block_t **out ) block_t *in, block_t **out )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
aout_buffer_t *p_audio_buf; VLC_UNUSED(id);
block_t *p_block, *p_audio_block;
*out = NULL; *out = NULL;
while( (p_audio_buf = id->p_decoder->pf_decode_audio( id->p_decoder, block_FifoPut( p_sys->p_audio->fifo_in, in );
&in )) ) while( block_FifoCount( p_sys->p_audio->fifo_out ) > 0 )
{ {
sout_UpdateStatistic( p_stream->p_sout, SOUT_STATISTIC_DECODED_AUDIO, 1 ); block_t *p_block = block_FifoGet( p_sys->p_audio->fifo_out );
if( p_sys->b_master_sync ) if( !p_block )
break;
block_ChainAppend( out, p_block );
}
return VLC_SUCCESS;
}
static void* AudioThread( vlc_object_t* p_this )
{
sout_stream_audio_t *p_audio = (sout_stream_audio_t*)p_this;
sout_stream_sys_t *p_sys = p_audio->p_sys;
sout_stream_t *p_stream = p_sys->p_out;
sout_stream_id_t *id = p_audio->id;
int canc = vlc_savecancel ();
while( vlc_object_alive (p_audio) && !p_audio->b_error )
{
aout_buffer_t *p_audio_buf;
block_t *p_block, *p_audio_block;
block_t *in;
do {
in = block_FifoGet( p_audio->fifo_in );
if( !vlc_object_alive (p_audio) || p_audio->b_error )
goto cleanup;
} while( !in );
while( (p_audio_buf = id->p_decoder->pf_decode_audio( id->p_decoder, &in )) )
{ {
mtime_t i_dts = date_Get( &id->interpolated_pts ) + 1; if( !vlc_object_alive (p_audio) || p_audio->b_error )
if ( p_audio_buf->start_date - i_dts > MASTER_SYNC_MAX_DRIFT
|| p_audio_buf->start_date - i_dts < -MASTER_SYNC_MAX_DRIFT )
{ {
msg_Dbg( p_stream, "drift is too high, resetting master sync" ); free( p_audio_buf );
date_Set( &id->interpolated_pts, p_audio_buf->start_date ); goto cleanup;
i_dts = p_audio_buf->start_date + 1; }
sout_UpdateStatistic( p_stream->p_sout, SOUT_STATISTIC_DECODED_AUDIO, 1 );
if( p_sys->b_master_sync )
{
mtime_t i_dts = date_Get( &id->interpolated_pts ) + 1;
if ( p_audio_buf->start_date - i_dts > MASTER_SYNC_MAX_DRIFT
|| p_audio_buf->start_date - i_dts < -MASTER_SYNC_MAX_DRIFT )
{
msg_Dbg( p_this, "drift is too high, resetting master sync" );
date_Set( &id->interpolated_pts, p_audio_buf->start_date );
i_dts = p_audio_buf->start_date + 1;
}
p_sys->i_master_drift = p_audio_buf->start_date - i_dts;
date_Increment( &id->interpolated_pts, p_audio_buf->i_nb_samples );
p_audio_buf->start_date -= p_sys->i_master_drift;
p_audio_buf->end_date -= p_sys->i_master_drift;
} }
p_sys->i_master_drift = p_audio_buf->start_date - i_dts;
date_Increment( &id->interpolated_pts, p_audio_buf->i_nb_samples );
p_audio_buf->start_date -= p_sys->i_master_drift;
p_audio_buf->end_date -= p_sys->i_master_drift;
}
p_audio_block = p_audio_buf->p_sys; p_audio_block = p_audio_buf->p_sys;
p_audio_block->i_buffer = p_audio_buf->i_nb_bytes; p_audio_block->i_buffer = p_audio_buf->i_nb_bytes;
p_audio_block->i_dts = p_audio_block->i_pts = p_audio_block->i_dts = p_audio_block->i_pts =
p_audio_buf->start_date; p_audio_buf->start_date;
p_audio_block->i_length = p_audio_buf->end_date - p_audio_block->i_length = p_audio_buf->end_date -
p_audio_buf->start_date; p_audio_buf->start_date;
p_audio_block->i_samples = p_audio_buf->i_nb_samples; p_audio_block->i_samples = p_audio_buf->i_nb_samples;
/* Run filter chain */ /* Run filter chain */
if( id->p_uf_chain ) if( id->p_uf_chain )
{ {
p_audio_block = filter_chain_AudioFilter( id->p_uf_chain, p_audio_block ); p_audio_block = filter_chain_AudioFilter( id->p_uf_chain, p_audio_block );
assert( p_audio_block ); assert( p_audio_block );
} }
p_audio_block = filter_chain_AudioFilter( id->p_f_chain, p_audio_block ); p_audio_block = filter_chain_AudioFilter( id->p_f_chain, p_audio_block );
assert( p_audio_block ); assert( p_audio_block );
p_audio_buf->p_buffer = p_audio_block->p_buffer; p_audio_buf->p_buffer = p_audio_block->p_buffer;
p_audio_buf->i_nb_bytes = p_audio_block->i_buffer; p_audio_buf->i_nb_bytes = p_audio_block->i_buffer;
p_audio_buf->i_nb_samples = p_audio_block->i_samples; p_audio_buf->i_nb_samples = p_audio_block->i_samples;
p_audio_buf->start_date = p_audio_block->i_dts; p_audio_buf->start_date = p_audio_block->i_dts;
p_audio_buf->end_date = p_audio_block->i_dts + p_audio_block->i_length; p_audio_buf->end_date = p_audio_block->i_dts + p_audio_block->i_length;
audio_timer_start( id->p_encoder ); audio_timer_start( id->p_encoder );
p_block = id->p_encoder->pf_encode_audio( id->p_encoder, p_audio_buf ); p_block = id->p_encoder->pf_encode_audio( id->p_encoder, p_audio_buf );
audio_timer_stop( id->p_encoder ); audio_timer_stop( id->p_encoder );
block_ChainAppend( out, p_block ); block_FifoPut( p_audio->fifo_out, p_block );
block_Release( p_audio_block ); block_Release( p_audio_block );
free( p_audio_buf ); free( p_audio_buf );
}
} }
return VLC_SUCCESS; cleanup:
block_FifoEmpty( p_audio->fifo_in );
block_FifoEmpty( p_audio->fifo_out );
vlc_restorecancel (canc);
return NULL;
} }
static void audio_release_buffer( aout_buffer_t *p_buffer ) static void audio_release_buffer( aout_buffer_t *p_buffer )
...@@ -1371,7 +1524,7 @@ static aout_buffer_t *audio_new_buffer( decoder_t *p_dec, int i_samples ) ...@@ -1371,7 +1524,7 @@ static aout_buffer_t *audio_new_buffer( decoder_t *p_dec, int i_samples )
if( p_dec->fmt_out.audio.i_bitspersample ) if( p_dec->fmt_out.audio.i_bitspersample )
{ {
i_size = i_samples * p_dec->fmt_out.audio.i_bitspersample / 8 * i_size = ((i_samples * p_dec->fmt_out.audio.i_bitspersample) >> 3) *
p_dec->fmt_out.audio.i_channels; p_dec->fmt_out.audio.i_channels;
} }
else if( p_dec->fmt_out.audio.i_bytes_per_frame && else if( p_dec->fmt_out.audio.i_bytes_per_frame &&
...@@ -1390,7 +1543,11 @@ static aout_buffer_t *audio_new_buffer( decoder_t *p_dec, int i_samples ) ...@@ -1390,7 +1543,11 @@ static aout_buffer_t *audio_new_buffer( decoder_t *p_dec, int i_samples )
p_buffer->b_discontinuity = false; p_buffer->b_discontinuity = false;
p_buffer->pf_release = audio_release_buffer; p_buffer->pf_release = audio_release_buffer;
p_buffer->p_sys = p_block = block_New( p_dec, i_size ); p_buffer->p_sys = p_block = block_New( p_dec, i_size );
if( !p_block )
{
free( p_buffer );
return NULL;
}
p_buffer->p_buffer = p_block->p_buffer; p_buffer->p_buffer = p_block->p_buffer;
p_buffer->i_size = p_buffer->i_nb_bytes = p_block->i_buffer; p_buffer->i_size = p_buffer->i_nb_bytes = p_block->i_buffer;
p_buffer->i_nb_samples = i_samples; p_buffer->i_nb_samples = i_samples;
...@@ -1447,6 +1604,7 @@ static void transcode_video_filter_allocation_clear( filter_t *p_filter ) ...@@ -1447,6 +1604,7 @@ static void transcode_video_filter_allocation_clear( filter_t *p_filter )
static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_video_t *p_video = p_sys->p_video;
int i; int i;
/* Open decoder /* Open decoder
...@@ -1469,7 +1627,7 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) ...@@ -1469,7 +1627,7 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
for( i = 0; i < PICTURE_RING_SIZE; i++ ) for( i = 0; i < PICTURE_RING_SIZE; i++ )
id->p_decoder->p_owner->pp_pics[i] = NULL; id->p_decoder->p_owner->pp_pics[i] = NULL;
id->p_decoder->p_owner->p_sys = p_sys; id->p_decoder->p_owner->p_sys = p_sys;
/* id->p_decoder->p_cfg = p_sys->p_video_cfg; */ /* id->p_decoder->p_cfg = p_video->p_video_cfg; */
id->p_decoder->p_module = id->p_decoder->p_module =
module_need( id->p_decoder, "decoder", "$codec", false ); module_need( id->p_decoder, "decoder", "$codec", false );
...@@ -1508,18 +1666,18 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) ...@@ -1508,18 +1666,18 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
id->p_encoder->fmt_in.video.i_frame_rate = ENC_FRAMERATE; id->p_encoder->fmt_in.video.i_frame_rate = ENC_FRAMERATE;
id->p_encoder->fmt_in.video.i_frame_rate_base = ENC_FRAMERATE_BASE; id->p_encoder->fmt_in.video.i_frame_rate_base = ENC_FRAMERATE_BASE;
id->p_encoder->i_threads = p_sys->i_threads; id->p_encoder->i_threads = p_video->i_threads;
id->p_encoder->p_cfg = p_sys->p_video_cfg; id->p_encoder->p_cfg = p_video->p_video_cfg;
id->p_encoder->p_module = id->p_encoder->p_module =
module_need( id->p_encoder, "encoder", p_sys->psz_venc, true ); module_need( id->p_encoder, "encoder", p_video->psz_venc, true );
if( !id->p_encoder->p_module ) if( !id->p_encoder->p_module )
{ {
msg_Err( p_stream, "cannot find video encoder (module:%s fourcc:%4.4s)", msg_Err( p_stream, "cannot find video encoder (module:%s fourcc:%4.4s)",
p_sys->psz_venc ? p_sys->psz_venc : "any", p_video->psz_venc ? p_video->psz_venc : "any",
(char *)&p_sys->i_vcodec ); (char *)&p_video->i_vcodec );
module_unneed( id->p_decoder, id->p_decoder->p_module ); module_unneed( id->p_decoder, id->p_decoder->p_module );
id->p_decoder->p_module = 0; id->p_decoder->p_module = NULL;
free( id->p_decoder->p_owner ); free( id->p_decoder->p_owner );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -1535,23 +1693,23 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) ...@@ -1535,23 +1693,23 @@ static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
} }
id->p_encoder->p_module = NULL; id->p_encoder->p_module = NULL;
if( p_sys->i_threads >= 1 ) if( p_video->i_threads >= 1 )
{ {
int i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT : int i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT :
VLC_THREAD_PRIORITY_VIDEO; VLC_THREAD_PRIORITY_VIDEO;
p_sys->id_video = id; p_video->id = id;
vlc_mutex_init( &p_sys->lock_out ); vlc_mutex_init( &p_video->lock_out );
vlc_cond_init( &p_sys->cond ); vlc_cond_init( &p_video->cond );
memset( p_sys->pp_pics, 0, sizeof(p_sys->pp_pics) ); memset( p_video->pp_pics, 0, sizeof(p_video->pp_pics) );
p_sys->i_first_pic = 0; p_video->i_first_pic = 0;
p_sys->i_last_pic = 0; p_video->i_last_pic = 0;
p_sys->p_buffers = NULL; p_video->p_buffers = NULL;
p_sys->b_die = p_sys->b_error = 0; p_video->b_die = p_video->b_error = 0;
if( vlc_thread_create( p_sys, "encoder", EncoderThread, i_priority ) ) if( vlc_thread_create( p_video, "video encoder", VideoEncoderThread, i_priority ) )
{ {
msg_Err( p_stream, "cannot spawn encoder thread" ); msg_Err( p_stream, "cannot spawn encoder thread" );
module_unneed( id->p_decoder, id->p_decoder->p_module ); module_unneed( id->p_decoder, id->p_decoder->p_module );
id->p_decoder->p_module = 0; id->p_decoder->p_module = NULL;
free( id->p_decoder->p_owner ); free( id->p_decoder->p_owner );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -1564,6 +1722,7 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream, ...@@ -1564,6 +1722,7 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream,
sout_stream_id_t *id ) sout_stream_id_t *id )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_video_t *p_video = p_sys->p_video;
/* Calculate scaling /* Calculate scaling
* width/height of source */ * width/height of source */
...@@ -1591,12 +1750,12 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream, ...@@ -1591,12 +1750,12 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream,
/* Calculate scaling factor for specified parameters */ /* Calculate scaling factor for specified parameters */
if( id->p_encoder->fmt_out.video.i_width <= 0 && if( id->p_encoder->fmt_out.video.i_width <= 0 &&
id->p_encoder->fmt_out.video.i_height <= 0 && p_sys->f_scale ) id->p_encoder->fmt_out.video.i_height <= 0 && p_video->f_scale )
{ {
/* Global scaling. Make sure width will remain a factor of 16 */ /* Global scaling. Make sure width will remain a factor of 16 */
float f_real_scale; float f_real_scale;
int i_new_height; int i_new_height;
int i_new_width = i_src_width * p_sys->f_scale; int i_new_width = i_src_width * p_video->f_scale;
if( i_new_width % 16 <= 7 && i_new_width >= 16 ) if( i_new_width % 16 <= 7 && i_new_width >= 16 )
i_new_width -= i_new_width % 16; i_new_width -= i_new_width % 16;
...@@ -1634,16 +1793,16 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream, ...@@ -1634,16 +1793,16 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream,
/* check maxwidth and maxheight /* check maxwidth and maxheight
*/ */
if( p_sys->i_maxwidth && f_scale_width > (float)p_sys->i_maxwidth / if( p_video->i_maxwidth && f_scale_width >
i_src_width ) (float)p_video->i_maxwidth / i_src_width )
{ {
f_scale_width = (float)p_sys->i_maxwidth / i_src_width; f_scale_width = (float)p_video->i_maxwidth / i_src_width;
} }
if( p_sys->i_maxheight && f_scale_height > (float)p_sys->i_maxheight / if( p_video->i_maxheight && f_scale_height >
i_src_height ) (float)p_video->i_maxheight / i_src_height )
{ {
f_scale_height = (float)p_sys->i_maxheight / i_src_height; f_scale_height = (float)p_video->i_maxheight / i_src_height;
} }
/* Change aspect ratio from source pixel to scaled pixel */ /* Change aspect ratio from source pixel to scaled pixel */
...@@ -1724,19 +1883,19 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, ...@@ -1724,19 +1883,19 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream,
sout_stream_id_t *id ) sout_stream_id_t *id )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_video_t *p_video = p_sys->p_video;
msg_Dbg( p_stream, "destination (after video filters) %ix%i", msg_Dbg( p_stream, "destination (after video filters) %ix%i",
id->p_encoder->fmt_in.video.i_width, id->p_encoder->fmt_in.video.i_width,
id->p_encoder->fmt_in.video.i_height ); id->p_encoder->fmt_in.video.i_height );
id->p_encoder->p_module = id->p_encoder->p_module =
module_need( id->p_encoder, "encoder", p_sys->psz_venc, true ); module_need( id->p_encoder, "encoder", p_video->psz_venc, true );
if( !id->p_encoder->p_module ) if( !id->p_encoder->p_module )
{ {
msg_Err( p_stream, "cannot find video encoder (module:%s fourcc:%4.4s)", msg_Err( p_stream, "cannot find video encoder (module:%s fourcc:%4.4s)",
p_sys->psz_venc ? p_sys->psz_venc : "any", p_video->psz_venc ? p_video->psz_venc : "any",
(char *)&p_sys->i_vcodec ); (char *)&p_video->i_vcodec );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -1749,7 +1908,7 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, ...@@ -1749,7 +1908,7 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream,
id->p_encoder->fmt_out.i_codec = VLC_FOURCC('m','p','g','v'); id->p_encoder->fmt_out.i_codec = VLC_FOURCC('m','p','g','v');
} }
id->id = sout_StreamIdAdd( p_stream->p_sys->p_out, id->id = sout_StreamIdAdd( p_sys->p_out,
&id->p_encoder->fmt_out ); &id->p_encoder->fmt_out );
if( !id->id ) if( !id->id )
{ {
...@@ -1763,17 +1922,19 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, ...@@ -1763,17 +1922,19 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream,
static void transcode_video_close( sout_stream_t *p_stream, static void transcode_video_close( sout_stream_t *p_stream,
sout_stream_id_t *id ) sout_stream_id_t *id )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_video_t *p_video = p_sys->p_video;
int i; int i;
if( p_stream->p_sys->i_threads >= 1 ) if( p_video->i_threads >= 1 )
{ {
vlc_mutex_lock( &p_stream->p_sys->lock_out ); vlc_mutex_lock( &p_video->lock_out );
vlc_object_kill( p_stream->p_sys ); vlc_object_kill( p_video );
vlc_cond_signal( &p_stream->p_sys->cond ); vlc_cond_signal( &p_video->cond );
vlc_mutex_unlock( &p_stream->p_sys->lock_out ); vlc_mutex_unlock( &p_video->lock_out );
vlc_thread_join( p_stream->p_sys ); vlc_thread_join( p_video );
vlc_mutex_destroy( &p_stream->p_sys->lock_out ); vlc_mutex_destroy( &p_video->lock_out );
vlc_cond_destroy( &p_stream->p_sys->cond ); vlc_cond_destroy( &p_video->cond );
} }
video_timer_close( id->p_encoder ); video_timer_close( id->p_encoder );
...@@ -1812,6 +1973,7 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -1812,6 +1973,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
block_t *in, block_t **out ) block_t *in, block_t **out )
{ {
sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_sys_t *p_sys = p_stream->p_sys;
sout_stream_video_t *p_video = p_sys->p_video;
int i_duplicate = 1; int i_duplicate = 1;
picture_t *p_pic, *p_pic2 = NULL; picture_t *p_pic, *p_pic2 = NULL;
*out = NULL; *out = NULL;
...@@ -1884,11 +2046,11 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -1884,11 +2046,11 @@ static int transcode_video_process( sout_stream_t *p_stream,
p_stream->p_sys ); p_stream->p_sys );
/* Deinterlace */ /* Deinterlace */
if( p_stream->p_sys->b_deinterlace ) if( p_video->b_deinterlace )
{ {
filter_chain_AppendFilter( id->p_f_chain, filter_chain_AppendFilter( id->p_f_chain,
p_sys->psz_deinterlace, p_video->psz_deinterlace,
p_sys->p_deinterlace_cfg, p_video->p_deinterlace_cfg,
&id->p_decoder->fmt_out, &id->p_decoder->fmt_out,
&id->p_decoder->fmt_out ); &id->p_decoder->fmt_out );
} }
...@@ -1907,7 +2069,7 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -1907,7 +2069,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
&id->p_encoder->fmt_in ); &id->p_encoder->fmt_in );
} }
if( p_sys->psz_vf2 ) if( p_video->psz_vf2 )
{ {
const es_format_t *p_fmt_out; const es_format_t *p_fmt_out;
id->p_uf_chain = filter_chain_New( p_stream, "video filter2", id->p_uf_chain = filter_chain_New( p_stream, "video filter2",
...@@ -1917,7 +2079,7 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -1917,7 +2079,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
p_stream->p_sys ); p_stream->p_sys );
filter_chain_Reset( id->p_uf_chain, &id->p_encoder->fmt_in, filter_chain_Reset( id->p_uf_chain, &id->p_encoder->fmt_in,
&id->p_encoder->fmt_in ); &id->p_encoder->fmt_in );
filter_chain_AppendFromString( id->p_uf_chain, p_sys->psz_vf2 ); filter_chain_AppendFromString( id->p_uf_chain, p_video->psz_vf2 );
p_fmt_out = filter_chain_GetFmtOut( id->p_uf_chain ); p_fmt_out = filter_chain_GetFmtOut( id->p_uf_chain );
es_format_Copy( &id->p_encoder->fmt_in, p_fmt_out ); es_format_Copy( &id->p_encoder->fmt_in, p_fmt_out );
id->p_encoder->fmt_out.video.i_width = id->p_encoder->fmt_out.video.i_width =
...@@ -1987,7 +2149,7 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -1987,7 +2149,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
if( id->p_uf_chain ) if( id->p_uf_chain )
p_pic = filter_chain_VideoFilter( id->p_uf_chain, p_pic ); p_pic = filter_chain_VideoFilter( id->p_uf_chain, p_pic );
if( p_sys->i_threads == 0 ) if( p_video->i_threads == 0 )
{ {
block_t *p_block; block_t *p_block;
...@@ -2023,7 +2185,7 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -2023,7 +2185,7 @@ static int transcode_video_process( sout_stream_t *p_stream,
} }
date_Increment( &id->interpolated_pts, 1 ); date_Increment( &id->interpolated_pts, 1 );
if( p_sys->i_threads >= 1 ) if( p_video->i_threads >= 1 )
{ {
/* We can't modify the picture, we need to duplicate it */ /* We can't modify the picture, we need to duplicate it */
p_pic2 = video_new_buffer_decoder( id->p_decoder ); p_pic2 = video_new_buffer_decoder( id->p_decoder );
...@@ -2038,81 +2200,81 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -2038,81 +2200,81 @@ static int transcode_video_process( sout_stream_t *p_stream,
block_t *p_block; block_t *p_block;
p_pic->date = i_pts; p_pic->date = i_pts;
video_timer_start( id->p_encoder ); video_timer_start( id->p_encoder );
p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic); p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
video_timer_stop( id->p_encoder ); video_timer_stop( id->p_encoder );
block_ChainAppend( out, p_block ); block_ChainAppend( out, p_block );
} }
} }
if( p_sys->i_threads == 0 ) if( p_video->i_threads == 0 )
{ {
p_pic->pf_release( p_pic ); p_pic->pf_release( p_pic );
} }
else else
{ {
vlc_mutex_lock( &p_sys->lock_out ); vlc_mutex_lock( &p_video->lock_out );
p_sys->pp_pics[p_sys->i_last_pic++] = p_pic; p_video->pp_pics[p_video->i_last_pic++] = p_pic;
p_sys->i_last_pic %= PICTURE_RING_SIZE; p_video->i_last_pic %= PICTURE_RING_SIZE;
*out = p_sys->p_buffers; *out = p_video->p_buffers;
p_sys->p_buffers = NULL; p_video->p_buffers = NULL;
if( p_pic2 != NULL ) if( p_pic2 != NULL )
{ {
p_sys->pp_pics[p_sys->i_last_pic++] = p_pic2; p_video->pp_pics[p_video->i_last_pic++] = p_pic2;
p_sys->i_last_pic %= PICTURE_RING_SIZE; p_video->i_last_pic %= PICTURE_RING_SIZE;
} }
vlc_cond_signal( &p_sys->cond ); vlc_cond_signal( &p_video->cond );
vlc_mutex_unlock( &p_sys->lock_out ); vlc_mutex_unlock( &p_video->lock_out );
} }
} }
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static void* EncoderThread( vlc_object_t* p_this ) static void* VideoEncoderThread( vlc_object_t* p_this )
{ {
sout_stream_sys_t *p_sys = (sout_stream_sys_t*)p_this; sout_stream_video_t *p_video = (sout_stream_video_t*)p_this;
sout_stream_id_t *id = p_sys->id_video; sout_stream_id_t *id = p_video->id;
picture_t *p_pic; picture_t *p_pic;
int canc = vlc_savecancel (); int canc = vlc_savecancel ();
while( vlc_object_alive (p_sys) && !p_sys->b_error ) while( vlc_object_alive (p_video) && !p_video->b_error )
{ {
block_t *p_block; block_t *p_block;
vlc_mutex_lock( &p_sys->lock_out ); vlc_mutex_lock( &p_video->lock_out );
while( p_sys->i_last_pic == p_sys->i_first_pic ) while( p_video->i_last_pic == p_video->i_first_pic )
{ {
vlc_cond_wait( &p_sys->cond, &p_sys->lock_out ); vlc_cond_wait( &p_video->cond, &p_video->lock_out );
if( !vlc_object_alive (p_sys) || p_sys->b_error ) break; if( !vlc_object_alive (p_video) || p_video->b_error ) break;
} }
if( !vlc_object_alive (p_sys) || p_sys->b_error ) if( !vlc_object_alive (p_video) || p_video->b_error )
{ {
vlc_mutex_unlock( &p_sys->lock_out ); vlc_mutex_unlock( &p_video->lock_out );
break; break;
} }
p_pic = p_sys->pp_pics[p_sys->i_first_pic++]; p_pic = p_video->pp_pics[p_video->i_first_pic++];
p_sys->i_first_pic %= PICTURE_RING_SIZE; p_video->i_first_pic %= PICTURE_RING_SIZE;
vlc_mutex_unlock( &p_sys->lock_out ); vlc_mutex_unlock( &p_video->lock_out );
video_timer_start( id->p_encoder ); video_timer_start( id->p_encoder );
p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
video_timer_stop( id->p_encoder ); video_timer_stop( id->p_encoder );
vlc_mutex_lock( &p_sys->lock_out ); vlc_mutex_lock( &p_video->lock_out );
block_ChainAppend( &p_sys->p_buffers, p_block ); block_ChainAppend( &p_video->p_buffers, p_block );
vlc_mutex_unlock( &p_sys->lock_out ); vlc_mutex_unlock( &p_video->lock_out );
p_pic->pf_release( p_pic ); p_pic->pf_release( p_pic );
} }
while( p_sys->i_last_pic != p_sys->i_first_pic ) while( p_video->i_last_pic != p_video->i_first_pic )
{ {
p_pic = p_sys->pp_pics[p_sys->i_first_pic++]; p_pic = p_video->pp_pics[p_video->i_first_pic++];
p_sys->i_first_pic %= PICTURE_RING_SIZE; p_video->i_first_pic %= PICTURE_RING_SIZE;
p_pic->pf_release( p_pic ); p_pic->pf_release( p_pic );
} }
block_ChainRelease( p_sys->p_buffers ); block_ChainRelease( p_video->p_buffers );
vlc_restorecancel (canc); vlc_restorecancel (canc);
return NULL; return NULL;
...@@ -2136,6 +2298,7 @@ static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring, ...@@ -2136,6 +2298,7 @@ static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring,
sout_stream_sys_t *p_sys ) sout_stream_sys_t *p_sys )
{ {
decoder_t *p_dec = (decoder_t *)p_this; decoder_t *p_dec = (decoder_t *)p_this;
sout_stream_video_t *p_video = p_sys->p_video;
picture_t *p_pic; picture_t *p_pic;
int i; int i;
...@@ -2153,14 +2316,14 @@ static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring, ...@@ -2153,14 +2316,14 @@ static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring,
if( pp_ring[i] == NULL ) break; if( pp_ring[i] == NULL ) break;
} }
if( i == PICTURE_RING_SIZE && p_sys->i_threads >= 1 ) if( i == PICTURE_RING_SIZE && p_video->i_threads >= 1 )
{ {
int i_first_pic = p_sys->i_first_pic; int i_first_pic = p_video->i_first_pic;
if( p_sys->i_first_pic != p_sys->i_last_pic ) if( p_video->i_first_pic != p_video->i_last_pic )
{ {
/* Encoder still has stuff to encode, wait to clear-up the list */ /* Encoder still has stuff to encode, wait to clear-up the list */
while( p_sys->i_first_pic == i_first_pic ) while( p_video->i_first_pic == i_first_pic )
msleep( 5000 ); msleep( 5000 );
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment