"Fossies" - the Fresh Open Source Software Archive

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

    1 /*
    2  *    webu_stream.c
    3  *
    4  *    Create the web streams for Motion
    5  *
    6  *    This software is distributed under the GNU Public License Version 2
    7  *    See also the file 'COPYING'.
    8  *
    9  *    Functional naming scheme
   10  *    webu_stream*      - All functions in this module
   11  *    webu_stream_mjpeg*    - Create the motion-jpeg stream for the user
   12  *    webu_stream_static*   - Create the static jpg image for the user.
   13  *    webu_stream_checks    - Edit/validate request from user
   14  */
   15 
   16 #include "motion.h"
   17 #include "webu.h"
   18 #include "webu_stream.h"
   19 #include "translate.h"
   20 
   21 static void webu_stream_mjpeg_checkbuffers(struct webui_ctx *webui) {
   22     /* Allocate buffers if needed */
   23     if (webui->resp_size < (size_t)webui->cnt->imgs.size_norm){
   24         if (webui->resp_page   != NULL) free(webui->resp_page);
   25         webui->resp_page   = mymalloc(webui->cnt->imgs.size_norm);
   26         memset(webui->resp_page,'\0',webui->cnt->imgs.size_norm);
   27         webui->resp_size = webui->cnt->imgs.size_norm;
   28         webui->resp_used = 0;
   29     }
   30 
   31 }
   32 
   33 static void webu_stream_mjpeg_delay(struct webui_ctx *webui) {
   34     /* Sleep required time to get to the user requested frame
   35      * rate for the stream
   36      */
   37 
   38     long   stream_rate;
   39     struct timeval time_curr;
   40     long   stream_delay;
   41 
   42     gettimeofday(&time_curr, NULL);
   43 
   44     /* The stream rate MUST be less than 1000000000 otherwise undefined behaviour
   45      * will occur with the SLEEP function.
   46      */
   47     stream_delay = ((time_curr.tv_usec - webui->time_last.tv_usec)*1000) +
   48         ((time_curr.tv_sec - webui->time_last.tv_sec)*1000000000);
   49     if (stream_delay < 0)  stream_delay = 0;
   50     if (stream_delay > 1000000000 ) stream_delay = 1000000000;
   51 
   52     if (webui->stream_fps >= 1){
   53         stream_rate = ( (1000000000 / webui->stream_fps) - stream_delay);
   54         if ((stream_rate > 0) && (stream_rate < 1000000000)){
   55             SLEEP(0,stream_rate);
   56         } else if (stream_rate == 1000000000) {
   57             SLEEP(1,0);
   58         }
   59     }
   60     gettimeofday(&webui->time_last, NULL);
   61 
   62 }
   63 
   64 static void webu_stream_mjpeg_getimg(struct webui_ctx *webui) {
   65     long jpeg_size;
   66     char resp_head[80];
   67     int  header_len;
   68     struct stream_data *local_stream;
   69 
   70     memset(webui->resp_page, '\0', webui->resp_size);
   71 
   72     /* Assign to a local pointer the stream we want */
   73     if (webui->cnct_type == WEBUI_CNCT_FULL){
   74         local_stream = &webui->cnt->stream_norm;
   75 
   76     } else if (webui->cnct_type == WEBUI_CNCT_SUB){
   77         local_stream = &webui->cnt->stream_sub;
   78 
   79     } else if (webui->cnct_type == WEBUI_CNCT_MOTION){
   80         local_stream = &webui->cnt->stream_motion;
   81 
   82     } else if (webui->cnct_type == WEBUI_CNCT_SOURCE){
   83         local_stream = &webui->cnt->stream_source;
   84 
   85     } else {
   86         return;
   87     }
   88 
   89     /* Copy jpg from the motion loop thread */
   90     pthread_mutex_lock(&webui->cnt->mutex_stream);
   91         if ((!webui->cnt->detecting_motion) && (webui->cnt->conf.stream_motion)){
   92             webui->stream_fps = 1;
   93         } else {
   94             webui->stream_fps = webui->cnt->conf.stream_maxrate;
   95         }
   96         if (local_stream->jpeg_data == NULL) {
   97             pthread_mutex_unlock(&webui->cnt->mutex_stream);
   98             return;
   99         }
  100         jpeg_size = local_stream->jpeg_size;
  101         header_len = snprintf(resp_head, 80
  102             ,"--BoundaryString\r\n"
  103             "Content-type: image/jpeg\r\n"
  104             "Content-Length: %9ld\r\n\r\n"
  105             ,jpeg_size);
  106         memcpy(webui->resp_page, resp_head, header_len);
  107         memcpy(webui->resp_page + header_len
  108             ,local_stream->jpeg_data
  109             ,jpeg_size);
  110         /* Copy in the terminator after the jpg data at the end*/
  111         memcpy(webui->resp_page + header_len + jpeg_size,"\r\n",2);
  112         webui->resp_used = header_len + jpeg_size + 2;
  113     pthread_mutex_unlock(&webui->cnt->mutex_stream);
  114 
  115 }
  116 
  117 static ssize_t webu_stream_mjpeg_response (void *cls, uint64_t pos, char *buf, size_t max){
  118     /* This is the callback response function for MHD streams.  It is kept "open" and
  119      * in process during the entire time that the user has the stream open in the web
  120      * browser.  We sleep the requested amount of time between fetching images to match
  121      * the user configuration parameters.  This function may be called multiple times for
  122      * a single image so we can write what we can to the buffer and pick up remaining bytes
  123      * to send based upon the stream position
  124      */
  125     struct webui_ctx *webui = cls;
  126     size_t sent_bytes;
  127 
  128     (void)pos;  /*Remove compiler warning */
  129 
  130     if (webui->cnt->webcontrol_finish) return -1;
  131 
  132     if ((webui->stream_pos == 0) || (webui->resp_used == 0)){
  133 
  134         webu_stream_mjpeg_delay(webui);
  135 
  136         webui->stream_pos = 0;
  137         webui->resp_used = 0;
  138 
  139         webu_stream_mjpeg_getimg(webui);
  140 
  141         if (webui->resp_used == 0) return 0;
  142     }
  143 
  144     if ((webui->resp_used - webui->stream_pos) > max) {
  145         sent_bytes = max;
  146     } else {
  147         sent_bytes = webui->resp_used - webui->stream_pos;
  148     }
  149 
  150     memcpy(buf, webui->resp_page + webui->stream_pos, sent_bytes);
  151 
  152     webui->stream_pos = webui->stream_pos + sent_bytes;
  153     if (webui->stream_pos >= webui->resp_used){
  154         webui->stream_pos = 0;
  155     }
  156 
  157     return sent_bytes;
  158 
  159 }
  160 
  161 static void webu_stream_static_getimg(struct webui_ctx *webui) {
  162     /* Obtain the current image, compress it to a JPG and put into webui->resp_page
  163      * for MHD to send back to user
  164      */
  165     webui->resp_used = 0;
  166 
  167     memset(webui->resp_page, '\0', webui->resp_size);
  168 
  169     pthread_mutex_lock(&webui->cnt->mutex_stream);
  170         if (webui->cnt->stream_norm.jpeg_data == NULL){
  171             pthread_mutex_unlock(&webui->cnt->mutex_stream);
  172             return;
  173         }
  174         memcpy(webui->resp_page
  175             ,webui->cnt->stream_norm.jpeg_data
  176             ,webui->cnt->stream_norm.jpeg_size);
  177         webui->resp_used = webui->cnt->stream_norm.jpeg_size;
  178     pthread_mutex_unlock(&webui->cnt->mutex_stream);
  179 
  180 }
  181 
  182 static int webu_stream_checks(struct webui_ctx *webui) {
  183     /* Perform edits to determine whether the user specified a valid URL
  184      * for the particular port
  185      */
  186     if ((webui->cntlst != NULL) && (webui->thread_nbr >= webui->cam_threads)){
  187         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
  188             , _("Invalid thread specified: %s"),webui->url);
  189         return -1;
  190     }
  191 
  192     if ((webui->cntlst != NULL) && (webui->thread_nbr < 0) && (webui->cam_threads > 1)){
  193         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
  194             , _("Invalid thread specified: %s"),webui->url);
  195         return -1;
  196     }
  197 
  198     /* Thread numbers are not used for context specific ports. */
  199     if ((webui->cntlst == NULL) && (webui->thread_nbr >= 0)) {
  200         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
  201             , _("Invalid URL for a camera specific port: %s"),webui->url);
  202         return -1;
  203     }
  204 
  205     /* If multiple threads then thread zero is invalid. */
  206     if ((webui->cam_threads > 1) && (webui->thread_nbr == 0)) {
  207         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
  208             , _("URL for thread 0 is not valid when using camera specific files.: %s")
  209             ,webui->url);
  210         return -1;
  211     }
  212 
  213     /* Thread numbers are not used for context specific ports. */
  214     if ((webui->cntlst == NULL) && (strlen(webui->uri_cmd1) > 0)) {
  215         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
  216             , _("Bad URL for a camera specific port: %s"),webui->url);
  217         return -1;
  218     }
  219 
  220     return 0;
  221 }
  222 
  223 static void webu_stream_cnct_count(struct webui_ctx *webui) {
  224     /* Increment the counters for the connections to the streams */
  225     int cnct_count;
  226 
  227     cnct_count = 0;
  228     if (webui->cnct_type == WEBUI_CNCT_SUB) {
  229         pthread_mutex_lock(&webui->cnt->mutex_stream);
  230             webui->cnt->stream_sub.cnct_count++;
  231             cnct_count = webui->cnt->stream_sub.cnct_count;
  232         pthread_mutex_unlock(&webui->cnt->mutex_stream);
  233 
  234     } else if (webui->cnct_type == WEBUI_CNCT_MOTION) {
  235         pthread_mutex_lock(&webui->cnt->mutex_stream);
  236             webui->cnt->stream_motion.cnct_count++;
  237             cnct_count = webui->cnt->stream_motion.cnct_count;
  238         pthread_mutex_unlock(&webui->cnt->mutex_stream);
  239 
  240     } else if (webui->cnct_type == WEBUI_CNCT_SOURCE) {
  241         pthread_mutex_lock(&webui->cnt->mutex_stream);
  242             webui->cnt->stream_source.cnct_count++;
  243             cnct_count = webui->cnt->stream_source.cnct_count;
  244         pthread_mutex_unlock(&webui->cnt->mutex_stream);
  245 
  246     } else {
  247         /* Stream, Static */
  248         pthread_mutex_lock(&webui->cnt->mutex_stream);
  249             webui->cnt->stream_norm.cnct_count++;
  250             cnct_count = webui->cnt->stream_norm.cnct_count;
  251         pthread_mutex_unlock(&webui->cnt->mutex_stream);
  252     }
  253 
  254     if (cnct_count == 1){
  255         /* This is the first connection so we need to wait half a sec
  256          * so that the motion loop on the other thread can update image
  257          */
  258         SLEEP(0,500000000L);
  259     }
  260 
  261 }
  262 
  263 int webu_stream_mjpeg(struct webui_ctx *webui) {
  264     /* Create the stream for the motion jpeg */
  265     int retcd;
  266     struct MHD_Response *response;
  267 
  268     if (webu_stream_checks(webui) == -1) return MHD_NO;
  269 
  270     webu_stream_cnct_count(webui);
  271 
  272     webu_stream_mjpeg_checkbuffers(webui);
  273 
  274     gettimeofday(&webui->time_last, NULL);
  275 
  276     response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024
  277         ,&webu_stream_mjpeg_response, webui, NULL);
  278     if (!response){
  279         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Invalid response"));
  280         return MHD_NO;
  281     }
  282 
  283     if (webui->cnt->conf.stream_cors_header != NULL){
  284         MHD_add_response_header (response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
  285             , webui->cnt->conf.stream_cors_header);
  286     }
  287 
  288     MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE
  289         , "multipart/x-mixed-replace; boundary=BoundaryString");
  290 
  291     retcd = MHD_queue_response (webui->connection, MHD_HTTP_OK, response);
  292     MHD_destroy_response (response);
  293 
  294     return retcd;
  295 }
  296 
  297 int webu_stream_static(struct webui_ctx *webui) {
  298     /* Create the response for the static image request*/
  299     int retcd;
  300     struct MHD_Response *response;
  301     char resp_used[20];
  302 
  303     if (webu_stream_checks(webui) == -1) return MHD_NO;
  304 
  305     webu_stream_cnct_count(webui);
  306 
  307     webu_stream_mjpeg_checkbuffers(webui);
  308 
  309     webu_stream_static_getimg(webui);
  310 
  311     if (webui->resp_used == 0) {
  312         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Could not get image to stream."));
  313         return MHD_NO;
  314     }
  315 
  316     response = MHD_create_response_from_buffer (webui->resp_size
  317         ,(void *)webui->resp_page, MHD_RESPMEM_MUST_COPY);
  318     if (!response){
  319         MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Invalid response"));
  320         return MHD_NO;
  321     }
  322 
  323     if (webui->cnt->conf.stream_cors_header != NULL){
  324         MHD_add_response_header (response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
  325             , webui->cnt->conf.stream_cors_header);
  326     }
  327 
  328     MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "image/jpeg;");
  329     snprintf(resp_used, 20, "%9ld\r\n\r\n",(long)webui->resp_used);
  330     MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_LENGTH, resp_used);
  331 
  332     retcd = MHD_queue_response (webui->connection, MHD_HTTP_OK, response);
  333     MHD_destroy_response (response);
  334 
  335     return retcd;
  336 }