camogm.c 72.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/** @file camogm.c
 * @brief Program to write captured video (and audio) to camera file system
 * using Ogg container.
 * @copyright Copyright (C) 2016 Elphel, Inc.
 *
 * @par <b>License</b>
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Mikhail Karpenko's avatar
Mikhail Karpenko committed
17
 */
18
#include <unistd.h>
19 20 21 22
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
23
#include <linux/fs.h>
Mikhail Karpenko's avatar
Mikhail Karpenko committed
24
#include <sys/mman.h>
25
#include <sys/ioctl.h>
26 27
#include <sys/uio.h>
#include <sys/stat.h>
28
#include <string.h>
29
#include <getopt.h>
30 31 32 33 34

#include "camogm_ogm.h"
#include "camogm_jpeg.h"
#include "camogm_mov.h"
#include "camogm_kml.h"
35
#include "camogm_read.h"
36

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/** @brief Default debug level */
#define DEFAULT_DEBUG_LVL         6
/** @brief JPEG trailer syze in bytes */
#define TRAILER_SIZE              0x02
/** @brief Default segment duration in seconds */
#define DEFAULT_DURATION          600
/** @brief Default segment length in bytes */
#define DEFAULT_LENGTH            1073741824
/** @brief Behavior for the files: 0 clean buffer, 1 - save as much as possible */
#define DEFAULT_GREEDY            0
/** @brief 0 - restartf file if fps changed, 1 - ignore variable fps (and skip less frames) */
#define DEFAULT_IGNORE_FPS        0
/** @brief Maximal number of frames in file segment (each need 4* (1 + 1/frames_per_chunk) bytes for the frame index.
 * This parameter is mostrly for Quicktime */
#define DEFAULT_FRAMES            16384
/** @brief Second sparse index - for Quicktime fast forward */
#define DEFAULT_FRAMES_PER_CHUNK  10
/** @brief Use Exif */
#define DEFAULT_EXIF              1
/** @brief Bit mask indicating all 4 sensor port channels are active */
#define ALL_CHN_ACTIVE            0x0f
/** @brief Bit mask indicating all 4 sensor port channels are inactive */
#define ALL_CHN_INACTIVE          0x00
/** @brief This is a basic helper macro for processing all sensor ports at a time */
61
#define FOR_EACH_PORT(indtype, indvar) for (indtype indvar = 0; indvar < SENSOR_PORTS; indvar++)
62

63
char trailer[TRAILER_SIZE] = { 0xff, 0xd9 };
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
const char *exifFileNames[] = {	"/dev/exif_exif0", "/dev/exif_exif1",
								"/dev/exif_exif2", "/dev/exif_exif3"
};

const char *headFileNames[] = {	"/dev/jpeghead0", "/dev/jpeghead1",
								"/dev/jpeghead2", "/dev/jpeghead3"
};
const char *ctlFileNames[] = {   "/dev/frameparsall0", "/dev/frameparsall1",
								"/dev/frameparsall2", "/de/framepars3"
};
const char *circbufFileNames[] = {"/dev/circbuf0", "/dev/circbuf1",
								"/dev/circbuf2", "/dev/circbuf3"
};

int lastDaemonBit[SENSOR_PORTS] = {DAEMON_BIT_CAMOGM};
struct framepars_all_t   *frameParsAll[SENSOR_PORTS];
struct framepars_t       *framePars[SENSOR_PORTS];
82 83 84
/** @brief Parameters that are not frame-related, their changes do not initiate any actions */
unsigned long            *aglobalPars[SENSOR_PORTS];
/** @brief Global structure containing current state of the running program */
85
camogm_state sstate;
86 87
/** @brief Memory mapped circular buffer arrays */
unsigned long * ccam_dma_buf[SENSOR_PORTS];
88

89 90
pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;

91 92 93 94 95 96 97 98
/**
 * @enum path_type
 * @brief Define the path type passed to a function
 * @var path_type::RAW_PATH
 * The path specified is a path to raw device buffer and it starts with /dev
 * @var path_type::FILE_PATH
 * The path specified is a regular path
 */
99 100 101 102 103
typedef enum {
	RAW_PATH,
	FILE_PATH
} path_type;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
104
int debug_level;
105 106
FILE* debug_file;

107
void camogm_init(camogm_state *state, char *pipe_name, uint16_t port_num);
108 109
int camogm_start(camogm_state *state);
int camogm_stop(camogm_state *state);
110
void camogm_reset(camogm_state *state);
111
int camogm_debug(camogm_state *state, const char *fname);
112
int camogm_debug_level(int d);
113 114 115 116 117 118
void  camogm_set_segment_duration(camogm_state *state, int sd);
void  camogm_set_segment_length(camogm_state *state, int sl);
void  camogm_set_greedy(camogm_state *state, int d);
void  camogm_set_ignore_fps(camogm_state *state, int d);

void  camogm_set_save_gp(camogm_state *state, int d);
119
void  camogm_set_prefix(camogm_state *state, const char * p, path_type type);
120 121 122 123 124 125 126 127 128 129 130 131 132 133
void  camogm_set_exif(camogm_state *state, int d);
void  camogm_set_timescale(camogm_state *state, double d);   //! set timescale, default=1.0
void  camogm_set_frames_skip(camogm_state *state, int d);    //! set number of frames to skip, if negative - seconds between frames
void  camogm_set_format(camogm_state *state, int d);

void  camogm_kml_set_enable(camogm_state *state, int d);
void  camogm_kml_set_horHalfFov(camogm_state *state, double dd);
void  camogm_kml_set_vertHalfFov(camogm_state *state, double dd);
void  camogm_kml_set_height_mode(camogm_state *state, int d);
void  camogm_kml_set_height(camogm_state *state, double dd);
void  camogm_kml_set_period(camogm_state *state, int d);
void  camogm_kml_set_near(camogm_state *state, double dd); // distance to PhotoOverlay

int   parse_cmd(camogm_state *state, FILE* npipe);
134 135
char * getLineFromPipe(FILE* npipe);

136
int  sendImageFrame(camogm_state *state);
137

138 139 140
void  camogm_set_start_after_timestamp(camogm_state *state, double d);
void  camogm_set_max_frames(camogm_state *state, int d);
void  camogm_set_frames_per_chunk(camogm_state *state, int d);
141

142
uint64_t get_disk_size(const char *name);
143 144 145 146 147
int open_files(camogm_state *state);
unsigned long getGPValue(unsigned int port, unsigned long GPNumber);
void setGValue(unsigned int port, unsigned long GNumber, unsigned long value);
unsigned int select_port(camogm_state *states);
inline void set_chn_state(camogm_state *s, unsigned int port, unsigned int new_state);
148
inline int is_chn_active(camogm_state *s, unsigned int port);
149 150 151 152 153

void put_uint16(void *buf, u_int16_t val)
{
	unsigned char  *tmp;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
154
	tmp = (unsigned char*)buf;
155 156 157 158 159 160 161 162 163

	tmp[0] = val & 0xff;
	tmp[1] = (val >>= 8) & 0xff;
}

void put_uint32(void *buf, u_int32_t val)
{
	unsigned char  *tmp;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
164
	tmp = (unsigned char*)buf;
165 166 167 168 169 170 171 172 173 174 175

	tmp[0] = val & 0xff;
	tmp[1] = (val >>= 8) & 0xff;
	tmp[2] = (val >>= 8) & 0xff;
	tmp[3] = (val >>= 8) & 0xff;
}

void put_uint64(void *buf, u_int64_t val)
{
	unsigned char  *tmp;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
176
	tmp = (unsigned char*)buf;
177 178 179 180 181 182 183 184 185 186 187

	tmp[0] = val & 0xff;
	tmp[1] = (val >>= 8) & 0xff;
	tmp[2] = (val >>= 8) & 0xff;
	tmp[3] = (val >>= 8) & 0xff;
	tmp[4] = (val >>= 8) & 0xff;
	tmp[5] = (val >>= 8) & 0xff;
	tmp[6] = (val >>= 8) & 0xff;
	tmp[7] = (val >>= 8) & 0xff;
}

188
/**
189 190
 * @brief Initialize the state of the program
 * @param[in]   state   pointer to #camogm_state structure containing program state
191 192 193
 * @param[in]   pipe_name pointer to command pipe name string
 * @return      none
 */
194
void camogm_init(camogm_state *state, char *pipe_name, uint16_t port_num)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
195 196 197 198
{
	const char sserial[] = "elp0";
	int * ipser = (int*)sserial;

199
	memset(state, 0, sizeof(camogm_state));
200 201 202 203 204 205
	camogm_set_segment_duration(state, DEFAULT_DURATION);
	camogm_set_segment_length(state, DEFAULT_LENGTH);
	camogm_set_greedy(state, DEFAULT_GREEDY);
	camogm_set_ignore_fps(state, DEFAULT_IGNORE_FPS);
	camogm_set_max_frames(state, DEFAULT_FRAMES);
	camogm_set_frames_per_chunk(state, DEFAULT_FRAMES_PER_CHUNK);
206
	camogm_reset(state);                    // sets state->buf_overruns =- 1
Mikhail Karpenko's avatar
Mikhail Karpenko committed
207 208
	state->serialno = ipser[0];
	debug_file = stderr;
209
	camogm_debug_level(DEFAULT_DEBUG_LVL);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
210
	strcpy(state->debug_name, "stderr");
211
	camogm_set_timescale(state, 1.0);
212
	camogm_set_frames_skip(state, 0);       // don't skip
213
	camogm_set_format(state, CAMOGM_FORMAT_MOV);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
214 215
	state->exif = DEFAULT_EXIF;
	state->frame_lengths = NULL;
216

217
	// reading thread has not been started yet, mutex lock is not necessary
218 219
	state->prog_state = STATE_STOPPED;

220
	// kml stuff
221 222 223 224
	camogm_kml_set_horHalfFov(state, 20.0);
	camogm_kml_set_vertHalfFov(state, 15.0);
	camogm_kml_set_height_mode(state, 0);
	camogm_kml_set_height(state, 10.0);
225 226
	camogm_kml_set_period(state, 2);        // 2 sec
	camogm_kml_set_near(state, 40.0);       // 40 m (distance to PhotoOverlay)
227

228
	state->pipe_name = pipe_name;
229 230
	state->rawdev.start_pos = RAWDEV_START_OFFSET;
	state->rawdev.end_pos = state->rawdev.start_pos;
231 232
	state->rawdev.curr_pos_w = state->rawdev.start_pos;
	state->rawdev.curr_pos_r = state->rawdev.start_pos;
233
	state->active_chn = ALL_CHN_ACTIVE;
234
	state->rawdev.mmap_default_size = MMAP_CHUNK_SIZE;
235
	state->sock_port = port_num;
236 237
}

238 239 240 241 242 243
/**
 * @brief Set debug output file
 * @param[in]   state   a pointer to a structure containing current state
 * @param[in]   fname   file used for debug output
 * @return      0 if the file was opened successfully and negative error code otherwise
 */
244
int camogm_debug(camogm_state *state, const char * fname)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
{
	int none = 1;

	if (fname && strlen(fname) && strcmp(fname, "none") && strcmp(fname, "null")  && strcmp(fname, "/dev/null")) none = 0;
	if (debug_file) {
		if (strcmp(state->debug_name, "stdout") && strcmp(state->debug_name, "stderr")) fclose(debug_file);
		debug_file = NULL;
		state->debug_name[0] = '\0';
	}
	if (!none) {
		if (strcmp(fname, "stdout") == 0) debug_file = stdout;
		else if (strcmp(fname, "stderr") == 0) debug_file = stderr;
		else debug_file = fopen(fname, "w+");
	}
	if (debug_file) {
		strncpy(state->debug_name, fname, sizeof(state->debug_name) - 1);
		state->debug_name[sizeof(state->debug_name) - 1] = '\0';
	}
	return 0;
264 265
}

266
/** @brief Set current debug level */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
267 268 269 270
int camogm_debug_level(int d)
{
	debug_level = d;
	return 0;
271 272
}

273 274 275 276 277
/**
 * @brief Start recording for each active sensor port
 * @param[in]   state   a pointer to a structure containing current state
 * @return      0 if the recording finished successfully and negative error code otherwise
 */
278
int camogm_start(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
279 280 281 282
{
	int timestamp_start;
	int rslt;
	int next_metadata_start, next_jpeg_len, fp;
283
	int port = state->port_num;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
284

285
	if (state->active_chn == ALL_CHN_INACTIVE) {
286 287 288 289 290 291
		D0(fprintf(debug_file, "All channels are disabled, will not start\n"));
		return CAMOGM_FRAME_OTHER;
	}
	if (state->rawdev_op && state->format != CAMOGM_FORMAT_JPEG) {
		D0(fprintf(debug_file, "Raw device write initiated, but file format is not JPEG. Will not start\n"));
		return CAMOGM_FRAME_OTHER;
292 293
	}

Mikhail Karpenko's avatar
Mikhail Karpenko committed
294 295 296
	D1(fprintf(debug_file, "Starting recording\n"));
	double dtime_stamp;
	state->frameno = 0;
297
	state->timescale = state->set_timescale; //! current timescale, default 1
298
///debug
299 300
	int * ifp =      (int*)&(state->frame_params[port]);
	int * ifp_this = (int*)&(state->this_frame_params[port]);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
301 302 303 304 305 306 307
	if (state->kml_enable) camogm_init_kml();  // do nothing

	if (state->format != state->set_format) {
		state->format =  state->set_format;
		switch (state->format) {
		case CAMOGM_FORMAT_NONE: rslt = 0; break;
		case CAMOGM_FORMAT_OGM:  rslt = camogm_init_ogm(); break;
308
		case CAMOGM_FORMAT_JPEG: rslt = camogm_init_jpeg(state); break;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
309 310 311 312 313 314
		case CAMOGM_FORMAT_MOV:  rslt = camogm_init_mov(); break;
		}
		state->formats |= 1 << (state->format);
		//! exit on unknown formats?
	}
	state->max_frames =       state->set_max_frames;
315
	state->frames_per_chunk = state->set_frames_per_chunk;
316
	pthread_mutex_lock(&state->mutex);
317
	state->prog_state = STATE_STARTING;
318
	pthread_mutex_unlock(&state->mutex);
319 320 321 322 323 324 325 326 327 328 329 330 331
	FOR_EACH_PORT(int, chn) {
		//! Check/set circbuf read pointer
		D3(fprintf(debug_file, "1: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
		if ((state->cirbuf_rp[chn] < 0) || (lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET) < 0) || (lseek(state->fd_circ[chn], LSEEK_CIRC_VALID, SEEK_END) < 0 )) {
			D3(fprintf(debug_file, "2: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
			/* In "greedy" mode try to save as many frames from the circbuf as possible */
			state->cirbuf_rp[chn] = lseek(state->fd_circ[chn], state->greedy ? LSEEK_CIRC_SCND : LSEEK_CIRC_LAST, SEEK_END);
			if (!state->ignore_fps) {                                                                               // don't even try in ignore mode
				if (((fp = lseek(state->fd_circ[chn], LSEEK_CIRC_PREV, SEEK_END))) >= 0) state->cirbuf_rp[chn] = fp;      //!try to have 2 frames available for fps
			}
			state->buf_overruns[chn]++;
			//! file pointer here should match state->rp; so no need to do    lseek(state->fd_circ,state->cirbuf_rp,SEEK_SET);
			state->buf_min[chn] = getGPValue(chn, G_FREECIRCBUF);
332

333 334
		} else {
			if (state->buf_min[chn] > getGPValue(chn, G_FREECIRCBUF)) state->buf_min[chn] = getGPValue(chn, G_FREECIRCBUF);
335

336 337 338
		}
		D3(fprintf(debug_file, "3: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
		D3(fprintf(debug_file, "4:lseek(state->fd_circ,LSEEK_CIRC_READY,SEEK_END)=%d\n", (int)lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END)));
339

340 341 342 343 344
		//! is this frame ready?
		if (lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END) < 0) return -CAMOGM_FRAME_NOT_READY;  //! frame pointer valid, but no frames yet
		D3(fprintf(debug_file, "5: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
		state->metadata_start = (state->cirbuf_rp[chn]) - 32;
		if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size[chn];
345

346
		///==================================
347

348 349
		memcpy(&(state->frame_params[chn]), (unsigned long* )&ccam_dma_buf[chn][state->metadata_start >> 2], 32);
		state->jpeg_len = state->frame_params[chn].frame_length; //! frame_params.frame_length are now the length of bitstream
350 351


352 353 354 355 356 357 358
		if (state->frame_params[chn].signffff != 0xffff) {
			D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->frame_params[chn].signffff));
			state->cirbuf_rp[chn] = -1;
			ifp = (int *) &state->frame_params[chn];
			D1(fprintf(debug_file, "state->cirbuf_rp=0x%x\r\n", (int)state->cirbuf_rp[chn]));
			D1(fprintf(debug_file, "%08x %08x %08x %08x %08x %08x %08x %08x\r\n", ifp[0], ifp[1], ifp[2], ifp[3], ifp[4], ifp[5], ifp[6], ifp[7]));
			return -CAMOGM_FRAME_BROKEN;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
359
		}
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
		//!   find location of the timestamp and copy it to the frame_params structure
		///==================================
		timestamp_start = (state->cirbuf_rp[chn]) + ((state->jpeg_len + CCAM_MMAP_META + 3) & (~0x1f)) + 32 - CCAM_MMAP_META_SEC; //! magic shift - should index first byte of the time stamp
		if (timestamp_start >= state->circ_buff_size[chn]) timestamp_start -= state->circ_buff_size[chn];
		memcpy(&(state->frame_params[chn].timestamp_sec), (unsigned long* )&ccam_dma_buf[chn][timestamp_start >> 2], 8);
		/// New - see if current timestamp is later than start one, if not return "CAMOGM_TOO_EARLY" reset read pointer and buffer read pointer
		if (state->start_after_timestamp > 0.0) { /// don't bother if it is 0
			dtime_stamp = 0.000001 * state->frame_params[chn].timestamp_usec + state->frame_params[chn].timestamp_sec;
			if (dtime_stamp < state->start_after_timestamp) {
				state->cirbuf_rp[chn] = -1;
				D3(fprintf(debug_file, "Too early to start, %f < %f\n", dtime_stamp, state->start_after_timestamp));
				return -CAMOGM_TOO_EARLY;
			}
		}
		D3(fprintf(debug_file, "6: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
		//! see if next frame is available
		if ((lseek(state->fd_circ[chn], LSEEK_CIRC_NEXT, SEEK_END) < 0 ) ||
				//! is that next frame ready?
				(((fp = lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END))) < 0)) {
			D3(fprintf(debug_file, "6a:lseek(state->fd_circ,LSEEK_CIRC_NEXT,SEEK_END)=0x%x,  fp=0x%x\n", (int)lseek(state->fd_circ[chn], LSEEK_CIRC_NEXT, SEEK_END), (int)lseek(state->fd_circ[chn], LSEEK_CIRC_READY, SEEK_END)));

			lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET);      //!just in case - restore pointer
			return -CAMOGM_FRAME_NOT_READY;                         //! frame pointer valid, but no frames yet
		}
		next_metadata_start = fp - 32;
		if (next_metadata_start < 0) next_metadata_start += state->circ_buff_size[chn];
		memcpy(&(state->this_frame_params[chn]), (unsigned long* )&ccam_dma_buf[chn][next_metadata_start >> 2], 32);
		next_jpeg_len = state->this_frame_params[chn].frame_length;  //! frame_params.frame_length are now the length of bitstream
		if (state->this_frame_params[chn].signffff != 0xffff) {      //! should not happen ever
			D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->this_frame_params[chn].signffff));
			ifp_this = (int *) &state->this_frame_params[chn];
			D1(fprintf(debug_file, "fp=0x%x\r\n", (int)fp));
			D1(fprintf(debug_file, "%08x %08x %08x %08x %08x %08x %08x %08x\r\n", ifp_this[0], ifp_this[1], ifp_this[2], ifp_this[3], ifp_this[4], ifp_this[5], ifp_this[6], ifp_this[7]));
			state->cirbuf_rp[chn] = -1;
			return -CAMOGM_FRAME_BROKEN;
		}
		D3(fprintf(debug_file, "7: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));

		//! find location of the timestamp and copy it to the frame_params structure
		timestamp_start = fp + ((next_jpeg_len + CCAM_MMAP_META + 3) & (~0x1f)) + 32 - CCAM_MMAP_META_SEC; //! magic shift - should index first byte of the time stamp
		if (timestamp_start >= state->circ_buff_size[chn]) timestamp_start -= state->circ_buff_size[chn];
		memcpy(&(state->this_frame_params[chn].timestamp_sec), (unsigned long* )&ccam_dma_buf[chn][timestamp_start >> 2], 8);
		//! verify that the essential current frame params did not change, if they did - return an error (need new file header)
		if (!state->ignore_fps && ((state->frame_params[chn].width  != state->this_frame_params[chn].width) ||
				(state->frame_params[chn].height != state->this_frame_params[chn].height))) {
			//! Advance frame pointer to the next (caller should try again)
			state->cirbuf_rp[chn] = fp;
			return -CAMOGM_FRAME_CHANGED; // no yet checking for the FPS
		}
		D3(fprintf(debug_file, "8: state->cirbuf_rp=0x%x\n", state->cirbuf_rp[chn]));
410

411 412 413
		//! calcualte the frame period - time difference (in microseconds)
		state->frame_period[chn] = (state->this_frame_params[chn].timestamp_usec - state->frame_params[chn].timestamp_usec) +
				1000000 * (state->this_frame_params[chn].timestamp_sec  - state->frame_params[chn].timestamp_sec);
414

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
		//! correct for timelapse modes:
		state->frames_skip =      state->set_frames_skip;
		if (state->frames_skip > 0) {
			state->frames_skip_left[chn] = 0;
			state->frame_period[chn] *= (state->frames_skip + 1);
		} else if (state->frames_skip < 0) {
			state->frame_period[chn] = -(state->frames_skip); //! actual frame period will fluctuate to the nearest frame acquired (free running)
			state->frames_skip_left[chn] = state->frame_params[chn].timestamp_sec;
		}
		D3(fprintf(debug_file, "9: state->frame_period=0x%x\n", state->frame_period[chn]));

		state->time_unit = (ogg_int64_t)(((double)state->frame_period[chn]) * ((double)10) / ((double)state->timescale));
		state->width = state->frame_params[chn].width;
		state->height = state->frame_params[chn].height;

		//!read JPEG header - it should stay the same for the whole file (restart new file if any parameters changed)
		//!rebuild JPEG header:
		lseek(state->fd_head[chn], state->cirbuf_rp[chn] + 1, SEEK_END);  //!+1 to avoid condition when jpeg_start==0. overloaded lseek will ignore 5 LSBs when SEEK_END
		state->head_size[chn] = lseek(state->fd_head[chn], 0, SEEK_END);  /// In 8.0 the header size might change for some jp4 modes
		if (state->head_size[chn] > JPEG_HEADER_MAXSIZE) {
			D0(fprintf(debug_file, "%s:%d: Too big JPEG header (%d > %d)", __FILE__, __LINE__, state->head_size[chn], JPEG_HEADER_MAXSIZE ));
			return -2;
		}
		//! and read it
		lseek(state->fd_head[chn], 0, 0);
		read(state->fd_head[chn], state->jpegHeader[chn], state->head_size[chn]);
		//! Restore read pointer to the original (now there may be no frame ready there yet)
		lseek(state->fd_circ[chn], state->cirbuf_rp[chn], SEEK_SET);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
443
	}
444 445

//!here we are ready to initialize Ogm (or other) file
Mikhail Karpenko's avatar
Mikhail Karpenko committed
446 447
	switch (state->format) {
	case CAMOGM_FORMAT_NONE: rslt = 0;  break;
448 449 450
	case CAMOGM_FORMAT_OGM:  rslt = camogm_start_ogm(state);  break;
	case CAMOGM_FORMAT_JPEG: rslt = camogm_start_jpeg(state); break;
	case CAMOGM_FORMAT_MOV:  rslt = camogm_start_mov(state);  break;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
451 452 453 454 455 456
	default: rslt = 0; // do nothing
	}
	if (rslt) {
		D0(fprintf(debug_file, "camogm_start() error, rslt=0x%x\n", rslt));
		return rslt;
	}
457
	if (state->kml_enable) rslt = camogm_start_kml(state);  // will turn on state->kml_used if it can
Mikhail Karpenko's avatar
Mikhail Karpenko committed
458
	if (rslt) return rslt;
459
	pthread_mutex_lock(&state->mutex);
460
	state->prog_state = STATE_RUNNING;
461
	pthread_mutex_unlock(&state->mutex);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
462 463
	D1(fprintf(debug_file, "Started OK\n"));
	return 0;
464 465
}

466 467 468 469 470
/**
 * @brief Save a single image from circular buffer to a file.
 * @param[in]   state   a pointer to a structure containing current state
 * @return      0 if the image was successfully saved and negative error code otherwise
 */
471
int sendImageFrame(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
472 473 474 475
{
	int rslt;
	unsigned char frame_packet_type = PACKET_IS_SYNCPOINT;
	int timestamp_start;
476
	int * ifp_this = (int*)&(state->this_frame_params[state->port_num]);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
477
	int fp;
478
	int port = state->port_num;
479 480

//! This is probably needed only for Quicktime (not to exceed already allocated frame index)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
481 482 483 484
	if (state->frameno >= (state->max_frames)) {
		D3(fprintf(debug_file, "sendImageFrame:1: state->frameno(0x%x) >= state->max_frames(0x%x)\n", state->frameno, state->max_frames));
		return -CAMOGM_FRAME_CHANGED;
	}
485 486 487
//! Format changed?
//   D3(fprintf (debug_file,"sendImageFrame: format=%d, set_format=%d\n", state->format, state->set_format));

Mikhail Karpenko's avatar
Mikhail Karpenko committed
488 489 490 491
	if (state->format != state->set_format) {
		D3(fprintf(debug_file, "sendImageFrame:2: state->format(0x%x) != state->set_format(0x%x)\n", state->format, state->set_format));
		return -CAMOGM_FRAME_CHANGED;
	}
492
//!   check if file size is exceeded (assuming fopen),-CAMOGM_FRAME_CHANGED will trigger a new segment
Mikhail Karpenko's avatar
Mikhail Karpenko committed
493 494 495 496
	if ((state->vf) && (state->segment_length >= 0) && (ftell(state->vf) > state->segment_length)) {
		D3(fprintf(debug_file, "sendImageFrame:3: segment length exceeded\n"));
		return -CAMOGM_FRAME_CHANGED;
	}
497
//!same for open
Mikhail Karpenko's avatar
Mikhail Karpenko committed
498 499 500 501
	if (((state->ivf) >= 0) && (state->segment_length >= 0) && (lseek(state->ivf, 0, SEEK_CUR) > state->segment_length)) {
		D3(fprintf(debug_file, "sendImageFrame:4: segment length exceeded\n"));
		return -CAMOGM_FRAME_CHANGED;
	}
502
//! check the frame pointer is valid
503
	if ((fp = lseek(state->fd_circ[port], state->cirbuf_rp[port], SEEK_SET)) < 0) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
504
		D3(fprintf(debug_file, "sendImageFrame:5: invalid frame\n"));
505

Mikhail Karpenko's avatar
Mikhail Karpenko committed
506 507
		return -CAMOGM_FRAME_INVALID; //!it will probably be that allready
	}
508
//! is the frame ready?
509
	if (lseek(state->fd_circ[port], LSEEK_CIRC_READY, SEEK_END) < 0) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
510 511 512
		D3(fprintf(debug_file, "?6,fp=0x%x ", fp));     //frame not ready, frame pointer seems valid, but not ready
		return -CAMOGM_FRAME_NOT_READY;                 //! frame pointer valid, but no frames yet
	}
513 514

//! process skipping frames. TODO: add - skipping time between frames (or better -  actual time period - use the nearest frame) instead of the frame number
515 516
	if ( (state->frames_skip > 0) && (state->frames_skip_left[port] > 0 )) { //!skipping frames, not seconds.
		state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
517
//!optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
518 519
		if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
		state->frames_skip_left[port]--;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
520 521 522
		D3(fprintf(debug_file, "?7 ")); //frame not ready
		return -CAMOGM_FRAME_NOT_READY; //! the required frame is not ready
	}
523 524

//! Get metadata
Mikhail Karpenko's avatar
Mikhail Karpenko committed
525
	D3(fprintf(debug_file, "_1_"));
526 527
	state->metadata_start = state->cirbuf_rp[port] - 32;
	if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size[port];
528 529 530 531
	memcpy(&(state->this_frame_params[port]), (unsigned long* )&ccam_dma_buf[state->port_num][state->metadata_start >> 2], 32);
	state->jpeg_len = state->this_frame_params[port].frame_length; //! frame_params.frame_length are now the length of bitstream
	if (state->this_frame_params[port].signffff != 0xffff) {
		D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->this_frame_params[port].signffff));
532
		D1(fprintf(debug_file, "state->cirbuf_rp=0x%x\r\n", (int)state->cirbuf_rp[port]));
Mikhail Karpenko's avatar
Mikhail Karpenko committed
533 534 535 536 537 538
		D1(fprintf(debug_file, "%08x %08x %08x %08x %08x %08x %08x %08x\r\n", ifp_this[0], ifp_this[1], ifp_this[2], ifp_this[3], ifp_this[4], ifp_this[5], ifp_this[6], ifp_this[7]));

		D3(fprintf(debug_file, "sendImageFrame:8: frame broken\n"));
		return -CAMOGM_FRAME_BROKEN;
	}
	D3(fprintf(debug_file, "_2_"));
539
//!   find location of the timestamp and copy it to the frame_params structure
540 541
	timestamp_start = state->cirbuf_rp[port] + ((state->jpeg_len + CCAM_MMAP_META + 3) & (~0x1f)) + 32 - CCAM_MMAP_META_SEC; //! magic shift - should index first byte of the time stamp
	if (timestamp_start >= state->circ_buff_size[port]) timestamp_start -= state->circ_buff_size[port];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
542
	D3(fprintf(debug_file, "_3_"));
543
	memcpy(&(state->this_frame_params[port].timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
544
//! verify that the essential current frame params did not change, if they did - return an error (need new file header)
545 546
	if (!state->ignore_fps && ((state->frame_params[port].width  != state->this_frame_params[port].width) ||
				   (state->frame_params[port].height != state->this_frame_params[port].height))) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
547 548 549
		D3(fprintf(debug_file, "sendImageFrame:9: WOI changed\n"));
		return -CAMOGM_FRAME_CHANGED; //! not yet checking for the FPS
	}
550
//!   check if file duration (in seconds) exceeded ,-CAMOGM_FRAME_CHANGED will trigger a new segment
Mikhail Karpenko's avatar
Mikhail Karpenko committed
551
	if ((state->segment_duration > 0) &&
552
	    ((state->this_frame_params[port].timestamp_sec - state->frame_params[port].timestamp_sec) > state->segment_duration)) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
553 554 555
		D3(fprintf(debug_file, "sendImageFrame:10: segment duration in seconds exceeded\n"));
		return -CAMOGM_FRAME_CHANGED;
	}
556
//! check if (in timelapse mode)  it is too early for the frame to be stored
557
	if ((state->frames_skip < 0) && (state->frames_skip_left[port] > state->this_frame_params[port].timestamp_sec) ) {
558
		state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
559
//!optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
560
		if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
561 562 563
		D3(fprintf(debug_file, "sendImageFrame:11: timelapse: frame will be skipped\n"));
		return -CAMOGM_FRAME_NOT_READY; //! the required frame is not ready
	}
564 565


Mikhail Karpenko's avatar
Mikhail Karpenko committed
566 567 568
	D3(fprintf(debug_file, "_4_"));
	if (state->exif) {
		D3(fprintf(debug_file, "_5_"));
569
//! update the Exif header with the current frame metadata
570
		state->exifSize[port] = lseek(state->fd_exif[port], 1, SEEK_END); // at the beginning of page 1 - position == page length
Mikhail Karpenko's avatar
Mikhail Karpenko committed
571
		if (state->exifSize > 0) {
572
//state->this_frame_params.meta_index
573
			lseek(state->fd_exif[port], state->this_frame_params[port].meta_index, SEEK_END); //! select meta page to use (matching frame)
574
			rslt = read(state->fd_exif[port], state->ed[port], state->exifSize[port]);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
575
			if (rslt < 0) rslt = 0;
576 577 578
			state->exifSize[port] = rslt;
		} else state->exifSize[port] = 0;
	} else state->exifSize[port] = 0;
579

Mikhail Karpenko's avatar
Mikhail Karpenko committed
580
	D3(fprintf(debug_file, "_6_"));
581 582

//! prepare a packet to be sent (a lst of memory chunks)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
583 584 585 586 587 588
	state->chunk_index = 0;
	state->packetchunks[state->chunk_index  ].bytes = 1;
	state->packetchunks[state->chunk_index++].chunk = &frame_packet_type;
	if (state->exif > 0) { //! insert Exif
		D3(fprintf(debug_file, "_7_"));
		state->packetchunks[state->chunk_index  ].bytes = 2;
589 590 591 592 593
		state->packetchunks[state->chunk_index++].chunk = state->jpegHeader[port];
		state->packetchunks[state->chunk_index  ].bytes = state->exifSize[port];
		state->packetchunks[state->chunk_index++].chunk = state->ed[port];
		state->packetchunks[state->chunk_index  ].bytes = state->head_size[port] - 2;
		state->packetchunks[state->chunk_index++].chunk = &(state->jpegHeader[port][2]);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
594 595
	} else {
		D3(fprintf(debug_file, "_8_"));
596 597
		state->packetchunks[state->chunk_index  ].bytes = state->head_size[port];
		state->packetchunks[state->chunk_index++].chunk = state->jpegHeader[port];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
598 599
	}
	D3(fprintf(debug_file, "_9_"));
600 601

/*! JPEG image data may be split in two segments (rolled over buffer end) - process both variants */
602
	if ((state->cirbuf_rp[port] + state->jpeg_len) > state->circ_buff_size[port]) { //! two segments
603
/*! copy from the beginning of the frame to the end of the buffer */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
604
		D3(fprintf(debug_file, "_10_"));
605 606
		state->packetchunks[state->chunk_index  ].bytes = state->circ_buff_size[port] - state->cirbuf_rp[port];
		state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp[port] >> 2];
607
/*! copy from the beginning of the buffer to the end of the frame */
608
		state->packetchunks[state->chunk_index  ].bytes = state->jpeg_len - (state->circ_buff_size[port] - state->cirbuf_rp[port]);
609
		state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][0];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
610 611
	} else { // single segment
		D3(fprintf(debug_file, "_11_"));
612 613

/*! copy from the beginning of the frame to the end of the frame (no buffer rollovers) */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
614
		state->packetchunks[state->chunk_index  ].bytes = state->jpeg_len;
615
		state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp[port] >> 2];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
616 617 618 619 620 621 622
	}
	D3(fprintf(debug_file, "_12_"));
	state->packetchunks[state->chunk_index  ].bytes = 2;
	state->packetchunks[state->chunk_index++].chunk = (unsigned char*)trailer;

	switch (state->format) {
	case CAMOGM_FORMAT_NONE: rslt = 0; break;
623 624 625
	case CAMOGM_FORMAT_OGM:  rslt = camogm_frame_ogm(state); break;
	case CAMOGM_FORMAT_JPEG: rslt = camogm_frame_jpeg(state); break;
	case CAMOGM_FORMAT_MOV:  rslt = camogm_frame_mov(state); break;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
626 627 628
	default: rslt = 0; // do nothing
	}
	if (rslt) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
629
		D3(fprintf(debug_file, "sendImageFrame:12: camogm_frame_***() returned %d\n", rslt));
Mikhail Karpenko's avatar
Mikhail Karpenko committed
630 631
		return rslt;
	}
632
	if (state->kml_used) rslt = camogm_frame_kml(state);  // will turn on state->kml_used if it can
Mikhail Karpenko's avatar
Mikhail Karpenko committed
633 634 635
	if (rslt) return rslt;

	D3(fprintf(debug_file, "_14_"));
636
//!advance frame pointer
Mikhail Karpenko's avatar
Mikhail Karpenko committed
637
	state->frameno++;
638
	state->cirbuf_rp[port] = lseek(state->fd_circ[port], LSEEK_CIRC_NEXT, SEEK_END);
639
//!optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
640
	if (state->save_gp) lseek(state->fd_circ[port], LSEEK_CIRC_SETP, SEEK_END);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
641 642
	D3(fprintf(debug_file, "_15_\n"));
	if (state->frames_skip > 0) {
643
		state->frames_skip_left[port] = state->frames_skip;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
644
	} else if (state->frames_skip < 0) {
645
		state->frames_skip_left[port] += -(state->frames_skip);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
646 647
	}
	return 0;
648 649
}

650 651 652 653 654 655
/**
 * @brief Stop current recording. If recording was not started, this function has no effect. This function
 * calls @e camogm_end_* for the current file format.
 * @param[in]   state   a pointer to a structure containing current state
 * @return      0 if the operation was stopped successfully and negative error code otherwise
 */
656
int camogm_stop(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
657 658 659
{
	int rslt = 0;

660 661
	if (state->prog_state != STATE_RUNNING) {
		if (state->prog_state != STATE_STARTING) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
662 663
			D2(fprintf(debug_file, "Recording was not running, nothing to stop\n"));
		} else {
664
			pthread_mutex_lock(&state->mutex);
665
			state->prog_state = STATE_STOPPED;
666
			pthread_mutex_unlock(&state->mutex);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
667 668 669 670 671
			D1(fprintf(debug_file, "Dropping attempt to start\n"));
		}
		return 0;
	}
	D1(fprintf(debug_file, "Ending recording\n"));
672
	if (state->kml_used) camogm_end_kml(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
673 674
	switch (state->format) {
	case CAMOGM_FORMAT_NONE: rslt = 0; break;
675
	case CAMOGM_FORMAT_OGM:  rslt = camogm_end_ogm(state); break;
676
	case CAMOGM_FORMAT_JPEG: rslt = camogm_end_jpeg(state); break;
677
	case CAMOGM_FORMAT_MOV:  rslt = camogm_end_mov(state); break;
678
///    default: return 0; // do nothing
Mikhail Karpenko's avatar
Mikhail Karpenko committed
679
	}
680
//! now close video file (if it is open)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
681 682 683 684
	if (state->vf) fclose(state->vf);
	state->vf = NULL;
	if (rslt) return rslt;
	state->last = 1;
685
	pthread_mutex_lock(&state->mutex);
686
	state->prog_state = STATE_STOPPED;
687
	pthread_mutex_unlock(&state->mutex);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
688
	return 0;
689 690
}

691 692 693 694 695 696
/**
 * @brief Free all file format handlers that were used. @e camogm_free_* for
 * each format used will be called.
 * @param[in]   state   a pointer to a structure containing current state
 * @return      None
 */
697
void camogm_free(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
698
{
699
	for (int f = 0; f < 31; f++) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
700 701 702 703 704 705 706 707 708 709
		if (state->formats & ( 1 << (state->format))) {
			switch (f) {
			case CAMOGM_FORMAT_NONE: break;
			case CAMOGM_FORMAT_OGM:  camogm_free_ogm(); break;
			case CAMOGM_FORMAT_JPEG: camogm_free_jpeg(); break;
			case CAMOGM_FORMAT_MOV:  camogm_free_mov(); break;
			}
		}
	}
	state->formats = 0;
710 711
}

712 713 714 715 716 717
/**
 * @brief Reset read pointer and overruns counter for each sensor port
 * @param[in]   state   a pointer to a structure containing current state
 * @return      None
 */
void camogm_reset(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
718
{
719
	FOR_EACH_PORT(int, chn) {
720 721
		state->cirbuf_rp[chn] = -1;
		state->buf_overruns[chn] = -1; // first overrun will not count
722
	}
723 724
}

725
/** @brief kml parameter setter */
726
void  camogm_kml_set_enable(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
727 728
{
	state->kml_enable = d;
729
}
730
/** @brief kml parameter setter */
731
void  camogm_kml_set_horHalfFov(camogm_state *state, double dd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
732 733
{
	state->kml_horHalfFov = dd;
734
}
735
/** @brief kml parameter setter */
736
void  camogm_kml_set_vertHalfFov(camogm_state *state, double dd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
737 738
{
	state->kml_vertHalfFov = dd;
739
}
740
/** @brief kml parameter setter */
741
void  camogm_kml_set_height_mode(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
742 743
{
	state->kml_height_mode = d;
744
}
745
/** @brief kml parameter setter */
746
void  camogm_kml_set_height(camogm_state *state, double dd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
747 748
{
	state->kml_height = dd;
749
}
750
/** @brief kml parameter setter */
751
void  camogm_kml_set_period(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
752 753 754 755
{
	state->kml_period = d;
	state->kml_last_ts = 0;
	state->kml_last_uts = 0;
756
}
757
/** @brief kml parameter setter */
758
void  camogm_kml_set_near(camogm_state *state, double dd)   // distance to PhotoOverlay
Mikhail Karpenko's avatar
Mikhail Karpenko committed
759 760
{
	state->kml_near = dd;
761 762 763
}


764
void  camogm_set_segment_duration(camogm_state *state, int sd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
765 766
{
	state->segment_duration = sd;
767
}
768
void  camogm_set_segment_length(camogm_state *state, int sl)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
769 770
{
	state->segment_length =   sl;
771
}
772
void  camogm_set_save_gp(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
773 774
{
	state->save_gp =   d;
775
}
776
void  camogm_set_exif(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
777 778
{
	state->exif =   d;
779 780
}

781
void  camogm_set_greedy(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
782 783
{
	state->greedy =   d ? 1 : 0;
784
}
785
void  camogm_set_ignore_fps(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
786 787
{
	state->ignore_fps =   d ? 1 : 0;
788 789
}

790 791 792 793 794 795 796
/**
 * @brief Set file name prefix or raw device file name.
 * @param[in]   state   a pointer to a structure containing current state
 * @param[in]   p       a pointer to the prefix string. The string is prepended to
 * the file name as is.
 * @param[in]   type    the type of prefix, can be one of #path_type
 */
797
void  camogm_set_prefix(camogm_state *state, const char * p, path_type type)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
798
{
799 800 801 802 803 804 805
	if (type == FILE_PATH) {
		strncpy(state->path_prefix, p, sizeof(state->path_prefix) - 1);
		state->path_prefix[sizeof(state->path_prefix) - 1] = '\0';
	} else if (type == RAW_PATH && (strncmp(p, "/dev/", 5) == 0)) {
		strncpy(state->rawdev.rawdev_path, p, sizeof(state->rawdev.rawdev_path) - 1);
		state->rawdev.rawdev_path[sizeof(state->rawdev.rawdev_path) - 1] = '\0';
		state->rawdev.end_pos = get_disk_size(state->rawdev.rawdev_path);
806 807 808
		if (state->rawdev.end_pos == 0) {
			state->rawdev_op = 0;
			state->rawdev.end_pos = state->rawdev.start_pos;
809 810
			state->rawdev.rawdev_path[0] = '\0';
			D0(fprintf(debug_file, "ERROR: unable to initiate raw device operation\n"));
811
		} else {
812
			D0(fprintf(debug_file, "WARNING: raw device write initiated\n"));
813
			state->rawdev_op = 1;
814
			/* debug code follows */
815
			state->rawdev.end_pos = 134217728;
816
			/* end of debug code */
817 818
		}
	}
819 820
}

821
/** @brief Set timescale @e d */
822
void  camogm_set_timescale(camogm_state *state, double d)   //! set timescale, default=1,000,000
Mikhail Karpenko's avatar
Mikhail Karpenko committed
823 824
{
	state->set_timescale =  d;
825
	if (state->prog_state == STATE_STOPPED) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
826 827
		state->timescale = state->set_timescale;
	}
828 829
}

830
/** @brief Set the number of frames @e d to be skipped during recording. This values is common for each sensor port */
831
void  camogm_set_frames_skip(camogm_state *state, int d)   //! set frames to skip (for time lapse)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
832 833
{
	state->set_frames_skip =  d;
834
	if (state->prog_state == STATE_STOPPED) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
835
		state->frames_skip =      state->set_frames_skip;
836
		state->frames_skip_left[state->port_num] = 0;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
837
	}
838 839
}

840 841
/** @brief Set file format @e d the images will be recorded to. This function calls corresponding @e camogm_init_*
 * function for the format selected.*/
842
void  camogm_set_format(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
843 844 845 846
{
	int rslt = 0;

	state->set_format =  d;
847
	if (state->prog_state == STATE_STOPPED) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
848 849 850 851
		state->format =  state->set_format;
		switch (state->format) {
		case CAMOGM_FORMAT_NONE: rslt = 0; break;
		case CAMOGM_FORMAT_OGM:  rslt = camogm_init_ogm(); break;
852
		case CAMOGM_FORMAT_JPEG: rslt = camogm_init_jpeg(state); break;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
853 854 855 856 857 858 859
		case CAMOGM_FORMAT_MOV:  rslt = camogm_init_mov(); break;
		}
		if (rslt) {
			D0(fprintf(debug_file, "%s:%d: Error setting format to=%d\n", __FILE__, __LINE__, state->format));
		}
		state->formats |= 1 << (state->format);
	}
860
}
861 862

/** @brief Set max number of frames @e d */
863
void  camogm_set_max_frames(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
864 865
{
	state->set_max_frames =  d;
866 867
	if (state->prog_state == STATE_STOPPED)
		state->max_frames = d;
868
}
869 870

/** @brief Set the number of frames @e d recorded per chunk */
871
void  camogm_set_frames_per_chunk(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
872 873
{
	state->set_frames_per_chunk =  d;
874 875
	if (state->prog_state == STATE_STOPPED)
		state->frames_per_chunk = d;
876
}
877 878

/** @brief Set the time stamp @e d when recording should be started */
879
void  camogm_set_start_after_timestamp(camogm_state *state, double d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
880 881
{
	state->start_after_timestamp =  d;
882 883
}

884 885 886 887 888 889
/**
 * @brief Print current status either as plain text or in xml format
 * @param[in]   state   to a structure containing current state
 * @param[in]   fn      a pointer to a file name which will be used for output. Can be NULL or 'stdout' for
 * output to stdout, 'stderr' for output to stderr and a file name for output to a file
 * @param[in]   xml     flag indicating that the output should be in xml format
890
 * @note access to state->rawdev.curr_pos_r is not locked in reading thread
891 892
 * @return      None
 */
893
void  camogm_status(camogm_state *state, char * fn, int xml)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
894 895
{
	int _len = 0;
896
	int _dur = 0, _udur = 0, _dur_raw, _udur_raw;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
897
	FILE* f;
898
	char *_state, *_output_format, *_using_exif, *_using_global_pointer, *_compressor_state[SENSOR_PORTS];
899
	int _b_free[SENSOR_PORTS], _b_used[SENSOR_PORTS], _b_size[SENSOR_PORTS];
900 901
	int _frames_remain[SENSOR_PORTS] = {0};
	int _sec_remain[SENSOR_PORTS] = {0};
Mikhail Karpenko's avatar
Mikhail Karpenko committed
902 903 904
	int _frames_skip = 0;
	int _sec_skip = 0;
	char *_kml_enable, *_kml_used, *_kml_height_mode;
905
	unsigned int _percent_done;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
906 907 908 909 910

	_kml_enable =      state->kml_enable ? "yes" : "no";
	_kml_used =        state->kml_used ? "yes" : "no";
	_kml_height_mode = state->kml_height_mode ? "GPS altitude" : "map ground level"; //! 1 - actual, 0 - ground

911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
	for (int chn = 0; chn < SENSOR_PORTS; chn++) {
		_b_free[chn] = getGPValue(chn, G_FREECIRCBUF);
		_b_used[chn] = getGPValue(chn, G_CIRCBUFSIZE) - getGPValue(state->port_num, G_FREECIRCBUF);
		_b_size[chn] = getGPValue(chn, G_FRAME_SIZE);
		_compressor_state[chn] = (getGPValue(chn, P_COMPRESSOR_RUN) == 2) ? "running" : "stopped";
		if (state->frames_skip > 0)
			_frames_remain[chn] = state->frames_skip_left[chn];
		else if (state->frames_skip < 0)
			_sec_remain[chn] = (state->frames_skip_left[chn] - state->this_frame_params[chn].timestamp_sec);

		_dur_raw = state->this_frame_params[chn].timestamp_sec - state->frame_params[chn].timestamp_sec;
		_udur_raw = state->this_frame_params[chn].timestamp_usec - state->frame_params[chn].timestamp_usec;
		if (_udur_raw < 0) {
			_dur_raw -= 1;
			_udur_raw += 1000000;
		} else if (_udur_raw >= 1000000) {
			_dur_raw += 1;
			_udur_raw -= 1000000;
		}
		_dur += _dur_raw;
		_udur += _udur_raw;
		if (_udur > 1000000) {
			_dur += 1;
			_udur -= 1000000;
		}
	}
	if ( state->frames_skip > 0 ) {
		_frames_skip = state->frames_skip;
	} else if ( state->frames_skip < 0 ) {
		_sec_skip = -(state->frames_skip);
	}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
942 943 944 945 946 947 948 949 950 951 952 953

	if (!fn) f = stdout;
	else if (strcmp(fn, "stdout") == 0) f = stdout;
	else if (strcmp(fn, "stderr") == 0) f = stderr;
	else {
		if (!((f = fopen(fn, "w")))) {
			D0(fprintf(debug_file, "Error opening %s\n", fn));
			return;
		}
	}
	if (state->vf) _len = ftell(state->vf);                                 //! for ogm
	else if ((state->ivf) >= 0) _len = lseek(state->ivf, 0, SEEK_CUR);      //!for mov
954 955 956 957 958 959 960 961 962 963 964 965 966
	switch (state->prog_state) {
	case STATE_STOPPED:
		_state = "stopped";
		break;
	case STATE_RUNNING:
		_state = "running";
		break;
	case STATE_STARTING:
		_state = "starting";
		break;
	case STATE_READING:
		_state = "reading";
		break;
967 968
	default:
		_state = "stopped";
969
	}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
970 971 972 973 974 975
	_output_format = state->format ? ((state->format == CAMOGM_FORMAT_OGM) ? "ogm" :
					  ((state->format == CAMOGM_FORMAT_JPEG) ? "jpeg" :
					   ((state->format == CAMOGM_FORMAT_MOV) ? "mov" :
					       "other"))) : "none";
	_using_exif =    state->exif ? "yes" : "no";
	_using_global_pointer = state->save_gp ? "yes" : "no";
976 977 978 979
	if (state->rawdev.curr_pos_r != 0 && state->rawdev.curr_pos_r > state->rawdev.start_pos)
		_percent_done = 100 * state->rawdev.curr_pos_r / (state->rawdev.end_pos - state->rawdev.start_pos);
	else
		_percent_done = 0;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016

	if (xml) {
		fprintf(f, "<?xml version=\"1.0\"?>\n" \
			"<camogm_state>\n" \
			"  <state>\"%s\"</state>\n" \
			"  <file_name>\"%s\"</file_name>\n" \
			"  <frame_number>%d</frame_number>\n" \
			"  <start_after_timestamp>%f</start_after_timestamp>\n"	\
			"  <file_duration>%d.%06d</file_duration>\n" \
			"  <file_length>%d</file_length>\n" \
			"  <frames_skip_left>%d</frames_skip_left>\n" \
			"  <seconds_skip_left>%d</seconds_skip_left>\n"	\
			"  <frame_width>%d</frame_width>\n" \
			"  <frame_height>%d</frame_height>\n" \
			"  <format>\"%s\"</format>\n" \
			"  <exif>\"%s\"</exif>\n" \
			"  <prefix>\"%s\"</prefix>\n" \
			"  <max_duration>%d</max_duration>\n" \
			"  <max_length>%d</max_length>\n" \
			"  <max_frames>%d</max_frames>\n" \
			"  <timescale>%f</timescale>\n"	\
			"  <frames_per_chunk>%d</frames_per_chunk>\n" \
			"  <last_error_code>%d</last_error_code>\n" \
			"  <debug_output>\"%s\"</debug_output>\n" \
			"  <debug_level>%d</debug_level>\n" \
			"  <use_global_rp>\"%s\"</use_global_rp>\n" \
			"  <kml_enable>\"%s\"</kml_enable>\n" \
			"  <kml_used>\"%s\"</kml_used>\n" \
			"  <kml_path>\"%s\"</kml_path>\n" \
			"  <kml_horHalfFov>\"%f\"</kml_horHalfFov>\n" \
			"  <kml_vertHalfFov>\"%f\"</kml_vertHalfFov>\n"	\
			"  <kml_near>\"%f\"</kml_near>\n" \
			"  <kml_height_mode>\"%s\"</kml_height_mode>\n"	\
			"  <kml_height>\"%f\"</kml_height>\n" \
			"  <kml_period>%d</kml_period>\n" \
			"  <kml_last_ts>%d.%06d</kml_last_ts>\n" \
			"  <greedy>\"%s\"</greedy>\n" \
1017 1018
			"  <ignore_fps>\"%s\"</ignore_fps>\n" \
			"  <raw_device_path>\"%s\"</raw_device_path>\n" \
1019 1020
			"  <raw_device_overruns>%d</raw_device_overruns>\n" \
			"  <raw_device_pos_write>0x%llx</raw_dev_pos_write>\n" \
1021
			"  <raw_device_pos_read>0x%llx (%d%% done)</raw_device_pos_read>\n",
1022 1023
			_state,  state->path, state->frameno, state->start_after_timestamp, _dur, _udur, _len, \
			_frames_skip, _sec_skip, \
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1024 1025
			state->width, state->height, _output_format, _using_exif, \
			state->path_prefix, state->segment_duration, state->segment_length, state->max_frames, state->timescale, \
1026
			state->frames_per_chunk,  state->last_error_code, \
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1027 1028 1029
			state->debug_name, debug_level, _using_global_pointer, \
			_kml_enable, _kml_used, state->kml_path, state->kml_horHalfFov, state->kml_vertHalfFov, state->kml_near, \
			_kml_height_mode, state->kml_height, state->kml_period, state->kml_last_ts, state->kml_last_uts, \
1030
			state->greedy ? "yes" : "no", state->ignore_fps ? "yes" : "no", state->rawdev.rawdev_path,
1031
			state->rawdev.overrun, state->rawdev.curr_pos_w, state->rawdev.curr_pos_r, _percent_done);
1032 1033 1034 1035 1036

		FOR_EACH_PORT(int, chn) {
			char *_active = is_chn_active(state, chn) ? "yes" : "no";
			fprintf(f,
			"\t<sensor_port_%d>\n" \
1037
			"\t\t<channel_active>\"%s\"</channel_active>\n" \
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
			"\t\t<compressor_state>\"%s\"</compressor_state>\n" \
			"\t\t<frame_size>%d</frame_size>\n" \
			"\t\t<frames_skip>%d</frames_skip>\n" \
			"\t\t<seconds_skip>%d</seconds_skip>\n" \
			"\t\t<buffer_overruns>%d</buffer_overruns>\n" \
			"\t\t<buffer_minimal>%d</buffer_minimal>\n" \
			"\t\t<frame_period>%d</frame_period>\n" \
			"\t\t<buffer_free>%d</buffer_free>\n" \
			"\t\t<buffer_used>%d</buffer_used>\n" \
			"\t\t<circbuf_rp>%d</circbuf_rp>\n" \
			"\t</sensor_port_%d>\n",
				chn,
				_active,
				_compressor_state[chn],
				_b_size[chn],
				_frames_remain[chn],
				_sec_remain[chn],
				state->buf_overruns[chn],
				state->buf_min[chn],
				state->frame_period[chn],
				_b_free[chn],
				_b_used[chn],
				state->cirbuf_rp[chn],
				chn
				);
1063
		}
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
		fprintf(f, "</camogm_state>\n");
	} else {
		fprintf(f, "state              \t%s\n",        _state);
		fprintf(f, "file               \t%s\n",        state->path);
		fprintf(f, "frame              \t%d\n",        state->frameno);
		fprintf(f, "start_after_timestamp \t%f\n",     state->start_after_timestamp);
		fprintf(f, "file duration      \t%d.%06d sec\n", _dur, _udur);
		fprintf(f, "file length        \t%d B\n",      _len);
		fprintf(f, "width              \t%d (0x%x)\n", state->width, state->width);
		fprintf(f, "height             \t%d (0x%x)\n", state->height, state->height);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1074
		fprintf(f, "\n");
1075 1076
		fprintf(f, "output format      \t%s\n",        _output_format);
		fprintf(f, "using exif         \t%s\n",        _using_exif);
1077 1078 1079
		fprintf(f, "path prefix        \t%s\n",        state->path_prefix);
		fprintf(f, "raw device path    \t%s\n",        state->rawdev.rawdev_path);
		fprintf(f, "raw device overruns\t%d\n",        state->rawdev.overrun);
1080 1081
		fprintf(f, "raw write position \t0x%llx\n",    state->rawdev.curr_pos_w);
		fprintf(f, "raw read position  \t0x%llx\n",    state->rawdev.curr_pos_r);
1082
		fprintf(f, "   percent done    \t%d%%\n",      _percent_done);
1083 1084
		fprintf(f, "max file duration  \t%d sec\n",    state->segment_duration);
		fprintf(f, "max file length    \t%d B\n",      state->segment_length);
1085 1086 1087 1088 1089
		fprintf(f, "max frames         \t%d\n",        state->max_frames);
		fprintf(f, "timescale          \t%f\n",        state->timescale);
		fprintf(f, "frames per chunk   \t%d\n",        state->frames_per_chunk);
		fprintf(f, "greedy             \t%s\n",        state->greedy ? "yes" : "no");
		fprintf(f, "ignore fps         \t%s\n",        state->ignore_fps ? "yes" : "no");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1090
		fprintf(f, "\n");
1091
		fprintf(f, "last error code    \t%d\n",        state->last_error_code);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1092
		fprintf(f, "\n");
1093 1094 1095
		fprintf(f, "debug output to    \t%s\n",        state->debug_name);
		fprintf(f, "debug level        \t%d\n",        debug_level);
		fprintf(f, "use global pointer \t%s\n",        _using_global_pointer);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1096
		fprintf(f, "\n\n");
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
		fprintf(f, "kml_enable         \t%s\n",        _kml_enable);
		fprintf(f, "kml_used           \t%s\n",        _kml_used);
		fprintf(f, "kml_path           \t%s\n",        state->kml_path);
		fprintf(f, "kml_horHalfFov     \t%f degrees\n", state->kml_horHalfFov);
		fprintf(f, "kml_vertHalfFov    \t%f degrees\n", state->kml_vertHalfFov);
		fprintf(f, "kml_near           \t%f m\n",      state->kml_near);
		fprintf(f, "kml height mode    \t%s\n",        _kml_height_mode);
		fprintf(f, "kml_height (extra) \t%f m\n",      state->kml_height);
		fprintf(f, "kml_period         \t%d\n",        state->kml_period);
		fprintf(f, "kml_last_ts        \t%d.%06d\n",   state->kml_last_ts, state->kml_last_uts);
		fprintf(f, "\n");
		FOR_EACH_PORT(int, chn) {
			char *_active = is_chn_active(state, chn) ? "yes" : "no";
			fprintf(f, "===== Sensor port %d status =====\n", chn);
			fprintf(f, "enabled            \t%s\n",    _active);
			fprintf(f, "compressor state   \t%s\n",    _compressor_state[chn]);
			fprintf(f, "frame size         \t%d\n",    _b_size[chn]);
			if (_frames_skip > 0)
				fprintf(f, "frames to skip \t%d (left %d)\n", _frames_skip, _frames_remain[chn]);
			if (_sec_skip < 0 )
				fprintf(f, "timelapse period \t%d sec (remaining %d sec)\n", _sec_skip, _sec_remain[chn]);
			fprintf(f, "buffer overruns    \t%d\n",    state->buf_overruns[chn]);
			fprintf(f, "buffer minimal     \t%d\n",    state->buf_min[chn]);
			fprintf(f, "frame period       \t%d (0x%x)\n", state->frame_period[chn], state->frame_period[chn]);
			fprintf(f, "buffer free        \t%d\n",    _b_free[chn]);
			fprintf(f, "buffer used        \t%d\n",    _b_used[chn]);
			fprintf(f, "circbuf_rp         \t%d (0x%x)\n", state->cirbuf_rp[chn], state->cirbuf_rp[chn]);
			fprintf(f, "\n");
		}
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1126 1127
	}
	if ((f != stdout) && (f != stderr)) fclose(f);
1128
	FOR_EACH_PORT(int, chn) {if (state->buf_overruns[chn] >= 0) state->buf_overruns[chn] = 0;}  //! resets overruns after reading status , so "overruns" means since last reading status
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1129
	state->last_error_code = 0;                             //! Reset error
1130
	FOR_EACH_PORT(int, chn) {state->buf_min[chn] = _b_free[chn];}
1131 1132
}

1133
/**
1134
 * @brief Read a single command from pipe
1135 1136 1137
 * @param[in]   npipe   pointer to command pipe
 * @return Pointer to null terminated string if a command is available or NULL otherwise
 */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1138 1139 1140 1141
char * getLineFromPipe(FILE* npipe)
{
	int fl;
	char * nlp;
1142 1143 1144
	static char cmdbuf[1024];
	static int cmdbufp = 0; // current input pointer in the command buffer (read from pipe)
	static int cmdstrt = 0; // start of the next partial command
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1145

1146
//!remove used string if any
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1147
	if (cmdstrt > 0) {
1148
//!moving overlapping strings
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1149 1150 1151 1152
		memmove(cmdbuf, &cmdbuf[cmdstrt], sizeof(cmdbuf) - cmdstrt);
		cmdbufp -= cmdstrt;
		cmdstrt = 0;
	}
1153
//! is there any complete string in a buffer?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1154 1155 1156 1157 1158
	if (!cmdbufp) cmdbuf[cmdbufp] = 0;  //!null-terminate first access (probably not needed for the static buffer
	nlp = strpbrk(cmdbuf, ";\n");
	if (!nlp) { //!no complete string, try to read more
		fl = fread(&cmdbuf[cmdbufp], 1, sizeof(cmdbuf) - cmdbufp - 1, npipe);
		cmdbuf[cmdbufp + fl] = 0;
1159
//! is there any complete string in a buffer after reading?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
		nlp = strpbrk(&cmdbuf[cmdbufp], ";\n"); //! there were no new lines before cmdbufp
		cmdbufp += fl;                          //!advance pointer after pipe read
	}
	if (nlp) {
		nlp[0] = 0;
		cmdstrt = nlp - cmdbuf + 1;
		for (fl = 0; cmdbuf[fl] && strchr(" \t", cmdbuf[fl]); fl++) ;
		return &cmdbuf[fl];
	} else {
		return NULL;
	}
1171
}
1172

1173 1174 1175 1176 1177 1178 1179
/**
 * @brief Read and execute commands sent over command pipe
 * @param state   pointer to a structure containing current state
 * @param npipe   pointer to command pipe
 * @return        0 if pipe was empty, positive value corresponding to a command processed or
 * -1 in case of an error
 */
1180
int parse_cmd(camogm_state *state, FILE* npipe)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1181 1182 1183 1184 1185 1186 1187
{
	char * cmd;
	char * args;
	char * argse;
	int d;
	double dd;

1188
//!skip empty commands
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1189 1190 1191
	while (((cmd = getLineFromPipe(npipe))) && !cmd[0]) ;
	if (!cmd) return 0;  //! nothing in the pipe
	D2(fprintf(debug_file, "Got command: '%s'\n", cmd));
1192

1193
#ifdef DISABLE_CODE
1194
/// Acknowledge received command by copying frame number to per-daemon parameter
1195
//	GLOBALPARS(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num]) = GLOBALPARS(state->port_num, G_THIS_FRAME);
1196
	setGValue(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num], getGPValue(state->port_num, G_THIS_FRAME));
1197
#endif /* DISABLE_CODE */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1198
	args = strpbrk(cmd, "= \t");
1199
//! is it just a single word command or does it have parameters?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1200 1201 1202 1203 1204
	if (args) {
		args[0] = 0;
		args++;
		while (strchr("= \t", args[0])) args++;
		if (args[0]) {
1205
//! ltrim (args)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1206 1207 1208 1209
			for (argse = strchr(args, '\0') - 1; strchr("= \t", argse[0]); argse--) argse[0] = '\0';
		}
		if (!args[0]) args = NULL;
	}
1210
//! now cmd is trimmed, arg is NULL or a pointer to trimmed command arguments
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1211
	if (strcmp(cmd, "start") == 0) {
1212
		camogm_start(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1213 1214
		return 1;
	} else if (strcmp(cmd, "reset") == 0) { //! will reset pointer to the last acquired frame (if any)
1215
		camogm_reset(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1216 1217
		return 2;
	} else if (strcmp(cmd, "stop") == 0) {
1218
		camogm_stop(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1219 1220
		return 3;
	} else if (strcmp(cmd, "exit") == 0) {
1221 1222
		camogm_stop(state);
		camogm_free(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1223 1224 1225
		exit(0);
	} else if (strcmp(cmd, "duration") == 0) {
		if (!(args) || (((d = strtol(args, NULL, 10))) <= 0)) d = DEFAULT_DURATION;
1226
		camogm_set_segment_duration(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1227 1228 1229
		return 4;
	} else if (strcmp(cmd, "length") == 0) {
		if (!(args) || (((d = strtol(args, NULL, 10))) <= 0)) d = DEFAULT_LENGTH;
1230
		camogm_set_segment_length(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1231 1232
		return 5;
	} else if (strcmp(cmd, "prefix") == 0) {