comparison src/mediastreamer/msv4l.c @ 12024:e67993da8a22

[gaim-migrate @ 14317] I strongly suspect CruiseControl is going to yell at me for this. A voice chat API, GUI + mediastreamer. This is what I'm using for Google Talk. This doesn't actually do anything at all. There's no code in the Jabber plugin yet to use this API (although it Works For Me). All it will do is compile and link. If you're lucky. To build this, you should install oRTP from Linphone, Speex and iLBC (also from linphone, I believe). To not build this, ./configure --disable-vv. Most of the configure.ac and Makefile.am hackery was lifted right out of Linphone with a few modifications. It seems to work if you have everything installed or if you --disable-vv. I haven't really tested not having everything installed and not --disabling-vv. It's kinda funky to include all of mediastreamer in the source tree like this, but linphone doesn't build it as a separate library. I'll probably wind up writing them a patch to build it as a .so so we can link it dynamically instead. This code certainly isn't finished. It'll adapt as I progress on the Google code, but it's certainly of more use here in CVS than in my personal tree. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Wed, 09 Nov 2005 08:07:20 +0000
parents
children
comparison
equal deleted inserted replaced
12023:80faf1ca5280 12024:e67993da8a22
1 /*
2 The mediastreamer library aims at providing modular media processing and I/O
3 for linphone, but also for any telephony application.
4 Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "msv4l.h"
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/ioctl.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/mman.h>
30
31 char *v4l_palette_string[17]={
32 "none",
33 "GREY", /* Linear greyscale */
34 "HI240", /* High 240 cube (BT848) */
35 "RGB565", /* 565 16 bit RGB */
36 "RGB24", /* 24bit RGB */
37 "RGB32", /* 32bit RGB */
38 "RGB555", /* 555 15bit RGB */
39 "YUV422", /* YUV422 capture */
40 "YUYV",
41 "UYVY", /* The great thing about standards is ... */
42 "YUV420",
43 "YUV411", /* YUV411 capture */
44 "RAW", /* RAW capture (BT848) */
45 "YUV422P", /* YUV 4:2:2 Planar */
46 "YUV411P", /* YUV 4:1:1 Planar */
47 "YUV420P", /* YUV 4:2:0 Planar */
48 "YUV410P", /* YUV 4:1:0 Planar */
49 };
50
51 #define V4L_PALETTE_TO_STRING(pal) v4l_palette_string[(pal)]
52
53 MSFilterInfo v4l_info=
54 {
55 "Video4Linux",
56 0,
57 MS_FILTER_VIDEO_IO,
58 ms_v4l_new,
59 NULL
60 };
61
62
63 static MSV4lClass *ms_v4l_class=NULL;
64
65 MSFilter * ms_v4l_new()
66 {
67 MSV4l *obj;
68 obj=g_malloc0(sizeof(MSV4l));
69 if (ms_v4l_class==NULL)
70 {
71 ms_v4l_class=g_malloc0(sizeof(MSV4lClass));
72 ms_v4l_class_init(ms_v4l_class);
73 }
74 MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_v4l_class);
75 ms_v4l_init(obj);
76 return MS_FILTER(obj);
77 }
78
79 void ms_v4l_init(MSV4l *obj)
80 {
81 ms_video_source_init(MS_VIDEO_SOURCE(obj));
82 /* initialize the static buffer */
83 obj->use_mmap=0;
84 obj->fd=-1;
85 obj->device = g_strdup("/dev/video0");
86 obj->count=0;
87 obj->allocdbuf=NULL;
88 obj->grab_image=FALSE;
89 obj->image_grabbed=NULL;
90 obj->cond=g_cond_new();
91 obj->stopcond=g_cond_new();
92 obj->v4lthread=NULL;
93 obj->thread_exited=FALSE;
94 obj->frame=0;
95 MS_VIDEO_SOURCE(obj)->format="RGB24"; /*default value */
96 MS_VIDEO_SOURCE(obj)->width = VIDEO_SIZE_CIF_W; /*default value */
97 MS_VIDEO_SOURCE(obj)->height = VIDEO_SIZE_CIF_H; /*default value */
98 }
99
100 void ms_v4l_class_init(MSV4lClass *klass)
101 {
102 ms_video_source_class_init(MS_VIDEO_SOURCE_CLASS(klass));
103 MS_VIDEO_SOURCE_CLASS(klass)->start=(void (*)(MSVideoSource *))ms_v4l_start;
104 MS_VIDEO_SOURCE_CLASS(klass)->stop=(void (*)(MSVideoSource *))ms_v4l_stop;
105 MS_VIDEO_SOURCE_CLASS(klass)->set_device=(int (*)(MSVideoSource*,const gchar*))ms_v4l_set_device;
106 MS_FILTER_CLASS(klass)->process=(void (*)(MSFilter *))v4l_process;
107 MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_v4l_destroy;
108 ms_filter_class_set_name(MS_FILTER_CLASS(klass),"msv4l");
109 MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&v4l_info;
110 }
111
112 void *v4l_thread(MSV4l *obj);
113
114 void ms_v4l_start(MSV4l *obj)
115 {
116 int err;
117 ms_filter_lock(MS_FILTER(obj));
118 obj->fd=open(obj->device,O_RDONLY);
119 if (obj->fd<0)
120 {
121 g_warning("MSV4l: cannot open video device: %s.",strerror(errno));
122 }else{
123 err=v4l_configure(obj);
124 if (err<0)
125 {
126 g_warning("MSV4l: could not get configuration of video device");
127 }
128 }
129 obj->thread_exited=FALSE;
130 obj->v4lthread=g_thread_create((GThreadFunc)v4l_thread,(gpointer)obj,FALSE,NULL);
131 while(!obj->thread_run) g_cond_wait(obj->cond,MS_FILTER(obj)->lock);
132 ms_filter_unlock(MS_FILTER(obj));
133 }
134
135 void ms_v4l_stop(MSV4l *obj)
136 {
137 ms_filter_lock(MS_FILTER(obj));
138 obj->thread_run=FALSE;
139 obj->grab_image=FALSE;
140 g_cond_signal(obj->cond);
141 if (obj->fd>0)
142 {
143 close(obj->fd);
144 obj->fd=-1;
145 if (!obj->use_mmap){
146 if (obj->allocdbuf!=NULL) ms_buffer_destroy(obj->allocdbuf);
147 else obj->allocdbuf=NULL;
148 }else
149 {
150 munmap(obj->mmapdbuf,obj->vmbuf.size);
151 obj->mmapdbuf=NULL;
152 }
153 obj->image_grabbed=NULL;
154 }
155 while(!obj->thread_exited) g_cond_wait(obj->stopcond,MS_FILTER(obj)->lock);
156 obj->v4lthread=NULL;
157 ms_filter_unlock(MS_FILTER(obj));
158 }
159
160 gint ms_v4l_get_width(MSV4l *v4l)
161 {
162 return v4l->win.width;
163 }
164
165 gint ms_v4l_get_height(MSV4l *v4l)
166 {
167 return v4l->win.height;
168 }
169
170 int ms_v4l_set_device(MSV4l *obj, const gchar *device)
171 {
172 if (obj->device!=NULL) g_free(obj->device);
173 obj->device=g_strdup(device);
174 return 0;
175 }
176
177 void ms_v4l_set_size(MSV4l *obj, gint width, gint height)
178 {
179 gint err;
180 gboolean restart = FALSE;
181
182 if (obj->fd == -1)
183 {
184 obj->fd = open(obj->device, O_RDONLY);
185 if (obj->fd < 0)
186 {
187 g_warning("MSV4l: cannot open video device: %s.",strerror(errno));
188 return;
189 }
190 } else
191 restart = TRUE;
192
193 ms_filter_lock(MS_FILTER(obj));
194 err = ioctl(obj->fd, VIDIOCGCAP, &obj->cap);
195 if (err != 0)
196 {
197 g_warning("MSV4l: cannot get device capabilities: %s.",strerror(errno));
198 return;
199 }
200 if (width <= obj->cap.maxwidth && width >= obj->cap.minwidth &&
201 height <= obj->cap.maxheight && height >= obj->cap.minheight)
202 {
203 MS_VIDEO_SOURCE(obj)->width = width;
204 MS_VIDEO_SOURCE(obj)->height = height;
205 }
206 ms_filter_unlock(MS_FILTER(obj));
207
208 if (restart)
209 {
210 ms_v4l_stop(obj);
211 ms_v4l_start(obj);
212 }
213
214 }
215
216 int v4l_configure(MSV4l *f)
217 {
218 gint err;
219 gint i;
220 struct video_channel *chan=&f->channel;
221 struct video_window *win=&f->win;
222 struct video_picture *pict=&f->pict;
223 struct video_mmap *vmap=&f->vmap;
224 struct video_mbuf *vmbuf=&f->vmbuf;
225 struct video_capture *vcap=&f->vcap;
226 int found=0;
227
228 err=ioctl(f->fd,VIDIOCGCAP,&f->cap);
229 if (err!=0)
230 {
231 g_warning("MSV4l: cannot get device capabilities: %s.",strerror(errno));
232 return -1;
233 }
234 MS_VIDEO_SOURCE(f)->dev_name=f->cap.name;
235
236 for (i=0;i<f->cap.channels;i++)
237 {
238 chan->channel=i;
239 err=ioctl(f->fd,VIDIOCGCHAN,chan);
240 if (err==0)
241 {
242 g_message("Getting video channel %s",chan->name);
243 switch(chan->type){
244 case VIDEO_TYPE_TV:
245 g_message("Channel is a TV.");
246 break;
247 case VIDEO_TYPE_CAMERA:
248 g_message("Channel is a camera");
249 break;
250 default:
251 g_warning("unknown video channel type.");
252 }
253 found=1;
254 break; /* find the first channel */
255 }
256 }
257 if (found) g_message("A valid video channel was found.");
258 /* select this channel */
259 ioctl(f->fd,VIDIOCSCHAN,chan);
260
261 /* set/get the resolution */
262 err = -1;
263 /*
264 if (f->cap.type & VID_TYPE_SUBCAPTURE) {
265 vcap->x = vcap->y = 0;
266 vcap->width = MS_VIDEO_SOURCE(f)->width;
267 vcap->height = MS_VIDEO_SOURCE(f)->height;
268 err = ioctl(f->fd, VIDIOCSCAPTURE, vcap);
269 if (err > -1) {
270 f->width = MS_VIDEO_SOURCE(f)->width;
271 f->height = MS_VIDEO_SOURCE(f)->height;
272 }
273 }
274 */
275 if (err < 0) {
276 win->x = win->y = 0;
277 win->width = MS_VIDEO_SOURCE(f)->width;
278 win->height = MS_VIDEO_SOURCE(f)->height;
279 win->clipcount = win->flags = 0;
280 win->clips = NULL;
281 err=ioctl(f->fd,VIDIOCSWIN,win);
282 if (err < 0) {
283 g_warning("Could not set video window properties: %s",strerror(errno));
284
285 err=ioctl(f->fd,VIDIOCGWIN,win);
286 if (err < 0) {
287 g_warning("Could not set video window properties: %s",strerror(errno));
288 return -1;
289 }
290 f->width = win->width;
291 f->height = win->height;
292 }
293 else {
294 f->width = MS_VIDEO_SOURCE(f)->width;
295 f->height = MS_VIDEO_SOURCE(f)->height;
296 }
297 }
298
299 /* get picture properties */
300 err=ioctl(f->fd,VIDIOCGPICT,pict);
301 if (err<0){
302 g_warning("Could not get picture properties: %s",strerror(errno));
303 return -1;
304 }
305 g_message("Picture properties: depth=%i, palette=%i.",pict->depth, pict->palette);
306 f->bsize=(pict->depth/8) * f->height * f->width;
307
308 /* try to get mmap properties */
309 err=ioctl(f->fd,VIDIOCGMBUF,vmbuf);
310 if (err<0){
311 g_warning("Could not get mmap properties: %s",strerror(errno));
312 f->use_mmap=0;
313 }else
314 {
315 if (vmbuf->size>0){
316 f->use_mmap=1;
317 /* do the mmap */
318 f->mmapdbuf=mmap((void*)f,vmbuf->size,PROT_READ,MAP_PRIVATE,f->fd,0);
319 if (f->mmapdbuf==(void*)-1) {
320 g_warning("Could not mmap. Using read instead.");
321 f->use_mmap=0;
322 f->mmapdbuf=NULL;
323 }else {
324 /* initialize the mediastreamer buffers */
325 gint i;
326 g_message("Using %i-frames mmap'd buffer.",vmbuf->frames);
327 for(i=0;i<vmbuf->frames;i++){
328 f->img[i].buffer=f->mmapdbuf+vmbuf->offsets[i];
329 f->img[i].size=f->bsize;
330 f->img[i].ref_count=1;
331 }
332 f->frame=0;
333 }
334 } else g_warning("This device cannot support mmap.");
335 }
336
337 /* initialize the video map structure */
338 vmap->width=win->width;
339 vmap->height=win->height;
340 vmap->format=pict->palette;
341 vmap->frame=0;
342
343 MS_VIDEO_SOURCE(f)->format=V4L_PALETTE_TO_STRING(pict->palette);
344 return 0;
345 }
346
347 #define BPP 3
348 static inline
349 void crop( guchar *src, gint s_width, gint s_height, guchar *dest, gint d_width, gint d_height)
350 {
351 register int i;
352 register int stride = d_width*BPP;
353 register guchar *s = src, *d = dest;
354 s += ((s_height - d_height)/2 * s_width * BPP) + ((s_width - d_width)/2 * BPP);
355 for (i = 0; i < d_height; i++, d += stride, s += s_width * BPP)
356 memcpy( d, s, stride);
357 }
358
359 MSBuffer * v4l_grab_image_mmap(MSV4l *obj){
360 struct video_mmap *vmap=&obj->vmap;
361 struct video_mbuf *vmbuf=&obj->vmbuf;
362 int err;
363 int syncframe;
364 int jitter=vmbuf->frames-1;
365 obj->query_frame=(obj->frame) % vmbuf->frames;
366 ms_trace("v4l_mmap_process: query_frame=%i",
367 obj->query_frame);
368 vmap->frame=obj->query_frame;
369 err=ioctl(obj->fd,VIDIOCMCAPTURE,vmap);
370 if (err<0) {
371 g_warning("v4l_mmap_process: error in VIDIOCMCAPTURE: %s.",strerror(errno));
372 return NULL;
373 }
374 syncframe=(obj->frame-jitter);
375 obj->frame++;
376 if (syncframe>=0){
377 syncframe=syncframe%vmbuf->frames;
378 err=ioctl(obj->fd,VIDIOCSYNC,&syncframe);
379 if (err<0) {
380 g_warning("v4l_mmap_process: error in VIDIOCSYNC: %s.",strerror(errno));
381 return NULL;
382 }
383 }else {
384 return NULL;
385 }
386 /* not particularly efficient - hope for a capture source that
387 provides subcapture or setting window */
388
389 if (obj->width != MS_VIDEO_SOURCE(obj)->width || obj->height != MS_VIDEO_SOURCE(obj)->height){
390 guchar tmp[obj->bsize];
391 crop((guchar*) obj->img[syncframe].buffer, obj->width, obj->height, tmp,
392 MS_VIDEO_SOURCE(obj)->width, MS_VIDEO_SOURCE(obj)->height);
393 memcpy(obj->img[syncframe].buffer, tmp, MS_VIDEO_SOURCE(obj)->width *
394 MS_VIDEO_SOURCE(obj)->height * obj->pict.depth/8);
395 }
396 return &obj->img[syncframe];
397 }
398
399 MSBuffer *v4l_grab_image_read(MSV4l *obj){
400 int err;
401 if (obj->allocdbuf==NULL){
402 obj->allocdbuf=ms_buffer_new(obj->bsize);
403 obj->allocdbuf->ref_count++;
404 }
405 if (obj->width != MS_VIDEO_SOURCE(obj)->width || obj->height != MS_VIDEO_SOURCE(obj)->height)
406 {
407 guchar tmp[obj->bsize];
408 err=read(obj->fd,tmp,obj->bsize);
409 if (err>0)
410 crop(tmp, obj->width, obj->height, obj->allocdbuf->buffer, MS_VIDEO_SOURCE(obj)->width, MS_VIDEO_SOURCE(obj)->height);
411 else {
412 g_warning("MSV4l: Fail to read(): %s",strerror(errno));
413 return NULL;
414 }
415 }
416 else
417 {
418 err=read(obj->fd,obj->allocdbuf->buffer,obj->bsize);
419 if (err<0){
420 g_warning("MSV4l: Fail to read(): %s",strerror(errno));
421 return NULL;
422 }
423 }
424 return obj->allocdbuf;
425 }
426
427
428 MSBuffer * v4l_make_mire(MSV4l *obj){
429 gchar *data;
430 int i,j,line,pos;
431 int patternw=obj->parent.width/6;
432 int patternh=obj->parent.height/6;
433 int red,green=0,blue=0;
434 if (obj->allocdbuf==NULL){
435 obj->allocdbuf=ms_buffer_new(obj->parent.width*obj->parent.height*3);
436 obj->allocdbuf->ref_count++;
437 }
438 data=obj->allocdbuf->buffer;
439 for (i=0;i<obj->parent.height;++i){
440 line=i*obj->parent.width*3;
441 if ( ((i+obj->count)/patternh) & 0x1) red=255;
442 else red= 0;
443 for (j=0;j<obj->parent.width;++j){
444 int tmp;
445 pos=line+(j*3);
446
447 if ( ((j+obj->count)/patternw) & 0x1) blue=255;
448 else blue= 0;
449
450 data[pos]=red;
451 data[pos+1]=green;
452 data[pos+2]=blue;
453 }
454 }
455 obj->count++;
456 usleep(60000);
457 return obj->allocdbuf;
458 }
459
460
461 void *v4l_thread(MSV4l *obj){
462 GMutex *mutex=MS_FILTER(obj)->lock;
463 g_mutex_lock(mutex);
464 obj->thread_run=TRUE;
465 g_cond_signal(obj->cond);
466 while(obj->thread_run){
467 g_cond_wait(obj->cond,mutex);
468 if (obj->grab_image){
469 MSBuffer *grabbed;
470 g_mutex_unlock(mutex);
471 if (obj->fd>0){
472 if (obj->use_mmap){
473 grabbed=v4l_grab_image_mmap(obj);
474 }else{
475 grabbed=v4l_grab_image_read(obj);
476 }
477 }else grabbed=v4l_make_mire(obj);
478 g_mutex_lock(mutex);
479 if (grabbed){
480 obj->image_grabbed=grabbed;
481 obj->grab_image=FALSE;
482 }
483 }
484 }
485 g_cond_signal(obj->stopcond);
486 obj->thread_exited=TRUE;
487 g_mutex_unlock(mutex);
488 return NULL;
489 }
490
491
492
493
494 void v4l_process(MSV4l * obj)
495 {
496 GMutex *mutex=MS_FILTER(obj)->lock;
497 g_mutex_lock(mutex);
498 if (obj->image_grabbed!=NULL){
499 MSMessage *m=ms_message_alloc();
500 ms_message_set_buf(m,obj->image_grabbed);
501 ms_queue_put(MS_FILTER(obj)->outqueues[0],m);
502 obj->image_grabbed=NULL;
503 }else{
504 obj->grab_image=TRUE;
505 g_cond_signal(obj->cond);
506 }
507 g_mutex_unlock(mutex);
508 }
509
510 void ms_v4l_uninit(MSV4l *obj)
511 {
512 if (obj->device!=NULL) {
513 g_free(obj->device);
514 obj->device=NULL;
515 }
516 if (obj->v4lthread!=NULL) ms_v4l_stop(obj);
517 if (obj->allocdbuf!=NULL) {
518 ms_buffer_destroy(obj->allocdbuf);
519 obj->allocdbuf=NULL;
520 }
521 g_cond_free(obj->cond);
522 g_cond_free(obj->stopcond);
523 ms_filter_uninit(MS_FILTER(obj));
524 }
525
526 void ms_v4l_destroy(MSV4l *obj)
527 {
528 ms_v4l_uninit(obj);
529 g_free(obj);
530 }