Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
65eae2a7
Commit
65eae2a7
authored
Jun 25, 2011
by
Jonathan Baldwin
Committed by
Stefano Sabatini
Jun 27, 2011
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
lavdev: add openal input device
parent
cdeb803e
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
362 additions
and
2 deletions
+362
-2
Changelog
Changelog
+4
-0
configure
configure
+9
-0
indevs.texi
doc/indevs.texi
+89
-0
Makefile
libavdevice/Makefile
+1
-0
alldevices.c
libavdevice/alldevices.c
+1
-0
avdevice.h
libavdevice/avdevice.h
+2
-2
openal-dec.c
libavdevice/openal-dec.c
+256
-0
No files found.
Changelog
View file @
65eae2a7
Entries are sorted chronologically from oldest to youngest within each release,
Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
releases are sorted from youngest to oldest.
version next:
- openal input device added
version 0.8:
version 0.8:
...
...
configure
View file @
65eae2a7
...
@@ -188,6 +188,7 @@ External library support:
...
@@ -188,6 +188,7 @@ External library support:
--enable-libxavs enable AVS encoding via xavs [no]
--enable-libxavs enable AVS encoding via xavs [no]
--enable-libxvid enable Xvid encoding via xvidcore,
--enable-libxvid enable Xvid encoding via xvidcore,
native MPEG-4/Xvid encoder exists [no]
native MPEG-4/Xvid encoder exists [no]
--enable-openal enable OpenAL 1.1 capture support [no]
--enable-mlib enable Sun medialib [no]
--enable-mlib enable Sun medialib [no]
--enable-zlib enable zlib [autodetect]
--enable-zlib enable zlib [autodetect]
...
@@ -959,6 +960,7 @@ CONFIG_LIST="
...
@@ -959,6 +960,7 @@ CONFIG_LIST="
mpegaudiodsp
mpegaudiodsp
network
network
nonfree
nonfree
openal
pic
pic
postproc
postproc
rdft
rdft
...
@@ -1471,6 +1473,7 @@ dv1394_indev_deps="dv1394 dv_demuxer"
...
@@ -1471,6 +1473,7 @@ dv1394_indev_deps="dv1394 dv_demuxer"
fbdev_indev_deps
=
"linux_fb_h"
fbdev_indev_deps
=
"linux_fb_h"
jack_indev_deps
=
"jack_jack_h sem_timedwait"
jack_indev_deps
=
"jack_jack_h sem_timedwait"
libdc1394_indev_deps
=
"libdc1394"
libdc1394_indev_deps
=
"libdc1394"
openal_indev_deps
=
"openal"
oss_indev_deps_any
=
"soundcard_h sys_soundcard_h"
oss_indev_deps_any
=
"soundcard_h sys_soundcard_h"
oss_outdev_deps_any
=
"soundcard_h sys_soundcard_h"
oss_outdev_deps_any
=
"soundcard_h sys_soundcard_h"
sdl_outdev_deps
=
"sdl"
sdl_outdev_deps
=
"sdl"
...
@@ -2948,6 +2951,11 @@ enabled libx264 && require libx264 x264.h x264_encoder_encode -lx264 &&
...
@@ -2948,6 +2951,11 @@ enabled libx264 && require libx264 x264.h x264_encoder_encode -lx264 &&
die
"ERROR: libx264 version must be >= 0.115."
;
}
die
"ERROR: libx264 version must be >= 0.115."
;
}
enabled libxavs
&&
require libxavs xavs.h xavs_encoder_encode
-lxavs
enabled libxavs
&&
require libxavs xavs.h xavs_encoder_encode
-lxavs
enabled libxvid
&&
require libxvid xvid.h xvid_global
-lxvidcore
enabled libxvid
&&
require libxvid xvid.h xvid_global
-lxvidcore
enabled openal
&&
{
{
for
al_libs
in
"
${
OPENAL_LIBS
}
"
"-lopenal"
"-lOpenAL32"
;
do
check_lib
'AL/al.h'
alGetError
"
${
al_libs
}
"
&&
break
;
done
}
||
die
"ERROR: openal not found"
;
}
&&
{
check_cpp_condition
"AL/al.h"
"defined(AL_VERSION_1_1)"
||
die
"ERROR: openal version must be 1.1 or compatible"
;
}
enabled mlib
&&
require mediaLib mlib_types.h mlib_VectorSub_S16_U8_Mod
-lmlib
enabled mlib
&&
require mediaLib mlib_types.h mlib_VectorSub_S16_U8_Mod
-lmlib
SDL_CONFIG
=
"
${
cross_prefix
}
sdl-config"
SDL_CONFIG
=
"
${
cross_prefix
}
sdl-config"
...
@@ -3247,6 +3255,7 @@ echo "libvpx enabled ${libvpx-no}"
...
@@ -3247,6 +3255,7 @@ echo "libvpx enabled ${libvpx-no}"
echo
"libx264 enabled
${
libx264
-no
}
"
echo
"libx264 enabled
${
libx264
-no
}
"
echo
"libxavs enabled
${
libxavs
-no
}
"
echo
"libxavs enabled
${
libxavs
-no
}
"
echo
"libxvid enabled
${
libxvid
-no
}
"
echo
"libxvid enabled
${
libxvid
-no
}
"
echo
"openal enabled
${
openal
-no
}
"
echo
"zlib enabled
${
zlib
-no
}
"
echo
"zlib enabled
${
zlib
-no
}
"
echo
"bzlib enabled
${
bzlib
-no
}
"
echo
"bzlib enabled
${
bzlib
-no
}
"
echo
echo
...
...
doc/indevs.texi
View file @
65eae2a7
...
@@ -137,6 +137,95 @@ For more information read:
...
@@ -137,6 +137,95 @@ For more information read:
IIDC1394 input device, based on libdc1394 and libraw1394.
IIDC1394 input device, based on libdc1394 and libraw1394.
@section openal
The OpenAL input device provides audio capture on all systems with a
working OpenAL 1.1 implementation.
To enable this input device during configuration, you need OpenAL
headers and libraries installed on your system, and need to configure
FFmpeg with @code{--enable-openal}.
OpenAL headers and libraries should be provided as part of your OpenAL
implementation, or as an additional download (an SDK). Depending on your
installation you may need to specify additional flags via the
@code{--extra-cflags} and @code{--extra-ldflags} for allowing the build
system to locate the OpenAL headers and libraries.
An incomplete list of OpenAL implementations follows:
@table @strong
@item Creative
The official Windows implementation, providing hardware acceleration
with supported devices and software fallback.
See @url{http://openal.org/}.
@item OpenAL Soft
Portable, open source (LGPL) software implementation. Includes
backends for the most common sound APIs on the Windows, Linux,
Solaris, and BSD operating systems.
See @url{http://kcat.strangesoft.net/openal.html}.
@item Apple
OpenAL is part of Core Audio, the official Mac OS X Audio interface.
See @url{http://developer.apple.com/technologies/mac/audio-and-video.html}
@end table
This device allows to capture from an audio input device handled
through OpenAL.
You need to specify the name of the device to capture in the provided
filename. If the empty string is provided, the device will
automatically select the default device. You can get the list of the
supported devices by using the option @var{list_devices}.
@subsection Options
@table @option
@item channels
Set the number of channels in the captured audio. Only the values
@option{1} (monaural) and @option{2} (stereo) are currently supported.
Defaults to @option{2}.
@item sample_size
Set the sample size (in bits) of the captured audio. Only the values
@option{8} and @option{16} are currently supported. Defaults to
@option{16}.
@item sample_rate
Set the sample rate (in Hz) of the captured audio.
Defaults to @option{44.1k}.
@item list_devices
If set to @option{true}, print a list of devices and exit.
Defaults to @option{false}.
@end table
@subsection Examples
Print the list of OpenAL supported devices and exit:
@example
$ ffmpeg -list_devices true -f openal -i dummy out.ogg
@end example
Capture from the OpenAL device @file{DR-BT101 via PulseAudio}:
@example
$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' out.ogg
@end example
Capture from the default device (note the empty string '' as filename):
@example
$ ffmpeg -f openal -i '' out.ogg
@end example
Capture from two devices simultaneously, writing to two different files,
within the same @file{ffmpeg} command:
@example
$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' out1.ogg -f openal -i 'ALSA Default' out2.ogg
@end example
Note: not all OpenAL implementations support multiple simultaneous capture -
try the latest OpenAL Soft if the above does not work.
@section oss
@section oss
Open Sound System input device.
Open Sound System input device.
...
...
libavdevice/Makefile
View file @
65eae2a7
...
@@ -19,6 +19,7 @@ OBJS-$(CONFIG_DSHOW_INDEV) += dshow.o dshow_enummediatypes.o \
...
@@ -19,6 +19,7 @@ OBJS-$(CONFIG_DSHOW_INDEV) += dshow.o dshow_enummediatypes.o \
OBJS-$(CONFIG_DV1394_INDEV)
+=
dv1394.o
OBJS-$(CONFIG_DV1394_INDEV)
+=
dv1394.o
OBJS-$(CONFIG_FBDEV_INDEV)
+=
fbdev.o
OBJS-$(CONFIG_FBDEV_INDEV)
+=
fbdev.o
OBJS-$(CONFIG_JACK_INDEV)
+=
jack_audio.o
OBJS-$(CONFIG_JACK_INDEV)
+=
jack_audio.o
OBJS-$(CONFIG_OPENAL_INDEV)
+=
openal-dec.o
OBJS-$(CONFIG_OSS_INDEV)
+=
oss_audio.o
OBJS-$(CONFIG_OSS_INDEV)
+=
oss_audio.o
OBJS-$(CONFIG_OSS_OUTDEV)
+=
oss_audio.o
OBJS-$(CONFIG_OSS_OUTDEV)
+=
oss_audio.o
OBJS-$(CONFIG_SDL_OUTDEV)
+=
sdl.o
OBJS-$(CONFIG_SDL_OUTDEV)
+=
sdl.o
...
...
libavdevice/alldevices.c
View file @
65eae2a7
...
@@ -44,6 +44,7 @@ void avdevice_register_all(void)
...
@@ -44,6 +44,7 @@ void avdevice_register_all(void)
REGISTER_INDEV
(
DV1394
,
dv1394
);
REGISTER_INDEV
(
DV1394
,
dv1394
);
REGISTER_INDEV
(
FBDEV
,
fbdev
);
REGISTER_INDEV
(
FBDEV
,
fbdev
);
REGISTER_INDEV
(
JACK
,
jack
);
REGISTER_INDEV
(
JACK
,
jack
);
REGISTER_INDEV
(
OPENAL
,
openal
);
REGISTER_INOUTDEV
(
OSS
,
oss
);
REGISTER_INOUTDEV
(
OSS
,
oss
);
REGISTER_OUTDEV
(
SDL
,
sdl
);
REGISTER_OUTDEV
(
SDL
,
sdl
);
REGISTER_INOUTDEV
(
SNDIO
,
sndio
);
REGISTER_INOUTDEV
(
SNDIO
,
sndio
);
...
...
libavdevice/avdevice.h
View file @
65eae2a7
...
@@ -23,8 +23,8 @@
...
@@ -23,8 +23,8 @@
#include "libavformat/avformat.h"
#include "libavformat/avformat.h"
#define LIBAVDEVICE_VERSION_MAJOR 53
#define LIBAVDEVICE_VERSION_MAJOR 53
#define LIBAVDEVICE_VERSION_MINOR
1
#define LIBAVDEVICE_VERSION_MINOR
2
#define LIBAVDEVICE_VERSION_MICRO
1
#define LIBAVDEVICE_VERSION_MICRO
0
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
LIBAVDEVICE_VERSION_MINOR, \
LIBAVDEVICE_VERSION_MINOR, \
...
...
libavdevice/openal-dec.c
0 → 100644
View file @
65eae2a7
/*
* Copyright (c) 2011 Jonathan Baldwin
*
* This file is part of FFmpeg.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @file
* OpenAL 1.1 capture device for libavdevice
**/
#include <AL/al.h>
#include <AL/alc.h>
#include "libavutil/opt.h"
#include "avdevice.h"
typedef
struct
{
AVClass
*
class
;
/** OpenAL capture device context. **/
ALCdevice
*
device
;
/** The number of channels in the captured audio. **/
int
channels
;
/** The sample rate (in Hz) of the captured audio. **/
int
sample_rate
;
/** The sample size (in bits) of the captured audio. **/
int
sample_size
;
/** The OpenAL sample format of the captured audio. **/
ALCenum
sample_format
;
/** The number of bytes between two consecutive samples of the same channel/component. **/
ALCint
sample_step
;
/** If true, print a list of capture devices on this system and exit. **/
int
list_devices
;
}
al_data
;
typedef
struct
{
ALCenum
al_fmt
;
enum
CodecID
codec_id
;
int
channels
;
}
al_format_info
;
#define LOWEST_AL_FORMAT FFMIN(FFMIN(AL_FORMAT_MONO8,AL_FORMAT_MONO16),FFMIN(AL_FORMAT_STEREO8,AL_FORMAT_STEREO16))
/**
* Get information about an AL_FORMAT value.
* @param al_fmt the AL_FORMAT value to find information about.
* @return A pointer to a structure containing information about the AL_FORMAT value.
*/
static
inline
al_format_info
*
get_al_format_info
(
ALCenum
al_fmt
)
{
static
al_format_info
info_table
[]
=
{
[
AL_FORMAT_MONO8
-
LOWEST_AL_FORMAT
]
=
{
AL_FORMAT_MONO8
,
CODEC_ID_PCM_U8
,
1
},
[
AL_FORMAT_MONO16
-
LOWEST_AL_FORMAT
]
=
{
AL_FORMAT_MONO16
,
AV_NE
(
CODEC_ID_PCM_S16BE
,
CODEC_ID_PCM_S16LE
),
1
},
[
AL_FORMAT_STEREO8
-
LOWEST_AL_FORMAT
]
=
{
AL_FORMAT_STEREO8
,
CODEC_ID_PCM_U8
,
2
},
[
AL_FORMAT_STEREO16
-
LOWEST_AL_FORMAT
]
=
{
AL_FORMAT_STEREO16
,
AV_NE
(
CODEC_ID_PCM_S16BE
,
CODEC_ID_PCM_S16LE
),
2
},
};
return
&
info_table
[
al_fmt
-
LOWEST_AL_FORMAT
];
}
/**
* Get the OpenAL error code, translated into an av/errno error code.
* @param device The ALC device to check for errors.
* @param error_msg_ret A pointer to a char* in which to return the error message, or NULL if desired.
* @return The error code, or 0 if there is no error.
*/
static
inline
int
al_get_error
(
ALCdevice
*
device
,
const
char
**
error_msg_ret
)
{
ALCenum
error
=
alcGetError
(
device
);
if
(
error_msg_ret
)
*
error_msg_ret
=
(
const
char
*
)
alcGetString
(
device
,
error
);
switch
(
error
)
{
case
ALC_NO_ERROR
:
return
0
;
case
ALC_INVALID_DEVICE
:
return
AVERROR
(
ENODEV
);
break
;
case
ALC_INVALID_CONTEXT
:
case
ALC_INVALID_ENUM
:
case
ALC_INVALID_VALUE
:
return
AVERROR
(
EINVAL
);
break
;
case
ALC_OUT_OF_MEMORY
:
return
AVERROR
(
ENOMEM
);
break
;
default:
return
AVERROR
(
EIO
);
}
}
/**
* Print out a list of OpenAL capture devices on this system.
*/
static
inline
void
print_al_capture_devices
(
void
*
log_ctx
)
{
const
char
*
devices
;
if
(
!
(
devices
=
alcGetString
(
NULL
,
ALC_CAPTURE_DEVICE_SPECIFIER
)))
return
;
av_log
(
log_ctx
,
AV_LOG_INFO
,
"List of OpenAL capture devices on this system:
\n
"
);
for
(;
*
devices
!=
'\0'
;
devices
+=
strlen
(
devices
)
+
1
)
av_log
(
log_ctx
,
AV_LOG_INFO
,
" %s
\n
"
,
devices
);
}
static
int
read_header
(
AVFormatContext
*
ctx
,
AVFormatParameters
*
ap
)
{
al_data
*
ad
=
ctx
->
priv_data
;
static
const
ALCenum
sample_formats
[
2
][
2
]
=
{
{
AL_FORMAT_MONO8
,
AL_FORMAT_STEREO8
},
{
AL_FORMAT_MONO16
,
AL_FORMAT_STEREO16
}
};
int
error
=
0
;
const
char
*
error_msg
;
AVStream
*
st
=
NULL
;
AVCodecContext
*
codec
=
NULL
;
if
(
ad
->
list_devices
)
{
print_al_capture_devices
(
ctx
);
return
AVERROR_EXIT
;
}
ad
->
sample_format
=
sample_formats
[
ad
->
sample_size
/
8
-
1
][
ad
->
channels
-
1
];
/* Open device for capture */
ad
->
device
=
alcCaptureOpenDevice
(
ctx
->
filename
[
0
]
?
ctx
->
filename
:
NULL
,
ad
->
sample_rate
,
ad
->
sample_format
,
ad
->
sample_rate
);
/* Maximum 1 second of sample data to be read at once */
if
(
error
=
al_get_error
(
ad
->
device
,
&
error_msg
))
goto
fail
;
/* Create stream */
if
(
!
(
st
=
av_new_stream
(
ctx
,
0
)))
{
error
=
AVERROR
(
ENOMEM
);
goto
fail
;
}
/* We work in microseconds */
av_set_pts_info
(
st
,
64
,
1
,
1000000
);
/* Set codec parameters */
codec
=
st
->
codec
;
codec
->
codec_type
=
AVMEDIA_TYPE_AUDIO
;
codec
->
sample_rate
=
ad
->
sample_rate
;
codec
->
channels
=
get_al_format_info
(
ad
->
sample_format
)
->
channels
;
codec
->
codec_id
=
get_al_format_info
(
ad
->
sample_format
)
->
codec_id
;
/* This is needed to read the audio data */
ad
->
sample_step
=
(
av_get_bits_per_sample
(
get_al_format_info
(
ad
->
sample_format
)
->
codec_id
)
*
get_al_format_info
(
ad
->
sample_format
)
->
channels
)
/
8
;
/* Finally, start the capture process */
alcCaptureStart
(
ad
->
device
);
return
0
;
fail:
/* Handle failure */
if
(
ad
->
device
)
alcCaptureCloseDevice
(
ad
->
device
);
if
(
error_msg
)
av_log
(
ctx
,
AV_LOG_ERROR
,
"Cannot open device: %s
\n
"
,
error_msg
);
return
error
;
}
static
int
read_packet
(
AVFormatContext
*
ctx
,
AVPacket
*
pkt
)
{
al_data
*
ad
=
ctx
->
priv_data
;
int
error
=
0
;
const
char
*
error_msg
;
ALCint
nb_samples
;
/* Get number of samples available */
alcGetIntegerv
(
ad
->
device
,
ALC_CAPTURE_SAMPLES
,
(
ALCsizei
)
sizeof
(
ALCint
),
&
nb_samples
);
if
(
error
=
al_get_error
(
ad
->
device
,
&
error_msg
))
goto
fail
;
/* Create a packet of appropriate size */
av_new_packet
(
pkt
,
nb_samples
*
ad
->
sample_step
);
pkt
->
pts
=
av_gettime
();
/* Fill the packet with the available samples */
alcCaptureSamples
(
ad
->
device
,
pkt
->
data
,
nb_samples
);
if
(
error
=
al_get_error
(
ad
->
device
,
&
error_msg
))
goto
fail
;
return
pkt
->
size
;
fail:
/* Handle failure */
if
(
pkt
->
data
)
av_destruct_packet
(
pkt
);
if
(
error_msg
)
av_log
(
ctx
,
AV_LOG_ERROR
,
"Error: %s
\n
"
,
error_msg
);
return
error
;
}
static
int
read_close
(
AVFormatContext
*
ctx
)
{
al_data
*
ad
=
ctx
->
priv_data
;
if
(
ad
->
device
)
{
alcCaptureStop
(
ad
->
device
);
alcCaptureCloseDevice
(
ad
->
device
);
}
return
0
;
}
#define OFFSET(x) offsetof(al_data, x)
static
const
AVOption
options
[]
=
{
{
"channels"
,
"set number of channels"
,
OFFSET
(
channels
),
FF_OPT_TYPE_INT
,
{.
dbl
=
2
},
1
,
2
,
AV_OPT_FLAG_DECODING_PARAM
},
{
"sample_rate"
,
"set sample rate"
,
OFFSET
(
sample_rate
),
FF_OPT_TYPE_INT
,
{.
dbl
=
44100
},
1
,
192000
,
AV_OPT_FLAG_DECODING_PARAM
},
{
"sample_size"
,
"set sample size"
,
OFFSET
(
sample_size
),
FF_OPT_TYPE_INT
,
{.
dbl
=
16
},
8
,
16
,
AV_OPT_FLAG_DECODING_PARAM
},
{
"list_devices"
,
"list available devices"
,
OFFSET
(
list_devices
),
FF_OPT_TYPE_INT
,
{.
dbl
=
0
},
0
,
1
,
AV_OPT_FLAG_DECODING_PARAM
},
{
"true"
,
""
,
0
,
FF_OPT_TYPE_CONST
,
{.
dbl
=
1
},
0
,
0
,
AV_OPT_FLAG_DECODING_PARAM
,
"list_devices"
},
{
"false"
,
""
,
0
,
FF_OPT_TYPE_CONST
,
{.
dbl
=
0
},
0
,
0
,
AV_OPT_FLAG_DECODING_PARAM
,
"list_devices"
},
{
NULL
},
};
static
const
AVClass
class
=
{
.
class_name
=
"openal"
,
.
item_name
=
av_default_item_name
,
.
option
=
options
,
.
version
=
LIBAVUTIL_VERSION_INT
};
AVInputFormat
ff_openal_demuxer
=
{
.
name
=
"openal"
,
.
long_name
=
NULL_IF_CONFIG_SMALL
(
"OpenAL audio capture device"
),
.
priv_data_size
=
sizeof
(
al_data
),
.
read_probe
=
NULL
,
.
read_header
=
read_header
,
.
read_packet
=
read_packet
,
.
read_close
=
read_close
,
.
flags
=
AVFMT_NOFILE
,
.
priv_class
=
&
class
};
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