前言:
之前的博文将ffmpeg编入motion的结尾,提到了motion的一些简单的应用。本文将以录像为契入点,分析这部分的代码。
正文:
刷照片的效果实在太挫了,让我们看看如何打开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) { /* 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
// event.c:673 { EVENT_TIMELAPSE, event_ffmpeg_timelapse },
// 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:
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将图像加入录像。
motion源码分析(二)——录像,布布扣,bubuko.com
原文:http://blog.csdn.net/sakaue/article/details/23338471