[PD-dev] pdp: ov519 cam (eyetoy) working now

Frank Barknecht fbar at footils.org
Sun Jan 9 17:31:03 CET 2005


Hi,

finally I got my webcam with ov519 chipset (Eyetoy and others) working
in PDP/Linux.  Attached is a patch against pdp_v4l.c which is very
intrusive. ;)

All jpeg loading should go into an external file and maybe be called
as part of pdp_llconv. The code obviously more is a proof of concept,
not production level, however I intend to use it as it is for now. 

pdp_v4l was extended by a method: "jpeg 0/1" which switches off and on
the jpeg decompression of frames. Default is off, so your normal cams
or tv cards will continue to work. 

I would love to see ov519 support in Gem and Gridflow, too.

Adding the decompression turned out to be not too hard (it was hard
for me, though!). The basic approach is to create a jpeg decompression
setup, that does not read from a file, but from a memory location.  In
my code, this is done using the source manager: "jpeg_source_mgr
source"

The frames delivered by the ov519 cam carry some bookeeping
information in their first two bytes, which need to be skipped when
reading in the jpeg data and looking for their header. 
(Done through: "source.next_input_byte=newimage + 2;" in the code)

After that it's simply calling the jpeg_read_scanlines stuff as it's
also already done in various places where Gem or PDP load JPEG files. 

My code still gives some distortions and decompression errors in the
resulting image, but basically works fine now. To build, you need to
add "-ljpeg" to the makefile. I don't know how to add this to the
autoconf stuff/build system of pdp.

Have fun.

Ciao
-- 
 Frank Barknecht                               _ ______footils.org__
-------------- next part --------------
21d20
< 
54a54,58
> /* jpeg start */
> #include <jpeglib.h>
> #include <setjmp.h>
> /* jpeg end */
> 
73a78,80
>   // decompress jpeg frames?
>   bool x_jpeg;
> 
119a127,337
> /* fbar: jpeg start */
> /* for jpeg library - handling buffered input for decompression */
> static void init_source (j_decompress_ptr cinfo) { return; }
> static boolean fill_input_buffer (j_decompress_ptr cinfo) { return 1; }
> static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
>   if (num_bytes <= 0) return;
>   cinfo->src->next_input_byte+=num_bytes;
>   cinfo->src->bytes_in_buffer-=num_bytes;
> }
> static boolean resync_to_restart (j_decompress_ptr cinfo, int desired) { return 1; }
> static void term_source (j_decompress_ptr cinfo) { return; }
> 
> struct my_error_mgr {
>     struct jpeg_error_mgr pub;	/* "public" fields */
>     jmp_buf setjmp_buffer;	/* for return to caller */
> };
> 
> typedef struct my_error_mgr * my_error_ptr;
> 
> static void my_error_exit (j_common_ptr cinfo)
> {
>     /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
>     my_error_ptr myerr = (my_error_ptr) cinfo->err;
> 
>     /* Always display the message. */
>     /* We could postpone this until after returning, if we chose. */
>     (*cinfo->err->output_message) (cinfo);
>     
>     /* Return control to the setjmp point */
>     longjmp(myerr->setjmp_buffer, 1);
> }
> 
> 
> static int load_packet_jpeg(t_pdp_v4l *x, unsigned char * newimage)
> {
>     unsigned char * mydata = NULL;
>     x->x_v4l_palette = VIDEO_PALETTE_RGB24;
>     // mydata = x->x_videobuf;
>     // mydata = newimage + 2024;
> 
>     int packet = -1;
>     /* This struct contains the JPEG decompression parameters and pointers to
>      * working space (which is allocated as needed by the JPEG library).
>      */
>     struct jpeg_decompress_struct cinfo;
> 
>     /* We use our private extension JPEG error handler.
>      * Note that this struct must live as long as the main JPEG parameter
>      * struct, to avoid dangling-pointer problems.
>      */
>     struct my_error_mgr jerr;
>     
>     /* More stuff */
>     /* FILE * infile;*/		/* source file */ 
>     JSAMPARRAY buffer;		/* Output row buffer */
>     int row_stride;		/* physical row width in output buffer */
> 
>     /* In this example we want to open the input file before doing anything else,
>      * so that the setjmp() error recovery below can assume the file is open.
>      * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
>      * requires it in order to read binary files.
>      */
> 
>     /* if ((infile = fopen(filename, "rb")) == NULL) { return packet; } */
> 
>     /* Step 1: allocate and initialize JPEG decompression object */
> 
>     /* We set up the normal JPEG error routines, then override error_exit. */
>     cinfo.err = jpeg_std_error(&jerr.pub);
>     jerr.pub.error_exit = my_error_exit;
> 
>     /* Establish the setjmp return context for my_error_exit to use. */
>     if (setjmp(jerr.setjmp_buffer)) 
>     {
>           error:
>         /* If we get here, the JPEG code has signaled an error.
>          * We need to clean up the JPEG object, close the input file, and return.
>          */
>         jpeg_destroy_decompress(&cinfo);
>         /* fclose(infile); */
>         // post("Error in setjmp");
>         packet = -1;
>         return packet;
>     }
> 
>     /* Now we can initialize the JPEG decompression object. */
>     jpeg_create_decompress(&cinfo);
> 
>     /* Step 2: specify data source (eg, a file) */
>     /* jpeg_stdio_src(&cinfo, infile); */
>     struct jpeg_source_mgr source;
>     cinfo.src=&source;
>     /* functions for managing jpeg lib's input buffer */
>     source.init_source=init_source;
>     source.fill_input_buffer=fill_input_buffer;
>     source.skip_input_data=skip_input_data;
>     source.resync_to_restart=resync_to_restart;
>     source.term_source=term_source;    
>     // The length / 8 of the JPEG is stored in the first 2 bytes of vmbuffer
>     // char  *framepointer;
>     unsigned short *vmpointer = NULL;
>     // vmpointer = (unsigned short *)( x->x_videobuf + x->x_vmbuf.offsets[x->x_last_frame]);
>     // framepointer= x->x_videobuf + x->x_vmbuf.offsets[x->x_last_frame] + 2;
>     // or:
>     // framepointer= newimage + 2;
>     vmpointer = (unsigned short *) newimage;
>     
>     source.next_input_byte=newimage + 2; //framepointer;
>     source.bytes_in_buffer=(ssize_t)((unsigned int)(vmpointer[0])<<3); // count
>     // intersting results here:
>     //mydata = newimage + source.bytes_in_buffer;
>     //post("source.bytes_in_buffer: %d", source.bytes_in_buffer);
>     /* Step 3: read file parameters with jpeg_read_header() */
>     (void) jpeg_read_header(&cinfo, TRUE);
>     /* We can ignore the return value from jpeg_read_header since
>      *   (a) suspension is not possible with the stdio data source, and
>      *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
>      * See libjpeg.doc for more info.
>      */
> 
> 
>     /* Step 4: set parameters for decompression */
> 
>     /* In this example, we don't need to change any of the defaults set by
>      * jpeg_read_header(), so we do nothing here.
>      */
> 
>     /* Step 5: Start decompressor */
> 
>     (void) jpeg_start_decompress(&cinfo);
>     /* We can ignore the return value since suspension is not possible
>      * with the stdio data source.
>      */
> 
>     /* We may need to do some setup of our own at this point before reading
>      * the data.  After jpeg_start_decompress() we have the correct scaled
>      * output image dimensions available, as well as the output colormap
>      * if we asked for color quantization.
>      * In this example, we need to make an output work buffer of the right size.
>      */ 
> 
>     /* map cinfo struct to pdp packet */
>     int width = cinfo.output_width;
>     int height = cinfo.output_height;
>     int depth = cinfo.output_components;
> /*    char *encoding;
>     switch(cinfo.out_color_space){
>     case JCS_RGB:  encoding = "rgb"; break;
>     default:
> 	goto error;
>     } */
>     // packet = pdp_factory_newpacket(pdp_symbolf("bitmap/%s/%d/%d", encoding, width, height));
>     // if (-1 == packet) goto error;
>     // data == rgbdata from showvideo.c
>     /* unsigned char *data = malloc(height * width * 3); */
>     
>     /* JSAMPLEs per row in output buffer */
>     row_stride = cinfo.output_width * cinfo.output_components;
>     //post("row_stride: %d",row_stride );
>     /* Make a one-row-high sample array that will go away when done with image */
>     buffer = (*cinfo.mem->alloc_sarray)
> 	((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
>     mydata = malloc((size_t)(row_stride * height ));
> 
>     /* Step 6: while (scan lines remain to be read) */
>     /*           jpeg_read_scanlines(...); */
> 
>     /* Here we use the library's state variable cinfo.output_scanline as the
>      * loop counter, so that we don't have to keep track ourselves.
>      */
>     //post("cinfo.output_height: %d", cinfo.output_height);
>     int tmp = 0;
>     while (cinfo.output_scanline < cinfo.output_height) 
>     {
>         /* jpeg_read_scanlines expects an array of pointers to scanlines.
>          * Here the array is only one element long, but you could ask for
>          * more than one scanline at a time if that's more convenient.
>          */
>         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
>         // (void) jpeg_read_scanlines(&cinfo, buffer, cinfo.output_height-cinfo.output_scanline);
>         /* Assume put_scanline_someplace wants a pointer and sample count. */
>         // TODO put_scanline_someplace(buffer[0], row_stride);
>         memcpy(mydata + tmp, buffer[0], row_stride);
>         tmp += row_stride;
>     }
>     /* Step 7: Finish decompression */
> 
>     (void) jpeg_finish_decompress(&cinfo);
>     /* We can ignore the return value since suspension is not possible
>      * with the stdio data source.
>      */
>     /* Step 8: Release JPEG decompression object */
>     
>     /* This is an important step since it will release a good deal of memory. */
>     jpeg_destroy_decompress(&cinfo);
>     /* After finish_decompress, we can close the input file.
>      * Here we postpone it until after no more JPEG errors are possible,
>      * so as to simplify the setjmp error logic above.  (Actually, I don't
>      * think that jpeg_destroy can do an error exit, but why assume anything...)
>      */
>     /* fclose(infile); */
>     memcpy(newimage, mydata, tmp);
>     free(mydata);
> 
>     /* At this point you may want to check to see whether any corrupt-data
>      * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
>      */
> 
>     /* And we're done! */
>     return packet;
> }
120a339
> /* fbar: jpeg end */
562a782,790
> static void pdp_v4l_jpeg(t_pdp_v4l *x, t_float f)
> {
>     if (f == 0.f)
>     {
>         x->x_jpeg = false;
>     }
>     else 
>         x->x_jpeg = true;
> }
600a829,830
>     // Call it here??
>     //load_packet_jpeg(x, newimage);
637a868
>     
639c870,873
< 
---
>     // Or call it here??
>     if (x->x_jpeg) load_packet_jpeg(x, newimage);
>     /* fbar: much better: */
>     /* newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_frame + 2]; */
755a990,991
>     // don't try to decompress jpeg frames:
>     x->x_jpeg = false;
826a1063
>     class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_jpeg, gensym("jpeg"), A_FLOAT, A_NULL);


More information about the Pd-dev mailing list