aboutsummaryrefslogtreecommitdiff
path: root/modules/pdp_v4l.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pdp_v4l.c')
-rw-r--r--modules/pdp_v4l.c734
1 files changed, 734 insertions, 0 deletions
diff --git a/modules/pdp_v4l.c b/modules/pdp_v4l.c
new file mode 100644
index 0000000..e4dbff9
--- /dev/null
+++ b/modules/pdp_v4l.c
@@ -0,0 +1,734 @@
+/*
+ * Pure Data Packet module.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+#include "pdp.h"
+#include "pdp_llconv.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <linux/types.h>
+#include <linux/videodev.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <pthread.h>
+
+// dont open any more after a set number
+// of failed attempts
+// this is to prevent locks on auto-open
+// is reset when manually opened or closed
+#define PDP_XV_RETRIES 10
+
+//uncomment this for additional philips webcam control
+//#define HAVE_V4LPWC
+#ifdef HAVE_V4LPWC
+#include "pwc-ioctl.h"
+#endif
+
+
+#define DEVICENO 0
+#define NBUF 2
+#define COMPOSITEIN 1
+
+
+
+
+typedef struct pdp_v4l_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_outlet *x_outlet0;
+
+ bool x_initialized;
+ bool x_auto_open;
+
+ unsigned int x_width;
+ unsigned int x_height;
+ int x_channel;
+ unsigned int x_norm;
+ int x_freq;
+
+ unsigned int x_framerate;
+
+ struct video_tuner x_vtuner;
+ struct video_picture x_vpicture;
+ struct video_buffer x_vbuffer;
+ struct video_capability x_vcap;
+ struct video_channel x_vchannel;
+ struct video_audio x_vaudio;
+ struct video_mbuf x_vmbuf;
+ struct video_mmap x_vmmap[NBUF];
+ struct video_window x_vwin;
+ int x_tvfd;
+ int x_frame;
+ unsigned char *x_videobuf;
+ int x_skipnext;
+ int x_mytopmargin, x_mybottommargin;
+ int x_myleftmargin, x_myrightmargin;
+
+ t_symbol *x_device;
+ t_symbol *x_image_type;
+ //int x_pdp_image_type;
+ int x_v4l_palette;
+
+ pthread_t x_thread_id;
+ int x_continue_thread;
+ int x_frame_ready;
+ int x_only_new_frames;
+ int x_last_frame;
+
+
+ int x_open_retry;
+
+
+} t_pdp_v4l;
+
+
+
+
+
+static void pdp_v4l_close(t_pdp_v4l *x)
+{
+ /* close the v4l device and dealloc buffer */
+
+ void *dummy;
+
+ /* terminate thread if there is one */
+ if(x->x_continue_thread){
+ x->x_continue_thread = 0;
+ pthread_join (x->x_thread_id, &dummy);
+ }
+
+
+ if (x->x_tvfd >= 0)
+ {
+ close(x->x_tvfd);
+ x->x_tvfd = -1;
+ }
+
+ if (x->x_initialized){
+ munmap(x->x_videobuf, x->x_vmbuf.size);
+ x->x_initialized = false;
+ }
+
+}
+
+static void pdp_v4l_close_manual(t_pdp_v4l *x)
+{
+ x->x_open_retry = PDP_XV_RETRIES;
+ pdp_v4l_close(x);
+
+}
+
+static void pdp_v4l_close_error(t_pdp_v4l *x)
+{
+ pdp_v4l_close(x);
+ if(x->x_open_retry) x->x_open_retry--;
+}
+
+
+static void pdp_v4l_pwc_init(t_pdp_v4l *x)
+{
+ /* todo add detection code for pwc */
+
+#ifdef HAVE_V4LPWC
+
+ if(ioctl(x->x_tvfd, VIDIOCPWCRUSER)){
+ perror("pdp_v4l: pwc: VIDIOCPWCRUSER");
+ goto closit;
+ }
+
+ if (ioctl(x->x_tvfd, VIDIOCGWIN, &x->x_vwin)){
+ perror("pdp_v4l: pwc: VIDIOCGWIN");
+ goto closit;
+ }
+
+
+
+ if (x->x_vwin.flags & PWC_FPS_MASK){
+ post("pdp_v4l: pwc: framerate: %d", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT);
+ post("pdp_v4l: pwc: setting framerate to %d", x->x_framerate);
+ x->x_vwin.flags &= PWC_FPS_MASK;
+ x->x_vwin.flags |= (x->x_framerate << PWC_FPS_SHIFT);
+ if (ioctl(x->x_tvfd, VIDIOCSWIN, &x->x_vwin)){
+ perror("pdp_v4l: pwc: VIDIOCSWIN");
+ goto closit;
+ }
+ if (ioctl(x->x_tvfd, VIDIOCGWIN, &x->x_vwin)){
+ perror("pdp_v4l: pwc: VIDIOCGWIN");
+ goto closit;
+ }
+ post("pdp_v4l: pwc: framerate: %d", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT);
+
+ }
+
+
+ return;
+
+
+
+ closit:
+ pdp_v4l_close_error(x);
+ return;
+
+#else
+ post("pdp_v4l: additional pwc support disabled");
+ return;
+#endif
+}
+
+static void pdp_v4l_sync_frame(t_pdp_v4l* x){
+ /* grab frame */
+ if (ioctl(x->x_tvfd, VIDIOCSYNC, &x->x_vmmap[x->x_frame].frame) < 0){
+ perror("pdp_v4l: VIDIOCSYNC");
+ pdp_v4l_close(x);
+ return;
+ }
+}
+
+static void pdp_v4l_capture_frame(t_pdp_v4l* x){
+ if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0){
+ if (errno == EAGAIN)
+ post("pdp_v4l: can't sync (no video source?)\n");
+ else
+ perror("pdp_v4l: VIDIOCMCAPTURE");
+ if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0)
+ perror("pdp_v4l: VIDIOCMCAPTURE2");
+
+ post("pdp_v4l: frame %d %d, format %d, width %d, height %d",
+ x->x_frame, x->x_vmmap[x->x_frame].frame, x->x_vmmap[x->x_frame].format,
+ x->x_vmmap[x->x_frame].width, x->x_vmmap[x->x_frame].height);
+
+ pdp_v4l_close(x);
+ return;
+ }
+}
+
+
+static void *pdp_v4l_thread(void *voidx)
+{
+ t_pdp_v4l *x = ((t_pdp_v4l *)voidx);
+
+
+ /* flip buffers */
+ x->x_frame ^= 0x1;
+
+ /* capture with a double buffering scheme */
+ while (x->x_continue_thread){
+
+ /* schedule capture command for next frame */
+ pdp_v4l_capture_frame(x);
+
+ /* wait until previous capture is ready */
+ x->x_frame ^= 0x1;
+ pdp_v4l_sync_frame(x);
+
+ /* setup pointers for main thread */
+ x->x_frame_ready = 1;
+ x->x_last_frame = x->x_frame;
+
+ }
+
+ return 0;
+}
+
+
+static void pdp_v4l_open(t_pdp_v4l *x, t_symbol *name)
+{
+ /* open a v4l device and allocate a buffer */
+
+ unsigned int size;
+ int i;
+
+ unsigned int width, height;
+
+
+ /* if already opened -> close */
+ if (x->x_initialized) pdp_v4l_close(x);
+
+
+ /* exit if retried too much */
+ if (!x->x_open_retry){
+ post("pdp_v4l: retry count reached zero for %s", name->s_name);
+ post("pdp_v4l: try to open manually");
+ return;
+ }
+
+ post("pdp_v4l: opening %s", name->s_name);
+
+ x->x_device = name;
+
+ if ((x->x_tvfd = open(name->s_name, O_RDWR)) < 0)
+ {
+ post("pdp_v4l: error:");
+ perror(name->s_name);
+ goto closit;
+ }
+
+
+ if (ioctl(x->x_tvfd, VIDIOCGCAP, &x->x_vcap) < 0)
+ {
+ perror("get capabilities");
+ goto closit;
+ }
+
+ post("pdp_v4l: cap: name %s type %d channels %d maxw %d maxh %d minw %d minh %d",
+ x->x_vcap.name, x->x_vcap.type, x->x_vcap.channels, x->x_vcap.maxwidth, x->x_vcap.maxheight,
+ x->x_vcap.minwidth, x->x_vcap.minheight);
+
+ if (ioctl(x->x_tvfd, VIDIOCGPICT, &x->x_vpicture) < 0)
+ {
+ perror("VIDIOCGCAP");
+ goto closit;
+ }
+
+ post("pdp_v4l: picture: brightness %d depth %d palette %d",
+ x->x_vpicture.brightness, x->x_vpicture.depth, x->x_vpicture.palette);
+
+ /* get channel info */
+ for (i = 0; i < x->x_vcap.channels; i++)
+ {
+ x->x_vchannel.channel = i;
+ if (ioctl(x->x_tvfd, VIDIOCGCHAN, &x->x_vchannel) < 0)
+ {
+ perror("VDIOCGCHAN");
+ goto closit;
+ }
+ post("pdp_v4l: channel %d name %s type %d flags %d",
+ x->x_vchannel.channel, x->x_vchannel.name,
+ x->x_vchannel.type, x->x_vchannel.flags);
+ }
+
+ /* switch to the desired channel */
+ if (x->x_channel < 0) x->x_channel = 0;
+ if (x->x_channel >= x->x_vcap.channels) x->x_channel = x->x_vcap.channels - 1;
+
+ x->x_vchannel.channel = x->x_channel;
+ if (ioctl(x->x_tvfd, VIDIOCGCHAN, &x->x_vchannel) < 0)
+ {
+ perror("pdp_v4l: warning: VDIOCGCHAN");
+ post("pdp_v4l: cant change to channel %d",x->x_channel);
+
+ // ignore error
+ // goto closit;
+ }
+ else{
+ post("pdp_v4l: switched to channel %d", x->x_channel);
+ }
+
+ x->x_vchannel.norm = x->x_norm;
+ if (ioctl(x->x_tvfd, VIDIOCSCHAN, &x->x_vchannel) < 0)
+ {
+ perror("pdp_v4l: warning: VDIOCSCHAN");
+ post("pdp_v4l: cant change to norm %d",x->x_norm);
+
+ // ignore error
+ // goto closit;
+ }
+
+ if (x->x_freq > 0){
+ if (ioctl(x->x_tvfd, VIDIOCSFREQ, &x->x_freq) < 0)
+ perror ("couldn't set frequency :");
+ }
+
+
+
+
+ /* get mmap numbers */
+ if (ioctl(x->x_tvfd, VIDIOCGMBUF, &x->x_vmbuf) < 0)
+ {
+ perror("pdp_v4l: VIDIOCGMBUF");
+ goto closit;
+ }
+ post("pdp_v4l: buffer size %d, frames %d, offset %d %d", x->x_vmbuf.size,
+ x->x_vmbuf.frames, x->x_vmbuf.offsets[0], x->x_vmbuf.offsets[1]);
+ if (!(x->x_videobuf = (unsigned char *)
+ mmap(0, x->x_vmbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, x->x_tvfd, 0)))
+ {
+ perror("pdp_v4l: mmap");
+ goto closit;
+ }
+
+ width = (x->x_width > (unsigned int)x->x_vcap.minwidth) ? x->x_width : (unsigned int)x->x_vcap.minwidth;
+ width = (width > (unsigned int)x->x_vcap.maxwidth) ?(unsigned int) x->x_vcap.maxwidth : width;
+ height = (x->x_height > (unsigned int)x->x_vcap.minheight) ? x->x_height :(unsigned int) x->x_vcap.minheight;
+ height = (height > (unsigned int)x->x_vcap.maxheight) ? (unsigned int)x->x_vcap.maxheight : height;
+
+ for (i = 0; i < NBUF; i++)
+ {
+ //x->x_vmmap[i].format = VIDEO_PALETTE_YUV420P;
+ //x->x_vmmap[i].format = VIDEO_PALETTE_UYVY;
+ x->x_vmmap[i].width = width;
+ x->x_vmmap[i].height = height;
+ x->x_vmmap[i].frame = i;
+ }
+
+
+ //goto test;
+
+ //try yuv planar format
+ x->x_v4l_palette = VIDEO_PALETTE_YUV420P;
+ for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette;
+ if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0)
+ {
+ if (errno == EAGAIN)
+ post("pdp_v4l: can't sync (no video source?)");
+ }
+ else{
+ post("pdp_v4l: using VIDEO_PALETTE_YUV420P");
+ goto capture_ok;
+ }
+
+
+ //try VIDEO_PALETTE_YUV422 format
+ x->x_v4l_palette = VIDEO_PALETTE_YUV422;
+ for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette;
+ if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0)
+ {
+ if (errno == EAGAIN)
+ post("pdp_v4l: can't sync (no video source?)");
+ }
+ else{
+ post("pdp_v4l: using VIDEO_PALETTE_YUV422");
+ goto capture_ok;
+ }
+
+
+ test:
+
+ //try rgb packed format
+ x->x_v4l_palette = VIDEO_PALETTE_RGB24;
+ for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette;
+ if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0)
+ {
+ if (errno == EAGAIN)
+ post("pdp_v4l: can't sync (no video source?)");
+ }
+ else{
+ post("pdp_v4l: using VIDEO_PALETTE_RGB24");
+ goto capture_ok;
+ }
+
+
+ // none of the formats are supported
+ perror("pdp_v4l: VIDIOCMCAPTURE: format not supported");
+ goto closit;
+
+
+ capture_ok:
+
+ post("pdp_v4l: frame %d %d, format %d, width %d, height %d",
+ x->x_frame, x->x_vmmap[x->x_frame].frame, x->x_vmmap[x->x_frame].format,
+ x->x_vmmap[x->x_frame].width, x->x_vmmap[x->x_frame].height);
+
+ x->x_width = width;
+ x->x_height = height;
+
+ post("pdp_v4l: Opened video connection (%dx%d)",x->x_width,x->x_height);
+
+
+ /* do some pwc specific inits */
+ pdp_v4l_pwc_init(x);
+
+
+ x->x_initialized = true;
+
+ /* create thread */
+ x->x_continue_thread = 1;
+ x->x_frame_ready = 0;
+ pthread_create(&x->x_thread_id, 0, pdp_v4l_thread, x);
+
+ return;
+ closit:
+ pdp_v4l_close_error(x);
+
+}
+
+static void pdp_v4l_open_manual(t_pdp_v4l *x, t_symbol *name)
+{
+ x->x_open_retry = PDP_XV_RETRIES;
+ pdp_v4l_open(x, name);
+}
+
+
+static void pdp_v4l_channel(t_pdp_v4l *x, t_float f)
+{
+ int channel = (float)f;
+
+ if (x->x_initialized){
+ pdp_v4l_close(x);
+ x->x_channel = channel;
+ pdp_v4l_open(x, x->x_device);
+ }
+ else
+ x->x_channel = channel;
+}
+
+
+static void pdp_v4l_freq(t_pdp_v4l *x, t_float f)
+{
+ int freq = (int)f;
+
+ x->x_freq = freq;
+ if (x->x_freq > 0){
+ if (ioctl(x->x_tvfd, VIDIOCSFREQ, &x->x_freq) < 0)
+ perror ("couldn't set frequency :");
+ else {post("pdp_v4l: tuner frequency: %f MHz", f / 16.0f);}
+ }
+
+}
+
+static void pdp_v4l_freqMHz(t_pdp_v4l *x, t_float f)
+{
+ pdp_v4l_freq(x, f*16.0f);
+}
+
+
+static void pdp_v4l_bang(t_pdp_v4l *x)
+{
+
+ /* if initialized, grab a frame and output it */
+
+ unsigned int w,h,nbpixels,packet_size,plane1,plane2;
+ unsigned char *newimage;
+ int object,length,pos,i,encoding;
+ t_pdp* header;
+ short int * data;
+
+
+ static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff};
+
+ if (!(x->x_initialized)){
+ post("pdp_v4l: no device opened");
+
+ if (x->x_auto_open){
+ post("pdp_v4l: attempting auto open");
+ pdp_v4l_open(x, x->x_device);
+ if (!(x->x_initialized)){
+ post("pdp_v4l: auto open failed");
+ return;
+ }
+ }
+
+ else return;
+ }
+
+
+ /* do nothing if there is no frame ready */
+ if((!x->x_frame_ready) && (x->x_only_new_frames)) return;
+ x->x_frame_ready = 0;
+
+ /* get the address of the "other" frame */
+ newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_last_frame];
+
+ /* create new packet */
+ w = x->x_width;
+ h = x->x_height;
+
+ nbpixels = w * h;
+
+/*
+ switch(x->x_pdp_image_type){
+ case PDP_IMAGE_GREY:
+ packet_size = nbpixels << 1;
+ break;
+ case PDP_IMAGE_YV12:
+ packet_size = (nbpixels + (nbpixels >> 1)) << 1;
+ break;
+ default:
+ packet_size = 0;
+ post("pdp_v4l: internal error");
+ }
+*/
+
+ packet_size = (nbpixels + (nbpixels >> 1)) << 1;
+
+
+ plane1 = nbpixels;
+ plane2 = nbpixels + (nbpixels>>2);
+
+ object = pdp_packet_new(PDP_IMAGE, packet_size);
+ header = pdp_packet_header(object);
+ data = (short int *) pdp_packet_data(object);
+
+ //header->info.image.encoding = x->x_pdp_image_type;
+ header->info.image.encoding = PDP_IMAGE_YV12;
+ header->info.image.width = w;
+ header->info.image.height = h;
+
+ /* read from the "other" frame */
+ newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_frame ^ 0x1];
+
+
+ /* convert data to pdp packet */
+
+ switch(x->x_v4l_palette){
+ case VIDEO_PALETTE_YUV420P:
+ pdp_llconv(newimage, RIF_YUV__P411_U8, data, RIF_YVU__P411_S16, w, h);
+ break;
+
+ case VIDEO_PALETTE_RGB24:
+ pdp_llconv(newimage, RIF_RGB__P____U8, data, RIF_YVU__P411_S16, w, h);
+ break;
+
+ case VIDEO_PALETTE_YUV422:
+ pdp_llconv(newimage, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, w, h);
+ break;
+
+
+ default:
+ post("pdp_v4l: unsupported palette");
+ break;
+ }
+
+/*
+ if (PDP_IMAGE_YV12 == x->x_pdp_image_type){
+ pixel_unpack_u8s16_y(&newimage[0], data, nbpixels>>3, x->x_state_data->gain);
+ pixel_unpack_u8s16_uv(&newimage[plane1], &data[plane2], nbpixels>>5, x->x_state_data->gain);
+ pixel_unpack_u8s16_uv(&newimage[plane2], &data[plane1], nbpixels>>5, x->x_state_data->gain);
+ }
+*/
+ //x->x_v4l_palette = VIDEO_PALETTE_YUV420P;
+ //x->x_v4l_palette = VIDEO_PALETTE_RGB24;
+
+/*
+
+ else if(PDP_IMAGE_GREY == x->x_pdp_image_type){
+ pixel_unpack_u8s16_y(&newimage[0], data, nbpixels>>3, x->x_state_data->gain);
+ }
+*/
+ //post("pdp_v4l: mark unused %d", object);
+
+ pdp_packet_mark_unused(object);
+ outlet_pdp(x->x_outlet0, object);
+
+
+}
+
+
+static void pdp_v4l_dim(t_pdp_v4l *x, t_floatarg xx, t_floatarg yy)
+{
+ unsigned int w,h;
+
+ xx = (xx < 0.0f) ? 0.0f : xx;
+ yy = (yy < 0.0f) ? 0.0f : yy;
+
+ w = (unsigned int)xx;
+ h = (unsigned int)yy;
+
+
+ if (x->x_initialized){
+ pdp_v4l_close(x);
+ x->x_width = w;
+ x->x_height = h;
+ pdp_v4l_open(x, x->x_device);
+
+ }
+ else{
+ x->x_width = w;
+ x->x_height = h;
+ }
+}
+
+
+static void pdp_v4l_free(t_pdp_v4l *x)
+{
+ pdp_v4l_close(x);
+}
+
+t_class *pdp_v4l_class;
+
+
+
+void *pdp_v4l_new(void)
+{
+ t_pdp_v4l *x = (t_pdp_v4l *)pd_new(pdp_v4l_class);
+
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_initialized = false;
+
+
+ x->x_tvfd = -1;
+ x->x_frame = 0;
+ x->x_last_frame = 0;
+
+ x->x_framerate = 27;
+
+ x->x_auto_open = true;
+ x->x_device = gensym("/dev/video0");
+
+ x->x_continue_thread = 0;
+ x->x_only_new_frames = 1;
+
+ x->x_width = 320;
+ x->x_height = 240;
+
+// pdp_v4l_type(x, gensym("yv12"));
+
+
+ x->x_open_retry = PDP_XV_RETRIES;
+
+ x->x_channel = 0;
+ x->x_freq = -1; //don't set freq by default
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_v4l_setup(void)
+{
+
+
+ pdp_v4l_class = class_new(gensym("pdp_v4l"), (t_newmethod)pdp_v4l_new,
+ (t_method)pdp_v4l_free, sizeof(t_pdp_v4l), 0, A_NULL);
+
+
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_bang, gensym("bang"), A_NULL);
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_close_manual, gensym("close"), A_NULL);
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_open_manual, gensym("open"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_channel, gensym("channel"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_freq, gensym("freq"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_freqMHz, gensym("freqMHz"), A_FLOAT, A_NULL);
+
+
+
+}
+
+#ifdef __cplusplus
+}
+#endif