Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc-gpu
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc-gpu
Commits
7d6f5894
Commit
7d6f5894
authored
Aug 06, 2011
by
Rémi Denis-Courmont
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
aout: hide "input" from decoder
parent
6feb8beb
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
88 additions
and
92 deletions
+88
-92
src/audio_output/aout_internal.h
src/audio_output/aout_internal.h
+10
-11
src/audio_output/dec.c
src/audio_output/dec.c
+58
-48
src/input/decoder.c
src/input/decoder.c
+20
-33
No files found.
src/audio_output/aout_internal.h
View file @
7d6f5894
...
...
@@ -172,17 +172,16 @@ void aout_FormatsPrint( audio_output_t * p_aout, const char * psz_text, const au
bool
aout_ChangeFilterString
(
vlc_object_t
*
,
audio_output_t
*
,
const
char
*
psz_variable
,
const
char
*
psz_name
,
bool
b_add
);
/* From dec.c */
aout_input_t
*
aout_DecNew
(
audio_output_t
*
,
const
audio_sample_format_t
*
,
const
audio_replay_gain_t
*
,
const
aout_request_vout_t
*
);
void
aout_DecDelete
(
audio_output_t
*
,
aout_input_t
*
);
aout_buffer_t
*
aout_DecNewBuffer
(
aout_input_t
*
,
size_t
);
void
aout_DecDeleteBuffer
(
audio_output_t
*
,
aout_input_t
*
,
aout_buffer_t
*
);
int
aout_DecPlay
(
audio_output_t
*
,
aout_input_t
*
,
aout_buffer_t
*
,
int
i_input_rate
);
int
aout_DecGetResetLost
(
audio_output_t
*
,
aout_input_t
*
);
void
aout_DecChangePause
(
audio_output_t
*
,
aout_input_t
*
,
bool
b_paused
,
mtime_t
i_date
);
void
aout_DecFlush
(
audio_output_t
*
,
aout_input_t
*
);
bool
aout_DecIsEmpty
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
);
int
aout_DecNew
(
audio_output_t
*
,
const
audio_sample_format_t
*
,
const
audio_replay_gain_t
*
,
const
aout_request_vout_t
*
);
void
aout_DecDelete
(
audio_output_t
*
);
block_t
*
aout_DecNewBuffer
(
audio_output_t
*
,
size_t
);
void
aout_DecDeleteBuffer
(
audio_output_t
*
,
block_t
*
);
int
aout_DecPlay
(
audio_output_t
*
,
block_t
*
,
int
i_input_rate
);
int
aout_DecGetResetLost
(
audio_output_t
*
);
void
aout_DecChangePause
(
audio_output_t
*
,
bool
b_paused
,
mtime_t
i_date
);
void
aout_DecFlush
(
audio_output_t
*
);
bool
aout_DecIsEmpty
(
audio_output_t
*
);
/* Audio output locking */
...
...
src/audio_output/dec.c
View file @
7d6f5894
...
...
@@ -38,49 +38,48 @@
#include "aout_internal.h"
#include "libvlc.h"
#undef aout_DecNew
/**
* Creates an audio output
*/
aout_input_t
*
aout_DecNew
(
audio_output_t
*
p_aout
,
const
audio_sample_format_t
*
p_format
,
const
audio_replay_gain_t
*
p_replay_gain
,
const
aout_request_vout_t
*
p_request_vout
)
int
aout_DecNew
(
audio_output_t
*
p_aout
,
const
audio_sample_format_t
*
p_format
,
const
audio_replay_gain_t
*
p_replay_gain
,
const
aout_request_vout_t
*
p_request_vout
)
{
/* Sanitize audio format */
if
(
p_format
->
i_channels
>
32
)
{
msg_Err
(
p_aout
,
"too many audio channels (%u)"
,
p_format
->
i_channels
);
return
NULL
;
return
-
1
;
}
if
(
p_format
->
i_channels
<=
0
)
{
msg_Err
(
p_aout
,
"no audio channels"
);
return
NULL
;
return
-
1
;
}
if
(
p_format
->
i_channels
!=
aout_FormatNbChannels
(
p_format
)
)
{
msg_Err
(
p_aout
,
"incompatible audio channels count with layout mask"
);
return
NULL
;
return
-
1
;
}
if
(
p_format
->
i_rate
>
192000
)
{
msg_Err
(
p_aout
,
"excessive audio sample frequency (%u)"
,
p_format
->
i_rate
);
return
NULL
;
return
-
1
;
}
if
(
p_format
->
i_rate
<
4000
)
{
msg_Err
(
p_aout
,
"too low audio sample frequency (%u)"
,
p_format
->
i_rate
);
return
NULL
;
return
-
1
;
}
aout_input_t
*
p_input
=
calloc
(
1
,
sizeof
(
aout_input_t
));
if
(
!
p_input
)
return
NULL
;
return
-
1
;
p_input
->
b_error
=
true
;
...
...
@@ -113,26 +112,27 @@ aout_input_t *aout_DecNew( audio_output_t *p_aout,
owner
->
input
=
p_input
;
aout_InputNew
(
p_aout
,
p_input
,
p_request_vout
);
aout_unlock
(
p_aout
);
return
p_input
;
return
0
;
error:
aout_unlock
(
p_aout
);
free
(
p_input
);
return
NULL
;
return
-
1
;
}
/*****************************************************************************
* aout_DecDelete : delete a decoder
*****************************************************************************/
void
aout_DecDelete
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
)
void
aout_DecDelete
(
audio_output_t
*
p_aout
)
{
aout_owner_t
*
owner
=
aout_owner
(
p_aout
);
aout_input_t
*
input
;
struct
audio_mixer
*
mixer
;
aout_lock
(
p_aout
);
/* Remove the input. */
assert
(
owner
->
input
==
p_input
);
/* buggy decoder? */
input
=
owner
->
input
;
aout_InputDelete
(
p_aout
,
input
);
owner
->
input
=
NULL
;
aout_InputDelete
(
p_aout
,
p_input
);
aout_OutputDelete
(
p_aout
);
mixer
=
owner
->
volume
.
mixer
;
...
...
@@ -143,7 +143,7 @@ void aout_DecDelete( audio_output_t * p_aout, aout_input_t * p_input )
aout_unlock
(
p_aout
);
aout_MixerDelete
(
mixer
);
free
(
p_input
);
free
(
input
);
}
static
void
aout_CheckRestart
(
audio_output_t
*
aout
)
...
...
@@ -191,15 +191,18 @@ error:
/*****************************************************************************
* aout_DecNewBuffer : ask for a new empty buffer
*****************************************************************************/
aout_buffer_t
*
aout_DecNewBuffer
(
aout_input_t
*
p_input
,
size_t
i_nb_samples
)
block_t
*
aout_DecNewBuffer
(
audio_output_t
*
aout
,
size_t
samples
)
{
size_t
length
=
i_nb_samples
*
p_input
->
input
.
i_bytes_per_frame
/
p_input
->
input
.
i_frame_length
;
/* NOTE: the caller is responsible for serializing input change */
aout_owner_t
*
owner
=
aout_owner
(
aout
);
aout_input_t
*
input
=
owner
->
input
;
size_t
length
=
samples
*
input
->
input
.
i_bytes_per_frame
/
input
->
input
.
i_frame_length
;
block_t
*
block
=
block_Alloc
(
length
);
if
(
likely
(
block
!=
NULL
)
)
{
block
->
i_nb_samples
=
i_nb_
samples
;
block
->
i_nb_samples
=
samples
;
block
->
i_pts
=
block
->
i_length
=
0
;
}
return
block
;
...
...
@@ -208,20 +211,20 @@ aout_buffer_t * aout_DecNewBuffer( aout_input_t * p_input,
/*****************************************************************************
* aout_DecDeleteBuffer : destroy an undecoded buffer
*****************************************************************************/
void
aout_DecDeleteBuffer
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
,
aout_buffer_t
*
p_buffer
)
void
aout_DecDeleteBuffer
(
audio_output_t
*
aout
,
block_t
*
block
)
{
(
void
)
p_aout
;
(
void
)
p_inp
ut
;
aout_BufferFree
(
p_buffer
);
(
void
)
ao
ut
;
aout_BufferFree
(
block
);
}
/*****************************************************************************
* aout_DecPlay : filter & mix the decoded buffer
*****************************************************************************/
int
aout_DecPlay
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
,
aout_buffer_t
*
p_buffer
,
int
i_input_rate
)
int
aout_DecPlay
(
audio_output_t
*
p_aout
,
block_t
*
p_buffer
,
int
i_input_rate
)
{
aout_owner_t
*
owner
=
aout_owner
(
p_aout
);
aout_input_t
*
p_input
=
owner
->
input
;
assert
(
i_input_rate
>=
INPUT_RATE_DEFAULT
/
AOUT_MAX_INPUT_RATE
&&
i_input_rate
<=
INPUT_RATE_DEFAULT
*
AOUT_MAX_INPUT_RATE
);
assert
(
p_buffer
->
i_pts
>
0
);
...
...
@@ -257,46 +260,53 @@ int aout_DecPlay( audio_output_t * p_aout, aout_input_t * p_input,
return
0
;
}
int
aout_DecGetResetLost
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
)
int
aout_DecGetResetLost
(
audio_output_t
*
aout
)
{
aout_owner_t
*
owner
=
aout_owner
(
aout
);
aout_input_t
*
input
=
owner
->
input
;
int
val
;
aout_lock
(
p_aout
);
val
=
p_
input
->
i_buffer_lost
;
p_
input
->
i_buffer_lost
=
0
;
aout_unlock
(
p_aout
);
aout_lock
(
aout
);
val
=
input
->
i_buffer_lost
;
input
->
i_buffer_lost
=
0
;
aout_unlock
(
aout
);
return
val
;
}
void
aout_DecChangePause
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
,
bool
b_paused
,
mtime_t
i_date
)
void
aout_DecChangePause
(
audio_output_t
*
aout
,
bool
paused
,
mtime_t
date
)
{
aout_owner_t
*
owner
=
aout_owner
(
p_aout
);
aout_owner_t
*
owner
=
aout_owner
(
aout
);
aout_input_t
*
p_input
=
owner
->
input
;
aout_lock
(
p_aout
);
assert
(
owner
->
input
==
p_input
);
aout_lock
(
aout
);
/* XXX: Should the input date be offset by the pause duration instead? */
date_Set
(
&
p_input
->
date
,
VLC_TS_INVALID
);
aout_OutputPause
(
p_aout
,
b_paused
,
i_date
);
aout_unlock
(
p_aout
);
aout_OutputPause
(
aout
,
paused
,
date
);
aout_unlock
(
aout
);
}
void
aout_DecFlush
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
)
void
aout_DecFlush
(
audio_output_t
*
aout
)
{
aout_lock
(
p_aout
);
date_Set
(
&
p_input
->
date
,
VLC_TS_INVALID
);
aout_OutputFlush
(
p_aout
,
false
);
aout_unlock
(
p_aout
);
aout_owner_t
*
owner
=
aout_owner
(
aout
);
aout_input_t
*
input
=
owner
->
input
;
aout_lock
(
aout
);
date_Set
(
&
input
->
date
,
VLC_TS_INVALID
);
aout_OutputFlush
(
aout
,
false
);
aout_unlock
(
aout
);
}
bool
aout_DecIsEmpty
(
audio_output_t
*
p_aout
,
aout_input_t
*
p_input
)
bool
aout_DecIsEmpty
(
audio_output_t
*
aout
)
{
aout_owner_t
*
owner
=
aout_owner
(
aout
);
aout_input_t
*
input
=
owner
->
input
;
mtime_t
end_date
;
aout_lock
(
p_aout
);
aout_lock
(
aout
);
/* FIXME: tell output to drain */
end_date
=
date_Get
(
&
p_
input
->
date
);
aout_unlock
(
p_aout
);
end_date
=
date_Get
(
&
input
->
date
);
aout_unlock
(
aout
);
return
end_date
==
VLC_TS_INVALID
||
end_date
<=
mdate
();
}
src/input/decoder.c
View file @
7d6f5894
...
...
@@ -122,7 +122,6 @@ struct decoder_owner_sys_t
/* -- These variables need locking on write(only) -- */
audio_output_t
*
p_aout
;
aout_input_t
*
p_aout_input
;
vout_thread_t
*
p_vout
;
...
...
@@ -428,7 +427,7 @@ bool input_DecoderIsEmpty( decoder_t * p_dec )
if
(
p_dec
->
fmt_out
.
i_cat
==
VIDEO_ES
&&
p_owner
->
p_vout
)
b_empty
=
vout_IsEmpty
(
p_owner
->
p_vout
);
else
if
(
p_dec
->
fmt_out
.
i_cat
==
AUDIO_ES
&&
p_owner
->
p_aout
)
b_empty
=
aout_DecIsEmpty
(
p_owner
->
p_aout
,
p_owner
->
p_aout_input
);
b_empty
=
aout_DecIsEmpty
(
p_owner
->
p_aout
);
vlc_mutex_unlock
(
&
p_owner
->
lock
);
}
return
b_empty
;
...
...
@@ -781,7 +780,6 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
p_owner
->
p_input
=
p_input
;
p_owner
->
p_resource
=
p_resource
;
p_owner
->
p_aout
=
NULL
;
p_owner
->
p_aout_input
=
NULL
;
p_owner
->
p_vout
=
NULL
;
p_owner
->
p_spu_vout
=
NULL
;
p_owner
->
i_spu_channel
=
0
;
...
...
@@ -1054,8 +1052,7 @@ static void DecoderOutputChangePause( decoder_t *p_dec, bool b_paused, mtime_t i
if
(
p_dec
->
fmt_out
.
i_cat
==
AUDIO_ES
)
{
if
(
p_owner
->
p_aout
)
aout_DecChangePause
(
p_owner
->
p_aout
,
p_owner
->
p_aout_input
,
b_paused
,
i_date
);
aout_DecChangePause
(
p_owner
->
p_aout
,
b_paused
,
i_date
);
}
else
if
(
p_dec
->
fmt_out
.
i_cat
==
VIDEO_ES
)
{
...
...
@@ -1180,7 +1177,6 @@ static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio,
{
decoder_owner_sys_t
*
p_owner
=
p_dec
->
p_owner
;
audio_output_t
*
p_aout
=
p_owner
->
p_aout
;
aout_input_t
*
p_aout_input
=
p_owner
->
p_aout_input
;
/* */
if
(
p_audio
->
i_pts
<=
VLC_TS_INVALID
)
// FIXME --VLC_TS_INVALID verify audio_output/*
...
...
@@ -1244,7 +1240,7 @@ static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio,
vlc_mutex_unlock
(
&
p_owner
->
lock
);
if
(
!
p_aout
||
!
p_aout_input
||
if
(
!
p_aout
||
p_audio
->
i_pts
<=
VLC_TS_INVALID
||
i_rate
<
INPUT_RATE_DEFAULT
/
AOUT_MAX_INPUT_RATE
||
i_rate
>
INPUT_RATE_DEFAULT
*
AOUT_MAX_INPUT_RATE
)
...
...
@@ -1255,9 +1251,9 @@ static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio,
if
(
!
b_reject
)
{
if
(
!
aout_DecPlay
(
p_aout
,
p_a
out_input
,
p_a
udio
,
i_rate
)
)
if
(
!
aout_DecPlay
(
p_aout
,
p_audio
,
i_rate
)
)
*
pi_played_sum
+=
1
;
*
pi_lost_sum
+=
aout_DecGetResetLost
(
p_aout
,
p_aout_input
);
*
pi_lost_sum
+=
aout_DecGetResetLost
(
p_aout
);
}
else
{
...
...
@@ -1293,12 +1289,11 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block )
while
(
(
p_aout_buf
=
p_dec
->
pf_decode_audio
(
p_dec
,
&
p_block
))
)
{
audio_output_t
*
p_aout
=
p_owner
->
p_aout
;
aout_input_t
*
p_aout_input
=
p_owner
->
p_aout_input
;
if
(
DecoderIsExitRequested
(
p_dec
)
)
{
/* It prevent freezing VLC in case of broken decoder */
aout_DecDeleteBuffer
(
p_aout
,
p_aout_
input
,
p_aout_
buf
);
aout_DecDeleteBuffer
(
p_aout
,
p_aout_buf
);
if
(
p_block
)
block_Release
(
p_block
);
break
;
...
...
@@ -1308,15 +1303,15 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block )
if
(
p_owner
->
i_preroll_end
>
VLC_TS_INVALID
&&
p_aout_buf
->
i_pts
<
p_owner
->
i_preroll_end
)
{
aout_DecDeleteBuffer
(
p_aout
,
p_aout_
input
,
p_aout_
buf
);
aout_DecDeleteBuffer
(
p_aout
,
p_aout_buf
);
continue
;
}
if
(
p_owner
->
i_preroll_end
>
VLC_TS_INVALID
)
{
msg_Dbg
(
p_dec
,
"End of audio preroll"
);
if
(
p_owner
->
p_aout
&&
p_owner
->
p_aout_input
)
aout_DecFlush
(
p_owner
->
p_aout
,
p_owner
->
p_aout_input
);
if
(
p_owner
->
p_aout
)
aout_DecFlush
(
p_owner
->
p_aout
);
/* */
p_owner
->
i_preroll_end
=
VLC_TS_INVALID
;
}
...
...
@@ -1941,7 +1936,7 @@ static void DecoderProcessAudio( decoder_t *p_dec, block_t *p_block, bool b_flus
}
if
(
b_flush
&&
p_owner
->
p_aout
)
aout_DecFlush
(
p_owner
->
p_aout
,
p_owner
->
p_aout_input
);
aout_DecFlush
(
p_owner
->
p_aout
);
}
/* This function process a subtitle block
...
...
@@ -2121,7 +2116,7 @@ static void DeleteDecoder( decoder_t * p_dec )
/* Cleanup */
if
(
p_owner
->
p_aout
)
{
aout_DecDelete
(
p_owner
->
p_aout
,
p_owner
->
p_aout_input
);
aout_DecDelete
(
p_owner
->
p_aout
);
input_resource_RequestAout
(
p_owner
->
p_resource
,
p_owner
->
p_aout
);
if
(
p_owner
->
p_input
!=
NULL
)
input_SendEventAout
(
p_owner
->
p_input
);
...
...
@@ -2229,14 +2224,13 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
decoder_owner_sys_t
*
p_owner
=
p_dec
->
p_owner
;
aout_buffer_t
*
p_buffer
;
if
(
p_owner
->
p_aout
_input
!=
NULL
&&
if
(
p_owner
->
p_aout
&&
(
p_dec
->
fmt_out
.
audio
.
i_rate
!=
p_owner
->
audio
.
i_rate
||
p_dec
->
fmt_out
.
audio
.
i_original_channels
!=
p_owner
->
audio
.
i_original_channels
||
p_dec
->
fmt_out
.
audio
.
i_bytes_per_frame
!=
p_owner
->
audio
.
i_bytes_per_frame
)
)
{
aout_input_t
*
p_aout_input
=
p_owner
->
p_aout_input
;
audio_output_t
*
p_aout
=
p_owner
->
p_aout
;
/* Parameters changed, restart the aout */
...
...
@@ -2244,19 +2238,17 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
DecoderFlushBuffering
(
p_dec
);
aout_DecDelete
(
p_owner
->
p_aout
,
p_aout_input
);
aout_DecDelete
(
p_owner
->
p_aout
);
p_owner
->
p_aout
=
NULL
;
p_owner
->
p_aout_input
=
NULL
;
vlc_mutex_unlock
(
&
p_owner
->
lock
);
input_resource_RequestAout
(
p_owner
->
p_resource
,
p_aout
);
}
if
(
p_owner
->
p_aout
_input
==
NULL
)
if
(
p_owner
->
p_aout
==
NULL
)
{
const
int
i_force_dolby
=
var_InheritInteger
(
p_dec
,
"force-dolby-surround"
);
audio_sample_format_t
format
;
aout_input_t
*
p_aout_input
;
audio_output_t
*
p_aout
;
aout_request_vout_t
request_vout
;
...
...
@@ -2288,22 +2280,18 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
if
(
p_aout
)
{
aout_FormatPrepare
(
&
format
);
p_aout_input
=
aout_DecNew
(
p_aout
,
&
format
,
&
p_dec
->
fmt_out
.
audio_replay_gain
,
&
request_vout
);
if
(
p_aout_input
==
NULL
)
if
(
aout_DecNew
(
p_aout
,
&
format
,
&
p_dec
->
fmt_out
.
audio_replay_gain
,
&
request_vout
)
)
{
input_resource_RequestAout
(
p_owner
->
p_resource
,
p_aout
);
p_aout
=
NULL
;
}
}
else
p_aout_input
=
NULL
;
vlc_mutex_lock
(
&
p_owner
->
lock
);
p_owner
->
p_aout
=
p_aout
;
p_owner
->
p_aout_input
=
p_aout_input
;
DecoderUpdateFormatLocked
(
p_dec
);
vlc_mutex_unlock
(
&
p_owner
->
lock
);
...
...
@@ -2311,7 +2299,7 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
if
(
p_owner
->
p_input
!=
NULL
)
input_SendEventAout
(
p_owner
->
p_input
);
if
(
p_
owner
->
p_aout_inp
ut
==
NULL
)
if
(
p_
ao
ut
==
NULL
)
{
msg_Err
(
p_dec
,
"failed to create audio output"
);
p_dec
->
b_error
=
true
;
...
...
@@ -2321,7 +2309,7 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples )
p_owner
->
audio
.
i_bytes_per_frame
;
}
p_buffer
=
aout_DecNewBuffer
(
p_owner
->
p_aout
_input
,
i_samples
);
p_buffer
=
aout_DecNewBuffer
(
p_owner
->
p_aout
,
i_samples
);
return
p_buffer
;
}
...
...
@@ -2330,8 +2318,7 @@ static void aout_del_buffer( decoder_t *p_dec, aout_buffer_t *p_buffer )
{
decoder_owner_sys_t
*
p_owner
=
p_dec
->
p_owner
;
aout_DecDeleteBuffer
(
p_owner
->
p_aout
,
p_owner
->
p_aout_input
,
p_buffer
);
aout_DecDeleteBuffer
(
p_owner
->
p_aout
,
p_buffer
);
}
static
picture_t
*
vout_new_buffer
(
decoder_t
*
p_dec
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment