motion源代码分析
来源:互联网 发布:mac dock 切换屏幕 编辑:程序博客网 时间:2024/05/17 03:42
转自:http://blog.csdn.net/sakaue/article/details/22816475
楔子
前几天研究了如何将ffmpeg编入motion,并实现录像功能。现在研究下motion的工作流程。
几个主要模块
motion.c主程序,视频采集编码主循环ffmpeg.c一个代理模块,封装了ffmpeg的方法,根据v4l获取的数据编码录像video.2.c提供video4linux功能video_common.c封装video.2.c中功能,为motion.c提供支持vebhttpd.c向客户端提供控制功能webcam.c向客户端提供实时图像服务(浏览器刷图片)
正文
照例从main函数进入。在这里,main起了两个线程,一个是集视频采集录像放送于一体的motion_loop:
-
-
-
- for (i = cnt_list[1] != NULL ? 1 : 0; cnt_list[i]; i++) {
-
- 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);
- }
一个是用于web控制的motion_web_control:-
-
-
- if (cnt_list[0]->conf.control_port)
- pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list);
而main中的迭代主要负责一些统计任务。我们主要关心motion的核心——motion_loop。
刷照片的效果实在太挫了,让我们看看如何打开ffmpeg录像的配置。这里需要修改motion-dist.conf中的两个选项(采用默认值则不会录像):
- # Use ffmpeg to encode a timelapse movie
- # Default value 0 = off - else save frame every Nth second
- ffmpeg_timelapse 1
-
-
- # Enables and defines variable bitrate for the ffmpeg encoder.
- # ffmpeg_bps is ignored if variable bitrate is enabled.
- # Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps,
- # or the range 2 - 31 where 2 means best quality and 31 is worst.
- ffmpeg_variable_bitrate 1
第一个选项是设置每一秒保存帧(帧的数量由配置文件中的framerate决定 ),第二个选项设置ffmpeg编码的比特率。下面还有个选项:- # Codec to used by ffmpeg for the video compression.
- # Timelapse mpegs are always made in mpeg1 format independent from this option.
- # Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4.
- # mpeg1 - gives you files with extension .mpg
- # mpeg4 or msmpeg4 - gives you files with extension .avi
- # msmpeg4 is recommended for use with Windows Media Player because
- # it requires no installation of codec on the Windows client.
- # swf - gives you a flash film with extension .swf
- # flv - gives you a flash video with extension .flv
- # ffv1 - FF video codec 1 for Lossless Encoding ( experimental )
- # mov - QuickTime ( testing )
- ffmpeg_video_codec mpeg4
这个其实该不该无所谓,因为编码器的格式在里面是写死的,稍后会提到。motion的录像在motion_loop(motion.c:1698)当中:
- #ifdef HAVE_FFMPEG
-
-
-
- if (cnt->conf.timelapse) {
-
-
-
-
-
- if (cnt->current_image->timestamp_tm.tm_min == 0 &&
- (time_current_frame % 60 < time_last_frame % 60) &&
- cnt->shots == 0) {
-
- if (strcasecmp(cnt->conf.timelapse_mode,"manual") == 0) {
- ;
-
-
- } else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) {
- if (cnt->current_image->timestamp_tm.tm_hour == 0)
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL,
- &cnt->current_image->timestamp_tm);
-
-
- } else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) {
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL,
- &cnt->current_image->timestamp_tm);
-
-
- } else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-sunday") == 0) {
- if (cnt->current_image->timestamp_tm.tm_wday == 0 && cnt->current_image->timestamp_tm.tm_hour == 0)
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm);
-
-
- } else if (strcasecmp(cnt->conf.timelapse_mode, "weekly-monday") == 0) {
- if (cnt->current_image->timestamp_tm.tm_wday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0)
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm);
-
-
- } else if (strcasecmp(cnt->conf.timelapse_mode, "monthly") == 0) {
- if (cnt->current_image->timestamp_tm.tm_mday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0)
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm);
-
-
- } else {
- motion_log(LOG_ERR, 0, "Invalid timelapse_mode argument '%s'",
- cnt->conf.timelapse_mode);
- motion_log(LOG_ERR, 0, "Defaulting to manual timelapse mode");
- conf_cmdparse(&cnt, (char *)"ffmpeg_timelapse_mode",(char *)"manual");
- }
- }
-
-
-
-
- if (cnt->shots == 0 &&
- time_current_frame % cnt->conf.timelapse <= time_last_frame % cnt->conf.timelapse)
- event(cnt, EVENT_TIMELAPSE, cnt->current_image->image, NULL, NULL,
- &cnt->current_image->timestamp_tm);
- } else if (cnt->ffmpeg_timelapse) {
-
-
-
-
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);
- }
-
- #endif /* HAVE_FFMPEG */
执行录制视频的方法在1757行,根据EVENT_TIMELAPSE,它对应的方法叫event_ffmpeg_timelapse-
-
- {
- EVENT_TIMELAPSE,
- event_ffmpeg_timelapse
- },
-
-
- static void event_ffmpeg_timelapse(struct context *cnt,
- int type ATTRIBUTE_UNUSED, unsigned char *img,
- char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED,
- struct tm *currenttime_tm)
- {
- int width = cnt->imgs.width;
- int height = cnt->imgs.height;
- unsigned char *convbuf, *y, *u, *v;
-
- if (!cnt->ffmpeg_timelapse) {
- char tmp[PATH_MAX];
- const char *timepath;
-
-
-
- if (cnt->conf.timepath)
- timepath = cnt->conf.timepath;
- else
- timepath = DEF_TIMEPATH;
-
- mystrftime(cnt, tmp, sizeof(tmp), timepath, currenttime_tm, NULL, 0);
-
-
- snprintf(cnt->timelapsefilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, tmp);
-
- if (cnt->imgs.type == VIDEO_PALETTE_GREY) {
- convbuf = mymalloc((width * height) / 2);
- y = img;
- u = convbuf;
- v = convbuf+(width * height) / 4;
- grey2yuv420p(u, v, width, height);
- } else {
- convbuf = NULL;
- y = img;
- u = img + width * height;
- v = u + (width * height) / 4;
- }
-
- if ((cnt->ffmpeg_timelapse =
- ffmpeg_open((char *)TIMELAPSE_CODEC, cnt->timelapsefilename, y, u, v,
- cnt->imgs.width, cnt->imgs.height, 24, cnt->conf.ffmpeg_bps,
- cnt->conf.ffmpeg_vbr)) == NULL) {
- motion_log(LOG_ERR, 1, "ffopen_open error creating (timelapse) file [%s]", cnt->timelapsefilename);
- cnt->finish = 1;
- return;
- }
-
- cnt->ffmpeg_timelapse->udata = convbuf;
- event(cnt, EVENT_FILECREATE, NULL, cnt->timelapsefilename, (void *)FTYPE_MPEG_TIMELAPSE, NULL);
- }
-
- y = img;
-
- if (cnt->imgs.type == VIDEO_PALETTE_GREY)
- u = cnt->ffmpeg_timelapse->udata;
- else
- u = img + width * height;
-
- v = u + (width * height) / 4;
- ffmpeg_put_other_image(cnt->ffmpeg_timelapse, y, u, v);
-
- }
其中Line:500,如果是第一次进入,则执行ffmpeg_open:- ffmpeg_open((char *)TIMELAPSE_CODEC,
- cnt->timelapsefilename,
- y, u, v,
- cnt->imgs.width, cnt->imgs.height,
- 24,
- cnt->conf.ffmpeg_bps,
- cnt->conf.ffmpeg_vbr)) == NULL)
如果不是第一次进入,则会直接执行ffmpeg_put_other_image将图像加入录像。