前言:
之前的博文将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