Update of /cvsroot/pure-data/externals/pdp/modules/image_io In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6756/modules/image_io
Added Files: Makefile README pdp_glx.c pdp_qt.c pdp_sdl.c pdp_v4l.c pdp_xv.c Log Message: checking in pdp 0.12.4 from http://zwizwa.fartit.com/pd/pdp/pdp-0.12.4.tar.gz
--- NEW FILE: pdp_glx.c --- /* * 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. * */
// gl stuff #include <GL/gl.h> #include <GL/glx.h> #include <GL/glu.h> //#include <GL/glut.h>
// 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;
t_pdp_xdisplay *x_xdpy;
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) { if (x->x_initialized) 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_xdpy->dpy, x->x_glx_context); pdp_xwindow_free(x->x_xwin); pdp_xdisplay_free(x->x_xdpy); x->x_xwin = 0; x->x_xdpy = 0; x->x_initialized = false; }
}
static void pdp_glx_fullscreen(t_pdp_glx *x) { if (x->x_initialized) pdp_xwindow_fullscreen(x->x_xwin); }
static void pdp_glx_resize(t_pdp_glx* x, t_floatarg width, t_floatarg height) { if (x->x_initialized) pdp_xwindow_resize(x->x_xwin, width, height); }
static void pdp_glx_move(t_pdp_glx* x, t_floatarg width, t_floatarg height) { if (x->x_initialized) 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) { if (x->x_initialized) 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) { if (x->x_initialized) 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; i<width*height*depth; i++){dummydata[i] = random(); }
/* set window context current */ glXMakeCurrent(x->x_xdpy->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_xdpy = pdp_xdisplay_new(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 */ x->x_xwin = pdp_xwindow_new(); if (!(x->x_initialized = pdp_xwindow_create_on_display(x->x_xwin, x->x_xdpy))) goto exit_error;
/* create a glx visual */ if (!(x->x_vis_info = glXChooseVisual(x->x_xdpy->dpy, x->x_xdpy->screen, vis_attr))){ post("pdp_glx: can't create visual"); goto exit_error; } //post("visual: %x", x->x_vis_info);
/* create the rendering context */ if (!(x->x_glx_context = glXCreateContext(x->x_xdpy->dpy, x->x_vis_info, 0 /*share list*/, GL_TRUE))){ post("pdp_glx: can't create render context"); goto exit_error; } //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_error: if (x->x_xwin){ pdp_xwindow_free(x->x_xwin); x->x_xwin = 0; }
if (x->x_xdpy){ pdp_xdisplay_free(x->x_xdpy); x->x_xdpy = 0; }
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_xdpy->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_xdpy->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_xdpy->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_xdpy->dpy);
}
static void pdp_glx_bang_callback(t_pdp_glx *x) { /* receive events + send to outputs */ t_pdp_list *eventlist = pdp_xwindow_get_eventlist(x->x_xwin); t_pdp_atom *a;
for (a=eventlist->first; a; a=a->next){ //pdp_list_print(a->w.w_list); outlet_pdp_list(x->x_outlet, a->w.w_list); }
/* free list */ pdp_tree_free(eventlist);
/* 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); 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);
x->x_xwin = 0; x->x_xdpy = 0;
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_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
--- NEW FILE: pdp_v4l.c --- /* * 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_config.h" #include "pdp.h" #include "pdp_llconv.h" #include "pdp_imageproc.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
//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;
int x_format; // 0 means autodetect
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); }
/* set norm */ 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; } else { post("pdp_v4l: set norm to %u", x->x_norm); }
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; }
/* fallthrough macro for case statement */ #define TRYPALETTE(palette) \ x->x_v4l_palette = palette; \ 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?)"); \ if (x->x_format) break; /* only break if not autodetecting */ \ } \ else{ \ post("pdp_v4l: using " #palette); \ goto capture_ok; \ }
switch(x->x_format){ default: case 0: case 1: TRYPALETTE(VIDEO_PALETTE_YUV420P); case 2: TRYPALETTE(VIDEO_PALETTE_YUV422); case 3: TRYPALETTE(VIDEO_PALETTE_RGB24); case 4: TRYPALETTE(VIDEO_PALETTE_RGB32); }
// 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_norm(t_pdp_v4l *x, t_symbol *s) { unsigned int norm;
if (gensym("PAL") == s) norm = VIDEO_MODE_PAL; else if (gensym("NTSC") == s) norm = VIDEO_MODE_NTSC; else if (gensym("SECAM") == s) norm = VIDEO_MODE_SECAM; else norm = VIDEO_MODE_AUTO;
if (x->x_initialized){ pdp_v4l_close(x); x->x_norm = norm; pdp_v4l_open(x, x->x_device); } else x->x_norm = norm; }
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_RGB32: pdp_llconv(newimage, RIF_BGRA_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_format(t_pdp_v4l *x, t_symbol *s) { if (s == gensym("YUV420P")) x->x_format = 1; else if (s == gensym("YUV422")) x->x_format = 2; else if (s == gensym("RGB24")) x->x_format = 3; else if (s == gensym("RGB32")) x->x_format = 4; else if (s == gensym("auto")) x->x_format = 0; else { post("pdp_v4l: format %s unknown, using autodetect", s->s_name); x->x_format = 0; }
if (x->x_initialized){ pdp_v4l_close(x); pdp_v4l_open(x, x->x_device); } }
static void pdp_v4l_free(t_pdp_v4l *x) { pdp_v4l_close(x); }
t_class *pdp_v4l_class;
void *pdp_v4l_new(t_symbol *vdef, t_symbol *format) { 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; if (vdef != gensym("")){ x->x_device = vdef; } else{ x->x_device = gensym("/dev/video0"); }
if (format != gensym("")){ pdp_v4l_format(x, format); } else { x->x_format = 0; // default is autodetect }
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_norm = 0; // PAL 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_DEFSYMBOL, A_DEFSYMBOL, 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_norm, gensym("norm"), A_SYMBOL, 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); class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_format, gensym("captureformat"), A_SYMBOL, A_NULL);
}
#ifdef __cplusplus } #endif
--- NEW FILE: Makefile --- current: all_modules
include ../../Makefile.config
# build optional modules all_modules: $(PDP_OPTMOD)
clean: rm -f *~ rm -f *.o
--- NEW FILE: pdp_xv.c --- /* * Pure Data Packet module. Xvideo image packet output * 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. * */
// pdp stuff #include "pdp.h" #include "pdp_base.h"
// some x window glue code #include "pdp_xwindow.h" #include "pdp_xvideo.h"
#define PDP_XV_AUTOCREATE_RETRY 10
typedef struct pdp_xv_struct { t_object x_obj;
t_pdp_xdisplay *x_xdpy; 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) { if (x->x_xwin) pdp_xwindow_cursor(x->x_xwin, f); }
/* delete all submodules */ static void _pdp_xv_cleanup(t_pdp_xv *x) { if (x->x_xwin) pdp_xwindow_free(x->x_xwin); if (x->x_xvid) pdp_xvideo_free(x->x_xvid); if (x->x_xdpy) pdp_xdisplay_free(x->x_xdpy); x->x_xwin = 0; x->x_xvid = 0; x->x_xdpy = 0; x->x_initialized = 0; }
/* wait for thread to finish */ static void _pdp_xv_waitforthread(t_pdp_xv *x) { t_pdp_procqueue *q = pdp_queue_get_queue(); pdp_procqueue_finish(q, x->x_queue_id); // wait for thread to finish x->x_queue_id = -1; }
// this destroys the window and all the x connections static void pdp_xv_destroy(t_pdp_xv* x) { if (x->x_initialized){ _pdp_xv_waitforthread(x); // wait for thread _pdp_xv_cleanup(x); // delete all objects
pdp_packet_mark_unused(x->x_packet0); // delete packet x->x_packet0 = -1; x->x_initialized = 0;
} }
/* this creates a window (opens a dpy connection, creates xvideo and xwinow objects) */ static void pdp_xv_create(t_pdp_xv* x) { int i; if(x->x_initialized) return;
/* open a display */ if (!(x->x_xdpy = pdp_xdisplay_new(x->x_display->s_name))) goto exit;
/* open an xv port on the display */ x->x_xvid = pdp_xvideo_new(); if (!pdp_xvideo_open_on_display(x->x_xvid, x->x_xdpy)) goto exit;
/* create a window on the display */ x->x_xwin = pdp_xwindow_new(); if (!pdp_xwindow_create_on_display(x->x_xwin, x->x_xdpy)) goto exit;
/* done */ x->x_initialized = 1; return;
/* cleanup exits */ exit: post("pdp_xv: cant open display %s\n",x->x_display->s_name); _pdp_xv_cleanup(x);
}
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 + send to outputs */ t_pdp_list *eventlist = pdp_xwindow_get_eventlist(x->x_xwin); t_pdp_atom *a;
for (a=eventlist->first; a; a=a->next){ //pdp_list_print(a->w.w_list); outlet_pdp_list(x->x_outlet, a->w.w_list); }
/* free list */ pdp_tree_free(eventlist);
/* release the packet if there is one */ pdp_packet_mark_unused(x->x_packet0); x->x_packet0 = -1;
}
/* manually poll for events */ static void pdp_xv_poll(t_pdp_xv *x) { if (x->x_initialized) pdp_xv_bang_callback(x); }
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) { _pdp_xv_waitforthread(x); x->x_display = s;
/* only create if already active */ if (x->x_initialized){ pdp_xv_destroy(x); pdp_xv_create(x); } }
static void pdp_xv_movecursor(t_pdp_xv *x, float cx, float cy) { if (x->x_initialized){ cx *= x->x_xwin->winwidth; cy *= x->x_xwin->winheight; pdp_xwindow_warppointer(x->x_xwin, cx, cy); } }
static void pdp_xv_fullscreen(t_pdp_xv *x) { if (x->x_initialized) pdp_xwindow_fullscreen(x->x_xwin); }
static void pdp_xv_resize(t_pdp_xv* x, t_floatarg width, t_floatarg height) { if (x->x_initialized) pdp_xwindow_resize(x->x_xwin, width, height); }
static void pdp_xv_move(t_pdp_xv* x, t_floatarg width, t_floatarg height) { if (x->x_initialized) 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) { if (x->x_initialized) 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) { if (x->x_initialized) 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) { pdp_xv_destroy(x); }
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); x->x_xwin = 0; x->x_xvid = 0; x->x_xdpy = 0; x->x_packet0 = -1; x->x_queue_id = -1; x->x_display = gensym(":0"); x->x_xdpy = 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_movecursor, gensym("movecursor"), A_FLOAT, 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_poll, gensym("poll"), 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
--- NEW FILE: README --- This directory contains input/output modules for image packets. Most of this is platform dependent stuff, and will be conditionally compiled.
--- NEW FILE: pdp_sdl.c --- /* * Pure Data Packet module. * Copyright (c) 2003 by martin pi pi@attacksyour.net * 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. * */
/*
pdp sdl output
DONE:
TODO: * close window (event) * fullscreen chose resolution * event handling in different object (and look at mplayer for that!)
*/
#include <stdio.h> #include <SDL/SDL.h> #include "pdp.h" #include "pdp_llconv.h"
/* initial image dimensions */
#define WINWIDTH 640 #define WINHEIGHT 480 #define OVERLAYWIDTH 320 #define OVERLAYHEIGHT 240
typedef struct pdp_sdl_struct { t_object x_obj;
SDL_Surface *x_surface; SDL_Overlay *x_overlay; int x_surface_flags; int x_surface_width; int x_surface_height;
unsigned int x_overlay_width; unsigned int x_overlay_height;
t_outlet *x_outlet;
} t_pdp_sdl;
static t_pdp_sdl *sdl_singleton; // only one instance allowed
static void destroy_overlay(t_pdp_sdl *x) { if (x->x_overlay){ SDL_FreeYUVOverlay(x->x_overlay); x->x_overlay = 0; } }
static void create_overlay(t_pdp_sdl *x, int width, int height) { if (x->x_surface){ if (x->x_overlay = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, x->x_surface)){ x->x_overlay_width = width; x->x_overlay_height = height; return; } } pdp_post("SDL: can't create overlay."); }
static void check_overlay(t_pdp_sdl *x, unsigned int width, unsigned int height){ if (!x->x_overlay || (x->x_overlay_width != width) || (x->x_overlay_height != height)){ destroy_overlay(x); create_overlay(x, width, height); } }
static void create_surface(t_pdp_sdl *x, int width, int height, int flags) { flags |= SDL_HWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE; // add default flags // flags |= SDL_HWSURFACE|SDL_ANYFORMAT; // add default flags // flags |= SDL_SWSURFACE|SDL_ANYFORMAT; // add default flags
/* flags: SDL_HWSURFACE use hardware surface SDL_ANYFORMAT return current surface, even if it doesn't match SDL_OPENGL|SDL_DOUBLEBUF double buffer and opengl SDL_RLEACCEL rle accelleration for blitting SDL_FULLSCREEN fullscreen window */
//pdp_post("create_surface %d %d %d", width, height, flags);
/* check args */ if (width < 1) width = 1; if (height < 1) height = 1;
/* free old stuff */ if (x->x_overlay) destroy_overlay(x); /* form manpage: The framebuffer surface, or NULL if it fails. The surface returned is freed by SDL_Quit() and should nt be freed by the caller. */ if (x->x_surface) { /*SDL_FreeSurface(surface);*/ }
/* create new surface */ if (!(x->x_surface = SDL_SetVideoMode(width, height, 16, flags))){ pdp_post("SDL: Couldn't create a surface: %s", SDL_GetError()); return; }
/* setup surface */ SDL_WM_SetCaption("pdp", "pdp"); SDL_ShowCursor(0); /* set event mask to something conservative and add a word to ask for some types of events */ x->x_surface_width = width; x->x_surface_height = height; x->x_surface_flags = flags;
}
static void poll_events(t_pdp_sdl *x){
SDL_Event event; static t_symbol *keydown=0, *keyup, *quit, *motion; t_atom atom;
/* cache symbols */ if (!keydown){ keydown = gensym("keypress"); keyup = gensym("keyrelease"); quit = gensym("quit"); }
if (!x->x_surface) return;
/* poll events */ while(SDL_PollEvent(&event)){ switch(event.type){
case SDL_KEYDOWN: SETFLOAT(&atom, (float)event.key.keysym.scancode); outlet_anything(x->x_outlet, keydown, 1, &atom); break; case SDL_KEYUP: SETFLOAT(&atom, (float)event.key.keysym.scancode); outlet_anything(x->x_outlet, keyup, 1, &atom); break; case SDL_QUIT: outlet_symbol(x->x_outlet, quit); break;
case SDL_VIDEORESIZE: create_surface(x, event.resize.w, event.resize.h, x->x_surface_flags); break; } } }
static void fullscreen(t_pdp_sdl *x, t_floatarg f) { post("fullscreen not implemented"); }
static void resize(t_pdp_sdl *x, t_floatarg fw, t_floatarg fh) { create_surface(x, (int)fw, (int)fh, 0); }
static void input_0(t_pdp_sdl *x, t_symbol *s, t_floatarg f) {
int input_packet = (int)f; if (s == gensym("register_ro")){ int p = pdp_packet_convert_ro(input_packet, pdp_gensym("bitmap/yv12/*"));
/* poll anyway. */ //poll_events(x);
/* check packet */ if (-1 == p){ post("SDL: can't convert image to bitmap/yv12/*"); return; } else { t_bitmap *bitmap = pdp_packet_subheader(p); unsigned char *data = pdp_packet_data(p); int planesize = bitmap->width * bitmap->height; check_overlay(x, bitmap->width, bitmap->height); if (x->x_overlay){ SDL_Rect rect = {0, 0, x->x_surface_width, x->x_surface_height};
/* copy */ SDL_LockYUVOverlay(x->x_overlay); memcpy(x->x_overlay->pixels[0], data, planesize); data += planesize; memcpy(x->x_overlay->pixels[1], data, planesize >> 2); data += (planesize >> 2); memcpy(x->x_overlay->pixels[2], data, planesize >> 2); SDL_UnlockYUVOverlay(x->x_overlay);
/* display */ if (SDL_DisplayYUVOverlay(x->x_overlay, &rect)){ pdp_post("SDL: can't display overlay"); return; } } else { pdp_post("SDL: error creating overlay"); } pdp_packet_mark_unused(p); return; } } }
static void pdp_sdl_free(t_pdp_sdl *x) { destroy_overlay(x); sdl_singleton = 0; SDL_Quit(); }
t_class *pdp_sdl_class;
void *pdp_sdl_new(t_floatarg width, t_floatarg height) {
t_pdp_sdl *x; int w = (int)width; int h = (int)height;
if (sdl_singleton) { post("Only one sdl object allowed."); return 0; }
if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { pdp_post("Could not initialize SDL: %s", SDL_GetError()); return 0; } atexit(SDL_Quit);
x = (t_pdp_sdl *)pd_new(pdp_sdl_class); sdl_singleton = x;
x->x_surface = NULL; x->x_overlay = NULL;
x->x_outlet = outlet_new(&x->x_obj, &s_anything);
/* try to create a surface */ create_surface(x, w ? w : WINWIDTH, h ? h : WINHEIGHT, 0); if (!x->x_surface){ pdp_post("Can't create surface"); goto error_cleanup; }
/* try to create overlay */ check_overlay(x, 320, 240); if (!x->x_overlay){ pdp_post("Can't create overlay"); goto error_cleanup; }
return (void *)x;
error_cleanup: pdp_sdl_free(x); return (void *)0; }
#ifdef __cplusplus extern "C" { #endif
void pdp_sdl_setup(void) {
sdl_singleton = 0;
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)resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); class_addmethod(pdp_sdl_class, (t_method)poll_events, gensym("poll"), A_NULL); class_addmethod(pdp_sdl_class, (t_method)fullscreen, gensym("fullscreen"), A_FLOAT, A_NULL); class_addmethod(pdp_sdl_class, (t_method)input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
}
#ifdef __cplusplus } #endif
--- NEW FILE: pdp_qt.c --- /* * 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 <quicktime/lqt.h> #include <quicktime/colormodels.h>
#include "pdp.h" #include "pdp_llconv.h"
#define min(x,y) ((x<y)?(x):(y))
#define FREE(x) {if (x) {pdp_dealloc(x); x=0;} else post("free null pointer");}
/* debug macro */ //#define DEBUG_MSG_ENABLED
#ifdef DEBUG_MSG_ENABLED
#define DEBUG_MSG(EXP)\ fprintf (stderr, "mark start: [" #EXP "], on line %d\n", __LINE__);\ EXP \ fprintf (stderr, "mark end: [" #EXP "], on line %d\n", __LINE__);
#else #define DEBUG_MSG(EXP) EXP #endif
typedef struct pdp_qt_struct { t_object x_obj; t_float x_f;
float x_gain;
t_symbol *x_name; // this is our name int x_istilde; // 0==pdp_qt / 1==pdp_qt~ int x_syncaudio;
/* clock object */ t_clock *x_clock; int x_counter; int x_queue_id;
/* audio outlets */ t_outlet *x_outleft; t_outlet *x_outright;
/* message outlets */ t_outlet *x_outlet0; t_outlet *x_outlet1; t_outlet *x_outlet2;
/* pdp data */ int x_packet0;
/* toggles */ int x_loop; int x_autoplay;
/* qt data */ unsigned char ** x_qt_rows; // pointer array to rows / colour planes float ** x_qt_audiochans; // pointer array to audio channel buffers unsigned char * x_qt_frame; quicktime_t *x_qt; int x_qt_cmodel;
//t_pdp_qt_data *x_state_data;
/* audio data */ int x_chunk_current; float *x_chunk_buf; float *x_chunk[2][2]; int x_chunk_used[2]; // marks if chunk is used or not int x_chunk_size; int x_chunk_pos;
/* global state */ int x_initialized; int x_frame; int x_frame_thread; int x_process_in_thread;
/* audio info */ int x_audio_tracks; // ==0 means audio not available int x_audio_channels; long x_audio_samplerate; long x_audio_length;
/* video info */ int x_video_tracks; // ==0 means video not available float x_video_framerate; long x_video_length; unsigned int x_video_width; unsigned int x_video_height;
} t_pdp_qt;
static void pdp_qt_bang(t_pdp_qt *x);
static void pdp_qt_close(t_pdp_qt *x) { t_pdp_procqueue *q = pdp_queue_get_queue();
/* disable clock */ clock_unset(x->x_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