camogm.c 64.7 KB
Newer Older
1
/*!***************************************************************************
Mikhail Karpenko's avatar
Mikhail Karpenko committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
   *! FILE NAME  : camogm.c
   *! DESCRIPTION: Program to write captured video (and audio) to camera file system
   *! using Ogg container.
   *! Original implementation will copy package data to a buffer to use library calls?
   *! Copyright (C) 2007 Elphel, Inc.
   *! -----------------------------------------------------------------------------**
   *!  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/>.
   *! -----------------------------------------------------------------------------**
   *!
   *!  $Log: camogm.c,v $
   *!  Revision 1.13  2011/04/22 22:38:30  elphel
   *!  added "greedy" and "ignore_fps" options
   *!
   *!  Revision 1.12  2010/08/16 17:10:59  elphel
   *!  typo
   *!
   *!  Revision 1.11  2010/08/08 21:14:04  elphel
   *!  8.0.8.38
   *!
   *!  Revision 1.10  2010/08/07 23:38:01  elphel
   *!  fixed skipping (sometimes) frames at start recording
   *!
   *!  Revision 1.9  2010/08/03 06:19:57  elphel
   *!  more debug for debug-level>=3
   *!
   *!  Revision 1.8  2010/08/02 01:26:42  elphel
   *!  error tracking
   *!
   *!  Revision 1.7  2010/08/01 19:30:24  elphel
   *!  new readonly parameter FRAME_SIZE and it support in the applications
   *!
   *!  Revision 1.6  2010/07/18 16:59:09  elphel
   *!  8.0.8.31 - added parameters to camogm (one is to start at certain absolute time, helps to synchronize multiple cameras)
   *!
   *!  Revision 1.5  2010/07/04 19:06:02  elphel
   *!  moved acknowledge earlier
   *!
   *!  Revision 1.4  2010/06/22 18:27:26  elphel
   *!  bug fix
   *!
   *!  Revision 1.3  2010/06/22 16:53:30  elphel
   *!  camogm acknowledges received command by copying G_THIS_FRAME value to G_DAEMON_ERR+3 (default number for camogm)
   *!
   *!  Revision 1.2  2009/02/25 17:50:02  spectr_rain
   *!  removed deprecated dependency
   *!
   *!  Revision 1.1.1.1  2008/11/27 20:04:01  elphel
   *!
   *!
   *!  Revision 1.6  2008/11/21 01:52:53  elphel
   *!  updated for 8.0
   *!
   *!  Revision 1.5  2008/11/20 23:21:32  elphel
   *!  Put FIXME notes and removed parameters that are not used anymore
   *!
   *!  Revision 1.4  2008/10/29 04:18:28  elphel
   *!  v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
   *!
   *!  Revision 1.3  2008/10/13 16:55:53  elphel
   *!  removed (some) obsolete P_* parameters, renamed CIRCLSEEK to LSEEK_CIRC constants (same as other similar)
   *!
   *!  Revision 1.2  2008/09/07 19:48:08  elphel
   *!  snapshot
   *!
   *!  Revision 1.9  2008/04/13 21:05:19  elphel
   *!  Fixing KML generation
   *!
   *!  Revision 1.8  2008/04/11 23:09:33  elphel
   *!  modified to handle kml generation
   *!
   *!  Revision 1.7  2008/04/07 09:13:34  elphel
   *!  Changes related to new Exif generation/processing
   *!
   *!  Revision 1.6  2008/01/14 22:59:00  elphel
   *!  7.1.7.4 - added timelapse mode to camogm
   *!
   *!  Revision 1.5  2007/12/03 08:28:45  elphel
   *!  Multiple changes, mostly cleanup
   *!
   *!  Revision 1.4  2007/11/29 00:38:57  elphel
   *!  fixed timescale bug
   *!
   *!  Revision 1.3  2007/11/19 05:07:19  elphel
   *!  fixed 2 typos
   *!
   *!  Revision 1.2  2007/11/19 03:23:21  elphel
   *!  7.1.5.5 Added support for *.mov files in camogm.
   *!
   *!  Revision 1.1  2007/11/16 08:49:56  elphel
   *!  Initial release of camogm - program to record video/image to the camera hard drive (or other storage)
   *!
 */
105 106 107 108 109 110 111 112 113 114
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
115
#include <linux/limits.h>
116 117 118 119 120 121
//#include <ctype.h>
//#include <getopt.h>
#include <time.h>
#include <string.h>

#include <netinet/in.h> /*little <-> big endian ?*/
Mikhail Karpenko's avatar
Mikhail Karpenko committed
122
#include <sys/mman.h>   /* mmap */
123 124
#include <sys/ioctl.h>

Mikhail Karpenko's avatar
Mikhail Karpenko committed
125 126
#include <c313a.h>
#include <exifa.h>
127 128
#include <asm/byteorder.h>

Mikhail Karpenko's avatar
Mikhail Karpenko committed
129
#include <ogg/ogg.h>    // has to be before ogmstreams.h
130 131 132 133 134 135 136 137 138
#include "ogmstreams.h" // move it to <>?

//#include "camogm_exif.h"
#include "camogm_ogm.h"
#include "camogm_jpeg.h"
#include "camogm_mov.h"
#include "camogm_kml.h"
#include "camogm.h"

139 140 141
/* debug code follows */
// this will be defined in c313a.h
#define SENSOR_PORTS 4
142

143 144
#undef GLOBALPARS
#define GLOBALPARS(p, x) (globalPars[(p)][(x)-FRAMEPAR_GLOBALS])
145

146
/* end of debug code */
147

148
#define COMMAND_LOOP_DELAY 500000 //0.5sec
149
#define DEFAULT_DEBUG_LVL 6
150 151
#define TRAILER_SIZE   0x02
#define MAP_OPTIONS MAP_FILE | MAP_PRIVATE
152

153
char trailer[TRAILER_SIZE] = { 0xff, 0xd9 };
154

155 156 157 158 159 160 161 162 163 164 165 166 167
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"
};
168
unsigned long * ccam_dma_buf[SENSOR_PORTS];           /* mmapped arrays */
169 170 171 172 173

int lastDaemonBit[SENSOR_PORTS] = {DAEMON_BIT_CAMOGM};
struct framepars_all_t   *frameParsAll[SENSOR_PORTS];
struct framepars_t       *framePars[SENSOR_PORTS];
unsigned long            *globalPars[SENSOR_PORTS]; /// parameters that are not frame-related, their changes do not initiate any actions
174

Mikhail Karpenko's avatar
Mikhail Karpenko committed
175 176 177 178
#define DEFAULT_DURATION 60             /*!default segment duration (seconds) */
#define DEFAULT_LENGTH 100000000        /*!default segment length (B) */
#define DEFAULT_GREEDY     0            /*!behavior for the files: 0 clean buffer, 1 - save as much as possible */
#define DEFAULT_IGNORE_FPS 0            /*!0 restartf file if fps changed, 1 - ignore variable fps (and skip less frames) */
179 180 181


//!Next 2 for Quicktime (mostly)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
182 183
#define DEFAULT_FRAMES           16384  /* Maximal number of frames in file segment (each need 4* (1 + 1/frames_per_chunk) bytes for the frame index */
#define DEFAULT_FRAMES_PER_CHUNK    10  /*second sparse index - for Quicktime fast forward */
184

Mikhail Karpenko's avatar
Mikhail Karpenko committed
185 186
#define DEFAULT_LENGTH 100000000        /*!default segment length (B) */
#define DEFAULT_EXIF 1                  /* use Exif */
187 188

static char cmdbuf[1024];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
189 190
static int cmdbufp = 0; // current input pointer in the command buffer (read from pipe)
static int cmdstrt = 0; // start of the next partial command
191

192 193
camogm_state sstate[SENSOR_PORTS];
//camogm_state * state;
194

Mikhail Karpenko's avatar
Mikhail Karpenko committed
195
int debug_level;
196 197
FILE* debug_file;

198
void camogm_init(camogm_state *state, unsigned int port, char *pipe_name);
199 200 201 202
int camogm_start(camogm_state *state);
int camogm_stop(camogm_state *state);
int camogm_reset(camogm_state *state); //! reset circbuf read pointer
int camogm_debug(camogm_state *state, const char *fname);
203
int camogm_debug_level(int d);
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
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);
void  camogm_set_prefix(camogm_state *state, const char * p);
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);
225 226
char * getLineFromPipe(FILE* npipe);

227
int  sendImageFrame(camogm_state *state);
228

229 230 231
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);
232 233 234 235 236 237 238


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

Mikhail Karpenko's avatar
Mikhail Karpenko committed
239
	tmp = (unsigned char*)buf;
240 241 242 243 244 245 246 247 248

	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
249
	tmp = (unsigned char*)buf;
250 251 252 253 254 255 256 257 258 259 260

	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
261
	tmp = (unsigned char*)buf;
262 263 264 265 266 267 268 269 270 271 272

	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;
}

273 274 275 276 277 278 279 280
/**
 * @brief Initialize state for a particular sensor port.
 * @param[in]   state   pointer to #camogm_state structure for a particular sensor channel
 * @param[in]   port    sensor port number
 * @param[in]   pipe_name pointer to command pipe name string
 * @return      none
 */
void camogm_init(camogm_state *state, unsigned int port, char *pipe_name)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
281 282 283 284
{
	const char sserial[] = "elp0";
	int * ipser = (int*)sserial;

285 286
	state->running = 0;                     // mo
	state->starting = 0;                    // mo
Mikhail Karpenko's avatar
Mikhail Karpenko committed
287
	state->vf = NULL;
288 289 290 291 292 293
	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);
294
	camogm_set_start_after_timestamp(state, 0.0); // start any time
295 296
	camogm_set_prefix(state, "\0");
	camogm_set_save_gp(state, 0);
297
	camogm_reset(state);                    // sets state->buf_overruns =- 1
Mikhail Karpenko's avatar
Mikhail Karpenko committed
298 299 300
	state->serialno = ipser[0];
	state->last = 0;
	debug_file = stderr;
301
	camogm_debug_level(DEFAULT_DEBUG_LVL);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
302
	strcpy(state->debug_name, "stderr");
303
	camogm_set_timescale(state, 1.0);
304
	camogm_set_frames_skip(state, 0);       // don't skip
305
	camogm_set_format(state, CAMOGM_FORMAT_OGM);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
306 307 308 309 310 311
	state->exifSize = 0;
	state->exif = DEFAULT_EXIF;
	state->frame_lengths = NULL;
	state->frameno = 0;
	state->formats = 0;
	state->last_error_code = 0;
312

313
	// kml stuff
314
	camogm_kml_set_enable(state, 0);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
315
	state->kml_file = NULL;
316 317 318 319
	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);
320 321
	camogm_kml_set_period(state, 2);        // 2 sec
	camogm_kml_set_near(state, 40.0);       // 40 m (distance to PhotoOverlay)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
322
	state->kml_path[0] = '\0';
323

324
	state->port_num = port;
325
	state->pipe_name = pipe_name;
326 327 328
}


329
int camogm_debug(camogm_state *state, const char * fname)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
{
	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;
349 350
}

Mikhail Karpenko's avatar
Mikhail Karpenko committed
351 352 353 354
int camogm_debug_level(int d)
{
	debug_level = d;
	return 0;
355 356
}

357
int camogm_start(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
358 359 360 361 362 363 364 365
{
	int timestamp_start;
	int rslt;
	int next_metadata_start, next_jpeg_len, fp;

	D1(fprintf(debug_file, "Starting recording\n"));
	double dtime_stamp;
	state->frameno = 0;
366
	state->timescale = state->set_timescale; //! current timescale, default 1
367
///debug
Mikhail Karpenko's avatar
Mikhail Karpenko committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
	int * ifp =      (int*)&(state->frame_params);
	int * ifp_this = (int*)&(state->this_frame_params);
	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;
		case CAMOGM_FORMAT_JPEG: rslt = camogm_init_jpeg(); break;
		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;
384
	state->frames_per_chunk = state->set_frames_per_chunk;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
385
	state->starting = 1; //!may be already set
386
//! Check/set circbuf read pointer
Mikhail Karpenko's avatar
Mikhail Karpenko committed
387 388 389
	D3(fprintf(debug_file, "1: state->cirbuf_rp=0x%x\n", state->cirbuf_rp));
	if ((state->cirbuf_rp < 0) || (lseek(state->fd_circ, state->cirbuf_rp, SEEK_SET) < 0) || (lseek(state->fd_circ, LSEEK_CIRC_VALID, SEEK_END) < 0 )) {
		D3(fprintf(debug_file, "2: state->cirbuf_rp=0x%x\n", state->cirbuf_rp));
390 391
//    state->cirbuf_rp=lseek(state->fd_circ,LSEEK_CIRC_LAST,SEEK_END);
/* In "greedy" mode try to save as many frames from the circbuf as possible */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
392 393 394 395 396
		state->cirbuf_rp = lseek(state->fd_circ, 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, LSEEK_CIRC_PREV, SEEK_END))) >= 0) state->cirbuf_rp = fp;      //!try to have 2 frames available for fps
		}
		state->buf_overruns++;
397
//! file pointer here should match state->rp; so no need to do    lseek(state->fd_circ,state->cirbuf_rp,SEEK_SET);
398
		state->buf_min = getGPValue(state->port_num, G_FREECIRCBUF);
399

Mikhail Karpenko's avatar
Mikhail Karpenko committed
400
	} else {
401
		if (state->buf_min > getGPValue(state->port_num, G_FREECIRCBUF)) state->buf_min = getGPValue(state->port_num, G_FREECIRCBUF);
402

Mikhail Karpenko's avatar
Mikhail Karpenko committed
403 404 405
	}
	D3(fprintf(debug_file, "3: state->cirbuf_rp=0x%x\n", state->cirbuf_rp));
	D3(fprintf(debug_file, "4:lseek(state->fd_circ,LSEEK_CIRC_READY,SEEK_END)=%d\n", (int)lseek(state->fd_circ, LSEEK_CIRC_READY, SEEK_END)));
406 407

//! is this frame ready?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
408 409 410 411
	if (lseek(state->fd_circ, 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));
	state->metadata_start = (state->cirbuf_rp) - 32;
	if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size;
412 413 414

///==================================

415
	memcpy(&(state->frame_params), (unsigned long* )&ccam_dma_buf[state->port_num][state->metadata_start >> 2], 32);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
416
	state->jpeg_len = state->frame_params.frame_length; //! frame_params.frame_length are now the length of bitstream
417 418


Mikhail Karpenko's avatar
Mikhail Karpenko committed
419 420 421 422 423 424 425
	if (state->frame_params.signffff != 0xffff) {
		D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->frame_params.signffff));
		state->cirbuf_rp = -1;
		D1(fprintf(debug_file, "state->cirbuf_rp=0x%x\r\n", (int)state->cirbuf_rp));
		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;
	}
426 427
//!   find location of the timestamp and copy it to the frame_params structure
///==================================
Mikhail Karpenko's avatar
Mikhail Karpenko committed
428 429
	timestamp_start = (state->cirbuf_rp) + ((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) timestamp_start -= state->circ_buff_size;
430
	memcpy(&(state->frame_params.timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
431
/// New - see if current timestamp is later than start one, if not return "CAMOGM_TOO_EARLY" reset read pointer and buffer read pointer
Mikhail Karpenko's avatar
Mikhail Karpenko committed
432 433 434 435 436 437 438 439 440 441 442
	if (state->start_after_timestamp > 0.0) { /// don't bother if it is 0
		dtime_stamp = 0.000001 * state->frame_params.timestamp_usec + state->frame_params.timestamp_sec;
		if (dtime_stamp < state->start_after_timestamp) {
			state->cirbuf_rp = -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));
//! see if next frame is available
	if ((lseek(state->fd_circ, LSEEK_CIRC_NEXT, SEEK_END) < 0 ) ||
443
//! is that next frame ready?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
444 445 446 447 448 449 450 451
	    (((fp = lseek(state->fd_circ, 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, LSEEK_CIRC_NEXT, SEEK_END), (int)lseek(state->fd_circ, LSEEK_CIRC_READY, SEEK_END)));

		lseek(state->fd_circ, state->cirbuf_rp, 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;
452
	memcpy(&(state->this_frame_params), (unsigned long* )&ccam_dma_buf[state->port_num][next_metadata_start >> 2], 32);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
453 454 455 456 457
	next_jpeg_len = state->this_frame_params.frame_length;  //! frame_params.frame_length are now the length of bitstream
	if (state->this_frame_params.signffff != 0xffff) {      //! should not happen ever
		D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->this_frame_params.signffff));
		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]));
458 459 460
//  int * ifp =      (int *) &(state->this_frame_params) ;
//  int * ifp_this = (int *) &(state->this_frame_params) ;

Mikhail Karpenko's avatar
Mikhail Karpenko committed
461 462 463 464
		state->cirbuf_rp = -1;
		return -CAMOGM_FRAME_BROKEN;
	}
	D3(fprintf(debug_file, "7: state->cirbuf_rp=0x%x\n", state->cirbuf_rp));
465 466

//! find location of the timestamp and copy it to the frame_params structure
Mikhail Karpenko's avatar
Mikhail Karpenko committed
467 468
	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) timestamp_start -= state->circ_buff_size;
469
	memcpy(&(state->this_frame_params.timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
470 471 472
//! 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.width  != state->this_frame_params.width) ||
				   (state->frame_params.height != state->this_frame_params.height))) {
473
//! Advance frame pointer to the next (caller should try again)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
474 475 476 477
		state->cirbuf_rp = 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));
478 479

//! calcualte the frame period - time difference (in microseconds)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
480 481
	state->frame_period = (state->this_frame_params.timestamp_usec - state->frame_params.timestamp_usec) +
			      1000000 * (state->this_frame_params.timestamp_sec  - state->frame_params.timestamp_sec);
482 483

//! correct for timelapse modes:
Mikhail Karpenko's avatar
Mikhail Karpenko committed
484 485 486 487
	state->frames_skip =      state->set_frames_skip;
	if (state->frames_skip > 0) {
		state->frames_skip_left = 0;
		state->frame_period *= (state->frames_skip + 1);
488
//    state->frames_skip_left= state->set_frames_skip;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
489 490 491 492 493
	} else if (state->frames_skip < 0) {
		state->frame_period = -(state->frames_skip); //! actual frame period will fluctuate to the nearest frame acquired (free running)
		state->frames_skip_left = state->frame_params.timestamp_sec;
	}
	D3(fprintf(debug_file, "9: state->frame_period=0x%x\n", state->frame_period));
494

Mikhail Karpenko's avatar
Mikhail Karpenko committed
495 496 497
	state->time_unit = (ogg_int64_t)(((double)state->frame_period) * ((double)10) / ((double)state->timescale));
	state->width = state->frame_params.width;
	state->height = state->frame_params.height;
498 499 500

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

//!here we are ready to initialize Ogm (or other) file
Mikhail Karpenko's avatar
Mikhail Karpenko committed
514 515
	switch (state->format) {
	case CAMOGM_FORMAT_NONE: rslt = 0;  break;
516 517 518
	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
519 520 521 522 523 524
	default: rslt = 0; // do nothing
	}
	if (rslt) {
		D0(fprintf(debug_file, "camogm_start() error, rslt=0x%x\n", rslt));
		return rslt;
	}
525
	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
526 527 528 529 530
	if (rslt) return rslt;
	state->running = 1;
	state->starting = 0;
	D1(fprintf(debug_file, "Started OK\n"));
	return 0;
531 532 533
}


534
int sendImageFrame(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
535 536 537 538
{
	int rslt;
	unsigned char frame_packet_type = PACKET_IS_SYNCPOINT;
	int timestamp_start;
539 540
///debugging:
//   int * ifp =      (int *) &(state->frame_params) ;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
541 542
	int * ifp_this = (int*)&(state->this_frame_params);
	int fp;
543 544

//! This is probably needed only for Quicktime (not to exceed already allocated frame index)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
545 546 547 548
	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;
	}
549 550 551
//! 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
552 553 554 555
	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;
	}
556
//!   check if file size is exceeded (assuming fopen),-CAMOGM_FRAME_CHANGED will trigger a new segment
Mikhail Karpenko's avatar
Mikhail Karpenko committed
557 558 559 560
	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;
	}
561
//!same for open
Mikhail Karpenko's avatar
Mikhail Karpenko committed
562 563 564 565
	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;
	}
566
//! check the frame pointer is valid
Mikhail Karpenko's avatar
Mikhail Karpenko committed
567 568
	if ((fp = lseek(state->fd_circ, state->cirbuf_rp, SEEK_SET)) < 0) {
		D3(fprintf(debug_file, "sendImageFrame:5: invalid frame\n"));
569

Mikhail Karpenko's avatar
Mikhail Karpenko committed
570 571
		return -CAMOGM_FRAME_INVALID; //!it will probably be that allready
	}
572
//! is the frame ready?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
573 574 575 576
	if (lseek(state->fd_circ, LSEEK_CIRC_READY, SEEK_END) < 0) {
		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
	}
577 578

//! process skipping frames. TODO: add - skipping time between frames (or better -  actual time period - use the nearest frame) instead of the frame number
Mikhail Karpenko's avatar
Mikhail Karpenko committed
579 580
	if ( (state->frames_skip > 0) && (state->frames_skip_left > 0 )) { //!skipping frames, not seconds.
		state->cirbuf_rp = lseek(state->fd_circ, LSEEK_CIRC_NEXT, SEEK_END);
581
//!optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
Mikhail Karpenko's avatar
Mikhail Karpenko committed
582 583 584 585 586
		if (state->save_gp) lseek(state->fd_circ, LSEEK_CIRC_SETP, SEEK_END);
		state->frames_skip_left--;
		D3(fprintf(debug_file, "?7 ")); //frame not ready
		return -CAMOGM_FRAME_NOT_READY; //! the required frame is not ready
	}
587 588

//! Get metadata
Mikhail Karpenko's avatar
Mikhail Karpenko committed
589 590 591
	D3(fprintf(debug_file, "_1_"));
	state->metadata_start = state->cirbuf_rp - 32;
	if (state->metadata_start < 0) state->metadata_start += state->circ_buff_size;
592
	memcpy(&(state->this_frame_params), (unsigned long* )&ccam_dma_buf[state->port_num][state->metadata_start >> 2], 32);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
593 594 595 596 597 598 599 600 601 602
	state->jpeg_len = state->this_frame_params.frame_length; //! frame_params.frame_length are now the length of bitstream
	if (state->this_frame_params.signffff != 0xffff) {
		D0(fprintf(debug_file, "%s:%d: wrong signature - %d\r\n", __FILE__, __LINE__, (int)state->this_frame_params.signffff));
		D1(fprintf(debug_file, "state->cirbuf_rp=0x%x\r\n", (int)state->cirbuf_rp));
		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_"));
603
//!   find location of the timestamp and copy it to the frame_params structure
Mikhail Karpenko's avatar
Mikhail Karpenko committed
604 605 606
	timestamp_start = state->cirbuf_rp + ((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) timestamp_start -= state->circ_buff_size;
	D3(fprintf(debug_file, "_3_"));
607
	memcpy(&(state->this_frame_params.timestamp_sec), (unsigned long* )&ccam_dma_buf[state->port_num][timestamp_start >> 2], 8);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
608 609 610 611 612 613
//! 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.width  != state->this_frame_params.width) ||
				   (state->frame_params.height != state->this_frame_params.height))) {
		D3(fprintf(debug_file, "sendImageFrame:9: WOI changed\n"));
		return -CAMOGM_FRAME_CHANGED; //! not yet checking for the FPS
	}
614
//!   check if file duration (in seconds) exceeded ,-CAMOGM_FRAME_CHANGED will trigger a new segment
Mikhail Karpenko's avatar
Mikhail Karpenko committed
615 616 617 618 619
	if ((state->segment_duration > 0) &&
	    ((state->this_frame_params.timestamp_sec - state->frame_params.timestamp_sec) > state->segment_duration)) {
		D3(fprintf(debug_file, "sendImageFrame:10: segment duration in seconds exceeded\n"));
		return -CAMOGM_FRAME_CHANGED;
	}
620
//! check if (in timelapse mode)  it is too early for the frame to be stored
Mikhail Karpenko's avatar
Mikhail Karpenko committed
621 622
	if ((state->frames_skip < 0) && (state->frames_skip_left > state->this_frame_params.timestamp_sec) ) {
		state->cirbuf_rp = lseek(state->fd_circ, LSEEK_CIRC_NEXT, SEEK_END);
623
//!optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
Mikhail Karpenko's avatar
Mikhail Karpenko committed
624 625 626 627
		if (state->save_gp) lseek(state->fd_circ, LSEEK_CIRC_SETP, SEEK_END);
		D3(fprintf(debug_file, "sendImageFrame:11: timelapse: frame will be skipped\n"));
		return -CAMOGM_FRAME_NOT_READY; //! the required frame is not ready
	}
628 629


Mikhail Karpenko's avatar
Mikhail Karpenko committed
630 631 632
	D3(fprintf(debug_file, "_4_"));
	if (state->exif) {
		D3(fprintf(debug_file, "_5_"));
633 634
//! update the Exif header with the current frame metadata
//     updateExif(ep, state->ed, &(state->frame_params));
Mikhail Karpenko's avatar
Mikhail Karpenko committed
635
		state->exifSize = lseek(state->fd_exif, 1, SEEK_END); // at the beginning of page 1 - position == page length
636
//     if (state->exifSize < 0) state->exifSize=0; // error from lseek;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
637
		if (state->exifSize > 0) {
638
//state->this_frame_params.meta_index
Mikhail Karpenko's avatar
Mikhail Karpenko committed
639 640 641 642 643 644
			lseek(state->fd_exif, state->this_frame_params.meta_index, SEEK_END); //! select meta page to use (matching frame)
			rslt = read(state->fd_exif, state->ed, state->exifSize);
			if (rslt < 0) rslt = 0;
			state->exifSize = rslt;
		} else state->exifSize = 0;
	} else state->exifSize = 0;
645

Mikhail Karpenko's avatar
Mikhail Karpenko committed
646
	D3(fprintf(debug_file, "_6_"));
647 648

//! prepare a packet to be sent (a lst of memory chunks)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
	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;
		state->packetchunks[state->chunk_index++].chunk = state->jpegHeader;
		state->packetchunks[state->chunk_index  ].bytes = state->exifSize;
		state->packetchunks[state->chunk_index++].chunk = state->ed;
		state->packetchunks[state->chunk_index  ].bytes = state->head_size - 2;
		state->packetchunks[state->chunk_index++].chunk = &(state->jpegHeader[2]);
	} else {
		D3(fprintf(debug_file, "_8_"));
		state->packetchunks[state->chunk_index  ].bytes = state->head_size;
		state->packetchunks[state->chunk_index++].chunk = state->jpegHeader;
	}
	D3(fprintf(debug_file, "_9_"));
666 667

/*! JPEG image data may be split in two segments (rolled over buffer end) - process both variants */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
668
	if ((state->cirbuf_rp + state->jpeg_len) > state->circ_buff_size) { //! two segments
669
/*! copy from the beginning of the frame to the end of the buffer */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
670 671
		D3(fprintf(debug_file, "_10_"));
		state->packetchunks[state->chunk_index  ].bytes = state->circ_buff_size - state->cirbuf_rp;
672
		state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp >> 2];
673
/*! copy from the beginning of the buffer to the end of the frame */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
674
		state->packetchunks[state->chunk_index  ].bytes = state->jpeg_len - (state->circ_buff_size - state->cirbuf_rp);
675
		state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][0];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
676 677
	} else { // single segment
		D3(fprintf(debug_file, "_11_"));
678 679

/*! copy from the beginning of the frame to the end of the frame (no buffer rollovers) */
Mikhail Karpenko's avatar
Mikhail Karpenko committed
680
		state->packetchunks[state->chunk_index  ].bytes = state->jpeg_len;
681
		state->packetchunks[state->chunk_index++].chunk = (unsigned char*)&ccam_dma_buf[state->port_num][state->cirbuf_rp >> 2];
Mikhail Karpenko's avatar
Mikhail Karpenko committed
682 683 684 685 686 687 688
	}
	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;
689 690 691
	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
692 693 694
	default: rslt = 0; // do nothing
	}
	if (rslt) {
Mikhail Karpenko's avatar
Mikhail Karpenko committed
695
		D3(fprintf(debug_file, "sendImageFrame:12: camogm_frame_***() returned %d\n", rslt));
Mikhail Karpenko's avatar
Mikhail Karpenko committed
696 697
		return rslt;
	}
698
	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
699 700 701
	if (rslt) return rslt;

	D3(fprintf(debug_file, "_14_"));
702
//!advance frame pointer
Mikhail Karpenko's avatar
Mikhail Karpenko committed
703 704
	state->frameno++;
	state->cirbuf_rp = lseek(state->fd_circ, LSEEK_CIRC_NEXT, SEEK_END);
705
//!optionally save it to global read pointer (i.e. for debugging with imgsrv "/pointers")
Mikhail Karpenko's avatar
Mikhail Karpenko committed
706 707 708 709 710 711 712 713
	if (state->save_gp) lseek(state->fd_circ, LSEEK_CIRC_SETP, SEEK_END);
	D3(fprintf(debug_file, "_15_\n"));
	if (state->frames_skip > 0) {
		state->frames_skip_left = state->frames_skip;
	} else if (state->frames_skip < 0) {
		state->frames_skip_left += -(state->frames_skip);
	}
	return 0;
714 715 716
}


717
int camogm_stop(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
718 719 720 721 722 723 724 725 726 727 728 729 730
{
	int rslt = 0;

	if (!state->running) {
		if (!state->starting) {
			D2(fprintf(debug_file, "Recording was not running, nothing to stop\n"));
		} else {
			state->starting = 0;
			D1(fprintf(debug_file, "Dropping attempt to start\n"));
		}
		return 0;
	}
	D1(fprintf(debug_file, "Ending recording\n"));
731
	if (state->kml_used) camogm_end_kml(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
732 733
	switch (state->format) {
	case CAMOGM_FORMAT_NONE: rslt = 0; break;
734
	case CAMOGM_FORMAT_OGM:  rslt = camogm_end_ogm(state); break;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
735
	case CAMOGM_FORMAT_JPEG: rslt = camogm_end_jpeg(); break;
736
	case CAMOGM_FORMAT_MOV:  rslt = camogm_end_mov(state); break;
737
///    default: return 0; // do nothing
Mikhail Karpenko's avatar
Mikhail Karpenko committed
738
	}
739
//! now close video file (if it is open)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
740 741 742 743
	if (state->vf) fclose(state->vf);
	state->vf = NULL;
	if (rslt) return rslt;
	state->last = 1;
744
//!state->running=0 should be output after file is finished and closed
Mikhail Karpenko's avatar
Mikhail Karpenko committed
745 746 747
	state->running = 0;
	state->starting = 0;
	return 0;
748 749
}

750
void camogm_free(camogm_state *state)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
751 752 753
{
	int f;

754 755
//! free all file format handlers that were used
//add kml when needed
Mikhail Karpenko's avatar
Mikhail Karpenko committed
756 757 758 759 760 761 762 763 764 765 766
	for (f = 0; f < 31; f++) {
		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;
767 768
}

769
int camogm_reset(camogm_state *state)   //! reset circbuf read pointer
Mikhail Karpenko's avatar
Mikhail Karpenko committed
770
{
771

Mikhail Karpenko's avatar
Mikhail Karpenko committed
772 773 774
	state->cirbuf_rp = -1;
	state->buf_overruns = -1; //!first will not count
	return 0;
775 776 777
}

///kml stuff
778
void  camogm_kml_set_enable(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
779 780
{
	state->kml_enable = d;
781
}
782
void  camogm_kml_set_horHalfFov(camogm_state *state, double dd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
783 784
{
	state->kml_horHalfFov = dd;
785
}
786
void  camogm_kml_set_vertHalfFov(camogm_state *state, double dd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
787 788
{
	state->kml_vertHalfFov = dd;
789
}
790
void  camogm_kml_set_height_mode(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
791 792
{
	state->kml_height_mode = d;
793
}
794
void  camogm_kml_set_height(camogm_state *state, double dd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
795 796
{
	state->kml_height = dd;
797
}
798
void  camogm_kml_set_period(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
799 800 801 802
{
	state->kml_period = d;
	state->kml_last_ts = 0;
	state->kml_last_uts = 0;
803
}
804
void  camogm_kml_set_near(camogm_state *state, double dd)   // distance to PhotoOverlay
Mikhail Karpenko's avatar
Mikhail Karpenko committed
805 806
{
	state->kml_near = dd;
807 808 809
}


810
void  camogm_set_segment_duration(camogm_state *state, int sd)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
811 812
{
	state->segment_duration = sd;
813
}
814
void  camogm_set_segment_length(camogm_state *state, int sl)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
815 816
{
	state->segment_length =   sl;
817
}
818
void  camogm_set_save_gp(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
819 820
{
	state->save_gp =   d;
821
}
822
void  camogm_set_exif(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
823 824
{
	state->exif =   d;
825 826 827
}


828
void  camogm_set_greedy(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
829 830
{
	state->greedy =   d ? 1 : 0;
831
}
832
void  camogm_set_ignore_fps(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
833 834
{
	state->ignore_fps =   d ? 1 : 0;
835 836
}

837
void  camogm_set_prefix(camogm_state *state, const char * p)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
838 839 840
{
	strncpy(state->path_prefix, p, sizeof(state->path_prefix) - 1);
	state->path_prefix[sizeof(state->path_prefix) - 1] = '\0';
841 842
}

843
void  camogm_set_timescale(camogm_state *state, double d)   //! set timescale, default=1,000,000
Mikhail Karpenko's avatar
Mikhail Karpenko committed
844 845 846 847 848
{
	state->set_timescale =  d;
	if ((state->running == 0) && (state->starting == 0)) {
		state->timescale = state->set_timescale;
	}
849 850
}

851
void  camogm_set_frames_skip(camogm_state *state, int d)   //! set frames to skip (for time lapse)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
852 853 854 855
{
	state->set_frames_skip =  d;
	if ((state->running == 0) && (state->starting == 0)) {
		state->frames_skip =      state->set_frames_skip;
856
//     state->frames_skip_left= state->set_fram_skip;
Mikhail Karpenko's avatar
Mikhail Karpenko committed
857 858
		state->frames_skip_left = 0;
	}
859 860 861
}


862
void  camogm_set_format(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
{
	int rslt = 0;

	state->set_format =  d;
	if ((state->running == 0) && (state->starting == 0)) {
		state->format =  state->set_format;
		switch (state->format) {
		case CAMOGM_FORMAT_NONE: rslt = 0; break;
		case CAMOGM_FORMAT_OGM:  rslt = camogm_init_ogm(); break;
		case CAMOGM_FORMAT_JPEG: rslt = camogm_init_jpeg(); break;
		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);
	}
880 881
}
//! needed for Quicktime - maybe something else?
882
void  camogm_set_max_frames(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
883 884 885
{
	state->set_max_frames =  d;
	if ((state->running == 0) && (state->starting == 0)) state->max_frames =  d;
886
}
887
void  camogm_set_frames_per_chunk(camogm_state *state, int d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
888 889 890
{
	state->set_frames_per_chunk =  d;
	if ((state->running == 0) && (state->starting == 0)) state->frames_per_chunk =  d;
891
}
892
void  camogm_set_start_after_timestamp(camogm_state *state, double d)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
893 894
{
	state->start_after_timestamp =  d;
895 896 897
}


898
void  camogm_status(camogm_state *state, char * fn, int xml)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
899 900 901
{
	int _len = 0;
	int _dur, _udur;
902
//TODO:make it XML file
Mikhail Karpenko's avatar
Mikhail Karpenko committed
903 904 905 906 907 908 909 910 911 912 913 914 915 916
	FILE* f;
	char *_state, *_output_format, *_using_exif, *_using_global_pointer, *_compressor_state;
	int _b_free, _b_used, _b_size; // , save_p;
	int _frames_remain = 0;
	int _sec_remain = 0;
	int _frames_skip = 0;
	int _sec_skip = 0;
	char *_kml_enable, *_kml_used, *_kml_height_mode;

	_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


917 918 919
	_b_free = getGPValue(state->port_num, G_FREECIRCBUF);
	_b_used = getGPValue(state->port_num, G_CIRCBUFSIZE) - getGPValue(state->port_num, G_FREECIRCBUF);
	_b_size = getGPValue(state->port_num, G_FRAME_SIZE);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947

	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
	_dur = state->this_frame_params.timestamp_sec - state->frame_params.timestamp_sec;
	_udur = state->this_frame_params.timestamp_usec - state->frame_params.timestamp_usec;
	if (_udur < 0) {
		_dur -= 1;
		_udur += 1000000;
	} else if (_udur >= 1000000) {
		_dur += 1;
		_udur -= 1000000;
	}
	_state =         state->running ? "running" : (state->starting ? "starting" : "stopped");
	_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";
948
	_compressor_state = (getGPValue(state->port_num, P_COMPRESSOR_RUN) == 2) ? "running" : "stoppped";
Mikhail Karpenko's avatar
Mikhail Karpenko committed
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 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 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
	if ( state->frames_skip > 0 ) {
		_frames_remain = state->frames_skip_left;
		_frames_skip = state->frames_skip;
	} else if ( state->frames_skip < 0 ) {
		_sec_remain = (state->frames_skip_left - state->this_frame_params.timestamp_sec);
		_sec_skip = -(state->frames_skip);
	}


	if (xml) {
		fprintf(f, "<?xml version=\"1.0\"?>\n" \
			"<camogm_state>\n" \
			"  <state>\"%s\"</state>\n" \
			"  <compressor_state>\"%s\"</compressor_state>\n" \
			"  <file_name>\"%s\"</file_name>\n" \
			"  <frame_number>%d</frame_number>\n" \
			"  <frame_size>%d</frame_size>\n" \
			"  <start_after_timestamp>%f</start_after_timestamp>\n"	\
			"  <file_duration>%d.%06d</file_duration>\n" \
			"  <file_length>%d</file_length>\n" \
			"  <frame_period>%d</frame_period>\n" \
			"  <frames_skip>%d</frames_skip>\n" \
			"  <seconds_skip>%d</seconds_skip>\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" \
			"  <buffer_overruns>%d</buffer_overruns>\n" \
			"  <buffer_minimal>%d</buffer_minimal>\n" \
			"  <buffer_free>%d</buffer_free>\n" \
			"  <buffer_used>%d</buffer_used>\n" \
			"  <circbuf_rp>%d</circbuf_rp>\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" \
			"  <ignore_fps>\"%s\"</ignore_fps>\n" \
			"</camogm_state>\n",
			_state, _compressor_state, state->path, state->frameno, _b_size, state->start_after_timestamp, _dur, _udur, _len, state->frame_period, \
			_frames_skip, _sec_skip, _frames_remain, _sec_remain, \
			state->width, state->height, _output_format, _using_exif, \
			state->path_prefix, state->segment_duration, state->segment_length, state->max_frames, state->timescale, \
			state->frames_per_chunk,  state->last_error_code, state->buf_overruns, state->buf_min, _b_free, _b_used, state->cirbuf_rp, \
			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, \
			state->greedy ? "yes" : "no", state->ignore_fps ? "yes" : "no");
	} else {
		fprintf(f, "state              %s\n",        _state);
		fprintf(f, "compressor state   %s\n",        _compressor_state);
		fprintf(f, "file               %s\n",        state->path);
		fprintf(f, "frame              %d\n",        state->frameno);
		fprintf(f, "frame size         %d\n",        _b_size);
		fprintf(f, "start_after_timestamp %f\n",     state->start_after_timestamp);
		fprintf(f, "file duration      %d.%06d sec\n", _dur, _udur);
		fprintf(f, "file length        %d B\n",      _len);
		fprintf(f, "frame period       %d (0x%x)\n", state->frame_period, state->frame_period);
		if ( _frames_skip > 0 ) fprintf(f, "frames to skip   %d (left %d)\n", _frames_skip, _frames_remain);
		if ( _sec_skip    < 0 ) fprintf(f, "timelapse period  %d sec (remaining %d sec)\n", _sec_skip, _sec_remain);
		fprintf(f, "width              %d (0x%x)\n", state->width, state->width);
		fprintf(f, "height             %d (0x%x)\n", state->height, state->height);
		fprintf(f, "\n");
		fprintf(f, "output format      %s\n",        _output_format);
		fprintf(f, "using exif         %s\n",        _using_exif);
		fprintf(f, "path prefix:       %s\n",        state->path_prefix);
		fprintf(f, "max file duration: %d sec\n",    state->segment_duration);
		fprintf(f, "max file length:   %d B\n",      state->segment_length);
		fprintf(f, "max frames         %d\n",        state->max_frames);
		fprintf(f, "timescale          %f\n",        state->timescale);
		fprintf(f, "frames per chunk   %d\n",        state->frames_per_chunk);
		fprintf(f, "greedy             %s\n",        state->greedy ? "yes" : "no");
		fprintf(f, "ignore fps         %s\n",        state->ignore_fps ? "yes" : "no");
		fprintf(f, "\n");
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1041
		fprintf(f, "last error code    %d\n",        state->last_error_code);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
		fprintf(f, "buffer overruns    %d\n",        state->buf_overruns);
		fprintf(f, "buffer minimal     %d\n",        state->buf_min);
		fprintf(f, "buffer free        %d\n",        _b_free);
		fprintf(f, "buffer used        %d\n",        _b_used);
		fprintf(f, "circbuf_rp         %d (0x%x)\n", state->cirbuf_rp, state->cirbuf_rp);
		fprintf(f, "\n");
		fprintf(f, "debug output to    %s\n",        state->debug_name);
		fprintf(f, "debug level        %d\n",        debug_level);
		fprintf(f, "use global pointer %s\n",        _using_global_pointer);
		fprintf(f, "\n\n");
		fprintf(f, "kml_enable         %s\n",        _kml_enable);
		fprintf(f, "kml_used           %s\n",        _kml_used);
		fprintf(f, "kml_path           %s\n",        state->kml_path);
		fprintf(f, "kml_horHalfFov     %f degrees\n", state->kml_horHalfFov);
		fprintf(f, "kml_vertHalfFov    %f degrees\n", state->kml_vertHalfFov);
		fprintf(f, "kml_near           %f m\n",      state->kml_near);
		fprintf(f, "kml height mode    %s\n",        _kml_height_mode);
		fprintf(f, "kml_height (extra) %f m\n",      state->kml_height);
		fprintf(f, "kml_period         %d\n",        state->kml_period);
		fprintf(f, "kml_last_ts        %d.%06d\n",   state->kml_last_ts, state->kml_last_uts);
		fprintf(f, "\n\n");

	}
	if ((f != stdout) && (f != stderr)) fclose(f);
	if (state->buf_overruns >= 0) state->buf_overruns = 0;  //! resets overruns after reading status , so "overruns" means since last reading status
	state->last_error_code = 0;                             //! Reset error
	state->buf_min = _b_free;
1069 1070 1071
}

//! will read from pipe, return pointer to null terminated string if available, NULL otherwise
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1072 1073 1074 1075 1076
char * getLineFromPipe(FILE* npipe)
{
	int fl;
	char * nlp;

1077
//!remove used string if any
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1078
	if (cmdstrt > 0) {
1079
//!moving overlapping strings
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1080 1081 1082 1083
		memmove(cmdbuf, &cmdbuf[cmdstrt], sizeof(cmdbuf) - cmdstrt);
		cmdbufp -= cmdstrt;
		cmdstrt = 0;
	}
1084
//! is there any complete string in a buffer?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1085
	if (!cmdbufp) cmdbuf[cmdbufp] = 0;  //!null-terminate first access (probably not needed for the static buffer
1086
//      nlp= strchr(cmdbuf,'\n');
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1087 1088 1089 1090
	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;
1091 1092
//! is there any complete string in a buffer after reading?
//        nlp= strchr(&cmdbuf[cmdbufp],'\n'); //! there were no new lines before cmdbufp
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1093 1094 1095 1096
		nlp = strpbrk(&cmdbuf[cmdbufp], ";\n"); //! there were no new lines before cmdbufp
		cmdbufp += fl;                          //!advance pointer after pipe read
	}
	if (nlp) {
1097
//printf ("++nlp=%d\n", (int) (nlp-cmdbuf));
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1098 1099
		nlp[0] = 0;
		cmdstrt = nlp - cmdbuf + 1;
1100 1101
//printf ("++cmdstrt=%d\n", cmdstrt);
//printf ("cmdbuf[0]=%d, cmdbuf[1]=%d, cmdbuf[2]=%d, cmdbuf[3]=%d, \n",cmdbuf[0],cmdbuf[1],cmdbuf[2],cmdbuf[3]);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1102
		for (fl = 0; cmdbuf[fl] && strchr(" \t", cmdbuf[fl]); fl++) ;
1103
//printf ("++fl=%d\n", fl);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1104 1105
		return &cmdbuf[fl];
	} else {
1106
//printf ("notready: cmdbufp=%d, cmdstrt=%d\n",cmdbufp, cmdstrt);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1107 1108
		return NULL;
	}
1109 1110
}
// command[= \t]*args[ \t]*
1111
int parse_cmd(camogm_state *state, FILE* npipe)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1112 1113 1114 1115 1116 1117 1118
{
	char * cmd;
	char * args;
	char * argse;
	int d;
	double dd;

1119 1120
//  if (!((cmd=getLineFromPipe(npipe)))) return 0; //! nothing in the pipe
//!skip empty commands
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1121 1122 1123
	while (((cmd = getLineFromPipe(npipe))) && !cmd[0]) ;
	if (!cmd) return 0;  //! nothing in the pipe
	D2(fprintf(debug_file, "Got command: '%s'\n", cmd));
1124 1125

/// Acknowledge received command by copying frame number to per-daemon parameter
1126 1127 1128 1129
#ifdef DISABLE_CODE
//	GLOBALPARS(state->port_num, G_DAEMON_ERR + lastDaemonBit[state->port_num]) = GLOBALPARS(state->port_num, G_THIS_FRAME);
	setGValue(state->port, G_DAEMON_ERR + lastDaemonBit[state->port_nun], getGValue(state->port_num, G_THIS_FRAME));
#endif /* DISABLE_CODE */
1130
//  printf ("cmd[0]=%d:%s\n",(int) cmd[0],cmd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1131
	args = strpbrk(cmd, "= \t");
1132
//! is it just a single word command or does it have parameters?
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1133 1134 1135 1136 1137
	if (args) {
		args[0] = 0;
		args++;
		while (strchr("= \t", args[0])) args++;
		if (args[0]) {
1138
//! ltrim (args)
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1139 1140 1141 1142
			for (argse = strchr(args, '\0') - 1; strchr("= \t", argse[0]); argse--) argse[0] = '\0';
		}
		if (!args[0]) args = NULL;
	}
1143
//! now cmd is trimmed, arg is NULL or a pointer to trimmed command arguments
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1144
	if (strcmp(cmd, "start") == 0) {
1145
		camogm_start(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1146 1147
		return 1;
	} else if (strcmp(cmd, "reset") == 0) { //! will reset pointer to the last acquired frame (if any)
1148
		camogm_reset(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1149 1150
		return 2;
	} else if (strcmp(cmd, "stop") == 0) {
1151
		camogm_stop(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1152 1153
		return 3;
	} else if (strcmp(cmd, "exit") == 0) {
1154 1155
		camogm_stop(state);
		camogm_free(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1156 1157 1158
		exit(0);
	} else if (strcmp(cmd, "duration") == 0) {
		if (!(args) || (((d = strtol(args, NULL, 10))) <= 0)) d = DEFAULT_DURATION;
1159
		camogm_set_segment_duration(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1160 1161 1162
		return 4;
	} else if (strcmp(cmd, "length") == 0) {
		if (!(args) || (((d = strtol(args, NULL, 10))) <= 0)) d = DEFAULT_LENGTH;
1163
		camogm_set_segment_length(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1164 1165
		return 5;
	} else if (strcmp(cmd, "prefix") == 0) {
1166
		if (args) camogm_set_prefix(state, args);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1167 1168
		return 6;
	} else if (strcmp(cmd, "status") == 0) {
1169
		camogm_status(state, args, 0);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1170 1171
		return 7;
	} else if (strcmp(cmd, "xstatus") == 0) {
1172
		camogm_status(state, args, 1);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1173 1174
		return 7;
	} else if (strcmp(cmd, "save_gp") == 0) {
1175
		if ((args) && (((d = strtol(args, NULL, 10))) >= 0)) camogm_set_save_gp(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1176 1177
		return 8;
	} else if (strcmp(cmd, "exif") == 0) {
1178
		if ((args) && (((d = strtol(args, NULL, 10))) >= 0)) camogm_set_exif(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1179 1180
		return 8;
	} else if (strcmp(cmd, "debug") == 0) {
1181
		camogm_debug(state, args);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1182 1183 1184
		return 9;
	} else if (strcmp(cmd, "timescale") == 0) {
		dd = strtod(args, NULL);
1185
		camogm_set_timescale(state, dd ? dd : 1.0);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1186
		return 10;
1187 1188 1189 1190
//!TODO: fix period calculation/check for frame skipping (just disable in frame skip mode?)
//!TODO: add time period (system clock), not just frame skipping


Mikhail Karpenko's avatar
Mikhail Karpenko committed
1191 1192
	} else if (strcmp(cmd, "frameskip") == 0) {
		d = strtol(args, NULL, 10);
1193
		camogm_set_frames_skip(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1194 1195 1196
		return 11;
	} else if (strcmp(cmd, "timelapse") == 0) { //! period (in seconds) between stored frames
		d = strtol(args, NULL, 10);
1197
		camogm_set_frames_skip(state, -d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1198 1199 1200
		return 11;
	} else if (strcmp(cmd, "format") == 0) {
		if (args) {
1201 1202 1203 1204
			if (strcmp(args,  "none") == 0) camogm_set_format(state, 0);
			else if ((strcmp(args, "ogm" ) == 0) || (strcmp(args, "ogg") == 0)) camogm_set_format(state, CAMOGM_FORMAT_OGM);
			else if ((strcmp(args, "jpeg") == 0) || (strcmp(args, "jpg") == 0)) camogm_set_format(state, CAMOGM_FORMAT_JPEG);
			else if (strcmp(args,  "mov" ) == 0) camogm_set_format(state, CAMOGM_FORMAT_MOV);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1205 1206 1207 1208 1209 1210 1211
		}
		return 12;
	} else if (strcmp(cmd, "debuglev") == 0) {
		d = strtol(args, NULL, 10);
		camogm_debug_level(d ? d : 0);
		return 13;
	} else if (strcmp(cmd, "kml") == 0) {
1212
		if ((args) && (((d = strtol(args, NULL, 10))) >= 0)) camogm_kml_set_enable(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1213 1214 1215
		return 14;
	} else if (strcmp(cmd, "kml_hhf") == 0) {
		dd = strtod(args, NULL);
1216
		camogm_kml_set_horHalfFov(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1217 1218 1219
		return 15;
	} else if (strcmp(cmd, "kml_vhf") == 0) {
		dd = strtod(args, NULL);
1220
		camogm_kml_set_vertHalfFov(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1221 1222 1223
		return 16;
	} else if (strcmp(cmd, "kml_near") == 0) {
		dd = strtod(args, NULL);
1224
		camogm_kml_set_near(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1225 1226 1227
		return 17;
	} else if (strcmp(cmd, "kml_alt") == 0) {
		if (args) {
1228 1229
			if (strcmp(args, "gps"   ) == 0) camogm_kml_set_height_mode(state, 1);
			else if (strcmp(args, "ground") == 0) camogm_kml_set_height_mode(state, 0);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1230 1231 1232 1233
		}
		return 18;
	} else if (strcmp(cmd, "kml_height") == 0) {
		dd = strtod(args, NULL);
1234
		camogm_kml_set_height(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1235 1236 1237
		return 19;
	} else if (strcmp(cmd, "kml_period") == 0) {
		d = strtol(args, NULL, 10);
1238
		camogm_kml_set_period(state, d ? d : 1);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1239 1240 1241
		return 20;
	} else if (strcmp(cmd, "frames_per_chunk") == 0) {
		d = strtol(args, NULL, 10);
1242
		camogm_set_frames_per_chunk(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1243 1244 1245
		return 21;
	} else if (strcmp(cmd, "max_frames") == 0) {
		d = strtol(args, NULL, 10);
1246
		camogm_set_max_frames(state, d);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1247 1248 1249
		return 22;
	} else if (strcmp(cmd, "start_after_timestamp") == 0) {
		dd = strtod(args, NULL);
1250
		camogm_set_start_after_timestamp(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1251 1252 1253
		return 23;
	} else if (strcmp(cmd, "greedy") == 0) {
		dd = strtod(args, NULL);
1254
		camogm_set_greedy(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1255 1256 1257
		return 24;
	} else if (strcmp(cmd, "ignore_fps") == 0) {
		dd = strtod(args, NULL);
1258
		camogm_set_ignore_fps(state, dd);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1259 1260 1261
		return 25;
	}
	return -1;
1262 1263
}

1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
/**
 * @brief Create a list of pipe names from a single name given
 *
 * This function accepts a pipe name string provided by the user to the program and
 * creates a list of #SENSOR_PORTS similar names but with port number added to the end of
 * the name.
 * @param[in]   pipe_name   pipe name provided by user
 * @param[out]  names       a list of names with port number added
 * return       0 if the list was successfully created or -1 otherwise
 */
int create_pipe_names(const char *pipe_name, char **names)
{
	int ret = 0;
	unsigned int len = strlen(pipe_name);

	for (int i = 0; i < SENSOR_PORTS; i++) {
		char *name = malloc(PATH_MAX);
		if (name == NULL) {
			ret = -1;
			break;
		}
		strncpy(name, pipe_name, PATH_MAX - 1);
		snprintf(&name[len], PATH_MAX - len - 1, "%u", i);
		names[i] = name;
	}

1290 1291 1292 1293 1294 1295 1296
	if (ret < 0) {
		for (int i = 0; i < SENSOR_PORTS; i++) {
			free(names[i]);
			names[i] = NULL;
		}
	}

1297 1298 1299 1300 1301 1302 1303 1304
	return ret;
}

/**
 * @brief This function closes open files and deletes allocated memory.
 * @param[in]   state   pointer to #camogm_state structure for a particular sensor channel
 * return       none
 */
1305
void clean_up(camogm_state *state)
1306 1307 1308 1309 1310 1311 1312 1313 1314
{
	if (state->fd_exif > 0)
		close(state->fd_exif);
	if (state->fd_head > 0)
		close(state->fd_head);
	if (state->fd_circ > 0)
		close(state->fd_circ);
	if (state->fd_fparmsall)
		close(state->fd_fparmsall);
1315 1316
	free(state->pipe_name);
	state->pipe_name = NULL;
1317 1318
}

1319 1320 1321 1322 1323 1324 1325 1326 1327 1328
/**
 * @brief Main processing loop
 *
 * If recording is on, this function will check for new commands in command pipe after each frame. If recording is
 * turn off, it will poll command pipe each #COMMAND_LOOP_DELAYS microseconds and then sleep.
 * @param[in]   state   #camogm_state structure associated with a single port
 * @return      normally this function loops indefinitely processing commands but will return negative exit code in case
 * of error and \e EXIT_SUCCESS it eventually terminates in normal way.
 */
int listener_loop(camogm_state *state)
1329
{
1330 1331 1332 1333 1334 1335 1336 1337
	FILE *cmd_file;
	int rslt, ret, cmd;
	int fp0, fp1;
	int process = 1;
	unsigned int port = state->port_num;
	const char *pipe_name = state->pipe_name;

	// open Exif header file
1338
#ifdef DISABLE_CODE
1339
	state->fd_exif = open(exifFileNames[port], O_RDONLY);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1340
	if (state->fd_exif < 0) { // check control OK
1341
		D0(fprintf(debug_file, "Error opening %s\n", exifFileNames[port]));
1342
		clean_up(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1343 1344
		return -1;
	}
1345
#endif /* DESABLE_CODE */
1346

1347
	// open JPEG header file
1348
	state->fd_head = open(headFileNames[port], O_RDWR);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1349
	if (state->fd_head < 0) { // check control OK
1350
		D0(fprintf(debug_file, "Error opening %s\n", headFileNames[port]));
1351
		clean_up(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1352 1353 1354 1355 1356
		return -1;
	}
	state->head_size = lseek(state->fd_head, 0, SEEK_END);
	if (state->head_size > JPEG_HEADER_MAXSIZE) {
		D0(fprintf(debug_file, "%s:%d: Too big JPEG header (%d > %d)", __FILE__, __LINE__, state->head_size, JPEG_HEADER_MAXSIZE ));
1357
		clean_up(state);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1358 1359
		return -2;
	}
1360

1361
	// open circbuf and mmap it (once at startup)
1362
	state->fd_circ = open(circbufFileNames[port], O_RDWR);
Mikhail Karpenko's avatar
Mikhail Karpenko committed
1363