Commit b62ae460 authored by Jose Lopes's avatar Jose Lopes Committed by Commit Bot

media: Migrate EmitBufferCB to repeating callback.

This is a repeating callback because it's called from a while loop:
  * https://cs.chromium.org/chromium/src/media/formats/mp2t/es_adapter_video.cc?rcl=2d18a6603bb1f5bbe784dad83e7cad005808686a&l=166

This is part of the base::Callback migration.

Context: https://cs.chromium.org/chromium/src/docs/callback.md?rcl=9fcc3764aea8f97e9f6de4a9ee61d554e67edcda&l=40

Bug: 714018
Change-Id: I3b7dfb4b58ec6918ebc4dc504b839bddd115c64a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2080261
Commit-Queue: Jose Lopes <jabolopes@google.com>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746706}
parent ac90dafc
...@@ -26,9 +26,9 @@ static const int kDefaultFrameDurationMs = 40; ...@@ -26,9 +26,9 @@ static const int kDefaultFrameDurationMs = 40;
static const size_t kHistorySize = 5; static const size_t kHistorySize = 5;
EsAdapterVideo::EsAdapterVideo(NewVideoConfigCB new_video_config_cb, EsAdapterVideo::EsAdapterVideo(NewVideoConfigCB new_video_config_cb,
const EmitBufferCB& emit_buffer_cb) EmitBufferCB emit_buffer_cb)
: new_video_config_cb_(std::move(new_video_config_cb)), : new_video_config_cb_(std::move(new_video_config_cb)),
emit_buffer_cb_(emit_buffer_cb), emit_buffer_cb_(std::move(emit_buffer_cb)),
has_valid_config_(false), has_valid_config_(false),
has_valid_frame_(false), has_valid_frame_(false),
last_frame_duration_( last_frame_duration_(
......
...@@ -35,10 +35,11 @@ class MEDIA_EXPORT EsAdapterVideo { ...@@ -35,10 +35,11 @@ class MEDIA_EXPORT EsAdapterVideo {
public: public:
using NewVideoConfigCB = using NewVideoConfigCB =
base::RepeatingCallback<void(const VideoDecoderConfig&)>; base::RepeatingCallback<void(const VideoDecoderConfig&)>;
typedef base::Callback<void(scoped_refptr<StreamParserBuffer>)> EmitBufferCB; using EmitBufferCB =
base::RepeatingCallback<void(scoped_refptr<StreamParserBuffer>)>;
EsAdapterVideo(NewVideoConfigCB new_video_config_cb, EsAdapterVideo(NewVideoConfigCB new_video_config_cb,
const EmitBufferCB& emit_buffer_cb); EmitBufferCB emit_buffer_cb);
~EsAdapterVideo(); ~EsAdapterVideo();
// Force the emission of the pending video buffers. // Force the emission of the pending video buffers.
......
...@@ -28,7 +28,8 @@ namespace mp2t { ...@@ -28,7 +28,8 @@ namespace mp2t {
class MEDIA_EXPORT EsParser { class MEDIA_EXPORT EsParser {
public: public:
using EmitBufferCB = base::Callback<void(scoped_refptr<StreamParserBuffer>)>; using EmitBufferCB =
base::RepeatingCallback<void(scoped_refptr<StreamParserBuffer>)>;
using GetDecryptConfigCB = base::Callback<const DecryptConfig*()>; using GetDecryptConfigCB = base::Callback<const DecryptConfig*()>;
EsParser(); EsParser();
......
...@@ -116,10 +116,10 @@ void EsParserAdts::SkipAdtsFrame(const AdtsFrame& adts_frame) { ...@@ -116,10 +116,10 @@ void EsParserAdts::SkipAdtsFrame(const AdtsFrame& adts_frame) {
} }
EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb, EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
bool sbr_in_mimetype) bool sbr_in_mimetype)
: new_audio_config_cb_(new_audio_config_cb), : new_audio_config_cb_(new_audio_config_cb),
emit_buffer_cb_(emit_buffer_cb), emit_buffer_cb_(std::move(emit_buffer_cb)),
#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
get_decrypt_config_cb_(), get_decrypt_config_cb_(),
init_encryption_scheme_(EncryptionScheme::kUnencrypted), init_encryption_scheme_(EncryptionScheme::kUnencrypted),
...@@ -129,12 +129,12 @@ EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb, ...@@ -129,12 +129,12 @@ EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb,
#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb, EsParserAdts::EsParserAdts(const NewAudioConfigCB& new_audio_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
const GetDecryptConfigCB& get_decrypt_config_cb, const GetDecryptConfigCB& get_decrypt_config_cb,
EncryptionScheme init_encryption_scheme, EncryptionScheme init_encryption_scheme,
bool sbr_in_mimetype) bool sbr_in_mimetype)
: new_audio_config_cb_(new_audio_config_cb), : new_audio_config_cb_(new_audio_config_cb),
emit_buffer_cb_(emit_buffer_cb), emit_buffer_cb_(std::move(emit_buffer_cb)),
get_decrypt_config_cb_(get_decrypt_config_cb), get_decrypt_config_cb_(get_decrypt_config_cb),
init_encryption_scheme_(init_encryption_scheme), init_encryption_scheme_(init_encryption_scheme),
sbr_in_mimetype_(sbr_in_mimetype) {} sbr_in_mimetype_(sbr_in_mimetype) {}
......
...@@ -34,11 +34,11 @@ class MEDIA_EXPORT EsParserAdts : public EsParser { ...@@ -34,11 +34,11 @@ class MEDIA_EXPORT EsParserAdts : public EsParser {
typedef base::Callback<void(const AudioDecoderConfig&)> NewAudioConfigCB; typedef base::Callback<void(const AudioDecoderConfig&)> NewAudioConfigCB;
EsParserAdts(const NewAudioConfigCB& new_audio_config_cb, EsParserAdts(const NewAudioConfigCB& new_audio_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
bool sbr_in_mimetype); bool sbr_in_mimetype);
#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
EsParserAdts(const NewAudioConfigCB& new_audio_config_cb, EsParserAdts(const NewAudioConfigCB& new_audio_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
const GetDecryptConfigCB& get_decrypt_config_cb, const GetDecryptConfigCB& get_decrypt_config_cb,
EncryptionScheme init_encryption_scheme, EncryptionScheme init_encryption_scheme,
bool sbr_in_mimetype); bool sbr_in_mimetype);
......
...@@ -14,7 +14,7 @@ static void EmitBuffer(scoped_refptr<media::StreamParserBuffer> buffer) {} ...@@ -14,7 +14,7 @@ static void EmitBuffer(scoped_refptr<media::StreamParserBuffer> buffer) {}
// Entry point for LibFuzzer. // Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
media::mp2t::EsParserAdts es_parser(base::Bind(&NewAudioConfig), media::mp2t::EsParserAdts es_parser(base::Bind(&NewAudioConfig),
base::Bind(&EmitBuffer), true); base::BindRepeating(&EmitBuffer), true);
if (!es_parser.Parse(data, size, media::kNoTimestamp, if (!es_parser.Parse(data, size, media::kNoTimestamp,
media::kNoDecodeTimestamp())) { media::kNoDecodeTimestamp())) {
return 0; return 0;
......
...@@ -40,7 +40,8 @@ bool EsParserAdtsTest::Process(const std::vector<Packet>& pes_packets, ...@@ -40,7 +40,8 @@ bool EsParserAdtsTest::Process(const std::vector<Packet>& pes_packets,
bool sbr_in_mimetype) { bool sbr_in_mimetype) {
EsParserAdts es_parser( EsParserAdts es_parser(
base::Bind(&EsParserAdtsTest::NewAudioConfig, base::Unretained(this)), base::Bind(&EsParserAdtsTest::NewAudioConfig, base::Unretained(this)),
base::Bind(&EsParserAdtsTest::EmitBuffer, base::Unretained(this)), base::BindRepeating(&EsParserAdtsTest::EmitBuffer,
base::Unretained(this)),
sbr_in_mimetype); sbr_in_mimetype);
return ProcessPesPackets(&es_parser, pes_packets, false /* force_timing */); return ProcessPesPackets(&es_parser, pes_packets, false /* force_timing */);
} }
...@@ -87,4 +88,3 @@ TEST_F(EsParserAdtsTest, AacSampleRate) { ...@@ -87,4 +88,3 @@ TEST_F(EsParserAdtsTest, AacSampleRate) {
} }
} // namespace mp2t } // namespace mp2t
} // namespace media } // namespace media
...@@ -178,8 +178,8 @@ std::unique_ptr<uint8_t[]> AdjustAUForSampleAES( ...@@ -178,8 +178,8 @@ std::unique_ptr<uint8_t[]> AdjustAUForSampleAES(
const int kMinAUDSize = 4; const int kMinAUDSize = 4;
EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb, EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb,
const EmitBufferCB& emit_buffer_cb) EmitBufferCB emit_buffer_cb)
: es_adapter_(std::move(new_video_config_cb), emit_buffer_cb), : es_adapter_(std::move(new_video_config_cb), std::move(emit_buffer_cb)),
h264_parser_(new H264Parser()), h264_parser_(new H264Parser()),
current_access_unit_pos_(0), current_access_unit_pos_(0),
next_access_unit_pos_(0) next_access_unit_pos_(0)
...@@ -193,10 +193,10 @@ EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb, ...@@ -193,10 +193,10 @@ EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb,
#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb, EsParserH264::EsParserH264(NewVideoConfigCB new_video_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
EncryptionScheme init_encryption_scheme, EncryptionScheme init_encryption_scheme,
const GetDecryptConfigCB& get_decrypt_config_cb) const GetDecryptConfigCB& get_decrypt_config_cb)
: es_adapter_(std::move(new_video_config_cb), emit_buffer_cb), : es_adapter_(std::move(new_video_config_cb), std::move(emit_buffer_cb)),
h264_parser_(new H264Parser()), h264_parser_(new H264Parser()),
current_access_unit_pos_(0), current_access_unit_pos_(0),
next_access_unit_pos_(0), next_access_unit_pos_(0),
......
...@@ -44,10 +44,10 @@ class MEDIA_EXPORT EsParserH264 : public EsParser { ...@@ -44,10 +44,10 @@ class MEDIA_EXPORT EsParserH264 : public EsParser {
base::RepeatingCallback<void(const VideoDecoderConfig&)>; base::RepeatingCallback<void(const VideoDecoderConfig&)>;
EsParserH264(NewVideoConfigCB new_video_config_cb, EsParserH264(NewVideoConfigCB new_video_config_cb,
const EmitBufferCB& emit_buffer_cb); EmitBufferCB emit_buffer_cb);
#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
EsParserH264(NewVideoConfigCB new_video_config_cb, EsParserH264(NewVideoConfigCB new_video_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
EncryptionScheme init_encryption_scheme, EncryptionScheme init_encryption_scheme,
const GetDecryptConfigCB& get_decrypt_config_cb); const GetDecryptConfigCB& get_decrypt_config_cb);
#endif #endif
......
...@@ -13,8 +13,8 @@ static void EmitBuffer(scoped_refptr<media::StreamParserBuffer> buffer) {} ...@@ -13,8 +13,8 @@ static void EmitBuffer(scoped_refptr<media::StreamParserBuffer> buffer) {}
// Entry point for LibFuzzer. // Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
media::mp2t::EsParserH264 es_parser(base::Bind(&NewVideoConfig), media::mp2t::EsParserH264 es_parser(base::BindRepeating(&NewVideoConfig),
base::Bind(&EmitBuffer)); base::BindRepeating(&EmitBuffer));
if (!es_parser.Parse(data, size, media::kNoTimestamp, if (!es_parser.Parse(data, size, media::kNoTimestamp,
media::kNoDecodeTimestamp())) { media::kNoDecodeTimestamp())) {
return 0; return 0;
......
...@@ -38,11 +38,11 @@ struct EsParserMpeg1Audio::Mpeg1AudioFrame { ...@@ -38,11 +38,11 @@ struct EsParserMpeg1Audio::Mpeg1AudioFrame {
EsParserMpeg1Audio::EsParserMpeg1Audio( EsParserMpeg1Audio::EsParserMpeg1Audio(
const NewAudioConfigCB& new_audio_config_cb, const NewAudioConfigCB& new_audio_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
MediaLog* media_log) MediaLog* media_log)
: media_log_(media_log), : media_log_(media_log),
new_audio_config_cb_(new_audio_config_cb), new_audio_config_cb_(new_audio_config_cb),
emit_buffer_cb_(emit_buffer_cb) {} emit_buffer_cb_(std::move(emit_buffer_cb)) {}
EsParserMpeg1Audio::~EsParserMpeg1Audio() { EsParserMpeg1Audio::~EsParserMpeg1Audio() {
} }
......
...@@ -32,7 +32,7 @@ class MEDIA_EXPORT EsParserMpeg1Audio : public EsParser { ...@@ -32,7 +32,7 @@ class MEDIA_EXPORT EsParserMpeg1Audio : public EsParser {
typedef base::Callback<void(const AudioDecoderConfig&)> NewAudioConfigCB; typedef base::Callback<void(const AudioDecoderConfig&)> NewAudioConfigCB;
EsParserMpeg1Audio(const NewAudioConfigCB& new_audio_config_cb, EsParserMpeg1Audio(const NewAudioConfigCB& new_audio_config_cb,
const EmitBufferCB& emit_buffer_cb, EmitBufferCB emit_buffer_cb,
MediaLog* media_log); MediaLog* media_log);
~EsParserMpeg1Audio() override; ~EsParserMpeg1Audio() override;
......
...@@ -17,8 +17,9 @@ static void EmitBuffer(scoped_refptr<media::StreamParserBuffer> buffer) {} ...@@ -17,8 +17,9 @@ static void EmitBuffer(scoped_refptr<media::StreamParserBuffer> buffer) {}
// Entry point for LibFuzzer. // Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
media::NullMediaLog media_log; media::NullMediaLog media_log;
media::mp2t::EsParserMpeg1Audio es_parser( media::mp2t::EsParserMpeg1Audio es_parser(base::Bind(&NewAudioConfig),
base::Bind(&NewAudioConfig), base::Bind(&EmitBuffer), &media_log); base::BindRepeating(&EmitBuffer),
&media_log);
if (es_parser.Parse(data, size, media::kNoTimestamp, if (es_parser.Parse(data, size, media::kNoTimestamp,
media::kNoDecodeTimestamp())) { media::kNoDecodeTimestamp())) {
es_parser.Flush(); es_parser.Flush();
......
...@@ -41,7 +41,8 @@ bool EsParserMpeg1AudioTest::Process( ...@@ -41,7 +41,8 @@ bool EsParserMpeg1AudioTest::Process(
EsParserMpeg1Audio es_parser( EsParserMpeg1Audio es_parser(
base::Bind(&EsParserMpeg1AudioTest::NewAudioConfig, base::Bind(&EsParserMpeg1AudioTest::NewAudioConfig,
base::Unretained(this)), base::Unretained(this)),
base::Bind(&EsParserMpeg1AudioTest::EmitBuffer, base::Unretained(this)), base::BindRepeating(&EsParserMpeg1AudioTest::EmitBuffer,
base::Unretained(this)),
&media_log_); &media_log_);
return ProcessPesPackets(&es_parser, pes_packets, force_timing); return ProcessPesPackets(&es_parser, pes_packets, force_timing);
} }
......
...@@ -397,30 +397,31 @@ void Mp2tStreamParser::RegisterPmt(int program_number, int pmt_pid) { ...@@ -397,30 +397,31 @@ void Mp2tStreamParser::RegisterPmt(int program_number, int pmt_pid) {
std::unique_ptr<EsParser> Mp2tStreamParser::CreateH264Parser(int pes_pid) { std::unique_ptr<EsParser> Mp2tStreamParser::CreateH264Parser(int pes_pid) {
auto on_video_config_changed = base::BindRepeating( auto on_video_config_changed = base::BindRepeating(
&Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid); &Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid);
auto on_emit_video_buffer = base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, auto on_emit_video_buffer = base::BindRepeating(
base::Unretained(this), pes_pid); &Mp2tStreamParser::OnEmitVideoBuffer, base::Unretained(this), pes_pid);
return std::make_unique<EsParserH264>(std::move(on_video_config_changed), return std::make_unique<EsParserH264>(std::move(on_video_config_changed),
on_emit_video_buffer); std::move(on_emit_video_buffer));
} }
std::unique_ptr<EsParser> Mp2tStreamParser::CreateAacParser(int pes_pid) { std::unique_ptr<EsParser> Mp2tStreamParser::CreateAacParser(int pes_pid) {
auto on_audio_config_changed = base::Bind( auto on_audio_config_changed = base::Bind(
&Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid); &Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid);
auto on_emit_audio_buffer = base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, auto on_emit_audio_buffer = base::BindRepeating(
base::Unretained(this), pes_pid); &Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), pes_pid);
return std::make_unique<EsParserAdts>(on_audio_config_changed, return std::make_unique<EsParserAdts>(on_audio_config_changed,
on_emit_audio_buffer, sbr_in_mimetype_); std::move(on_emit_audio_buffer),
sbr_in_mimetype_);
} }
std::unique_ptr<EsParser> Mp2tStreamParser::CreateMpeg1AudioParser( std::unique_ptr<EsParser> Mp2tStreamParser::CreateMpeg1AudioParser(
int pes_pid) { int pes_pid) {
auto on_audio_config_changed = base::Bind( auto on_audio_config_changed = base::Bind(
&Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid); &Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid);
auto on_emit_audio_buffer = base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, auto on_emit_audio_buffer = base::BindRepeating(
base::Unretained(this), pes_pid); &Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), pes_pid);
return std::make_unique<EsParserMpeg1Audio>(on_audio_config_changed, return std::make_unique<EsParserMpeg1Audio>(
on_emit_audio_buffer, media_log_); on_audio_config_changed, std::move(on_emit_audio_buffer), media_log_);
} }
#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
...@@ -437,8 +438,8 @@ std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedH264Parser( ...@@ -437,8 +438,8 @@ std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedH264Parser(
bool emit_clear_buffers) { bool emit_clear_buffers) {
auto on_video_config_changed = base::BindRepeating( auto on_video_config_changed = base::BindRepeating(
&Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid); &Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid);
auto on_emit_video_buffer = base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, auto on_emit_video_buffer = base::BindRepeating(
base::Unretained(this), pes_pid); &Mp2tStreamParser::OnEmitVideoBuffer, base::Unretained(this), pes_pid);
auto get_decrypt_config = auto get_decrypt_config =
emit_clear_buffers ? EsParser::GetDecryptConfigCB() emit_clear_buffers ? EsParser::GetDecryptConfigCB()
: base::Bind(&Mp2tStreamParser::GetDecryptConfig, : base::Bind(&Mp2tStreamParser::GetDecryptConfig,
...@@ -453,15 +454,15 @@ std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedAacParser( ...@@ -453,15 +454,15 @@ std::unique_ptr<EsParser> Mp2tStreamParser::CreateEncryptedAacParser(
bool emit_clear_buffers) { bool emit_clear_buffers) {
auto on_audio_config_changed = base::Bind( auto on_audio_config_changed = base::Bind(
&Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid); &Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid);
auto on_emit_audio_buffer = base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, auto on_emit_audio_buffer = base::BindRepeating(
base::Unretained(this), pes_pid); &Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), pes_pid);
auto get_decrypt_config = auto get_decrypt_config =
emit_clear_buffers ? EsParser::GetDecryptConfigCB() emit_clear_buffers ? EsParser::GetDecryptConfigCB()
: base::Bind(&Mp2tStreamParser::GetDecryptConfig, : base::Bind(&Mp2tStreamParser::GetDecryptConfig,
base::Unretained(this)); base::Unretained(this));
return std::make_unique<EsParserAdts>( return std::make_unique<EsParserAdts>(
on_audio_config_changed, on_emit_audio_buffer, get_decrypt_config, on_audio_config_changed, std::move(on_emit_audio_buffer),
initial_encryption_scheme_, sbr_in_mimetype_); get_decrypt_config, initial_encryption_scheme_, sbr_in_mimetype_);
} }
#endif #endif
......
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