Commit c4ef6af1 authored by Mikhail Karpenko's avatar Mikhail Karpenko

WIP: single threaded app

Convert some major members of the global struct to arrays. The app can
take frames from first channel.
parent 4debbbaa
This diff is collapsed.
...@@ -78,10 +78,10 @@ ...@@ -78,10 +78,10 @@
typedef struct { typedef struct {
int rawdev_fd; int rawdev_fd;
uint32_t overrun;
uint64_t start_pos; uint64_t start_pos;
uint64_t end_pos; uint64_t end_pos;
uint64_t curr_pos; uint64_t curr_pos;
uint32_t overrun;
} rawdev_buffer; } rawdev_buffer;
typedef struct { typedef struct {
...@@ -92,19 +92,18 @@ typedef struct { ...@@ -92,19 +92,18 @@ typedef struct {
int save_gp; //if non zero, current circbuf pointer will be saved to global pointer, so imgsrv can report /pointers int save_gp; //if non zero, current circbuf pointer will be saved to global pointer, so imgsrv can report /pointers
char path_prefix[256]; char path_prefix[256];
char path[300]; char path[300];
int cirbuf_rp; //!-1 - invalid int cirbuf_rp[SENSOR_PORTS]; //!-1 - invalid
int fd_circ; //! file descriptor for circbuf int fd_circ[SENSOR_PORTS]; //! file descriptor for circbuf
int fd_head; //! file descriptor for JPEG header int fd_head[SENSOR_PORTS]; //! file descriptor for JPEG header
// int fd_sens; //! file descriptor for sensor/compressor parameters int fd_fparmsall[SENSOR_PORTS]; //! file descriptor for sensor/compressor parameters
int fd_fparmsall; //! file descriptor for sensor/compressor parameters int fd_exif[SENSOR_PORTS]; //! file descriptor for Exif data
int fd_exif; //! file descriptor for Exif data int head_size[SENSOR_PORTS]; //! JPEG header size
int head_size; //! JPEG header size unsigned char jpegHeader[SENSOR_PORTS][JPEG_HEADER_MAXSIZE];
unsigned char jpegHeader [JPEG_HEADER_MAXSIZE];
int metadata_start; int metadata_start;
struct interframe_params_t frame_params; struct interframe_params_t frame_params;
struct interframe_params_t this_frame_params; struct interframe_params_t this_frame_params;
int jpeg_len; int jpeg_len;
int frame_period; //!in microseconds (1/10 of what is needed for the Ogm header) int frame_period[SENSOR_PORTS]; //!in microseconds (1/10 of what is needed for the Ogm header)
int width; int width;
int height; int height;
int starting; int starting;
...@@ -122,16 +121,14 @@ typedef struct { ...@@ -122,16 +121,14 @@ typedef struct {
int last; //last packet in a file int last; //last packet in a file
int exif; // 1 - calculate and include Exif headers in each frame int exif; // 1 - calculate and include Exif headers in each frame
// exif_pointers_t ep; int exifSize[SENSOR_PORTS]; //signed
// int exifValid; unsigned char ed[SENSOR_PORTS][MAX_EXIF_SIZE];
int exifSize; //signed
unsigned char ed[MAX_EXIF_SIZE];
int circ_buff_size; int circ_buff_size[SENSOR_PORTS];
int senspars_size; // int senspars_size;
char debug_name[256]; char debug_name[256];
// FILE* debug_file; // FILE* debug_file;
int set_samples_per_unit; // int set_samples_per_unit;
double timescale; //! current timescale, default 1.0 double timescale; //! current timescale, default 1.0
double set_timescale; double set_timescale;
double start_after_timestamp; /// delay recording start to after frame timestamp double start_after_timestamp; /// delay recording start to after frame timestamp
...@@ -140,7 +137,7 @@ typedef struct { ...@@ -140,7 +137,7 @@ typedef struct {
int frames_per_chunk; int frames_per_chunk;
int set_frames_per_chunk; // quicktime - index for fast forward? int set_frames_per_chunk; // quicktime - index for fast forward?
int frameno; int frameno;
int* frame_lengths; int *frame_lengths;
off_t frame_data_start; //! Quicktime (and else?) - frame data start (0xff 0xd8...) off_t frame_data_start; //! Quicktime (and else?) - frame data start (0xff 0xd8...)
ogg_int64_t time_unit; ogg_int64_t time_unit;
int formats; //! bitmask of used (initialized) formats int formats; //! bitmask of used (initialized) formats
...@@ -148,12 +145,12 @@ typedef struct { ...@@ -148,12 +145,12 @@ typedef struct {
int set_format; //! output format to set (will be updated after stop) int set_format; //! output format to set (will be updated after stop)
elph_packet_chunk packetchunks[7]; elph_packet_chunk packetchunks[7];
int chunk_index; int chunk_index;
int buf_overruns; int buf_overruns[SENSOR_PORTS];
int buf_min; int buf_min[SENSOR_PORTS];
int set_frames_skip; //! will be copied to frames_skip if stopped or at start int set_frames_skip; //! will be copied to frames_skip if stopped or at start
int frames_skip; //! number of frames to skip after the one recorded (for time lapse) int frames_skip; //! number of frames to skip after the one recorded (for time lapse)
//! if negetive - -(interval between frames in seconds) //! if negetive - -(interval between frames in seconds)
int frames_skip_left; //! number of frames left to skip before the next one to be processed int frames_skip_left[SENSOR_PORTS]; //! number of frames left to skip before the next one to be processed
//! if (frames_skip <0) - next timestamp to save an image //! if (frames_skip <0) - next timestamp to save an image
//kml stuff //kml stuff
int kml_enable; //! enable KML file generation int kml_enable; //! enable KML file generation
...@@ -170,10 +167,11 @@ typedef struct { ...@@ -170,10 +167,11 @@ typedef struct {
int kml_last_uts; //! last generated kml file timestamp, microseconds int kml_last_uts; //! last generated kml file timestamp, microseconds
struct exif_dir_table_t kml_exif[ExifKmlNumber]; //! store locations of the fields needed for KML generations in the Exif block struct exif_dir_table_t kml_exif[ExifKmlNumber]; //! store locations of the fields needed for KML generations in the Exif block
unsigned int port_num; // sensor port this state is assigned to unsigned int port_num; // sensor port we are currently working with
char *pipe_name; // command pipe name for this sensor port char *pipe_name; // command pipe name
int rawdev_op; // flag indicating writing to raw device int rawdev_op; // flag indicating writing to raw device
rawdev_buffer rawdev; // contains pointers to raw device buffer rawdev_buffer rawdev; // contains pointers to raw device buffer
int active_chn; // bitmask of active sensor ports
} camogm_state; } camogm_state;
extern int debug_level; extern int debug_level;
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
*! *!
*/ */
#define LARGEFILES64_SOURCE #define _LARGEFILE64_SOURCE
//!Not all are needed, just copied from the camogm.c //!Not all are needed, just copied from the camogm.c
#include <unistd.h> #include <unistd.h>
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
//#include <sys/socket.h> //#include <sys/socket.h>
//#include <sys/stat.h> #include <sys/stat.h>
//#include <ctype.h> //#include <ctype.h>
//#include <getopt.h> //#include <getopt.h>
//#include <time.h> //#include <time.h>
...@@ -70,9 +70,9 @@ ...@@ -70,9 +70,9 @@
#define IOVEC_SIZE 10 #define IOVEC_SIZE 10
/** @brief File starting marker, contains "stelphel" string in ASCII symbols */ /** @brief File starting marker, contains "stelphel" string in ASCII symbols */
const unsigned char elphelst[] = {0x73, 0x74, 0x65, 0x6c, 0x70, 0x68, 0x65, 0x6c}; unsigned char elphelst[] = {0x73, 0x74, 0x65, 0x6c, 0x70, 0x68, 0x65, 0x6c};
/** @brief File ending marker, contains "enelphel" string in ASCII symbols */ /** @brief File ending marker, contains "enelphel" string in ASCII symbols */
const unsigned char elphelen[] = {0x65, 0x6e, 0x65, 0x6c, 0x70, 0x68, 0x65, 0x6c}; unsigned char elphelen[] = {0x65, 0x6e, 0x65, 0x6c, 0x70, 0x68, 0x65, 0x6c};
static struct iovec start_marker = { static struct iovec start_marker = {
.iov_base = elphelst, .iov_base = elphelst,
.iov_len = sizeof(elphelst) .iov_len = sizeof(elphelst)
...@@ -130,12 +130,10 @@ int camogm_frame_jpeg(camogm_state *state) ...@@ -130,12 +130,10 @@ int camogm_frame_jpeg(camogm_state *state)
{ {
int i, j, split_index; int i, j, split_index;
int chunks_used = state->chunk_index - 1; int chunks_used = state->chunk_index - 1;
ssize_t iovlen, l; ssize_t iovlen, l = 0;
struct iovec chunks_iovec[8]; struct iovec chunks_iovec[8];
unsigned char *split_ptr = NULL; unsigned char *split_ptr = NULL;
long split_cntr; long split_cntr = 0;
long total_len;
const uint64_t storage_sz = state->rawdev.end_pos - state->rawdev.start_pos;
if (!state->rawdev_op) { if (!state->rawdev_op) {
l = 0; l = 0;
......
...@@ -165,6 +165,7 @@ int camogm_frame_kml(camogm_state *state) ...@@ -165,6 +165,7 @@ int camogm_frame_kml(camogm_state *state)
int hours = 0, minutes = 0; int hours = 0, minutes = 0;
double seconds = 0.0; double seconds = 0.0;
int * ip; int * ip;
int port = state->port_num;
if (state->kml_file) { // probably not needed if (state->kml_file) { // probably not needed
i = state->this_frame_params.timestamp_sec - (state->kml_last_ts + state->kml_period); i = state->this_frame_params.timestamp_sec - (state->kml_last_ts + state->kml_period);
...@@ -204,7 +205,7 @@ int camogm_frame_kml(camogm_state *state) ...@@ -204,7 +205,7 @@ int camogm_frame_kml(camogm_state *state)
///generating KML itself ///generating KML itself
/// Using GPS time - in the same structure /// Using GPS time - in the same structure
if (state->kml_exif[Exif_GPSInfo_GPSDateStamp_Index].ltag == Exif_GPSInfo_GPSDateStamp) { // Exif_GPSInfo_GPSDateStamp is present in template if (state->kml_exif[Exif_GPSInfo_GPSDateStamp_Index].ltag == Exif_GPSInfo_GPSDateStamp) { // Exif_GPSInfo_GPSDateStamp is present in template
memcpy(datestr, &(state->ed[state->kml_exif[Exif_GPSInfo_GPSDateStamp_Index].dst]), 10); memcpy(datestr, &(state->ed[port][state->kml_exif[Exif_GPSInfo_GPSDateStamp_Index].dst]), 10);
datestr[4] = '-'; datestr[7] = '-'; datestr[10] = '\0'; datestr[4] = '-'; datestr[7] = '-'; datestr[10] = '\0';
} }
if (state->kml_exif[Exif_GPSInfo_GPSTimeStamp_Index].ltag == Exif_GPSInfo_GPSTimeStamp) { // Exif_GPSInfo_GPSTimeStamp is present in template if (state->kml_exif[Exif_GPSInfo_GPSTimeStamp_Index].ltag == Exif_GPSInfo_GPSTimeStamp) { // Exif_GPSInfo_GPSTimeStamp is present in template
...@@ -227,31 +228,31 @@ int camogm_frame_kml(camogm_state *state) ...@@ -227,31 +228,31 @@ int camogm_frame_kml(camogm_state *state)
ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_GPSLongitude_Index].dst]); ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_GPSLongitude_Index].dst]);
longitude = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3])); longitude = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3]));
if ((state->kml_exif[Exif_GPSInfo_GPSLongitudeRef_Index].ltag == Exif_GPSInfo_GPSLongitudeRef) && if ((state->kml_exif[Exif_GPSInfo_GPSLongitudeRef_Index].ltag == Exif_GPSInfo_GPSLongitudeRef) &&
(state->ed[state->kml_exif[Exif_GPSInfo_GPSLongitudeRef_Index].dst] != 'E')) longitude = -longitude; (state->ed[port][state->kml_exif[Exif_GPSInfo_GPSLongitudeRef_Index].dst] != 'E')) longitude = -longitude;
D2(fprintf(debug_file, "(longitude) 0x%x 0x%x 0x%x 0x%x '%c'\n", ip[0], ip[1], ip[2], ip[3], state->ed[state->kml_exif[Exif_GPSInfo_GPSLongitudeRef_Index].dst])); D2(fprintf(debug_file, "(longitude) 0x%x 0x%x 0x%x 0x%x '%c'\n", ip[0], ip[1], ip[2], ip[3], state->ed[port][state->kml_exif[Exif_GPSInfo_GPSLongitudeRef_Index].dst]));
} }
if (state->kml_exif[Exif_GPSInfo_GPSLatitude_Index].ltag == Exif_GPSInfo_GPSLatitude) { // Exif_GPSInfo_GPSLatitude is present in template if (state->kml_exif[Exif_GPSInfo_GPSLatitude_Index].ltag == Exif_GPSInfo_GPSLatitude) { // Exif_GPSInfo_GPSLatitude is present in template
ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_GPSLatitude_Index].dst]); ip = (int*)&(state->ed[port][state->kml_exif[Exif_GPSInfo_GPSLatitude_Index].dst]);
latitude = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3])); latitude = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3]));
if ((state->kml_exif[Exif_GPSInfo_GPSLatitudeRef_Index].ltag == Exif_GPSInfo_GPSLatitudeRef) && if ((state->kml_exif[Exif_GPSInfo_GPSLatitudeRef_Index].ltag == Exif_GPSInfo_GPSLatitudeRef) &&
(state->ed[state->kml_exif[Exif_GPSInfo_GPSLatitudeRef_Index].dst] != 'N')) latitude = -latitude; (state->ed[port][state->kml_exif[Exif_GPSInfo_GPSLatitudeRef_Index].dst] != 'N')) latitude = -latitude;
D2(fprintf(debug_file, "(latitude) 0x%x 0x%x 0x%x 0x%x '%c'\n", ip[0], ip[1], ip[2], ip[3], state->ed[state->kml_exif[Exif_GPSInfo_GPSLatitudeRef_Index].dst] ? '-' : '+')); D2(fprintf(debug_file, "(latitude) 0x%x 0x%x 0x%x 0x%x '%c'\n", ip[0], ip[1], ip[2], ip[3], state->ed[port][state->kml_exif[Exif_GPSInfo_GPSLatitudeRef_Index].dst] ? '-' : '+'));
} }
/// altitude - will be modified/replaced later /// altitude - will be modified/replaced later
if (state->kml_exif[Exif_GPSInfo_GPSAltitude_Index].ltag == Exif_GPSInfo_GPSAltitude) { // Exif_GPSInfo_GPSAltitude is present in template if (state->kml_exif[Exif_GPSInfo_GPSAltitude_Index].ltag == Exif_GPSInfo_GPSAltitude) { // Exif_GPSInfo_GPSAltitude is present in template
ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_GPSAltitude_Index].dst]); ip = (int*)&(state->ed[port][state->kml_exif[Exif_GPSInfo_GPSAltitude_Index].dst]);
altitude = (1.0 * __cpu_to_be32( ip[0])) / __cpu_to_be32( ip[1]); altitude = (1.0 * __cpu_to_be32( ip[0])) / __cpu_to_be32( ip[1]);
if ((state->kml_exif[Exif_GPSInfo_GPSAltitudeRef_Index].ltag == Exif_GPSInfo_GPSAltitudeRef) && if ((state->kml_exif[Exif_GPSInfo_GPSAltitudeRef_Index].ltag == Exif_GPSInfo_GPSAltitudeRef) &&
(state->ed[state->kml_exif[Exif_GPSInfo_GPSAltitudeRef_Index].dst] != '\0')) altitude = -altitude; (state->ed[port][state->kml_exif[Exif_GPSInfo_GPSAltitudeRef_Index].dst] != '\0')) altitude = -altitude;
D2(fprintf(debug_file, "(altitude) 0x%x 0x%x '%c'\n", ip[0], ip[1], state->ed[state->kml_exif[Exif_GPSInfo_GPSAltitudeRef_Index].dst])); D2(fprintf(debug_file, "(altitude) 0x%x 0x%x '%c'\n", ip[0], ip[1], state->ed[port][state->kml_exif[Exif_GPSInfo_GPSAltitudeRef_Index].dst]));
} }
D1(fprintf(debug_file, "longitude=%f, latitude=%f, altitude=%f\n", longitude, latitude, altitude)); D1(fprintf(debug_file, "longitude=%f, latitude=%f, altitude=%f\n", longitude, latitude, altitude));
/// Heading - no processing of "True/Magnetic" Exif_GPSInfo_CompassDirectionRef now (always M) /// Heading - no processing of "True/Magnetic" Exif_GPSInfo_CompassDirectionRef now (always M)
if (state->kml_exif[Exif_GPSInfo_CompassDirection_Index].ltag == Exif_GPSInfo_CompassDirection) { // Exif_GPSInfo_CompassDirection is present in template if (state->kml_exif[Exif_GPSInfo_CompassDirection_Index].ltag == Exif_GPSInfo_CompassDirection) { // Exif_GPSInfo_CompassDirection is present in template
ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_CompassDirection_Index].dst]); ip = (int*)&(state->ed[port][state->kml_exif[Exif_GPSInfo_CompassDirection_Index].dst]);
heading = (1.0 * __cpu_to_be32( ip[0])) / __cpu_to_be32( ip[1]); heading = (1.0 * __cpu_to_be32( ip[0])) / __cpu_to_be32( ip[1]);
D2(fprintf(debug_file, "(heading) 0x%x 0x%x\n", ip[0], ip[1])); D2(fprintf(debug_file, "(heading) 0x%x 0x%x\n", ip[0], ip[1]));
} }
...@@ -260,16 +261,16 @@ int camogm_frame_kml(camogm_state *state) ...@@ -260,16 +261,16 @@ int camogm_frame_kml(camogm_state *state)
ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_CompassRoll_Index].dst]); ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_CompassRoll_Index].dst]);
roll = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3])); roll = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3]));
if ((state->kml_exif[Exif_GPSInfo_CompassRollRef_Index].ltag == Exif_GPSInfo_CompassRollRef) && if ((state->kml_exif[Exif_GPSInfo_CompassRollRef_Index].ltag == Exif_GPSInfo_CompassRollRef) &&
(state->ed[state->kml_exif[Exif_GPSInfo_CompassRollRef_Index].dst] != EXIF_COMPASS_ROLL_ASCII[0])) roll = -roll; (state->ed[port][state->kml_exif[Exif_GPSInfo_CompassRollRef_Index].dst] != EXIF_COMPASS_ROLL_ASCII[0])) roll = -roll;
D2(fprintf(debug_file, "(roll) 0x%x 0x%x '%c'\n", ip[0], ip[1], state->ed[state->kml_exif[Exif_GPSInfo_CompassRollRef_Index].dst])); D2(fprintf(debug_file, "(roll) 0x%x 0x%x '%c'\n", ip[0], ip[1], state->ed[port][state->kml_exif[Exif_GPSInfo_CompassRollRef_Index].dst]));
} }
if (state->kml_exif[Exif_GPSInfo_CompassPitch_Index].ltag == Exif_GPSInfo_CompassPitch) { // Exif_GPSInfo_CompassPitch is present in template if (state->kml_exif[Exif_GPSInfo_CompassPitch_Index].ltag == Exif_GPSInfo_CompassPitch) { // Exif_GPSInfo_CompassPitch is present in template
ip = (int*)&(state->ed[state->kml_exif[Exif_GPSInfo_CompassPitch_Index].dst]); ip = (int*)&(state->ed[port][state->kml_exif[Exif_GPSInfo_CompassPitch_Index].dst]);
pitch = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3])); pitch = __cpu_to_be32( ip[0]) / (1.0 * __cpu_to_be32( ip[1])) + __cpu_to_be32( ip[2]) / (60.0 * __cpu_to_be32( ip[3]));
if ((state->kml_exif[Exif_GPSInfo_CompassPitchRef_Index].ltag == Exif_GPSInfo_CompassPitchRef) && if ((state->kml_exif[Exif_GPSInfo_CompassPitchRef_Index].ltag == Exif_GPSInfo_CompassPitchRef) &&
(state->ed[state->kml_exif[Exif_GPSInfo_CompassPitchRef_Index].dst] != EXIF_COMPASS_PITCH_ASCII[0])) pitch = -pitch; (state->ed[port][state->kml_exif[Exif_GPSInfo_CompassPitchRef_Index].dst] != EXIF_COMPASS_PITCH_ASCII[0])) pitch = -pitch;
D2(fprintf(debug_file, "(pitch) 0x%x 0x%x '%c'\n", ip[0], ip[1], state->ed[state->kml_exif[Exif_GPSInfo_CompassPitchRef_Index].dst])); D2(fprintf(debug_file, "(pitch) 0x%x 0x%x '%c'\n", ip[0], ip[1], state->ed[port][state->kml_exif[Exif_GPSInfo_CompassPitchRef_Index].dst]));
} }
/// convert from GPS heading, pitch, roll to KML heading, tilt, roll /// convert from GPS heading, pitch, roll to KML heading, tilt, roll
tilt = pitch + 90.0; tilt = pitch + 90.0;
......
...@@ -200,7 +200,7 @@ int camogm_end_mov(camogm_state *state) ...@@ -200,7 +200,7 @@ int camogm_end_mov(camogm_state *state)
off_t l/*,he; off_t l/*,he;
unsigned char mdat_tag[8]; unsigned char mdat_tag[8];
unsigned char skip_tag[]="\0\0\0\0skip"*/; unsigned char skip_tag[]="\0\0\0\0skip"*/;
int port = state->port_num;
timescale = 10000; //! frame period measured in 1/10000 of a second? timescale = 10000; //! frame period measured in 1/10000 of a second?
//! that was in old code. If that works - try to switch to microseconds //! that was in old code. If that works - try to switch to microseconds
l = lseek(state->ivf, 0, SEEK_CUR) - (state->frame_data_start) + 8; //!4-byte length+"mdat" l = lseek(state->ivf, 0, SEEK_CUR) - (state->frame_data_start) + 8; //!4-byte length+"mdat"
...@@ -213,7 +213,7 @@ int camogm_end_mov(camogm_state *state) ...@@ -213,7 +213,7 @@ int camogm_end_mov(camogm_state *state)
state->width, //! width in pixels state->width, //! width in pixels
state->height, state->height,
state->frameno, state->frameno,
state->frame_period / (1000000 / timescale), state->frame_period[port] / (1000000 / timescale),
state->frames_per_chunk, state->frames_per_chunk,
0, //!frame size - will look in the table 0, //!frame size - will look in the table
(int)((float)timescale / (state->timescale)), (int)((float)timescale / (state->timescale)),
......
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