修改代码
下面是修改之后的完整代码
/mjpg-streamer/mjpg-streamer/plugins/output_file.c
/******************************************************************************* # # # MJPG-streamer allows to stream JPG frames from an input-plugin # # to several output plugins # # # # Copyright (C) 2007 Tom St?veken # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; version 2 of the License. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the Free Software # # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # *******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <linux/videodev2.h> #include <sys/ioctl.h> #include <errno.h> #include <signal.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> #include <getopt.h> #include <pthread.h> #include <fcntl.h> #include <time.h> #include <syslog.h> #include <dirent.h> #include "../../utils.h" #include "../../mjpg_streamer.h" #define OUTPUT_PLUGIN_NAME "FILE output plugin" static pthread_t worker; static globals *pglobal; static int fd, delay, ringbuffer_size = -1, ringbuffer_exceed = 0, max_frame_size; static char *folder = "/tmp"; static unsigned char *frame = NULL; static char *command = NULL; static int input_number = 0; /****************************************************************************** Description.: print a help message Input Value.: - Return Value: - ******************************************************************************/ void help(void) { fprintf(stderr, " ---------------------------------------------------------------\n" \ " Help for output plugin..: "OUTPUT_PLUGIN_NAME"\n" \ " ---------------------------------------------------------------\n" \ " The following parameters can be passed to this plugin:\n\n" \ " [-f | --folder ]........: folder to save pictures\n" \ " [-m | --mjpeg ]........: save the stream to an mjpeg file\n" \ " [-d | --delay ].........: delay after saving pictures in ms\n" \ " [-s | --size ]..........: size of ring buffer (max number of pictures to hold)\n" \ " [-e | --exceed ]........: allow ringbuffer to exceed limit by this amount\n" \ " [-c | --command ].......: execute command after saving picture\n\n" \ " [-i | --input ].......: read frames from the specified input plugin\n\n" \ " ---------------------------------------------------------------\n"); } /****************************************************************************** Description.: clean up allocated ressources Input Value.: unused argument Return Value: - ******************************************************************************/ void worker_cleanup(void *arg) { static unsigned char first_run = 1; if(!first_run) { DBG("already cleaned up ressources\n"); return; } first_run = 0; OPRINT("cleaning up ressources allocated by worker thread\n"); if(frame != NULL) { free(frame); } close(fd); } /****************************************************************************** Description.: compares a directory entry with a pattern Input Value.: directory entry Return Value: 0 if string do not match, 1 if they match ******************************************************************************/ int check_for_filename(const struct dirent *entry) { int rc; int year, month, day, hour, minute, second; unsigned long long number; /* * try to scan the string using scanf * I would like to use a define for this format string later... */ rc = sscanf(entry->d_name, "%d_%d_%d_%d_%d_%d_picture_%09llu.jpg", &year, \ &month, \ &day, \ &hour, \ &minute, \ &second, \ &number); DBG("%s, rc is %d (%d, %d, %d, %d, %d, %d, %llu)\n", entry->d_name, \ rc, \ year, \ month, \ day, \ hour, \ minute, \ second, \ number); /* if scanf could find all values, it matches our filenames */ if(rc != 7) return 0; return 1; } /****************************************************************************** Description.: delete oldest files, just keep "size" most recent files This funtion MAY delete the wrong files if the time is not valid Input Value.: how many files to keep Return Value: - ******************************************************************************/ void maintain_ringbuffer(int size) { struct dirent **namelist; int n, i; char buffer[1<<16]; /* do nothing if ringbuffer is not set or wrong value is set */ if(size < 0) return; /* get a sorted list of directory items */ n = scandir(folder, &namelist, check_for_filename, alphasort); if(n < 0) { perror("scandir"); return; } DBG("found %d directory entries\n", n); /* delete the first (thus oldest) number of files */ for(i = 0; i < (n - size); i++) { /* put together the folder name and the directory item */ snprintf(buffer, sizeof(buffer), "%s/%s", folder, namelist[i]->d_name); DBG("delete: %s\n", buffer); /* mark item for deletion */ if(unlink(buffer) == -1) { perror("could not delete file"); } /* free allocated memory for name */ free(namelist[i]); } /* keep the rest, but we still have to free every result */ for(i = MAX(n - size, 0); i < n; i++) { DBG("keep: %s\n", namelist[i]->d_name); free(namelist[i]); } /* free last just allocated ressources */ free(namelist); } /****************************************************************************** Description.: this is the main worker thread it loops forever, grabs a fresh frame and stores it to file Input Value.: Return Value: ******************************************************************************/ void *worker_thread(void *arg) { int ok = 1, frame_size = 0, rc = 0; char buffer1[1024] = {0}, buffer2[1024] = {0}; unsigned long long counter = 0; time_t t; struct tm *now; unsigned char *tmp_framebuffer = NULL; char buf[10]; //用于存放从管道读取的命令 int flags = 0; //拍照标志,1:表示11张照片,2:表示1张照片 int fd_com = 0; //打开管道的文件描述符 int stop_num = 0; //拍照计数 if ( access("/tmp/webcom",F_OK) < 0 ) //创建有名管道用于接收拍照命令 { if ( mkfifo("/tmp/webcom",0666 ) < 0) { printf("photo fifo create failed\n"); } } fd_com = open ("/tmp/webcom",O_RDONLY,0666); if (fd_com < 0) { perror ("open the file webcom error"); } /* set cleanup handler to cleanup allocated ressources */ pthread_cleanup_push(worker_cleanup, NULL); while(ok >= 0 && !pglobal->stop) { if (flags == 0) { while(1) { read(fd_com,buf,sizeof(buf)); if (strncmp(buf,"danger",6) == 0) //拍11张照片 { flags = 1; bzero(buf,sizeof(buf)); break; } if (strncmp(buf,"one",3) == 0) //拍1张照片 { flags = 2; bzero(buf,sizeof(buf)); break; } } } DBG("waiting for fresh frame\n"); pthread_mutex_lock(&pglobal->in[input_number].db); pthread_cond_wait(&pglobal->in[input_number].db_update, &pglobal->in[input_number].db); /* read buffer */ frame_size = pglobal->in[input_number].size; /* check if buffer for frame is large enough, increase it if necessary */ if(frame_size > max_frame_size) { DBG("increasing buffer size to %d\n", frame_size); max_frame_size = frame_size + (1 << 16); if((tmp_framebuffer = realloc(frame, max_frame_size)) == NULL) { pthread_mutex_unlock(&pglobal->in[input_number].db); LOG("not enough memory\n"); return NULL; } frame = tmp_framebuffer; } /* copy frame to our local buffer now */ memcpy(frame, pglobal->in[input_number].buf, frame_size); /* allow others to access the global buffer again */ pthread_mutex_unlock(&pglobal->in[input_number].db); /* prepare filename */ memset(buffer1, 0, sizeof(buffer1)); memset(buffer2, 0, sizeof(buffer2)); /* get current time */ t = time(NULL); now = localtime(&t); if(now == NULL) { perror("localtime"); return NULL; } /* prepare string, add time and date values */ if(strftime(buffer1, sizeof(buffer1), "%%s/%Y_%m_%d_%H_%M_%S_picture_%%09llu.jpg", now) == 0) { OPRINT("strftime returned 0\n"); free(frame); frame = NULL; return NULL; } /* finish filename by adding the foldername and a counter value */ snprintf(buffer2, sizeof(buffer2), buffer1, folder, counter); counter++; DBG("writing file: %s\n", buffer2); /* open file for write */ if((fd = open(buffer2, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { OPRINT("could not open the file %s\n", buffer2); return NULL; } /* save picture to file */ if(write(fd, frame, frame_size) < 0) { OPRINT("could not write to file %s\n", buffer2); perror("write()"); close(fd); return NULL; } close(fd); /* call the command if user specified one, pass current filename as argument */ if(command != NULL) { memset(buffer1, 0, sizeof(buffer1)); /* buffer2 still contains the filename, pass it to the command as parameter */ snprintf(buffer1, sizeof(buffer1), "%s \"%s\"", command, buffer2); DBG("calling command %s", buffer1); /* in addition provide the filename as environment variable */ if((rc = setenv("MJPG_FILE", buffer2, 1)) != 0) { LOG("setenv failed (return value %d)\n", rc); } /* execute the command now */ if((rc = system(buffer1)) != 0) { LOG("command failed (return value %d)\n", rc); } } /* * maintain ringbuffer * do not maintain ringbuffer for each picture, this saves ressources since * each run of the maintainance function involves sorting/malloc/free operations */ if(ringbuffer_exceed <= 0) { /* keep ringbuffer excactly at specified size */ maintain_ringbuffer(ringbuffer_size); } else if(counter == 1 || counter % (ringbuffer_exceed + 1) == 0) { DBG("counter: %llu, will clean-up now\n", counter); maintain_ringbuffer(ringbuffer_size); } /* if specified, wait now */ if(delay > 0) { usleep(1000 * delay); } stop_num++; if (flags == 1) //判断拍照的数量 { if ( stop_num > 9) { stop_num = 0; flags = 0; } } else if (flags == 2) { stop_num = 0; flags = 0; } } /* cleanup now */ pthread_cleanup_pop(1); return NULL; } /*** plugin interface functions ***/ /****************************************************************************** Description.: this function is called first, in order to initialize this plugin and pass a parameter string Input Value.: parameters Return Value: 0 if everything is OK, non-zero otherwise ******************************************************************************/ int output_init(output_parameter *param) { int i; delay = 0; param->argv[0] = OUTPUT_PLUGIN_NAME; /* show all parameters for DBG purposes */ for(i = 0; i < param->argc; i++) { DBG("argv[%d]=%s\n", i, param->argv[i]); } reset_getopt(); while(1) { int option_index = 0, c = 0; static struct option long_options[] = { {"h", no_argument, 0, 0 }, {"help", no_argument, 0, 0}, {"f", required_argument, 0, 0}, {"folder", required_argument, 0, 0}, {"d", required_argument, 0, 0}, {"delay", required_argument, 0, 0}, {"s", required_argument, 0, 0}, {"size", required_argument, 0, 0}, {"e", required_argument, 0, 0}, {"exceed", required_argument, 0, 0}, {"c", required_argument, 0, 0}, {"command", required_argument, 0, 0}, {"m", required_argument, 0, 0}, {"mjpeg", required_argument, 0, 0}, {"i", required_argument, 0, 0}, {"input", required_argument, 0, 0}, {0, 0, 0, 0} }; c = getopt_long_only(param->argc, param->argv, "", long_options, &option_index); /* no more options to parse */ if(c == -1) break; /* unrecognized option */ if(c == '?') { help(); return 1; } switch(option_index) { /* h, help */ case 0: case 1: DBG("case 0,1\n"); help(); return 1; break; /* f, folder */ case 2: case 3: DBG("case 2,3\n"); folder = malloc(strlen(optarg) + 1); strcpy(folder, optarg); if(folder[strlen(folder)-1] == '/') folder[strlen(folder)-1] = '\0'; break; /* d, delay */ case 4: case 5: DBG("case 4,5\n"); delay = atoi(optarg); break; /* s, size */ case 6: case 7: DBG("case 6,7\n"); ringbuffer_size = atoi(optarg); break; /* e, exceed */ case 8: case 9: DBG("case 8,9\n"); ringbuffer_exceed = atoi(optarg); break; /* c, command */ case 10: case 11: DBG("case 10,11\n"); command = strdup(optarg); break; case 12: case 13: DBG("case 12,13\n"); input_number = atoi(optarg); break; } } pglobal = param->global; if(!(input_number < pglobal->incnt)) { OPRINT("ERROR: the %d input_plugin number is too much only %d plugins loaded\n", input_number, pglobal->incnt); return 1; } OPRINT("output folder.....: %s\n", folder); OPRINT("input plugin.....: %d: %s\n", input_number, pglobal->in[input_number].plugin); OPRINT("delay after save..: %d\n", delay); if(ringbuffer_size > 0) { OPRINT("ringbuffer size...: %d to %d\n", ringbuffer_size, ringbuffer_size + ringbuffer_exceed); } else { OPRINT("ringbuffer size...: %s\n", "no ringbuffer"); } OPRINT("command...........: %s\n", (command == NULL) ? "disabled" : command); return 0; } /****************************************************************************** Description.: calling this function stops the worker thread Input Value.: - Return Value: always 0 ******************************************************************************/ int output_stop(int id) { DBG("will cancel worker thread\n"); pthread_cancel(worker); return 0; } /****************************************************************************** Description.: calling this function creates and starts the worker thread Input Value.: - Return Value: always 0 ******************************************************************************/ int output_run(int id) { DBG("launching worker thread\n"); pthread_create(&worker, 0, worker_thread, NULL); pthread_detach(worker); return 0; } int output_cmd(int plugin, unsigned int control_id, unsigned int group, int value) { DBG("command (%d, value: %d) for group %d triggered for plugin instance #%02d\n", control_id, value, group, plugin); return 0; }
2.修改后测试
先重新编译
make
如果出现make: Nothing to be done for ‘all’.那么先清理掉之前编译的内容,然后再编译
make clean
make
执行脚本文件start.sh
sudo ./start.sh
重新打开一个终端来实现拍照功能
单拍
sudo echo one > /tmp/webcom
连拍
echo danger > /tmp/webcom
拍照保存的照片位于/www/pice
拍照成功,连拍时,由于我的电脑性能,及其网络环境,连拍太卡,实际连拍一次拍10多张,我这因配置没能体现。