Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Open sidebar
Martin Finkel
LibVLCSharp
Commits
8d736293
Commit
8d736293
authored
Mar 29, 2018
by
Martin Finkel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Tests now use the nuget package for libvlc. Added renderer APIs on Instance and MediaPlayer
parent
8c15518c
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
151 additions
and
107 deletions
+151
-107
LibVLCSharp.Tests/BaseSetup.cs
LibVLCSharp.Tests/BaseSetup.cs
+21
-0
LibVLCSharp.Tests/DialogTests.cs
LibVLCSharp.Tests/DialogTests.cs
+1
-1
LibVLCSharp.Tests/EqualizerTests.cs
LibVLCSharp.Tests/EqualizerTests.cs
+1
-1
LibVLCSharp.Tests/EventManagerTests.cs
LibVLCSharp.Tests/EventManagerTests.cs
+7
-18
LibVLCSharp.Tests/InstanceTests.cs
LibVLCSharp.Tests/InstanceTests.cs
+1
-1
LibVLCSharp.Tests/LibVLCSharp.Tests.csproj
LibVLCSharp.Tests/LibVLCSharp.Tests.csproj
+1
-0
LibVLCSharp.Tests/MediaDiscovererTests.cs
LibVLCSharp.Tests/MediaDiscovererTests.cs
+1
-1
LibVLCSharp.Tests/MediaListTests.cs
LibVLCSharp.Tests/MediaListTests.cs
+1
-1
LibVLCSharp.Tests/MediaPlayerTests.cs
LibVLCSharp.Tests/MediaPlayerTests.cs
+1
-1
LibVLCSharp.Tests/MediaTests.cs
LibVLCSharp.Tests/MediaTests.cs
+1
-3
LibVLCSharp.Tests/RendererDiscovererTests.cs
LibVLCSharp.Tests/RendererDiscovererTests.cs
+36
-18
LibVLCSharp.Tests/VersionCheckTests.cs
LibVLCSharp.Tests/VersionCheckTests.cs
+1
-1
LibVLCSharp/Instance.cs
LibVLCSharp/Instance.cs
+67
-11
LibVLCSharp/MediaPlayer.cs
LibVLCSharp/MediaPlayer.cs
+9
-0
LibVLCSharp/RendererDiscoverer.cs
LibVLCSharp/RendererDiscoverer.cs
+2
-50
No files found.
LibVLCSharp.Tests/BaseSetup.cs
0 → 100644
View file @
8d736293
using
System
;
using
System.IO
;
using
NUnit.Framework
;
using
VideoLAN.LibVLC
;
namespace
LibVLCSharp.Tests
{
public
abstract
class
BaseSetup
{
[
SetUp
]
public
void
SetUp
()
{
Core
.
Initialize
();
}
protected
string
RealStreamMediaPath
=>
"http://streams.videolan.org/streams/mp3/Owner-MPEG2.5.mp3"
;
protected
string
RealMp3Path
=>
Path
.
Combine
(
AppDomain
.
CurrentDomain
.
SetupInformation
.
ApplicationBase
,
"sample.mp3"
);
}
}
\ No newline at end of file
LibVLCSharp.Tests/DialogTests.cs
View file @
8d736293
...
...
@@ -5,7 +5,7 @@ using VideoLAN.LibVLC;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
DialogTests
public
class
DialogTests
:
BaseSetup
{
const
string
UrlRequireAuth
=
"http://httpbin.org/basic-auth/user/passwd"
;
const
string
Username
=
"username"
;
...
...
LibVLCSharp.Tests/EqualizerTests.cs
View file @
8d736293
...
...
@@ -4,7 +4,7 @@ using VideoLAN.LibVLC;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
EqualizerTests
public
class
EqualizerTests
:
BaseSetup
{
[
Test
]
public
void
BasicNativeCallTest
()
...
...
LibVLCSharp.Tests/EventManagerTests.cs
View file @
8d736293
...
...
@@ -9,7 +9,7 @@ using Media = VideoLAN.LibVLC.Media;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
EventManagerTests
public
class
EventManagerTests
:
BaseSetup
{
[
Test
]
public
void
MetaChangedEventSubscribe
()
...
...
@@ -32,7 +32,7 @@ namespace LibVLCSharp.Tests
{
// FIXME
var
instance
=
new
Instance
();
var
media
=
new
Media
(
instance
,
RealM
edia
Path
,
Media
.
FromType
.
FromPath
);
var
media
=
new
Media
(
instance
,
RealM
p3
Path
,
Media
.
FromType
.
FromPath
);
var
subItem
=
new
Media
(
instance
,
Path
.
GetTempFileName
(),
Media
.
FromType
.
FromPath
);
var
eventManager
=
media
.
EventManager
;
...
...
@@ -47,22 +47,11 @@ namespace LibVLCSharp.Tests
media
.
SubItems
.
Unlock
();
Assert
.
True
(
eventHandlerCalled
);
}
string
RealMediaPath
{
get
{
var
dir
=
AppDomain
.
CurrentDomain
.
SetupInformation
.
ApplicationBase
;
//var binDir = Path.Combine(dir, "..\\..\\..\\");
var
files
=
Directory
.
GetFiles
(
dir
);
return
files
.
First
(
f
=>
f
.
Contains
(
"Klang"
));
}
}
[
Test
]
public
void
DurationChanged
()
{
var
media
=
new
Media
(
new
Instance
(),
RealM
edia
Path
,
Media
.
FromType
.
FromPath
);
var
media
=
new
Media
(
new
Instance
(),
RealM
p3
Path
,
Media
.
FromType
.
FromPath
);
var
called
=
false
;
long
duration
=
0
;
...
...
@@ -81,7 +70,7 @@ namespace LibVLCSharp.Tests
[
Test
]
public
void
FreedMedia
()
{
var
media
=
new
Media
(
new
Instance
(),
RealM
edia
Path
,
Media
.
FromType
.
FromPath
);
var
media
=
new
Media
(
new
Instance
(),
RealM
p3
Path
,
Media
.
FromType
.
FromPath
);
var
eventCalled
=
false
;
media
.
EventManager
.
MediaFreed
+=
(
sender
,
args
)
=>
{
...
...
@@ -96,7 +85,7 @@ namespace LibVLCSharp.Tests
[
Test
]
public
async
Task
StateChanged
()
{
var
media
=
new
Media
(
new
Instance
(),
RealM
edia
Path
,
Media
.
FromType
.
FromPath
);
var
media
=
new
Media
(
new
Instance
(),
RealM
p3
Path
,
Media
.
FromType
.
FromPath
);
var
tcs
=
new
TaskCompletionSource
<
bool
>();
var
openingCalled
=
false
;
media
.
EventManager
.
StateChanged
+=
(
sender
,
args
)
=>
...
...
@@ -121,7 +110,7 @@ namespace LibVLCSharp.Tests
[
Test
]
public
void
SubItemTreeAdded
()
{
var
media
=
new
Media
(
new
Instance
(),
RealM
edia
Path
,
Media
.
FromType
.
FromPath
);
var
media
=
new
Media
(
new
Instance
(),
RealM
p3
Path
,
Media
.
FromType
.
FromPath
);
//TODO: Implement MediaList.cs
Assert
.
Fail
();
}
...
...
LibVLCSharp.Tests/InstanceTests.cs
View file @
8d736293
...
...
@@ -10,7 +10,7 @@ using VideoLAN.LibVLC.Events;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
InstanceTests
public
class
InstanceTests
:
BaseSetup
{
[
Test
]
public
void
DisposeInstanceNativeRelease
()
...
...
LibVLCSharp.Tests/LibVLCSharp.Tests.csproj
View file @
8d736293
...
...
@@ -11,6 +11,7 @@
<PackageReference Include="Moq" Version="4.7.142" />
<PackageReference Include="NUnit" Version="3.8.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibVLCSharp\LibVLCSharp.csproj" />
...
...
LibVLCSharp.Tests/MediaDiscovererTests.cs
View file @
8d736293
...
...
@@ -5,7 +5,7 @@ using VideoLAN.LibVLC;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
MediaDiscovererTests
public
class
MediaDiscovererTests
:
BaseSetup
{
[
Test
]
public
void
CreateStartAndStopDiscoverer
()
...
...
LibVLCSharp.Tests/MediaListTests.cs
View file @
8d736293
...
...
@@ -8,7 +8,7 @@ using MediaList = VideoLAN.LibVLC.MediaList;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
MediaListTests
public
class
MediaListTests
:
BaseSetup
{
[
Test
]
public
void
AddAndRemoveMediaFromMediaList
()
...
...
LibVLCSharp.Tests/MediaPlayerTests.cs
View file @
8d736293
...
...
@@ -11,7 +11,7 @@ using MediaPlayer = VideoLAN.LibVLC.MediaPlayer;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
MediaPlayerTests
public
class
MediaPlayerTests
:
BaseSetup
{
[
Test
]
public
void
CreateAndDestroy
()
...
...
LibVLCSharp.Tests/MediaTests.cs
View file @
8d736293
...
...
@@ -9,7 +9,7 @@ using Media = VideoLAN.LibVLC.Media;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
MediaTests
public
class
MediaTests
:
BaseSetup
{
[
Test
]
public
void
CreateMedia
()
...
...
@@ -75,8 +75,6 @@ namespace LibVLCSharp.Tests
}
}
string
RealStreamMediaPath
=>
"http://streams.videolan.org/streams/mp3/Owner-MPEG2.5.mp3"
;
[
Test
]
public
void
Duplicate
()
{
...
...
LibVLCSharp.Tests/RendererDiscovererTests.cs
View file @
8d736293
using
System.Linq
;
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Threading.Tasks
;
using
NUnit.Framework
;
using
VideoLAN.LibVLC
;
using
static
System
.
Diagnostics
.
Debug
;
using
Assert
=
NUnit
.
Framework
.
Assert
;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
RendererDiscovererTests
public
class
RendererDiscovererTests
:
BaseSetup
{
[
Test
]
public
async
Task
DiscoverItems
()
{
var
instance
=
new
Instanc
e
();
Core
.
Initializ
e
();
var
rendererDiscoverer
=
new
RendererDiscoverer
(
instance
,
"microdns"
);
var
em
=
rendererDiscoverer
.
EventManager
;
var
itemAdded
=
false
;
var
instance
=
new
Instance
(
new
[]{
"--verbose=2"
});
instance
.
Log
+=
(
sender
,
args
)
=>
{
WriteLine
(
args
.
Message
);
};
var
mp
=
new
MediaPlayer
(
instance
)
{
Media
=
new
Media
(
instance
,
"http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"
,
Media
.
FromType
.
FromLocation
)
};
// Assert.True(mp.Play());
var
rendererList
=
instance
.
RendererList
;
Assert
.
IsNotEmpty
(
rendererList
);
em
.
ItemAdded
+=
(
sender
,
args
)
=>
var
rendererDiscoverer
=
new
RendererDiscoverer
(
instance
,
/*"microdns"*/
rendererList
[
0
].
Name
);
var
rendererItems
=
new
List
<
RendererItem
>();
var
tcs
=
new
TaskCompletionSource
<
bool
>();
rendererDiscoverer
.
EventManager
.
ItemAdded
+=
(
sender
,
args
)
=>
{
WriteLine
(
$"New item discovered:
{
args
.
RendererItem
.
Name
}
of type
{
args
.
RendererItem
.
Type
}
"
);
if
(
args
.
RendererItem
.
CanRenderVideo
)
WriteLine
(
"Can render video"
);
if
(
args
.
RendererItem
.
CanRenderAudio
)
WriteLine
(
"Can render audio"
);
itemAdded
=
true
;
tcs
.
SetResult
(
true
);
rendererItems
.
Add
(
args
.
RendererItem
);
};
if
(!
rendererDiscoverer
.
Start
())
NUnit
.
Framework
.
Assert
.
Fail
();
await
Task
.
Delay
(
10000
);
Assert
.
True
(
rendererDiscoverer
.
Start
()
);
NUnit
.
Framework
.
Assert
.
True
(
itemAdded
);
}
//await Task.Delay(10000);
await
tcs
.
Task
;
Assert
.
True
(
tcs
.
Task
.
Result
);
Assert
.
IsNotEmpty
(
rendererItems
);
Assert
.
True
(
mp
.
SetRenderer
(
rendererItems
.
First
()));
[
Test
]
public
void
RetrieveListInformation
()
{
var
rd
=
new
RendererDiscoverer
(
new
Instance
(),
"rd"
);
NUnit
.
Framework
.
Assert
.
Positive
(
rd
.
List
.
Length
);
Console
.
ReadKey
();
}
}
}
LibVLCSharp.Tests/VersionCheckTests.cs
View file @
8d736293
...
...
@@ -4,7 +4,7 @@ using VideoLAN.LibVLC;
namespace
LibVLCSharp.Tests
{
[
TestFixture
]
public
class
VersionCheckTests
public
class
VersionCheckTests
:
BaseSetup
{
[
Test
]
public
void
ShouldThrowIfDllVersionNotHighEnough
()
...
...
LibVLCSharp/Instance.cs
View file @
8d736293
...
...
@@ -51,7 +51,7 @@ namespace VideoLAN.LibVLC
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_new"
)]
internal
static
extern
unsafe
IntPtr
LibVLCNew
(
int
argc
,
sbyte
**
argv
);
internal
static
extern
IntPtr
LibVLCNew
(
int
argc
,
IntPtr
[]
argv
);
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
...
...
@@ -150,6 +150,16 @@ namespace VideoLAN.LibVLC
EntryPoint
=
"libvlc_dialog_set_callbacks"
)]
internal
static
extern
void
LibVLCDialogSetCallbacks
(
IntPtr
instance
,
IntPtr
callbacks
,
IntPtr
data
);
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_renderer_discoverer_list_get"
)]
internal
static
extern
ulong
LibVLCRendererDiscovererGetList
(
IntPtr
instance
,
ref
IntPtr
discovererList
);
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_renderer_discoverer_list_release"
)]
internal
static
extern
void
LibVLCRendererDiscovererReleaseList
(
IntPtr
discovererList
,
ulong
count
);
#
region
Windows
/// <summary>
...
...
@@ -258,21 +268,30 @@ namespace VideoLAN.LibVLC
/// <para>cross-platform compatibility with regards to libvlc_new() arguments.</para>
/// <para>We recommend that you do not use them, other than when debugging.</para>
/// </remarks>
public
Instance
(
int
argc
=
0
,
string
[]
args
=
null
)
public
Instance
(
string
[]
args
=
null
)
:
base
(()
=>
{
unsafe
var
utf8Args
=
new
IntPtr
[
args
?.
Length
??
0
];
try
{
if
(
args
==
null
||
!
args
.
Any
())
return
Native
.
LibVLCNew
(
argc
,
null
);
fixed
(
byte
*
arg0
=
Encoding
.
ASCII
.
GetBytes
(
args
[
0
]),
arg1
=
Encoding
.
ASCII
.
GetBytes
(
args
[
1
]),
arg2
=
Encoding
.
ASCII
.
GetBytes
(
args
[
2
]))
for
(
var
i
=
0
;
i
<
utf8Args
.
Length
;
i
++)
{
sbyte
*[]
arr
=
{
(
sbyte
*)
arg0
,
(
sbyte
*)
arg1
,
(
sbyte
*)
arg2
};
fixed
(
sbyte
**
argv
=
arr
)
var
bytes
=
Encoding
.
UTF8
.
GetBytes
(
args
[
i
]);
var
buffer
=
Marshal
.
AllocHGlobal
(
bytes
.
Length
+
1
);
Marshal
.
Copy
(
bytes
,
0
,
buffer
,
bytes
.
Length
);
Marshal
.
WriteByte
(
buffer
,
bytes
.
Length
,
0
);
utf8Args
[
i
]
=
buffer
;
}
return
Native
.
LibVLCNew
(
utf8Args
.
Length
,
utf8Args
);
}
finally
{
foreach
(
var
arg
in
utf8Args
)
{
if
(
arg
!=
IntPtr
.
Zero
)
{
return
Native
.
LibVLCNew
(
argc
,
arg
v
);
Marshal
.
FreeHGlobal
(
arg
);
}
}
}
...
...
@@ -659,6 +678,43 @@ namespace VideoLAN.LibVLC
public
bool
DialogHandlersSet
=>
_dialogCbsPtr
!=
IntPtr
.
Zero
;
public
RendererDescription
[]
RendererList
{
get
{
// TODO: Move marshalling logic to generic MarshalUtils func
var
discoverList
=
IntPtr
.
Zero
;
var
count
=
Native
.
LibVLCRendererDiscovererGetList
(
NativeReference
,
ref
discoverList
);
if
(
count
==
0
)
return
Array
.
Empty
<
RendererDescription
>();
var
rendererDiscovererDescription
=
new
RendererDescription
[(
int
)
count
];
for
(
var
i
=
0
;
i
<
(
int
)
count
;
i
++)
{
var
ptr
=
Marshal
.
ReadIntPtr
(
discoverList
,
i
*
IntPtr
.
Size
);
var
managedStruct
=
(
RendererDescription
)
Marshal
.
PtrToStructure
(
ptr
,
typeof
(
RendererDescription
));
rendererDiscovererDescription
[
i
]
=
managedStruct
;
}
Native
.
LibVLCRendererDiscovererReleaseList
(
discoverList
,
count
);
return
rendererDiscovererDescription
;
}
}
public
struct
RendererDescription
{
public
string
Name
{
get
;
}
public
string
LongName
{
get
;
}
public
RendererDescription
(
string
name
,
string
longName
)
{
Name
=
name
;
LongName
=
longName
;
}
}
/// <summary>
/// Code taken from Vlc.DotNet
/// </summary>
...
...
LibVLCSharp/MediaPlayer.cs
View file @
8d736293
...
...
@@ -617,6 +617,11 @@ namespace VideoLAN.LibVLC
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_audio_output_device_list_release"
)]
internal
static
extern
void
LibVLCAudioOutputDeviceListRelease
(
IntPtr
list
);
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_media_player_set_renderer"
)]
internal
static
extern
int
LibVLCMediaPlayerSetRenderer
(
IntPtr
mediaplayer
,
IntPtr
renderItem
);
}
MediaPlayerEventManager
_eventManager
;
...
...
@@ -1589,6 +1594,10 @@ namespace VideoLAN.LibVLC
public
bool
UpdateViewpoint
(
VideoViewpoint
viewpoint
,
bool
absolute
)
=>
Native
.
LibVLCVideoUpdateViewpoint
(
NativeReference
,
viewpoint
,
absolute
)
==
0
;
[
LibVLC
(
3
)]
public
bool
SetRenderer
(
RendererItem
rendererItem
)
=>
Native
.
LibVLCMediaPlayerSetRenderer
(
NativeReference
,
rendererItem
.
NativeReference
)
==
0
;
#
region
Enums
...
...
LibVLCSharp/RendererDiscoverer.cs
View file @
8d736293
...
...
@@ -6,8 +6,6 @@ namespace VideoLAN.LibVLC
{
public
class
RendererDiscoverer
:
Internal
{
readonly
IntPtr
_instanceNativeReference
;
struct
Native
{
[
SuppressUnmanagedCodeSecurity
]
...
...
@@ -34,22 +32,12 @@ namespace VideoLAN.LibVLC
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_renderer_discoverer_event_manager"
)]
internal
static
extern
IntPtr
LibVLCRendererDiscovererEventManager
(
IntPtr
rendererDiscoverer
);
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_renderer_discoverer_list_get"
)]
internal
static
extern
ulong
LibVLCRendererDiscovererGetList
(
IntPtr
instance
,
ref
IntPtr
discovererList
);
[
SuppressUnmanagedCodeSecurity
]
[
DllImport
(
"libvlc"
,
CallingConvention
=
CallingConvention
.
Cdecl
,
EntryPoint
=
"libvlc_renderer_discoverer_list_release"
)]
internal
static
extern
void
LibVLCRendererDiscovererReleaseList
(
IntPtr
discovererList
,
ulong
count
);
}
public
RendererDiscoverer
(
Instance
instance
,
string
name
)
:
base
(()
=>
Native
.
LibVLCRendererDiscovererNew
(
instance
.
NativeReference
,
name
),
Native
.
LibVLCRendererDiscovererRelease
)
:
base
(()
=>
Native
.
LibVLCRendererDiscovererNew
(
instance
.
NativeReference
,
name
),
Native
.
LibVLCRendererDiscovererRelease
)
{
_instanceNativeReference
=
instance
.
NativeReference
;
}
RendererDiscovererEventManager
_eventManager
;
...
...
@@ -70,42 +58,6 @@ namespace VideoLAN.LibVLC
public
bool
Start
()
=>
Native
.
LibVLCRendererDiscovererStart
(
NativeReference
)
==
0
;
public
void
Stop
()
=>
Native
.
LibVLCRendererDiscovererStop
(
NativeReference
);
public
Description
[]
List
{
get
{
var
discoverList
=
IntPtr
.
Zero
;
var
count
=
Native
.
LibVLCRendererDiscovererGetList
(
_instanceNativeReference
,
ref
discoverList
);
if
(
count
==
0
)
return
Array
.
Empty
<
Description
>();
var
rendererDiscovererDescription
=
new
Description
[(
int
)
count
];
for
(
var
i
=
0
;
i
<
(
int
)
count
;
i
++)
{
var
ptr
=
Marshal
.
ReadIntPtr
(
discoverList
,
i
*
IntPtr
.
Size
);
var
managedStruct
=
(
Description
)
Marshal
.
PtrToStructure
(
ptr
,
typeof
(
Description
));
rendererDiscovererDescription
[
i
]
=
managedStruct
;
}
Native
.
LibVLCRendererDiscovererReleaseList
(
discoverList
,
count
);
return
rendererDiscovererDescription
;
}
}
public
struct
Description
{
public
string
Name
{
get
;
}
public
string
LongName
{
get
;
}
public
Description
(
string
name
,
string
longName
)
{
Name
=
name
;
LongName
=
longName
;
}
}
}
public
class
RendererItem
:
Internal
...
...
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