"Fossies" - the Fresh Open Source Software Archive

Member "motion-Release-4.3.0/src/netcam_jpeg.c" (14 Jan 2020, 17328 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 "netcam_jpeg.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  *    netcam_jpeg.c
    3  *
    4  *    Module for handling JPEG decompression for network cameras.
    5  *
    6  *    This code was inspired by the original module written by
    7  *    Jeroen Vreeken and enhanced by several Motion project
    8  *    contributors, particularly Angel Carpintero and
    9  *    Christopher Price.
   10  *
   11  *    Copyright 2005, William M. Brack
   12  *    This program is published under the GNU Public license
   13  */
   14 #include "translate.h"
   15 #include "rotate.h"    /* already includes motion.h */
   16 
   17 /* This is a workaround regarding these defines.  The config.h file defines
   18  * HAVE_STDLIB_H as 1 whereas the jpeglib.h just defines it without a value.
   19  * this causes massive warnings/error on mis-matched definitions.  We do not
   20  * control either of these so we have to suffer through this workaround hack
   21 */
   22 #if (HAVE_STDLIB_H == 1)
   23     #undef HAVE_STDLIB_H
   24     #define HAVE_STDLIB_H_ORIG 1
   25 #endif
   26 
   27 #include <jpeglib.h>
   28 
   29 #ifdef HAVE_STDLIB_H
   30   #ifdef HAVE_STDLIB_H_ORIG
   31     #undef HAVE_STDLIB_H
   32     #undef HAVE_STDLIB_H_ORIG
   33     #define HAVE_STDLIB_H 1
   34   #else
   35     #undef HAVE_STDLIB_H
   36   #endif
   37 #endif
   38 
   39 
   40 #include <jerror.h>
   41 
   42 /*
   43  * netcam_source_mgr is a locally-defined structure to contain elements
   44  * which are not present in the standard libjpeg (the element 'pub' is a
   45  * pointer to the standard information).
   46  */
   47 typedef struct {
   48     struct jpeg_source_mgr pub;
   49     char           *data;
   50     int             length;
   51     JOCTET         *buffer;
   52     boolean         start_of_file;
   53 } netcam_source_mgr;
   54 
   55 typedef netcam_source_mgr *netcam_src_ptr;
   56 
   57 
   58 /*
   59  * Here we declare function prototypes for all the routines involved with
   60  * overriding libjpeg functions.  The reason these are required is that,
   61  * although the standard library handles input and output with stdio,
   62  * we are working with "remote" data (from the camera or from a file).
   63  */
   64 static void     netcam_init_source(j_decompress_ptr);
   65 static boolean  netcam_fill_input_buffer(j_decompress_ptr);
   66 static void     netcam_skip_input_data(j_decompress_ptr, long);
   67 static void     netcam_term_source(j_decompress_ptr);
   68 static void     netcam_memory_src(j_decompress_ptr, char *, int);
   69 static void     netcam_error_exit(j_common_ptr);
   70 
   71 static void netcam_init_source(j_decompress_ptr cinfo)
   72 {
   73     /* Get our "private" structure from the libjpeg structure. */
   74     netcam_src_ptr  src = (netcam_src_ptr) cinfo->src;
   75     /*
   76      * Set the 'start_of_file' flag in our private structure
   77      * (used by my_fill_input_buffer).
   78      */
   79     src->start_of_file = TRUE;
   80 }
   81 
   82 static boolean netcam_fill_input_buffer(j_decompress_ptr cinfo)
   83 {
   84     netcam_src_ptr  src = (netcam_src_ptr) cinfo->src;
   85     size_t nbytes;
   86 
   87     /*
   88      * start_of_file is set any time netcam_init_source has been called
   89      * since the last entry to this routine.  This would be the normal
   90      * path when a new image is to be processed.  It is assumed that
   91      * this routine will only be called once for the entire image.
   92      * If an unexpected call (with start_of_file FALSE) occurs, the
   93      * routine calls ERREXIT().
   94      */
   95     if (src->start_of_file) {
   96         nbytes = src->length;
   97         src->buffer = (JOCTET *) src->data;
   98     } else {
   99         nbytes = 2;
  100         MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("Not enough data from netcam."));
  101         ERREXIT(cinfo, JERR_INPUT_EOF);
  102     }
  103 
  104     src->pub.next_input_byte = src->buffer;
  105     src->pub.bytes_in_buffer = nbytes;
  106     src->start_of_file = FALSE;
  107 
  108     return TRUE;
  109 }
  110 
  111 static void netcam_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
  112 {
  113     netcam_src_ptr  src = (netcam_src_ptr) cinfo->src;
  114 
  115     if (num_bytes > 0) {
  116         while (num_bytes > (long) src->pub.bytes_in_buffer) {
  117             num_bytes -= (long) src->pub.bytes_in_buffer;
  118             (void) netcam_fill_input_buffer (cinfo);
  119         }
  120         src->pub.next_input_byte += (size_t) num_bytes;
  121         src->pub.bytes_in_buffer -= (size_t) num_bytes;
  122     }
  123 }
  124 
  125 static void netcam_term_source(j_decompress_ptr cinfo ATTRIBUTE_UNUSED)
  126 {
  127 }
  128 
  129 /**
  130  * netcam_memory_src
  131  *
  132  *    Routine to setup for fetching data from a netcam buffer, used by the
  133  *    JPEG library decompression routine.
  134  *
  135  * Parameters:
  136  *    cinfo           pointer to the jpeg decompression object.
  137  *    data            pointer to the image data received from a netcam.
  138  *    length          total size of the image.
  139  *
  140  * Returns:             Nothing
  141  *
  142  */
  143 static void netcam_memory_src(j_decompress_ptr cinfo, char *data, int length)
  144 {
  145     netcam_src_ptr src;
  146 
  147     if (cinfo->src == NULL)
  148         cinfo->src = (struct jpeg_source_mgr *)
  149                      (*cinfo->mem->alloc_small)
  150                      ((j_common_ptr) cinfo, JPOOL_PERMANENT,
  151                       sizeof (netcam_source_mgr));
  152 
  153 
  154     src = (netcam_src_ptr)cinfo->src;
  155     src->data = data;
  156     src->length = length;
  157     src->pub.init_source = netcam_init_source;
  158     src->pub.fill_input_buffer = netcam_fill_input_buffer;
  159     src->pub.skip_input_data = netcam_skip_input_data;
  160     src->pub.resync_to_restart = jpeg_resync_to_restart;
  161     src->pub.term_source = netcam_term_source;
  162     src->pub.bytes_in_buffer = 0;
  163     src->pub.next_input_byte = NULL;
  164 }
  165 
  166 /**
  167  * netcam_error_exit
  168  *
  169  *     Routine to override the libjpeg error exit routine so
  170  *     that we can just throw away the bad frame and continue
  171  *     with more data from the netcam.
  172  *
  173  * Parameters
  174  *
  175  *     cinfo           pointer to the decompression control structure.
  176  *
  177  * Returns:             does an (ugly) longjmp to get back to netcam_jpeg
  178  *                      code.
  179  *
  180  */
  181 static void netcam_error_exit(j_common_ptr cinfo)
  182 {
  183     /* Fetch our pre-stored pointer to the netcam context. */
  184     netcam_context_ptr netcam = cinfo->client_data;
  185     /* Output the message associated with the error. */
  186     (*cinfo->err->output_message)(cinfo);
  187     /* Set flag to show the decompression had errors. */
  188     netcam->jpeg_error |= 1;
  189     /* Need to "cleanup" the aborted decompression. */
  190     jpeg_destroy (cinfo);
  191 
  192     MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("netcam->jpeg_error %d"), netcam->jpeg_error);
  193 
  194     /* Jump back to wherever we started. */
  195     longjmp(netcam->setjmp_buffer, 1);
  196 }
  197 
  198 /**
  199  * netcam_output_message
  200  *
  201  *     Routine to override the libjpeg error message output routine.
  202  *     We do this so that we can output our module and thread i.d.,
  203  *     as well as put the message to the motion log.
  204  *
  205  * Parameters
  206  *
  207  *     cinfo           pointer to the decompression control structure.
  208  *
  209  * Returns              Nothing
  210  *
  211  */
  212 static void netcam_output_message(j_common_ptr cinfo)
  213 {
  214     char buffer[JMSG_LENGTH_MAX];
  215 
  216     /* Fetch our pre-stored pointer to the netcam context. */
  217     netcam_context_ptr netcam = cinfo->client_data;
  218 
  219     /*
  220      * While experimenting with a "appro" netcam it was discovered
  221      * that the jpeg data produced by the camera caused warning
  222      * messages from libjpeg (JWRN_EXTRANEOUS_DATA).  The following
  223      * code is to assure that specific warning is ignored.
  224      *
  225      * NOTE: It's likely that we will discover other error message
  226      * codes which we want to ignore.  In that case, we should have
  227      * some sort of table-lookup to decide which messages we really
  228      * care about.
  229      */
  230     if ((cinfo->err->msg_code != JWRN_EXTRANEOUS_DATA) &&
  231         (cinfo->err->msg_code == JWRN_NOT_SEQUENTIAL) && (!netcam->netcam_tolerant_check))
  232         netcam->jpeg_error |= 2;    /* Set flag to show problem */
  233 
  234     /*
  235      * Format the message according to library standards.
  236      * Write it out to the motion log.
  237      */
  238      (*cinfo->err->format_message)(cinfo, buffer);
  239      MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s", buffer);
  240 
  241 }
  242 
  243 /**
  244  * netcam_init_jpeg
  245  *
  246  *     Initialises the JPEG library prior to doing a
  247  *     decompression.
  248  *
  249  * Parameters:
  250  *     netcam          pointer to netcam_context.
  251  *     cinfo           pointer to JPEG decompression context.
  252  *
  253  * Returns:           Error code.
  254  */
  255 static int netcam_init_jpeg(netcam_context_ptr netcam, j_decompress_ptr cinfo)
  256 {
  257     netcam_buff_ptr buff;
  258 
  259     /*
  260      * First we check whether a new image has arrived.  If not, we
  261      * setup to wait for 1/2 a frame time.  This will (hopefully)
  262      * help in synchronizing the camera frames with the motion main
  263      * loop.
  264      */
  265     pthread_mutex_lock(&netcam->mutex);
  266 
  267     if (netcam->imgcnt_last == netcam->imgcnt) {    /* Need to wait */
  268         struct timespec waittime;
  269         struct timeval curtime;
  270         int retcode;
  271 
  272         /*
  273          * We calculate the delay time (representing the desired frame
  274          * rate).  This delay time is in *nanoseconds*.
  275          * We will wait 0.5 seconds which gives a practical minimum
  276          * framerate of 2 which is desired for the motion_loop to
  277          * function.
  278          */
  279         gettimeofday(&curtime, NULL);
  280         curtime.tv_usec += 500000;
  281 
  282         if (curtime.tv_usec > 1000000) {
  283             curtime.tv_usec -= 1000000;
  284             curtime.tv_sec++;
  285         }
  286 
  287         waittime.tv_sec = curtime.tv_sec;
  288         waittime.tv_nsec = 1000L * curtime.tv_usec;
  289 
  290         do {
  291             retcode = pthread_cond_timedwait(&netcam->pic_ready,
  292                                              &netcam->mutex, &waittime);
  293         } while (retcode == EINTR);
  294 
  295         if (retcode) {    /* We assume a non-zero reply is ETIMEOUT */
  296             pthread_mutex_unlock(&netcam->mutex);
  297 
  298             MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
  299                 ,_("no new pic, no signal rcvd"));
  300 
  301             return NETCAM_GENERAL_ERROR | NETCAM_NOTHING_NEW_ERROR;
  302         }
  303 
  304         MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("***new pic delay successful***"));
  305     }
  306 
  307     netcam->imgcnt_last = netcam->imgcnt;
  308 
  309     /* Set latest buffer as "current". */
  310     buff = netcam->latest;
  311     netcam->latest = netcam->jpegbuf;
  312     netcam->jpegbuf = buff;
  313     pthread_mutex_unlock(&netcam->mutex);
  314 
  315     /* Clear any error flag from previous work. */
  316     netcam->jpeg_error = 0;
  317 
  318     buff = netcam->jpegbuf;
  319     /*
  320      * Prepare for the decompression.
  321      * Initialize the JPEG decompression object.
  322      */
  323     jpeg_create_decompress(cinfo);
  324 
  325     /* Set up own error exit routine. */
  326     cinfo->err = jpeg_std_error(&netcam->jerr);
  327     cinfo->client_data = netcam;
  328     netcam->jerr.error_exit = netcam_error_exit;
  329     netcam->jerr.output_message = netcam_output_message;
  330 
  331     /* Specify the data source as our own routine. */
  332     netcam_memory_src(cinfo, buff->ptr, buff->used);
  333 
  334     /* Read file parameters (rejecting tables-only). */
  335     jpeg_read_header(cinfo, TRUE);
  336 
  337     /* Override the desired colour space. */
  338     cinfo->out_color_space = JCS_YCbCr;
  339 
  340     /* Start the decompressor. */
  341     jpeg_start_decompress(cinfo);
  342 
  343     if (netcam->jpeg_error)
  344         MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("jpeg_error %d"), netcam->jpeg_error);
  345 
  346     return netcam->jpeg_error;
  347 }
  348 
  349 /**
  350  * netcam_image_conv
  351  *
  352  * Parameters:
  353  *      netcam          pointer to netcam_context
  354  *      cinfo           pointer to JPEG decompression context
  355  *      image           pointer to buffer of destination image (yuv420)
  356  *
  357  * Returns :  netcam->jpeg_error
  358  */
  359 static int netcam_image_conv(netcam_context_ptr netcam,
  360                                struct jpeg_decompress_struct *cinfo,
  361                                 struct image_data *img_data)
  362 {
  363     JSAMPARRAY      line;           /* Array of decomp data lines */
  364     unsigned char  *wline;          /* Will point to line[0] */
  365     /* Working variables */
  366     int             linesize, i;
  367     unsigned char  *upic, *vpic;
  368     unsigned char  *pic = img_data->image_norm;
  369     unsigned char   y;              /* Switch for decoding YUV data */
  370     unsigned int    width, height;
  371 
  372     width = cinfo->output_width;
  373     height = cinfo->output_height;
  374 
  375     if (width && ((width != netcam->width) || (height != netcam->height))) {
  376         MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
  377             ,_("JPEG image size %dx%d, JPEG was %dx%d")
  378             ,netcam->width, netcam->height, width, height);
  379         jpeg_destroy_decompress(cinfo);
  380         netcam->jpeg_error |= 4;
  381         return netcam->jpeg_error;
  382     }
  383     /* Set the output pointers (these come from YUV411P definition. */
  384     upic = pic + width * height;
  385     vpic = upic + (width * height) / 4;
  386 
  387 
  388     /* YCbCr format will give us one byte each for YUV. */
  389     linesize = cinfo->output_width * 3;
  390 
  391     /* Allocate space for one line. */
  392     line = (cinfo->mem->alloc_sarray)((j_common_ptr) cinfo, JPOOL_IMAGE,
  393                                        cinfo->output_width * cinfo->output_components, 1);
  394 
  395     wline = line[0];
  396     y = 0;
  397 
  398     while (cinfo->output_scanline < height) {
  399         jpeg_read_scanlines(cinfo, line, 1);
  400 
  401         for (i = 0; i < linesize; i += 3) {
  402             pic[i / 3] = wline[i];
  403             if (i & 1) {
  404                 upic[(i / 3) / 2] = wline[i + 1];
  405                 vpic[(i / 3) / 2] = wline[i + 2];
  406             }
  407         }
  408 
  409         pic += linesize / 3;
  410 
  411         if (y++ & 1) {
  412             upic += width / 2;
  413             vpic += width / 2;
  414         }
  415     }
  416 
  417     jpeg_finish_decompress(cinfo);
  418     jpeg_destroy_decompress(cinfo);
  419 
  420     rotate_map(netcam->cnt, img_data);
  421 
  422     if (netcam->jpeg_error)
  423         MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO,_("jpeg_error %d"), netcam->jpeg_error);
  424 
  425     return netcam->jpeg_error;
  426 }
  427 
  428 /**
  429  * netcam_proc_jpeg
  430  *
  431  *    Routine to decode an image received from a netcam into a YUV420P buffer
  432  *    suitable for processing by motion.
  433  *
  434  * Parameters:
  435  *    netcam    pointer to the netcam_context structure.
  436  *     image    pointer to a buffer for the returned image.
  437  *
  438  * Returns:
  439  *
  440  *      0         Success
  441  *      non-zero  error code from other routines
  442  *                (e.g. netcam_init_jpeg or netcam_image_conv)
  443  *                or just NETCAM_GENERAL_ERROR
  444  */
  445 int netcam_proc_jpeg(netcam_context_ptr netcam,  struct image_data *img_data)
  446 {
  447     struct jpeg_decompress_struct cinfo;    /* Decompression control struct. */
  448     int retval = 0;                         /* Value returned to caller. */
  449     int ret;                                /* Working var. */
  450 
  451     /*
  452      * This routine is only called from the main thread.
  453      * We need to "protect" the "latest" image while we
  454      * decompress it.  netcam_init_jpeg uses
  455      * netcam->mutex to do this.
  456      */
  457     MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO
  458         ,_("processing jpeg image - content length %d"), netcam->latest->content_length);
  459 
  460     ret = netcam_init_jpeg(netcam, &cinfo);
  461 
  462     if (ret != 0) {
  463         MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("return code %d"), ret);
  464         return ret;
  465     }
  466 
  467     /*
  468      * Do a sanity check on dimensions
  469      * If dimensions have changed we throw an
  470      * error message that will cause
  471      * restart of Motion.
  472      */
  473     if (netcam->width) {    /* 0 means not yet init'ed */
  474         if ((cinfo.output_width != netcam->width) ||
  475             (cinfo.output_height != netcam->height)) {
  476             retval = NETCAM_RESTART_ERROR;
  477             MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
  478                 ,_("Camera width/height mismatch with JPEG image - "
  479                 " expected %dx%d, JPEG %dx%d retval %d")
  480                 ,netcam->width, netcam->height
  481                 ,cinfo.output_width, cinfo.output_height, retval);
  482             return retval;
  483         }
  484     }
  485 
  486     /* Do the conversion */
  487     ret = netcam_image_conv(netcam, &cinfo,  img_data);
  488 
  489     if (ret != 0) {
  490         retval |= NETCAM_JPEG_CONV_ERROR;
  491         MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
  492             ,_("ret %d retval %d"), ret, retval);
  493     }
  494 
  495     return retval;
  496 }
  497 
  498 /**
  499   * netcam_fix_jpeg_header
  500   *
  501   *    Routine to decode an image received from a netcam into a YUV420P buffer
  502   *    suitable for processing by motion.
  503   *
  504   * Parameters:
  505   *    netcam    pointer to the netcam_context structure
  506   *
  507   * Returns:  Nothing
  508   *
  509   */
  510 void netcam_fix_jpeg_header(netcam_context_ptr netcam)
  511 {
  512     char *ptr_buffer;
  513 
  514     ptr_buffer = memmem(netcam->receiving->ptr, netcam->receiving->used, "\xff\xd8", 2);
  515 
  516     if (ptr_buffer != NULL) {
  517         size_t soi_position = 0;
  518 
  519         soi_position = ptr_buffer - netcam->receiving->ptr;
  520 
  521         if (soi_position > 0) {
  522             memmove(netcam->receiving->ptr, netcam->receiving->ptr + soi_position,
  523                     netcam->receiving->used - soi_position);
  524             netcam->receiving->used -= soi_position;
  525         }
  526 
  527         // if (debug_level > CAMERA_INFO)
  528         //    motion_log(LOG_INFO, 0, "SOI found , position %d",
  529         //               __FUNCTION__, soi_position);
  530     }
  531 }
  532 
  533 
  534 /**
  535  * netcam_get_dimensions
  536  *
  537  *    This function gets the height and width of the JPEG image
  538  *    located in the supplied netcam_image_buffer.
  539  *
  540  * Parameters
  541  *
  542  *    netcam     pointer to the netcam context.
  543  *
  544  * Returns:   Nothing, but fills in width and height into context.
  545  *
  546  */
  547 void netcam_get_dimensions(netcam_context_ptr netcam)
  548 {
  549     struct jpeg_decompress_struct cinfo; /* Decompression control struct. */
  550     int ret;
  551 
  552     ret = netcam_init_jpeg(netcam, &cinfo);
  553 
  554     netcam->width = cinfo.output_width;
  555     netcam->height = cinfo.output_height;
  556     netcam->JFIF_marker = cinfo.saw_JFIF_marker;
  557 
  558     jpeg_destroy_decompress(&cinfo);
  559 
  560     MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "JFIF_marker %s PRESENT ret %d",
  561                netcam->JFIF_marker ? "IS" : "NOT", ret);
  562 }