/* * This file is part of MotionPlus. * * MotionPlus 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. * * MotionPlus 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 MotionPlus. If not, see . * * Copyright 2020 MotionMrDave@gmail.com */ #include "motionplus.hpp" #include "conf.hpp" #include "logger.hpp" #include "util.hpp" #include "picture.hpp" #include "netcam.hpp" #include "movie.hpp" #include "event.hpp" #include "dbse.hpp" #include "video_loopback.hpp" #include "video_common.hpp" #include "webu_stream.hpp" /* Various functions (most doing the actual action) * TODO Items: * Rework the snprintf uses. * Edit directories so they can never be null and eliminate defaults from here * Move the movie initialize stuff to movie module * eliminate #if for v4l2 * Eliminate #IF for database items * Move database functions out of here. * Move stream stuff to webu_stream * Use (void) alternative for ATTRIBUTE_UNUSED */ const char *eventList[] = { "NULL", "EVENT_FILECREATE", "EVENT_MOTION", "EVENT_FIRSTMOTION", "EVENT_ENDMOTION", "EVENT_STOP", "EVENT_TIMELAPSE", "EVENT_TIMELAPSEEND", "EVENT_STREAM", "EVENT_IMAGE_DETECTED", "EVENT_IMAGEM_DETECTED", "EVENT_IMAGE_SNAPSHOT", "EVENT_IMAGE", "EVENT_IMAGEM", "EVENT_IMAGE_PREVIEW", "EVENT_FILECLOSE", "EVENT_DEBUG", "EVENT_CRITICAL", "EVENT_AREA_DETECTED", "EVENT_CAMERA_LOST", "EVENT_CAMERA_FOUND", "EVENT_MOVIE_PUT", "EVENT_LAST" }; /** * exec_command * Execute 'command' with 'arg' as its argument. * if !arg command is started with no arguments * Before we call execl we need to close all the file handles * that the fork inherited from the parent in order not to pass * the open handles on to the shell */ static void exec_command(struct ctx_cam *cam, const char *command, char *filename, int filetype) { char stamp[PATH_MAX]; mystrftime(cam, stamp, sizeof(stamp), command, &cam->current_image->imgts, filename, filetype); if (!fork()) { int i; /* Detach from parent */ setsid(); /* * Close any file descriptor except console because we will * like to see error messages */ for (i = getdtablesize() - 1; i > 2; i--) close(i); execl("/bin/sh", "sh", "-c", stamp, " &",(char*)NULL); /* if above function succeeds the program never reach here */ MOTION_LOG(ALR, TYPE_EVENTS, SHOW_ERRNO ,_("Unable to start external command '%s'"), stamp); exit(1); } MOTION_LOG(DBG, TYPE_EVENTS, NO_ERRNO ,_("Executing external command '%s'"), stamp); } static void event_newfile(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)cam; (void)evnt; (void)img_data; (void)ts1; MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO ,_("File of type %ld saved to: %s") ,(unsigned long)ftype, fname); } static void event_beep(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (!cam->conf->quiet) printf("\a"); } /** * on_picture_save_command * handles both on_picture_save and on_movie_start * If arg = FTYPE_IMAGE_ANY on_picture_save script is executed * If arg = FTYPE_MPEG_ANY on_movie_start script is executed * The scripts are executed with the filename of picture or movie appended * to the config parameter. */ static void on_picture_save_command(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int filetype = (unsigned long)ftype; (void)evnt; (void)img_data; (void)ts1; if ((filetype & FTYPE_IMAGE_ANY) != 0 && (cam->conf->on_picture_save != "")) exec_command(cam, cam->conf->on_picture_save.c_str(), fname, filetype); if ((filetype & FTYPE_MPEG_ANY) != 0 && (cam->conf->on_movie_start != "")) exec_command(cam, cam->conf->on_movie_start.c_str(), fname, filetype); } static void on_motion_detected_command(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->on_motion_detected != "") exec_command(cam, cam->conf->on_motion_detected.c_str(), NULL, 0); } static void event_sqlfirstmotion(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->database_type == "") { return; } else { dbse_firstmotion(cam); } } static void event_sqlnewfile(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int sqltype = (unsigned long)ftype; (void)evnt; (void)img_data; /* Only log the file types we want */ if ((cam->conf->database_type == "") || (sqltype & cam->dbse->sql_mask) == 0){ return; } else { dbse_newfile(cam, fname, sqltype, ts1); } } static void event_sqlfileclose(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int sqltype = (unsigned long)ftype; (void)evnt; (void)img_data; /* Only log the file types we want */ if ((cam->conf->database_type == "") || (sqltype & cam->dbse->sql_mask) == 0){ return; } else { dbse_fileclose(cam, fname, sqltype, ts1); } } static void on_area_command(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->on_area_detected != "") exec_command(cam, cam->conf->on_area_detected.c_str(), NULL, 0); } static void on_event_start_command(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->on_event_start != "") exec_command(cam, cam->conf->on_event_start.c_str(), NULL, 0); } static void on_event_end_command(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->on_event_end != "") exec_command(cam, cam->conf->on_event_end.c_str(), NULL, 0); } static void event_stream_put(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)fname; (void)ftype; (void)ts1; webu_stream_getimg(cam, img_data); } static void event_vlp_putpipe(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)fname; (void)ts1; if (*(int *)ftype >= 0) { if (vlp_putpipe(*(int *)ftype, img_data->image_norm, cam->imgs.size_norm) == -1) MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("Failed to put image into video pipe")); } } const char *imageext(struct ctx_cam *cam) { if (cam->conf->picture_type == "ppm") return "ppm"; if (cam->conf->picture_type == "webp") return "webp"; return "jpg"; } static void event_image_detect(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { char fullfilename[PATH_MAX]; char filename[PATH_MAX]; int passthrough, retcd; (void)evnt; (void)fname; (void)ftype; if (cam->new_img & NEWIMG_ON) { mystrftime(cam, filename, sizeof(filename), cam->conf->picture_filename.c_str(), ts1, NULL, 0); retcd = snprintf(fullfilename, PATH_MAX, "%s/%s.%s" , cam->conf->target_dir.c_str(), filename, imageext(cam)); if ((retcd < 0) || (retcd >= PATH_MAX)){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error creating image file name")); return; } passthrough = mycheck_passthrough(cam); if ((cam->imgs.size_high > 0) && (!passthrough)) { pic_save_norm(cam, fullfilename,img_data->image_high, FTYPE_IMAGE); } else { pic_save_norm(cam, fullfilename,img_data->image_norm, FTYPE_IMAGE); } event(cam, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE, ts1); } } static void event_imagem_detect(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { char fullfilename[PATH_MAX]; char filename[PATH_MAX]; int retcd; (void)evnt; (void)img_data; (void)fname; (void)ftype; if (cam->conf->picture_output_motion == "on") { mystrftime(cam, filename, sizeof(filename), cam->conf->picture_filename.c_str(), ts1, NULL, 0); retcd = snprintf(fullfilename, PATH_MAX, "%s/%sm.%s" , cam->conf->target_dir.c_str(), filename, imageext(cam)); if ((retcd < 0) || (retcd >= PATH_MAX)){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error creating image motion file name")); return; } pic_save_norm(cam, fullfilename, cam->imgs.image_motion.image_norm, FTYPE_IMAGE_MOTION); event(cam, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE, ts1); } else if (cam->conf->picture_output_motion == "roi") { mystrftime(cam, filename, sizeof(filename), cam->conf->picture_filename.c_str(), ts1, NULL, 0); retcd = snprintf(fullfilename, PATH_MAX, "%s/%sr.%s" , cam->conf->target_dir.c_str(), filename, imageext(cam)); if ((retcd < 0) || (retcd >= PATH_MAX)){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error creating image motion roi file name")); return; } pic_save_roi(cam, fullfilename, cam->current_image->image_norm); event(cam, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE, ts1); } } static void event_image_snapshot(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { char fullfilename[PATH_MAX]; char filename[PATH_MAX]; char filepath[PATH_MAX]; char linkpath[PATH_MAX]; int offset, retcd; (void)evnt; (void)fname; (void)ftype; offset = cam->conf->snapshot_filename.length() - 8; if (offset < 0) offset = 1; if (cam->conf->snapshot_filename.compare(offset, 8, "lastsnap") != 0) { mystrftime(cam, filepath, sizeof(filepath), cam->conf->snapshot_filename.c_str(), ts1, NULL, 0); retcd = snprintf(filename, PATH_MAX, "%s.%s", filepath, imageext(cam)); if (retcd <0) MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, _("Error option")); retcd =snprintf(fullfilename, PATH_MAX, "%s/%s", cam->conf->target_dir.c_str(), filename); if (retcd <0) MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, _("Error option")); pic_save_norm(cam, fullfilename, img_data->image_norm, FTYPE_IMAGE_SNAPSHOT); event(cam, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE_SNAPSHOT, ts1); /* Update symbolic link */ snprintf(linkpath, PATH_MAX, "%s/lastsnap.%s", cam->conf->target_dir.c_str(), imageext(cam)); remove(linkpath); if (symlink(filename, linkpath)) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("Could not create symbolic link [%s]"), filename); return; } } else { mystrftime(cam, filepath, sizeof(filepath), cam->conf->snapshot_filename.c_str(), ts1, NULL, 0); retcd = snprintf(filename, PATH_MAX, "%s.%s", filepath, imageext(cam)); if (retcd <0) MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, _("Error option")); retcd = snprintf(fullfilename, PATH_MAX, "%s/%s", cam->conf->target_dir.c_str(), filename); if (retcd <0) MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, _("Error option")); remove(fullfilename); pic_save_norm(cam, fullfilename, img_data->image_norm, FTYPE_IMAGE_SNAPSHOT); event(cam, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE_SNAPSHOT, ts1); } cam->snapshot = 0; } static void event_image_preview(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { char previewname[PATH_MAX]; char filename[PATH_MAX]; struct ctx_image_data *saved_current_image; int passthrough, retcd; (void)evnt; (void)img_data; (void)fname; (void)ftype; if (cam->imgs.image_preview.diffs) { saved_current_image = cam->current_image; cam->current_image = &cam->imgs.image_preview; mystrftime(cam, filename, sizeof(filename), cam->conf->picture_filename.c_str() , &cam->imgs.image_preview.imgts, NULL, 0); retcd = snprintf(previewname, PATH_MAX, "%s/%s.%s" , cam->conf->target_dir.c_str(), filename, imageext(cam)); if ((retcd < 0) || (retcd >= PATH_MAX)){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error creating preview file name")); return; } passthrough = mycheck_passthrough(cam); if ((cam->imgs.size_high > 0) && (!passthrough)) { pic_save_norm(cam, previewname, cam->imgs.image_preview.image_high , FTYPE_IMAGE); } else { pic_save_norm(cam, previewname, cam->imgs.image_preview.image_norm, FTYPE_IMAGE); } event(cam, EVENT_FILECREATE, NULL, previewname, (void *)FTYPE_IMAGE, ts1); /* Restore global context values. */ cam->current_image = saved_current_image; } } static void event_camera_lost(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->on_camera_lost != "") exec_command(cam, cam->conf->on_camera_lost.c_str(), NULL, 0); } static void event_secondary_detect(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO,_("Event secondary detect")); if (cam->conf->on_secondary_detect != "") exec_command(cam, cam->conf->on_secondary_detect.c_str(), NULL, 0); } static void event_camera_found(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; if (cam->conf->on_camera_found != "") exec_command(cam, cam->conf->on_camera_found.c_str(), NULL, 0); } static void on_movie_end_command(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int filetype = (unsigned long) ftype; (void)evnt; (void)img_data; (void)ts1; if ((filetype & FTYPE_MPEG_ANY) && (cam->conf->on_movie_end != "")) exec_command(cam, cam->conf->on_movie_end.c_str(), fname, filetype); } static void event_extpipe_end(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; if (cam->extpipe_open) { cam->extpipe_open = 0; fflush(cam->extpipe); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO ,_("CLOSING: extpipe file desc %d, error state %d") ,fileno(cam->extpipe), ferror(cam->extpipe)); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "pclose return: %d", pclose(cam->extpipe)); event(cam, EVENT_FILECLOSE, NULL, cam->extpipefilename, (void *)FTYPE_MPEG, ts1); } } static void event_create_extpipe(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int retcd; char stamp[PATH_MAX] = ""; (void)evnt; (void)img_data; (void)fname; (void)ftype; if ((cam->conf->movie_extpipe_use) && (cam->conf->movie_extpipe != "" )) { mystrftime(cam, stamp, sizeof(stamp), cam->conf->movie_filename.c_str(), ts1, NULL, 0); retcd = snprintf(cam->extpipefilename, PATH_MAX - 4, "%s/%s" , cam->conf->target_dir.c_str(), stamp); if (retcd <0) MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, _("Error option")); if (access(cam->conf->target_dir.c_str(), W_OK)!= 0) { /* Permission denied */ if (errno == EACCES) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("no write access to target directory %s"), cam->conf->target_dir.c_str()); return ; /* Path not found - create it */ } else if (errno == ENOENT) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("path not found, trying to create it %s ..."), cam->conf->target_dir.c_str()); if (mycreate_path(cam->extpipefilename) == -1) return ; } else { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("error accesing path %s"), cam->conf->target_dir.c_str()); return ; } } /* Always create any path specified as file name */ if (mycreate_path(cam->extpipefilename) == -1) return ; mystrftime(cam, stamp, sizeof(stamp), cam->conf->movie_extpipe.c_str() , ts1, cam->extpipefilename, 0); retcd = snprintf(cam->extpipecmdline, PATH_MAX, "%s", stamp); if ((retcd < 0 ) || (retcd >= PATH_MAX)){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO , _("Error specifying command line: %s"), cam->extpipecmdline); return; } MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("pipe: %s"), cam->extpipecmdline); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "cam->moviefps: %d", cam->movie_fps); event(cam, EVENT_FILECREATE, NULL, cam->extpipefilename, (void *)FTYPE_MPEG, ts1); cam->extpipe = popen(cam->extpipecmdline, "we"); if (cam->extpipe == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, _("popen failed")); return; } setbuf(cam->extpipe, NULL); cam->extpipe_open = 1; } } static void event_extpipe_put(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int passthrough; (void)evnt; (void)fname; (void)ftype; (void)ts1; /* Check use_extpipe enabled and ext_pipe not NULL */ if ((cam->conf->movie_extpipe_use) && (cam->extpipe != NULL)) { MOTION_LOG(DBG, TYPE_EVENTS, NO_ERRNO, _("Using extpipe")); passthrough = mycheck_passthrough(cam); /* Check that is open */ if ((cam->extpipe_open) && (fileno(cam->extpipe) > 0)) { if ((cam->imgs.size_high > 0) && (!passthrough)){ if (!fwrite(img_data->image_high, cam->imgs.size_high, 1, cam->extpipe)) MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("Error writing in pipe , state error %d"), ferror(cam->extpipe)); } else { if (!fwrite(img_data->image_norm, cam->imgs.size_norm, 1, cam->extpipe)) MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO ,_("Error writing in pipe , state error %d"), ferror(cam->extpipe)); } } else { MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("pipe %s not created or closed already "), cam->extpipecmdline); } } } static void event_new_video(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; (void)ts1; cam->movie_last_shot = -1; cam->movie_fps = cam->lastrate; MOTION_LOG(INF, TYPE_EVENTS, NO_ERRNO, _("Source FPS %d"), cam->movie_fps); if (cam->movie_fps < 2) cam->movie_fps = 2; } static void event_movie_newfile(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int retcd; (void)evnt; (void)img_data; (void)fname; (void)ftype; if (!cam->conf->movie_output && !cam->conf->movie_output_motion) return; if (cam->conf->movie_output) { retcd = movie_init_norm(cam, ts1); if (retcd < 0){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error opening ctx_cam for movie output.")); free(cam->movie_norm); cam->movie_norm=NULL; return; } event(cam, EVENT_FILECREATE, NULL, cam->movie_norm->filename, (void *)FTYPE_MPEG, ts1); } if (cam->conf->movie_output_motion) { retcd = movie_init_motion(cam, ts1); if (retcd < 0){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error creating motion file [%s]"), cam->movie_motion->filename); free(cam->movie_motion); cam->movie_motion = NULL; return; } } } static void event_movie_timelapse(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { int retcd; (void)evnt; (void)fname; (void)ftype; if (!cam->movie_timelapse) { retcd = movie_init_timelapse(cam, ts1); if (retcd < 0){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO ,_("Error creating timelapse file [%s]"), cam->movie_timelapse->filename); free(cam->movie_timelapse); cam->movie_timelapse = NULL; return; } event(cam, EVENT_FILECREATE, NULL, cam->movie_timelapse->filename , (void *)FTYPE_MPEG_TIMELAPSE, ts1); } if (movie_put_image(cam->movie_timelapse, img_data, ts1) == -1) { MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, _("Error encoding image")); } } static void event_movie_put(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)fname; (void)ftype; if (cam->movie_norm) { if (movie_put_image(cam->movie_norm, img_data, ts1) == -1){ MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, _("Error encoding image")); } } if (cam->movie_motion) { if (movie_put_image(cam->movie_motion, &cam->imgs.image_motion, ts1) == -1) { MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, _("Error encoding image")); } } } static void event_movie_closefile(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; if (cam->movie_norm) { movie_close(cam->movie_norm); free(cam->movie_norm); cam->movie_norm = NULL; event(cam, EVENT_FILECLOSE, NULL, cam->newfilename, (void *)FTYPE_MPEG, ts1); } if (cam->movie_motion) { movie_close(cam->movie_motion); free(cam->movie_motion); cam->movie_motion = NULL; event(cam, EVENT_FILECLOSE, NULL, cam->motionfilename, (void *)FTYPE_MPEG_MOTION, ts1); } } static void event_movie_timelapseend(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname, void *ftype, struct timespec *ts1) { (void)evnt; (void)img_data; (void)fname; (void)ftype; if (cam->movie_timelapse) { movie_close(cam->movie_timelapse); free(cam->movie_timelapse); cam->movie_timelapse = NULL; event(cam, EVENT_FILECLOSE, NULL, cam->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, ts1); } } struct event_handlers { motion_event type; event_handler handler; }; struct event_handlers event_handlers[] = { { EVENT_FILECREATE, event_sqlnewfile }, { EVENT_FILECREATE, on_picture_save_command }, { EVENT_FILECREATE, event_newfile }, { EVENT_MOTION, event_beep }, { EVENT_MOTION, on_motion_detected_command }, { EVENT_AREA_DETECTED, on_area_command }, { EVENT_FIRSTMOTION, event_sqlfirstmotion }, { EVENT_FIRSTMOTION, on_event_start_command }, { EVENT_ENDMOTION, on_event_end_command }, { EVENT_IMAGE_DETECTED, event_image_detect }, { EVENT_IMAGEM_DETECTED, event_imagem_detect }, { EVENT_IMAGE_SNAPSHOT, event_image_snapshot }, { EVENT_IMAGE, event_vlp_putpipe }, { EVENT_IMAGEM, event_vlp_putpipe }, { EVENT_IMAGE_PREVIEW, event_image_preview }, { EVENT_STREAM, event_stream_put }, { EVENT_FIRSTMOTION, event_new_video }, { EVENT_FIRSTMOTION, event_movie_newfile }, { EVENT_IMAGE_DETECTED, event_movie_put }, { EVENT_MOVIE_PUT, event_movie_put }, { EVENT_ENDMOTION, event_movie_closefile }, { EVENT_TIMELAPSE, event_movie_timelapse }, { EVENT_TIMELAPSEEND, event_movie_timelapseend }, { EVENT_FILECLOSE, event_sqlfileclose }, { EVENT_FILECLOSE, on_movie_end_command }, { EVENT_FIRSTMOTION, event_create_extpipe }, { EVENT_IMAGE_DETECTED, event_extpipe_put }, { EVENT_MOVIE_PUT, event_extpipe_put }, { EVENT_ENDMOTION, event_extpipe_end }, { EVENT_CAMERA_LOST, event_camera_lost }, { EVENT_CAMERA_FOUND, event_camera_found }, { EVENT_SECDETECT, event_secondary_detect }, {(motion_event)0, NULL} }; /** * event * defined with the following parameters: * - Type as defined in event.h (EVENT_...) * - The global context struct cam * - img_data - A pointer to a ctx_image_data used for images * - filename - A pointer to typically a string for a file path * - eventdata - A void pointer that can be cast to anything. E.g. FTYPE_... * - tm - A tm struct that carries a full time structure * The split between unsigned images and signed filenames was introduced in 3.2.2 * as a code reading friendly solution to avoid a stream of compiler warnings in gcc 4.0. */ void event(struct ctx_cam *cam, motion_event evnt ,struct ctx_image_data *img_data, char *fname,void *ftype, struct timespec *ts1) { int i=-1; while (event_handlers[++i].handler) { if (evnt == event_handlers[i].type) event_handlers[i].handler(cam, evnt, img_data, fname, ftype, ts1); } }