msv4l2.c 15.8 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

smorlat's avatar
smorlat committed
20
#ifdef HAVE_CONFIG_H
aymeric's avatar
aymeric committed
21
#include "mediastreamer-config.h"
smorlat's avatar
smorlat committed
22
#endif
aymeric's avatar
aymeric committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

#ifdef HAVE_LINUX_VIDEODEV2_H


#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <poll.h>

#include <linux/videodev2.h>

#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/mswebcam.h"

43 44 45 46 47 48 49 50 51 52 53 54
#ifdef HAVE_LIBV4L2
#include <libv4l2.h>
#else

#define v4l2_open open
#define v4l2_close close
#define v4l2_mmap mmap
#define v4l2_munmap munmap
#define v4l2_ioctl ioctl

#endif

55

Simon Morlat's avatar
Simon Morlat committed
56 57 58 59 60 61 62 63 64 65 66 67
static void inc_ref(mblk_t*m){
	m->b_datap->db_ref++;
	if (m->b_cont)
		inc_ref(m->b_cont);
}

static void dec_ref(mblk_t *m){
	m->b_datap->db_ref--;
	if (m->b_cont)
		dec_ref(m->b_cont);
}

aymeric's avatar
aymeric committed
68 69
typedef struct V4l2State{
	int fd;
70 71 72 73
	ms_thread_t thread;
	bool_t thread_run;
	queue_t rq;
	ms_mutex_t mutex;
aymeric's avatar
aymeric committed
74 75 76 77 78 79 80
	char *dev;
	char *mmapdbuf;
	int msize;/*mmapped size*/
	MSVideoSize vsize;
	MSVideoSize got_vsize;
	int pix_fmt;
	int int_pix_fmt; /*internal pixel format */
smorlat's avatar
smorlat committed
81
	int picture_size;
aymeric's avatar
aymeric committed
82 83 84 85
	mblk_t *frames[VIDEO_MAX_FRAME];
	int frame_ind;
	int frame_max;
	float fps;
86 87 88 89
	unsigned int start_time;
	unsigned int last_frame_time;
	float mean_inter_frame;
	int th_frame_count;
aymeric's avatar
aymeric committed
90
	int queued;
smorlat's avatar
smorlat committed
91
	bool_t configured;
aymeric's avatar
aymeric committed
92 93
}V4l2State;

94 95
static int msv4l2_open(V4l2State *s){
	int fd=v4l2_open(s->dev,O_RDWR|O_NONBLOCK);
aymeric's avatar
aymeric committed
96 97 98 99 100 101 102 103
	if (fd==-1){
		ms_error("Could not open %s: %s",s->dev,strerror(errno));
		return -1;
	}
	s->fd=fd;
	return 0;
}

104
static int msv4l2_close(V4l2State *s){
aymeric's avatar
aymeric committed
105
	if (s->fd!=-1){
106
		v4l2_close(s->fd);
aymeric's avatar
aymeric committed
107
		s->fd=-1;
smorlat's avatar
smorlat committed
108
		s->configured=FALSE;
aymeric's avatar
aymeric committed
109 110 111 112
	}
	return 0;
}

smorlat's avatar
smorlat committed
113 114 115 116 117
static bool_t v4lv2_try_format( V4l2State *s, struct v4l2_format *fmt, int fmtid){
	
	fmt->type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt->fmt.pix.pixelformat = fmtid;
	fmt->fmt.pix.field = V4L2_FIELD_ANY;
aymeric's avatar
aymeric committed
118

119
        if (v4l2_ioctl (s->fd, VIDIOC_TRY_FMT, fmt)<0){
smorlat's avatar
smorlat committed
120 121 122
		ms_message("VIDIOC_TRY_FMT: %s",strerror(errno));
		return FALSE;
	}
123
	if (v4l2_ioctl (s->fd, VIDIOC_S_FMT, fmt)<0){
124
		ms_message("VIDIOC_S_FMT: %s",strerror(errno));
aymeric's avatar
aymeric committed
125 126 127 128 129
		return FALSE;
	}
	return TRUE;
}

smorlat's avatar
smorlat committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
static int get_picture_buffer_size(MSPixFmt pix_fmt, int w, int h){
	switch(pix_fmt){
		case MS_YUV420P:
			return (w*h*3)/2;
		break;
		case MS_RGB24:
			return w*h*3;
		break;
		case MS_YUYV:
			return w*h*2;
		break;
		default:
			return 0;
	}
	return 0;
}

147
static int msv4l2_configure(V4l2State *s){
aymeric's avatar
aymeric committed
148 149
	struct v4l2_capability cap;
	struct v4l2_format fmt;
smorlat's avatar
smorlat committed
150
	MSVideoSize vsize;
aymeric's avatar
aymeric committed
151

152
	if (v4l2_ioctl (s->fd, VIDIOC_QUERYCAP, &cap)<0) {
aymeric's avatar
aymeric committed
153 154
		ms_message("Not a v4lv2 driver.");
		return -1;
155
	}
aymeric's avatar
aymeric committed
156

157
	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
aymeric's avatar
aymeric committed
158 159
		ms_error("%s is not a video capture device\n",s->dev);
		return -1;
160
	}
aymeric's avatar
aymeric committed
161 162 163 164 165

	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
		ms_error("%s does not support streaming i/o\n",s->dev);
		return -1;
	}
smorlat's avatar
smorlat committed
166
	
smorlat's avatar
smorlat committed
167
	ms_message("Driver is %s",cap.driver);
smorlat's avatar
smorlat committed
168 169 170
	memset(&fmt,0,sizeof(fmt));

	fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
171
	if (v4l2_ioctl (s->fd, VIDIOC_G_FMT, &fmt)<0){
smorlat's avatar
smorlat committed
172 173
		ms_error("VIDIOC_G_FMT failed: %s",strerror(errno));
	}
smorlat's avatar
smorlat committed
174 175
	vsize=s->vsize;
	do{
smorlat's avatar
smorlat committed
176 177
		fmt.fmt.pix.width       = s->vsize.width; 
		fmt.fmt.pix.height      = s->vsize.height;
178
		ms_message("v4l2: trying %ix%i",s->vsize.width,s->vsize.height);
smorlat's avatar
smorlat committed
179
		if (v4lv2_try_format(s,&fmt,V4L2_PIX_FMT_YUV420)){
smorlat's avatar
smorlat committed
180 181
			s->pix_fmt=MS_YUV420P;
			s->int_pix_fmt=V4L2_PIX_FMT_YUV420;
Yann Diorcet's avatar
Yann Diorcet committed
182
			ms_message("v4lv2: YUV420P chosen");
smorlat's avatar
smorlat committed
183
			break;
smorlat's avatar
smorlat committed
184
		}else if (v4lv2_try_format(s,&fmt,V4L2_PIX_FMT_YUYV)){
smorlat's avatar
smorlat committed
185 186
			s->pix_fmt=MS_YUYV;
			s->int_pix_fmt=V4L2_PIX_FMT_YUYV;
Yann Diorcet's avatar
Yann Diorcet committed
187
			ms_message("v4lv2: V4L2_PIX_FMT_YUYV chosen");
smorlat's avatar
smorlat committed
188
			break;
smorlat's avatar
smorlat committed
189
		}else if (v4lv2_try_format(s,&fmt,V4L2_PIX_FMT_RGB24)){
smorlat's avatar
smorlat committed
190 191
			s->pix_fmt=MS_RGB24;
			s->int_pix_fmt=V4L2_PIX_FMT_RGB24;
Yann Diorcet's avatar
Yann Diorcet committed
192
			ms_message("v4lv2: RGB24 chosen");
smorlat's avatar
smorlat committed
193
			break;
194 195 196
		}else if (v4lv2_try_format(s,&fmt,V4L2_PIX_FMT_MJPEG)){
			s->pix_fmt=MS_MJPEG;
			s->int_pix_fmt=V4L2_PIX_FMT_MJPEG;
Yann Diorcet's avatar
Yann Diorcet committed
197
			ms_message("v4lv2: MJPEG chosen");
198
			break;
smorlat's avatar
smorlat committed
199 200 201 202 203 204 205 206
		}else{
			ms_error("Could not find supported pixel format for %ix%i", s->vsize.width, s->vsize.height);
		}
		s->vsize=ms_video_size_get_just_lower_than(s->vsize);
	}while(s->vsize.width!=0);
	if (s->vsize.width==0){
		ms_message("Could not find any combination of resolution/pixel-format that works !");
		s->vsize=vsize;
aymeric's avatar
aymeric committed
207 208 209 210 211 212
		return -1;
	}
	memset(&fmt,0,sizeof(fmt));

	fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

213
	if (v4l2_ioctl (s->fd, VIDIOC_G_FMT, &fmt)<0){
aymeric's avatar
aymeric committed
214 215
		ms_error("VIDIOC_G_FMT failed: %s",strerror(errno));
	}else{
smorlat's avatar
smorlat committed
216 217 218
		ms_message("Size of webcam delivered pictures is %ix%i",fmt.fmt.pix.width,fmt.fmt.pix.height);
		s->vsize.width=fmt.fmt.pix.width;
		s->vsize.height=fmt.fmt.pix.height;
aymeric's avatar
aymeric committed
219
	}
smorlat's avatar
smorlat committed
220
	s->picture_size=get_picture_buffer_size(s->pix_fmt,s->vsize.width,s->vsize.height);
smorlat's avatar
smorlat committed
221
	s->configured=TRUE;
aymeric's avatar
aymeric committed
222 223 224
	return 0;
}

225
static int msv4l2_do_mmap(V4l2State *s){
aymeric's avatar
aymeric committed
226 227 228 229 230 231 232 233 234 235
	struct v4l2_requestbuffers req;
	int i;
	enum v4l2_buf_type type;
	
	memset(&req,0,sizeof(req));
	
	req.count               = 4;
	req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory              = V4L2_MEMORY_MMAP;
	
236
	if (v4l2_ioctl (s->fd, VIDIOC_REQBUFS, &req)<0) {
aymeric's avatar
aymeric committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250
		ms_error("Error requesting info on mmap'd buffers: %s",strerror(errno));
		return -1;
	}
	
	for (i=0; i<req.count; ++i) {
		struct v4l2_buffer buf;
		mblk_t *msg;
		void *start;
		memset(&buf,0,sizeof(buf));
	
		buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory=V4L2_MEMORY_MMAP;
		buf.index=i;
	
251
		if (v4l2_ioctl (s->fd, VIDIOC_QUERYBUF, &buf)<0){
aymeric's avatar
aymeric committed
252 253 254 255
			ms_error("Could not VIDIOC_QUERYBUF : %s",strerror(errno));
			return -1;
		}
		
256
		start=v4l2_mmap (NULL /* start anywhere */,
aymeric's avatar
aymeric committed
257 258 259 260 261 262
			buf.length,
			PROT_READ | PROT_WRITE /* required */,
			MAP_SHARED /* recommended */,
			s->fd, buf.m.offset);
	
		if (start==NULL){
263
			ms_error("Could not v4l2_mmap: %s",strerror(errno));
aymeric's avatar
aymeric committed
264 265 266
		}
		msg=esballoc(start,buf.length,0,NULL);
		msg->b_wptr+=buf.length;
267
		s->frames[i]=ms_yuv_buf_alloc_from_buffer(s->vsize.width, s->vsize.height, msg);
aymeric's avatar
aymeric committed
268 269 270 271 272 273 274 275 276
	}
	s->frame_max=req.count;
	for (i = 0; i < s->frame_max; ++i) {
		struct v4l2_buffer buf;

		memset(&buf,0,sizeof(buf));
		buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory      = V4L2_MEMORY_MMAP;
		buf.index       = i;
277
		if (-1==v4l2_ioctl (s->fd, VIDIOC_QBUF, &buf)){
aymeric's avatar
aymeric committed
278
			ms_error("VIDIOC_QBUF failed: %s",strerror(errno));
279
		}else {
Simon Morlat's avatar
Simon Morlat committed
280
			inc_ref(s->frames[i]);
281
			s->queued++;
aymeric's avatar
aymeric committed
282 283 284 285
		}
	}
	/*start capture immediately*/
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
286
	if (-1 ==v4l2_ioctl (s->fd, VIDIOC_STREAMON, &type)){
aymeric's avatar
aymeric committed
287 288 289 290 291 292
		ms_error("VIDIOC_STREAMON failed: %s",strerror(errno));
		return -1;
	}
	return 0;
}

Simon Morlat's avatar
Simon Morlat committed
293 294 295 296 297 298
static mblk_t *v4l2_dequeue_ready_buffer(V4l2State *s, int poll_timeout_ms){
	struct v4l2_buffer buf;
	mblk_t *ret=NULL;
	struct pollfd fds;
	
	memset(&buf,0,sizeof(buf));
299 300 301
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	
Simon Morlat's avatar
Simon Morlat committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
	memset(&fds,0,sizeof(fds));
	fds.events=POLLIN;
	fds.fd=s->fd;
	/*check with poll if there is something to read */
	if (poll(&fds,1,poll_timeout_ms)==1 && fds.revents==POLLIN){
		if (v4l2_ioctl(s->fd, VIDIOC_DQBUF, &buf)<0) {
			switch (errno) {
			case EAGAIN:
			case EIO:
				/* Could ignore EIO, see spec. */
				break;
			default:
				ms_warning("VIDIOC_DQBUF failed: %s",strerror(errno));
			}
		}else{
			s->queued--;
			ms_debug("v4l2: de-queue buf %i",buf.index);
			/*decrement ref count of dequeued buffer */
			ret=s->frames[buf.index];
Simon Morlat's avatar
Simon Morlat committed
321
			dec_ref(ret);
Simon Morlat's avatar
Simon Morlat committed
322 323 324 325 326 327 328 329 330 331 332
			if (buf.index >= s->frame_max){
				ms_error("buf.index>=s->max_frames !");
				return NULL;
			}
			if (buf.bytesused<=30){
				ms_warning("Ignoring empty buffer...");
				return NULL;
			}
			/*normally buf.bytesused should contain the right buffer size; however we have found a buggy
			driver that puts a random value inside */
			if (s->picture_size!=0)
333 334
				ret->b_cont->b_wptr=ret->b_cont->b_rptr+s->picture_size;
			else ret->b_cont->b_wptr=ret->b_cont->b_rptr+buf.bytesused;
Simon Morlat's avatar
Simon Morlat committed
335 336 337 338 339
		}
	}
	return ret;
}

340
static mblk_t * v4lv2_grab_image(V4l2State *s, int poll_timeout_ms){
aymeric's avatar
aymeric committed
341 342 343 344 345 346 347 348 349 350 351 352 353
	struct v4l2_buffer buf;
	unsigned int k;
	memset(&buf,0,sizeof(buf));
	mblk_t *ret=NULL;

	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	
	/*queue buffers whose ref count is 1, because they are not
	still used anywhere in the filter chain */
	for(k=0;k<s->frame_max;++k){
		if (s->frames[k]->b_datap->db_ref==1){
			buf.index=k;
354
			if (-1==v4l2_ioctl (s->fd, VIDIOC_QBUF, &buf))
aymeric's avatar
aymeric committed
355 356 357 358
				ms_warning("VIDIOC_QBUF %i failed: %s",k,  strerror(errno));
			else {
				ms_debug("v4l2: queue buf %i",k);
				/*increment ref count of queued buffer*/
Simon Morlat's avatar
Simon Morlat committed
359
				inc_ref(s->frames[k]);
aymeric's avatar
aymeric committed
360 361 362 363 364 365
				s->queued++;
			}
		}
	}

	if (s->queued){
Simon Morlat's avatar
Simon Morlat committed
366
		ret=v4l2_dequeue_ready_buffer(s,poll_timeout_ms);
aymeric's avatar
aymeric committed
367 368 369 370
	}
	return ret;
}

371
static void msv4l2_do_munmap(V4l2State *s){
aymeric's avatar
aymeric committed
372 373 374 375
	int i;
	enum v4l2_buf_type type;
	/*stop capture immediately*/
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
376
	if (-1 ==v4l2_ioctl (s->fd, VIDIOC_STREAMOFF, &type)){
aymeric's avatar
aymeric committed
377 378 379 380
		ms_error("VIDIOC_STREAMOFF failed: %s",strerror(errno));
	}

	for(i=0;i<s->frame_max;++i){
Simon Morlat's avatar
Simon Morlat committed
381
		mblk_t *msg=s->frames[i]->b_cont;
aymeric's avatar
aymeric committed
382
		int len=msg->b_datap->db_lim-msg->b_datap->db_base;
383
		if (v4l2_munmap(msg->b_datap->db_base,len)<0){
aymeric's avatar
aymeric committed
384 385 386 387 388 389 390 391 392
			ms_warning("MSV4l2: Fail to unmap: %s",strerror(errno));
		}
		freemsg(s->frames[i]);
		s->frames[i]=NULL;
	}
}



393
static void msv4l2_init(MSFilter *f){
aymeric's avatar
aymeric committed
394 395 396 397 398
	V4l2State *s=ms_new0(V4l2State,1);
	s->dev=ms_strdup("/dev/video0");
	s->fd=-1;
	s->vsize=MS_VIDEO_SIZE_CIF;
	s->fps=15;
smorlat's avatar
smorlat committed
399
	s->configured=FALSE;
aymeric's avatar
aymeric committed
400
	f->data=s;
401
	qinit(&s->rq);
aymeric's avatar
aymeric committed
402 403
}

404
static void msv4l2_uninit(MSFilter *f){
aymeric's avatar
aymeric committed
405 406
	V4l2State *s=(V4l2State*)f->data;
	ms_free(s->dev);
407
	flushq(&s->rq,0);
408
	ms_mutex_destroy(&s->mutex);	
409
	ms_free(s);
aymeric's avatar
aymeric committed
410 411
}

412 413
static void *msv4l2_thread(void *ptr){
	V4l2State *s=(V4l2State*)ptr;
Simon Morlat's avatar
Simon Morlat committed
414 415
	int try=0;
	
416
	ms_message("msv4l2_thread starting");
417 418 419 420 421
	if (s->fd==-1){
		if( msv4l2_open(s)!=0){
			ms_warning("msv4l2 could not be openned");
			goto close;
		}
422
	}
423
	
424 425 426 427 428 429 430 431 432 433 434 435 436 437
	if (!s->configured && msv4l2_configure(s)!=0){
		ms_warning("msv4l2 could not be configured");		
		goto close;
	}
	if (msv4l2_do_mmap(s)!=0)
	{
		ms_warning("msv4l2 do mmap");
		goto close;
	}
	ms_message("V4L2 video capture started.");
	while(s->thread_run)
	{
		if (s->fd!=-1){
			mblk_t *m;
438
			m=v4lv2_grab_image(s,50);
439
			if (m){
440
				mblk_t *om=dupmsg(m);
441 442 443 444 445 446
				mblk_set_marker_info(om,(s->pix_fmt==MS_MJPEG));
				ms_mutex_lock(&s->mutex);
				putq(&s->rq,om);
				ms_mutex_unlock(&s->mutex);
			}
		}
447
	}
Simon Morlat's avatar
Simon Morlat committed
448 449 450 451 452
	/*dequeue pending buffers so that we can properly unref them (avoids memleak )*/
	while(s->queued && try<10){
		v4l2_dequeue_ready_buffer(s,50);
	}
	if (try==10) ms_warning("msv4l2: buffers not dequeued at exit !");
453 454 455 456 457
	msv4l2_do_munmap(s);
close:
	msv4l2_close(s);
	ms_message("msv4l2_thread exited.");
	ms_thread_exit(NULL);
Simon Morlat's avatar
Simon Morlat committed
458
	return NULL;
459 460
}

461
static void msv4l2_preprocess(MSFilter *f){
aymeric's avatar
aymeric committed
462
	V4l2State *s=(V4l2State*)f->data;
463
	s->thread_run=TRUE;
464
	ms_thread_create(&s->thread,NULL,msv4l2_thread,s);
465 466
	s->th_frame_count=-1;
	s->mean_inter_frame=0;
smorlat's avatar
smorlat committed
467 468
}

469
static void msv4l2_process(MSFilter *f){
smorlat's avatar
smorlat committed
470
	V4l2State *s=(V4l2State*)f->data;
471 472
	uint32_t timestamp;
	int cur_frame;
473 474 475 476 477 478
	uint32_t curtime=f->ticker->time;
	float elapsed;
	
	if (s->th_frame_count==-1){
		s->start_time=curtime;
		s->th_frame_count=0;
479
	}
480 481
	elapsed=((float)(curtime-s->start_time))/1000.0;
	cur_frame=elapsed*s->fps;
482
	
483
	if (cur_frame>=s->th_frame_count){
484 485 486 487
		mblk_t *om=NULL;
		ms_mutex_lock(&s->mutex);
		/*keep the most recent frame if several frames have been captured */
		if (s->fd!=-1){
488 489 490 491 492
			mblk_t *tmp=NULL;
			while((tmp=getq(&s->rq))!=NULL){
				if (om!=NULL) freemsg(om);
				om=tmp;
			}
493 494 495 496 497 498 499
		}
		ms_mutex_unlock(&s->mutex);
		if (om!=NULL){
			timestamp=f->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
			mblk_set_timestamp_info(om,timestamp);
			mblk_set_marker_info(om,TRUE);
			ms_queue_put(f->outputs[0],om);
500 501 502 503 504 505 506
			if (s->last_frame_time!=-1){
				float frame_interval=(float)(curtime-s->last_frame_time)/1000.0;
				if (s->mean_inter_frame==0){
					s->mean_inter_frame=frame_interval;
				}else{
					s->mean_inter_frame=(0.8*s->mean_inter_frame)+(0.2*frame_interval);
				}
aymeric's avatar
aymeric committed
507
			}
508 509 510 511 512
			s->last_frame_time=curtime;
		}
		s->th_frame_count++;
		if (s->th_frame_count%50==0 && s->mean_inter_frame!=0){
			ms_message("Captured mean fps=%f, expected=%f",1/s->mean_inter_frame, s->fps);
aymeric's avatar
aymeric committed
513 514 515 516
		}
	}
}

517
static void msv4l2_postprocess(MSFilter *f){
aymeric's avatar
aymeric committed
518
	V4l2State *s=(V4l2State*)f->data;
519

520
	s->thread_run = FALSE;
521 522
	if(s->thread) {
		ms_thread_join(s->thread,NULL);
523
		ms_message("msv4l2 thread has joined.");
524 525 526 527 528
	}
	else {
		ms_warning("msv4l2 thread was already stopped");
	}

529
	flushq(&s->rq,0);
aymeric's avatar
aymeric committed
530 531
}

532
static int msv4l2_set_fps(MSFilter *f, void *arg){
aymeric's avatar
aymeric committed
533 534 535 536 537
	V4l2State *s=(V4l2State*)f->data;
	s->fps=*(float*)arg;
	return 0;
}

538
static int msv4l2_set_vsize(MSFilter *f, void *arg){
aymeric's avatar
aymeric committed
539 540
	V4l2State *s=(V4l2State*)f->data;
	s->vsize=*(MSVideoSize*)arg;
541 542 543 544 545 546 547 548 549 550 551 552
	s->configured=FALSE;
	return 0;
}

static int msv4l2_check_configured(V4l2State *s){
	if (s->configured) return 0;
	if (s->fd!=-1){
		msv4l2_close(s);
	}
	if (msv4l2_open(s)==0){
		msv4l2_configure(s);
	}
aymeric's avatar
aymeric committed
553 554 555
	return 0;
}

556
static int msv4l2_get_vsize(MSFilter *f, void *arg){
aymeric's avatar
aymeric committed
557
	V4l2State *s=(V4l2State*)f->data;
558
	msv4l2_check_configured(s);
aymeric's avatar
aymeric committed
559 560 561 562
	*(MSVideoSize*)arg=s->vsize;
	return 0;
}

563
static int msv4l2_get_pixfmt(MSFilter *f, void *arg){
aymeric's avatar
aymeric committed
564
	V4l2State *s=(V4l2State*)f->data;
565
	msv4l2_check_configured(s);
aymeric's avatar
aymeric committed
566 567 568 569
	*(MSPixFmt*)arg=s->pix_fmt;
	return 0;
}

570
static int msv4l2_set_devfile(MSFilter *f, void *arg){
aymeric's avatar
aymeric committed
571 572 573 574 575 576
	V4l2State *s=(V4l2State*)f->data;
	if (s->dev) ms_free(s->dev);
	s->dev=ms_strdup((char*)arg);
	return 0;
}

577 578 579 580 581
static MSFilterMethod msv4l2_methods[]={
	{	MS_FILTER_SET_FPS	,	msv4l2_set_fps	},
	{	MS_FILTER_SET_VIDEO_SIZE,	msv4l2_set_vsize	},
	{	MS_FILTER_GET_VIDEO_SIZE,	msv4l2_get_vsize	},
	{	MS_FILTER_GET_PIX_FMT	,	msv4l2_get_pixfmt	},
aymeric's avatar
aymeric committed
582 583 584 585 586 587
	{	0			,	NULL		}
};

MSFilterDesc ms_v4l2_desc={
	.id=MS_V4L2_CAPTURE_ID,
	.name="MSV4L2Capture",
588
	.text=N_("A filter to grab pictures from Video4Linux2-powered cameras"),
aymeric's avatar
aymeric committed
589 590 591
	.category=MS_FILTER_OTHER,
	.ninputs=0,
	.noutputs=1,
592 593 594 595 596 597
	.init=msv4l2_init,
	.preprocess=msv4l2_preprocess,
	.process=msv4l2_process,
	.postprocess=msv4l2_postprocess,
	.uninit=msv4l2_uninit,
	.methods=msv4l2_methods
aymeric's avatar
aymeric committed
598 599 600 601
};

MS_FILTER_DESC_EXPORT(ms_v4l2_desc)

602
static MSFilter *msv4l2_create_reader(MSWebCam *obj){
aymeric's avatar
aymeric committed
603
	MSFilter *f=ms_filter_new(MS_V4L2_CAPTURE_ID);
604
	msv4l2_set_devfile(f,obj->name);
aymeric's avatar
aymeric committed
605 606 607
	return f;
}

608
static void msv4l2_detect(MSWebCamManager *obj);
aymeric's avatar
aymeric committed
609

610
static void msv4l2_cam_init(MSWebCam *cam){
aymeric's avatar
aymeric committed
611 612
}

613
MSWebCamDesc v4l2_card_desc={
aymeric's avatar
aymeric committed
614
	"V4L2",
615 616 617
	&msv4l2_detect,
	&msv4l2_cam_init,
	&msv4l2_create_reader,
aymeric's avatar
aymeric committed
618 619 620
	NULL
};

621
static void msv4l2_detect(MSWebCamManager *obj){
aymeric's avatar
aymeric committed
622 623 624 625 626 627 628 629
	struct v4l2_capability cap;
	char devname[32];
	int i;
	for(i=0;i<10;++i){
		int fd;
		snprintf(devname,sizeof(devname),"/dev/video%i",i);
		fd=open(devname,O_RDWR);
		if (fd!=-1){
630
			if (v4l2_ioctl (fd, VIDIOC_QUERYCAP, &cap)==0) {
aymeric's avatar
aymeric committed
631
				/* is a V4LV2 */
632 633 634 635 636 637
				uint32_t camera_caps = cap.capabilities;
#ifdef V4L2_CAP_DEVICE_CAPS
				if (cap.capabilities & V4L2_CAP_DEVICE_CAPS) {
					camera_caps = cap.device_caps;
				}
#endif
638 639 640 641 642 643 644 645 646
				if (((camera_caps & V4L2_CAP_VIDEO_CAPTURE)
#ifdef V4L2_CAP_VIDEO_CAPTURE_MPLANE
					|| (camera_caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
#endif
					) && !((camera_caps & V4L2_CAP_VIDEO_OUTPUT)
#ifdef V4L2_CAP_VIDEO_OUTPUT_MPLANE
					|| (camera_caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE)
#endif
					)) {
647 648 649 650
					MSWebCam *cam=ms_web_cam_new(&v4l2_card_desc);
					cam->name=ms_strdup(devname);
					ms_web_cam_manager_add_cam(obj,cam);
				}
aymeric's avatar
aymeric committed
651 652 653 654 655 656 657 658
			}
			close(fd);
		}
	}
}


#endif