Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 #include "config.h"
  2 
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 #include <unistd.h>
  6 #include <fcntl.h>
  7 #include <errno.h>
  8 #include <signal.h>
  9 #include <string.h>
 10 #include <pthread.h>
 11 #include <inttypes.h>
 12 #include <sys/time.h>
 13 #include <sys/types.h>
 14 #include <sys/socket.h>
 15 #include <sys/ipc.h>
 16 #include <sys/shm.h>
 17 
 18 #include "grab-ng.h"
 19 #include "commands.h"       /* FIXME: *drv globals */
 20 #include "sound.h"
 21 #include "capture.h"
 22 #include "webcam.h"
 23 
 24 #define MAX_THREADS    4
 25 #define REORDER_SIZE  32
 26 
 27 /*-------------------------------------------------------------------------*/
 28 /* data fifos (audio/video)                                                */
 29 
 30 void
 31 fifo_init(struct FIFO *fifo, char *name, int slots, int writers)
 32 {
 33     pthread_mutex_init(&fifo->lock, NULL);
 34     pthread_cond_init(&fifo->hasdata, NULL);
 35     fifo->name    = name;
 36     fifo->slots   = slots;
 37     fifo->writers = writers;
 38     fifo->read    = 0;
 39     fifo->write   = 0;
 40     fifo->eof     = 0;
 41     fifo->max     = 0;
 42 }
 43 
 44 int
 45 fifo_put(struct FIFO *fifo, void *data)
 46 {
 47     int full;
 48     
 49     pthread_mutex_lock(&fifo->lock);
 50     if (NULL == data) {
 51         fifo->eof++;
 52         if (debug)
 53             fprintf(stderr,"fifo %s: EOF %d/%d\n",
 54                     fifo->name,fifo->eof,fifo->writers);
 55         if (fifo->writers == fifo->eof)
 56             pthread_cond_broadcast(&fifo->hasdata);
 57         pthread_mutex_unlock(&fifo->lock);
 58         return 0;
 59     }
 60     if ((fifo->write + 1) % fifo->slots == fifo->read) {
 61         pthread_mutex_unlock(&fifo->lock);
 62         fprintf(stderr,"fifo %s is full\n",fifo->name);
 63         return -1;
 64     }
 65     if (debug > 1)
 66         fprintf(stderr,"put %s %d=%p [pid=%d]\n",
 67                 fifo->name,fifo->write,data,getpid());
 68     fifo->data[fifo->write] = data;
 69     fifo->write++;
 70     full = (fifo->write + fifo->slots - fifo->read) % fifo->slots;
 71     if (fifo->max < full)
 72         fifo->max = full;
 73     if (fifo->write >= fifo->slots)
 74         fifo->write = 0;
 75     pthread_cond_signal(&fifo->hasdata);
 76     pthread_mutex_unlock(&fifo->lock);
 77     return 0;
 78 }
 79 
 80 void*
 81 fifo_get(struct FIFO *fifo)
 82 {
 83     void *data;
 84 
 85     pthread_mutex_lock(&fifo->lock);
 86     while (fifo->write == fifo->read && fifo->writers != fifo->eof) {
 87         pthread_cond_wait(&fifo->hasdata, &fifo->lock);
 88     }
 89     if (fifo->write == fifo->read) {
 90         pthread_cond_signal(&fifo->hasdata);
 91         pthread_mutex_unlock(&fifo->lock);
 92         return NULL;
 93     }
 94     if (debug > 1)
 95         fprintf(stderr,"get %s %d=%p [pid=%d]\n",
 96                 fifo->name,fifo->read,fifo->data[fifo->read],getpid());
 97     data = fifo->data[fifo->read];
 98     fifo->read++;
 99     if (fifo->read >= fifo->slots)
100         fifo->read = 0;
101     pthread_mutex_unlock(&fifo->lock);
102     return data;
103 }
104 
105 static void*
106 flushit(void *arg)
107 {
108     int old;
109 
110     pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,&old);
111     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&old);
112     for (;;) {
113         sleep(1);
114         sync();
115     }
116     return NULL;
117 }
118 
119 /*-------------------------------------------------------------------------*/
120 /* color space conversion / compression thread                             */
121 
122 struct ng_convthread_handle {
123     /* converter data / state */
124     struct ng_convert_handle *c;
125 
126     /* thread data */
127     struct FIFO              *in;
128     struct FIFO              *out;
129 };
130 
131 void*
132 ng_convert_thread(void *arg)
133 {
134     struct ng_convthread_handle *h = arg;
135     struct ng_video_buf *in, *out;
136     
137     if (debug)
138         fprintf(stderr,"convert_thread start [pid=%d]\n",getpid());
139     ng_convert_init(h->c);
140     for (;;) {
141         in  = fifo_get(h->in);
142         if (NULL == in)
143             break;
144         out = ng_convert_frame(h->c,NULL,in);
145         if (NULL != webcam && 0 == webcam_put(webcam,out)) {
146             free(webcam);
147             webcam = NULL;
148         }
149         fifo_put(h->out,out);
150     }
151     fifo_put(h->out,NULL);
152     ng_convert_fini(h->c);
153     if (debug)
154         fprintf(stderr,"convert_thread done [pid=%d]\n",getpid());
155     return NULL;
156 }
157 
158 /*-------------------------------------------------------------------------*/
159 /* parameter negotiation -- look what the driver can do and what           */
160 /* convert functions are available                                         */
161 
162 int
163 ng_grabber_setformat(struct ng_video_fmt *fmt, int fix_ratio)
164 {
165     struct ng_video_fmt gfmt;
166     int rc;
167     
168     /* no capture support */
169     if (!(f_drv & CAN_CAPTURE))
170         return -1;
171 
172     /* try setting the format */
173     gfmt = *fmt;
174     rc = drv->setformat(h_drv,&gfmt);
175     if (debug)
176         fprintf(stderr,"setformat: %s (%dx%d): %s\n",
177                 ng_vfmt_to_desc[gfmt.fmtid],
178                 gfmt.width,gfmt.height,
179                 (0 == rc) ? "ok" : "failed");
180     if (0 != rc)
181         return -1;
182 
183     if (fix_ratio) {
184         /* fixup aspect ratio if needed */
185         ng_ratio_fixup(&gfmt.width, &gfmt.height, NULL, NULL);
186         gfmt.bytesperline = 0;
187         if (0 != drv->setformat(h_drv,&gfmt)) {
188             fprintf(stderr,"Oops: ratio size renegotiation failed\n");
189             exit(1);
190         }
191     }
192 
193     /* return the real format the grabber uses now */
194     *fmt = gfmt;
195     return 0;
196 }
197 
198 struct ng_video_conv*
199 ng_grabber_findconv(struct ng_video_fmt *fmt,
200                     int fix_ratio)
201 {
202     struct ng_video_fmt  gfmt;
203     struct ng_video_conv *conv;
204     int i;
205     
206     /* check all available conversion functions */
207     for (i = 0;;) {
208         conv = ng_conv_find_to(fmt->fmtid, &i);
209         if (NULL == conv)
210             break;
211         gfmt = *fmt;
212         gfmt.fmtid = conv->fmtid_in;
213         if (0 == ng_grabber_setformat(&gfmt,fix_ratio))
214             goto found;
215     }
216     fprintf(stderr,"no way to get: %dx%d %s\n",
217             fmt->width,fmt->height,ng_vfmt_to_desc[fmt->fmtid]);
218     return NULL;
219 
220  found:
221     *fmt = gfmt;
222     return conv;
223 }
224 
225 struct ng_video_buf*
226 ng_grabber_grab_image(int single)
227 {
228     return single ? drv->getimage(h_drv) : drv->nextframe(h_drv);
229 }
230 
231 struct ng_video_buf*
232 ng_grabber_get_image(struct ng_video_fmt *fmt)
233 {
234     struct ng_video_fmt gfmt;
235     struct ng_video_conv *conv;
236     struct ng_convert_handle *ch;
237     struct ng_video_buf *buf;
238     
239     if (0 == ng_grabber_setformat(fmt,1))
240         return ng_grabber_grab_image(1);
241     gfmt = *fmt;
242     if (NULL == (conv = ng_grabber_findconv(&gfmt,1)))
243         return NULL;
244     ch = ng_convert_alloc(conv,&gfmt,fmt);
245     if (NULL == (buf = ng_grabber_grab_image(1)))
246         return NULL;
247     buf = ng_convert_single(ch,buf);
248     return buf;
249 }
250 
251 /*-------------------------------------------------------------------------*/
252 
253 struct movie_handle {
254     /* general */
255     pthread_mutex_t           lock;
256     const struct ng_writer    *writer;
257     void                      *handle;
258     pthread_t                 tflush;
259     uint64_t                  start;
260     uint64_t                  rts;
261     uint64_t                  stopby;
262     int                       slots;
263 
264     /* video */
265     struct ng_video_fmt       vfmt;
266     int                       fps;
267     int                       frames;
268     int                       seq;
269     struct FIFO               vfifo;
270     pthread_t                 tvideo;
271     uint64_t                  vts;
272 
273     /* video converter thread */
274     struct FIFO               cfifo;
275     int                       cthreads;
276     struct ng_convthread_handle *hconv[MAX_THREADS];
277     pthread_t                 tconv[MAX_THREADS];
278 
279     /* audio */
280     const struct ng_dsp_driver *dsp;
281     void                      *hdsp;
282     struct ng_audio_fmt       afmt;
283     unsigned long             bytes_per_sec;
284     unsigned long             bytes;
285     struct FIFO               afifo;
286     pthread_t                 taudio;
287     pthread_t                 raudio;
288     uint64_t                  ats;
289 
290     uint64_t                  rdrift;
291     uint64_t                  vdrift;
292 };
293 
294 static void*
295 writer_audio_thread(void *arg)
296 {
297     struct movie_handle *h = arg;
298     struct ng_audio_buf *buf;
299 
300     if (debug)
301         fprintf(stderr,"writer_audio_thread start [pid=%d]\n",getpid());
302     for (;;) {
303         buf = fifo_get(&h->afifo);
304         if (NULL == buf)
305             break;
306         pthread_mutex_lock(&h->lock);
307         h->writer->wr_audio(h->handle,buf);
308         pthread_mutex_unlock(&h->lock);
309         free(buf);
310     }
311     if (debug)
312         fprintf(stderr,"writer_audio_thread done\n");
313     return NULL;
314 }
315 
316 /*
317  * with multiple compression threads we might receive
318  * the frames out-of-order
319  */
320 static void*
321 writer_video_thread(void *arg)
322 {
323     struct movie_handle *h = arg;
324     struct ng_video_buf *buf;
325     struct ng_video_buf *reorder[REORDER_SIZE];
326     int seq,slot;
327 
328     if (debug)
329         fprintf(stderr,"writer_video_thread start [pid=%d]\n",getpid());
330     seq = 0;
331     memset(&reorder,0,sizeof(reorder));
332     for (;;) {
333         buf = fifo_get(&h->vfifo);
334         if (NULL == buf)
335             break;
336         slot = buf->info.seq % REORDER_SIZE;
337         if (debug > 1)
338             fprintf(stderr,"video write: get seq=%d [%d]\n",
339                     buf->info.seq,slot);
340         if (reorder[slot]) {
341             fprintf(stderr,"panic: reorder buffer full\n");
342             exit(1);
343         }
344         reorder[slot] = buf;
345         
346         for (;;) {
347             slot = seq % REORDER_SIZE;
348             if (NULL == reorder[slot])
349                 break;
350             buf = reorder[slot];
351             reorder[slot] = NULL;
352             if (debug > 1)
353                 fprintf(stderr,"video write: put seq=%d [%d/%d]\n",
354                         buf->info.seq,slot,seq);
355             seq++;
356 
357             pthread_mutex_lock(&h->lock);
358             h->writer->wr_video(h->handle,buf);
359             if (buf->info.twice)
360                 h->writer->wr_video(h->handle,buf);
361             pthread_mutex_unlock(&h->lock);
362             ng_release_video_buf(buf);
363         }
364     }
365     if (debug)
366         fprintf(stderr,"writer_video_thread done\n");
367     return NULL;
368 }
369 
370 static void*
371 record_audio_thread(void *arg)
372 {
373     struct movie_handle *h = arg;
374     struct ng_audio_buf *buf;
375 
376     if (debug)
377         fprintf(stderr,"record_audio_thread start [pid=%d]\n",getpid());
378     for (;;) {
379         buf = h->dsp->read(h->hdsp,h->stopby);
380         if (NULL == buf)
381             break;
382         if (0 == buf->size)
383             continue;
384         h->ats    = buf->info.ts;
385         h->rts    = ng_get_timestamp() - h->start;
386         h->rdrift = h->rts - h->ats;
387         h->vdrift = h->vts - h->ats;
388         if (0 != fifo_put(&h->afifo,buf))
389             free(buf);
390     }
391     fifo_put(&h->afifo,NULL);
392     if (debug)
393         fprintf(stderr,"record_audio_thread done\n");
394     return NULL;
395 }
396 
397 struct movie_handle*
398 movie_writer_init(char *moviename, char *audioname,
399                   const struct ng_writer *writer, 
400                   struct ng_video_fmt *video,const void *priv_video,int fps,
401                   struct ng_audio_fmt *audio,const void *priv_audio,char *dsp,
402                   int slots, int threads)
403 {
404     struct movie_handle *h;
405     struct ng_video_conv *conv;
406     void *dummy;
407     int i;
408 
409     if (debug)
410         fprintf(stderr,"movie_init_writer start\n");
411     h = malloc(sizeof(*h));
412     if (NULL == h)
413         return NULL;
414     memset(h,0,sizeof(*h));
415     pthread_mutex_init(&h->lock, NULL);
416     h->writer = writer;
417     h->slots = slots;
418 
419     /* audio */
420     if (audio->fmtid != AUDIO_NONE) {
421         h->dsp = ng_dsp_open(dsp,audio,1,&h->hdsp);
422         if (NULL == h->dsp) {
423             free(h);
424             return NULL;
425         }
426         fifo_init(&h->afifo,"audio",slots,1);
427         pthread_create(&h->taudio,NULL,writer_audio_thread,h);
428         h->bytes_per_sec = ng_afmt_to_bits[audio->fmtid] *
429             ng_afmt_to_channels[audio->fmtid] * audio->rate / 8;
430         h->afmt = *audio;
431     }
432 
433     /* video */
434     if (video->fmtid != VIDEO_NONE) {
435         if (0 == ng_grabber_setformat(video,1)) {
436             /* native format works -- no conversion needed */
437             fifo_init(&h->vfifo,"video",slots,1);
438             pthread_create(&h->tvideo,NULL,writer_video_thread,h);
439         } else {
440             /* have to convert video frames */
441             struct ng_video_fmt gfmt = *video;
442             if (NULL == (conv = ng_grabber_findconv(&gfmt,1))) {
443                 if (h->afmt.fmtid != AUDIO_NONE)
444                     h->dsp->close(h->hdsp);
445                 free(h);
446                 return NULL;
447             }
448             h->cthreads = threads;
449             if (h->cthreads < 1)
450                 h->cthreads = 1;
451             if (h->cthreads > MAX_THREADS)
452                 h->cthreads = MAX_THREADS;
453             fifo_init(&h->vfifo,"video",slots,h->cthreads);
454             fifo_init(&h->cfifo,"conv",slots,1);
455             pthread_create(&h->tvideo,NULL,writer_video_thread,h);
456             for (i = 0; i < h->cthreads; i++) {
457                 h->hconv[i] = malloc(sizeof(struct ng_convthread_handle));
458                 memset(h->hconv[i],0,sizeof(struct ng_convthread_handle));
459                 h->hconv[i]->c   = ng_convert_alloc(conv,&gfmt,video);
460                 h->hconv[i]->in  = &h->cfifo;
461                 h->hconv[i]->out = &h->vfifo;
462                 pthread_create(&h->tconv[i],NULL,ng_convert_thread,
463                                h->hconv[i]);
464             }
465         }
466         h->vfmt = *video;
467         h->fps  = fps;
468     }   
469     
470     /* open file */
471     h->handle = writer->wr_open(moviename,audioname,
472                                 video,priv_video,fps,
473                                 audio,priv_audio);
474     if (debug)
475         fprintf(stderr,"movie_init_writer end (h=%p)\n",h->handle);
476     if (NULL != h->handle)
477         return h;
478 
479     /* Oops -- wr_open() didn't work.  cleanup.  */
480     if (h->afmt.fmtid != AUDIO_NONE) {
481         pthread_cancel(h->taudio);
482         pthread_join(h->taudio,&dummy);
483         h->dsp->close(h->hdsp);
484     }
485     if (h->vfmt.fmtid != VIDEO_NONE) {
486         pthread_cancel(h->tvideo);
487         pthread_join(h->tvideo,&dummy);
488     }
489     for (i = 0; i < h->cthreads; i++) {
490         pthread_cancel(h->tconv[i]);
491         pthread_join(h->tconv[i],&dummy);
492     }
493     free(h);
494     return NULL;
495 }
496 
497 int
498 movie_writer_start(struct movie_handle *h)
499 {
500     int rc = 0;
501 
502     if (debug)
503         fprintf(stderr,"movie_writer_start\n");
504     h->start = ng_get_timestamp();
505     if (h->afmt.fmtid != AUDIO_NONE)
506         if (0 != h->dsp->startrec(h->hdsp))
507             rc = -1;
508     if (h->vfmt.fmtid != VIDEO_NONE)
509         if (0 != drv->startvideo(h_drv,h->fps,h->slots))
510             rc = -1;
511     if (h->afmt.fmtid != AUDIO_NONE)
512         pthread_create(&h->raudio,NULL,record_audio_thread,h);
513     pthread_create(&h->tflush,NULL,flushit,NULL);
514     return rc;
515 }
516 
517 int
518 movie_writer_stop(struct movie_handle *h)
519 {
520     char line[128];
521     uint64_t  stopby;
522     int frames,i;
523     void *dummy;
524 
525     if (debug)
526         fprintf(stderr,"movie_writer_stop\n");
527 
528     if (h->vfmt.fmtid != VIDEO_NONE && h->afmt.fmtid != AUDIO_NONE) {
529         for (frames = 0; frames < 16; frames++) {
530             stopby = (uint64_t)(h->frames + frames) * (uint64_t)1000000000000 / h->fps;
531             if (stopby > h->ats)
532                 break;
533         }
534         frames++;
535         h->stopby = (uint64_t)(h->frames + frames) * (uint64_t)1000000000000 / h->fps;
536         while (frames) {
537             movie_grab_put_video(h,NULL);
538             frames--;
539         }
540     } else if (h->afmt.fmtid != AUDIO_NONE) {
541         h->stopby = h->ats;
542     }
543 
544     /* send EOF */
545     if (h->cthreads)
546         fifo_put(&h->cfifo,NULL);
547     else
548         fifo_put(&h->vfifo,NULL);
549 
550     /* join threads */
551     if (h->afmt.fmtid != AUDIO_NONE) {
552         pthread_join(h->raudio,&dummy);
553         pthread_join(h->taudio,&dummy);
554     }
555     if (h->vfmt.fmtid != VIDEO_NONE)
556         pthread_join(h->tvideo,&dummy);
557     for (i = 0; i < h->cthreads; i++)
558         pthread_join(h->tconv[i],&dummy);
559     pthread_cancel(h->tflush);
560     pthread_join(h->tflush,&dummy);
561 
562     /* close file */
563     h->writer->wr_close(h->handle);
564     if (h->afmt.fmtid != AUDIO_NONE)
565         h->dsp->close(h->hdsp);
566     if (h->vfmt.fmtid != VIDEO_NONE)
567         drv->stopvideo(h_drv);
568 
569     /* fifo stats */
570     sprintf(line, "fifo max fill: audio %d/%d, video %d/%d, convert %d/%d",
571             h->afifo.max,h->afifo.slots,
572             h->vfifo.max,h->vfifo.slots,
573             h->cfifo.max,h->cfifo.slots);
574     rec_status(line);
575 
576     free(h);
577     return 0;
578 }
579 
580 /*-------------------------------------------------------------------------*/
581 
582 static void
583 movie_print_timestamps(struct movie_handle *h)
584 {
585     char line[128];
586 
587     if (NULL == rec_status)
588         return;
589 
590     sprintf(line,"rec %d:%02d.%02d  -  a/r: %c%d.%02ds [%d], a/v: %c%d.%02ds [%d]",
591             (int)(h->rts / 1000000000 / 60),
592             (int)(h->rts / 1000000000 % 60),
593             (int)((h->rts % 1000000000) / 10000000),
594             (h->rdrift > 0) ? '+' : '-',
595             (int)((abs(h->rdrift) / 1000000000)),
596             (int)((abs(h->rdrift) % 1000000000) / 10000000),
597             (int)(h->rdrift * h->fps / (uint64_t)1000000000000),
598             (h->vdrift > 0) ? '+' : '-',
599             (int)((abs(h->vdrift) / 1000000000)),
600             (int)((abs(h->vdrift) % 1000000000) / 10000000),
601             (int)(h->vdrift * h->fps / (uint64_t)1000000000000));
602     rec_status(line);
603 }
604 
605 int
606 movie_grab_put_video(struct movie_handle *h, struct ng_video_buf **ret)
607 {
608     struct ng_video_buf *buf;
609     int expected,rc;
610 
611     if (debug > 1)
612         fprintf(stderr,"grab_put_video\n");
613 
614     /* fetch next frame */
615     buf = ng_grabber_grab_image(0);
616     if (NULL == buf) {
617         if (debug)
618             fprintf(stderr,"grab_put_video: grab image failed\n");
619         return -1;
620     }
621 #if 0 /* FIXME */
622     buf = ng_filter_single(cur_filter,buf);
623 #endif
624 
625     /* rate control */
626     expected = (buf->info.ts - h->vdrift) * h->fps / (uint64_t)1000000000000;
627     if (expected < h->frames-1) {
628         if (debug > 1)
629             fprintf(stderr,"rate: ignoring frame [%d %d]\n",
630                     expected, h->frames);
631         ng_release_video_buf(buf);
632         return 0;
633     }
634     if (expected > h->frames+1) {
635         fprintf(stderr,"rate: queueing frame twice (%d)\n",
636                 expected-h->frames);
637         buf->info.twice++;
638         h->frames++;
639     }
640     h->frames++;
641     h->vts = buf->info.ts;
642     buf->info.seq = h->seq;
643 
644     /* return a pointer to the frame if requested */
645     if (NULL != ret) {
646         buf->refcount++;
647         *ret = buf;
648     }
649     
650     /* put into fifo */
651     if (h->cthreads)
652         rc = fifo_put(&h->cfifo,buf);
653     else
654         rc = fifo_put(&h->vfifo,buf);
655     if (0 != rc) {
656         ng_release_video_buf(buf);    
657         return h->frames;
658     }
659     h->seq++;
660 
661     /* feedback */
662     movie_print_timestamps(h);
663     return h->frames;
664 }
665 
  This page was automatically generated by the LXR engine.