"Fossies" - the Fresh Open Source Software Archive

Member "motion-Release-4.3.0/src/event.c" (14 Jan 2020, 52903 Bytes) of package /linux/misc/motion-Release-4.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "event.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2     event.c
    3 
    4     Generalised event handling for motion
    5 
    6     Copyright Jeroen Vreeken, 2002
    7     This software is distributed under the GNU Public License Version 2
    8     see also the file 'COPYING'.
    9 */
   10 #include "picture.h"   /* already includes motion.h */
   11 #include "translate.h"
   12 #include "netcam_rtsp.h"
   13 #include "ffmpeg.h"
   14 #include "event.h"
   15 #include "video_loopback.h"
   16 #include "video_common.h"
   17 
   18 /* Various functions (most doing the actual action)
   19  * TODO Items:
   20  * Rework the snprintf uses.
   21  * Edit directories so they can never be null and eliminate defaults from here
   22  * Move the ffmpeg initialize stuff to ffmpeg module
   23  * eliminate #if for v4l2
   24  * Eliminate #IF for database items
   25  * Move database functions out of here.
   26  * Move stream stuff to webu_stream
   27  * Use (void) alternative for ATTRIBUTE_UNUSED
   28  */
   29 
   30 const char *eventList[] = {
   31     "NULL",
   32     "EVENT_FILECREATE",
   33     "EVENT_MOTION",
   34     "EVENT_FIRSTMOTION",
   35     "EVENT_ENDMOTION",
   36     "EVENT_STOP",
   37     "EVENT_TIMELAPSE",
   38     "EVENT_TIMELAPSEEND",
   39     "EVENT_STREAM",
   40     "EVENT_IMAGE_DETECTED",
   41     "EVENT_IMAGEM_DETECTED",
   42     "EVENT_IMAGE_SNAPSHOT",
   43     "EVENT_IMAGE",
   44     "EVENT_IMAGEM",
   45     "EVENT_IMAGE_PREVIEW",
   46     "EVENT_FILECLOSE",
   47     "EVENT_DEBUG",
   48     "EVENT_CRITICAL",
   49     "EVENT_AREA_DETECTED",
   50     "EVENT_CAMERA_LOST",
   51     "EVENT_CAMERA_FOUND",
   52     "EVENT_FFMPEG_PUT",
   53     "EVENT_LAST"
   54 };
   55 
   56 /**
   57  * eventToString
   58  *
   59  * returns string label of the event
   60  */
   61  /**
   62  * Future use debug / notification function
   63 static const char *eventToString(motion_event e)
   64 {
   65     return eventList[(int)e];
   66 }
   67 */
   68 
   69 /**
   70  * exec_command
   71  *      Execute 'command' with 'arg' as its argument.
   72  *      if !arg command is started with no arguments
   73  *      Before we call execl we need to close all the file handles
   74  *      that the fork inherited from the parent in order not to pass
   75  *      the open handles on to the shell
   76  */
   77 static void exec_command(struct context *cnt, char *command, char *filename, int filetype)
   78 {
   79     char stamp[PATH_MAX];
   80     mystrftime(cnt, stamp, sizeof(stamp), command, &cnt->current_image->timestamp_tv, filename, filetype);
   81 
   82     if (!fork()) {
   83         int i;
   84 
   85         /* Detach from parent */
   86         setsid();
   87 
   88         /*
   89          * Close any file descriptor except console because we will
   90          * like to see error messages
   91          */
   92         for (i = getdtablesize() - 1; i > 2; i--)
   93             close(i);
   94 
   95         execl("/bin/sh", "sh", "-c", stamp, " &", NULL);
   96 
   97         /* if above function succeeds the program never reach here */
   98         MOTION_LOG(ALR, TYPE_EVENTS, SHOW_ERRNO
   99             ,_("Unable to start external command '%s'"), stamp);
  100 
  101         exit(1);
  102     }
  103 
  104     MOTION_LOG(DBG, TYPE_EVENTS, NO_ERRNO
  105         ,_("Executing external command '%s'"), stamp);
  106 }
  107 
  108 /*
  109  * Event handlers
  110  */
  111 
  112 static void event_newfile(struct context *cnt ATTRIBUTE_UNUSED,
  113             motion_event type ATTRIBUTE_UNUSED,
  114             struct image_data *dummy ATTRIBUTE_UNUSED, char *filename, void *ftype,
  115             struct timeval *tv1 ATTRIBUTE_UNUSED)
  116 {
  117     MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO
  118         ,_("File of type %ld saved to: %s")
  119         ,(unsigned long)ftype, filename);
  120 }
  121 
  122 
  123 static void event_beep(struct context *cnt, motion_event type ATTRIBUTE_UNUSED,
  124             struct image_data *dummy ATTRIBUTE_UNUSED,
  125             char *filename ATTRIBUTE_UNUSED,
  126             void *ftype ATTRIBUTE_UNUSED,
  127             struct timeval *tv1 ATTRIBUTE_UNUSED)
  128 {
  129     if (!cnt->conf.quiet)
  130         printf("\a");
  131 }
  132 
  133 /**
  134  * on_picture_save_command
  135  *      handles both on_picture_save and on_movie_start
  136  *      If arg = FTYPE_IMAGE_ANY on_picture_save script is executed
  137  *      If arg = FTYPE_MPEG_ANY on_movie_start script is executed
  138  *      The scripts are executed with the filename of picture or movie appended
  139  *      to the config parameter.
  140  */
  141 static void on_picture_save_command(struct context *cnt,
  142             motion_event type ATTRIBUTE_UNUSED,
  143             struct image_data *dummy ATTRIBUTE_UNUSED,
  144             char *filename, void *arg, struct timeval *tv1 ATTRIBUTE_UNUSED)
  145 {
  146     int filetype = (unsigned long)arg;
  147 
  148     if ((filetype & FTYPE_IMAGE_ANY) != 0 && cnt->conf.on_picture_save)
  149         exec_command(cnt, cnt->conf.on_picture_save, filename, filetype);
  150 
  151     if ((filetype & FTYPE_MPEG_ANY) != 0 && cnt->conf.on_movie_start)
  152         exec_command(cnt, cnt->conf.on_movie_start, filename, filetype);
  153 }
  154 
  155 static void on_motion_detected_command(struct context *cnt,
  156             motion_event type ATTRIBUTE_UNUSED,
  157             struct image_data *dummy1 ATTRIBUTE_UNUSED,
  158             char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
  159             struct timeval *tv1 ATTRIBUTE_UNUSED)
  160 {
  161     if (cnt->conf.on_motion_detected)
  162         exec_command(cnt, cnt->conf.on_motion_detected, NULL, 0);
  163 }
  164 
  165 #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) || defined(HAVE_MARIADB)
  166 
  167 static void do_sql_query(char *sqlquery, struct context *cnt, int save_id)
  168 {
  169 
  170     if (strlen(sqlquery) <= 0) {
  171         /* don't try to execute empty queries */
  172         MOTION_LOG(WRN, TYPE_DB, NO_ERRNO, _("Ignoring empty sql query"));
  173         return;
  174     }
  175 
  176 #if defined(HAVE_MYSQL) || defined(HAVE_MARIADB)
  177     if (!strcmp(cnt->conf.database_type, "mysql")) {
  178         MOTION_LOG(DBG, TYPE_DB, NO_ERRNO, _("Executing mysql query"));
  179         if (mysql_query(cnt->database, sqlquery) != 0) {
  180             int error_code = mysql_errno(cnt->database);
  181 
  182             MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO
  183                 ,_("Mysql query failed %s error code %d")
  184                 ,mysql_error(cnt->database), error_code);
  185             /* Try to reconnect ONCE if fails continue and discard this sql query */
  186             if (error_code >= 2000) {
  187                 // Close connection before start a new connection
  188                 mysql_close(cnt->database);
  189 
  190                 cnt->database = (MYSQL *) mymalloc(sizeof(MYSQL));
  191                 mysql_init(cnt->database);
  192 
  193                 if (!mysql_real_connect(cnt->database, cnt->conf.database_host,
  194                                         cnt->conf.database_user, cnt->conf.database_password,
  195                                         cnt->conf.database_dbname, 0, NULL, 0)) {
  196                     MOTION_LOG(ALR, TYPE_DB, NO_ERRNO
  197                         ,_("Cannot reconnect to MySQL"
  198                         " database %s on host %s with user %s MySQL error was %s"),
  199                         cnt->conf.database_dbname,
  200                         cnt->conf.database_host, cnt->conf.database_user,
  201                         mysql_error(cnt->database));
  202                 } else {
  203                     MOTION_LOG(INF, TYPE_DB, NO_ERRNO
  204                         ,_("Re-Connection to Mysql database '%s' Succeed")
  205                         ,cnt->conf.database_dbname);
  206                     if (mysql_query(cnt->database, sqlquery) != 0) {
  207                         int error_my = mysql_errno(cnt->database);
  208                         MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO
  209                             ,_("after re-connection Mysql query failed %s error code %d")
  210                             ,mysql_error(cnt->database), error_my);
  211                     }
  212                 }
  213             }
  214         }
  215         if (save_id) {
  216             cnt->database_event_id = (unsigned long long) mysql_insert_id(cnt->database);
  217         }
  218     }
  219 #endif /* HAVE_MYSQL HAVE_MARIADB*/
  220 
  221 
  222 #ifdef HAVE_PGSQL
  223     if (!strcmp(cnt->conf.database_type, "postgresql")) {
  224         MOTION_LOG(DBG, TYPE_DB, NO_ERRNO, _("Executing postgresql query"));
  225         PGresult *res;
  226 
  227         res = PQexec(cnt->database_pg, sqlquery);
  228 
  229         if (PQstatus(cnt->database_pg) == CONNECTION_BAD) {
  230 
  231             MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
  232                 ,_("Connection to PostgreSQL database '%s' failed: %s")
  233                 ,cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg));
  234 
  235         // This function will close the connection to the server and attempt to reestablish a new connection to the same server,
  236         // using all the same parameters previously used. This may be useful for error recovery if a working connection is lost
  237             PQreset(cnt->database_pg);
  238 
  239             if (PQstatus(cnt->database_pg) == CONNECTION_BAD) {
  240                 MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
  241                     ,_("Re-Connection to PostgreSQL database '%s' failed: %s")
  242                     ,cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg));
  243             } else {
  244                 MOTION_LOG(INF, TYPE_DB, NO_ERRNO
  245                     ,_("Re-Connection to PostgreSQL database '%s' Succeed")
  246                     ,cnt->conf.database_dbname);
  247             }
  248 
  249         } else if (!(PQresultStatus(res) == PGRES_COMMAND_OK || PQresultStatus(res) == PGRES_TUPLES_OK)) {
  250             MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO, _("PGSQL query failed: [%s]  %s %s"),
  251                        sqlquery, PQresStatus(PQresultStatus(res)), PQresultErrorMessage(res));
  252         }
  253         if (save_id) {
  254             //ToDO:  Find the equivalent option for pgsql
  255             cnt->database_event_id = 0;
  256         }
  257 
  258         PQclear(res);
  259     }
  260 #endif /* HAVE_PGSQL */
  261 
  262 #ifdef HAVE_SQLITE3
  263     if ((!strcmp(cnt->conf.database_type, "sqlite3")) && (cnt->conf.database_dbname)) {
  264         int res;
  265         char *errmsg = 0;
  266         MOTION_LOG(DBG, TYPE_DB, NO_ERRNO, _("Executing sqlite query"));
  267         res = sqlite3_exec(cnt->database_sqlite3, sqlquery, NULL, 0, &errmsg);
  268         if (res != SQLITE_OK ) {
  269             MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, _("SQLite error was %s"), errmsg);
  270             sqlite3_free(errmsg);
  271         }
  272         if (save_id) {
  273             //ToDO:  Find the equivalent option for sqlite3
  274             cnt->database_event_id = 0;
  275         }
  276 
  277     }
  278 #endif /* HAVE_SQLITE3 */
  279 }
  280 
  281 static void event_sqlfirstmotion(struct context *cnt, motion_event type  ATTRIBUTE_UNUSED,
  282                                  struct image_data *dummy1 ATTRIBUTE_UNUSED,
  283                                  char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
  284                                  struct timeval *tv1 ATTRIBUTE_UNUSED)
  285 {
  286     /* Only log the file types we want */
  287     if (!(cnt->conf.database_type)) {
  288         return;
  289     }
  290 
  291     /*
  292      * We place the code in a block so we only spend time making space in memory
  293      * for the sqlquery and timestr when we actually need it.
  294      */
  295     {
  296         char sqlquery[PATH_MAX];
  297 
  298         mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query_start,
  299                    &cnt->current_image->timestamp_tv, NULL, 0);
  300 
  301         do_sql_query(sqlquery, cnt, 1);
  302     }
  303 }
  304 
  305 static void event_sqlnewfile(struct context *cnt, motion_event type  ATTRIBUTE_UNUSED,
  306             struct image_data *dummy ATTRIBUTE_UNUSED,
  307             char *filename, void *arg, struct timeval *currenttime_tv)
  308 {
  309     int sqltype = (unsigned long)arg;
  310 
  311     /* Only log the file types we want */
  312     if (!(cnt->conf.database_type) || (sqltype & cnt->sql_mask) == 0)
  313         return;
  314 
  315     /*
  316      * We place the code in a block so we only spend time making space in memory
  317      * for the sqlquery and timestr when we actually need it.
  318      */
  319     {
  320         char sqlquery[PATH_MAX];
  321 
  322         mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query,
  323                    currenttime_tv, filename, sqltype);
  324 
  325         do_sql_query(sqlquery, cnt, 0);
  326     }
  327 }
  328 
  329 static void event_sqlfileclose(struct context *cnt, motion_event type  ATTRIBUTE_UNUSED,
  330             struct image_data *dummy ATTRIBUTE_UNUSED,
  331             char *filename, void *arg, struct timeval *currenttime_tv)
  332 {
  333     int sqltype = (unsigned long)arg;
  334 
  335     /* Only log the file types we want */
  336     if (!(cnt->conf.database_type) || (sqltype & cnt->sql_mask) == 0)
  337         return;
  338 
  339     /*
  340      * We place the code in a block so we only spend time making space in memory
  341      * for the sqlquery and timestr when we actually need it.
  342      */
  343     {
  344         char sqlquery[PATH_MAX];
  345 
  346         mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query_stop,
  347                    currenttime_tv, filename, sqltype);
  348 
  349         do_sql_query(sqlquery, cnt, 0);
  350     }
  351 }
  352 
  353 #endif /* defined HAVE_MYSQL || defined HAVE_PGSQL || defined(HAVE_SQLITE3) || defined(HAVE_MARIADB) */
  354 
  355 static void on_area_command(struct context *cnt,
  356             motion_event type ATTRIBUTE_UNUSED,
  357             struct image_data *dummy1 ATTRIBUTE_UNUSED,
  358             char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
  359             struct timeval *tv1 ATTRIBUTE_UNUSED)
  360 {
  361     if (cnt->conf.on_area_detected)
  362         exec_command(cnt, cnt->conf.on_area_detected, NULL, 0);
  363 }
  364 
  365 static void on_event_start_command(struct context *cnt,
  366             motion_event type ATTRIBUTE_UNUSED,
  367             struct image_data *dummy1 ATTRIBUTE_UNUSED,
  368             char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
  369             struct timeval *tv1 ATTRIBUTE_UNUSED)
  370 {
  371     if (cnt->conf.on_event_start)
  372         exec_command(cnt, cnt->conf.on_event_start, NULL, 0);
  373 }
  374 
  375 static void on_event_end_command(struct context *cnt,
  376             motion_event type ATTRIBUTE_UNUSED,
  377             struct image_data *dummy1 ATTRIBUTE_UNUSED,
  378             char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
  379             struct timeval *tv1 ATTRIBUTE_UNUSED)
  380 {
  381     if (cnt->conf.on_event_end)
  382         exec_command(cnt, cnt->conf.on_event_end, NULL, 0);
  383 }
  384 
  385 static void event_stream_put(struct context *cnt,
  386             motion_event type ATTRIBUTE_UNUSED,
  387             struct image_data *img_data, char *dummy1 ATTRIBUTE_UNUSED,
  388             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *tv1 ATTRIBUTE_UNUSED)
  389 {
  390     int subsize;
  391 
  392     if (cnt->conf.stream_preview_method == 99){
  393         if (cnt->conf.stream_port)
  394             stream_put(cnt, &cnt->stream, &cnt->stream_count, img_data->image_norm, 0);
  395     } else {
  396         pthread_mutex_lock(&cnt->mutex_stream);
  397             /* Normal stream processing */
  398             if (cnt->stream_norm.cnct_count > 0){
  399                 if (cnt->stream_norm.jpeg_data == NULL){
  400                     cnt->stream_norm.jpeg_data = mymalloc(cnt->imgs.size_norm);
  401                 }
  402                 if (img_data->image_norm != NULL){
  403                     cnt->stream_norm.jpeg_size = put_picture_memory(cnt
  404                         ,cnt->stream_norm.jpeg_data
  405                         ,cnt->imgs.size_norm
  406                         ,img_data->image_norm
  407                         ,cnt->conf.stream_quality
  408                         ,cnt->imgs.width
  409                         ,cnt->imgs.height);
  410                 }
  411             }
  412 
  413             /* Substream processing */
  414             if (cnt->stream_sub.cnct_count > 0){
  415                 if (cnt->stream_sub.jpeg_data == NULL){
  416                     cnt->stream_sub.jpeg_data = mymalloc(cnt->imgs.size_norm);
  417                 }
  418                 if (img_data->image_norm != NULL){
  419                     /* Resulting substream image must be multiple of 8 */
  420                     if (((cnt->imgs.width  % 16) == 0)  &&
  421                         ((cnt->imgs.height % 16) == 0)) {
  422 
  423                         subsize = ((cnt->imgs.width / 2) * (cnt->imgs.height / 2) * 3 / 2);
  424                         if (cnt->imgs.substream_image == NULL){
  425                             cnt->imgs.substream_image = mymalloc(subsize);
  426                         }
  427                         pic_scale_img(cnt->imgs.width
  428                             ,cnt->imgs.height
  429                             ,img_data->image_norm
  430                             ,cnt->imgs.substream_image);
  431                         cnt->stream_sub.jpeg_size = put_picture_memory(cnt
  432                             ,cnt->stream_sub.jpeg_data
  433                             ,subsize
  434                             ,cnt->imgs.substream_image
  435                             ,cnt->conf.stream_quality
  436                             ,(cnt->imgs.width / 2)
  437                             ,(cnt->imgs.height / 2));
  438                     } else {
  439                         /* Substream was not multiple of 8 so send full image*/
  440                         cnt->stream_sub.jpeg_size = put_picture_memory(cnt
  441                             ,cnt->stream_sub.jpeg_data
  442                             ,cnt->imgs.size_norm
  443                             ,img_data->image_norm
  444                             ,cnt->conf.stream_quality
  445                             ,cnt->imgs.width
  446                             ,cnt->imgs.height);
  447                     }
  448                 }
  449             }
  450 
  451             /* Motion stream processing */
  452             if (cnt->stream_motion.cnct_count > 0){
  453                 if (cnt->stream_motion.jpeg_data == NULL){
  454                     cnt->stream_motion.jpeg_data = mymalloc(cnt->imgs.size_norm);
  455                 }
  456                 if (cnt->imgs.img_motion.image_norm != NULL){
  457                     cnt->stream_motion.jpeg_size = put_picture_memory(cnt
  458                         ,cnt->stream_motion.jpeg_data
  459                         ,cnt->imgs.size_norm
  460                         ,cnt->imgs.img_motion.image_norm
  461                         ,cnt->conf.stream_quality
  462                         ,cnt->imgs.width
  463                         ,cnt->imgs.height);
  464                 }
  465             }
  466 
  467             /* Source stream processing */
  468             if (cnt->stream_source.cnct_count > 0){
  469                 if (cnt->stream_source.jpeg_data == NULL){
  470                     cnt->stream_source.jpeg_data = mymalloc(cnt->imgs.size_norm);
  471                 }
  472                 if (cnt->imgs.image_virgin.image_norm != NULL){
  473                     cnt->stream_source.jpeg_size = put_picture_memory(cnt
  474                         ,cnt->stream_source.jpeg_data
  475                         ,cnt->imgs.size_norm
  476                         ,cnt->imgs.image_virgin.image_norm
  477                         ,cnt->conf.stream_quality
  478                         ,cnt->imgs.width
  479                         ,cnt->imgs.height);
  480                 }
  481             }
  482         pthread_mutex_unlock(&cnt->mutex_stream);
  483     }
  484 }
  485 
  486 
  487 #if defined(HAVE_V4L2) && !defined(BSD)
  488 static void event_vlp_putpipe(struct context *cnt,
  489             motion_event type ATTRIBUTE_UNUSED,
  490             struct image_data *img_data, char *dummy ATTRIBUTE_UNUSED, void *devpipe,
  491             struct timeval *tv1 ATTRIBUTE_UNUSED)
  492 {
  493     if (*(int *)devpipe >= 0) {
  494         if (vlp_putpipe(*(int *)devpipe, img_data->image_norm, cnt->imgs.size_norm) == -1)
  495             MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  496                 ,_("Failed to put image into video pipe"));
  497     }
  498 }
  499 #endif /* defined(HAVE_V4L2) && !defined(BSD)  */
  500 
  501 const char *imageext(struct context *cnt)
  502 {
  503     if (cnt->imgs.picture_type == IMAGE_TYPE_PPM)
  504         return "ppm";
  505 
  506     if (cnt->imgs.picture_type == IMAGE_TYPE_WEBP)
  507         return "webp";
  508 
  509     return "jpg";
  510 }
  511 
  512 static void event_image_detect(struct context *cnt,
  513         motion_event type ATTRIBUTE_UNUSED,
  514         struct image_data *img_data, char *dummy1 ATTRIBUTE_UNUSED,
  515         void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
  516 {
  517     char fullfilename[PATH_MAX];
  518     char filename[PATH_MAX];
  519     int  passthrough;
  520 
  521     if (cnt->new_img & NEWIMG_ON) {
  522         const char *imagepath;
  523 
  524         /*
  525          *  conf.imagepath would normally be defined but if someone deleted it by control interface
  526          *  it is better to revert to the default than fail
  527          */
  528         if (cnt->conf.picture_filename)
  529             imagepath = cnt->conf.picture_filename;
  530         else
  531             imagepath = DEF_IMAGEPATH;
  532 
  533         mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tv, NULL, 0);
  534         snprintf(fullfilename, PATH_MAX, "%.*s/%.*s.%s"
  535             , (int)(PATH_MAX-2-strlen(filename)-strlen(imageext(cnt)))
  536             , cnt->conf.target_dir
  537             , (int)(PATH_MAX-2-strlen(cnt->conf.target_dir)-strlen(imageext(cnt)))
  538             , filename, imageext(cnt));
  539 
  540         passthrough = util_check_passthrough(cnt);
  541         if ((cnt->imgs.size_high > 0) && (!passthrough)) {
  542             put_picture(cnt, fullfilename,img_data->image_high, FTYPE_IMAGE);
  543         } else {
  544             put_picture(cnt, fullfilename,img_data->image_norm, FTYPE_IMAGE);
  545         }
  546         event(cnt, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE, currenttime_tv);
  547     }
  548 }
  549 
  550 static void event_imagem_detect(struct context *cnt,
  551             motion_event type ATTRIBUTE_UNUSED,
  552             struct image_data *img_data ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  553             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
  554 {
  555     struct config *conf = &cnt->conf;
  556     char fullfilenamem[PATH_MAX];
  557     char filename[PATH_MAX];
  558     char filenamem[PATH_MAX];
  559 
  560     if (conf->picture_output_motion) {
  561         const char *imagepath;
  562 
  563         /*
  564          *  conf.picture_filename would normally be defined but if someone deleted it by control interface
  565          *  it is better to revert to the default than fail
  566          */
  567         if (cnt->conf.picture_filename)
  568             imagepath = cnt->conf.picture_filename;
  569         else
  570             imagepath = DEF_IMAGEPATH;
  571 
  572         mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tv, NULL, 0);
  573 
  574         /* motion images gets same name as normal images plus an appended 'm' */
  575         snprintf(filenamem, PATH_MAX, "%.*sm"
  576             , (int)(PATH_MAX-1-strlen(filename))
  577             , filename);
  578         snprintf(fullfilenamem, PATH_MAX, "%.*s/%.*s.%s"
  579             , (int)(PATH_MAX-2-strlen(filenamem)-strlen(imageext(cnt)))
  580             , cnt->conf.target_dir
  581             , (int)(PATH_MAX-2-strlen(cnt->conf.target_dir)-strlen(imageext(cnt)))
  582             , filenamem, imageext(cnt));
  583         put_picture(cnt, fullfilenamem, cnt->imgs.img_motion.image_norm, FTYPE_IMAGE_MOTION);
  584         event(cnt, EVENT_FILECREATE, NULL, fullfilenamem, (void *)FTYPE_IMAGE, currenttime_tv);
  585     }
  586 }
  587 
  588 static void event_image_snapshot(struct context *cnt,
  589             motion_event type ATTRIBUTE_UNUSED,
  590             struct image_data *img_data, char *dummy1 ATTRIBUTE_UNUSED,
  591             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
  592 {
  593     char fullfilename[PATH_MAX];
  594     char filename[PATH_MAX];
  595     char filepath[PATH_MAX];
  596     int offset = 0;
  597     int len = strlen(cnt->conf.snapshot_filename);
  598 
  599     if (len >= 9)
  600         offset = len - 8;
  601 
  602     if (strcmp(cnt->conf.snapshot_filename+offset, "lastsnap")) {
  603         char linkpath[PATH_MAX];
  604         const char *snappath;
  605         /*
  606          *  conf.snapshot_filename would normally be defined but if someone deleted it by control interface
  607          *  it is better to revert to the default than fail
  608          */
  609         if (cnt->conf.snapshot_filename)
  610             snappath = cnt->conf.snapshot_filename;
  611         else
  612             snappath = DEF_SNAPPATH;
  613 
  614         mystrftime(cnt, filepath, sizeof(filepath), snappath, currenttime_tv, NULL, 0);
  615         snprintf(filename, PATH_MAX, "%.*s.%s"
  616             , (int)(PATH_MAX-1-strlen(filepath)-strlen(imageext(cnt)))
  617             , filepath, imageext(cnt));
  618         snprintf(fullfilename, PATH_MAX, "%.*s/%.*s"
  619             , (int)(PATH_MAX-1-strlen(filename))
  620             , cnt->conf.target_dir
  621             , (int)(PATH_MAX-1-strlen(cnt->conf.target_dir))
  622             , filename);
  623         put_picture(cnt, fullfilename, img_data->image_norm, FTYPE_IMAGE_SNAPSHOT);
  624         event(cnt, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE_SNAPSHOT, currenttime_tv);
  625 
  626         /*
  627          *  Update symbolic link *after* image has been written so that
  628          *  the link always points to a valid file.
  629          */
  630         snprintf(linkpath, PATH_MAX, "%.*s/lastsnap.%s"
  631             , (int)(PATH_MAX-strlen("/lastsnap.")-strlen(imageext(cnt)))
  632             , cnt->conf.target_dir, imageext(cnt));
  633 
  634         remove(linkpath);
  635 
  636         if (symlink(filename, linkpath)) {
  637             MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  638                 ,_("Could not create symbolic link [%s]"), filename);
  639             return;
  640         }
  641     } else {
  642         mystrftime(cnt, filepath, sizeof(filepath), cnt->conf.snapshot_filename, currenttime_tv, NULL, 0);
  643         snprintf(filename, PATH_MAX, "%.*s.%s"
  644             , (int)(PATH_MAX-1-strlen(imageext(cnt)))
  645             , filepath, imageext(cnt));
  646         snprintf(fullfilename, PATH_MAX, "%.*s/%.*s"
  647             , (int)(PATH_MAX-1-strlen(filename))
  648             , cnt->conf.target_dir
  649             , (int)(PATH_MAX-1-strlen(cnt->conf.target_dir))
  650             , filename);
  651         remove(fullfilename);
  652         put_picture(cnt, fullfilename, img_data->image_norm, FTYPE_IMAGE_SNAPSHOT);
  653         event(cnt, EVENT_FILECREATE, NULL, fullfilename, (void *)FTYPE_IMAGE_SNAPSHOT, currenttime_tv);
  654     }
  655 
  656     cnt->snapshot = 0;
  657 }
  658 
  659 /**
  660  * event_image_preview
  661  *      event_image_preview
  662  *
  663  * Returns nothing.
  664  */
  665 static void event_image_preview(struct context *cnt,
  666             motion_event type ATTRIBUTE_UNUSED,
  667             struct image_data *img_data ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  668             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
  669 {
  670     int use_imagepath;
  671     const char *imagepath;
  672     char previewname[PATH_MAX];
  673     char filename[PATH_MAX];
  674     struct image_data *saved_current_image;
  675     int passthrough, retcd;
  676 
  677     if (cnt->imgs.preview_image.diffs) {
  678         saved_current_image = cnt->current_image;
  679         cnt->current_image = &cnt->imgs.preview_image;
  680 
  681         /* Use filename of movie i.o. jpeg_filename when set to 'preview'. */
  682         use_imagepath = strcmp(cnt->conf.picture_filename, "preview");
  683 
  684         if ((cnt->ffmpeg_output || (cnt->conf.movie_extpipe_use && cnt->extpipe)) && !use_imagepath) {
  685 
  686             if (cnt->conf.movie_extpipe_use && cnt->extpipe) {
  687                 retcd = snprintf(previewname, PATH_MAX,"%s.%s"
  688                     , cnt->extpipefilename, imageext(cnt));
  689                 if ((retcd < 0) || (retcd >= PATH_MAX)) {
  690                     MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
  691                         ,_("Error creating preview pipe name %d %s")
  692                         ,retcd, previewname);
  693                     return;
  694                 }
  695             } else {
  696                 /* Replace avi/mpg with jpg/ppm and keep the rest of the filename. */
  697                 /* TODO:  Hope that extensions are always 3 bytes*/
  698                 /* -2 to allow for null terminating byte*/
  699                 retcd = snprintf(filename, strlen(cnt->newfilename) - 2
  700                     ,"%s", cnt->newfilename);
  701                 if (retcd < 0) {
  702                     MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
  703                         ,_("Error creating file name base %d %s")
  704                         ,retcd, filename);
  705                     return;
  706                 }
  707                 retcd = snprintf(previewname, PATH_MAX
  708                     ,"%s%s", filename, imageext(cnt));
  709                 if ((retcd < 0) || (retcd >= PATH_MAX)) {
  710                     MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
  711                         ,_("Error creating preview name %d %s")
  712                         , retcd, previewname);
  713                     return;
  714                 }
  715             }
  716 
  717             passthrough = util_check_passthrough(cnt);
  718             if ((cnt->imgs.size_high > 0) && (!passthrough)) {
  719                 put_picture(cnt, previewname, cnt->imgs.preview_image.image_high , FTYPE_IMAGE);
  720             } else {
  721                 put_picture(cnt, previewname, cnt->imgs.preview_image.image_norm , FTYPE_IMAGE);
  722             }
  723             event(cnt, EVENT_FILECREATE, NULL, previewname, (void *)FTYPE_IMAGE, currenttime_tv);
  724         } else {
  725             /*
  726              * Save best preview-shot also when no movies are recorded or imagepath
  727              * is used. Filename has to be generated - nothing available to reuse!
  728              */
  729 
  730             /*
  731              * conf.picture_filename would normally be defined but if someone deleted it by
  732              * control interface it is better to revert to the default than fail.
  733              */
  734             if (cnt->conf.picture_filename)
  735                 imagepath = cnt->conf.picture_filename;
  736             else
  737                 imagepath = (char *)DEF_IMAGEPATH;
  738 
  739             mystrftime(cnt, filename, sizeof(filename), imagepath, &cnt->imgs.preview_image.timestamp_tv, NULL, 0);
  740             snprintf(previewname, PATH_MAX, "%.*s/%.*s.%s"
  741                 , (int)(PATH_MAX-2-strlen(filename)-strlen(imageext(cnt)))
  742                 , cnt->conf.target_dir
  743                 , (int)(PATH_MAX-2-strlen(cnt->conf.target_dir)-strlen(imageext(cnt)))
  744                 , filename, imageext(cnt));
  745 
  746             passthrough = util_check_passthrough(cnt);
  747             if ((cnt->imgs.size_high > 0) && (!passthrough)) {
  748                 put_picture(cnt, previewname, cnt->imgs.preview_image.image_high , FTYPE_IMAGE);
  749             } else {
  750                 put_picture(cnt, previewname, cnt->imgs.preview_image.image_norm, FTYPE_IMAGE);
  751             }
  752             event(cnt, EVENT_FILECREATE, NULL, previewname, (void *)FTYPE_IMAGE, currenttime_tv);
  753         }
  754 
  755         /* Restore global context values. */
  756         cnt->current_image = saved_current_image;
  757     }
  758 }
  759 
  760 
  761 static void event_camera_lost(struct context *cnt,
  762             motion_event type ATTRIBUTE_UNUSED,
  763             struct image_data *img_data ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  764             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *tv1 ATTRIBUTE_UNUSED)
  765 {
  766     if (cnt->conf.on_camera_lost)
  767         exec_command(cnt, cnt->conf.on_camera_lost, NULL, 0);
  768 }
  769 
  770 static void event_camera_found(struct context *cnt,
  771             motion_event type ATTRIBUTE_UNUSED,
  772             struct image_data *img_data ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  773             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *tv1 ATTRIBUTE_UNUSED)
  774 {
  775     if (cnt->conf.on_camera_found)
  776         exec_command(cnt, cnt->conf.on_camera_found, NULL, 0);
  777 }
  778 
  779 static void on_movie_end_command(struct context *cnt,
  780                                  motion_event type ATTRIBUTE_UNUSED,
  781                                  struct image_data *dummy ATTRIBUTE_UNUSED, char *filename,
  782                                  void *arg, struct timeval *tv1 ATTRIBUTE_UNUSED)
  783 {
  784     int filetype = (unsigned long) arg;
  785 
  786     if ((filetype & FTYPE_MPEG_ANY) && cnt->conf.on_movie_end)
  787         exec_command(cnt, cnt->conf.on_movie_end, filename, filetype);
  788 }
  789 
  790 static void event_extpipe_end(struct context *cnt,
  791             motion_event type ATTRIBUTE_UNUSED,
  792             struct image_data *dummy ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  793             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
  794 {
  795     if (cnt->extpipe_open) {
  796         cnt->extpipe_open = 0;
  797         fflush(cnt->extpipe);
  798         MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO
  799             ,_("CLOSING: extpipe file desc %d, error state %d")
  800             ,fileno(cnt->extpipe), ferror(cnt->extpipe));
  801         MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("pclose return: %d"),
  802                    pclose(cnt->extpipe));
  803         event(cnt, EVENT_FILECLOSE, NULL, cnt->extpipefilename, (void *)FTYPE_MPEG, currenttime_tv);
  804     }
  805 }
  806 
  807 static void event_create_extpipe(struct context *cnt,
  808             motion_event type ATTRIBUTE_UNUSED,
  809             struct image_data *dummy ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  810             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
  811 {
  812     int retcd;
  813 
  814     if ((cnt->conf.movie_extpipe_use) && (cnt->conf.movie_extpipe)) {
  815         char stamp[PATH_MAX] = "";
  816         const char *moviepath;
  817 
  818         /*
  819          *  conf.mpegpath would normally be defined but if someone deleted it by control interface
  820          *  it is better to revert to the default than fail
  821          */
  822         if (cnt->conf.movie_filename) {
  823             moviepath = cnt->conf.movie_filename;
  824         } else {
  825             moviepath = DEF_MOVIEPATH;
  826             MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("moviepath: %s"), moviepath);
  827         }
  828 
  829         mystrftime(cnt, stamp, sizeof(stamp), moviepath, currenttime_tv, NULL, 0);
  830         snprintf(cnt->extpipefilename, PATH_MAX - 4, "%.*s/%.*s"
  831             , (int)(PATH_MAX-5-strlen(stamp))
  832             , cnt->conf.target_dir
  833             , (int)(PATH_MAX-5-strlen(cnt->conf.target_dir))
  834             , stamp);
  835 
  836         if (access(cnt->conf.target_dir, W_OK)!= 0) {
  837             /* Permission denied */
  838             if (errno ==  EACCES) {
  839                 MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  840                     ,_("no write access to target directory %s"), cnt->conf.target_dir);
  841                 return ;
  842             /* Path not found - create it */
  843             } else if (errno ==  ENOENT) {
  844                 MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  845                     ,_("path not found, trying to create it %s ..."), cnt->conf.target_dir);
  846                 if (create_path(cnt->extpipefilename) == -1)
  847                     return ;
  848             }
  849             else {
  850                 MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  851                     ,_("error accesing path %s"), cnt->conf.target_dir);
  852                 return ;
  853             }
  854         }
  855 
  856         /* Always create any path specified as file name */
  857         if (create_path(cnt->extpipefilename) == -1)
  858             return ;
  859 
  860         mystrftime(cnt, stamp, sizeof(stamp), cnt->conf.movie_extpipe, currenttime_tv, cnt->extpipefilename, 0);
  861 
  862         retcd = snprintf(cnt->extpipecmdline, PATH_MAX, "%s", stamp);
  863         if ((retcd < 0 ) || (retcd >= PATH_MAX)){
  864             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
  865                 , _("Error specifying command line: %s"), cnt->extpipecmdline);
  866             return;
  867         }
  868         MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("pipe: %s"), cnt->extpipecmdline);
  869 
  870         MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("cnt->moviefps: %d"), cnt->movie_fps);
  871 
  872         event(cnt, EVENT_FILECREATE, NULL, cnt->extpipefilename, (void *)FTYPE_MPEG, currenttime_tv);
  873         cnt->extpipe = popen(cnt->extpipecmdline, "we");
  874 
  875         if (cnt->extpipe == NULL) {
  876             MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, _("popen failed"));
  877             return;
  878         }
  879 
  880         setbuf(cnt->extpipe, NULL);
  881         cnt->extpipe_open = 1;
  882     }
  883 }
  884 
  885 static void event_extpipe_put(struct context *cnt,
  886             motion_event type ATTRIBUTE_UNUSED,
  887             struct image_data *img_data, char *dummy1 ATTRIBUTE_UNUSED,
  888             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *tv1 ATTRIBUTE_UNUSED)
  889 {
  890     int passthrough;
  891 
  892     /* Check use_extpipe enabled and ext_pipe not NULL */
  893     if ((cnt->conf.movie_extpipe_use) && (cnt->extpipe != NULL)) {
  894         MOTION_LOG(DBG, TYPE_EVENTS, NO_ERRNO, _("Using extpipe"));
  895         passthrough = util_check_passthrough(cnt);
  896         /* Check that is open */
  897         if ((cnt->extpipe_open) && (fileno(cnt->extpipe) > 0)) {
  898             if ((cnt->imgs.size_high > 0) && (!passthrough)){
  899                 if (!fwrite(img_data->image_high, cnt->imgs.size_high, 1, cnt->extpipe))
  900                     MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  901                         ,_("Error writing in pipe , state error %d"), ferror(cnt->extpipe));
  902             } else {
  903                 if (!fwrite(img_data->image_norm, cnt->imgs.size_norm, 1, cnt->extpipe))
  904                     MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO
  905                         ,_("Error writing in pipe , state error %d"), ferror(cnt->extpipe));
  906            }
  907         } else {
  908             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
  909                 ,_("pipe %s not created or closed already "), cnt->extpipecmdline);
  910         }
  911     }
  912 }
  913 
  914 
  915 static void event_new_video(struct context *cnt,
  916             motion_event type ATTRIBUTE_UNUSED,
  917             struct image_data *dummy ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED,
  918             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *tv1 ATTRIBUTE_UNUSED)
  919 {
  920     cnt->movie_last_shot = -1;
  921 
  922     cnt->movie_fps = cnt->lastrate;
  923 
  924     MOTION_LOG(INF, TYPE_EVENTS, NO_ERRNO, _("Source FPS %d"), cnt->movie_fps);
  925 
  926     if (cnt->movie_fps < 2) cnt->movie_fps = 2;
  927 
  928 }
  929 
  930 
  931 static void event_ffmpeg_newfile(struct context *cnt,
  932             motion_event type ATTRIBUTE_UNUSED,
  933             struct image_data *dummy0 ATTRIBUTE_UNUSED,
  934             char *dummy1 ATTRIBUTE_UNUSED,
  935             void *dummy2 ATTRIBUTE_UNUSED,
  936             struct timeval *currenttime_tv)
  937 {
  938     char stamp[PATH_MAX];
  939     const char *moviepath;
  940     const char *codec;
  941     long codenbr;
  942     int retcd;
  943 
  944     if (!cnt->conf.movie_output && !cnt->conf.movie_output_motion)
  945         return;
  946 
  947     /*
  948      *  conf.mpegpath would normally be defined but if someone deleted it by control interface
  949      *  it is better to revert to the default than fail
  950      */
  951     if (cnt->conf.movie_filename)
  952         moviepath = cnt->conf.movie_filename;
  953     else
  954         moviepath = DEF_MOVIEPATH;
  955 
  956     mystrftime(cnt, stamp, sizeof(stamp), moviepath, currenttime_tv, NULL, 0);
  957 
  958     /*
  959      *  motion movies get the same name as normal movies plus an appended 'm'
  960      *  PATH_MAX - 4 to allow for .mpg to be appended without overflow
  961      */
  962 
  963      /* The following section allows for testing of all the various containers
  964       * that Motion permits. The container type is pre-pended to the name of the
  965       * file so that we can determine which container type created what movie.
  966       * The intent for this is be used for developer testing when the ffmpeg libs
  967       * change or the code inside our ffmpeg module changes.  For each event, the
  968       * container type will change.  This way, you can turn on emulate motion, then
  969       * specify a maximum movie time and let Motion run for days creating all the
  970       * different types of movies checking for crashes, warnings, etc.
  971      */
  972     codec = cnt->conf.movie_codec;
  973     if (strcmp(codec, "ogg") == 0) {
  974         MOTION_LOG(WRN, TYPE_ENCODER, NO_ERRNO, _("The ogg container is no longer supported.  Changing to mpeg4"));
  975         codec = "mpeg4";
  976     }
  977     if (strcmp(codec, "test") == 0) {
  978         MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, _("Running test of the various output formats."));
  979         codenbr = cnt->event_nr % 10;
  980         switch (codenbr) {
  981         case 1:
  982             codec = "mpeg4";
  983             break;
  984         case 2:
  985             codec = "msmpeg4";
  986             break;
  987         case 3:
  988             codec = "swf";
  989             break;
  990         case 4:
  991             codec = "flv";
  992             break;
  993         case 5:
  994             codec = "ffv1";
  995             break;
  996         case 6:
  997             codec = "mov";
  998             break;
  999         case 7:
 1000             codec = "mp4";
 1001             break;
 1002         case 8:
 1003             codec = "mkv";
 1004             break;
 1005         case 9:
 1006             codec = "hevc";
 1007             break;
 1008         default:
 1009             codec = "msmpeg4";
 1010             break;
 1011         }
 1012         snprintf(cnt->motionfilename, PATH_MAX - 4, "%.*s/%s_%.*sm"
 1013             , (int)(PATH_MAX-7-strlen(stamp)-strlen(codec))
 1014             , cnt->conf.target_dir, codec
 1015             , (int)(PATH_MAX-7-strlen(cnt->conf.target_dir)-strlen(codec))
 1016             , stamp);
 1017         snprintf(cnt->newfilename, PATH_MAX - 4, "%.*s/%s_%.*s"
 1018             , (int)(PATH_MAX-6-strlen(stamp)-strlen(codec))
 1019             , cnt->conf.target_dir, codec
 1020             , (int)(PATH_MAX-6-strlen(cnt->conf.target_dir)-strlen(codec))
 1021             , stamp);
 1022     } else {
 1023         snprintf(cnt->motionfilename, PATH_MAX - 4, "%.*s/%.*sm"
 1024             , (int)(PATH_MAX-6-strlen(stamp))
 1025             , cnt->conf.target_dir
 1026             , (int)(PATH_MAX-6-strlen(cnt->conf.target_dir))
 1027             , stamp);
 1028         snprintf(cnt->newfilename, PATH_MAX - 4, "%.*s/%.*s"
 1029             , (int)(PATH_MAX-5-strlen(stamp))
 1030             , cnt->conf.target_dir
 1031             , (int)(PATH_MAX-5-strlen(cnt->conf.target_dir))
 1032             , stamp);
 1033     }
 1034     if (cnt->conf.movie_output) {
 1035         cnt->ffmpeg_output = mymalloc(sizeof(struct ffmpeg));
 1036         if (cnt->imgs.size_high > 0){
 1037             cnt->ffmpeg_output->width  = cnt->imgs.width_high;
 1038             cnt->ffmpeg_output->height = cnt->imgs.height_high;
 1039             cnt->ffmpeg_output->high_resolution = TRUE;
 1040             cnt->ffmpeg_output->rtsp_data = cnt->rtsp_high;
 1041         } else {
 1042             cnt->ffmpeg_output->width  = cnt->imgs.width;
 1043             cnt->ffmpeg_output->height = cnt->imgs.height;
 1044             cnt->ffmpeg_output->high_resolution = FALSE;
 1045             cnt->ffmpeg_output->rtsp_data = cnt->rtsp;
 1046         }
 1047         cnt->ffmpeg_output->tlapse = TIMELAPSE_NONE;
 1048         cnt->ffmpeg_output->fps = cnt->movie_fps;
 1049         cnt->ffmpeg_output->bps = cnt->conf.movie_bps;
 1050         cnt->ffmpeg_output->filename = cnt->newfilename;
 1051         cnt->ffmpeg_output->quality = cnt->conf.movie_quality;
 1052         cnt->ffmpeg_output->start_time.tv_sec = currenttime_tv->tv_sec;
 1053         cnt->ffmpeg_output->start_time.tv_usec = currenttime_tv->tv_usec;
 1054         cnt->ffmpeg_output->last_pts = -1;
 1055         cnt->ffmpeg_output->base_pts = 0;
 1056         cnt->ffmpeg_output->gop_cnt = 0;
 1057         cnt->ffmpeg_output->codec_name = codec;
 1058         if (strcmp(cnt->conf.movie_codec, "test") == 0) {
 1059             cnt->ffmpeg_output->test_mode = 1;
 1060         } else {
 1061             cnt->ffmpeg_output->test_mode = 0;
 1062         }
 1063         cnt->ffmpeg_output->motion_images = 0;
 1064         cnt->ffmpeg_output->passthrough =util_check_passthrough(cnt);
 1065 
 1066 
 1067         retcd = ffmpeg_open(cnt->ffmpeg_output);
 1068         if (retcd < 0){
 1069             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
 1070                 ,_("Error opening context for movie output."));
 1071             free(cnt->ffmpeg_output);
 1072             cnt->ffmpeg_output=NULL;
 1073             return;
 1074         }
 1075         event(cnt, EVENT_FILECREATE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, currenttime_tv);
 1076     }
 1077 
 1078     if (cnt->conf.movie_output_motion) {
 1079         cnt->ffmpeg_output_motion = mymalloc(sizeof(struct ffmpeg));
 1080         cnt->ffmpeg_output_motion->width  = cnt->imgs.width;
 1081         cnt->ffmpeg_output_motion->height = cnt->imgs.height;
 1082         cnt->ffmpeg_output_motion->rtsp_data = NULL;
 1083         cnt->ffmpeg_output_motion->tlapse = TIMELAPSE_NONE;
 1084         cnt->ffmpeg_output_motion->fps = cnt->movie_fps;
 1085         cnt->ffmpeg_output_motion->bps = cnt->conf.movie_bps;
 1086         cnt->ffmpeg_output_motion->filename = cnt->motionfilename;
 1087         cnt->ffmpeg_output_motion->quality = cnt->conf.movie_quality;
 1088         cnt->ffmpeg_output_motion->start_time.tv_sec = currenttime_tv->tv_sec;
 1089         cnt->ffmpeg_output_motion->start_time.tv_usec = currenttime_tv->tv_usec;
 1090         cnt->ffmpeg_output_motion->last_pts = -1;
 1091         cnt->ffmpeg_output_motion->base_pts = 0;
 1092         cnt->ffmpeg_output_motion->gop_cnt = 0;
 1093         cnt->ffmpeg_output_motion->codec_name = codec;
 1094         if (strcmp(cnt->conf.movie_codec, "test") == 0) {
 1095             cnt->ffmpeg_output_motion->test_mode = TRUE;
 1096         } else {
 1097             cnt->ffmpeg_output_motion->test_mode = FALSE;
 1098         }
 1099         cnt->ffmpeg_output_motion->motion_images = TRUE;
 1100         cnt->ffmpeg_output_motion->passthrough = FALSE;
 1101         cnt->ffmpeg_output_motion->high_resolution = FALSE;
 1102         cnt->ffmpeg_output_motion->rtsp_data = NULL;
 1103 
 1104         retcd = ffmpeg_open(cnt->ffmpeg_output_motion);
 1105         if (retcd < 0){
 1106             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
 1107                 ,_("ffopen_open error creating (motion) file [%s]"), cnt->motionfilename);
 1108             free(cnt->ffmpeg_output_motion);
 1109             cnt->ffmpeg_output_motion = NULL;
 1110             return;
 1111         }
 1112     }
 1113 }
 1114 
 1115 static void event_ffmpeg_timelapse(struct context *cnt,
 1116             motion_event type ATTRIBUTE_UNUSED, struct image_data *img_data,
 1117             char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED,
 1118             struct timeval *currenttime_tv)
 1119 {
 1120     int retcd;
 1121     int passthrough;
 1122 
 1123     if (!cnt->ffmpeg_timelapse) {
 1124         char tmp[PATH_MAX];
 1125         const char *timepath;
 1126         const char *codec_mpg = "mpg";
 1127         const char *codec_mpeg = "mpeg4";
 1128 
 1129         /*
 1130          *  conf.timelapse_filename would normally be defined but if someone deleted it by control interface
 1131          *  it is better to revert to the default than fail
 1132          */
 1133         if (cnt->conf.timelapse_filename)
 1134             timepath = cnt->conf.timelapse_filename;
 1135         else
 1136             timepath = DEF_TIMEPATH;
 1137 
 1138         mystrftime(cnt, tmp, sizeof(tmp), timepath, currenttime_tv, NULL, 0);
 1139 
 1140         /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */
 1141         snprintf(cnt->timelapsefilename, PATH_MAX - 4, "%.*s/%.*s"
 1142             , (int)(PATH_MAX-5-strlen(tmp))
 1143             , cnt->conf.target_dir
 1144             , (int)(PATH_MAX-5-strlen(cnt->conf.target_dir))
 1145             , tmp);
 1146         passthrough = util_check_passthrough(cnt);
 1147         cnt->ffmpeg_timelapse = mymalloc(sizeof(struct ffmpeg));
 1148         if ((cnt->imgs.size_high > 0) && (!passthrough)){
 1149             cnt->ffmpeg_timelapse->width  = cnt->imgs.width_high;
 1150             cnt->ffmpeg_timelapse->height = cnt->imgs.height_high;
 1151             cnt->ffmpeg_timelapse->high_resolution = TRUE;
 1152         } else {
 1153             cnt->ffmpeg_timelapse->width  = cnt->imgs.width;
 1154             cnt->ffmpeg_timelapse->height = cnt->imgs.height;
 1155             cnt->ffmpeg_timelapse->high_resolution = FALSE;
 1156         }
 1157         cnt->ffmpeg_timelapse->fps = cnt->conf.timelapse_fps;
 1158         cnt->ffmpeg_timelapse->bps = cnt->conf.movie_bps;
 1159         cnt->ffmpeg_timelapse->filename = cnt->timelapsefilename;
 1160         cnt->ffmpeg_timelapse->quality = cnt->conf.movie_quality;
 1161         cnt->ffmpeg_timelapse->start_time.tv_sec = currenttime_tv->tv_sec;
 1162         cnt->ffmpeg_timelapse->start_time.tv_usec = currenttime_tv->tv_usec;
 1163         cnt->ffmpeg_timelapse->last_pts = -1;
 1164         cnt->ffmpeg_timelapse->base_pts = 0;
 1165         cnt->ffmpeg_timelapse->test_mode = FALSE;
 1166         cnt->ffmpeg_timelapse->gop_cnt = 0;
 1167         cnt->ffmpeg_timelapse->motion_images = FALSE;
 1168         cnt->ffmpeg_timelapse->passthrough = FALSE;
 1169         cnt->ffmpeg_timelapse->rtsp_data = NULL;
 1170 
 1171         if ((strcmp(cnt->conf.timelapse_codec,"mpg") == 0) ||
 1172             (strcmp(cnt->conf.timelapse_codec,"swf") == 0) ){
 1173 
 1174             if (strcmp(cnt->conf.timelapse_codec,"swf") == 0) {
 1175                 MOTION_LOG(WRN, TYPE_EVENTS, NO_ERRNO
 1176                     ,_("The swf container for timelapse no longer supported.  Using mpg container."));
 1177             }
 1178 
 1179             MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("Timelapse using mpg codec."));
 1180             MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("Events will be appended to file"));
 1181 
 1182             cnt->ffmpeg_timelapse->tlapse = TIMELAPSE_APPEND;
 1183             cnt->ffmpeg_timelapse->codec_name = codec_mpg;
 1184             retcd = ffmpeg_open(cnt->ffmpeg_timelapse);
 1185         } else {
 1186             MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("Timelapse using mpeg4 codec."));
 1187             MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, _("Events will be trigger new files"));
 1188 
 1189             cnt->ffmpeg_timelapse->tlapse = TIMELAPSE_NEW;
 1190             cnt->ffmpeg_timelapse->codec_name = codec_mpeg;
 1191             retcd = ffmpeg_open(cnt->ffmpeg_timelapse);
 1192         }
 1193 
 1194         if (retcd < 0){
 1195             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO
 1196                 ,_("ffopen_open error creating (timelapse) file [%s]"), cnt->timelapsefilename);
 1197             free(cnt->ffmpeg_timelapse);
 1198             cnt->ffmpeg_timelapse = NULL;
 1199             return;
 1200         }
 1201         event(cnt, EVENT_FILECREATE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, currenttime_tv);
 1202     }
 1203 
 1204     if (ffmpeg_put_image(cnt->ffmpeg_timelapse, img_data, currenttime_tv) == -1) {
 1205         MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, _("Error encoding image"));
 1206     }
 1207 
 1208 }
 1209 
 1210 static void event_ffmpeg_put(struct context *cnt,
 1211             motion_event type ATTRIBUTE_UNUSED,
 1212             struct image_data *img_data, char *dummy1 ATTRIBUTE_UNUSED,
 1213             void *dummy2 ATTRIBUTE_UNUSED, struct timeval *currenttime_tv)
 1214 {
 1215     if (cnt->ffmpeg_output) {
 1216         if (ffmpeg_put_image(cnt->ffmpeg_output, img_data, currenttime_tv) == -1){
 1217             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, _("Error encoding image"));
 1218         }
 1219     }
 1220     if (cnt->ffmpeg_output_motion) {
 1221         if (ffmpeg_put_image(cnt->ffmpeg_output_motion, &cnt->imgs.img_motion, currenttime_tv) == -1) {
 1222             MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, _("Error encoding image"));
 1223         }
 1224     }
 1225 }
 1226 
 1227 static void event_ffmpeg_closefile(struct context *cnt,
 1228             motion_event type ATTRIBUTE_UNUSED,
 1229             struct image_data *dummy1 ATTRIBUTE_UNUSED,
 1230             char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
 1231             struct timeval *currenttime_tv)
 1232 {
 1233 
 1234     if (cnt->ffmpeg_output) {
 1235         ffmpeg_close(cnt->ffmpeg_output);
 1236         free(cnt->ffmpeg_output);
 1237         cnt->ffmpeg_output = NULL;
 1238         event(cnt, EVENT_FILECLOSE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, currenttime_tv);
 1239     }
 1240 
 1241     if (cnt->ffmpeg_output_motion) {
 1242         ffmpeg_close(cnt->ffmpeg_output_motion);
 1243         free(cnt->ffmpeg_output_motion);
 1244         cnt->ffmpeg_output_motion = NULL;
 1245         event(cnt, EVENT_FILECLOSE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, currenttime_tv);
 1246     }
 1247 
 1248 }
 1249 
 1250 static void event_ffmpeg_timelapseend(struct context *cnt,
 1251             motion_event type ATTRIBUTE_UNUSED,
 1252             struct image_data *dummy1 ATTRIBUTE_UNUSED,
 1253             char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED,
 1254             struct timeval *currenttime_tv)
 1255 {
 1256     if (cnt->ffmpeg_timelapse) {
 1257         ffmpeg_close(cnt->ffmpeg_timelapse);
 1258         free(cnt->ffmpeg_timelapse);
 1259         cnt->ffmpeg_timelapse = NULL;
 1260         event(cnt, EVENT_FILECLOSE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, currenttime_tv);
 1261     }
 1262 }
 1263 
 1264 
 1265 
 1266 /*
 1267  * Starting point for all events
 1268  */
 1269 
 1270 struct event_handlers {
 1271     motion_event type;
 1272     event_handler handler;
 1273 };
 1274 
 1275 struct event_handlers event_handlers[] = {
 1276 #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) || defined(HAVE_MARIADB)
 1277     {
 1278     EVENT_FILECREATE,
 1279     event_sqlnewfile
 1280     },
 1281 #endif
 1282     {
 1283     EVENT_FILECREATE,
 1284     on_picture_save_command
 1285     },
 1286     {
 1287     EVENT_FILECREATE,
 1288     event_newfile
 1289     },
 1290     {
 1291     EVENT_MOTION,
 1292     event_beep
 1293     },
 1294     {
 1295     EVENT_MOTION,
 1296     on_motion_detected_command
 1297     },
 1298     {
 1299     EVENT_AREA_DETECTED,
 1300     on_area_command
 1301     },
 1302 #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) || defined(HAVE_MARIADB)
 1303     {
 1304     EVENT_FIRSTMOTION,
 1305     event_sqlfirstmotion
 1306     },
 1307 #endif
 1308     {
 1309     EVENT_FIRSTMOTION,
 1310     on_event_start_command
 1311     },
 1312     {
 1313     EVENT_ENDMOTION,
 1314     on_event_end_command
 1315     },
 1316     {
 1317     EVENT_IMAGE_DETECTED,
 1318     event_image_detect
 1319     },
 1320     {
 1321     EVENT_IMAGEM_DETECTED,
 1322     event_imagem_detect
 1323     },
 1324     {
 1325     EVENT_IMAGE_SNAPSHOT,
 1326     event_image_snapshot
 1327     },
 1328 #if defined(HAVE_V4L2) && !defined(BSD)
 1329     {
 1330     EVENT_IMAGE,
 1331     event_vlp_putpipe
 1332     },
 1333     {
 1334     EVENT_IMAGEM,
 1335     event_vlp_putpipe
 1336     },
 1337 #endif /* defined(HAVE_V4L2) && !defined(BSD) */
 1338     {
 1339     EVENT_IMAGE_PREVIEW,
 1340     event_image_preview
 1341     },
 1342     {
 1343     EVENT_STREAM,
 1344     event_stream_put
 1345     },
 1346     {
 1347     EVENT_FIRSTMOTION,
 1348     event_new_video
 1349     },
 1350     {
 1351     EVENT_FIRSTMOTION,
 1352     event_ffmpeg_newfile
 1353     },
 1354     {
 1355     EVENT_IMAGE_DETECTED,
 1356     event_ffmpeg_put
 1357     },
 1358     {
 1359     EVENT_FFMPEG_PUT,
 1360     event_ffmpeg_put
 1361     },
 1362     {
 1363     EVENT_ENDMOTION,
 1364     event_ffmpeg_closefile
 1365     },
 1366     {
 1367     EVENT_TIMELAPSE,
 1368     event_ffmpeg_timelapse
 1369     },
 1370     {
 1371     EVENT_TIMELAPSEEND,
 1372     event_ffmpeg_timelapseend
 1373     },
 1374 #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) || defined(HAVE_MARIADB)
 1375     {
 1376     EVENT_FILECLOSE,
 1377     event_sqlfileclose
 1378     },
 1379 #endif
 1380     {
 1381     EVENT_FILECLOSE,
 1382     on_movie_end_command
 1383     },
 1384     {
 1385     EVENT_FIRSTMOTION,
 1386     event_create_extpipe
 1387     },
 1388     {
 1389     EVENT_IMAGE_DETECTED,
 1390     event_extpipe_put
 1391     },
 1392     {
 1393     EVENT_FFMPEG_PUT,
 1394     event_extpipe_put
 1395     },
 1396     {
 1397     EVENT_ENDMOTION,
 1398     event_extpipe_end
 1399     },
 1400     {
 1401     EVENT_CAMERA_LOST,
 1402     event_camera_lost
 1403     },
 1404     {
 1405     EVENT_CAMERA_FOUND,
 1406     event_camera_found
 1407     },
 1408     {0, NULL}
 1409 };
 1410 
 1411 
 1412 /**
 1413  * event
 1414  *   defined with the following parameters:
 1415  *      - Type as defined in event.h (EVENT_...)
 1416  *      - The global context struct cnt
 1417  *      - img_data - A pointer to a image_data context used for images
 1418  *      - filename - A pointer to typically a string for a file path
 1419  *      - eventdata - A void pointer that can be cast to anything. E.g. FTYPE_...
 1420  *      - tm - A tm struct that carries a full time structure
 1421  * The split between unsigned images and signed filenames was introduced in 3.2.2
 1422  * as a code reading friendly solution to avoid a stream of compiler warnings in gcc 4.0.
 1423  */
 1424 void event(struct context *cnt, motion_event type, struct image_data *img_data,
 1425            char *filename, void *eventdata, struct timeval *tv1)
 1426 {
 1427     int i=-1;
 1428 
 1429     while (event_handlers[++i].handler) {
 1430         if (type == event_handlers[i].type)
 1431             event_handlers[i].handler(cnt, type, img_data, filename, eventdata, tv1);
 1432     }
 1433 }