From 213978fa8a868661dda88e6b4e7cacec1c90677a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Mart=C3=ADn?= Date: Sun, 7 Sep 2003 21:45:05 +0000 Subject: Updating pdp to current version 0.12.2 svn path=/trunk/externals/pdp/; revision=941 --- modules/image_io/Makefile | 11 + modules/image_io/README | 2 + modules/image_io/pdp_glx.c | 558 ++++++++++++++++++++++++++ modules/image_io/pdp_qt.c | 974 +++++++++++++++++++++++++++++++++++++++++++++ modules/image_io/pdp_sdl.c | 510 ++++++++++++++++++++++++ modules/image_io/pdp_v4l.c | 794 ++++++++++++++++++++++++++++++++++++ modules/image_io/pdp_xv.c | 300 ++++++++++++++ 7 files changed, 3149 insertions(+) create mode 100644 modules/image_io/Makefile create mode 100644 modules/image_io/README create mode 100644 modules/image_io/pdp_glx.c create mode 100644 modules/image_io/pdp_qt.c create mode 100644 modules/image_io/pdp_sdl.c create mode 100644 modules/image_io/pdp_v4l.c create mode 100644 modules/image_io/pdp_xv.c (limited to 'modules/image_io') diff --git a/modules/image_io/Makefile b/modules/image_io/Makefile new file mode 100644 index 0000000..b306ad8 --- /dev/null +++ b/modules/image_io/Makefile @@ -0,0 +1,11 @@ +current: all_modules + +include ../../Makefile.config + +# build optional modules +all_modules: $(PDP_OPTMOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/image_io/README b/modules/image_io/README new file mode 100644 index 0000000..9493047 --- /dev/null +++ b/modules/image_io/README @@ -0,0 +1,2 @@ +This directory contains input/output modules for image packets. +Most of this is platform dependent stuff, and will be conditionally compiled. diff --git a/modules/image_io/pdp_glx.c b/modules/image_io/pdp_glx.c new file mode 100644 index 0000000..810912e --- /dev/null +++ b/modules/image_io/pdp_glx.c @@ -0,0 +1,558 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten + * + * 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. + * + */ + +// gl stuff +#include +#include +#include +//#include + +// pdp stuff +#include "pdp.h" +#include "pdp_base.h" + +// some x window glue code +#include "pdp_xwindow.h" + +// pdp stuff +#include "pdp.h" +#include "pdp_llconv.h" +//#include "pdp_opengl.h" + + +/* initial image dimensions */ +#define PDP_OGL_W 320 +#define PDP_OGL_H 240 + +#define PDP_OGL_AUTOCREATE_RETRY 10 + + +typedef struct pdp_glx_struct +{ + t_object x_obj; + + t_pdp_xwindow x_xwin; + + t_outlet *x_outlet; + + int x_packet0; + int x_queue_id; + t_symbol *x_display; + + Display *x_dpy; + + XVisualInfo *x_vis_info; + GLXContext x_glx_context; + + GLuint x_texture; + u32 x_tex_width; + u32 x_tex_height; + + unsigned char *x_data; + unsigned int x_width; + unsigned int x_height; + int x_last_encoding; + + int x_initialized; + int x_autocreate; + +} t_pdp_glx; + + + +static void pdp_glx_cursor(t_pdp_glx *x, t_floatarg f) +{ + pdp_xwindow_cursor(&x->x_xwin, f); +} + +static void pdp_glx_destroy(t_pdp_glx* x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + XEvent e; + + if (x->x_initialized){ + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + glXDestroyContext(x->x_dpy, x->x_glx_context); + pdp_xwindow_close(&x->x_xwin); + XCloseDisplay(x->x_dpy); + x->x_initialized = false; + } + +} + + +static void pdp_glx_fullscreen(t_pdp_glx *x) +{ + pdp_xwindow_fullscreen(&x->x_xwin); +} + +static void pdp_glx_resize(t_pdp_glx* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_resize(&x->x_xwin, width, height); +} + +static void pdp_glx_move(t_pdp_glx* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_move(&x->x_xwin, width, height); +} + +static void pdp_glx_moveresize(t_pdp_glx* x, t_floatarg xoff, t_floatarg yoff, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_moveresize(&x->x_xwin, xoff, yoff, width, height); +} + +static void pdp_glx_tile(t_pdp_glx* x, t_floatarg xtiles, t_floatarg ytiles, t_floatarg i, t_floatarg j) +{ + pdp_xwindow_tile(&x->x_xwin, xtiles, ytiles, i, j); +} + + + + +void pdp_glx_generate_texture(t_pdp_glx *x) +{ + u32 width = x->x_tex_width; + u32 height = x->x_tex_height; + u32 depth = 4; + u32 i; + + u8 *dummydata = 0; + + while (x->x_width > width) width <<= 1; + while (x->x_height > height) height <<= 1; + + dummydata = (u8 *)pdp_alloc(width*height*depth); + + for (i=0; ix_dpy, x->x_xwin.win, x->x_glx_context); + + /* generate texture if necessary */ + if (!glIsTexture(x->x_texture)) glGenTextures(1, &(x->x_texture)); + + glBindTexture(GL_TEXTURE_2D, x->x_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummydata); + + pdp_dealloc(dummydata); + + x->x_tex_width = width; + x->x_tex_height = height; +} + +void pdp_glx_regenerate_texture(t_pdp_glx *x) +{ + if ((x->x_width > x->x_tex_width) || (x->x_height > x->x_tex_height)) pdp_glx_generate_texture(x); + +} + + +static void pdp_glx_create(t_pdp_glx* x) +{ + unsigned int *uintdata = (unsigned int *)(x->x_data); + XEvent e; + unsigned int i; + static int vis_attr[] = {GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None}; + + + if (x->x_initialized) return; + + /* manually open a display */ + if (NULL == (x->x_dpy = XOpenDisplay(x->x_display->s_name))){ + post("pdp_glx: cant open display %s\n",x->x_display->s_name); + x->x_initialized = false; + return; + } + + /* create a window on the display */ + if (!(x->x_initialized = pdp_xwindow_create_on_display(&x->x_xwin, x->x_dpy))) + goto exit_close_dpy; + + + /* create a glx visual */ + if (!(x->x_vis_info = glXChooseVisual(x->x_dpy, x->x_xwin.screen, vis_attr))){ + post("pdp_glx: can't create visual"); + goto exit_close_win; + } + //post("visual: %x", x->x_vis_info); + + /* create the rendering context */ + if (!(x->x_glx_context = glXCreateContext(x->x_dpy, x->x_vis_info, 0 /*share list*/, GL_TRUE))){ + post("pdp_glx: can't create render context"); + goto exit_close_win; + } + //post("context: %x", x->x_glx_context); + + + /* create texture */ + pdp_glx_generate_texture(x); + + + /* we're done initializing */ + x->x_initialized = true; + + /* disable/enable cursor */ + //pdp_glx_cursor(x, x->x_cursor); + return; + + + exit_close_win: + pdp_xwindow_close(&x->x_xwin); + exit_close_dpy: + XCloseDisplay(x->x_dpy); + x->x_initialized = false; + return; +} + +static int pdp_glx_try_autocreate(t_pdp_glx *x) +{ + + if (x->x_autocreate){ + post("pdp_glx: autocreate window"); + pdp_glx_create(x); + if (!(x->x_initialized)){ + x->x_autocreate--; + if (!x->x_autocreate){ + post ("pdp_glx: autocreate failed %d times: disabled", PDP_OGL_AUTOCREATE_RETRY); + post ("pdp_glx: send [autocreate 1] message to re-enable"); + return 0; + } + } + else return 1; + + } + return 0; +} + +static void pdp_glx_bang(t_pdp_glx *x); + +static void pdp_glx_fill_texture(t_pdp_glx *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + void *data = pdp_packet_data (x->x_packet0); + + int i = header->info.image.width; + + + /* ensure image buffer is correct dim */ + if ((header->info.image.width != x->x_width) + || (header->info.image.height != x->x_height)) { + if (x->x_data) pdp_dealloc (x->x_data); + x->x_width = header->info.image.width; + x->x_height = header->info.image.height; + x->x_data = pdp_alloc(4*x->x_width*x->x_height); + } + + /* ensure texture is correct dim */ + pdp_glx_regenerate_texture(x); + + + /* set window context current */ + glXMakeCurrent(x->x_dpy, x->x_xwin.win, x->x_glx_context); + glBindTexture(GL_TEXTURE_2D, x->x_texture); + + switch (header->info.image.encoding){ + case PDP_IMAGE_GREY: + /* convert image to greyscale 8 bit */ + pdp_llconv(data,RIF_GREY______S16, x->x_data, RIF_GREY______U8, x->x_width, x->x_height); + + /* upload grey subtexture */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x->x_width, x->x_height, GL_LUMINANCE, GL_UNSIGNED_BYTE, x->x_data); + + break; + case PDP_IMAGE_YV12: + + /* convert image to rgb 8 bit */ + pdp_llconv(data,RIF_YVU__P411_S16, x->x_data, RIF_RGB__P____U8, x->x_width, x->x_height); + + /* upload subtexture */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x->x_width, x->x_height, GL_RGB, GL_UNSIGNED_BYTE, x->x_data); + + break; + default: + break; + } + + +} + +static void pdp_glx_process(t_pdp_glx *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + void *data = pdp_packet_data (x->x_packet0); + + + if (-1 != x->x_queue_id) return; + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_glx_try_autocreate(x)) return; + } + + /* check data packet */ + if (!(header)) { + post("pdp_glx: invalid packet header"); + return; + } + if (PDP_IMAGE != header->type) { + post("pdp_glx: packet is not a PDP_IMAGE"); + return; + } + if ((PDP_IMAGE_YV12 != header->info.image.encoding) + && (PDP_IMAGE_GREY != header->info.image.encoding)) { + post("pdp_glx: packet is not a PDP_IMAGE_YV12/GREY"); + return; + } + + + /* fill the texture with the data in the packet */ + pdp_glx_fill_texture(x); + + /* display the new image */ + pdp_glx_bang(x); + + +} + + + +static void pdp_glx_display_texture(t_pdp_glx *x) +{ + float fx = (float)x->x_width / x->x_tex_width; + float fy = (float)x->x_height / x->x_tex_height; + + if (!x->x_initialized) return; + + /* set window context current */ + glXMakeCurrent(x->x_dpy, x->x_xwin.win, x->x_glx_context); + + /* setup viewport, projection and modelview */ + glViewport(0, 0, x->x_xwin.winwidth, x->x_xwin.winheight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.0, x->x_xwin.winwidth, 0.0, x->x_xwin.winheight); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + + /* enable default texture */ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, x->x_texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + /* display texture */ + glBegin(GL_QUADS); + glTexCoord2f(fx, fy); + glVertex2i(x->x_xwin.winwidth,0); + glTexCoord2f(fx, 0); + glVertex2i(x->x_xwin.winwidth, x->x_xwin.winheight); + glTexCoord2f(0.0, 0.0); + glVertex2i(0, x->x_xwin.winheight); + glTexCoord2f(0, fy); + glVertex2i(0,0); + glEnd(); + + + glFlush(); + glXSwapBuffers(x->x_dpy,x->x_xwin.win); + +} + + + +/* redisplays image */ +static void pdp_glx_bang_thread(t_pdp_glx *x) +{ + + + pdp_glx_display_texture(x); + XFlush(x->x_dpy); + +} + +static void pdp_glx_bang_callback(t_pdp_glx *x) +{ + /* receive events & output them */ + pdp_xwindow_send_events(&x->x_xwin, x->x_outlet); + + /* release the packet if there is one */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1;} + +static void pdp_glx_bang(t_pdp_glx *x) +{ + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_glx_try_autocreate(x)) return; + } + + + /* if previous queued method returned + schedule a new one, else ignore */ + +/* + if (-1 == x->x_queue_id) { + pdp_queue_add(x, pdp_glx_bang_thread, pdp_glx_bang_callback, &x->x_queue_id); + } +*/ + /* don't process in thread */ + pdp_glx_bang_thread(x); + pdp_glx_bang_callback(x); + +} + + + +static void pdp_glx_input_0(t_pdp_glx *x, t_symbol *s, t_floatarg f) +{ + + if (s == gensym("register_ro")) pdp_packet_copy_ro_or_drop(&x->x_packet0, (int)f); + if (s == gensym("process")) pdp_glx_process(x); +} + + + +static void pdp_glx_vga(t_pdp_glx *x) +{ + pdp_glx_resize(x, 640, 480); +} + +static void pdp_glx_autocreate(t_pdp_glx *x, t_floatarg f) +{ + if (f != 0.0f) x->x_autocreate = PDP_OGL_AUTOCREATE_RETRY; + else x->x_autocreate = 0; +} + +static void pdp_glx_display(t_pdp_glx *x, t_symbol *s) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + x->x_display = s; + if (x->x_initialized){ + pdp_glx_destroy(x); + pdp_glx_create(x); + } +} + + + +static void pdp_glx_free(t_pdp_glx *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + pdp_glx_destroy(x); + pdp_xwindow_free(&x->x_xwin); + if (x->x_data) pdp_dealloc (x->x_data); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_glx_class; + + + +void *pdp_glx_new(void) +{ + t_pdp_glx *x = (t_pdp_glx *)pd_new(pdp_glx_class); + + pdp_xwindow_init(&x->x_xwin); + + x->x_outlet = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_display = gensym(":0"); + + x->x_dpy = 0; + + x->x_width = PDP_OGL_W; + x->x_height = PDP_OGL_H; + + x->x_data = pdp_alloc(4*PDP_OGL_W*PDP_OGL_H); + + x->x_initialized = 0; + pdp_glx_autocreate(x,1); + x->x_last_encoding = -1; + + x->x_tex_width = 64; + x->x_tex_height = 64; + + //pdp_glx_create(x); + + return (void *)x; +} + + + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_glx_setup(void) +{ + + + pdp_glx_class = class_new(gensym("pdp_glx"), (t_newmethod)pdp_glx_new, + (t_method)pdp_glx_free, sizeof(t_pdp_glx), 0, A_NULL); + + /* add creator for pdp_tex_win */ + + class_addmethod(pdp_glx_class, (t_method)pdp_glx_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_create, gensym("open"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_create, gensym("create"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_autocreate, gensym("autocreate"), A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_destroy, gensym("destroy"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_destroy, gensym("close"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_move, gensym("move"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_move, gensym("pos"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_display, gensym("display"), A_SYMBOL, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_fullscreen, gensym("fullscreen"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_moveresize, gensym("posdim"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_tile, gensym("tile"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + + /* accept both pdp and pdp_tex packets */ + class_addmethod(pdp_glx_class, (t_method)pdp_glx_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + + /* some shortcuts for the lazy */ + class_addmethod(pdp_glx_class, (t_method)pdp_glx_vga, gensym("vga"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif + + diff --git a/modules/image_io/pdp_qt.c b/modules/image_io/pdp_qt.c new file mode 100644 index 0000000..5e1111c --- /dev/null +++ b/modules/image_io/pdp_qt.c @@ -0,0 +1,974 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten + * + * 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 +#include + +#include "pdp.h" +#include "pdp_llconv.h" + + +#define min(x,y) ((xx_clock); + pdp_procqueue_finish(q, x->x_queue_id); + + if (x->x_initialized){ + /* close file */ + quicktime_close(x->x_qt); + x->x_initialized = 0; + + + /* free video data */ + if (x->x_video_tracks){ + FREE(x->x_qt_frame); + FREE(x->x_qt_rows); + x->x_video_tracks = 0; + //x->x_qt_rows = 0; + //x->x_qt_frame = 0; + } + + /* free audio data */ + if (x->x_audio_tracks){ + x->x_chunk_used[0] = 0; + x->x_chunk_used[1] = 0; + FREE(x->x_chunk_buf); + FREE(x->x_qt_audiochans); + x->x_audio_tracks = 0; + //x->x_qt_audiochans = 0; + //x->x_chunk_buf = 0; + x->x_chunk[0][0] = 0; + x->x_chunk[0][1] = 0; + x->x_chunk[1][0] = 0; + x->x_chunk[1][1] = 0; + } + + + } + + +} + +void pdp_qt_create_pdp_packet(t_pdp_qt *x) +{ + t_pdp *header; + t_image *image; + + + /* round to next legal size */ + /* if size is illegal, image distortion will occur */ + u32 w = pdp_imageproc_legalwidth(x->x_video_width); + u32 h = pdp_imageproc_legalheight(x->x_video_height); + + + int nbpixels = w * h; + int packet_size = (nbpixels + (nbpixels >> 1)) << 1; + + + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_new_image_YCrCb(w, h); + header = pdp_packet_header(x->x_packet0); + image = pdp_packet_image_info(x->x_packet0); + + if (!header){ + post("%s: ERROR: can't create new packet", x->x_name->s_name); + return; + } + + + //header->info.image.encoding = (x->x_qt_cmodel == BC_RGB888) ? PDP_IMAGE_GREY : PDP_IMAGE_YV12; + //image->encoding = PDP_IMAGE_YV12; + //image->width = w; + //image->height = h; +} + + + + +static void pdp_qt_open(t_pdp_qt *x, t_symbol *name) +{ + unsigned int size; + unsigned int i; + unsigned int chunk_bytesize; + + post("%s: opening %s", x->x_name->s_name, name->s_name); + + + /* close previous one */ + pdp_qt_close(x); + + + /* check if qt file */ + if(0 == quicktime_check_sig(name->s_name)){ + post("%s: ERROR: not a quicktime file", x->x_name->s_name); + goto exit; + } + + /* open */ + DEBUG_MSG(x->x_qt = quicktime_open(name->s_name, 1, 0);) + if (!(x->x_qt)){ + post("%s: ERROR: can't open file", x->x_name->s_name); + goto exit; + } + + /* check video */ + x->x_video_tracks = 0; + if (quicktime_has_video(x->x_qt)) { + x->x_video_framerate = quicktime_frame_rate (x->x_qt, 0); + x->x_video_length = quicktime_video_length (x->x_qt, 0); + x->x_video_width = quicktime_video_width (x->x_qt, 0); + x->x_video_height = quicktime_video_height (x->x_qt, 0); + post("%s: video stream found (%dx%d pixels, %0.00f fps, %d frames, %s codec)", + x->x_name->s_name, x->x_video_width, x->x_video_height, x->x_video_framerate, + x->x_video_length, quicktime_video_compressor(x->x_qt, 0)); + x->x_video_tracks = quicktime_video_tracks(x->x_qt); + + } + + + /* check audior */ + x->x_audio_tracks = 0; + if (quicktime_has_audio(x->x_qt)) { + x->x_audio_tracks = quicktime_audio_tracks (x->x_qt); + //x->x_audio_channels = quicktime_track_channels (x->x_qt, 0); + x->x_audio_channels = lqt_total_channels (x->x_qt); + x->x_audio_samplerate = quicktime_sample_rate (x->x_qt, 0); + x->x_audio_length = quicktime_audio_length (x->x_qt, 0); + x->x_chunk_size = (int)((float)x->x_audio_samplerate / x->x_video_framerate); + post("%s: audio stream found (%d channels, %d Hz, %d samples, chunksize %d)", + x->x_name->s_name, x->x_audio_channels, x->x_audio_samplerate, x->x_audio_length, x->x_chunk_size); + } + + /* check if video codec is supported */ + if (x->x_video_tracks){ + if (!quicktime_supported_video(x->x_qt,0)) { + post("%s: WARNING: unsupported video codec",x->x_name->s_name); + x->x_video_tracks = 0; + } + } + + /* check if audio codec is supported */ + if (x->x_audio_tracks){ + if (!quicktime_supported_audio(x->x_qt,0)) { + post("%s: WARNING: unsupported audio codec", x->x_name->s_name); + x->x_audio_tracks = 0; + } + } + + + + /* check which colormodel to use */ + if (x->x_video_tracks){ + + if (quicktime_reads_cmodel(x->x_qt,BC_YUV420P,0)){ + post("%s: using colormodel YUV420P", x->x_name->s_name); + x->x_qt_cmodel = BC_YUV420P; + } + else if (quicktime_reads_cmodel(x->x_qt,BC_YUV422,0)){ + post("%s: using colormodel YUV422", x->x_name->s_name); + x->x_qt_cmodel = BC_YUV422; + } + else if (quicktime_reads_cmodel(x->x_qt,BC_RGB888,0)){ + post("%s: using colormodel RGB888", x->x_name->s_name); + x->x_qt_cmodel = BC_RGB888; + } + else { + post("%s: WARNING: can't find a usable colour model", x->x_name->s_name); + x->x_video_tracks = 0; + } + + } + + + + /* no video == errors */ + if (!x->x_video_tracks) { + post("%s: ERROR: no usable video stream found.", x->x_name->s_name); + goto exit_close; + } + + + /* initialize video data structures */ + if (x->x_video_tracks){ + + /* allocate enough space for all supported colormodels (24bpp)*/ + x->x_frame = 0; + x->x_qt_frame = (unsigned char*)pdp_alloc(x->x_video_width * x->x_video_height * 3); + x->x_qt_rows = (unsigned char **)pdp_alloc(sizeof(unsigned char *) * x->x_video_height); + size = x->x_video_width * x->x_video_height; + + switch(x->x_qt_cmodel){ + case BC_YUV420P: + /* planar with u&v 2x2 subsampled */ + x->x_qt_rows[0] = &x->x_qt_frame[0]; + x->x_qt_rows[2] = &x->x_qt_frame[size]; + x->x_qt_rows[1] = &x->x_qt_frame[size + (size>>2)]; + break; + + case BC_YUV422: + /* packed with u&v 2x subsampled (lines) */ + /* later on we will convert this to planar */ + for(i=0; i< x->x_video_height; i++) x->x_qt_rows[i] = &x->x_qt_frame[i * x->x_video_width * 2]; + break; + + case BC_RGB888: + /* packed rgb */ + /* later on we will convert this to planar */ + for(i=0; i< x->x_video_height; i++) x->x_qt_rows[i] = &x->x_qt_frame[i * x->x_video_width * 3]; + break; + + default: + post("%s: error on init: unkown colour model",x->x_name->s_name); + break; + } + + DEBUG_MSG(quicktime_set_cmodel(x->x_qt, x->x_qt_cmodel);) + outlet_float(x->x_outlet2, (float)quicktime_video_length(x->x_qt,0)); + + } + + /* initialize audio data structures */ + if (x->x_audio_tracks){ + x->x_chunk_pos = 0; + x->x_chunk_current = 0; + + chunk_bytesize = sizeof(float)*x->x_chunk_size; + x->x_chunk_buf = (float *)pdp_alloc(chunk_bytesize * 4); + memset(x->x_chunk_buf, 0, chunk_bytesize * 4); + x->x_chunk[0][0] = x->x_chunk_buf; + x->x_chunk[0][1] = x->x_chunk_buf + x->x_chunk_size ; + x->x_chunk[1][0] = x->x_chunk_buf + x->x_chunk_size * 2; + x->x_chunk[1][1] = x->x_chunk_buf + x->x_chunk_size * 3; + x->x_chunk_used[0] = 0; + x->x_chunk_used[1] = 0; + x->x_syncaudio = x->x_istilde; //sync on audio if this is a tilde object + + DEBUG_MSG(if (x->x_audio_channels == 0) exit(1);) + x->x_qt_audiochans = (float **)pdp_alloc(x->x_audio_channels * sizeof(float **)); + memset(x->x_qt_audiochans, 0, x->x_audio_channels * sizeof(float **)); + } + else { + x->x_syncaudio = 0; + } + + + /* everything went well */ + x->x_initialized = 1; + + /* start playback if outplay is on */ + if(x->x_autoplay) clock_delay(x->x_clock, 1000.0L / (double)x->x_video_framerate); + + /* brag about success */ + post("%s: %s opened", x->x_name->s_name, name->s_name); + + return; + + /* error exits */ + + exit_close: + DEBUG_MSG(quicktime_close(x->x_qt);) + + exit: + x->x_initialized = 0; + x->x_audio_tracks = 0; + x->x_video_tracks = 0; + return; + +} + + +//static void pdp_qt_setposition(t_pdp_qt *x, int pos) +//{ +// x->x_frame = pos; +// DEBUG_MSG(if(x->x_video_tracks) quicktime_set_video_position(x->x_qt, pos, 0);) +// DEBUG_MSG(if(x->x_audio_tracks) quicktime_set_audio_position(x->x_qt, pos * x->x_chunk_size, 0);) +// +//} + + +static void pdp_qt_bangaudio(t_pdp_qt *x) +{ + int lefterr=0; + int righterr=0; + int err=0; + int sample = 0; + int remaining = 0; + int readamount = 0; + + + + if (!x->x_initialized){ + //post("pdp_qt: no qt file opened"); + return; + } + + + if (!x->x_audio_tracks){ + //post("pdp_qt: no audio stream present"); + return; + } + + + + //DEBUG_MSG(sample = quicktime_audio_position(x->x_qt,0);) + + + // if the active chunk is unused, clear it and mark it used + if (!x->x_chunk_used[x->x_chunk_current]){ + //post("%s: clearing unused active chunk",x->x_name->s_name); + + + + //probably this is the !@#%&*(*)&!$() bug + //memset(x->x_chunk[0][x->x_chunk_current], 0, sizeof(float)*2*x->x_chunk_size); + //memset(x->x_chunk[1][x->x_chunk_current], 0, sizeof(float)*2*x->x_chunk_size); + + memset(x->x_chunk[0][x->x_chunk_current], 0, sizeof(float) * x->x_chunk_size); + memset(x->x_chunk[1][x->x_chunk_current], 0, sizeof(float) * x->x_chunk_size); + + + + + x->x_chunk_used[x->x_chunk_current] = 1; + } + + // compute the remaining time + DEBUG_MSG(remaining = (int ) ( quicktime_audio_length(x->x_qt, 0) - quicktime_audio_position(x->x_qt, 0) );) + readamount = min(remaining, x->x_chunk_size); + if (!readamount) return; + + + // if the inactive chunk is unused, fill it with the current frame's audio data and mark it used + if (!x->x_chunk_used[!x->x_chunk_current]){ + switch(x->x_audio_channels){ + case 1: + x->x_qt_audiochans[0] = x->x_chunk[0][!x->x_chunk_current]; + x->x_qt_audiochans[1] = 0; + DEBUG_MSG(err = lqt_decode_audio(x->x_qt, NULL, x->x_qt_audiochans, readamount);) + break; + default: + x->x_qt_audiochans[0] = x->x_chunk[0][!x->x_chunk_current]; + x->x_qt_audiochans[1] = x->x_chunk[1][!x->x_chunk_current]; + DEBUG_MSG(err = lqt_decode_audio(x->x_qt, NULL, x->x_qt_audiochans, readamount);) + break; + } + x->x_chunk_used[!x->x_chunk_current] = 1; + } + // if it is used, something went wrong with sync + else{ + //post("%s: dropping audio chunk %d.",x->x_name->s_name, x->x_frame_thread); + } + + + if (err) post("%s: error decoding audio",x->x_name->s_name, x->x_frame_thread); + + // ensure audio pointer points to next frame's data + //DEBUG_MSG(quicktime_set_audio_position(x->x_qt, sample + readamount, 0);) + +} + + + + +static void pdp_qt_bangvideo(t_pdp_qt *x) +{ + unsigned int w, h, nbpixels, packet_size, i,j; + unsigned int *source, *dest; + unsigned int uoffset, voffset; + short int* data; + t_pdp* header; + + // check if we want greyscale output or not + //int grey = (x->x_qt_cmodel == BC_RGB888); + + static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff}; + + if ((!x->x_initialized) || (!x->x_video_tracks)){ + //post("pdp_qt: no qt file opened"); + return; + } + + w = x->x_video_width; + h = x->x_video_height; + nbpixels = x->x_video_width * x->x_video_height; + + // create a new packet + pdp_qt_create_pdp_packet(x); + + header = pdp_packet_header(x->x_packet0); + + if (!header) { + post("%s: ERROR: no packet available", x->x_name->s_name); + return; + } + + data = (short int *) pdp_packet_data(x->x_packet0); + + + DEBUG_MSG(lqt_decode_video(x->x_qt, x->x_qt_rows, 0);) + + + switch(x->x_qt_cmodel){ + case BC_YUV420P: + pdp_llconv(x->x_qt_frame, RIF_YVU__P411_U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); + break; + + case BC_YUV422: + pdp_llconv(x->x_qt_frame, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); + break; + + case BC_RGB888: + pdp_llconv(x->x_qt_frame, RIF_RGB__P____U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); + break; + + default: + post("%s: error on decode: unkown colour model",x->x_name->s_name); + break; + } + + + +} + +static void pdp_qt_sendpacket(t_pdp_qt *x) +{ + + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + + //if (x->x_packet0 != -1){ + //pdp_packet_mark_unused(x->x_packet0); + //outlet_pdp(x->x_outlet0, x->x_packet0); + //x->x_packet0 = -1; + //} +} + + +static void pdp_qt_thread_bang(t_pdp_qt *x) +{ + // set audio position + if(x->x_video_tracks) quicktime_set_video_position(x->x_qt, x->x_frame_thread, 0); + + // bang video + pdp_qt_bangvideo(x); + + // if it's a tilde object, bang audio + if (x->x_istilde && x->x_audio_tracks){ + quicktime_set_audio_position(x->x_qt, x->x_frame_thread * x->x_chunk_size, 0); + pdp_qt_bangaudio(x); + } + +} + + +static void pdp_qt_bang(t_pdp_qt *x) +{ + int length, pos; + + t_pdp_procqueue *q = pdp_queue_get_queue(); + + /* return if not initialized */ + if (!x->x_initialized) return; + + //length = quicktime_video_length(x->x_qt,0); + //pos = quicktime_video_position(x->x_qt,0); + length = x->x_video_length; + pos = x->x_frame; + + + /* check bounds */ + if (x->x_loop){ + pos = x->x_frame % length; + if (pos < 0) pos += length; + } + else{ + if (pos < 0) pos = 0; + if (pos >= length) pos = length - 1; + } + + /* store next frame for access in thread */ + x->x_frame_thread = pos; + + + // if autoplay is on and we do not have audio synchro + // set clock to play next frame + if (x->x_autoplay && !x->x_syncaudio) clock_delay(x->x_clock, 1000.0L / (double)x->x_video_framerate); + + + // make sure prev decode is finished don't drop frames in this one + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + + /* only decode new stuff if previous is done */ + if (-1 == x->x_queue_id){ + // send the current frame number to outlet + outlet_float(x->x_outlet1, (float)pos); + + //pdp_qt_setposition(x, pos); + + // start process method + if (x->x_process_in_thread) pdp_procqueue_add(q, x, pdp_qt_thread_bang, pdp_qt_sendpacket, &x->x_queue_id); + else { + pdp_qt_thread_bang(x); + pdp_qt_sendpacket(x); + } + } + // advance frame + x->x_frame = pos + 1; + + + // send the packet + //pdp_qt_sendpacket(x); +} + + + +//static void pdp_qt_getaudiochunk(t_pdp_qt *x, int channel) +//{ +// if (!x->x_audio_tracks) return; +// quicktime_decode_audio(x->x_qt, NULL, x->x_chunk[channel][x->x_chunk_current], x->x_chunk_size<<1, channel); +// +//} + +static void pdp_qt_loop(t_pdp_qt *x, t_floatarg loop) +{ + int loopi = (int)loop; + x->x_loop = !(loopi == 0); +} + +static void pdp_qt_autoplay(t_pdp_qt *x, t_floatarg play) +{ + int playi = (int)play; + x->x_autoplay = !(playi == 0); + + + // reset clock if autoplay is off + if (!x->x_autoplay) clock_unset(x->x_clock); + + +} + + + +static void pdp_qt_frame_cold(t_pdp_qt *x, t_floatarg frameindex) +{ + int frame = (int)frameindex; + //int length; + + + x->x_frame = frame; + + //if (!(x->x_initialized)) return; + + //length = quicktime_video_length(x->x_qt,0); + + //frame = (frame >= length) ? length-1 : frame; + //frame = (frame < 0) ? 0 : frame; + + //pdp_qt_setposition(x, frame); +} + +static void pdp_qt_frame(t_pdp_qt *x, t_floatarg frameindex) +{ + pdp_qt_frame_cold(x, frameindex); + pdp_qt_bang(x); +} + +static void pdp_qt_stop(t_pdp_qt *x) +{ + pdp_qt_autoplay(x, 0); +} + +static void pdp_qt_continue(t_pdp_qt *x) +{ + pdp_qt_autoplay(x, 1); + pdp_qt_bang(x); +} + + +static void pdp_qt_play(t_pdp_qt *x){ + pdp_qt_frame_cold(x, 0); + pdp_qt_continue(x); +} + + + + +static void pdp_qt_importaudio(t_pdp_qt *x, t_symbol *array, t_floatarg channel) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + int c = (int)channel; + t_garray *g; + int vecsize; + int sample; + float *f; + int i; + + /* if there's no audio, there's nothing to export */ + if (!x->x_audio_tracks) return; + + /* check audio channel */ + if ((c < 0) || (c >= x->x_audio_channels)) return; + + /* check if array exists */ + if (!(g = (t_garray *)pd_findbyclass(array, garray_class))){ + pd_error(x, "%s: no such table", array->s_name); + return; + } + + post("%s: importing audio channel %d into array %s", x->x_name->s_name, c, array->s_name); + + + // make sure decode is finished + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + + + /* resize array */ + garray_resize(g, x->x_audio_length); + + /* for sanity's sake let's clear the save-in-patch flag here */ + garray_setsaveit(g, 0); + garray_getfloatarray(g, &vecsize, &f); + + /* if the resize failed, garray_resize reported the error */ + if (vecsize != x->x_audio_length){ + pd_error(x, "array resize failed"); + return; + } + + /* save pointer in file */ + DEBUG_MSG(sample = quicktime_audio_position(x->x_qt, 0);) + DEBUG_MSG(quicktime_set_audio_position(x->x_qt, 0, 0);) + + /* transfer the audio file to the end of the array */ + DEBUG_MSG(quicktime_decode_audio(x->x_qt, NULL, f, vecsize, c);) + + /* restore pointer in file */ + DEBUG_MSG(quicktime_set_audio_position(x->x_qt, sample, 0);) + + +} + + + +static t_int *pdp_qt_perform(t_int *w) +{ + t_pdp_qt *x = (t_pdp_qt *)w[1]; + t_float *out0 = (t_float *)w[2]; + t_float *out1 = (t_float *)w[3]; + t_int n = (t_int)w[4]; + + t_int xfer_samples; + if (!x->x_initialized || !x->x_audio_tracks) goto zero; + + while(1){ + // check current chunk + if (!x->x_chunk_used[x->x_chunk_current]) goto zero; + + + // transfer from chunk to output + xfer_samples = min(n, x->x_chunk_size - x->x_chunk_pos); + + //x->x_audio_channels = 1; + + if (x->x_audio_channels == 1){ + memcpy(out0, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + memcpy(out1, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + } + else { + memcpy(out0, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + memcpy(out1, x->x_chunk[1][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + } + out0 += xfer_samples; + out1 += xfer_samples; + n -= xfer_samples; + x->x_chunk_pos += xfer_samples; + + + // check if chunk is finished, if so mark unused, swap buffers and set clock + if (x->x_chunk_size == x->x_chunk_pos){ + x->x_chunk_used[x->x_chunk_current] = 0; + x->x_chunk_pos = 0; + x->x_chunk_current ^= 1; + if (x->x_autoplay) clock_delay(x->x_clock, 0L); + } + + // if chunk is not finished, the output buffer is full + else{ + goto exit; + } + + } + + + zero: + // fill the rest of the output with zeros + memset(out0, 0, sizeof(float)*n); + memset(out1, 0, sizeof(float)*n); + + exit: + return(w+5); +} + +static void pdp_qt_dsp(t_pdp_qt *x, t_signal **sp) +{ + dsp_add(pdp_qt_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); + +} + +static void pdp_qt_process_in_thread(t_pdp_qt *x, t_float f) +{ + + int t = (f != 0.0f); + + x->x_process_in_thread = t; + + post("pdp_qt: thread processing switched %d", t ? "on" : "off"); + +} + +static void pdp_qt_tick(t_pdp_qt *x) +{ + + // bang audio/video + pdp_qt_bang(x); +} + +static void pdp_qt_free(t_pdp_qt *x) +{ + clock_unset(x->x_clock); + pdp_qt_close(x); + clock_free(x->x_clock); + //free (x->x_state_data); + +} + + + +t_class *pdp_qt_class; +t_class *pdp_qt_tilde_class; + + +void pdp_qt_init_common(t_pdp_qt *x) +{ + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold")); + + /* add common outlets */ + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_outlet1 = outlet_new(&x->x_obj, &s_float); + x->x_outlet2 = outlet_new(&x->x_obj, &s_float); + + /* init */ + x->x_gain = 1.0f; + x->x_process_in_thread = 0; + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_initialized = 0; + x->x_audio_tracks = 0; + x->x_video_tracks = 0; + x->x_loop = 0; + x->x_autoplay = 0; + x->x_chunk[0][0] = 0; + x->x_chunk[0][1] = 0; + x->x_chunk[1][0] = 0; + x->x_chunk[1][1] = 0; + + /* initialize clock object */ + x->x_clock = clock_new(x, (t_method)pdp_qt_tick); + + + + +} + +void *pdp_qt_new(void) +{ + t_pdp_qt *x = (t_pdp_qt *)pd_new(pdp_qt_class); + x->x_name = gensym("pdp_qt"); + x->x_istilde = 0; + pdp_qt_init_common(x); + return (void *)x; +} + +void *pdp_qt_tilde_new(void) +{ + t_pdp_qt *x = (t_pdp_qt *)pd_new(pdp_qt_tilde_class); + x->x_name = gensym("pdp_qt"); + x->x_istilde = 1; + + pdp_qt_init_common(x); + + /* add outlets to the right so pdp_qt~ can replace pdp_qt without breaking a patch */ + x->x_outleft = outlet_new(&x->x_obj, &s_signal); + x->x_outright = outlet_new(&x->x_obj, &s_signal); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_qt_setup_common(t_class *class) +{ + class_addmethod(class, (t_method)pdp_qt_bang, gensym("bang"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_close, gensym("close"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(class, (t_method)pdp_qt_autoplay, gensym("autoplay"), A_DEFFLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_stop, gensym("stop"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_play, gensym("play"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_continue, gensym("cont"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_loop, gensym("loop"), A_DEFFLOAT, A_NULL); + class_addfloat (class, (t_method)pdp_qt_frame); + class_addmethod(class, (t_method)pdp_qt_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_process_in_thread, gensym("thread"), A_FLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_importaudio, gensym("importaudio"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_importaudio, gensym("dump"), A_SYMBOL, A_DEFFLOAT, A_NULL); +} + +void pdp_qt_setup(void) +{ + + /* plain class */ + pdp_qt_class = class_new(gensym("pdp_qt"), (t_newmethod)pdp_qt_new, + (t_method)pdp_qt_free, sizeof(t_pdp_qt), 0, A_NULL); + pdp_qt_setup_common(pdp_qt_class); + + + /* tilde class */ + pdp_qt_tilde_class = class_new(gensym("pdp_qt~"), (t_newmethod)pdp_qt_tilde_new, + (t_method)pdp_qt_free, sizeof(t_pdp_qt), 0, A_NULL); + pdp_qt_setup_common(pdp_qt_tilde_class); + + class_addmethod(pdp_qt_tilde_class, (t_method)pdp_qt_dsp, gensym("dsp"), 0); +} + +#ifdef __cplusplus +} +#endif + + diff --git a/modules/image_io/pdp_sdl.c b/modules/image_io/pdp_sdl.c new file mode 100644 index 0000000..d30ef84 --- /dev/null +++ b/modules/image_io/pdp_sdl.c @@ -0,0 +1,510 @@ +/* + * Pure Data Packet module. + * Copyright (c) 2003 by martin pi + * Copyright (c) by Tom Schouten + * + * 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. + * + */ + +/* + +pdp sdl output + +DONE: + +TODO: + * close window (event) + * fullscreen chose resolution + * event handling in different object (and look at mplayer for that!) + +*/ + + +#include +//#include +//#include +//#include + +#include + +//#include +//#include + +#include "pdp.h" +#include "pdp_llconv.h" + + +/* initial image dimensions */ +#define PDP_SDL_W 320 +#define PDP_SDL_H 240 + + +typedef struct pdp_sdl_struct { + t_object x_obj; + t_float x_f; + + int x_packet0; + int x_queue_id; + + SDL_Surface *x_sdl_surface; + SDL_Overlay *x_sdl_overlay; + SDL_Rect x_sdl_rect; + + int x_xid; + + Uint32 x_sdl_format; + + int x_winwidth; + int x_winheight; + + unsigned int x_width; + unsigned int x_height; + int x_last_encoding; + int x_cursor; + + int x_initialized; + int x_backfromthread; + + int x_fullscreen; + +} t_pdp_sdl; + +static SDL_Surface *pdp_sdl_getSurface(int xid, char* title, int width, int height, int bits, int fullscreenflag, int cursorflag) { + Uint32 flags; + int size,i; + SDL_Surface *screen; + char SDL_hack[32]; + + /* next lines from gstreamer plugin sdlvideosink */ + if (xid < 0) unsetenv("SDL_WINDOWID"); + else { + sprintf(SDL_hack, "%d", xid); + setenv("SDL_WINDOWID", SDL_hack, 1); + } + + /* Initialize SDL */ + if (!SDL_WasInit(SDL_INIT_VIDEO)) { + if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)) { + post("SDL: Initializing of SDL failed: %s.\n", SDL_GetError()); + return NULL; + } + /* ignore events :: only keys and wm_quit */ + for ( i=SDL_NOEVENT; i0 ) { + flags |= SDL_FULLSCREEN|SDL_DOUBLEBUF; + } + + screen = SDL_SetVideoMode(width, height, bits, flags); + if ( screen == NULL ) { + post("Couldn't set video mode: %s\n", SDL_GetError()); + return NULL; + } + + SDL_WM_SetCaption (title, title); + + SDL_ShowCursor(cursorflag); + + return screen; +} + +static SDL_Surface *pdp_sdl_recreateSurface(SDL_Surface *old, int xid, char* title, int width, int height, int bits, int fullscreenflag, int cursorflag) { + SDL_Surface *new = pdp_sdl_getSurface(xid, title, width, height, bits, fullscreenflag, cursorflag); + if (new != NULL) SDL_FreeSurface(old); + return new; +} + +static inline void pdp_sdl_getOverlay(t_pdp_sdl* x) { + x->x_sdl_overlay = SDL_CreateYUVOverlay(x->x_width, x->x_height, x->x_sdl_format, x->x_sdl_surface); +} + +static inline void pdp_sdl_freeOverlay(t_pdp_sdl* x) { + SDL_FreeYUVOverlay(x->x_sdl_overlay); +} + +static int pdp_sdl_drawImage(t_pdp_sdl* x, t_image *image, short int *pixels) { + + unsigned int width = image->width; + unsigned int height = image->height; + int encoding = image->encoding; + unsigned int* uintdata; + int i; + + + /* 8bit y fulscale and 8bit u,v 2x2 subsampled */ + //static short int gain[4] = {0x0100, 0x0100, 0x0100, 0x0100}; + int nbpixels = width * height; + long size = (width * height + (((width>>1)*(height>>1))<<1)); + + /* check if xvimage needs to be recreated */ + if ((width != x->x_width) || (height != x->x_height)){ + post("pdp_xv: replace image"); + x->x_width = width; + x->x_height = height; + pdp_sdl_freeOverlay(x); + pdp_sdl_getOverlay(x); + } + + SDL_LockYUVOverlay(x->x_sdl_overlay); + if (pixels) { + pdp_llconv(pixels,RIF_YVU__P411_S16, (Uint8 *)(* x->x_sdl_overlay->pixels), RIF_YVU__P411_U8, x->x_width, x->x_height); + } else bzero((Uint8 *)(* x->x_sdl_overlay->pixels), size); + SDL_UnlockYUVOverlay(x->x_sdl_overlay); + + return 1; +} + +static void pdp_sdl_fullscreen(t_pdp_sdl *x, t_floatarg f); + +static inline void pdp_sdl_recreate(t_pdp_sdl *x) { + x->x_sdl_surface = pdp_sdl_recreateSurface(x->x_sdl_surface, x->x_xid,"pdp-sdl", x->x_winwidth, x->x_winheight, 16, x->x_fullscreen,x->x_cursor); +} + +static int pdp_sdl_create(t_pdp_sdl *x) { + if (x->x_initialized){ + return 0; + } + x->x_initialized = 0; + + x->x_sdl_surface = pdp_sdl_getSurface(x->x_xid, "pdp-sdl", x->x_winwidth, x->x_winheight, 16, x->x_fullscreen,x->x_cursor); + if (x->x_sdl_surface != NULL) { + pdp_sdl_getOverlay(x); + if (x->x_sdl_overlay != NULL) { + x->x_sdl_rect.x = 0; + x->x_sdl_rect.y = 0; + x->x_sdl_rect.w = x->x_winwidth; + x->x_sdl_rect.h = x->x_winheight; + x->x_initialized = 1; + post("created successfully"); + } + } + x->x_backfromthread = 1; + + return x->x_initialized; +} + +static void pdp_sdl_destroy(t_pdp_sdl *x); +static void pdp_sdl_resize(t_pdp_sdl *x,t_floatarg,t_floatarg); + +static void pdp_sdl_checkEvents(t_pdp_sdl *x) { + Uint8 *keys; + SDL_Event event; + + if (SDL_PollEvent(&event)!=1) return; + + switch( event.type ){ + case SDL_KEYDOWN: + case SDL_KEYUP: + keys = SDL_GetKeyState(NULL); + + if(keys[SDLK_UP]) { post("up"); } + if(keys[SDLK_DOWN]) { post("down"); } + + if(keys[SDLK_ESCAPE]) pdp_sdl_fullscreen(x,0); + break; + + case SDL_QUIT: + pdp_sdl_destroy(x); + break; + case SDL_VIDEORESIZE: + pdp_sdl_resize(x,(t_floatarg)event.resize.w,(t_floatarg)event.resize.h); + break; + default: + break; + } + + +} + +static void pdp_sdl_bang(t_pdp_sdl *x); + +static void pdp_sdl_process(t_pdp_sdl *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + void *data = pdp_packet_data (x->x_packet0); + + + if (!x->x_backfromthread) return; + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_sdl_create(x)) return; + } + + /* check for pending sdl events */ + pdp_sdl_checkEvents(x); + + /* check data packet */ + if (!(header)) { + post("pdp_sdl: invalid packet header"); + return; + } + if (PDP_IMAGE != header->type) { + post("pdp_sdl: packet is not a PDP_IMAGE"); + return; + } + if (header->info.image.encoding != PDP_IMAGE_YV12) { + post("pdp_sdl: packet is not a PDP_IMAGE_YV12"); + return; + } + + /* copy the packet to the sdlimage */ + pdp_sdl_drawImage(x, &header->info.image, (short int *)data); + + /* display the new image */ + pdp_sdl_bang(x); +} + +static void pdp_sdl_destroy(t_pdp_sdl *x) { + if (x->x_initialized){ + pdp_sdl_freeOverlay(x); + SDL_FreeSurface(x->x_sdl_surface); + x->x_initialized = 0; + SDL_Quit(); + } +} + +static void pdp_sdl_random(t_pdp_sdl *x) { + unsigned int i; + long *intdata = (long *)(* x->x_sdl_overlay->pixels); + SDL_LockYUVOverlay(x->x_sdl_overlay); + for(i=0; ix_width*x->x_height/4; i++) intdata[i]=random(); + SDL_UnlockYUVOverlay(x->x_sdl_overlay); +} + +/* redisplays image */ +static void pdp_sdl_bang_thread(t_pdp_sdl *x) { + if (SDL_DisplayYUVOverlay(x->x_sdl_overlay, &(* x).x_sdl_rect) <0) + post("pdp_sdl: __LINE__ cannot display"); +} + +static void pdp_sdl_bang_callback(t_pdp_sdl *x) +{ + x->x_backfromthread = 1; + + /* release the packet if there is one */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1;} + +static void pdp_sdl_bang(t_pdp_sdl *x) { + + /* if previous queued method returned + schedule a new one, else ignore */ + if (x->x_backfromthread) { + x->x_backfromthread = 0; + pdp_queue_add(x, pdp_sdl_bang_thread, pdp_sdl_bang_callback, &x->x_queue_id); + } +} + +static void pdp_sdl_input_0(t_pdp_sdl *x, t_symbol *s, t_floatarg f) { + + if (s == gensym("register_ro")) pdp_packet_copy_ro_or_drop(&x->x_packet0, (int)f); + if (s == gensym("process")) pdp_sdl_process(x); + +} + +static void pdp_sdl_resize(t_pdp_sdl* x, t_floatarg width, t_floatarg height) { + + if (x->x_initialized && (!x->x_fullscreen) && (width>0) && (height>0)) { + post("should get %d/%d",(int)width,(int) height); + x->x_winwidth=(int)width; + x->x_winheight=(int)height; + pdp_sdl_recreate(x); + } +} + + +static void pdp_sdl_fullscreen(t_pdp_sdl *x, t_floatarg f) { + if (f == x->x_fullscreen) return; + + x->x_fullscreen = (f != 0.0f); + x->x_cursor=0; + + pdp_sdl_recreate(x); +} + +static void pdp_sdl_cursor(t_pdp_sdl *x, t_floatarg f) { + if (f == x->x_cursor) return; + + x->x_cursor = (f != 0.0f); + SDL_ShowCursor(x->x_cursor); +} + + +/* sets new target window */ + +static void pdp_sdl_win(t_pdp_sdl *x, t_floatarg *f) { + pdp_queue_finish(x->x_queue_id); + x->x_queue_id = -1; + x->x_xid = (int)f; + pdp_sdl_recreate(x); +} + +/* be very carefule not to set DGA fro here! */ +/* use export SDL_VIDEODRIVER=dga (or equivalent for your shell) instead */ + +static void pdp_sdl_renderer(t_pdp_sdl *x, t_symbol *s) { + char SDL_hack[32]; + + pdp_sdl_destroy(x); + + /* next lines from gstreamer plugin sdlvideosink */ + unsetenv("SDL_VIDEODRIVER"); + + sprintf(SDL_hack, "%s", s->s_name); + setenv("SDL_VIDEODRIVER", SDL_hack, 1); + + pdp_sdl_create(x); +} + +static void pdp_sdl_free(t_pdp_sdl *x) +{ + pdp_queue_finish(x->x_queue_id); + pdp_sdl_destroy(x); + pdp_packet_mark_unused(x->x_packet0); +// SDL_Quit(); +} + +static void pdp_sdl_listmodes(const char* title, Uint32 flags) { + SDL_Rect ** modes; + int i; + + /* Get available modes */ + modes = SDL_ListModes(NULL, flags); + + /* Check is there are any modes available */ + if(modes == (SDL_Rect **)0){ + printf("%s : No modes available!", title); + return; + } + + /* Check if our resolution is restricted */ + if(modes == (SDL_Rect **)-1){ + post("%s : All resolutions available.", title); + } else { + /* Print valid modes */ + for(i=0;modes[i];++i) + post("%s : %d x %d", title, modes[i]->w, modes[i]->h); + } + +} + +static void pdp_sdl_modes(t_pdp_sdl *x) { + pdp_sdl_listmodes("FULL|HWSURF|||||||||||||||||||||||||", SDL_FULLSCREEN|SDL_HWSURFACE); + pdp_sdl_listmodes("HWSURF|RESIZ|ASYNC|HWACCEL||||||||||", SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL); + pdp_sdl_listmodes("HWSURF|RESIZ|ASYNC|HWACCEL|FULL|DBUF", SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_FULLSCREEN|SDL_DOUBLEBUF); + pdp_sdl_listmodes("OPENGL|DBUF|HWSURF|ANYF|GLBLIT||||||", SDL_OPENGL|SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_ANYFORMAT|SDL_OPENGLBLIT); + pdp_sdl_listmodes("ANYF|RESIZ|RLEA|||||||||||||||||||||", SDL_ANYFORMAT|SDL_RESIZABLE|SDL_RLEACCEL); +} + +static void pdp_sdl_info(t_pdp_sdl *x) { + const SDL_VideoInfo *narf; + post("\nSDL video info: note that this only works under dga mode\n"); + narf = SDL_GetVideoInfo(); + post("Is it possible to create hardware surfaces?\t\thw_available=%d",narf->hw_available); + post("Is there a window manager available?\t\t\twm_available=%d",narf->wm_available); + post("Are hardware to hardware blits accelerated?\t\tblit_hw=%d",narf->blit_hw); + post("Are hardware to hardware colorkey blits accelerated?\tblit_hw_CC=%d",narf->blit_hw_CC); + post("Are hardware to hardware alpha bits accelerated?\tblit_hw_A=%d",narf->blit_hw_A); + post("Are software to hardware blits accelerated?\t\tblit_sw=%d",narf->blit_sw); + post("Are software to hardware colorkey blits accelerated?\tblit_sw_CC=%d",narf->blit_sw_CC); + post("Are software to hardware alpha blits accelerated?\tblit_sw_A=%d",narf->blit_sw_A); + post("Are color fills accelerated?\t\t\t\tblit_fill=%d",narf->blit_fill); + post("Total amount of video_mem: %d",narf->video_mem); + +} + +t_class *pdp_sdl_class; + +void *pdp_sdl_new(void) { + + + t_pdp_sdl *x = (t_pdp_sdl *)pd_new(pdp_sdl_class); + + x->x_packet0 = -1; + x->x_queue_id = -1; + + x->x_sdl_surface = NULL; + x->x_sdl_overlay = NULL; + x->x_sdl_format = SDL_YV12_OVERLAY; + + x->x_winwidth = PDP_SDL_W; + x->x_winheight = PDP_SDL_H; + + x->x_width = PDP_SDL_W; + x->x_height = PDP_SDL_H; + + x->x_backfromthread = 1; + x->x_initialized = 0; + + x->x_fullscreen = 0; + x->x_cursor=1; + + x->x_xid = -1; + + pdp_sdl_create(x); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_sdl_setup(void) +{ + + + pdp_sdl_class = class_new(gensym("pdp_sdl"), (t_newmethod)pdp_sdl_new, + (t_method)pdp_sdl_free, sizeof(t_pdp_sdl), 0, A_NULL); + + + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_random, gensym("random"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_create, gensym("create"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_info, gensym("info"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_modes, gensym("modes"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_destroy, gensym("destroy"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_destroy, gensym("close"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_fullscreen, gensym("fullscreen"), A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_win, gensym("window"), A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_renderer, gensym("renderer"), A_SYMBOL, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif + diff --git a/modules/image_io/pdp_v4l.c b/modules/image_io/pdp_v4l.c new file mode 100644 index 0000000..2fdf12d --- /dev/null +++ b/modules/image_io/pdp_v4l.c @@ -0,0 +1,794 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten + * + * 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_config.h" +#include "pdp.h" +#include "pdp_llconv.h" +#include "pdp_imageproc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 + +//include it anyway +//#ifdef HAVE_PWCV4L +#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; + + u32 x_minwidth; + u32 x_maxwidth; + u32 x_minheight; + u32 x_maxheight; + + +} t_pdp_v4l; + + + + + +static void pdp_v4l_audio(t_pdp_v4l *x, t_floatarg f) +{ + int i = 0; + if (x->x_initialized){ + fprintf(stderr," audios : %d\n",x->x_vcap.audios); + x->x_vaudio.audio = 0; + ioctl(x->x_tvfd,VIDIOCGAUDIO, &x->x_vaudio); + + fprintf(stderr," %d (%s): ",i,x->x_vaudio.name); + if (x->x_vaudio.flags & VIDEO_AUDIO_MUTABLE) + fprintf(stderr,"muted=%s ", + (x->x_vaudio.flags & VIDEO_AUDIO_MUTE) ? "yes":"no"); + if (x->x_vaudio.flags & VIDEO_AUDIO_VOLUME) + fprintf(stderr,"volume=%d ",x->x_vaudio.volume); + if (x->x_vaudio.flags & VIDEO_AUDIO_BASS) + fprintf(stderr,"bass=%d ",x->x_vaudio.bass); + if (x->x_vaudio.flags & VIDEO_AUDIO_TREBLE) + fprintf(stderr,"treble=%d ",x->x_vaudio.treble); + fprintf(stderr,"\n"); + + } +} + + +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) +{ + struct pwc_probe probe; + int isPhilips = 0; + +#ifdef HAVE_PWCV4L + /* skip test */ + isPhilips = 1; +#else + /* test for pwc */ + if (ioctl(x->x_tvfd, VIDIOCPWCPROBE, &probe) == 0) + if (!strcmp(x->x_vcap.name, probe.name)) + isPhilips = 1; + +#endif + + /* don't do pwc specific stuff */ + if (!isPhilips) return; + + post("pdp_v4l: detected pwc"); + + 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: camera framerate: %d", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT); + //post("pdp_v4l: pwc: setting camera 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: camera framerate set to %d fps", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT); + + } + + + return; + + + + closit: + pdp_v4l_close_error(x); + return; + +} + +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_setlegaldim(t_pdp_v4l *x, int xx, int yy); + +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); + + x->x_minwidth = pdp_imageproc_legalwidth(x->x_vcap.minwidth); + x->x_maxwidth = pdp_imageproc_legalwidth_round_down(x->x_vcap.maxwidth); + x->x_minheight = pdp_imageproc_legalheight(x->x_vcap.minheight); + x->x_maxheight = pdp_imageproc_legalheight_round_down(x->x_vcap.maxheight); + + + 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; + } + + pdp_v4l_setlegaldim(x, x->x_width, x->x_height); + width = x->x_width; + height = x->x_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; + t_image* image; + 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_image(PDP_IMAGE_YV12, w, h); + header = pdp_packet_header(object); + image = pdp_packet_image_info(object); + + if (!header){ + post("pdp_v4l: ERROR: can't allocate packet"); + return; + } + + data = (short int *) pdp_packet_data(object); + 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; + + /* long live standards. v4l's rgb is in fact ogl's bgr */ + case VIDEO_PALETTE_RGB24: + pdp_llconv(newimage, RIF_BGR__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_pass_if_valid(x->x_outlet0, &object); + +} + + +static void pdp_v4l_setlegaldim(t_pdp_v4l *x, int xx, int yy) +{ + + unsigned int w,h; + + w = pdp_imageproc_legalwidth((int)xx); + h = pdp_imageproc_legalheight((int)yy); + + w = (w < x->x_maxwidth) ? w : x->x_maxwidth; + w = (w > x->x_minwidth) ? w : x->x_minwidth; + + h = (h < x->x_maxheight) ? h : x->x_maxheight; + h = (h > x->x_minheight) ? h : x->x_minheight; + + x->x_width = w; + x->x_height = h; +} + +static void pdp_v4l_dim(t_pdp_v4l *x, t_floatarg xx, t_floatarg yy) +{ + if (x->x_initialized){ + pdp_v4l_close(x); + pdp_v4l_setlegaldim(x, (int)xx, (int)yy); + pdp_v4l_open(x, x->x_device); + + } + else{ + pdp_v4l_setlegaldim(x, (int)xx, (int)yy); + } +} + + +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 + + x->x_minwidth = pdp_imageproc_legalwidth(0); + x->x_maxwidth = pdp_imageproc_legalwidth_round_down(0x7fffffff); + x->x_minheight = pdp_imageproc_legalheight(0); + x->x_maxheight = pdp_imageproc_legalheight_round_down(0x7fffffff); + + + 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_audio, gensym("audio"), 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 diff --git a/modules/image_io/pdp_xv.c b/modules/image_io/pdp_xv.c new file mode 100644 index 0000000..e2d864b --- /dev/null +++ b/modules/image_io/pdp_xv.c @@ -0,0 +1,300 @@ +/* + * Pure Data Packet module. Xvideo image packet output + * Copyright (c) by Tom Schouten + * + * 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. + * + */ + +// pdp stuff +#include "pdp.h" +#include "pdp_base.h" + +// some x window glue code +#include "pdp_xwindow.h" + + +#define PDP_XV_AUTOCREATE_RETRY 10 + + +typedef struct pdp_xv_struct +{ + t_object x_obj; + + t_pdp_xwindow x_xwin; + t_pdp_xvideo x_xvid; + + t_outlet *x_outlet; + + int x_packet0; + int x_queue_id; + t_symbol *x_display; + + Display *x_dpy; + + int x_initialized; + int x_autocreate; + + +} t_pdp_xv; + + +static void pdp_xv_cursor(t_pdp_xv *x, t_floatarg f) +{ + pdp_xwindow_cursor(&x->x_xwin, f); +} + + +static void pdp_xv_destroy(t_pdp_xv* x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + if (x->x_initialized){ + + pdp_procqueue_finish(q, x->x_queue_id); // wait for thread to finish + x->x_queue_id = -1; + pdp_xvideo_close(&x->x_xvid); // close xvideo connection + pdp_xwindow_close(&x->x_xwin); // close the window + XCloseDisplay(x->x_dpy); // close the display connection + x->x_dpy = 0; + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + x->x_initialized = 0; + + } +} + + + +static void pdp_xv_create(t_pdp_xv* x) +{ + int i; + if(x->x_initialized) return; + + /* manually open a display */ + if (NULL == (x->x_dpy = XOpenDisplay(x->x_display->s_name))){ + post("pdp_xv: cant open display %s\n",x->x_display->s_name); + x->x_initialized = false; + return; + } + + /* open an xv port on the display */ + if (!(x->x_initialized = pdp_xvideo_open_on_display(&x->x_xvid, x->x_dpy))) goto exit_close_dpy; + + /* create a window on the display */ + if (!(x->x_initialized = pdp_xwindow_create_on_display(&x->x_xwin, x->x_dpy))) goto exit_close_win; + + /* done */ + return; + + /* cleanup exits */ + exit_close_win: + pdp_xwindow_close(&x->x_xwin); // cose window + exit_close_dpy: + XCloseDisplay(x->x_dpy); // close display + x->x_dpy = 0; +} + +static int pdp_xv_try_autocreate(t_pdp_xv *x) +{ + + if (x->x_autocreate){ + post("pdp_xv: autocreate window"); + pdp_xv_create(x); + if (!(x->x_initialized)){ + x->x_autocreate--; + if (!x->x_autocreate){ + post ("pdp_xv: autocreate failed %d times: disabled", PDP_XV_AUTOCREATE_RETRY); + post ("pdp_xv: send [autocreate 1] message to re-enable"); + return 0; + } + } + else return 1; + + } + return 0; +} + +static void pdp_xv_bang(t_pdp_xv *x); + +static void pdp_xv_bang_thread(t_pdp_xv *x) +{ + pdp_xvideo_display_packet(&x->x_xvid, &x->x_xwin, x->x_packet0); +} + + +static void pdp_xv_bang_callback(t_pdp_xv *x) +{ + /* receive events & output them */ + pdp_xwindow_send_events(&x->x_xwin, x->x_outlet); + + + /* release the packet if there is one */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + +} + +static void pdp_xv_bang(t_pdp_xv *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_xv_try_autocreate(x)) return; + } + + /* check if we can proceed */ + if (-1 != x->x_queue_id) return; + if (-1 == x->x_packet0) return; + + /* if previous queued method returned + schedule a new one, else ignore */ + if (-1 == x->x_queue_id) { + pdp_procqueue_add(q, x, pdp_xv_bang_thread, pdp_xv_bang_callback, &x->x_queue_id); + } + +} + +static void pdp_xv_input_0(t_pdp_xv *x, t_symbol *s, t_floatarg f) +{ + + if (s == gensym("register_ro")) pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*")); + if (s == gensym("process")) pdp_xv_bang(x); + +} + + + +static void pdp_xv_autocreate(t_pdp_xv *x, t_floatarg f) +{ + if (f != 0.0f) x->x_autocreate = PDP_XV_AUTOCREATE_RETRY; + else x->x_autocreate = 0; +} + +static void pdp_xv_display(t_pdp_xv *x, t_symbol *s) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + x->x_display = s; + if (x->x_initialized){ + pdp_xv_destroy(x); + pdp_xv_create(x); + } +} + +static void pdp_xv_fullscreen(t_pdp_xv *x) +{ + pdp_xwindow_fullscreen(&x->x_xwin); +} + +static void pdp_xv_resize(t_pdp_xv* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_resize(&x->x_xwin, width, height); +} + +static void pdp_xv_move(t_pdp_xv* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_move(&x->x_xwin, width, height); +} + +static void pdp_xv_moveresize(t_pdp_xv* x, t_floatarg xoff, t_floatarg yoff, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_moveresize(&x->x_xwin, xoff, yoff, width, height); +} + +static void pdp_xv_tile(t_pdp_xv* x, t_floatarg xtiles, t_floatarg ytiles, t_floatarg i, t_floatarg j) +{ + pdp_xwindow_tile(&x->x_xwin, xtiles, ytiles, i, j); +} + +static void pdp_xv_vga(t_pdp_xv *x) +{ + pdp_xv_resize(x, 640, 480); +} + +static void pdp_xv_free(t_pdp_xv *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + pdp_xv_destroy(x); + pdp_xvideo_free(&x->x_xvid); + pdp_xwindow_free(&x->x_xwin); +} + +t_class *pdp_xv_class; + + + +void *pdp_xv_new(void) +{ + t_pdp_xv *x = (t_pdp_xv *)pd_new(pdp_xv_class); + x->x_outlet = outlet_new(&x->x_obj, &s_anything); + + pdp_xwindow_init(&x->x_xwin); + pdp_xvideo_init(&x->x_xvid); + + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_display = gensym(":0"); + x->x_dpy = 0; + pdp_xv_autocreate(x,1); + + return (void *)x; +} + + + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_xv_setup(void) +{ + pdp_xv_class = class_new(gensym("pdp_xv"), (t_newmethod)pdp_xv_new, + (t_method)pdp_xv_free, sizeof(t_pdp_xv), 0, A_NULL); + + + class_addmethod(pdp_xv_class, (t_method)pdp_xv_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_create, gensym("open"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_create, gensym("create"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_autocreate, gensym("autocreate"), A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_destroy, gensym("destroy"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_destroy, gensym("close"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_move, gensym("move"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_move, gensym("pos"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_display, gensym("display"), A_SYMBOL, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_fullscreen, gensym("fullscreen"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_moveresize, gensym("posdim"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_tile, gensym("tile"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + /* some shortcuts for the lazy */ + class_addmethod(pdp_xv_class, (t_method)pdp_xv_vga, gensym("vga"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif + + -- cgit v1.2.1