1 /*
2  * import_v4lcam.c -- imports video frames from v4l2 using libv4l*
3  *                    with special focus on webcams.
4  * (C) 2009-2010 Francesco Romani <fromani at gmail dot com>
5  * based on import_v4l2.c code, which is
6  * (C) Erik Slagter <erik@slagter.name> Sept 2003
7  *
8  * This file is part of transcode, a video stream processing tool.
9  *
10  * transcode is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * transcode is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #define MOD_NAME        "import_v4lcam.so"
25 #define MOD_VERSION     "v0.1.0 (2009-08-30)"
26 #define MOD_CODEC       "(video) v4l2"
27 
28 #include "src/transcode.h"
29 
30 
31 static int verbose_flag     = TC_QUIET;
32 static int capability_flag  = TC_CAP_RGB|TC_CAP_YUV;
33 
34 /*
35  * Briefing
36  *
37  * Q: why a new module?
38  * Q: why don't just enhance import_v4l2?
39  * A: because I want take this chance to do a fresh start with a v4l import
40  *    module, so we can get rid of some old code, try to redesign/rewrite
41  *    it in a better way, experimenting new designes and so on. I want the
42  *    freedom to add special code and special design decisions useful for
43  *    webcams only (or just mostly). import_v4l2 will stay and the experiments
44  *    which time proven to be good will be backported.
45  *    Eventually, v4lcam can be merged into the main v4l module.
46  *
47  * Q: there is some duplicate code with import_v4l2.c. Why?
48  * A: because I'm taking advantage of being a separate module, and because
49  *    I'm experimenting new stuff. After a while, the remaining duplicated
50  *    parts will be merged in a common source.
51  *
52  * Q: why libv4lconvert? We can just extend aclib.
53  * A: no objections of course (but no time either!). However, libv4lconvert
54  *    has IMHO a slightly different focus wrt aclib and I think it's just
55  *    fine to use both of them. As example, the MJPG->I420 conversion
56  *    should NOT enter into aclib (eventually v4lcam can emit MJPG frames
57  *    too, when the module pipeline get enhanced enough).
58 
59  */
60 
61 /*%*
62  *%* DESCRIPTION
63  *%*   This module allow to capture video frames through a V4L2 (V4L api version 2)
64  *%*   device. This module is specialized for webcam devices.
65  *%*
66  *%* #BUILD-DEPENDS
67  *%*
68  *%* #DEPENDS
69  *%*
70  *%* PROCESSING
71  *%*   import/demuxer
72  *%*
73  *%* MEDIA
74  *%*   video
75  *%*
76  *%* #INPUT
77  *%*
78  *%* OUTPUT
79  *%*   YUV420P, RGB24
80  *%*/
81 
82 #define MOD_PRE         tc_v4lcam
83 #include "import_def.h"
84 
85 #define _ISOC9X_SOURCE 1
86 
87 #include <sys/ioctl.h>
88 #include <sys/mman.h>
89 
90 #include <linux/types.h>
91 
92 // The v4l2_buffer struct check is because some distributions protect that
93 // struct in videodev2 with a #ifdef __KERNEL__ (SuSE 9.0)
94 
95 #if defined(HAVE_LINUX_VIDEODEV2_H) && defined(HAVE_STRUCT_V4L2_BUFFER)
96 #define _LINUX_TIME_H
97 #include <linux/videodev2.h>
98 #else
99 #include "videodev2.h"
100 #endif
101 
102 #include "libv4l2.h"
103 #include "libv4lconvert.h"
104 
105 #include "libtc/libtc.h"
106 #include "libtc/optstr.h"
107 
108 #define TC_V4L2_BUFFERS_NUM             (32)
109 
110 /* TODO: memset() verify and sanitization */
111 
112 typedef struct tcv4lbuffer TCV4LBuffer;
113 struct tcv4lbuffer {
114     void    *start;
115     size_t  length;
116 };
117 
118 typedef struct v4l2source_ V4L2Source;
119 
120 /* FIXME: naming */
121 typedef int (*TCV4LFetchDataFn)(V4L2Source *vs,
122                                 uint8_t *src, int src_len,
123                                 uint8_t *dst, int dst_len);
124 
125 struct v4l2source_ {
126     int                     video_fd;
127     int                     video_sequence;
128 
129     int                     v4l_dst_csp;
130     struct v4l2_format      v4l_dst_fmt;
131     struct v4l2_format      v4l_src_fmt;
132     struct v4lconvert_data  *v4l_convert;
133     int                     buffers_count;
134 
135     int                     width;
136     int                     height;
137 
138     TCV4LFetchDataFn        fetch_data;
139     TCV4LBuffer             buffers[TC_V4L2_BUFFERS_NUM];
140 };
141 
tc_v4l2_fetch_data_memcpy(V4L2Source * vs,uint8_t * src,int src_len,uint8_t * dst,int dst_len)142 static int tc_v4l2_fetch_data_memcpy(V4L2Source *vs,
143                                      uint8_t *src, int src_len,
144                                      uint8_t *dst, int dst_len)
145 {
146     int ret = TC_ERROR;
147     if (dst_len >= src_len) {
148         ac_memcpy(dst, src, src_len);
149         ret = TC_OK;
150     }
151     return ret;
152 }
153 
tc_v4l2_fetch_data_v4lconv(V4L2Source * vs,uint8_t * src,int src_len,uint8_t * dst,int dst_len)154 static int tc_v4l2_fetch_data_v4lconv(V4L2Source *vs,
155                                       uint8_t *src, int src_len,
156                                       uint8_t *dst, int dst_len)
157 {
158     int err = v4lconvert_convert(vs->v4l_convert,
159                                  &(vs->v4l_src_fmt),
160                                  &(vs->v4l_dst_fmt),
161                                  src, src_len, dst, dst_len);
162 
163     return (err == -1) ?TC_ERROR :TC_OK; /* FIXME */
164 }
165 
166 /* FIXME: reorganize the layout */
tc_v4l2_video_grab_frame(V4L2Source * vs,uint8_t * dest,size_t length)167 static int tc_v4l2_video_grab_frame(V4L2Source *vs, uint8_t *dest, size_t length)
168 {
169     static struct v4l2_buffer buffer; /* FIXME */
170     int ix, err = 0, eio = 0, ret = TC_ERROR;
171 
172     // get buffer
173     buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
174     buffer.memory = V4L2_MEMORY_MMAP;
175 
176     err = v4l2_ioctl(vs->video_fd, VIDIOC_DQBUF, &buffer);
177     if (err < 0) {
178         tc_log_perror(MOD_NAME,
179                       "error in setup grab buffer (ioctl(VIDIOC_DQBUF) failed)");
180 
181         if (errno != EIO) {
182             return TC_OK;
183         } else {
184             eio = 1;
185 
186             for (ix = 0; ix < vs->buffers_count; ix++) {
187                 buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
188                 buffer.memory = V4L2_MEMORY_MMAP;
189                 buffer.index  = ix;
190                 buffer.flags  = 0;
191 
192                 err = v4l2_ioctl(vs->video_fd, VIDIOC_DQBUF, &buffer);
193                 if (err < 0)
194                     tc_log_perror(MOD_NAME,
195                                   "error in recovering grab buffer (ioctl(DQBUF) failed)");
196             }
197 
198             for (ix = 0; ix < vs->buffers_count; ix++) {
199                 buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
200                 buffer.memory = V4L2_MEMORY_MMAP;
201                 buffer.index  = ix;
202                 buffer.flags  = 0;
203 
204                 err = v4l2_ioctl(vs->video_fd, VIDIOC_QBUF, &buffer);
205                 if (err < 0)
206                     tc_log_perror(MOD_NAME,
207                                   "error in recovering grab buffer (ioctl(QBUF) failed)");
208             }
209         }
210     }
211 
212     ix  = buffer.index;
213 
214     ret = vs->fetch_data(vs,
215                          vs->buffers[ix].start, buffer.bytesused,
216                          dest, length);
217 
218     // enqueue buffer again
219     if (!eio) {
220         buffer.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
221         buffer.memory   = V4L2_MEMORY_MMAP;
222         buffer.flags    = 0;
223 
224         err = v4l2_ioctl(vs->video_fd, VIDIOC_QBUF, &buffer);
225         if (err < 0) {
226             tc_log_perror(MOD_NAME, "error in enqueuing buffer (ioctl(VIDIOC_QBUF) failed)");
227             return TC_OK;
228         }
229     }
230 
231     return ret;
232 }
233 
tc_v4l2_video_count_buffers(V4L2Source * vs)234 static int tc_v4l2_video_count_buffers(V4L2Source *vs)
235 {
236     struct v4l2_buffer buffer;
237     int ix, ret, buffers_filled = 0;
238 
239     for (ix = 0; ix < vs->buffers_count; ix++) {
240         buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
241         buffer.memory = V4L2_MEMORY_MMAP;
242         buffer.index  = ix;
243 
244         ret = v4l2_ioctl(vs->video_fd, VIDIOC_QUERYBUF, &buffer);
245         if (ret < 0) {
246             tc_log_perror(MOD_NAME,
247                           "error in querying buffers"
248                           " (ioctl(VIDIOC_QUERYBUF) failed)");
249             return -1;
250         }
251 
252         if (buffer.flags & V4L2_BUF_FLAG_DONE)
253             buffers_filled++;
254     }
255     return buffers_filled;
256 }
257 
tc_v4l2_video_check_capabilities(V4L2Source * vs)258 static int tc_v4l2_video_check_capabilities(V4L2Source *vs)
259 {
260     struct v4l2_capability caps;
261     int err = 0;
262 
263     err = v4l2_ioctl(vs->video_fd, VIDIOC_QUERYCAP, &caps);
264     if (err < 0) {
265         tc_log_error(MOD_NAME, "driver does not support querying capabilities");
266         return TC_ERROR;
267     }
268 
269     if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
270         tc_log_error(MOD_NAME, "driver does not support video capture");
271         return TC_ERROR;
272     }
273 
274     if (!(caps.capabilities & V4L2_CAP_STREAMING)) {
275         tc_log_error(MOD_NAME, "driver does not support streaming (mmap) video capture");
276         return TC_ERROR;
277     }
278 
279     if (verbose_flag > TC_INFO) {
280         tc_log_info(MOD_NAME, "v4l2 video grabbing, driver = %s, device = %s",
281                     caps.driver, caps.card);
282     }
283 
284     return TC_OK;
285 }
286 
287 #define pixfmt_to_fourcc(pixfmt, fcc) do { \
288     fcc[0] = (pixfmt >> 0 ) & 0xFF; \
289     fcc[1] = (pixfmt >> 8 ) & 0xFF; \
290     fcc[2] = (pixfmt >> 16) & 0xFF; \
291     fcc[3] = (pixfmt >> 24) & 0xFF; \
292 } while (0)
293 
tc_v4l2_video_setup_image_format(V4L2Source * vs,int width,int height)294 static int tc_v4l2_video_setup_image_format(V4L2Source *vs, int width, int height)
295 {
296     int err = 0;
297 
298     vs->width  = width;
299     vs->height = height;
300 
301     vs->v4l_convert = v4lconvert_create(vs->video_fd);
302     if (!vs->v4l_convert) {
303         return TC_ERROR;
304     }
305 
306     memset(&(vs->v4l_dst_fmt), 0, sizeof(vs->v4l_dst_fmt));
307     vs->v4l_dst_fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
308     vs->v4l_dst_fmt.fmt.pix.width       = width;
309     vs->v4l_dst_fmt.fmt.pix.height      = height;
310     vs->v4l_dst_fmt.fmt.pix.pixelformat = vs->v4l_dst_csp;
311 
312     err = v4lconvert_try_format(vs->v4l_convert,
313                                 &(vs->v4l_dst_fmt), &(vs->v4l_src_fmt));
314     if (err) {
315         tc_log_error(MOD_NAME, "unable to match formats: %s",
316                      v4lconvert_get_error_message(vs->v4l_convert));
317         return TC_ERROR;
318     }
319 
320     err = v4l2_ioctl(vs->video_fd, VIDIOC_S_FMT, &(vs->v4l_src_fmt));
321     if (err < 0) {
322         tc_log_error(MOD_NAME, "error while setting the cam image format");
323         return TC_ERROR;
324     }
325 
326     if (!v4lconvert_needs_conversion(vs->v4l_convert,
327                                     &(vs->v4l_src_fmt),
328                                     &(vs->v4l_dst_fmt))) {
329         tc_log_info(MOD_NAME, "fetch frames directly");
330         vs->fetch_data = tc_v4l2_fetch_data_memcpy;
331         /* Into the near future we should aim for zero-copy. -- FR */
332     } else {
333         char src_fcc[5] = { '\0' };
334         char dst_fcc[5] = { '\0' };
335 
336         pixfmt_to_fourcc(vs->v4l_src_fmt.fmt.pix.pixelformat, src_fcc);
337         pixfmt_to_fourcc(vs->v4l_dst_fmt.fmt.pix.pixelformat, dst_fcc);
338 
339         tc_log_info(MOD_NAME, "fetch frames using libv4lconvert "
340                               "[%s] -> [%s]",
341                               src_fcc, dst_fcc);
342         vs->fetch_data = tc_v4l2_fetch_data_v4lconv;
343     }
344 
345     return TC_OK;
346 }
347 
tc_v4l2_teardown_image_format(V4L2Source * vs)348 static void tc_v4l2_teardown_image_format(V4L2Source *vs)
349 {
350     if (vs->v4l_convert) {
351         v4lconvert_destroy(vs->v4l_convert);
352         vs->v4l_convert = NULL;
353     }
354 }
355 
tc_v4l2_video_setup_stream_parameters(V4L2Source * vs,int fps)356 static int tc_v4l2_video_setup_stream_parameters(V4L2Source *vs, int fps)
357 {
358     struct v4l2_streamparm streamparm;
359     int err = 0;
360 
361     memset(&streamparm, 0, sizeof(streamparm));
362     streamparm.type                                  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
363     streamparm.parm.capture.capturemode              = 0;
364     streamparm.parm.capture.timeperframe.numerator   = 1e7;
365     streamparm.parm.capture.timeperframe.denominator = fps;
366 
367     err = v4l2_ioctl(vs->video_fd, VIDIOC_S_PARM, &streamparm);
368     if (err < 0) {
369         tc_log_warn(MOD_NAME, "driver does not support setting parameters"
370                               " (ioctl(VIDIOC_S_PARM) returns \"%s\")",
371                     errno <= sys_nerr ? sys_errlist[errno] : "unknown");
372     }
373     return TC_OK;
374 }
375 
tc_v4l2_video_get_capture_buffer_count(V4L2Source * vs)376 static int tc_v4l2_video_get_capture_buffer_count(V4L2Source *vs)
377 {
378     struct v4l2_requestbuffers reqbuf;
379     int err = 0;
380 
381     reqbuf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
382     reqbuf.memory = V4L2_MEMORY_MMAP;
383     reqbuf.count  = TC_V4L2_BUFFERS_NUM;
384 
385     err = v4l2_ioctl(vs->video_fd, VIDIOC_REQBUFS, &reqbuf);
386     if (err < 0) {
387         tc_log_perror(MOD_NAME, "VIDIOC_REQBUFS");
388         return TC_ERROR;
389     }
390 
391     vs->buffers_count = TC_MIN(reqbuf.count, TC_V4L2_BUFFERS_NUM);
392 
393     if (vs->buffers_count < 2) {
394         tc_log_error(MOD_NAME, "not enough buffers for capture");
395         return TC_ERROR;
396     }
397 
398     if (verbose_flag > TC_INFO) {
399         tc_log_info(MOD_NAME, "%i buffers available (maximum supported: %i)",
400                     vs->buffers_count, TC_V4L2_BUFFERS_NUM);
401     }
402     return TC_OK;
403 }
404 
405 
tc_v4l2_video_setup_capture_buffers(V4L2Source * vs)406 static int tc_v4l2_video_setup_capture_buffers(V4L2Source *vs)
407 {
408     struct v4l2_buffer buffer;
409     int ix, err = 0;
410 
411     /* map the buffers */
412     for (ix = 0; ix < vs->buffers_count; ix++) {
413         buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
414         buffer.memory = V4L2_MEMORY_MMAP;
415         buffer.index  = ix;
416 
417         err = v4l2_ioctl(vs->video_fd, VIDIOC_QUERYBUF, &buffer);
418         if (err < 0) {
419             tc_log_perror(MOD_NAME, "VIDIOC_QUERYBUF");
420             return TC_ERROR;
421         }
422 
423         vs->buffers[ix].length = buffer.length;
424         vs->buffers[ix].start  = v4l2_mmap(0, buffer.length,
425                                            PROT_READ|PROT_WRITE, MAP_SHARED,
426                                            vs->video_fd, buffer.m.offset);
427 
428         if (vs->buffers[ix].start == MAP_FAILED) {
429             tc_log_perror(MOD_NAME, "mmap");
430             return TC_ERROR;
431         }
432     }
433 
434     /* then enqueue them all */
435     for (ix = 0; ix < vs->buffers_count; ix++) {
436         buffer.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
437         buffer.memory = V4L2_MEMORY_MMAP;
438         buffer.index  = ix;
439 
440         err = v4l2_ioctl(vs->video_fd, VIDIOC_QBUF, &buffer);
441         if (err < 0) {
442             tc_log_perror(MOD_NAME, "VIDIOC_QBUF");
443             return TC_ERROR;
444         }
445     }
446 
447     return TC_OK;
448 }
449 
tc_v4l2_capture_start(V4L2Source * vs)450 static int tc_v4l2_capture_start(V4L2Source *vs)
451 {
452     int err = 0, arg = V4L2_BUF_TYPE_VIDEO_CAPTURE;
453 
454     err = v4l2_ioctl(vs->video_fd, VIDIOC_STREAMON, &arg);
455     if (err < 0) {
456         /* ugh, needs VIDEO_CAPTURE */
457         tc_log_perror(MOD_NAME, "VIDIOC_STREAMON");
458         return TC_ERROR;
459     }
460 
461     return TC_OK;
462 }
463 
tc_v4l2_capture_stop(V4L2Source * vs)464 static int tc_v4l2_capture_stop(V4L2Source *vs)
465 {
466     int err = 0, arg = V4L2_BUF_TYPE_VIDEO_CAPTURE;
467 
468     err = v4l2_ioctl(vs->video_fd, VIDIOC_STREAMOFF, &arg);
469     if (err < 0) {
470         /* ugh, needs VIDEO_CAPTURE */
471         tc_log_perror(MOD_NAME, "VIDIOC_STREAMOFF");
472         return TC_ERROR;
473     }
474 
475     return TC_OK;
476 }
477 
tc_v4l2_parse_options(V4L2Source * vs,int layout,const char * options)478 static int tc_v4l2_parse_options(V4L2Source *vs, int layout, const char *options)
479 {
480     switch (layout) {
481       case CODEC_RGB:
482       case TC_CODEC_RGB:
483         vs->v4l_dst_csp = V4L2_PIX_FMT_RGB24;
484         break;
485       case CODEC_YUV:
486       case TC_CODEC_YUV420P:
487         vs->v4l_dst_csp = V4L2_PIX_FMT_YUV420;
488         break;
489       case CODEC_YUV422:
490       case TC_CODEC_YUV422P:
491         vs->v4l_dst_csp = V4L2_PIX_FMT_YYUV;
492         break;
493       default:
494         tc_log_error(MOD_NAME,
495                      "colorspace (0x%X) must be one of"
496                      " RGB24, YUV 4:2:0 or YUV 4:2:2",
497                      layout);
498         return TC_ERROR;
499     }
500 
501     return TC_OK;
502 }
503 
504 /* ============================================================
505  * V4L2 CORE
506  * ============================================================*/
507 
508 #define RETURN_IF_FAILED(RET) do { \
509     if ((RET) != TC_OK) { \
510         return (RET); \
511     } \
512 } while (0)
513 
tc_v4l2_video_init(V4L2Source * vs,int layout,const char * device,int width,int height,int fps,const char * options)514 static int tc_v4l2_video_init(V4L2Source *vs,
515                            int layout, const char *device,
516                            int width, int height, int fps,
517                            const char *options)
518 {
519     int ret = tc_v4l2_parse_options(vs, layout, options);
520     RETURN_IF_FAILED(ret);
521 
522     vs->video_fd = v4l2_open(device, O_RDWR, 0);
523     if (vs->video_fd < 0) {
524         tc_log_error(MOD_NAME, "cannot open video device %s", device);
525         return TC_ERROR;
526     }
527 
528     ret = tc_v4l2_video_check_capabilities(vs);
529     RETURN_IF_FAILED(ret);
530 
531     ret = tc_v4l2_video_setup_image_format(vs, width, height);
532     RETURN_IF_FAILED(ret);
533 
534     ret = tc_v4l2_video_setup_stream_parameters(vs, fps);
535     RETURN_IF_FAILED(ret);
536 
537     ret = tc_v4l2_video_get_capture_buffer_count(vs);
538     RETURN_IF_FAILED(ret);
539 
540     ret = tc_v4l2_video_setup_capture_buffers(vs);
541     RETURN_IF_FAILED(ret);
542 
543     return tc_v4l2_capture_start(vs);
544 }
545 
tc_v4l2_video_get_frame(V4L2Source * vs,uint8_t * data,size_t size)546 static int tc_v4l2_video_get_frame(V4L2Source *vs, uint8_t *data, size_t size)
547 {
548     int ret;
549     int buffers_filled = tc_v4l2_video_count_buffers(vs);
550 
551     if (buffers_filled == -1) {
552         tc_log_warn(MOD_NAME, "unable to get the capture buffers count,"
553                               " assuming OK");
554         buffers_filled = 0;
555     }
556 
557     if (buffers_filled > (vs->buffers_count * 3 / 4)) {
558         tc_log_error(MOD_NAME, "running out of capture buffers (%d left from %d total), "
559                                "stopping capture",
560                                vs->buffers_count - buffers_filled,
561                                vs->buffers_count);
562 
563         ret = tc_v4l2_capture_stop(vs);
564     } else {
565         ret = tc_v4l2_video_grab_frame(vs, data, size);
566         vs->video_sequence++;
567     }
568 
569     return ret;
570 }
571 
tc_v4l2_video_grab_stop(V4L2Source * vs)572 static int tc_v4l2_video_grab_stop(V4L2Source *vs)
573 {
574     int ix, ret;
575 
576     tc_v4l2_teardown_image_format(vs);
577 
578     ret = tc_v4l2_capture_stop(vs);
579     RETURN_IF_FAILED(ret);
580 
581     for (ix = 0; ix < vs->buffers_count; ix++)
582         v4l2_munmap(vs->buffers[ix].start, vs->buffers[ix].length);
583 
584     v4l2_close(vs->video_fd);
585     vs->video_fd = -1;
586 
587     return TC_OK;
588 }
589 
590 /* ============================================================
591  * TRANSCODE INTERFACE
592  * ============================================================*/
593 
594 static V4L2Source VS;
595 
596 /* ------------------------------------------------------------
597  * open stream
598  * ------------------------------------------------------------*/
599 
600 MOD_open
601 {
602     if (param->flag == TC_VIDEO) {
603         if (tc_v4l2_video_init(&VS,
604                                vob->im_v_codec, vob->video_in_file,
605                                vob->im_v_width, vob->im_v_height,
606                                vob->fps, vob->im_v_string)) {
607             return TC_ERROR;
608         }
609     } else {
610         tc_log_error(MOD_NAME, "unsupported request (init)");
611         return TC_ERROR;
612     }
613 
614     return TC_OK;
615 }
616 
617 /* ------------------------------------------------------------
618  * decode  stream
619  * ------------------------------------------------------------*/
620 
621 MOD_decode
622 {
623     if (param->flag == TC_VIDEO) {
624         if (tc_v4l2_video_get_frame(&VS, param->buffer, param->size)) {
625             tc_log_error(MOD_NAME, "error in grabbing video");
626             return TC_ERROR;
627         }
628     } else {
629         tc_log_error(MOD_NAME, "unsupported request (decode)");
630         return TC_ERROR;
631     }
632 
633     return TC_OK;
634 }
635 
636 /* ------------------------------------------------------------
637  * close stream
638  * ------------------------------------------------------------*/
639 
640 MOD_close
641 {
642     if (param->flag == TC_VIDEO) {
643         tc_v4l2_video_grab_stop(&VS);
644     } else {
645         tc_log_error(MOD_NAME, "unsupported request (close)");
646         return TC_ERROR;
647     }
648 
649     return TC_OK;
650 }
651 
652 /*************************************************************************/
653 
654 /*
655  * Local variables:
656  *   c-file-style: "stroustrup"
657  *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
658  *   indent-tabs-mode: nil
659  * End:
660  *
661  * vim: expandtab shiftwidth=4:
662  */
663 
664