Loading camogm.c +167 −154 Original line number Original line Diff line number Diff line Loading @@ -145,6 +145,7 @@ /* end of debug code */ /* end of debug code */ #define COMMAND_LOOP_DELAY 500000 //0.5sec #define DEFAULT_DEBUG_LVL 6 #define DEFAULT_DEBUG_LVL 6 #define TRAILER_SIZE 0x02 #define TRAILER_SIZE 0x02 #define MAP_OPTIONS MAP_FILE | MAP_PRIVATE #define MAP_OPTIONS MAP_FILE | MAP_PRIVATE Loading Loading @@ -194,7 +195,7 @@ camogm_state sstate[SENSOR_PORTS]; int debug_level; int debug_level; FILE* debug_file; FILE* debug_file; int camogm_init(camogm_state *state, unsigned int port); void camogm_init(camogm_state *state, unsigned int port, char *pipe_name); int camogm_start(camogm_state *state); int camogm_start(camogm_state *state); int camogm_stop(camogm_state *state); int camogm_stop(camogm_state *state); int camogm_reset(camogm_state *state); //! reset circbuf read pointer int camogm_reset(camogm_state *state); //! reset circbuf read pointer Loading Loading @@ -269,7 +270,14 @@ void put_uint64(void *buf, u_int64_t val) tmp[7] = (val >>= 8) & 0xff; tmp[7] = (val >>= 8) & 0xff; } } int camogm_init(camogm_state *state, unsigned int port) /** * @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) { { const char sserial[] = "elp0"; const char sserial[] = "elp0"; int * ipser = (int*)sserial; int * ipser = (int*)sserial; Loading @@ -283,17 +291,17 @@ int camogm_init(camogm_state *state, unsigned int port) camogm_set_ignore_fps(state, DEFAULT_IGNORE_FPS); camogm_set_ignore_fps(state, DEFAULT_IGNORE_FPS); camogm_set_max_frames(state, DEFAULT_FRAMES); camogm_set_max_frames(state, DEFAULT_FRAMES); camogm_set_frames_per_chunk(state, DEFAULT_FRAMES_PER_CHUNK); camogm_set_frames_per_chunk(state, DEFAULT_FRAMES_PER_CHUNK); camogm_set_start_after_timestamp(state, 0.0); /// start any time camogm_set_start_after_timestamp(state, 0.0); // start any time camogm_set_prefix(state, "\0"); camogm_set_prefix(state, "\0"); camogm_set_save_gp(state, 0); camogm_set_save_gp(state, 0); camogm_reset(state); //! sets state->buf_overruns=-1; //!first does not count camogm_reset(state); // sets state->buf_overruns =- 1 state->serialno = ipser[0]; state->serialno = ipser[0]; state->last = 0; state->last = 0; debug_file = stderr; debug_file = stderr; camogm_debug_level(DEFAULT_DEBUG_LVL); camogm_debug_level(DEFAULT_DEBUG_LVL); strcpy(state->debug_name, "stderr"); strcpy(state->debug_name, "stderr"); camogm_set_timescale(state, 1.0); camogm_set_timescale(state, 1.0); camogm_set_frames_skip(state, 0); //! don't skip camogm_set_frames_skip(state, 0); // don't skip camogm_set_format(state, CAMOGM_FORMAT_OGM); camogm_set_format(state, CAMOGM_FORMAT_OGM); state->exifSize = 0; state->exifSize = 0; state->exif = DEFAULT_EXIF; state->exif = DEFAULT_EXIF; Loading @@ -302,7 +310,7 @@ int camogm_init(camogm_state *state, unsigned int port) state->formats = 0; state->formats = 0; state->last_error_code = 0; state->last_error_code = 0; ///kml stuff // kml stuff camogm_kml_set_enable(state, 0); camogm_kml_set_enable(state, 0); state->kml_file = NULL; state->kml_file = NULL; camogm_kml_set_horHalfFov(state, 20.0); camogm_kml_set_horHalfFov(state, 20.0); Loading @@ -314,8 +322,7 @@ int camogm_init(camogm_state *state, unsigned int port) state->kml_path[0] = '\0'; state->kml_path[0] = '\0'; state->port_num = port; state->port_num = port; state->pipe_name = pipe_name; return 0; } } Loading Loading @@ -1280,16 +1287,22 @@ int create_pipe_names(const char *pipe_name, char **names) names[i] = name; names[i] = name; } } if (ret < 0) { for (int i = 0; i < SENSOR_PORTS; i++) { free(names[i]); names[i] = NULL; } } return ret; return ret; } } /** /** * @brief This function closes open files and deletes allocated memory. * @brief This function closes open files and deletes allocated memory. * @param[in] state pointer to #camogm_state structure for a particular sensor channel * @param[in] state pointer to #camogm_state structure for a particular sensor channel * @param[in] names list of strings containing control pipe names * return none * return none */ */ void clean_up(camogm_state *state, char **names) void clean_up(camogm_state *state) { { if (state->fd_exif > 0) if (state->fd_exif > 0) close(state->fd_exif); close(state->fd_exif); Loading @@ -1299,177 +1312,130 @@ void clean_up(camogm_state *state, char **names) close(state->fd_circ); close(state->fd_circ); if (state->fd_fparmsall) if (state->fd_fparmsall) close(state->fd_fparmsall); close(state->fd_fparmsall); for (int i = 0; i < SENSOR_PORTS; i++) { free(state->pipe_name); free(names[i]); state->pipe_name = NULL; names[i] = NULL; } } } int main(int argc, char *argv[]) /** * @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) { { // int fd_circ; FILE *cmd_file; FILE *cmd_file; const char usage[] = "This program allows recording of the video/images acquired by Elphel camera to the storage media.\n" \ int rslt, ret, cmd; "It is designed to run in the background and accept commands through a named pipe.\n\n" \ int fp0, fp1; "Usage:\n\n" \ int process = 1; "%s <named_pipe_name>\n\n" \ unsigned int port = state->port_num; "i.e.:\n\n" \ const char *pipe_name = state->pipe_name; "%s /var/state/camogm_cmd\n\n" \ "When the program is runninig you may send commands by writing strings to the command file\n" \ "(/var/state/camogm_cmd in the example above). The complete list of available commands is available\n" \ "on Elphel Wiki (http://wiki.elphel.com/index.php?title=Camogm), here is the example of usage\n" \ "from the shell prompt in the camera:\n\n" \ "echo \"status; exif=1; format=jpeg;status=/var/tmp/camogm.status\" > /var/state/camogm_cmd\n\n" \ "That will print status information on the standard output (may not be visible if the program was not\n" \ "started from the same session), set exif mode on (each frame will have the full Exif header including\n" \ "a precise time stamp), set output format to a series of individual JPEG files, and then send status\n" \ "information to a file /var/tmp/camogm.status in the camera file system.\n\n" \ "This program does not control the process of acquisition of the video/images to the camera internal\n" \ "buffer, it only retrieves that data from the buffer (waiting when needed), packages it to selected\n" \ "format and stores the result files.\n\n"; int go = 1; int cmd; int i, rslt; int fp0, fp1; // debugging /* debug code follows */ unsigned int port = 2; /*end of debug code */ camogm_state *state = &sstate[port]; char *pipe_names[SENSOR_PORTS] = {0}; // state = &sstate; //extern //! no command line options processing yet if ((argc < 2) || (argv[1][1] == '-')) { printf(usage, argv[0], argv[0]); clean_up(state, pipe_names); return 0; } camogm_init(state, port); create_pipe_names(argv[1], pipe_names); //! open Exif header file // open Exif header file #ifdef DISABLE_CODE #ifdef DISABLE_CODE state->fd_exif = open(exifFileNames[port], O_RDONLY); state->fd_exif = open(exifFileNames[port], O_RDONLY); if (state->fd_exif < 0) { // check control OK if (state->fd_exif < 0) { // check control OK D0(fprintf(debug_file, "Error opening %s\n", exifFileNames[port])); D0(fprintf(debug_file, "Error opening %s\n", exifFileNames[port])); clean_up(state); return -1; return -1; } } #endif /* DESABLE_CODE */ #endif /* DESABLE_CODE */ //! open JPEG header file // open JPEG header file state->fd_head = open(headFileNames[port], O_RDWR); state->fd_head = open(headFileNames[port], O_RDWR); if (state->fd_head < 0) { // check control OK if (state->fd_head < 0) { // check control OK D0(fprintf(debug_file, "Error opening %s\n", headFileNames[port])); D0(fprintf(debug_file, "Error opening %s\n", headFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -1; return -1; } } state->head_size = lseek(state->fd_head, 0, SEEK_END); state->head_size = lseek(state->fd_head, 0, SEEK_END); if (state->head_size > JPEG_HEADER_MAXSIZE) { 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 )); D0(fprintf(debug_file, "%s:%d: Too big JPEG header (%d > %d)", __FILE__, __LINE__, state->head_size, JPEG_HEADER_MAXSIZE )); clean_up(state, pipe_names); clean_up(state); return -2; return -2; } } //! open circbuf and mmap it (once at startup) // open circbuf and mmap it (once at startup) state->fd_circ = open(circbufFileNames[port], O_RDWR); state->fd_circ = open(circbufFileNames[port], O_RDWR); if (state->fd_circ < 0) { // check control OK if (state->fd_circ < 0) { // check control OK D0(fprintf(debug_file, "Error opening %s\n", circbufFileNames[port])); D0(fprintf(debug_file, "Error opening %s\n", circbufFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -2; return -2; } } /*! find total buffer length (it is in defines, actually in c313a.h */ // find total buffer length (it is in defines, actually in c313a.h state->circ_buff_size = lseek(state->fd_circ, 0, SEEK_END); state->circ_buff_size = lseek(state->fd_circ, 0, SEEK_END); ccam_dma_buf[port] = (unsigned long*)mmap(0, state->circ_buff_size, PROT_READ, MAP_SHARED, state->fd_circ, 0); ccam_dma_buf[port] = (unsigned long*)mmap(0, state->circ_buff_size, PROT_READ, MAP_SHARED, state->fd_circ, 0); if ((int)ccam_dma_buf[port] == -1) { if ((int)ccam_dma_buf[port] == -1) { D0(fprintf(debug_file, "Error in mmap of %s\n", circbufFileNames[port])); D0(fprintf(debug_file, "Error in mmap of %s\n", circbufFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -3; return -3; } } //! Now open/mmap file to read sensor/compressor parameters (currently - just free memory in circbuf and compressor state) // now open/mmap file to read sensor/compressor parameters (currently - just free memory in circbuf and compressor state) //! open circbuf and mmap it (once at startup) #ifdef DISABLE_CODE #ifdef DISABLE_CODE state->fd_fparmsall = open(ctlFileNames[port], O_RDWR); state->fd_fparmsall = open(ctlFileNames[port], O_RDWR); if (state->fd_fparmsall < 0) { // check control OK if (state->fd_fparmsall < 0) { // check control OK D0(fprintf(debug_file, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); D0(fprintf(debug_file, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -2; return -2; } } //! now try to mmap // now try to mmap /// frameParsAll = (struct framepars_all_t *) mmap(0, sizeof (struct framepars_all_t) , PROT_READ, MAP_SHARED, state->fd_fparmsall, 0); // PROT_WRITE - only to write acknowledge /// PROT_WRITE - only to write acknowledge frameParsAll[port] = (struct framepars_all_t*)mmap(0, sizeof(struct framepars_all_t), PROT_READ | PROT_WRITE, MAP_SHARED, state->fd_fparmsall, 0); frameParsAll[port] = (struct framepars_all_t*)mmap(0, sizeof(struct framepars_all_t), PROT_READ | PROT_WRITE, MAP_SHARED, state->fd_fparmsall, 0); if ((int)frameParsAll[port] == -1) { if ((int)frameParsAll[port] == -1) { D0(fprintf(debug_file, "%s:%d:%s: Error in mmap in %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); D0(fprintf(debug_file, "%s:%d:%s: Error in mmap in %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -3; return -3; } } framePars[port] = frameParsAll[port]->framePars; framePars[port] = frameParsAll[port]->framePars; globalPars[port] = frameParsAll[port]->globalPars; globalPars[port] = frameParsAll[port]->globalPars; #endif /* DESABLE_CODE */ #endif /* DESABLE_CODE */ //!create a named pipe // create a named pipe //!always delete the pipe if it existed, start a fresh one // always delete the pipe if it existed, start a fresh one i = unlink(pipe_names[port]); ret = unlink(pipe_name); if (i) { if (ret) { D1(fprintf(debug_file, "Unlink %s returned %d, errno=%d \n", pipe_names[port], i, errno)); D1(fprintf(debug_file, "Unlink %s returned %d, errno=%d \n", pipe_name, ret, errno)); } } i = mkfifo(pipe_names[port], 0777); //EEXIST ret = mkfifo(pipe_name, 0777); //EEXIST //! now should not exist // now should not exist if (i) { if (ret) { if (errno == EEXIST) { if (errno == EEXIST) { D1(fprintf(debug_file, "Named pipe %s already exists, will use it.\n", pipe_names[port])); D1(fprintf(debug_file, "Named pipe %s already exists, will use it.\n", pipe_name)); } else { } else { D0(fprintf(debug_file, "Can not create a named pipe %s, errno=%d \n", pipe_names[port], errno)); D0(fprintf(debug_file, "Can not create a named pipe %s, errno=%d \n", pipe_name, errno)); clean_up(state, pipe_names); clean_up(state); return -4; return -4; } } } } //!now open the pipe - will block until something will be written (or just open for writing // now open the pipe - will block until something will be written (or just open for writing, //!Reads themselves will not block // reads themselves will not block) if (!((cmd_file = fopen(pipe_names[port], "r")))) { if (!((cmd_file = fopen(pipe_name, "r")))) { D0(fprintf(debug_file, "Can not open command file %s\n", pipe_names[port])); D0(fprintf(debug_file, "Can not open command file %s\n", pipe_name)); clean_up(state, pipe_names); clean_up(state); return -5; return -5; } } // D1(fprintf (debug_file,"Pipe %s open for reading\n",argv[1])); D0(fprintf(debug_file, "Pipe %s open for reading\n", pipe_name)); // to make sure something is sent out D0(fprintf(debug_file, "Pipe %s open for reading\n", pipe_names[port])); // to make sure something is sent out //! Here is a main loop. If recording is on, it will check for commands after each frame, if it is off - poll with fixed usleep #define COMMAND_LOOP_DELAY 500000 //0.5sec while (go) { // D3(fprintf (debug_file,"%s:%d: format=%d, set_format=%d\n",__FILE__,__LINE__, state->format, state->set_format)); //! look at command queue first // enter main processing loop while (process) { // look at command queue first cmd = parse_cmd(state, cmd_file); cmd = parse_cmd(state, cmd_file); if (cmd) { if (cmd) { if (cmd < 0) D0(fprintf(debug_file, "Unrecognized command\n")); if (cmd < 0) D0(fprintf(debug_file, "Unrecognized command\n")); /// Acknowledge received command by copying frame number to per-daemon parameter } else if (state->running) { // no commands in queue, started // GLOBALPARS(G_DAEMON_ERR+lastDaemonBit)=GLOBALPARS(G_THIS_FRAME); } else if (state->running) { //!no commands in queue, started // D3(fprintf (debug_file,"%s:%d: format=%d, set_format=%d\n",__FILE__,__LINE__, state->format, state->set_format)); switch ((rslt = -sendImageFrame(state))) { switch ((rslt = -sendImageFrame(state))) { case 0: case 0: /* break; // frame sent OK, nothing to do (TODO: check file length/duration) D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned %d\n" \ case CAMOGM_FRAME_NOT_READY: // just wait for the frame to appear at the current pointer "state->cirbuf_rp= 0x%x\n",__FILE__,__LINE__,rslt,state->cirbuf_rp)); // we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands */ // TODO - add another wait with (short) timeout? break; //! frame sent OK, nothing to do (TODO: check file length/duration) case CAMOGM_FRAME_NOT_READY: //! just wait for the frame to appear at the current pointer //! we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands //! TODO - add another wait with (short) timeout? // D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned -%d\n",__FILE__,__LINE__,rslt)); /* D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned -%d\n" \ "state->cirbuf_rp= 0x%x\n",__FILE__,__LINE__,rslt,state->cirbuf_rp)); */ // lseek(state->fd_circ,LSEEK_CIRC_WAIT,SEEK_END); fp0 = lseek(state->fd_circ, 0, SEEK_CUR); fp0 = lseek(state->fd_circ, 0, SEEK_CUR); if (fp0 < 0) { if (fp0 < 0) { D0(fprintf(debug_file, "%s:line %d got broken frame (%d) before waiting for ready\n", __FILE__, __LINE__, fp0)); D0(fprintf(debug_file, "%s:line %d got broken frame (%d) before waiting for ready\n", __FILE__, __LINE__, fp0)); Loading @@ -1483,64 +1449,111 @@ int main(int argc, char *argv[]) break; break; } } } } // break; // no break case CAMOGM_FRAME_CHANGED: //! frame parameters have changed case CAMOGM_FRAME_CHANGED: // frame parameters have changed case CAMOGM_FRAME_NEXTFILE: //! next file needed (need to switch to a new file (time/size exceeded limit) case CAMOGM_FRAME_NEXTFILE: // next file needed (need to switch to a new file (time/size exceeded limit) case CAMOGM_FRAME_INVALID: //! invalid frame pointer case CAMOGM_FRAME_INVALID: // invalid frame pointer case CAMOGM_FRAME_BROKEN: //! frame broken (buffer overrun) case CAMOGM_FRAME_BROKEN: // frame broken (buffer overrun) //! restart the file // restart the file // D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned -%d\n",__FILE__,__LINE__,rslt)); D3(fprintf(debug_file,"%s:line %d - sendImageFrame() returned -%d\n", __FILE__, __LINE__, rslt)); camogm_stop(state); camogm_stop(state); camogm_start(state); camogm_start(state); break; break; case CAMOGM_FRAME_FILE_ERR: //! error with file I/O case CAMOGM_FRAME_FILE_ERR: // error with file I/O case CAMOGM_FRAME_OTHER: //! other errors case CAMOGM_FRAME_OTHER: // other errors D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); break; break; default: default: D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); clean_up(state, pipe_names); clean_up(state); exit(-1); exit(-1); } //switch } // switch sendImageFrame() if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED)) state->last_error_code = rslt; if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED)) state->last_error_code = rslt; } else if (state->starting) { //!no commands in queue,starting (but not started yet) } else if (state->starting) { // no commands in queue,starting (but not started yet) // D3(fprintf (debug_file,"%s:%d: format=%d, set_format=%d\n",__FILE__,__LINE__, state->format, state->set_format)); //!retry starting // retry starting switch ((rslt = -camogm_start(state))) { switch ((rslt = -camogm_start(state))) { case 0: break; //! file started OK, nothing to do case 0: break; // file started OK, nothing to do case CAMOGM_TOO_EARLY: case CAMOGM_TOO_EARLY: lseek(state->fd_circ, LSEEK_CIRC_TOWP, SEEK_END); /// set pointer to the frame to wait for lseek(state->fd_circ, LSEEK_CIRC_TOWP, SEEK_END); // set pointer to the frame to wait for lseek(state->fd_circ, LSEEK_CIRC_WAIT, SEEK_END); /// It already passed CAMOGM_FRAME_NOT_READY, so compressor may be running already lseek(state->fd_circ, LSEEK_CIRC_WAIT, SEEK_END); // It already passed CAMOGM_FRAME_NOT_READY, so compressor may be running already break; /// no need to wait extra break; // no need to wait extra case CAMOGM_FRAME_NOT_READY: //! just wait for the frame to appear at the current pointer case CAMOGM_FRAME_NOT_READY: // just wait for the frame to appear at the current pointer //! we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands // we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands //! TODO - add another wait with (short) timeout? // TODO - add another wait with (short) timeout? // lseek(state->fd_circ,LSEEK_CIRC_WAIT,SEEK_END); case CAMOGM_FRAME_CHANGED: // frame parameters have changed case CAMOGM_FRAME_CHANGED: //! frame parameters have changed case CAMOGM_FRAME_NEXTFILE: case CAMOGM_FRAME_NEXTFILE: case CAMOGM_FRAME_INVALID: //! invalid frame pointer case CAMOGM_FRAME_INVALID: // invalid frame pointer case CAMOGM_FRAME_BROKEN: //! frame broken (buffer overrun) case CAMOGM_FRAME_BROKEN: // frame broken (buffer overrun) // D3(fprintf (debug_file,"%s:line %d - camogm_start() returned -%d, state->cirbuf_rp= 0x%x\n", __FILE__,__LINE__,rslt,state->cirbuf_rp)); usleep(COMMAND_LOOP_DELAY); // it should be not too long so empty buffer will not be overrun usleep( COMMAND_LOOP_DELAY); //! it should be not too long so empty buffer will not be overrun break; break; case CAMOGM_FRAME_FILE_ERR: //! error with file I/O case CAMOGM_FRAME_FILE_ERR: // error with file I/O case CAMOGM_FRAME_OTHER: //! other errors case CAMOGM_FRAME_OTHER: // other errors D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); break; break; default: default: D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); clean_up(state, pipe_names); clean_up(state); exit(-1); exit(-1); } //switch } // switch camogm_start() if ((rslt != 0) && (rslt != CAMOGM_TOO_EARLY) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED) ) state->last_error_code = rslt; if ((rslt != 0) && (rslt != CAMOGM_TOO_EARLY) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED) ) state->last_error_code = rslt; } else { //! not running, not starting } else { // not running, not starting usleep( COMMAND_LOOP_DELAY); //! make it longer but interruptible by signals? usleep(COMMAND_LOOP_DELAY); // make it longer but interruptible by signals? } } } // while (process) // normally, we should not be here clean_up(state); return EXIT_SUCCESS; } } clean_up(state, pipe_names); return 0; int main(int argc, char *argv[]) { const char usage[] = "This program allows recording of the video/images acquired by Elphel camera to the storage media.\n" \ "It is designed to run in the background and accept commands through a named pipe.\n\n" \ "Usage:\n\n" \ "%s <named_pipe_name>\n\n" \ "i.e.:\n\n" \ "%s /var/state/camogm_cmd\n\n" \ "When the program is runninig you may send commands by writing strings to the command file\n" \ "(/var/state/camogm_cmd in the example above). The complete list of available commands is available\n" \ "on Elphel Wiki (http://wiki.elphel.com/index.php?title=Camogm), here is the example of usage\n" \ "from the shell prompt in the camera:\n\n" \ "echo \"status; exif=1; format=jpeg;status=/var/tmp/camogm.status\" > /var/state/camogm_cmd\n\n" \ "That will print status information on the standard output (may not be visible if the program was not\n" \ "started from the same session), set exif mode on (each frame will have the full Exif header including\n" \ "a precise time stamp), set output format to a series of individual JPEG files, and then send status\n" \ "information to a file /var/tmp/camogm.status in the camera file system.\n\n" \ "This program does not control the process of acquisition of the video/images to the camera internal\n" \ "buffer, it only retrieves that data from the buffer (waiting when needed), packages it to selected\n" \ "format and stores the result files.\n\n"; char *pipe_names[SENSOR_PORTS] = {0}; int ret; // no command line options processing yet if ((argc < 2) || (argv[1][1] == '-')) { printf(usage, argv[0], argv[0]); return EXIT_SUCCESS; } if (create_pipe_names(argv[1], pipe_names) < 0) { printf("Error: unable to allocate memory for command pipe name\n"); return EXIT_FAILURE; } // spawn a process for each sensor port for (int i = 0; i < SENSOR_PORTS; i++) { camogm_init(&sstate[i], i, pipe_names[i]); if (fork() == 0) { ret = listener_loop(&sstate[i]); exit(ret); } } return EXIT_SUCCESS; } } /** /** Loading camogm.h +3 −2 Original line number Original line Diff line number Diff line Loading @@ -87,7 +87,7 @@ typedef struct { int fd_fparmsall; //! file descriptor for sensor/compressor parameters int fd_fparmsall; //! file descriptor for sensor/compressor parameters int fd_exif; //! file descriptor for Exif data int fd_exif; //! file descriptor for Exif data int head_size; //! JPEG header size int head_size; //! JPEG header size char jpegHeader [JPEG_HEADER_MAXSIZE]; unsigned char jpegHeader [JPEG_HEADER_MAXSIZE]; int metadata_start; int metadata_start; struct interframe_params_t frame_params; struct interframe_params_t frame_params; struct interframe_params_t this_frame_params; struct interframe_params_t this_frame_params; Loading Loading @@ -158,7 +158,8 @@ typedef struct { int kml_last_uts; //! last generated kml file timestamp, microseconds int kml_last_uts; //! last generated kml file timestamp, microseconds struct exif_dir_table_t kml_exif[ExifKmlNumber]; //! store locations of the fields needed for KML generations in the Exif block struct exif_dir_table_t kml_exif[ExifKmlNumber]; //! store locations of the fields needed for KML generations in the Exif block unsigned int port_num; unsigned int port_num; // sensor port this state assigned to char *pipe_name; // command pipe name for this sensor port } camogm_state; } camogm_state; extern int debug_level; extern int debug_level; Loading Loading
camogm.c +167 −154 Original line number Original line Diff line number Diff line Loading @@ -145,6 +145,7 @@ /* end of debug code */ /* end of debug code */ #define COMMAND_LOOP_DELAY 500000 //0.5sec #define DEFAULT_DEBUG_LVL 6 #define DEFAULT_DEBUG_LVL 6 #define TRAILER_SIZE 0x02 #define TRAILER_SIZE 0x02 #define MAP_OPTIONS MAP_FILE | MAP_PRIVATE #define MAP_OPTIONS MAP_FILE | MAP_PRIVATE Loading Loading @@ -194,7 +195,7 @@ camogm_state sstate[SENSOR_PORTS]; int debug_level; int debug_level; FILE* debug_file; FILE* debug_file; int camogm_init(camogm_state *state, unsigned int port); void camogm_init(camogm_state *state, unsigned int port, char *pipe_name); int camogm_start(camogm_state *state); int camogm_start(camogm_state *state); int camogm_stop(camogm_state *state); int camogm_stop(camogm_state *state); int camogm_reset(camogm_state *state); //! reset circbuf read pointer int camogm_reset(camogm_state *state); //! reset circbuf read pointer Loading Loading @@ -269,7 +270,14 @@ void put_uint64(void *buf, u_int64_t val) tmp[7] = (val >>= 8) & 0xff; tmp[7] = (val >>= 8) & 0xff; } } int camogm_init(camogm_state *state, unsigned int port) /** * @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) { { const char sserial[] = "elp0"; const char sserial[] = "elp0"; int * ipser = (int*)sserial; int * ipser = (int*)sserial; Loading @@ -283,17 +291,17 @@ int camogm_init(camogm_state *state, unsigned int port) camogm_set_ignore_fps(state, DEFAULT_IGNORE_FPS); camogm_set_ignore_fps(state, DEFAULT_IGNORE_FPS); camogm_set_max_frames(state, DEFAULT_FRAMES); camogm_set_max_frames(state, DEFAULT_FRAMES); camogm_set_frames_per_chunk(state, DEFAULT_FRAMES_PER_CHUNK); camogm_set_frames_per_chunk(state, DEFAULT_FRAMES_PER_CHUNK); camogm_set_start_after_timestamp(state, 0.0); /// start any time camogm_set_start_after_timestamp(state, 0.0); // start any time camogm_set_prefix(state, "\0"); camogm_set_prefix(state, "\0"); camogm_set_save_gp(state, 0); camogm_set_save_gp(state, 0); camogm_reset(state); //! sets state->buf_overruns=-1; //!first does not count camogm_reset(state); // sets state->buf_overruns =- 1 state->serialno = ipser[0]; state->serialno = ipser[0]; state->last = 0; state->last = 0; debug_file = stderr; debug_file = stderr; camogm_debug_level(DEFAULT_DEBUG_LVL); camogm_debug_level(DEFAULT_DEBUG_LVL); strcpy(state->debug_name, "stderr"); strcpy(state->debug_name, "stderr"); camogm_set_timescale(state, 1.0); camogm_set_timescale(state, 1.0); camogm_set_frames_skip(state, 0); //! don't skip camogm_set_frames_skip(state, 0); // don't skip camogm_set_format(state, CAMOGM_FORMAT_OGM); camogm_set_format(state, CAMOGM_FORMAT_OGM); state->exifSize = 0; state->exifSize = 0; state->exif = DEFAULT_EXIF; state->exif = DEFAULT_EXIF; Loading @@ -302,7 +310,7 @@ int camogm_init(camogm_state *state, unsigned int port) state->formats = 0; state->formats = 0; state->last_error_code = 0; state->last_error_code = 0; ///kml stuff // kml stuff camogm_kml_set_enable(state, 0); camogm_kml_set_enable(state, 0); state->kml_file = NULL; state->kml_file = NULL; camogm_kml_set_horHalfFov(state, 20.0); camogm_kml_set_horHalfFov(state, 20.0); Loading @@ -314,8 +322,7 @@ int camogm_init(camogm_state *state, unsigned int port) state->kml_path[0] = '\0'; state->kml_path[0] = '\0'; state->port_num = port; state->port_num = port; state->pipe_name = pipe_name; return 0; } } Loading Loading @@ -1280,16 +1287,22 @@ int create_pipe_names(const char *pipe_name, char **names) names[i] = name; names[i] = name; } } if (ret < 0) { for (int i = 0; i < SENSOR_PORTS; i++) { free(names[i]); names[i] = NULL; } } return ret; return ret; } } /** /** * @brief This function closes open files and deletes allocated memory. * @brief This function closes open files and deletes allocated memory. * @param[in] state pointer to #camogm_state structure for a particular sensor channel * @param[in] state pointer to #camogm_state structure for a particular sensor channel * @param[in] names list of strings containing control pipe names * return none * return none */ */ void clean_up(camogm_state *state, char **names) void clean_up(camogm_state *state) { { if (state->fd_exif > 0) if (state->fd_exif > 0) close(state->fd_exif); close(state->fd_exif); Loading @@ -1299,177 +1312,130 @@ void clean_up(camogm_state *state, char **names) close(state->fd_circ); close(state->fd_circ); if (state->fd_fparmsall) if (state->fd_fparmsall) close(state->fd_fparmsall); close(state->fd_fparmsall); for (int i = 0; i < SENSOR_PORTS; i++) { free(state->pipe_name); free(names[i]); state->pipe_name = NULL; names[i] = NULL; } } } int main(int argc, char *argv[]) /** * @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) { { // int fd_circ; FILE *cmd_file; FILE *cmd_file; const char usage[] = "This program allows recording of the video/images acquired by Elphel camera to the storage media.\n" \ int rslt, ret, cmd; "It is designed to run in the background and accept commands through a named pipe.\n\n" \ int fp0, fp1; "Usage:\n\n" \ int process = 1; "%s <named_pipe_name>\n\n" \ unsigned int port = state->port_num; "i.e.:\n\n" \ const char *pipe_name = state->pipe_name; "%s /var/state/camogm_cmd\n\n" \ "When the program is runninig you may send commands by writing strings to the command file\n" \ "(/var/state/camogm_cmd in the example above). The complete list of available commands is available\n" \ "on Elphel Wiki (http://wiki.elphel.com/index.php?title=Camogm), here is the example of usage\n" \ "from the shell prompt in the camera:\n\n" \ "echo \"status; exif=1; format=jpeg;status=/var/tmp/camogm.status\" > /var/state/camogm_cmd\n\n" \ "That will print status information on the standard output (may not be visible if the program was not\n" \ "started from the same session), set exif mode on (each frame will have the full Exif header including\n" \ "a precise time stamp), set output format to a series of individual JPEG files, and then send status\n" \ "information to a file /var/tmp/camogm.status in the camera file system.\n\n" \ "This program does not control the process of acquisition of the video/images to the camera internal\n" \ "buffer, it only retrieves that data from the buffer (waiting when needed), packages it to selected\n" \ "format and stores the result files.\n\n"; int go = 1; int cmd; int i, rslt; int fp0, fp1; // debugging /* debug code follows */ unsigned int port = 2; /*end of debug code */ camogm_state *state = &sstate[port]; char *pipe_names[SENSOR_PORTS] = {0}; // state = &sstate; //extern //! no command line options processing yet if ((argc < 2) || (argv[1][1] == '-')) { printf(usage, argv[0], argv[0]); clean_up(state, pipe_names); return 0; } camogm_init(state, port); create_pipe_names(argv[1], pipe_names); //! open Exif header file // open Exif header file #ifdef DISABLE_CODE #ifdef DISABLE_CODE state->fd_exif = open(exifFileNames[port], O_RDONLY); state->fd_exif = open(exifFileNames[port], O_RDONLY); if (state->fd_exif < 0) { // check control OK if (state->fd_exif < 0) { // check control OK D0(fprintf(debug_file, "Error opening %s\n", exifFileNames[port])); D0(fprintf(debug_file, "Error opening %s\n", exifFileNames[port])); clean_up(state); return -1; return -1; } } #endif /* DESABLE_CODE */ #endif /* DESABLE_CODE */ //! open JPEG header file // open JPEG header file state->fd_head = open(headFileNames[port], O_RDWR); state->fd_head = open(headFileNames[port], O_RDWR); if (state->fd_head < 0) { // check control OK if (state->fd_head < 0) { // check control OK D0(fprintf(debug_file, "Error opening %s\n", headFileNames[port])); D0(fprintf(debug_file, "Error opening %s\n", headFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -1; return -1; } } state->head_size = lseek(state->fd_head, 0, SEEK_END); state->head_size = lseek(state->fd_head, 0, SEEK_END); if (state->head_size > JPEG_HEADER_MAXSIZE) { 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 )); D0(fprintf(debug_file, "%s:%d: Too big JPEG header (%d > %d)", __FILE__, __LINE__, state->head_size, JPEG_HEADER_MAXSIZE )); clean_up(state, pipe_names); clean_up(state); return -2; return -2; } } //! open circbuf and mmap it (once at startup) // open circbuf and mmap it (once at startup) state->fd_circ = open(circbufFileNames[port], O_RDWR); state->fd_circ = open(circbufFileNames[port], O_RDWR); if (state->fd_circ < 0) { // check control OK if (state->fd_circ < 0) { // check control OK D0(fprintf(debug_file, "Error opening %s\n", circbufFileNames[port])); D0(fprintf(debug_file, "Error opening %s\n", circbufFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -2; return -2; } } /*! find total buffer length (it is in defines, actually in c313a.h */ // find total buffer length (it is in defines, actually in c313a.h state->circ_buff_size = lseek(state->fd_circ, 0, SEEK_END); state->circ_buff_size = lseek(state->fd_circ, 0, SEEK_END); ccam_dma_buf[port] = (unsigned long*)mmap(0, state->circ_buff_size, PROT_READ, MAP_SHARED, state->fd_circ, 0); ccam_dma_buf[port] = (unsigned long*)mmap(0, state->circ_buff_size, PROT_READ, MAP_SHARED, state->fd_circ, 0); if ((int)ccam_dma_buf[port] == -1) { if ((int)ccam_dma_buf[port] == -1) { D0(fprintf(debug_file, "Error in mmap of %s\n", circbufFileNames[port])); D0(fprintf(debug_file, "Error in mmap of %s\n", circbufFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -3; return -3; } } //! Now open/mmap file to read sensor/compressor parameters (currently - just free memory in circbuf and compressor state) // now open/mmap file to read sensor/compressor parameters (currently - just free memory in circbuf and compressor state) //! open circbuf and mmap it (once at startup) #ifdef DISABLE_CODE #ifdef DISABLE_CODE state->fd_fparmsall = open(ctlFileNames[port], O_RDWR); state->fd_fparmsall = open(ctlFileNames[port], O_RDWR); if (state->fd_fparmsall < 0) { // check control OK if (state->fd_fparmsall < 0) { // check control OK D0(fprintf(debug_file, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); D0(fprintf(debug_file, "%s:%d:%s: Error opening %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -2; return -2; } } //! now try to mmap // now try to mmap /// frameParsAll = (struct framepars_all_t *) mmap(0, sizeof (struct framepars_all_t) , PROT_READ, MAP_SHARED, state->fd_fparmsall, 0); // PROT_WRITE - only to write acknowledge /// PROT_WRITE - only to write acknowledge frameParsAll[port] = (struct framepars_all_t*)mmap(0, sizeof(struct framepars_all_t), PROT_READ | PROT_WRITE, MAP_SHARED, state->fd_fparmsall, 0); frameParsAll[port] = (struct framepars_all_t*)mmap(0, sizeof(struct framepars_all_t), PROT_READ | PROT_WRITE, MAP_SHARED, state->fd_fparmsall, 0); if ((int)frameParsAll[port] == -1) { if ((int)frameParsAll[port] == -1) { D0(fprintf(debug_file, "%s:%d:%s: Error in mmap in %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); D0(fprintf(debug_file, "%s:%d:%s: Error in mmap in %s\n", __FILE__, __LINE__, __FUNCTION__, ctlFileNames[port])); clean_up(state, pipe_names); clean_up(state); return -3; return -3; } } framePars[port] = frameParsAll[port]->framePars; framePars[port] = frameParsAll[port]->framePars; globalPars[port] = frameParsAll[port]->globalPars; globalPars[port] = frameParsAll[port]->globalPars; #endif /* DESABLE_CODE */ #endif /* DESABLE_CODE */ //!create a named pipe // create a named pipe //!always delete the pipe if it existed, start a fresh one // always delete the pipe if it existed, start a fresh one i = unlink(pipe_names[port]); ret = unlink(pipe_name); if (i) { if (ret) { D1(fprintf(debug_file, "Unlink %s returned %d, errno=%d \n", pipe_names[port], i, errno)); D1(fprintf(debug_file, "Unlink %s returned %d, errno=%d \n", pipe_name, ret, errno)); } } i = mkfifo(pipe_names[port], 0777); //EEXIST ret = mkfifo(pipe_name, 0777); //EEXIST //! now should not exist // now should not exist if (i) { if (ret) { if (errno == EEXIST) { if (errno == EEXIST) { D1(fprintf(debug_file, "Named pipe %s already exists, will use it.\n", pipe_names[port])); D1(fprintf(debug_file, "Named pipe %s already exists, will use it.\n", pipe_name)); } else { } else { D0(fprintf(debug_file, "Can not create a named pipe %s, errno=%d \n", pipe_names[port], errno)); D0(fprintf(debug_file, "Can not create a named pipe %s, errno=%d \n", pipe_name, errno)); clean_up(state, pipe_names); clean_up(state); return -4; return -4; } } } } //!now open the pipe - will block until something will be written (or just open for writing // now open the pipe - will block until something will be written (or just open for writing, //!Reads themselves will not block // reads themselves will not block) if (!((cmd_file = fopen(pipe_names[port], "r")))) { if (!((cmd_file = fopen(pipe_name, "r")))) { D0(fprintf(debug_file, "Can not open command file %s\n", pipe_names[port])); D0(fprintf(debug_file, "Can not open command file %s\n", pipe_name)); clean_up(state, pipe_names); clean_up(state); return -5; return -5; } } // D1(fprintf (debug_file,"Pipe %s open for reading\n",argv[1])); D0(fprintf(debug_file, "Pipe %s open for reading\n", pipe_name)); // to make sure something is sent out D0(fprintf(debug_file, "Pipe %s open for reading\n", pipe_names[port])); // to make sure something is sent out //! Here is a main loop. If recording is on, it will check for commands after each frame, if it is off - poll with fixed usleep #define COMMAND_LOOP_DELAY 500000 //0.5sec while (go) { // D3(fprintf (debug_file,"%s:%d: format=%d, set_format=%d\n",__FILE__,__LINE__, state->format, state->set_format)); //! look at command queue first // enter main processing loop while (process) { // look at command queue first cmd = parse_cmd(state, cmd_file); cmd = parse_cmd(state, cmd_file); if (cmd) { if (cmd) { if (cmd < 0) D0(fprintf(debug_file, "Unrecognized command\n")); if (cmd < 0) D0(fprintf(debug_file, "Unrecognized command\n")); /// Acknowledge received command by copying frame number to per-daemon parameter } else if (state->running) { // no commands in queue, started // GLOBALPARS(G_DAEMON_ERR+lastDaemonBit)=GLOBALPARS(G_THIS_FRAME); } else if (state->running) { //!no commands in queue, started // D3(fprintf (debug_file,"%s:%d: format=%d, set_format=%d\n",__FILE__,__LINE__, state->format, state->set_format)); switch ((rslt = -sendImageFrame(state))) { switch ((rslt = -sendImageFrame(state))) { case 0: case 0: /* break; // frame sent OK, nothing to do (TODO: check file length/duration) D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned %d\n" \ case CAMOGM_FRAME_NOT_READY: // just wait for the frame to appear at the current pointer "state->cirbuf_rp= 0x%x\n",__FILE__,__LINE__,rslt,state->cirbuf_rp)); // we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands */ // TODO - add another wait with (short) timeout? break; //! frame sent OK, nothing to do (TODO: check file length/duration) case CAMOGM_FRAME_NOT_READY: //! just wait for the frame to appear at the current pointer //! we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands //! TODO - add another wait with (short) timeout? // D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned -%d\n",__FILE__,__LINE__,rslt)); /* D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned -%d\n" \ "state->cirbuf_rp= 0x%x\n",__FILE__,__LINE__,rslt,state->cirbuf_rp)); */ // lseek(state->fd_circ,LSEEK_CIRC_WAIT,SEEK_END); fp0 = lseek(state->fd_circ, 0, SEEK_CUR); fp0 = lseek(state->fd_circ, 0, SEEK_CUR); if (fp0 < 0) { if (fp0 < 0) { D0(fprintf(debug_file, "%s:line %d got broken frame (%d) before waiting for ready\n", __FILE__, __LINE__, fp0)); D0(fprintf(debug_file, "%s:line %d got broken frame (%d) before waiting for ready\n", __FILE__, __LINE__, fp0)); Loading @@ -1483,64 +1449,111 @@ int main(int argc, char *argv[]) break; break; } } } } // break; // no break case CAMOGM_FRAME_CHANGED: //! frame parameters have changed case CAMOGM_FRAME_CHANGED: // frame parameters have changed case CAMOGM_FRAME_NEXTFILE: //! next file needed (need to switch to a new file (time/size exceeded limit) case CAMOGM_FRAME_NEXTFILE: // next file needed (need to switch to a new file (time/size exceeded limit) case CAMOGM_FRAME_INVALID: //! invalid frame pointer case CAMOGM_FRAME_INVALID: // invalid frame pointer case CAMOGM_FRAME_BROKEN: //! frame broken (buffer overrun) case CAMOGM_FRAME_BROKEN: // frame broken (buffer overrun) //! restart the file // restart the file // D3(fprintf (debug_file,"%s:line %d - sendImageFrame() returned -%d\n",__FILE__,__LINE__,rslt)); D3(fprintf(debug_file,"%s:line %d - sendImageFrame() returned -%d\n", __FILE__, __LINE__, rslt)); camogm_stop(state); camogm_stop(state); camogm_start(state); camogm_start(state); break; break; case CAMOGM_FRAME_FILE_ERR: //! error with file I/O case CAMOGM_FRAME_FILE_ERR: // error with file I/O case CAMOGM_FRAME_OTHER: //! other errors case CAMOGM_FRAME_OTHER: // other errors D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); break; break; default: default: D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); clean_up(state, pipe_names); clean_up(state); exit(-1); exit(-1); } //switch } // switch sendImageFrame() if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED)) state->last_error_code = rslt; if ((rslt != 0) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED)) state->last_error_code = rslt; } else if (state->starting) { //!no commands in queue,starting (but not started yet) } else if (state->starting) { // no commands in queue,starting (but not started yet) // D3(fprintf (debug_file,"%s:%d: format=%d, set_format=%d\n",__FILE__,__LINE__, state->format, state->set_format)); //!retry starting // retry starting switch ((rslt = -camogm_start(state))) { switch ((rslt = -camogm_start(state))) { case 0: break; //! file started OK, nothing to do case 0: break; // file started OK, nothing to do case CAMOGM_TOO_EARLY: case CAMOGM_TOO_EARLY: lseek(state->fd_circ, LSEEK_CIRC_TOWP, SEEK_END); /// set pointer to the frame to wait for lseek(state->fd_circ, LSEEK_CIRC_TOWP, SEEK_END); // set pointer to the frame to wait for lseek(state->fd_circ, LSEEK_CIRC_WAIT, SEEK_END); /// It already passed CAMOGM_FRAME_NOT_READY, so compressor may be running already lseek(state->fd_circ, LSEEK_CIRC_WAIT, SEEK_END); // It already passed CAMOGM_FRAME_NOT_READY, so compressor may be running already break; /// no need to wait extra break; // no need to wait extra case CAMOGM_FRAME_NOT_READY: //! just wait for the frame to appear at the current pointer case CAMOGM_FRAME_NOT_READY: // just wait for the frame to appear at the current pointer //! we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands // we'll wait for a frame, not to waste resources. But if the compressor is stopped this program will not respond to any commands //! TODO - add another wait with (short) timeout? // TODO - add another wait with (short) timeout? // lseek(state->fd_circ,LSEEK_CIRC_WAIT,SEEK_END); case CAMOGM_FRAME_CHANGED: // frame parameters have changed case CAMOGM_FRAME_CHANGED: //! frame parameters have changed case CAMOGM_FRAME_NEXTFILE: case CAMOGM_FRAME_NEXTFILE: case CAMOGM_FRAME_INVALID: //! invalid frame pointer case CAMOGM_FRAME_INVALID: // invalid frame pointer case CAMOGM_FRAME_BROKEN: //! frame broken (buffer overrun) case CAMOGM_FRAME_BROKEN: // frame broken (buffer overrun) // D3(fprintf (debug_file,"%s:line %d - camogm_start() returned -%d, state->cirbuf_rp= 0x%x\n", __FILE__,__LINE__,rslt,state->cirbuf_rp)); usleep(COMMAND_LOOP_DELAY); // it should be not too long so empty buffer will not be overrun usleep( COMMAND_LOOP_DELAY); //! it should be not too long so empty buffer will not be overrun break; break; case CAMOGM_FRAME_FILE_ERR: //! error with file I/O case CAMOGM_FRAME_FILE_ERR: // error with file I/O case CAMOGM_FRAME_OTHER: //! other errors case CAMOGM_FRAME_OTHER: // other errors D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - error=%d\n", __FILE__, __LINE__, rslt)); break; break; default: default: D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); D0(fprintf(debug_file, "%s:line %d - should not get here (rslt=%d)\n", __FILE__, __LINE__, rslt)); clean_up(state, pipe_names); clean_up(state); exit(-1); exit(-1); } //switch } // switch camogm_start() if ((rslt != 0) && (rslt != CAMOGM_TOO_EARLY) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED) ) state->last_error_code = rslt; if ((rslt != 0) && (rslt != CAMOGM_TOO_EARLY) && (rslt != CAMOGM_FRAME_NOT_READY) && (rslt != CAMOGM_FRAME_CHANGED) ) state->last_error_code = rslt; } else { //! not running, not starting } else { // not running, not starting usleep( COMMAND_LOOP_DELAY); //! make it longer but interruptible by signals? usleep(COMMAND_LOOP_DELAY); // make it longer but interruptible by signals? } } } // while (process) // normally, we should not be here clean_up(state); return EXIT_SUCCESS; } } clean_up(state, pipe_names); return 0; int main(int argc, char *argv[]) { const char usage[] = "This program allows recording of the video/images acquired by Elphel camera to the storage media.\n" \ "It is designed to run in the background and accept commands through a named pipe.\n\n" \ "Usage:\n\n" \ "%s <named_pipe_name>\n\n" \ "i.e.:\n\n" \ "%s /var/state/camogm_cmd\n\n" \ "When the program is runninig you may send commands by writing strings to the command file\n" \ "(/var/state/camogm_cmd in the example above). The complete list of available commands is available\n" \ "on Elphel Wiki (http://wiki.elphel.com/index.php?title=Camogm), here is the example of usage\n" \ "from the shell prompt in the camera:\n\n" \ "echo \"status; exif=1; format=jpeg;status=/var/tmp/camogm.status\" > /var/state/camogm_cmd\n\n" \ "That will print status information on the standard output (may not be visible if the program was not\n" \ "started from the same session), set exif mode on (each frame will have the full Exif header including\n" \ "a precise time stamp), set output format to a series of individual JPEG files, and then send status\n" \ "information to a file /var/tmp/camogm.status in the camera file system.\n\n" \ "This program does not control the process of acquisition of the video/images to the camera internal\n" \ "buffer, it only retrieves that data from the buffer (waiting when needed), packages it to selected\n" \ "format and stores the result files.\n\n"; char *pipe_names[SENSOR_PORTS] = {0}; int ret; // no command line options processing yet if ((argc < 2) || (argv[1][1] == '-')) { printf(usage, argv[0], argv[0]); return EXIT_SUCCESS; } if (create_pipe_names(argv[1], pipe_names) < 0) { printf("Error: unable to allocate memory for command pipe name\n"); return EXIT_FAILURE; } // spawn a process for each sensor port for (int i = 0; i < SENSOR_PORTS; i++) { camogm_init(&sstate[i], i, pipe_names[i]); if (fork() == 0) { ret = listener_loop(&sstate[i]); exit(ret); } } return EXIT_SUCCESS; } } /** /** Loading
camogm.h +3 −2 Original line number Original line Diff line number Diff line Loading @@ -87,7 +87,7 @@ typedef struct { int fd_fparmsall; //! file descriptor for sensor/compressor parameters int fd_fparmsall; //! file descriptor for sensor/compressor parameters int fd_exif; //! file descriptor for Exif data int fd_exif; //! file descriptor for Exif data int head_size; //! JPEG header size int head_size; //! JPEG header size char jpegHeader [JPEG_HEADER_MAXSIZE]; unsigned char jpegHeader [JPEG_HEADER_MAXSIZE]; int metadata_start; int metadata_start; struct interframe_params_t frame_params; struct interframe_params_t frame_params; struct interframe_params_t this_frame_params; struct interframe_params_t this_frame_params; Loading Loading @@ -158,7 +158,8 @@ typedef struct { int kml_last_uts; //! last generated kml file timestamp, microseconds int kml_last_uts; //! last generated kml file timestamp, microseconds struct exif_dir_table_t kml_exif[ExifKmlNumber]; //! store locations of the fields needed for KML generations in the Exif block struct exif_dir_table_t kml_exif[ExifKmlNumber]; //! store locations of the fields needed for KML generations in the Exif block unsigned int port_num; unsigned int port_num; // sensor port this state assigned to char *pipe_name; // command pipe name for this sensor port } camogm_state; } camogm_state; extern int debug_level; extern int debug_level; Loading