Eurotech CTR-1474 driver

This driver if for the Eurotech CTR1474 JPEG2000 frame grabber. It was developed and tested under the Eurotech AlphaBox version of the Linux kernel (and will support vanilla kernels of course). The driver supports encoding, decoding and HIPI (both encoded data and raw images come from the host).

0. CTR-1474 Description

The Eurotech CTR-1474 is a frame grabber for embedded surveillance/monitoring applications.

The CTR1474 uses the Analog Devices ADV212 for encoding and decoding a JPEG2000 video stream, it is interfaced to the PCI bus via a PLX 9056 and the video front-end is the TWA2835. Both PAL and NTSC standards are supported (even in de-interlace mode).

ctr1474.jpg

The board has 8 input channels but the video front-end can work on 4 channels at a time because of TWA2835 architecture. So each of the actual channels (numbered from 0 to 3) has an independent mux that can select one of 2 inputs (A and B). Each of the 4 channels can be used to display playback video stream coming from ADV212 instead of the analog video-in.

The video front-end has 2 internal path: the monitor and the record path. It's possible to do a lot of video operations on each of these (like composing 4 channels in a picture, mirroring, filtering and much more). The monitor path is more specialized for end-user video output (has capabilities such as zooming, identifying area of motions, etc.), the record path for archiving purposes (DVR mode, automatic timed switching of channels, etc.). Each path can be dependently routed to one of 3 available outputs: analog video X, analog video Y and digital connection to the ADV212. The video front-end provides motion, blind, night and loss of video detection so such an event can generate an interrupt and polling is not required. It's also possible to overlay the video with a user defined bitmap.

The ADV212 is connected digitally to the video front-end. The connection can be switched in direction, so it's possible to record and to playback a video stream. The ADV212 is very flexible with a lot of options that can be set to achieve the right balance between quality and size of the obtained JPEG2000 frames. HIPI mode is also supported so the ADV212 can be used as a hardware accelerator for JPEG2000 encoding/decoding.

1. COMPILATION

After unpacking the source distribution you have to compile the kernel module and the user libraries:

cd linux
make KERNELDIR=[path to your kernel tree]
cd ..
cd linux_user
make
cd ..

2. USING THE DRIVER

If you don't use an udev based system just use the script load.sh to load the kernel module and create the devices. You will get 3 devices for each board installed:

/dev/ctr1474-0enc
/dev/ctr1474-0dec
/dev/ctr1474-0ctrl

The first device if for encoding a video stream (camera to computer), the second for decoding and the third for controlling additional features of the board (the TWA2835 video front-end and the GPIO lines).

If your system is udev based you can just load the module with insmod (or, better, with modprobe after you have installed it in the standard place with make modules_install)

The module (and load.sh) accepts the following parameters:

The maximum image that the ADV212 will generate can be set using the quality parameter of test_encode (see below). You can change these parameters at run-time by using ht ioctl IOCTL_ETHJPG2K_LINUX_SETBUF.

3. THE EXAMPLE PROGRAMS

3.1 test_encode

In the directory linux_user there is the test_encode program. It is a good programming example of the library (see below) but also a powerful utility for testing the board. It can be easily used from HLL like Python so, for example, a simple monitoring system can be written using it.

test_encode accepts command either via command line arguments or standard input. The syntax is:

linux_user/test_encode [encoding device] [control device] [commands ...]

Keep in mind that every argument entry is a command, so you have to use the ' character (on a bourne shell) to group them. You can obtain the following list of commands with:

test_encode /dev/ctr1474-0enc /dev/ctr1474-0ctrl help quit

Every command has an explanation of it use and its parameters. test_encode can be compiled with support for readline an the PNG graphic format. If you don't want or need this support (for example if the needed libraries are missing) edit the Makefile in the linux_user directory.

Please note that if you move test_encode outside the standard directory structure from the tarball you have to use the firmware command to tell the library where to find it.

The generated .jp2 file can be converted with the jasper library to some well known format:

jasper --input prova.jp2 --input-format jp2 --output prova.pnm --output-format pnm --force-srgb

3.1. test_decode

Syntax:

test_decode [decoding dev] [ctrl dev] [n sec] [jp2 images .....]

This program reads the given jp2 files and displays them in sequence for n seconds. The channel 0 of the TWA2835 is put in playback mode and it's displayed full-screen on the X and Y output paths.

3.2. test_loop

Syntax:

test_loop [enc dev] [dec dev] [firmware] [mode: 0=loop, 1=encode 2=decode] [pmode] [xtot] [ytot] [images ...]

This is an example of the HIPI mode, this means that both raw image and encoded data come from or go to the host. For this reason you have to specify both the encoding end the decoding device.

With the correct firmware and the mode parameter you can specify if you want to encode or decode the given images. pmode is the format of the raw data. Supported formats are:

0x14 4x8-bit Single Component
0x15 4x8-bit Packed YCbYCr
0x16 4x8-bit Packed YYCbCr
0x17 4x8-bit Packed CbCrCbCr
0x18 2x16-bit Packed Single Component
0x19 2x16-bit Packed YCb/YCr
0x1A 2x16-bit Packed YY/CbCr
0x1B 2x16-bit Packed CbCr

For more information refer to the ADV212 data sheet. xtot and ytot are respectively the total number of bytes per line and the number of lines. xtot is twice the horizontal resolution for color images, otherwise it's thew same value.

3.4. Example programs source code

The example programs are heavily commented so should be used as a guide while developing custom applications.

test_encode is built as command processor so the interesting code is in the actual routines called by the parser. See also typical usage of test_encode for the common usage patterns.

/*
  Copyright 2007 Eurotech S.P.A. (Luca Calligaris)
  Copyright 2007 Christian Pellegrin <chripell@gmail.com> EVOL S.R.L.

  This software is released under the GPL v2. Please see the file
  COPYING in the top-level directory.
*/

#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#include "version.h"

#ifdef HAS_READLINE

/* We use readline for interactive user input if available. */

#include <readline/readline.h>
#include <readline/history.h>

/* A static variable for holding the line. */
static char *line_read = (char *)NULL;

/* Read a string, and return a pointer to it.
   Returns NULL on EOF. */
char *
rl_gets (void)
{
  /* If the buffer has already been allocated,
     return the memory to the free pool. */
  if (line_read)
    {
      free (line_read);
      line_read = (char *)NULL;
    }

  /* Get a line from the user. */
  line_read = readline ("> ");

  /* If the line has any text in it,
     save it on the history. */
  if (line_read && *line_read)
    add_history (line_read);

  return (line_read);
}
#endif /* HAS_READLINE */

#ifdef HAS_PNG
#include <png.h>

/* Simple toutines for working with PNG files. Adapted from:
   http://zarb.org/~gc/html/libpng.html

   PNG is a good image format for our application because it supports
   alpha channel.
 */

#define abort_(x...) do { \
printf(x); \
printf("\n"); \
if (png) free(png); \
return -1; \
} while(0)


/* We encapsulate a PNG image in this data structure. */

struct png_s {
  int width, height;
  png_byte color_type;
  png_byte bit_depth;

  png_structp png_ptr;
  png_infop info_ptr;
  int number_of_passes;
  png_bytep * row_pointers;
};

/* Simple routine to read a PNG image. Returns 0 if succesful. */

int read_png_file(char* file_name, struct png_s **png_out)
{
        unsigned char header[8];        // 8 is the maximum size that can be checked
        struct png_s *png;
        FILE *fp;
        int y;

        png = malloc(sizeof(*png));
        assert(png);
        *png_out = NULL;

        /* open file and test for it being a png */
        fp = fopen(file_name, "rb");
        if (!fp)
          abort_("[read_png_file] File %s could not be opened for reading", file_name);
        fread(header, 1, 8, fp);
        if (png_sig_cmp(header, 0, 8))
          abort_("[read_png_file] File %s is not recognized as a PNG file", file_name);


        /* initialize stuff */
        png->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

        if (!png->png_ptr)
          abort_("[read_png_file] png_create_read_struct failed");

        png->info_ptr = png_create_info_struct(png->png_ptr);
        if (!png->info_ptr)
          abort_("[read_png_file] png_create_info_struct failed");

        if (setjmp(png_jmpbuf(png->png_ptr)))
          abort_("[read_png_file] Error during init_io");

        png_init_io(png->png_ptr, fp);
        png_set_sig_bytes(png->png_ptr, 8);

        png_read_info(png->png_ptr, png->info_ptr);

        png->width = png->info_ptr->width;
        png->height = png->info_ptr->height;
        png->color_type = png->info_ptr->color_type;
        png->bit_depth = png->info_ptr->bit_depth;

        if (png->info_ptr->color_type != PNG_COLOR_TYPE_RGBA)
          abort_("[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (is %d)",
                 png->info_ptr->color_type);

        png->number_of_passes = png_set_interlace_handling(png->png_ptr);
        png_read_update_info(png->png_ptr, png->info_ptr);

        /* read file */
        if (setjmp(png_jmpbuf(png->png_ptr)))
          abort_("[read_png_file] Error during read_image");

        png->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * png->height);
        for (y=0; y<png->height; y++)
                png->row_pointers[y] = (png_byte*) malloc(png->info_ptr->rowbytes);

        png_read_image(png->png_ptr, png->row_pointers);

        fclose(fp);
        *png_out = png;
        return 0;
}

int free_png(struct png_s *png) {
  int y;

  for (y=0; y<png->height; y++)
    free(png->row_pointers[y]);
  free(png->row_pointers);
  free(png);
  return 0;
}

/* this function is used to retrieve RGBA components of a pixel in a
   PNG image */

inline int get_png(struct png_s *png, int x, int y, int *r, int *g, int *b, int *a) {
  png_byte* row;
  png_byte* ptr;

  row = png->row_pointers[y];
  ptr = &(row[x*4]);
  *r = ptr[0];
  *g = ptr[1];
  *b = ptr[2];
  *a = ptr[3];
  return 0;
}

#endif /* HAS_PNG */

#include "libctr1474.h"

#define MAX_PATH 255

/* are we running? */
volatile int running = 0;
/* used by encoding thread to confirm it has stopped */
volatile int ack_finished = 0;

/* the encoder */
struct ctr1474_ch* enc = NULL;
/* the encoding thread */
pthread_t eth;

/* global configuration parameters */

/* NTSC mode? */
int is_ntsc = 0;

/* path for the firmware */
char firmware[MAX_PATH]="firmware/encode_2_11_0.sea";

ETHJPG2K_FIRMWARE_DATA fwdata = {};

/* JPEG2000 encoder parameters. See ADV212 data sheet for more
   information */
ETHJPG2K_FIRMWARE_PARAMS fwparams = {
  /* these are values suggested by Christine Bako*/
  .EncodeParams= {
    .VFORMAT = 1,
    .XFORMLEV = 5,
    .UNI = 3,
    .CBSIZE = 3,
    .RCTYPE = 2,
    .RCVAL = {0, 5, 0},
    .COD_STYLE = 2,
    .PICFG = 1,
  },
};
volatile int verbose = 1;               /* 0 quiet, 1 just errors, 2 verbose */
volatile int every = 0;                 /* if > 0 take a pic every every */
char every_prefix[MAX_PATH];            /* prefix to use */
volatile int shot = 0;                  /* take just a shot */
char shot_name[MAX_PATH];       /* name of the shot */

/* running parameters */
volatile int dropped = 0;
volatile int frames = 0;
volatile int resets = 0;

/* callback that processes every captured frame */
static int cb(struct ethjpg2k_linux_hdr *hdr, unsigned char * data, unsigned long len) {
  char b[255];
  FILE *f;
  static int private_prog = 0;

  /* print information if asked to be verbose */
  if (verbose == 2) {
    printf("%08d: %08lx %08ld %08lx %08ld -> %08ld ! %d T %d\n", frames, hdr->field, hdr->index, hdr->Info_u.info,
           hdr->size*4, hdr->start, hdr->overflow, hdr->ticket );
  }
  else if (verbose == 1) {
    if (hdr->index < private_prog) {
      printf("reset needed after %d frames\n", private_prog);
    }
    if (hdr->overflow > 0) {
      printf("dropped %d frames\n", hdr->overflow);
    }
  }

  if (every > 0 && (frames % every == 0)) {
    if (fwparams.EncodeParams.VFORMAT == 9 || fwparams.EncodeParams.VFORMAT == 8) {
      /* for the de-interlaced mode no need to add field information */
      sprintf(b, "%s%08d-%08ld.jp2", every_prefix, frames, hdr->index);
    }
    else {
      /* differentiate between odd and even field since this is important in playback */
      sprintf(b, "%s%08d-%08ld_%ld.jp2", every_prefix, frames, hdr->index, hdr->field & 0xf);
    }
    f = fopen(b, "w");
    assert(f);
    fwrite(data, len, 1, f);
    fclose(f);
  }

  /* write snapshot */
  if (shot) {
    f = fopen(shot_name, "w");
    assert(f);
    fwrite(data, len, 1, f);
    fclose(f);
    shot = 0;
  }

  /* check if encoder was reset: internal fram sequence is lower
     that the one we are bookeeping */
  if (hdr->index < private_prog) {
    resets++;
    private_prog = 0;
  }
  else
    private_prog ++;

  dropped += hdr->overflow;
  frames++;

  if (!running)
    return 1;
  return 0;
}

/* acquiring thread, we do encoding ina separate thread so the user
   interface is responsive */
void * ctr1474_thread(void * priv) {

  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  running = 1;
  ack_finished = 0;
  dropped = frames = resets = 0;
  do {
    /*assert(!simple_ctr1474_board_init(enc, is_ntsc, firmware, &fwdata, &fwparams));*/
    simple_ctr1474_encoder_run(enc, cb);
  } while(running);
  ack_finished = 1;

  return NULL;
}

/* callback type for command */
typedef int (*cmd_cb) (int argc, char *argv[]);

/* user comands: most of them directly map in library calls */

#define _TW TWA(enc)
#define _TI(X) strtol(argv[X], NULL, 0)

int do_every(int argc, char *argv[]) {
  every = strtol(argv[1], NULL, 0);
  strcpy(every_prefix, argv[2]);
  return 0;
}

int do_fw(int argc, char *argv[]) {
  strcpy(firmware, argv[1]);
  return 0;
}

int do_help(int argc, char *argv[]);

int do_shot(int argc, char *argv[]) {
  if (!running) {
    printf("not running\n");
    return 1;
  }

  strcpy(shot_name, argv[1]);
  shot = 1;
  return 0;
}

int do_start(int argc, char *argv[]) {
  int res;

  if (running) {
    printf("already running\n");
    return 1;
  }

  res = pthread_create(&eth, NULL, ctr1474_thread, NULL);
  assert(res == 0);

  return 0;
}

int do_stat(int argc, char *argv[]) {
  printf("r %d f %d d %d r %d\n", running, frames, dropped, resets);
  return 0;
}

int do_stop(int argc, char *argv[]) {
  int res, i;
  void *thread_result;

  if (!running) {
    printf("not running\n");
    return 1;
  }

  running = 0;

#define LOOP 10
  for(i=0; i<LOOP; i++) {
    if (ack_finished)
      break;
    usleep(10 * 1000);
  }
  if (i==LOOP) {                        /* brute force stop */
    simple_ctr1474_force_stop(enc);
    pthread_cancel(eth);
  }
#undef LOOP

  res = pthread_join(eth, &thread_result);

  return 0;
}

int do_verbose(int argc, char *argv[]) {
  verbose = strtol(argv[1], NULL, 0);
  return 0;
}

int do_quit(int argc, char *argv[]) {
  exit(0);
}

int do_wait(int argc, char *argv[]) {
  int target;

  if (!running) {
    printf("not running\n");
    return 1;
  }

  target = frames + strtol(argv[1], NULL, 0);
  while (frames < target)
    usleep(10 * 1000);
  return 0;
}

int do_quality(int argc, char *argv[]) {
  int val, i;

  if (running) {
    simple_ctr1474_encoder_quality(enc, strtol(argv[2], NULL, 0));
  }
  else {
    fwparams.EncodeParams.RCTYPE = strtol(argv[1], NULL, 0);
    val = strtol(argv[2], NULL, 0);
    for(i=2; i>=0; i--) {
      fwparams.EncodeParams.RCVAL[i] = val % 256;
      val /= 256;
    }
  }
  return 0;
}

int do_skip(int argc, char *argv[]) {
  fwparams.EncodeParams.STALLPAR = strtol(argv[1], NULL, 0);
  return 0;
}

int do_norm(int argc, char *argv[]) {
  if (!strcmp(argv[1], "pal")) {
    is_ntsc = 0;
    fwparams.EncodeParams.VFORMAT = 1;
  }
  else if (!strcmp(argv[1], "pali")) {
    is_ntsc = 0;
    /* these parameters are from rev19 example Analog Board */

    fwparams.EncodeParams.VFORMAT = 9;
#if 1
    /* without these pal de-interlace doesn't work */
    fwparams.EncodeParams.PREC = 1;
    fwparams.EncodeParams.XFORMLEV = 5;
    fwparams.EncodeParams.UNI = 3;

    fwparams.EncodeParams.CBSIZE = 3;
    fwparams.EncodeParams.WKERNEL_QUANT = 0;
    fwparams.EncodeParams.STALLPAR = 0;
    fwparams.EncodeParams.ATTRTYPE = 0;

    fwparams.EncodeParams.RCTYPE = 0;
    fwparams.EncodeParams.RCVAL[0] = 0;
    fwparams.EncodeParams.RCVAL[1] = 2;
    fwparams.EncodeParams.RCVAL[2] = 0;

    fwparams.EncodeParams.J2KPROG = 0;
    fwparams.EncodeParams.PICFG = 1;
    fwparams.EncodeParams.QFACT = 0;
    fwparams.EncodeParams.COD_STYLE = 2;
#endif
  }
  else if (!strcmp(argv[1], "ntsc")) {
    is_ntsc = 1;
    fwparams.EncodeParams.VFORMAT = 0;
  }
  else if (!strcmp(argv[1], "ntsci")) {
    is_ntsc = 1;
    fwparams.EncodeParams.VFORMAT = 8;
#if 1
    /* without these pal de-interlace doesn't work */
    fwparams.EncodeParams.PREC = 1;
    fwparams.EncodeParams.XFORMLEV = 5;
    fwparams.EncodeParams.UNI = 3;

    fwparams.EncodeParams.CBSIZE = 3;
    fwparams.EncodeParams.WKERNEL_QUANT = 0;
    fwparams.EncodeParams.STALLPAR = 0;
    fwparams.EncodeParams.ATTRTYPE = 0;

    fwparams.EncodeParams.RCTYPE = 00;
    fwparams.EncodeParams.RCVAL[0] = 00;
    fwparams.EncodeParams.RCVAL[1] = 02;
    fwparams.EncodeParams.RCVAL[2] = 00;

    fwparams.EncodeParams.J2KPROG = 0;
    fwparams.EncodeParams.PICFG = 0x01;
    fwparams.EncodeParams.QFACT = 0;
    fwparams.EncodeParams.COD_STYLE = 2;
#endif
  }
  else {
    printf("Invalid norm!\n");
    return -1;
  }
  return 0;
}

int do_mon1(int argc, char *argv[]) {
  return twa2835_mon_full(TWA(enc), strtol(argv[1], NULL, 0));
}

int do_mon4(int argc, char *argv[]) {
  return twa2835_mon_quad(TWA(enc));
}

int do_rec1(int argc, char *argv[]) {
  return twa2835_rec_full(TWA(enc), strtol(argv[1], NULL, 0));
}

int do_rec4(int argc, char *argv[]) {
  return twa2835_rec_quad(TWA(enc));
}

int do_brightness(int argc, char *argv[]) {
  return twa2835_brightness(TWA(enc),strtol(argv[1], NULL, 0),strtol(argv[2], NULL, 0));
}

int do_contrast(int argc, char *argv[]) {
  return twa2835_contrast(TWA(enc),strtol(argv[1], NULL, 0),strtol(argv[2], NULL, 0));
}

int do_hue(int argc, char *argv[]) {
  return twa2835_hue(TWA(enc),strtol(argv[1], NULL, 0),strtol(argv[2], NULL, 0));
}

int do_saturation(int argc, char *argv[]) {
  return twa2835_saturation(TWA(enc),strtol(argv[1], NULL, 0),strtol(argv[2], NULL, 0));
}

int do_mon_freeze(int argc, char *argv[]) {
  return twa2835_mon_freeze(TWA(enc));
}

int do_mon_continue(int argc, char *argv[]) {
  return twa2835_mon_continue(TWA(enc));
}

int do_init_board(int argc, char *argv[]) {
  if (running) {
    printf("Invalid while running\n");
    return 1;
  }

  assert(!simple_ctr1474_board_init(enc, is_ntsc, firmware, &fwdata, &fwparams));

  return 0;
}

int do_noinit_board(int argc, char *argv[]) {
  if (running) {
    printf("Invalid while running\n");
    return 1;
  }

  simple_ctr1474_board_noinit(enc, is_ntsc);

  return 0;
}

int do_msleep(int argc, char *argv[]) {
  usleep(1000* strtol(argv[1], NULL, 0));
  return 0;
}

int do_twa2835_read_reg(int argc, char *argv[]) {
  printf ("%02x\n", twa2835_read_reg(TWA(enc), strtol(argv[1], NULL, 0)));
  return 0;
}

int do_twa2835_write_reg(int argc, char *argv[]) {
  twa2835_write_reg(TWA(enc), strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0));
  return 0;
}

int do_adv212_read_reg(int argc, char *argv[]) {
  printf ("%02x\n", adv212_read_reg(enc, strtol(argv[1], NULL, 0)));
  return 0;
}

int do_adv212_write_reg(int argc, char *argv[]) {
  adv212_write_reg(enc, strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0));
  return 0;
}

int do_ch_status(int argc, char *argv[]) {
  int ch = strtol(argv[1], NULL, 0);
  struct twa2835_status s;

  twa2835_get_status(TWA(enc), ch, &s);
#define PRS(X) printf(#X " %08x\n", s.X)
  PRS(det_format);
  PRS(det_color);
  PRS(lock_color);
  PRS(lock_gain);
  PRS(lock_ofst);
  PRS(lock_hpll);
  PRS(det_nonstd);
  PRS(det_fld60);
  PRS(id);
  PRS(ver);
#undef PRS
  return 0;
}

int do_ch_ab(int argc, char *argv[]) {
  twa2835_ch_ab(TWA(enc), strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0));
  return 0;
}

int do_mon_bitmap(int argc, char *argv[]) {
  char *filename = argv[1];
  int x = strtol(argv[2], NULL, 0);
  int y = strtol(argv[3], NULL, 0);
  struct twa2835_pixel video[720*288];
  int i,j,r,g,b,a,ind,ret;
  struct png_s *png;
  static int bitmap_on = 0;

  if (!strcmp(filename, "none")) {
    /* clear the frame buffer */
    for(i=0; i<720; i++) {
      for(j=0; j<288; j++) {
        video[j*720 + i].off = 1;
      }
    }
    twa2835_mon_write_bitmap(TWA(enc), 0, 0, 720, 288, 0, video);
    /* and turn off the bitmap: in real applications this should be
       done in reverse order to avoid flickering */
    twa2835_mon_bitmap_enable(_TW, 0, 0);
    bitmap_on = 0;
    return 0;
  }

#ifdef HAS_PNG
  if (bitmap_on == 0) {
    /* clear the frame buffer first time an image is loaded */
    for(i=0; i<720; i++) {
      for(j=0; j<288; j++) {
        video[j*720 + i].off = 1;
      }
    }
    twa2835_mon_write_bitmap(TWA(enc), 0, 0, 720, 288, 0, video);

    /* setup a palette that is a quantized RGB cube */
    twa2835_mon_bitmap_enable(_TW, 1, 0);
    for(ind=0; ind<64; ind++) {
      r = ((ind >> 0) & 3) * 85;
      g = ((ind >> 2) & 3) * 85;
      b = ((ind >> 4) & 3) * 85;
      twa2835_set_palette(TWA(enc), ind, r, g, b);
    }
    bitmap_on = 1;
  }

  ret = read_png_file(filename, &png);
  if (ret == -1) {
    printf("Erro reading PNG image %s\n", filename);
    return -1;
  }

  /* round positions to nearest / 8 pixel (limitation of the
     TWA2835) */
  x &= ~7;
  y &= ~7;
  png->height &= ~7;
  png->width &= ~7;

  for(j=0; j < png->height; j++)
    for(i=0; i < png->width; i++) {
      struct twa2835_pixel *p = &video[j * png->width + i];

      get_png(png, i, j, &r, &g, &b, &a);
      p->index = (((r >> 6) & (3 << 0)) +
                  ((g >> 4) & (3 << 2)) +
                  ((b >> 2) & (3 << 4)));
      /* map alpha channel */
      if (a < 80) {
        /* don't display bitmap */
        p->mix = 0;
        p->off = 1;
      }
      else if (a < 160) {
        /* mix bitmap and image */
        p->mix = 1;
        p->off = 0;
      }
      else {
        /* display just the bitmap */
        p->mix = 0;
        p->off = 0;
      }
      p->blink = 0;
    }

  twa2835_mon_write_bitmap(TWA(enc), x, y, png->width, png->height, 0, video);
  free_png(png);
#else  /* !HAS_PNG */

  printf("PNG loading not supported, using default test bitmap\n");

  for(i=0; i<16; i++) {
    twa2835_set_palette(TWA(enc), i, i*16, i*16, i*16);
    twa2835_set_palette(TWA(enc), i+16, i*16, 0, 0);
    twa2835_set_palette(TWA(enc), i+32, 0, i*16, 0);
    twa2835_set_palette(TWA(enc), i+48, 0, 0, i*16);
  }

#define REGION(SX,EX,SY,EY,COL,MIX,BLINK) do { \
if ( i >= (SX) && i < (EX) && j >= (SY) && j < (EY) ) { \
video[j*720 + i].index = (COL); \
video[j*720 + i].mix = (MIX); \
video[j*720 + i].blink = (BLINK); \
video[j*720 + i].off = 0; \
} \
} while (0)
  for(i=0; i<720; i++) {
    for(j=0; j<288; j++) {
      REGION(100, 200, 50, 100, (i % 16), 0, 0);
      REGION(200, 300, 50, 100, (i % 16) + 16, 0, 0);
      REGION(300, 400, 50, 100, (i % 16) + 32, 0, 0);
      REGION(400, 500, 50, 100, (i % 16) + 48, 0, 0);
      REGION(100, 200, 100, 150, (i % 16), 1, 0);
      REGION(200, 300, 100, 150, (i % 16) + 16, 1, 0);
      REGION(300, 400, 100, 150, (i % 16) + 32, 1, 0);
      REGION(400, 500, 100, 150, (i % 16) + 48, 1, 0);
      REGION(100, 200, 150, 200, (i % 16), 0, 1);
      REGION(200, 300, 150, 200, (i % 16) + 16, 0, 1);
      REGION(300, 400, 150, 200, (i % 16) + 32, 0, 1);
      REGION(400, 500, 150, 200, (i % 16) + 48, 0, 1);
    }
  }
#undef REGION

  twa2835_mon_write_bitmap(TWA(enc), 0, 0, 720, 288, 0, video);
#endif  /* HAS_PNG */
  return 0;
}

int do_mon_show_motion(int argc, char *argv[]) {
  twa2835_mon_show_motion_box(_TW, _TI(1), _TI(2), _TI(3), _TI(4));
  return 0;
}

int do_mon_ch_attr(int argc, char *argv[]) {
  twa2835_mon_ch_attr(_TW, _TI(1), _TI(2), _TI(3), _TI(4), _TI(5));
  return 0;
}

int do_mon_blink_vl(int argc, char *argv[]) {
  twa2835_mon_blink_on_loss(_TW, _TI(1));
  return 0;
}

int do_mon_zoom(int argc, char *argv[]) {
  twa2835_mon_zoom(_TW, _TI(1), _TI(2), _TI(3));
  return 0;
}

int do_mon_switch(int argc, char *argv[]) {
  return twa2835_mon_switch_mode_ch(TWA(enc), strtol(argv[1], NULL, 0));
}

int do_mon_switch_trigger(int argc, char *argv[]) {
  return twa2835_mon_switch_trigger(TWA(enc), strtol(argv[1], NULL, 0));
}

int do_rec_ch_attr(int argc, char *argv[]) {
  twa2835_rec_ch_attr(_TW, _TI(1), _TI(2), _TI(3), _TI(4), _TI(5));
  return 0;
}

int do_mon_ch_pb(int argc, char *argv[]) {
  twa2835_mon_pb_enable(_TW, _TI(1), _TI(2));
  return 0;
}

int do_gpio_read(int argc, char *argv[]) {
  printf ("%08x\n", simple_ctr1474_gpio_read(enc));
  return 0;
}

int do_gpio_write(int argc, char *argv[]) {
  simple_ctr1474_gpio_write(enc, strtol(argv[1], NULL, 0));
  return 0;
}

int do_led(int argc, char *argv[]) {
  simple_ctr1474_led(enc, strtol(argv[1], NULL, 0));
  return 0;
}

int do_pb(int argc, char *argv[]) {
  simple_ctr1474_pb(enc, strtol(argv[1], NULL, 0));
  return 0;
}

int do_twa2835_reset(int argc, char *argv[]) {
  twa2835_reset(TWA(enc));
  return 0;
}

int do_rec_freeze(int argc, char *argv[]) {
  return twa2835_rec_freeze(TWA(enc));
}

int do_rec_continue(int argc, char *argv[]) {
  return twa2835_rec_continue(TWA(enc));
}

int do_rec_switch_cycle(int argc, char *argv[]) {
  int list[] = {0,1,2,3};

  return twa2835_rec_switch_mode(TWA(enc), strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0), 4, list);
}

int do_rec_switch_trigger(int argc, char *argv[]) {
  return twa2835_rec_switch_trigger(TWA(enc), strtol(argv[1], NULL, 0));
}

int do_rec_id_enable(int argc, char *argv[]) {
  int code = _TI(1);
#define BIT(X,I) ( (X & (1 << I)) > 0)
  return twa2835_rec_id_enable(_TW, BIT(code, 0), BIT(code, 1), BIT(code, 2), BIT(code, 3),
                               BIT(code, 4), BIT(code, 5), BIT(code, 6));
#undef BIT
}

int do_rec_mode(int argc, char *argv[]) {
  twa2835_rec_mode(_TW, _TI(1), _TI(2));
  return 0;
}

void do_motion_status_sub(struct twa2835_motion_substatus *s) {
  int i;

  printf("ch\tnight\tblint\tmotion\tloss\n");
  for(i=0; i<4; i++) {
    printf("%d\t%d\t%d\t%d\t%d\n", i, s->night[i], s->blind[i], s->motion[i], s->loss[i]);
  }
}

int do_motion_status(int argc, char *argv[]) {
  struct twa2835_motion_status s;

  memset(&s, 0, sizeof(s));
  twa2835_get_motion_status(_TW, &s);
  printf("VinA:\n");
  do_motion_status_sub(&s.vina);
  printf("VinB:\n");
  do_motion_status_sub(&s.vinb);
  return 0;
}

int do_motion_wait(int argc, char *argv[]) {
  int nv, md, bd, nd;
  int i;

  twa2835_enairq(_TW, _TI(2), _TI(3), _TI(4), _TI(5));
  twa2835_wait_motion(_TW, _TI(1), &nv, &md, &bd, &nd);
#define PX(X) do { \
for(i=0; i<8; i++) printf("\t%d", (X & (1<<i)) > 0); \
} while(0);
  for(i=0;i<8;i++) printf("\t%d", i);
  printf("\nnovid");
  PX(nv);
  printf("\nmd");
  PX(md);
  printf("\nbd");
  PX(bd);
  printf("\nnd");
  PX(nd);
  printf("\n");
#undef PX
  return 0;
}

int do_output_mode(int argc, char *argv[]) {
  twa2835_output_mode(_TW, _TI(1), _TI(2), _TI(3), _TI(4), _TI(5), _TI(6));
  return 0;
}

int do_audio_passtru(int argc, char *argv[]) {
#define BIT(X,I) ( (X & (1 << I)) > 0)
  int in_gain[] = {_TI(1),_TI(2),_TI(3),_TI(4),};
  int mute_mask = _TI(5);
  int mute[] = {BIT(mute_mask, 0),BIT(mute_mask, 1),BIT(mute_mask, 2),BIT(mute_mask, 3),};

  return twa2835_audio_passtru(_TW, in_gain, mute, _TI(6));
#undef BIT
}


/* command table */
struct cmd_table_s {
  char *name;
  cmd_cb f;
  int argc;
  char *help;
  int need_restart;
} cmd_table[] = {
  /* initialization */
  { "init_board", do_init_board, 0, "Initializes companion chips to the ADV202"},
  { "noinit_board", do_noinit_board, 0, "Simulates the initializaion of the board. Useful is already initialized."},
  { "norm", do_norm, 1, "[arg1] can be pal, ntsc, pali, ntsci", 1},
  { "output_mode", do_output_mode, 6, "[arg1] rec/mon tv_x [arg2] ovl tv_x [arg3] rec/mon tv_y [arg4] ovl tv_y\n[arg5] "
    "rec/mon enc [arg5] ovl enc"},
  /* adv 2x2 */
  { "every", do_every, 2, "takes a shot every [arg1] frames and saves to a file with prefix [arg2]" },
  { "fw", do_fw, 1, "pathname of firmware to use", 1},
  { "shot", do_shot, 1, "takes a shot of a picture to filename [arg1]" },
  { "stat", do_stat, 0, "display statistics: running status, frames got, dropped, resets" },
  { "start", do_start, 0, "starts encoding" },
  { "stop", do_stop, 0, "stops encoding" },
  { "verbose", do_verbose, 1, "if [arg1] is 0 be quiet, if 1 show just errors, if 2 be verbose" },
  { "wait", do_wait, 1, "wait [arg1] frames before continuing" },
  { "quality", do_quality, 2, "applies scheme [arg1] to quality of generated images.\n[arg1] = 0 no truncation\n"
    "[arg1] = 1 images are +/- 5% of dimensions specified by [arg2]\n"
    "[arg1] = 2 [arg2] is the quality factor (best = 0x100, very bad = 0x900)", 1},
  { "skip", do_skip, 1, "skips [arg1] frames between 2 acquired pictures in hardware. [arg1] is in the range 0-63."},
  /* video input */
  { "brightness", do_brightness, 2, "set brightness of channel [arg1] to [arg2]"},
  { "contrast", do_contrast, 2, "set contrast of channel [arg1] to [arg2]"},
  { "hue", do_hue, 2, "set hue of channel [arg1] to [arg2]"},
  { "saturation", do_saturation, 2, "set saturation of channel [arg1] to [arg2]"},
  { "ch_status", do_ch_status, 1, "statu of channel [arg1]"},
  { "ch_ab", do_ch_ab, 2, "selects for channel [arg1] vin a or b [arg2]"},
  /* monitor path */
  { "mon1", do_mon1, 1, "monitor path full channel [arg1]"},
  { "mon4", do_mon4, 0, "monitor path 4 channel"},
  { "mon_freeze", do_mon_freeze, 0, "freeze monitor path"},
  { "mon_continue", do_mon_continue, 0, "live mode monitor path"},
  { "mon_bitmap", do_mon_bitmap, 3, "loads PNG with name [arg1] at coords [arg2] [arg3]. none to clean display"},
  { "mon_show_motion", do_mon_show_motion, 4, "show motion boxes in box [arg1], [arg2] enables/disables\n"
    " [arg3] selects vina or vinb, [arg4] selects channel"},
  { "mon_ch_attr", do_mon_ch_attr, 5, "[arg1] channel [arg2] blank [arg3/4] h/vmirror [arg5] blink"},
  { "mon_ch_pb", do_mon_ch_pb, 2, "[arg1] channel [arg2] enable/disable pb"},
  { "mon_blink_vl", do_mon_blink_vl, 1, "blink on video loss [arg1] enable/disable"},
  { "mon_zoom", do_mon_zoom, 3, "zoom on/off ([arg1]), [arg2] x, [arg3] y"},
  { "mon_switch", do_mon_switch, 1, "[arg1] channel to put in switch mode"},
  { "mon_switch_trigger", do_mon_switch_trigger, 1, "[arg1] switch to this channel"},
  /* record path */
  { "rec1", do_rec1, 1, "record path to full channel [arg1] is 0-3"},
  { "rec4", do_rec4, 0, "record path to quad"},
  { "rec_freeze", do_rec_freeze, 0, "freeze rec path"},
  { "rec_continue", do_rec_continue, 0, "live mode rec path"},
  { "rec_id_enable", do_rec_id_enable, 1, "[arg1] is bitmap for what to enable/disable"},
  { "rec_ch_attr", do_rec_ch_attr, 5, "[arg1] channel [arg2] blank [arg3/4] h/vmirror [arg5] blink"},
  { "rec_mode", do_rec_mode, 2, "[arg1] frame mode on/off [arg2] DVR mode on/off"},
  { "rec_switch_cycle", do_rec_switch_cycle, 2, "[arg1] channel [arg2] period (0 for manual)"},
  { "rec_switch_trigger", do_rec_switch_trigger, 1, "[arg1] switch to this channel"},
  /* motion detection */
  { "motion_status", do_motion_status, 0, "get motion status"},
  { "motion_wait", do_motion_wait, 5, "wait for motion for at most [arg1] secs [arg2-5] enable novid, md, bd, nd"},
  /* audio */
  { "audio_passtru", do_audio_passtru, 6, "sets audio in pass-through mode [arg1-4] gain for ins (0-15), "
    "[arg5] is mute mask, [arg6] is out gain"},
  /* TWA generic */
  { "twa2835_read_reg", do_twa2835_read_reg, 1, "read register [arg1]"},
  { "twa2835_write_reg", do_twa2835_write_reg, 2, "write [arg2] to register [arg1]"},
  { "twa2835_reset", do_twa2835_reset, 0, "reset the chip"},
  /* ADV212 direct IO */
  { "adv212_read_reg", do_adv212_read_reg, 1, "read register [arg1]"},
  { "adv212_write_reg", do_adv212_write_reg, 2, "write [arg2] to register [arg1]"},
  /* gpio */
  { "gpio_read", do_gpio_read, 0, "read gpio"},
  { "gpio_write", do_gpio_write, 1, "write gpio"},
  { "led", do_led, 1, "write led"},
  { "pb", do_pb, 1, "enable/disable pb"},
  /* misc */
  { "msleep", do_msleep, 1, "sleeps [arg1] ms"},
  { "quit", do_quit, 0, "exits this application" },
  { "help", do_help, 0, "this help" },
  { NULL, },
};

/* help needs to have the table of command */
int do_help(int argc, char *argv[]) {
  struct cmd_table_s *c = cmd_table;
  int i;

  printf("Available commands: (*) means must restart encoding to take effects\n");
  while(c->name) {
    printf("-> %s", c->name);
    for(i=1; i < c->argc+1; i++) {
      printf(" [arg%d]", i);
    }
    printf("%s\n", c->need_restart ? " (*)" : "");
    printf("%s\n", c->help);
    c++;
  }
  return 0;
}

/* command parser */
int parse_cmd(char *c) {
#define MAX_ARGV 30
  char *argv[MAX_ARGV];
  int i, ret = 0;
  struct cmd_table_s *cmd = cmd_table;

  argv[0] = c;
  i = 1;
  while(*c) {
    if(*c != ' ')
      c++;
    else {
      *c = '\0';
      c++;
      while (*c && *c == ' ')
        c++;
      if (*c == '\0')
        break;
      argv[i] = c;
      i++;
    }
  }
  while(cmd->name) {
    if (!strcmp(cmd->name, argv[0])) {
      if (cmd->argc != i-1) {
        printf("Bad number of args, got %d expected %d\n", i, cmd->argc);
        ret = -1;
        break;
      }
      ret = cmd->f(i, argv);
      break;
    }
    cmd++;
  }
  if (!cmd->name) {
    printf("Unknown command\n");
    ret = -2;
  }
  if (ret == 0 || ret == -666)
    printf("OK\n");
  else
    printf("ERR %d\n", ret);
  return ret;
}



int main(int argc, char *argv[]) {
  char bline[201];
  char *line;
  int i, ret = 0, len;
  int use_readline = 0;

  printf("Eurotech CTR1474 utility, version " VERSION " compiled on " __DATE__ " " __TIME__ "\n");

  if (argc < 3) {
    printf("Usage: %s [enc dev] [ctrl dev] [commands...]\n"
           " Command are also read from stdinput.\n"
           " For a list of command: %s [dev] help quit\n",
           argv[0], argv[0]);
  }

  enc = simple_ctr1474(argv[1], argv[2]);
  assert(enc);

  simple_ctr1474_pb(enc, 0);

  for(i=3; i<argc; i++) {
    ret = parse_cmd(argv[i]);
    if (ret == -666)
      return 0;
  }

#ifdef HAS_READLINE
  if (isatty(0)) {
    use_readline = 1;
    rl_bind_key ('\t', rl_insert);
  }
#endif

#ifdef HAS_READLINE
  while ((line = (use_readline
                  ? rl_gets()
                  : fgets(bline, 200, stdin)))
         != NULL)
#else
  while ((line = fgets(bline, 200, stdin)) != NULL)
#endif
    {
      len = strlen(line);
      if(line[len-1] == '\n')
        line[len-1] = '\0';
      ret = parse_cmd(line);
      if (ret == -666)
        return 0;
    }

  return 0;
}

test_decode is really simple so it is a good example of how a custom application should be constructed.

/*
  Copyright 2007 Eurotech S.P.A. (Luca Calligaris)
  Copyright 2007 Christian Pellegrin <chripell@gmail.com> EVOL S.R.L.

  This software is released under the GPL v2. Please see the file
  COPYING in the top-level directory.
*/

#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <netinet/in.h>

#include "version.h"
#include "libctr1474.h"

#define MAX_PATH 255

int is_ntsc = 0;

void usage(void) {
  printf("usage: test_decode [dev] [cdev] [n sec] [jp2 images .....]\n");
  exit(1);
}

/* path for the firmware */
char firmware[MAX_PATH]="firmware/decode_2_14_1.sea";

ETHJPG2K_FIRMWARE_DATA fwdata = {};

/* JPEG2000 decoder parameters. See ADV212 data sheet for more
   information */
ETHJPG2K_FIRMWARE_PARAMS fwparams = {
  /* these are values suggested by Christine Bako*/
  .DecodeParams= {
    .VFORMAT = 0,               /* will be automatically filled */
    .UNI = 3,
    .COD_STYLE = 2,             /* 1 is JC2, 2 is JP2 */
    .PICFG = 0x11,
  },
};

/* array of images */
struct image_s {
  char *data;
  int size;
} *images;

/* for every image keep track if this is an odd or even field */
int *oddeven;

/* needed to calculate the actual FPS */
static int cur, n, c=0;
static time_t first = 0;
static time_t finish_epoch;

/* get the content of the image */
void get_image(unsigned char *data, unsigned long len, int *field) {
#if 0
  struct timeval tv;

  gettimeofday(&tv, NULL);
  fprintf(stderr, "%d %ld.%ld\n", c, tv.tv_sec, tv.tv_usec);
#endif
  *field = oddeven[cur];
  memcpy(data, images[cur].data, len);
  cur = (cur + 1) % n;
  c++;
}

/* get the image len */
int get_len (void) {
  time_t last;

  if (time(&last) > finish_epoch) {
    if (c>0)
      printf("%f fps\n", ((float) c) / (last-first) );
    return -1;
  }
  if (!first)
    first = last;
  return images[cur].size;
}


int main(int argc, char *argv[]) {
  int i, j;
  char *dev;
  int coe = 0;;
  struct ctr1474_ch *e;

  printf("Eurotech CTR1474 utility, version " VERSION " compiled on " __DATE__ " " __TIME__ "\n");

  if (argc < 4) {
    usage();
  }
  dev = argv[1];

  if (is_ntsc)
    fwparams.DecodeParams.VFORMAT = 0;
  else
    fwparams.DecodeParams.VFORMAT = 1;

  n = argc - 4;
  images = calloc(n, sizeof(struct image_s));
  assert(images);
  oddeven = calloc(n, sizeof(int));
  assert(oddeven);
  j = 0;
  for(i=0; i<n; i++) {
    char *fname = argv[i+4];
    char *suffix;
    FILE *f;
    int padded;

    /* load the images to memory. If the stream is interlaced be
       careful to alternate an even and an old field. */
    suffix = strstr(fname, ".jp2");
    if (suffix) {
      if (suffix[-2] == '_') {
        int cur;

        cur = strtol(&suffix[-1], NULL, 0);
        if (coe == cur) {
          coe = (coe + 1) & 1;
          oddeven[j] = cur;
        }
        else
          continue;             /* the for loop */
      }
      else {
        oddeven[j] = 0;
        if (is_ntsc)
          fwparams.DecodeParams.VFORMAT = 8;
        else
          fwparams.DecodeParams.VFORMAT = 9;
      }
    }
    else {
      fprintf(stderr, "Not a .jp2 file!\n");
      exit(1);
    }

    /* get the size and then load the image bits */
    f = fopen(fname, "r");
    if (!f) {
      fprintf(stderr, "Cannot open %s!\n", fname);
      exit(1);
    }
    fseek(f, 0, SEEK_END);
    images[j].size = ftell(f);
    assert(images[j].size);
    printf("image %d is %s, size %d\n", j, fname, images[j].size);
    rewind(f);
    padded = images[j].size;
    images[j].data = calloc(1, padded);
    assert(images[j].data);
    fread(images[j].data, images[j].size, 1, f);
    fclose(f);
    images[j].size = padded;
    j++;
  }
  n = j;
  assert(n);

  /* initialize the decoder */
  e = simple_ctr1474(argv[1], argv[2]);
  assert(e);
  simple_ctr1474_pb(e, 1);
  assert(!simple_ctr1474_board_init(e, is_ntsc, firmware, &fwdata, &fwparams));

  /* put the video output in one channel playback */
  twa2835_mon_full(TWA(e), 0);
  twa2835_mon_pb_enable(TWA(e), 0, 1);

  finish_epoch = time(NULL) + strtoul(argv[3], NULL, 0);
  cur = 0;
  simple_ctr1474_decoder_run(e, get_len, get_image, 1);

  /* restore monitoring mode */
  twa2835_mon_pb_enable(TWA(e), 0, 0);

  return 0;
}

test_loop doesn't focus on real-time usage since it focus on a JPEG2000 acceleration application. Images are presented in a batch and not acquired in real-time.

/*
  Copyright 2007 Eurotech S.P.A. (Luca Calligaris)
  Copyright 2007 Christian Pellegrin <chripell@gmail.com> EVOL S.R.L.

  This software is released under the GPL v2. Please see the file
  COPYING in the top-level directory.
*/

#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <netinet/in.h>

#include "version.h"
#include "libctr1474.h"

#define MAX_PATH 255

ETHJPG2K_FIRMWARE_DATA fwdata;
ETHJPG2K_FIRMWARE_PARAMS fwparams;

int main(int argc, char *argv[]) {
  struct ctr1474_ch *enc;
  struct ctr1474_ch *dec;
  int mode, pmode, xtot, ytot, i;
  char *fw;
  int *ins, *outs;
  char **in, **out;

  printf("Eurotech CTR1474 utility, version " VERSION " compiled on " __DATE__ " " __TIME__ "\n");

  if (argc < 9) {
    printf("Syntax:\n");
    printf("test_loop [enc dev] [dec dev] [ctr dev] [firmware] [mode: 0=loop, 1=encode 2=decode] [pmode] [xtot] [ytot] [images ...]\n");
    exit(1);
  }

  enc = simple_ctr1474(argv[1], argv[3]);
  assert(enc);
  dec = simple_ctr1474(argv[2], argv[3]);
  assert(dec);
  fw = argv[4];
  mode = strtol(argv[5], NULL, 0);
  pmode = strtol(argv[6], NULL, 0);
  xtot = strtol(argv[7], NULL, 0);
  ytot = strtol(argv[8], NULL, 0);
  argc -= 9;
  argv += 9;

  ins = calloc(argc, sizeof(int)); assert(ins+1); /* 1 image since hipi is one frame behind */
  outs = calloc(argc, sizeof(int)); assert(outs);
  in = calloc(argc, sizeof(char *)); assert(in+1);
  out = calloc(argc, sizeof(char *)); assert(out);

  for(i=0; i<argc; i++) {
    char *fname = argv[i];
    FILE *f;
    int padded;

    f = fopen(fname, "r");
    if (!f) {
      fprintf(stderr, "Cannot open %s!\n", fname);
      exit(1);
    }
    fseek(f, 0, SEEK_END);
    ins[i] = ftell(f);
    assert(ins[i]);
    printf("image %d size %d\n", i, ins[i]);
    rewind(f);
    padded = ins[i];
    in[i] = calloc(1, padded);
    assert(in[i]);
    fread(in[i], ins[i], 1, f);
    ins[i] = padded;
    fclose(f);
  }
  in[i] = in[i-1];
  ins[i] = ins[i-1];

  memset(&fwdata, 0, sizeof(fwdata));
  memset(&fwparams, 0, sizeof(fwparams));
  switch(mode) {
  case 0:
    fwdata.Type = FW_HIPI_LOOP;
    fwdata.FirmwareSizeInDwords = xtot * ytot;
    break;
  case 1:
    fwdata.Type = FW_HIPI_ENCODE;
    fwparams.HipiParams.XTOT = xtot;
    fwparams.HipiParams.YTOT = ytot;
    fwparams.HipiParams.PMODE = pmode;
    fwparams.HipiParams.sub.EncodeParams.VFORMAT = 4;
    fwparams.HipiParams.sub.EncodeParams.XFORMLEV = 5;
    fwparams.HipiParams.sub.EncodeParams.UNI = 3;
    fwparams.HipiParams.sub.EncodeParams.CBSIZE = 1;
    fwparams.HipiParams.sub.EncodeParams.RCTYPE = 2;
    fwparams.HipiParams.sub.EncodeParams.RCVAL[1] = 1;
    fwparams.HipiParams.sub.EncodeParams.COD_STYLE = 2;
    break;
  case 2:
    fwdata.Type = FW_HIPI_DECODE;
    fwparams.HipiParams.XTOT = xtot;
    fwparams.HipiParams.YTOT = ytot;
    fwparams.HipiParams.PMODE = pmode;
    fwparams.HipiParams.sub.DecodeParams.VFORMAT = 4;
    fwparams.HipiParams.sub.DecodeParams.UNI = 3;
    fwparams.HipiParams.sub.DecodeParams.COD_STYLE = 1;
    break;
  default:
    assert(0);
  }

  simple_ctr1474_hipi(enc, dec,
                    fw, &fwdata, &fwparams,
                    argc,
                    in, ins,
                    out, outs);

  for(i=0; i<argc; i++) {
    char fname[MAX_PATH];
    FILE *f;

    sprintf(fname, "%s.jp2kout", argv[i]);
    f = fopen(fname, "w");
    if (!f) {
      fprintf(stderr, "Cannot open %s for writting!\n", fname);
      exit(1);
    }
    fwrite(out[i], outs[i], 1, f);
    printf("out image %d size %d\n", i, outs[i]);
    fclose(f);
  }

  return 0;
}

3.5. Typical test_encode usage

Initializes the board in de-interlaced PAL. Enables monitor path on analog output X, record path on analog output Y and to the ADV212. No overlays.

twa2835_reset
norm pali
init_board
output_mode 0 0 1 0 1 0

Starts an acquisition of a JPEG2000 stream for 5 minutes. Each image should be around 50 kbytes and should have a prefix "test". Saves to disk one image out of 10.

quality 1 50000
skip 9
every 1 test
start
msleep 300000
stop

Puts monitor to channel 1 adjusting brightness and record path in 4 channel mode

mon1 1
brightness 1 100
rec4

Enables overlay on monitor path and loads a PNG at coordinates 64,64

output_mode 0 1 1 0 1 0
mon_bitmap /home/ovl.png 64 64

Shows full screen the first channel with motion boxes. Enables motion detection and blind detection on all channels. Waits until a motion event or a timeout of 100 seconds expires. After that motion detection status is displayed

mon1 0
mon_show_motion 0 1 0 0
motion_wait 100 0 255 255 0
motion_status

4. PROGRAMMING WITH THE LIBRARY

The easiest way to program the CTR1474 is using the support library written in C. It is fully described in the Doxygen documentation embedded with it.

For a practical example of house to use it and the needed Makefile please have a look in the example programs provided in the linux_user directory.

6. INTERACTING DIRECTLY WITH THE DRIVER

You may want to directly access the device driver without passing through the library interface. This chapter describes how to do this. You should have a look at the library code for an example of the concepts described here.

It's important to know that for every ADV212 in the system there are 3 device inodes, one for encoding, one for decoding and one for controlling the video front-end. For example if you have just one CTR1474 card in the system:

minor function

0 first ADV212 encoding
1 first ADV212 decoding
128 first TWA2835 video front-end

2 second ADV212 encoding
3 second ADV212 decoding
129 second TWA2835 video front-end

The encoding one always outputs data (usually JPEG2000 encoded but in HIPI decode mode they are raw image bits), the decoding always accepts data.

6.1 MEMORY MAPPED INTERFACE

For efficiency (avoiding data copies) the data buffer is shared between kernel and user level. You have to map it via a mmap function cal. The dimension of this buffer is set by the module load parameters. The arbitration to the buffer is done via read and write system calls.

6.2 USING READ AND WRITE

When accessing an encoding device (so receiving data from the driver) an user level program has to:

Info_u describes the attributes of the frame (see ADV212 data sheet for more information).

size is the size (in bytes) of the acquired image.

start is the location in the mmaped area of the image.

overflow is the number of image lost since last read (if we don't keep up with the pace of image acquired).

ticket is a number that has to be written to the device when finished.

The procedure for reading from a decoding device is quite similar:

6.3 IOCTLS

Here are listed the IOCTLs that are useful only if your directly interacting with the driver without using the helper library. For each code the device for which it's useful is specified.

IOCTL_ETHJPG2K_LINUX_INIT (enc/dec)

This IOCTL initializes the companion chips on the CTR1474. Should be called before any attempt to encode/decode a video stream.

IOCTL_ETHJPG2K_ENCDEC_START (enc/dec)

The encoder/decoder is started.

IOCTL_ETHJPG2K_ENCDEC_STOP (enc/dec)

The encoder/decoder is stopped.

IOCTL_ETHJPG2K_LINUX_GETBSIZE (enc/dec)

The size (in pages) of the buffer for the acquisition of images is returned.

IOCTL_ETHJPG2K_ENCDEC_LOAD_FIRMWARE (enc/dec)

The firmware is transferred to the driver via a struct ETHJPG2K_FIRMWARE_DATA.

IOCTL_ETHJPG2K_TW2835_WRITE_REG (ctrl)

Writes a register of the TWA2835. The argument is a pointer to an array of 2 ints: first is the register number, second is the value.

IOCTL_ETHJPG2K_TW2835_READ_REG (ctrl)

Reads a register from the TWA2835. The argument is a pointer to an array of 2 ints: first is the register number, second is the value.

IOCTL_ETHJPG2K_TW2835_RESET (ctrl)

Resets the TWA2835.

IOCTL_ETHJPG2K_TW2835_TOUT_SEC (ctrl)

Sets the timeout (in seconds) when waiting for an event from the video front-end.

IOCTL_ETHJPG2K_TW2835_BATCH (ctrl)

Does a batch of operations to reduce the number of context switches (it's useful when downloading bitmaps for example). The argument is a pointer to an an array of ints. The first one is the length of remaining elements. After it the actual operations.

IOCTL_ETHJPG2K_GPIO_WRITE (ctrl)

Writes to he output GPIO pins implemented on the CTR1474. The argument is a pointer to an int.Only the pins 0-7 are user available. Bits 8 and 9 control the 2 LEDs on the board. Bit 9 switches the data path between the video front-end and the ADV212 between encoding and decoding modes.

IOCTL_ETHJPG2K_GPIO_WRITE_READ (ctrl)

Reads the current output GPIO value. The argument is a pointer to an int. This IOCTL is used for masking only the bits that the user needs to change.

IOCTL_ETHJPG2K_GPIO_READ (ctrl)

Reads the value of the input GPIO to the given pointer to an int.

6.4 WAITING ON A TWA2835 EVENT

You can wait for an event from the video front-end by doing a read on the control device. You should program which events do trigger the TWA2835 interrupt (motion detection, blind detection, night detection, video loss). The read will return after an active interrupt happens or the timeout set with IOCTL_ETHJPG2K_TW2835_TOUT_SEC expires.

7. CONCLUSIONS

For technical support about the CTR1474 please contact Eurotech Technical Support (techsupp at eurotech.com).

If you have questions, patches, comments or suggestions that directly involve the software feel free to put me in CC: Christian Pellegrin, chripell at gmail.com.


Generated on Sat Dec 20 08:27:53 2008 for CTR1474 by  doxygen 1.5.1