【原创】动态图像监测开源代码 motion 学习 ----- Motion源码分析(1)
经过这两天对motion源码的分析,发现关于motion代码分析的资料很少,现在终于看出来点儿意思了。因为motion源码文件不是太多,所以直接从main函数入手。
分析工具:Source Insight。
main函数位于源码文件motion.c中。下面是main函数的源代码。
/**
* main
*
* Main entry point of Motion. Launches all the motion threads and contains
* the logic for starting up, restarting and cleaning up everything.
*
* Parameters:
*
* argc - size of argv
* argv - command-line options
*
* Returns: Motion exit status = 0 always
*/
int main (int argc, char **argv)
{
int i;
/* pthread_attr_t为线程属性结构体。 */
pthread_attr_t thread_attr;
/* pthread_t为线程id类型,打印时要用%u。 */
pthread_t thread_id;
/* Setup signals and do some initialization. 1 in the call to
* 'motion_startup' means that Motion will become a daemon if so has been
* requested, and argc and argc are necessary for reading the command
* line options.
设置信号并做一些初始化。motion_startup中的1表示Motion将以守护进程的方式
运行,前提是用户有这样的需要。另外,命令行选项需要argc。
*/
/*
sigaction为结构体,详细规定了信号处理函数和信号标志等信息。
sigaction()为信号处理函数。
*/
struct sigaction sig_handler_action;
struct sigaction sigchild_action;
setup_signals(&sig_handler_action, &sigchild_action);
motion_startup(1, argc, argv);
#ifdef HAVE_FFMPEG
/* FFMpeg initialization is only performed if FFMpeg support was found
* and not disabled during the configure phase.
*/
ffmpeg_init();
#endif /* HAVE_FFMPEG */
/* In setup mode, Motion is very communicative towards the user, which
* allows the user to experiment with the config parameters in order to
* optimize motion detection and stuff.
*/
if (cnt_list[0]->conf.setup_mode)
motion_log(-1, 0, "Motion running in setup mode.");
/* Create and a thread attribute for the threads we spawn later on.
* PTHREAD_CREATE_DETACHED means to create threads detached, i.e.
* their termination cannot be synchronized through 'pthread_join'.
*/
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
/* Create the TLS key for thread number. */
pthread_key_create(&tls_key_threadnr, NULL);
do {
if (restart) {
/* Handle the restart situation. Currently the approach is to
* cleanup everything, and then initialize everything again
* (including re-reading the config file(s)).
*/
motion_shutdown();
restart = 0; /* only one reset for now */
motion_log(LOG_INFO,0,"motion restarted");
#ifndef WITHOUT_V4L
SLEEP(5,0); // maybe some cameras needs less time
#endif
motion_startup(0, argc, argv); /* 0 = skip daemon init */
}
/* Start the motion threads. First 'cnt_list' item is global if 'thread'
* option is used, so start at 1 then and 0 otherwise.
*/
for (i = cnt_list[1] != NULL ? 1 : 0; cnt_list[i]; i++) {
/* If i is 0 it means no thread files and we then set the thread number to 1 */
cnt_list[i]->threadnr = i ? i : 1;
if (strcmp(cnt_list[i]->conf_filename,"") )
motion_log(LOG_INFO, 0, "Thread %d is from %s", cnt_list[i]->threadnr, cnt_list[i]->conf_filename );
if (cnt_list[0]->conf.setup_mode) {
motion_log(-1, 0, "Thread %d is device: %s input %d", cnt_list[i]->threadnr,
cnt_list[i]->conf.netcam_url ? cnt_list[i]->conf.netcam_url : cnt_list[i]->conf.video_device,
cnt_list[i]->conf.netcam_url ? -1 : cnt_list[i]->conf.input
);
}
if (cnt_list[0]->conf.setup_mode)
motion_log(LOG_ERR, 0, "Webcam port %d", cnt_list[i]->conf.webcam_port);
//线程处理函数入口
start_motion_thread(cnt_list[i], &thread_attr);
}
/* Create a thread for the control interface if requested. Create it
* detached and with 'motion_web_control' as the thread function.
*/
//创建一个线程,注册线程处理函数motion_web_control()
if (cnt_list[0]->conf.control_port)
pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list);
if (cnt_list[0]->conf.setup_mode)
motion_log(-1, 0,"Waiting for threads to finish, pid: %d", getpid());
/* Crude way of waiting for all threads to finish - check the thread
* counter (because we cannot do join on the detached threads).
*/
while (1) {
SLEEP(1,0);
/* Calculate how many threads runnig or wants to run
* if zero and we want to finish, break out
*/
int motion_threads_running = 0;
for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) {
if (cnt_list[i]->running || cnt_list[i]->restart)
motion_threads_running++;
}
if (((motion_threads_running == 0 ) && finish ) ||
((motion_threads_running == 0 ) && (threads_running == 0)) ){
if (debug_level >= CAMERA_DEBUG){
motion_log(LOG_INFO, 0, "DEBUG-1 threads_running %d motion_threads_running %d , finish %d",
threads_running, motion_threads_running, finish);
}
break;
}
for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) {
/* Check if threads wants to be restarted */
if ((!cnt_list[i]->running) && (cnt_list[i]->restart) ) {
motion_log(LOG_INFO, 0, "Motion thread %d restart", cnt_list[i]->threadnr);
start_motion_thread(cnt_list[i], &thread_attr);
}
if (cnt_list[i]->watchdog > WATCHDOG_OFF) {
cnt_list[i]->watchdog--;
if (cnt_list[i]->watchdog == 0) {
motion_log(LOG_ERR, 0, "Thread %d - Watchdog timeout, trying to do a graceful restart",
cnt_list[i]->threadnr);
cnt_list[i]->finish = 1;
}
if (cnt_list[i]->watchdog == -60) {
motion_log(LOG_ERR, 0, "Thread %d - Watchdog timeout, did NOT restart graceful,"
"killing it!", cnt_list[i]->threadnr);
pthread_cancel(cnt_list[i]->thread_id);
pthread_mutex_lock(&global_lock);
threads_running--;
pthread_mutex_unlock(&global_lock);
motion_cleanup(cnt_list[i]);
cnt_list[i]->running = 0;
cnt_list[i]->finish = 0;
}
}
}
if (debug_level >= CAMERA_DEBUG){
motion_log(LOG_INFO, 0, "DEBUG-2 threads_running %d motion_threads_running %d , finish %d",
threads_running, motion_threads_running, finish);
}
}
/* Reset end main loop flag */
finish = 0;
if (cnt_list[0]->conf.setup_mode)
motion_log(LOG_DEBUG, 0, "Threads finished");
/* Rest for a while if we're supposed to restart. */
if (restart)
SLEEP(2,0);
} while (restart); /* loop if we're supposed to restart */
// Be sure that http control exits fine
cnt_list[0]->finish = 1;
SLEEP(1,0);
motion_log(LOG_INFO, 0, "Motion terminating");
/* Perform final cleanup. */
pthread_key_delete(tls_key_threadnr);
pthread_attr_destroy(&thread_attr);
pthread_mutex_destroy(&global_lock);
motion_shutdown();
return 0;
}
代码流程:
从分析流程可知:
main函数在做一些必要的初始化之后,会创建两个重要的线程。
1、在函数start_motion_thread(cnt_list[i], &thread_attr)中调用
函数pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt);
进入static void *motion_loop(void *arg),正式进入motion_loop(),函数motion_loop()实现Motion的主要功能,如读取配置文件、采集图像、检测运动等。
2、pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list)创建第二个线程,进入web网页浏览视频控制函数void *motion_web_control(void *arg),然后函数motion_web_control()调用函数httpd_run(cnt),用以远程控制motion。(这个线程只有配置文件中对应的选项被设置才会执行)
关联博文 【原创】动态图像监测开源代码 motion 学习 ----- Motion源码分析(2)
将分析函数motion_loop()的执行流程和实现功能。