本文主要是介绍motion源代码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
转自: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:
[cpp] view plain copy
- /* 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); //在这里启动线程函数motion_loop
- }
一个是用于web控制的motion_web_control:
[cpp] view plain copy
- /* Create a thread for the control interface if requested. Create it
- * detached and with 'motion_web_control' as the thread function.
- */
- if (cnt_list[0]->conf.control_port)
- pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list); //启动线程函数motion_web_control
而main中的迭代主要负责一些统计任务。我们主要关心motion的核心——motion_loop。
刷照片的效果实在太挫了,让我们看看如何打开ffmpeg录像的配置。这里需要修改motion-dist.conf中的两个选项(采用默认值则不会录像):
[cpp] view plain copy
- # 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编码的比特率。下面还有个选项:
[cpp] view plain copy
- # 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)当中:
[cpp] view plain copy
- #ifdef HAVE_FFMPEG
- if (cnt->conf.timelapse) {
- /* Check to see if we should start a new timelapse file. We start one when
- * we are on the first shot, and and the seconds are zero. We must use the seconds
- * to prevent the timelapse file from getting reset multiple times during the minute.
- */
- 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) {
- ;/* No action */
- /* If we are daily, raise timelapseend event at midnight */
- } else if (strcasecmp(cnt->conf.timelapse_mode, "daily") == 0) { //根据配置文件中ffmpeg_timelapse_mode的选则执行相应代码
- if (cnt->current_image->timestamp_tm.tm_hour == 0)
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL,
- &cnt->current_image->timestamp_tm);
- /* handle the hourly case */
- } else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) {
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL,
- &cnt->current_image->timestamp_tm);
- /* If we are weekly-sunday, raise timelapseend event at midnight on sunday */
- } 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);
- /* If we are weekly-monday, raise timelapseend event at midnight on monday */
- } 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);
- /* If we are monthly, raise timelapseend event at midnight on first day of month */
- } 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);
- /* If invalid we report in syslog once and continue in manual mode */
- } 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 ffmpeg timelapse is enabled and time since epoch MOD ffmpeg_timelaps = 0
- * add a timelapse frame to the timelapse mpeg.
- */
- 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); // Line:1757 在这里创建并保存录制的视频
- } else if (cnt->ffmpeg_timelapse) {
- /* if timelapse mpeg is in progress but conf.timelapse is zero then close timelapse file
- * This is an important feature that allows manual roll-over of timelapse file using the http
- * remote control via a cron job.
- */
- event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm);
- }
- #endif /* HAVE_FFMPEG */
执行录制视频的方法在1757行,根据EVENT_TIMELAPSE,它对应的方法叫event_ffmpeg_timelapse
[cpp] view plain copy
- // event.c:673
- {
- EVENT_TIMELAPSE,
- event_ffmpeg_timelapse
- },
[cpp] view plain copy
- // event.c:461
- 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;
- /* conf.timepath would normally be defined but if someone deleted it by control interface
- it is better to revert to the default than fail */
- if (cnt->conf.timepath)
- timepath = cnt->conf.timepath;
- else
- timepath = DEF_TIMEPATH;
- mystrftime(cnt, tmp, sizeof(tmp), timepath, currenttime_tm, NULL, 0);
- /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */
- 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, //#define TIMELAPSE_CODEC "mpeg1_tl"
- 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); //put图像进录像
- }
其中Line:500,如果是第一次进入,则执行ffmpeg_open:
[cpp] view plain copy
- ffmpeg_open((char *)TIMELAPSE_CODEC, // #define TIMELAPSE_CODEC "mpeg1_tl"
- cnt->timelapsefilename, // 录像文件名
- y, u, v,//#define TIMELAPSE_CODEC "mpeg1_tl"
- cnt->imgs.width, cnt->imgs.height, // 录像的宽高
- 24, // fps
- cnt->conf.ffmpeg_bps, // bps
- cnt->conf.ffmpeg_vbr)) == NULL)
如果不是第一次进入,则会直接执行ffmpeg_put_other_image将图像加入录像。
更多Linux资料及视频教程点击这里
这篇关于motion源代码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!