Only in motion-3.2.4_snap5: .video.c.swp diff -ru motion-3.2.4_snap5/conf.c motion-3.2.4_snap5-pvrpatch/conf.c --- motion-3.2.4_snap5/conf.c 2005-10-29 07:53:09.000000000 +0200 +++ motion-3.2.4_snap5-pvrpatch/conf.c 2006-02-18 01:13:56.228971576 +0100 @@ -529,6 +529,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" "# Film (mpeg) File Output - ffmpeg based\n" diff -ru motion-3.2.4_snap5/conf.h motion-3.2.4_snap5-pvrpatch/conf.h --- motion-3.2.4_snap5/conf.h 2005-10-29 07:06:17.000000000 +0200 +++ motion-3.2.4_snap5-pvrpatch/conf.h 2006-02-18 01:13:56.229971424 +0100 @@ -55,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; diff -ru motion-3.2.4_snap5/video.c motion-3.2.4_snap5-pvrpatch/video.c --- motion-3.2.4_snap5/video.c 2006-02-18 01:49:03.147704216 +0100 +++ motion-3.2.4_snap5-pvrpatch/video.c 2006-02-18 01:39:24.665646856 +0100 @@ -711,10 +711,190 @@ } #endif /*WITHOUT_V4L*/ +#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, 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; ifcx->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 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_INVALIDDATA: + motion_log(LOG_ERR, 1, "Invalid data 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 or 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) @@ -722,7 +902,7 @@ * only copied to the from the conf struct to the imgs struct during program startup * The width and height can no leter 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. * @@ -743,35 +923,119 @@ { 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); } -#ifndef WITHOUT_V4L - /* Start a new block so we can make declarations without breaking good old - * gcc 2.95 or older. + /* We use width and height from conf in this function. They will be assigned + * to width and height in imgs here, and cap_width and cap_height in + * rotate_data won't be set until in rotate_init. + * Motion requires that width and height is a multiple of 16 so we check + * for this first. */ - { - int i = -1; - int width, height, input, norm, tuner_number; - unsigned long frequency; + 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; + } +#ifdef HAVE_FFMPEG + if (conf->ffmpeg_device) { + 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; - /* We use width and height from conf in this function. They will be assigned - * to width and height in imgs here, and cap_width and cap_height in - * rotate_data won't be set until in rotate_init. - * Motion requires that width and height is a multiple of 16 so we check - * for this first. - */ - if (conf->width % 16) { - motion_log(LOG_ERR, 0, "config image width (%d) is not modulo 16", conf->width); - return -1; + 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; - if (conf->height % 16) { - motion_log(LOG_ERR, 0, "config image height (%d) is not modulo 16", conf->height); + 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 + /* Start a new block so we can make declarations without breaking good old + * gcc 2.95 or older. + */ + { + int tuner_number; + unsigned long frequency; width = conf->width; height = conf->height; @@ -902,7 +1166,9 @@ int vid_next(struct context *cnt, unsigned char *map) { struct config *conf=&cnt->conf; - int ret = -1; + int ret = -1; + int i = -1; + int dev = cnt->video_dev; if (conf->netcam_url) { if (cnt->video_dev == -1) @@ -911,15 +1177,41 @@ ret = netcam_next(cnt, map); return ret; } +#ifdef HAVE_FFMPEG + if(conf->ffmpeg_device) { + 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 * gcc 2.95 or older */ { - int i = -1; int width, height; - int dev = cnt->video_dev; /* NOTE: Since this is a capture, we need to use capture dimensions. */ width = cnt->rotate_data.cap_width; diff -ru motion-3.2.4_snap5/video.h motion-3.2.4_snap5-pvrpatch/video.h --- motion-3.2.4_snap5/video.h 2005-10-30 12:41:52.000000000 +0100 +++ motion-3.2.4_snap5-pvrpatch/video.h 2006-02-18 01:13:56.237970208 +0100 @@ -13,6 +13,9 @@ #define _LINUX_TIME_H 1 #ifndef WITHOUT_V4L #include +#ifdef HAVE_FFMPEG +#include +#endif #endif /* video4linux stuff */ @@ -63,6 +66,19 @@ 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.c */