aboutsummaryrefslogtreecommitdiff
path: root/modules/pdp_xv.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pdp_xv.c')
-rw-r--r--modules/pdp_xv.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/modules/pdp_xv.c b/modules/pdp_xv.c
new file mode 100644
index 0000000..c859a40
--- /dev/null
+++ b/modules/pdp_xv.c
@@ -0,0 +1,534 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+
+pdp xvideo output
+
+*/
+
+
+// x stuff
+#include <X11/Xlib.h>
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+
+// image formats for communication with the X Server
+#define FOURCC_YV12 0x32315659 /* YV12 YUV420P */
+#define FOURCC_YUV2 0x32595559 /* YUV2 YUV422 */
+#define FOURCC_I420 0x30323449 /* I420 Intel Indeo 4 */
+
+// pdp stuff
+#include "pdp.h"
+#include "pdp_llconv.h"
+
+
+/* initial image dimensions */
+#define PDP_XV_W 320
+#define PDP_XV_H 240
+
+#define PDP_XV_AUTOCREATE_RETRY 10
+
+
+typedef struct pdp_xv_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ int x_packet0;
+ int x_queue_id;
+ t_symbol *x_display;
+
+ Display *x_dpy;
+ int x_screen;
+ Window x_win;
+ GC x_gc;
+
+ int x_xv_format;
+ int x_xv_port;
+
+ int x_winwidth;
+ int x_winheight;
+
+ XvImage *x_xvi;
+ unsigned char *x_data;
+ unsigned int x_width;
+ unsigned int x_height;
+ int x_last_encoding;
+
+ int x_initialized;
+ int x_autocreate;
+
+ float x_cursor;
+
+
+} t_pdp_xv;
+
+
+static int xv_init(t_pdp_xv *x)
+{
+ unsigned int ver, rel, req, ev, err, i, j;
+ unsigned int adaptors;
+ int formats;
+ XvAdaptorInfo *ai;
+
+ if (Success != XvQueryExtension(x->x_dpy,&ver,&rel,&req,&ev,&err)) return 0;
+
+ /* find + lock port */
+ if (Success != XvQueryAdaptors(x->x_dpy,DefaultRootWindow(x->x_dpy),&adaptors,&ai))
+ return 0;
+ for (i = 0; i < adaptors; i++) {
+ if ((ai[i].type & XvInputMask) && (ai[i].type & XvImageMask)) {
+ for (j=0; j < ai[i].num_ports; j++){
+ if (Success != XvGrabPort(x->x_dpy,ai[i].base_id+j,CurrentTime)) {
+ //fprintf(stderr,"INFO: Xvideo port %ld on adapter %d: is busy, skipping\n",ai[i].base_id+j, i);
+ }
+ else {
+ x->x_xv_port = ai[i].base_id + j;
+ goto breakout;
+ }
+ }
+ }
+ }
+
+
+ breakout:
+
+ XFree(ai);
+ if (0 == x->x_xv_port) return 0;
+ post("pdp_xv: grabbed port %d on adaptor %d", x->x_xv_port, i);
+ return 1;
+}
+
+
+void pdp_xv_create_xvimage(t_pdp_xv *x)
+{
+
+ long size = (x->x_width * x->x_height + (((x->x_width>>1)*(x->x_height>>1))<<1));
+ x->x_data = (unsigned char *)malloc(size);
+ x->x_xvi = XvCreateImage(x->x_dpy, x->x_xv_port, x->x_xv_format, (char *)x->x_data, x->x_width, x->x_height);
+ x->x_last_encoding = -1;
+ if (!(x->x_xvi) || (!x->x_data)) post ("ERROR CREATING XVIMAGE");
+
+}
+
+void pdp_xv_destroy_xvimage(t_pdp_xv *x)
+{
+ if(x->x_data) free(x->x_data);
+ if (x->x_xvi) XFree(x->x_xvi);
+ x->x_xvi = 0;
+ x->x_data = 0;
+}
+
+
+static void pdp_xv_cursor(t_pdp_xv *x, t_floatarg f)
+{
+ if (!x->x_initialized) return;
+
+ if (f == 0) {
+ static char data[1] = {0};
+
+ Cursor cursor;
+ Pixmap blank;
+ XColor dummy;
+
+ blank = XCreateBitmapFromData(x->x_dpy, x->x_win, data, 1, 1);
+ cursor = XCreatePixmapCursor(x->x_dpy, blank, blank, &dummy,
+ &dummy, 0, 0);
+ XFreePixmap(x->x_dpy, blank);
+ XDefineCursor(x->x_dpy, x->x_win,cursor);
+ }
+ else
+ XUndefineCursor(x->x_dpy, x->x_win);
+
+ x->x_cursor = f;
+}
+
+
+static void pdp_xv_destroy(t_pdp_xv* x)
+{
+ XEvent e;
+
+ if (x->x_initialized){
+ XFreeGC(x->x_dpy, x->x_gc);
+ XDestroyWindow(x->x_dpy, x->x_win);
+ while(XPending(x->x_dpy)) XNextEvent(x->x_dpy, &e);
+ XvUngrabPort(x->x_dpy, x->x_xv_port, CurrentTime);
+ pdp_xv_destroy_xvimage(x);
+ XCloseDisplay(x->x_dpy);
+ x->x_initialized = false;
+
+ }
+
+}
+
+static void pdp_xv_resize(t_pdp_xv* x, t_floatarg width, t_floatarg height)
+{
+ if (x->x_initialized && (width>0) && (height>0)){
+ XResizeWindow(x->x_dpy, x->x_win, (unsigned int)width, (unsigned int)height);
+ XFlush(x->x_dpy);
+ }
+}
+
+static void pdp_xv_create(t_pdp_xv* x)
+{
+ unsigned int *uintdata = (unsigned int *)(x->x_data);
+ XEvent e;
+ unsigned int i;
+
+ if( x->x_initialized ){
+ //post("pdp_xv: window already created");
+ return;
+ }
+
+ 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;
+ }
+
+ /* init xvideo */
+ if (xv_init(x)){
+
+ pdp_xv_create_xvimage(x);
+
+ }
+
+ else {
+ /* clean up mess */
+ post("pdp_xv: ERROR: no xv port available. closing display.");
+ XCloseDisplay(x->x_dpy);
+ x->x_initialized = false;
+ return;
+ }
+
+
+ /* create a window */
+ x->x_screen = DefaultScreen(x->x_dpy);
+
+
+ x->x_win = XCreateSimpleWindow(
+ x->x_dpy,
+ RootWindow(x->x_dpy, x->x_screen), 0, 0, x->x_winwidth, x->x_winheight, 0,
+ BlackPixel(x->x_dpy, x->x_screen),
+ BlackPixel(x->x_dpy, x->x_screen));
+
+ if(!(x->x_win)){
+ /* clean up mess */
+ post("pdp_xv: could not create window\n");
+ post("pdp_xv: unlocking xv port");
+ XvUngrabPort(x->x_dpy, x->x_xv_port, CurrentTime);
+ post("pdp_xv: freeing xvimage");
+ pdp_xv_destroy_xvimage(x);
+ post("pdp_xv: closing display");
+ XCloseDisplay(x->x_dpy);
+ x->x_initialized = false;
+ return;
+ }
+
+ XSelectInput(x->x_dpy, x->x_win, StructureNotifyMask);
+
+ XMapWindow(x->x_dpy, x->x_win);
+
+ x->x_gc = XCreateGC(x->x_dpy, x->x_win, 0, 0);
+
+ for(;;){
+ XNextEvent(x->x_dpy, &e);
+ if (e.type == MapNotify) break;
+ }
+
+
+ x->x_initialized = true;
+ pdp_xv_cursor(x, x->x_cursor);
+
+}
+
+void pdp_xv_copy_xvimage(t_pdp_xv *x, t_image *image, short int* data)
+{
+ 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};
+ long size = (width * height + (((width>>1)*(height>>1))<<1));
+ int nbpixels = width * height;
+
+ /* 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_xv_destroy_xvimage(x);
+ pdp_xv_create_xvimage(x);
+ }
+
+
+ /* data holds a 16bit version of a the xvimage, so it needs to be converted */
+
+
+
+ if (data) {
+ /* convert 16bit -> 8bit */
+ if(PDP_IMAGE_YV12 == encoding){
+ pdp_llconv(data,RIF_YVU__P411_S16, x->x_data, RIF_YVU__P411_U8, x->x_width, x->x_height);
+ x->x_last_encoding = PDP_IMAGE_YV12;
+ }
+ if(PDP_IMAGE_GREY == encoding){
+ pdp_llconv(data,RIF_GREY______S16, x->x_data, RIF_GREY______U8, x->x_width, x->x_height);
+ if (PDP_IMAGE_GREY != x->x_last_encoding) {
+ post("pdp_xv: switching to greyscale");
+ /* clear u&v planes if necessary */
+ uintdata = (unsigned int *)&x->x_data[nbpixels];
+ for(i=0; i < nbpixels>>3; i++) uintdata[i]=0x80808080;
+ }
+ x->x_last_encoding = PDP_IMAGE_GREY;
+
+ }
+ }
+ else bzero(x->x_data, size);
+
+
+
+}
+
+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_process(t_pdp_xv *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_xv_try_autocreate(x)) return;
+ }
+
+ /* check data packet */
+ if (!(header)) {
+ post("pdp_xv: invalid packet header");
+ return;
+ }
+ if (PDP_IMAGE != header->type) {
+ post("pdp_xv: packet is not a PDP_IMAGE");
+ return;
+ }
+ if ((PDP_IMAGE_YV12 != header->info.image.encoding)
+ && (PDP_IMAGE_GREY != header->info.image.encoding)) {
+ post("pdp_xv: packet is not a PDP_IMAGE_YV12/GREY");
+ return;
+ }
+
+ /* copy the packet to the xvimage */
+ pdp_xv_copy_xvimage(x, &header->info.image, (short int *)data);
+
+
+ /* display the new image */
+ pdp_xv_bang(x);
+
+
+}
+
+
+static void pdp_xv_random(t_pdp_xv *x)
+{
+ unsigned int i;
+ long *intdata = (long *)(x->x_data);
+ for(i=0; i<x->x_width*x->x_height/4; i++) intdata[i]=random();
+}
+
+/* redisplays image */
+static void pdp_xv_bang_thread(t_pdp_xv *x)
+{
+
+ XEvent e;
+ unsigned int i;
+
+ //while (XEventsQueued(x->x_dpy, QueuedAlready)){
+ while (XPending(x->x_dpy)){
+ //post("pdp_xv: waiting for event");
+ XNextEvent(x->x_dpy, &e);
+ //post("pdp_xv: XEvent %d", e.type);
+ if(e.type == ConfigureNotify){
+ XConfigureEvent *ce = (XConfigureEvent *)&e;
+ x->x_winwidth = ce->width;
+ x->x_winheight = ce->height;
+
+ }
+ //post("pdp_xv: received event");
+
+ }
+
+ XvPutImage(x->x_dpy,x->x_xv_port,x->x_win,x->x_gc,x->x_xvi,
+ 0,0,x->x_width,x->x_height, 0,0,x->x_winwidth,x->x_winheight);
+ XFlush(x->x_dpy);
+}
+
+static void pdp_xv_bang_callback(t_pdp_xv *x)
+{
+ /* 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)
+{
+
+
+ /* if previous queued method returned
+ schedule a new one, else ignore */
+ if (-1 == x->x_queue_id) {
+ pdp_queue_add(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_copy_ro_or_drop(&x->x_packet0, (int)f);
+ if (s == gensym("process")) pdp_xv_process(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_queue_finish(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_free(t_pdp_xv *x)
+{
+ pdp_queue_finish(x->x_queue_id);
+
+ pdp_xv_destroy(x);
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+t_class *pdp_xv_class;
+
+
+
+void *pdp_xv_new(void)
+{
+ t_pdp_xv *x = (t_pdp_xv *)pd_new(pdp_xv_class);
+
+
+ x->x_packet0 = -1;
+ x->x_queue_id = -1;
+ x->x_display = gensym(":0");
+
+
+ x->x_dpy = 0;
+ x->x_screen = -1;
+
+ x->x_xv_format = FOURCC_YV12;
+ x->x_xv_port = 0;
+
+ x->x_winwidth = PDP_XV_W;
+ x->x_winheight = PDP_XV_H;
+
+ x->x_width = PDP_XV_W;
+ x->x_height = PDP_XV_H;
+
+ x->x_data = 0;
+ x->x_xvi = 0;
+
+ x->x_initialized = 0;
+ pdp_xv_autocreate(x,1);
+ x->x_last_encoding = -1;
+
+ x->x_cursor = 0;
+
+ 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_random, gensym("random"), 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_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);
+
+}
+
+#ifdef __cplusplus
+}
+#endif