Index: conf.c =================================================================== --- conf.c (revisión: 306) +++ conf.c (copia de trabajo) @@ -53,6 +53,7 @@ input: IN_DEFAULT, norm: 0, frame_limit: DEF_MAXFRAMERATE, + discard_frames: 0, quiet: 1, ppm: 0, noise: DEF_NOISELEVEL, @@ -267,6 +268,15 @@ print_int }, { + "discard_frames", + "# This many frames are discarded for each frame that is read from the camera.\n" + "# This is for camera devices that get upset if you don't read all the frames\n" + "# they provide, but that provide too many frames. Default 0 (use all frames).", + CONF_OFFSET(discard_frames), + copy_int, + print_int + }, + { "netcam_url", "# URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// or file:///)\n" "# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined", @@ -533,6 +543,18 @@ }, #ifdef HAVE_FFMPEG + { + "ffmpeg_device", + "\n############################################################\n" + "# Treat device as ffmpeg compliant video stream\n" + "############################################################\n\n" + "# Setting this flag treats the device specified previously\n" + "# as an ffmpeg compliant video stream. This could be a mpeg1,\n" + "# mpeg2, mpeg4, divx, xvid, or other compliant video stream.\n", + CONF_OFFSET(ffmpeg_device), + copy_bool, + print_bool + }, { "ffmpeg_cap_new", "\n############################################################\n" Index: conf.h =================================================================== --- conf.h (revisión: 306) +++ conf.h (copia de trabajo) @@ -36,6 +36,7 @@ int input; int norm; int frame_limit; + int discard_frames; int quiet; int ppm; int noise; @@ -54,6 +55,7 @@ int pre_capture; int post_capture; int switchfilter; + int ffmpeg_device; int ffmpeg_cap_new; int ffmpeg_cap_motion; int ffmpeg_bps; Index: motion.c =================================================================== --- motion.c (revisión: 306) +++ motion.c (copia de trabajo) @@ -850,6 +850,9 @@ */ vid_return_code = vid_next(cnt, newimg); + for (j = 0; j < cnt->conf.discard_frames; j++) + vid_return_code = vid_next(cnt, newimg); + // VALID PICTURE if (vid_return_code == 0) { cnt->lost_connection = 0; Index: video.h =================================================================== --- video.h (revisión: 306) +++ video.h (copia de trabajo) @@ -13,7 +13,10 @@ #define _LINUX_TIME_H 1 #ifndef WITHOUT_V4L #include +#ifdef HAVE_FFMPEG +#include #endif +#endif /* video4linux stuff */ #define NORM_DEFAULT 0 @@ -65,8 +68,22 @@ int v4l_maxbuffer; int v4l_bufsize; #endif +#ifdef HAVE_FFMPEG + AVFormatContext * fcx; + AVCodecContext * ccx; + AVCodec * codec; + AVFormatParameters params; + unsigned char *ffmpeg_buffers[2]; + int ffmpeg_fmt; + int ffmpeg_video_index; + int ffmpeg_curbuffer; + int ffmpeg_maxbuffer; + int ffmpeg_bufsize; +#endif }; + + /* video functions, video_common.c */ int vid_start(struct context *); int vid_next(struct context *, unsigned char *map); Index: video_common.c =================================================================== --- video_common.c (revisión: 306) +++ video_common.c (copia de trabajo) @@ -582,10 +582,190 @@ } } + +#ifdef HAVE_FFMPEG +static unsigned char *ffmpeg_start(struct context *cnt, struct video_dev *viddev, int input, int norm) +{ + int dev=viddev->fd; + int err=0; + unsigned int i=0, width=0, height=0; + viddev->fcx = NULL; + viddev->ccx = NULL; + viddev->codec = NULL; + viddev->ffmpeg_video_index = -1; + struct video_channel vid_chnl; + + if (input != IN_DEFAULT) { + memset(&vid_chnl, 0, sizeof(struct video_channel)); + vid_chnl.channel = input; + if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCGCHAN)"); + } else { + vid_chnl.channel = input; + vid_chnl.norm = norm; + if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { + motion_log(LOG_ERR, 1, "ioctl (VIDIOCSCHAN)"); + return (NULL); + } + } + } + // Open the input file. + motion_log(LOG_DEBUG, 0, "Openning ffmpeg video stream device: %s", viddev->video_device); + err = av_open_input_file(&viddev->fcx, viddev->video_device, NULL, 0, &viddev->params); + if (err < 0) { + motion_log(LOG_ERR, 1, "Fatal: Can't open video device (%s) using ffmpeg", viddev->video_device); + motion_log(LOG_ERR, 1, "Motion Exits"); + exit(-1); + } + + // Find the stream info. + err = av_find_stream_info(viddev->fcx); + if (err < 0) { + motion_log(LOG_ERR, 0, "Fatal: Could not locate ffmpeg stream info."); + motion_log(LOG_ERR, 1, "Motion Exits"); + exit(-1); + } + + // Find the first video stream. + motion_log(LOG_DEBUG, 0, "Found %d ffmpeg streams.", viddev->fcx->nb_streams); + for (i=0; i < viddev->fcx->nb_streams; i++) { + viddev->ccx=viddev->fcx->streams[i]->codec; + if( viddev->ccx->codec_type == CODEC_TYPE_VIDEO ) + break; + } + viddev->ffmpeg_video_index = i; + motion_log(LOG_DEBUG, 0, "Obtained ffmpeg video_index (%d).", viddev->ffmpeg_video_index); + + + // Open stream. + if (viddev->ffmpeg_video_index >= 0) { + viddev->codec = avcodec_find_decoder(viddev->ccx->codec_id); + if (viddev->codec) + err = avcodec_open(viddev->ccx, viddev->codec); + if (err < 0) { + motion_log(LOG_ERR, 1, "Fatal: Can't open ffmpeg codec"); + motion_log(LOG_ERR, 1, "Motion Exits"); + av_close_input_file(viddev->fcx); + exit(-1); + } else { + motion_log(LOG_DEBUG, 0, "Found ffmpeg video stream codec: %s", viddev->codec->name); + } + } else { + motion_log(LOG_ERR, 1, "Fatal: ffmpeg video stream not found"); + motion_log(LOG_ERR, 1, "Motion Exits"); + av_close_input_file(viddev->fcx); + exit(-1); + } + width = viddev->ccx->width; + height = viddev->ccx->height; + motion_log(LOG_DEBUG, 0, "Size of image: %d x %d x 3/2 = %d.", width, height, (width*height*3)/2); + viddev->ffmpeg_fmt=VIDEO_PALETTE_YUV420P; + viddev->ffmpeg_bufsize=(width*height*3)/2; + viddev->ffmpeg_maxbuffer=1; + viddev->ffmpeg_curbuffer=0; + viddev->ffmpeg_buffers[0]=mymalloc((width*height*3)/2); + viddev->ffmpeg_buffers[1]=mymalloc((width*height*3)/2); + + return viddev->ffmpeg_buffers[0]; +} + +static int ffmpeg_next(struct video_dev *viddev, unsigned char *map) +{ + int len1=0, got_picture=0; + unsigned char *cap_map; + AVPicture av_pict; + AVPacket av_pkt; + AVFrame *av_frame=avcodec_alloc_frame(); + + int curbuf = 0, err = 0; + + viddev->ffmpeg_curbuffer++; + if (viddev->ffmpeg_curbuffer > viddev->ffmpeg_maxbuffer) + viddev->ffmpeg_curbuffer=0; + curbuf=viddev->ffmpeg_curbuffer; + cap_map=viddev->ffmpeg_buffers[curbuf]; + + while(!got_picture) { + if(!viddev->fcx) { + motion_log(LOG_ERR, 0, "NULL pointer for AVFrameContext viddev->fcx."); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + } + err = av_read_frame(viddev->fcx, &av_pkt); + switch(err) { + case AVERROR_UNKNOWN: + motion_log(LOG_ERR, 1, "Unknown error or Invalid data reading AVFrame"); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + break; + case AVERROR_IO: + motion_log(LOG_ERR, 1, "IO error reading AVFrame"); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + break; + case AVERROR_NUMEXPECTED: + motion_log(LOG_ERR, 1, "Number syntax expected in file name while reading AVFrame"); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + break; + case AVERROR_NOMEM: + motion_log(LOG_ERR, 1, "Out of memory while reading AVFrame"); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + break; + case AVERROR_NOFMT: + motion_log(LOG_ERR, 1, "Unkown format while reading AVFrame"); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + break; + case AVERROR_NOTSUPP: + motion_log(LOG_ERR, 1, "Operation not supported while reading AVFrame"); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + break; + default: + break; + } + if (av_pkt.stream_index==viddev->ffmpeg_video_index) { + if(!viddev->ccx) { + motion_log(LOG_ERR, 0, "NULL pointer for AVCodecContext viddev->ccx."); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(-1); + } + len1 = avcodec_decode_video(viddev->ccx, av_frame, &got_picture, av_pkt.data, av_pkt.size); + } + } + if (got_picture) { + avpicture_alloc(&av_pict, PIX_FMT_YUV420P, viddev->ccx->width, viddev->ccx->height); + + img_convert(&av_pict, PIX_FMT_YUV420P, (AVPicture*) av_frame, viddev->ccx->pix_fmt, viddev->ccx->width, viddev->ccx->height); + memset(map, 0, (viddev->ccx->width*viddev->ccx->height*3)/2); + memcpy(map, av_pict.data[0], (viddev->ccx->width*viddev->ccx->height*3)/2); + } else { + motion_log(LOG_WARNING, 0, "Fatal: Unable to decode video packets into frame."); + exit(-1); + } + + av_free_packet(&av_pkt); + + // Clean up + avpicture_free(&av_pict); + av_free(av_frame); + // FIXME: This should be closed before exiting motion + // av_close_input_file(av_fcx); + + return 0; +} + +#endif + + + + /** * vid_start * - * vid_start setup the capture device. This will be either a V4L device or a netcam. + * vid_start setup the capture device. This will be either a V4L device, a netcam or an mpeg stream. * The function does the following: * - If the camera is a netcam - netcam_start is called and function returns * - Width and height are checked for valid value (multiple of 16) @@ -593,7 +773,7 @@ * only copied to the from the conf struct to the imgs struct during program startup * The width and height can no later be changed via http remote control as this would * require major re-memory allocations of all image buffers. - * - Setup basic V4L properties incl palette incl setting + * - Setup basic V4L or ffmpeg stream properties incl palette incl setting * - Open the device * - Returns the device number. * @@ -778,10 +958,104 @@ { struct config *conf = &cnt->conf; int dev = -1; + int i=-1, width=0, height=0, input=0, norm=0; if (conf->netcam_url) { return netcam_start(cnt); } + +#ifdef HAVE_FFMPEG + if (conf->ffmpeg_device) { + + if (conf->width % 16) { + motion_log(LOG_ERR, 0,"config image width (%d) is not modulo 16", conf->width); + return -1; + } + if (conf->height % 16) { + motion_log(LOG_ERR, 0,"config image height (%d) is not modulo 16", conf->height); + return -1; + } + + cnt->imgs.width=conf->width; + cnt->imgs.height=conf->height; + input = conf->input; + norm = conf->norm; + pthread_mutex_lock(&vid_mutex); + while (viddevs[++i]) { + if (!strcmp(conf->video_device, viddevs[i]->video_device)) { + int fd; + cnt->imgs.type=viddevs[i]->ffmpeg_fmt; + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.motionsize=width*height; + cnt->imgs.size=width*height; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type=VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + cnt->imgs.motionsize=width*height; + cnt->imgs.size=(width*height*3)/2; + break; + } + fd=viddevs[i]->fd; + pthread_mutex_unlock(&vid_mutex); + motion_log(LOG_DEBUG, 0, "Found viddev %d using image size of %d.", i, cnt->imgs.size); + return fd; + } + } + viddevs=myrealloc(viddevs, sizeof(struct video_dev *)*(i+2), "vid_start"); + viddevs[i]=mymalloc(sizeof(struct video_dev)); + memset(viddevs[i], 0, sizeof(struct video_dev)); + viddevs[i+1]=NULL; + + pthread_mutexattr_init(&viddevs[i]->attr); + pthread_mutex_init(&viddevs[i]->mutex, NULL); + + dev=open(conf->video_device, O_RDWR); + if (dev <0) { + motion_log(LOG_ERR, 1, "Failed to open video device %s", conf->video_device); + motion_log(LOG_ERR, 0, "Motion Exits"); + exit(1); + } + viddevs[i]->fd = dev; + viddevs[i]->brightness=0; + viddevs[i]->contrast=0; + viddevs[i]->saturation=0; + viddevs[i]->hue=0; + viddevs[i]->owner=-1; + viddevs[i]->ffmpeg_fmt=VIDEO_PALETTE_YUV420P; + viddevs[i]->video_device = conf->video_device; + + if(!ffmpeg_start(cnt, viddevs[i], input, norm)) { + motion_log(LOG_DEBUG, 0, "Failed in ffmpeg_start."); + pthread_mutex_unlock(&vid_mutex); + return -1; + } + cnt->imgs.type=viddevs[i]->ffmpeg_fmt; + width = viddevs[i]->ccx->width; + height = viddevs[i]->ccx->height; + cnt->imgs.width=width; + cnt->imgs.height=height; + switch (cnt->imgs.type) { + case VIDEO_PALETTE_GREY: + cnt->imgs.size=width*height; + cnt->imgs.motionsize=width*height; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_YUV422: + cnt->imgs.type=VIDEO_PALETTE_YUV420P; + case VIDEO_PALETTE_YUV420P: + cnt->imgs.size=(width*height*3)/2; + cnt->imgs.motionsize=width*height; + break; + } + pthread_mutex_unlock(&vid_mutex); + motion_log(LOG_DEBUG, 0, "Started ffmpeg stream with image size of %d and motionsize %d.", cnt->imgs.size, cnt->imgs.motionsize); + return dev; + } +#endif + #ifndef WITHOUT_V4L dev = vid_v4lx_start(cnt); #endif /*WITHOUT_V4L */ @@ -821,6 +1095,38 @@ ret = netcam_next(cnt, map); return ret; } + +#ifdef HAVE_FFMPEG + if(conf->ffmpeg_device) { + int i = -1; + int dev = cnt->video_dev; + + while (viddevs[++i]) + if (viddevs[i]->fd==dev) + break; + + if (!viddevs[i]) + return -1; + + if (viddevs[i]->owner != cnt->threadnr) { + pthread_mutex_lock(&viddevs[i]->mutex); + viddevs[i]->owner = cnt->threadnr; + viddevs[i]->frames = conf->roundrobin_frames; + cnt->switched = 1; + } + map = ffmpeg_next(viddevs[i], map); + if (--viddevs[i]->frames <= 0) { + viddevs[i]->owner = -1; + pthread_mutex_unlock(&viddevs[i]->mutex); + } + if(cnt->rotate_data.degrees > 0) { + /* rotate the image as specified */ + rotate_map(cnt, map); + } + return (int)map; + } +#endif + #ifndef WITHOUT_V4L /* We start a new block so we can make declarations without breaking Index: motion-dist.conf =================================================================== --- motion-dist.conf (revisión: 306) +++ motion-dist.conf (copia de trabajo) @@ -57,6 +57,13 @@ # Valid range: 2-100. Default: 100 (almost no limit). framerate 2 +# Number of frames to discard for each frame captured. This is +# useful for devices that get upset if you are reading frames +# too slowly. One example is the ivtv driver (Hauppauge Win-TV +# cards). +# default: 0 +discard_frames 0 + # Minimum time in seconds between capturing picture frames from the camera. # Default: 0 = disabled - the capture rate is given by the camera framerate. # This option is used when you want to capture images at a rate lower than 2 per second. @@ -256,6 +263,11 @@ # (default: off) ffmpeg_deinterlace off +# Use ffmpeg to decode the data from the camera. Necessary if your +# data source delivers data in a format that ffmpeg understands (eg +# mpeg2 as delivered by ivtv cards). +# (default: off) +ffmpeg_device off ############################################################ # Snapshots (Traditional Periodic Webcam File Output)