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
d0967dc4
Commit
d0967dc4
authored
Jun 06, 2009
by
Laurent Aimar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Converted clone to "video splitter".
parent
c9b7fad4
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
83 additions
and
321 deletions
+83
-321
modules/video_filter/clone.c
modules/video_filter/clone.c
+79
-321
modules/video_filter/wrapper.c
modules/video_filter/wrapper.c
+4
-0
No files found.
modules/video_filter/clone.c
View file @
d0967dc4
/*****************************************************************************
/*****************************************************************************
* clone.c : Clone video plugin for vlc
* clone.c : Clone video plugin for vlc
*****************************************************************************
*****************************************************************************
* Copyright (C) 2002
, 2003
the VideoLAN team
* Copyright (C) 2002
-2009
the VideoLAN team
* $Id$
* $Id$
*
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Authors: Samuel Hocevar <sam@zoy.org>
...
@@ -31,28 +31,7 @@
...
@@ -31,28 +31,7 @@
#include <vlc_common.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_plugin.h>
#include <vlc_vout.h>
#include <vlc_video_splitter.h>
#include "filter_common.h"
#define VOUTSEPARATOR ','
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static
int
Create
(
vlc_object_t
*
);
static
void
Destroy
(
vlc_object_t
*
);
static
int
Init
(
vout_thread_t
*
);
static
void
End
(
vout_thread_t
*
);
static
void
Render
(
vout_thread_t
*
,
picture_t
*
);
static
void
RemoveAllVout
(
vout_thread_t
*
p_vout
);
static
int
FullscreenEventUp
(
vlc_object_t
*
,
char
const
*
,
vlc_value_t
,
vlc_value_t
,
void
*
);
static
int
FullscreenEventDown
(
vlc_object_t
*
,
char
const
*
,
vlc_value_t
,
vlc_value_t
,
void
*
);
/*****************************************************************************
/*****************************************************************************
* Module descriptor
* Module descriptor
...
@@ -67,9 +46,12 @@ static int FullscreenEventDown( vlc_object_t *, char const *,
...
@@ -67,9 +46,12 @@ static int FullscreenEventDown( vlc_object_t *, char const *,
#define CFG_PREFIX "clone-"
#define CFG_PREFIX "clone-"
static
int
Open
(
vlc_object_t
*
);
static
void
Close
(
vlc_object_t
*
);
vlc_module_begin
()
vlc_module_begin
()
set_description
(
N_
(
"Clone video filter"
)
)
set_description
(
N_
(
"Clone video filter"
)
)
set_capability
(
"video
fil
ter"
,
0
)
set_capability
(
"video
split
ter"
,
0
)
set_shortname
(
N_
(
"Clone"
))
set_shortname
(
N_
(
"Clone"
))
set_category
(
CAT_VIDEO
)
set_category
(
CAT_VIDEO
)
set_subcategory
(
SUBCAT_VIDEO_VFILTER
)
set_subcategory
(
SUBCAT_VIDEO_VFILTER
)
...
@@ -78,110 +60,62 @@ vlc_module_begin ()
...
@@ -78,110 +60,62 @@ vlc_module_begin ()
add_string
(
CFG_PREFIX
"vout-list"
,
NULL
,
NULL
,
VOUTLIST_TEXT
,
VOUTLIST_LONGTEXT
,
true
)
add_string
(
CFG_PREFIX
"vout-list"
,
NULL
,
NULL
,
VOUTLIST_TEXT
,
VOUTLIST_LONGTEXT
,
true
)
add_shortcut
(
"clone"
)
add_shortcut
(
"clone"
)
set_callbacks
(
Create
,
Destroy
)
set_callbacks
(
Open
,
Close
)
vlc_module_end
()
vlc_module_end
()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static
const
char
*
const
ppsz_filter_options
[]
=
{
static
const
char
*
const
ppsz_filter_options
[]
=
{
"count"
,
"vout-list"
,
NULL
"count"
,
"vout-list"
,
NULL
};
};
/*****************************************************************************
#define VOUTSEPARATOR ','
* vout_sys_t: Clone video output method descriptor
*****************************************************************************
* This structure is part of the video output thread descriptor.
* It describes the Clone specific properties of an output thread.
*****************************************************************************/
struct
vout_sys_t
{
int
i_clones
;
/* list of vout modules to use. "default" will launch a default
* module. If specified, overrides the setting in i_clones (which it
* sets to the list length) */
char
**
ppsz_vout_list
;
vout_thread_t
**
pp_vout
;
};
/*****************************************************************************
static
int
Filter
(
video_splitter_t
*
,
picture_t
*
pp_dst
[],
picture_t
*
);
* Control: control facility for the vout (forwards to child vout)
*****************************************************************************/
static
int
Control
(
vout_thread_t
*
p_vout
,
int
i_query
,
va_list
args
)
{
int
i_vout
;
for
(
i_vout
=
0
;
i_vout
<
p_vout
->
p_sys
->
i_clones
;
i_vout
++
)
{
vout_vaControl
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
i_query
,
args
);
}
return
VLC_SUCCESS
;
}
/*****************************************************************************
/**
* Create: allocates Clone video thread output method
* This function allocates and initializes a Clone splitter module
*****************************************************************************
*/
* This function allocates and initializes a Clone vout method.
static
int
Open
(
vlc_object_t
*
p_this
)
*****************************************************************************/
static
int
Create
(
vlc_object_t
*
p_this
)
{
{
vout_thread_t
*
p_vout
=
(
vout_thread_t
*
)
p_this
;
video_splitter_t
*
p_splitter
=
(
video_splitter_t
*
)
p_this
;
char
*
psz_clonelist
;
/* Allocate structure */
p_vout
->
p_sys
=
malloc
(
sizeof
(
vout_sys_t
)
);
if
(
p_vout
->
p_sys
==
NULL
)
return
VLC_ENOMEM
;
p_vout
->
pf_init
=
Init
;
p_vout
->
pf_end
=
End
;
p_vout
->
pf_manage
=
NULL
;
p_vout
->
pf_render
=
Render
;
p_vout
->
pf_display
=
NULL
;
p_vout
->
pf_control
=
Control
;
config_ChainParse
(
p_
vout
,
CFG_PREFIX
,
ppsz_filter_options
,
config_ChainParse
(
p_
splitter
,
CFG_PREFIX
,
ppsz_filter_options
,
p_
vout
->
p_cfg
);
p_
splitter
->
p_cfg
);
psz_clonelist
=
var_CreateGetNonEmptyString
(
p_vout
,
char
*
psz_clonelist
=
var_CreateGetNonEmptyString
(
p_splitter
,
CFG_PREFIX
"vout-list"
);
CFG_PREFIX
"vout-list"
);
if
(
psz_clonelist
)
if
(
psz_clonelist
)
{
{
int
i_dummy
;
char
*
psz_token
;
/* Count the number of defined vout */
/* Count the number of defined vout */
p_vout
->
p_sys
->
i_clones
=
1
;
p_splitter
->
i_output
=
1
;
i_dummy
=
0
;
for
(
int
i
=
0
;
psz_clonelist
[
i
];
i
++
)
while
(
psz_clonelist
[
i_dummy
]
!=
0
)
{
{
if
(
psz_clonelist
[
i_dummy
]
==
VOUTSEPARATOR
)
if
(
psz_clonelist
[
i
]
==
VOUTSEPARATOR
)
p_vout
->
p_sys
->
i_clones
++
;
p_splitter
->
i_output
++
;
i_dummy
++
;
}
}
p_vout
->
p_sys
->
ppsz_vout_list
=
malloc
(
p_vout
->
p_sys
->
i_clones
/* */
*
sizeof
(
char
*
)
);
p_splitter
->
p_output
=
calloc
(
p_splitter
->
i_output
,
if
(
!
p_vout
->
p_sys
->
ppsz_vout_list
)
sizeof
(
*
p_splitter
->
p_output
)
);
if
(
!
p_splitter
->
p_output
)
{
{
free
(
psz_clonelist
);
free
(
psz_clonelist
);
free
(
p_vout
->
p_sys
);
return
VLC_EGENERIC
;
return
VLC_ENOMEM
;
}
}
/* Tokenize the list */
/* Tokenize the list */
i_dummy
=
0
;
char
*
psz_tmp
=
psz_clonelist
;
psz_token
=
psz_clonelist
;
for
(
int
i
=
0
;
psz_tmp
&&
*
psz_tmp
;
i
++
)
while
(
psz_token
&&
*
psz_token
)
{
{
char
*
psz_module
;
char
*
psz_new
=
strchr
(
psz_tmp
,
VOUTSEPARATOR
);
psz_module
=
psz_token
;
if
(
psz_new
)
psz_token
=
strchr
(
psz_module
,
VOUTSEPARATOR
);
*
psz_new
++
=
'\0'
;
if
(
psz_token
)
{
p_splitter
->
p_output
[
i
].
psz_module
=
strdup
(
psz_tmp
);
*
psz_token
=
'\0'
;
psz_token
++
;
psz_tmp
=
psz_new
;
}
p_vout
->
p_sys
->
ppsz_vout_list
[
i_dummy
]
=
strdup
(
psz_module
);
i_dummy
++
;
}
}
free
(
psz_clonelist
);
free
(
psz_clonelist
);
...
@@ -190,244 +124,68 @@ static int Create( vlc_object_t *p_this )
...
@@ -190,244 +124,68 @@ static int Create( vlc_object_t *p_this )
{
{
/* No list was specified. We will use the default vout, and get
/* No list was specified. We will use the default vout, and get
* the number of clones from clone-count */
* the number of clones from clone-count */
p_vout
->
p_sys
->
i_clones
=
p_splitter
->
i_output
=
var_CreateGetInteger
(
p_splitter
,
CFG_PREFIX
"count"
);
var_CreateGetInteger
(
p_vout
,
CFG_PREFIX
"count"
);
if
(
p_splitter
->
i_output
<=
0
)
p_vout
->
p_sys
->
ppsz_vout_list
=
NULL
;
p_splitter
->
i_output
=
1
;
}
p_vout
->
p_sys
->
i_clones
=
__MAX
(
1
,
__MIN
(
99
,
p_vout
->
p_sys
->
i_clones
)
);
p_splitter
->
p_output
=
calloc
(
p_splitter
->
i_output
,
sizeof
(
*
p_splitter
->
p_output
)
);
msg_Dbg
(
p_vout
,
"spawning %i clone(s)"
,
p_vout
->
p_sys
->
i_clones
);
if
(
!
p_splitter
->
p_output
)
return
VLC_EGENERIC
;
p_vout
->
p_sys
->
pp_vout
=
malloc
(
p_vout
->
p_sys
->
i_clones
*
for
(
int
i
=
0
;
i
<
p_splitter
->
i_output
;
i
++
)
sizeof
(
vout_thread_t
*
)
);
p_splitter
->
p_output
[
i
].
psz_module
=
NULL
;
if
(
p_vout
->
p_sys
->
pp_vout
==
NULL
)
{
free
(
p_vout
->
p_sys
);
return
VLC_ENOMEM
;
}
}
return
VLC_SUCCESS
;
/* */
}
for
(
int
i
=
0
;
i
<
p_splitter
->
i_output
;
i
++
)
/*****************************************************************************
* Init: initialize Clone video thread output method
*****************************************************************************/
static
int
Init
(
vout_thread_t
*
p_vout
)
{
int
i_vout
;
char
*
psz_default_vout
;
video_format_t
fmt
;
I_OUTPUTPICTURES
=
0
;
memset
(
&
fmt
,
0
,
sizeof
(
video_format_t
)
);
/* Initialize the output structure */
p_vout
->
output
.
i_chroma
=
p_vout
->
render
.
i_chroma
;
p_vout
->
output
.
i_width
=
p_vout
->
render
.
i_width
;
p_vout
->
output
.
i_height
=
p_vout
->
render
.
i_height
;
p_vout
->
output
.
i_aspect
=
p_vout
->
render
.
i_aspect
;
p_vout
->
fmt_out
=
p_vout
->
fmt_in
;
fmt
=
p_vout
->
fmt_out
;
/* Try to open the real video output */
msg_Dbg
(
p_vout
,
"spawning the real video outputs"
);
/* Save the default vout */
psz_default_vout
=
config_GetPsz
(
p_vout
,
"vout"
);
for
(
i_vout
=
0
;
i_vout
<
p_vout
->
p_sys
->
i_clones
;
i_vout
++
)
{
{
if
(
p_vout
->
p_sys
->
ppsz_vout_list
==
NULL
video_splitter_output_t
*
p_cfg
=
&
p_splitter
->
p_output
[
i
];
||
(
!
strncmp
(
p_vout
->
p_sys
->
ppsz_vout_list
[
i_vout
],
video_format_Copy
(
&
p_cfg
->
fmt
,
&
p_splitter
->
fmt
);
"default"
,
8
)
)
)
p_cfg
->
window
.
i_x
=
-
1
;
{
p_cfg
->
window
.
i_y
=
-
1
;
p_vout
->
p_sys
->
pp_vout
[
i_vout
]
=
p_cfg
->
window
.
i_align
=
0
;
vout_Create
(
p_vout
,
&
fmt
);
}
else
{
/* create the appropriate vout instead of the default one */
config_PutPsz
(
p_vout
,
"vout"
,
p_vout
->
p_sys
->
ppsz_vout_list
[
i_vout
]
);
p_vout
->
p_sys
->
pp_vout
[
i_vout
]
=
vout_Create
(
p_vout
,
&
fmt
);
/* Reset the default value */
config_PutPsz
(
p_vout
,
"vout"
,
psz_default_vout
);
}
}
if
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
]
==
NULL
)
/* */
{
p_splitter
->
pf_filter
=
Filter
;
msg_Err
(
p_vout
,
"failed to clone %i vout threads"
,
p_splitter
->
pf_mouse
=
NULL
;
p_vout
->
p_sys
->
i_clones
);
p_vout
->
p_sys
->
i_clones
=
i_vout
;
free
(
psz_default_vout
);
RemoveAllVout
(
p_vout
);
return
VLC_EGENERIC
;
}
vout_filter_SetupChild
(
p_vout
,
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
NULL
,
FullscreenEventUp
,
FullscreenEventDown
,
true
);
}
free
(
psz_default_vout
);
msg_Dbg
(
p_splitter
,
"spawning %i clone(s)"
,
p_splitter
->
i_output
);
vout_filter_AllocateDirectBuffers
(
p_vout
,
VOUT_MAX_PICTURES
);
return
VLC_SUCCESS
;
return
VLC_SUCCESS
;
}
}
/*****************************************************************************
/**
* End: terminate Clone video thread output method
* This function closes a clone video splitter module
*****************************************************************************/
*/
static
void
End
(
vout_thread_t
*
p_vout
)
static
void
Close
(
vlc_object_t
*
p_this
)
{
RemoveAllVout
(
p_vout
);
vout_filter_ReleaseDirectBuffers
(
p_vout
);
}
/*****************************************************************************
* Destroy: destroy Clone video thread output method
*****************************************************************************
* Terminate an output method created by CloneCreateOutputMethod
*****************************************************************************/
static
void
Destroy
(
vlc_object_t
*
p_this
)
{
vout_thread_t
*
p_vout
=
(
vout_thread_t
*
)
p_this
;
free
(
p_vout
->
p_sys
->
pp_vout
);
free
(
p_vout
->
p_sys
);
}
/*****************************************************************************
* Render: displays previously rendered output
*****************************************************************************
* This function send the currently rendered image to Clone image, waits
* until it is displayed and switch the two rendering buffers, preparing next
* frame.
*****************************************************************************/
static
void
Render
(
vout_thread_t
*
p_vout
,
picture_t
*
p_pic
)
{
picture_t
*
p_outpic
=
NULL
;
int
i_vout
,
i_plane
;
for
(
i_vout
=
0
;
i_vout
<
p_vout
->
p_sys
->
i_clones
;
i_vout
++
)
{
while
(
(
p_outpic
=
vout_CreatePicture
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
0
,
0
,
0
)
)
==
NULL
)
{
if
(
!
vlc_object_alive
(
p_vout
)
||
p_vout
->
b_error
)
{
vout_DestroyPicture
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
p_outpic
);
return
;
}
msleep
(
VOUT_OUTMEM_SLEEP
);
}
p_outpic
->
date
=
p_pic
->
date
;
vout_LinkPicture
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
p_outpic
);
for
(
i_plane
=
0
;
i_plane
<
p_pic
->
i_planes
;
i_plane
++
)
{
uint8_t
*
p_in
,
*
p_in_end
,
*
p_out
;
int
i_in_pitch
=
p_pic
->
p
[
i_plane
].
i_pitch
;
const
int
i_out_pitch
=
p_outpic
->
p
[
i_plane
].
i_pitch
;
const
int
i_copy_pitch
=
p_outpic
->
p
[
i_plane
].
i_visible_pitch
;
p_in
=
p_pic
->
p
[
i_plane
].
p_pixels
;
p_out
=
p_outpic
->
p
[
i_plane
].
p_pixels
;
if
(
i_in_pitch
==
i_copy_pitch
&&
i_out_pitch
==
i_copy_pitch
)
{
vlc_memcpy
(
p_out
,
p_in
,
i_in_pitch
*
p_outpic
->
p
[
i_plane
].
i_visible_lines
);
}
else
{
p_in_end
=
p_in
+
i_in_pitch
*
p_outpic
->
p
[
i_plane
].
i_visible_lines
;
while
(
p_in
<
p_in_end
)
{
vlc_memcpy
(
p_out
,
p_in
,
i_copy_pitch
);
p_in
+=
i_in_pitch
;
p_out
+=
i_out_pitch
;
}
}
}
vout_UnlinkPicture
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
p_outpic
);
vout_DisplayPicture
(
p_vout
->
p_sys
->
pp_vout
[
i_vout
],
p_outpic
);
}
}
/*****************************************************************************
* RemoveAllVout: destroy all the child video output threads
*****************************************************************************/
static
void
RemoveAllVout
(
vout_thread_t
*
p_vout
)
{
{
v
out_sys_t
*
p_sys
=
p_vout
->
p_sy
s
;
v
ideo_splitter_t
*
p_splitter
=
(
video_splitter_t
*
)
p_thi
s
;
while
(
p_sys
->
i_clones
)
for
(
int
i
=
0
;
i
<
p_splitter
->
i_output
;
i
++
)
{
{
p_sys
->
i_clones
--
;
video_splitter_output_t
*
p_cfg
=
&
p_splitter
->
p_output
[
i
]
;
vout_filter_SetupChild
(
p_vout
,
p_sys
->
pp_vout
[
p_sys
->
i_clones
],
free
(
p_cfg
->
psz_module
);
NULL
,
FullscreenEventUp
,
FullscreenEventDown
,
false
);
video_format_Clean
(
&
p_cfg
->
fmt
);
vout_CloseAndRelease
(
p_sys
->
pp_vout
[
p_sys
->
i_clones
]
);
}
}
free
(
p_splitter
->
p_output
);
}
}
/**
/**
* Forward fullscreen event to/from the childrens.
* This function filter a picture
* FIXME pretty much duplicated from wall.c
*/
*/
static
bool
IsFullscreenActive
(
vout_thread_t
*
p_vout
)
static
int
Filter
(
video_splitter_t
*
p_splitter
,
picture_t
*
pp_dst
[],
picture_t
*
p_src
)
{
{
vout_sys_t
*
p_sys
=
p_vout
->
p_sys
;
if
(
video_splitter_NewPicture
(
p_splitter
,
pp_dst
)
)
for
(
int
i
=
0
;
i
<
p_sys
->
i_clones
;
i
++
)
return
VLC_EGENERIC
;
{
if
(
var_GetBool
(
p_sys
->
pp_vout
[
i
],
"fullscreen"
)
)
return
true
;
}
return
false
;
}
static
int
FullscreenEventUp
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
p_data
)
{
vout_thread_t
*
p_vout
=
p_data
;
VLC_UNUSED
(
oldval
);
VLC_UNUSED
(
p_this
);
VLC_UNUSED
(
psz_var
);
VLC_UNUSED
(
newval
);
const
bool
b_fullscreen
=
IsFullscreenActive
(
p_vout
);
if
(
!
var_GetBool
(
p_vout
,
"fullscreen"
)
!=
!
b_fullscreen
)
return
var_SetBool
(
p_vout
,
"fullscreen"
,
b_fullscreen
);
return
VLC_SUCCESS
;
}
static
int
FullscreenEventDown
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
p_data
)
{
vout_thread_t
*
p_vout
=
(
vout_thread_t
*
)
p_this
;
vout_sys_t
*
p_sys
=
p_vout
->
p_sys
;
VLC_UNUSED
(
oldval
);
VLC_UNUSED
(
p_data
);
VLC_UNUSED
(
psz_var
);
const
bool
b_fullscreen
=
IsFullscreenActive
(
p_vout
);
for
(
int
i
=
0
;
i
<
p_splitter
->
i_output
;
i
++
)
if
(
!
b_fullscreen
!=
!
newval
.
b_bool
)
picture_Copy
(
pp_dst
[
i
],
p_src
);
{
picture_Release
(
p_src
);
for
(
int
i
=
0
;
i
<
p_sys
->
i_clones
;
i
++
)
{
vout_thread_t
*
p_child
=
p_sys
->
pp_vout
[
i
];
if
(
!
var_GetBool
(
p_child
,
"fullscreen"
)
!=
!
newval
.
b_bool
)
{
var_SetBool
(
p_child
,
"fullscreen"
,
newval
.
b_bool
);
if
(
newval
.
b_bool
)
return
VLC_SUCCESS
;
}
}
}
return
VLC_SUCCESS
;
return
VLC_SUCCESS
;
}
}
modules/video_filter/wrapper.c
View file @
d0967dc4
...
@@ -50,6 +50,7 @@ static void Close( vlc_object_t * );
...
@@ -50,6 +50,7 @@ static void Close( vlc_object_t * );
DECLARE_OPEN
(
magnify
,
true
)
DECLARE_OPEN
(
magnify
,
true
)
DECLARE_OPEN
(
puzzle
,
true
)
DECLARE_OPEN
(
puzzle
,
true
)
DECLARE_OPEN
(
logo
,
true
)
DECLARE_OPEN
(
logo
,
true
)
DECLARE_OPEN
(
clone
,
false
)
#undef DECLARE_OPEN
#undef DECLARE_OPEN
...
@@ -72,6 +73,9 @@ vlc_module_begin()
...
@@ -72,6 +73,9 @@ vlc_module_begin()
add_submodule
()
add_submodule
()
DECLARE_MODULE
(
logo
)
DECLARE_MODULE
(
logo
)
add_submodule
()
DECLARE_MODULE
(
clone
)
vlc_module_end
()
vlc_module_end
()
#undef DECLARE_MODULE
#undef DECLARE_MODULE
...
...
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