PONTON
AVIS
<< >>

button Technote 1

Introduction

The purpose of this document is to support software developers in their efforts to write software for AVIS hardware. It contains a description of the AVIS dataformat and some Pascal sourcecode to show how to analyse the data coming from the AVIS hardware. This document is not confidential, no non-disclosure aggreement (NDA) needs to be signed. Nevertheless the recipients were chosen according to their qualification, so please do not redistribute this document. Software developed with the help of this document can be distributed freely, compiled or in sources. In order to avoid duplicate developments contact us to coordinate efforts. Any further questions should be directed to

Ponton European Media Art Lab
Benjamin Heidersberger
Lister Strasse 17 - 30163 Hannover, Germany
tel +49-511-627032 - fax +49-511-621799
benjamin@ponton.de


AVIS Dataformat by Christian Wolff      Versiondate: 950328-1711

AVIS (Asynchronous Vertical Image Scan) is a method to transmit pictures, 
text, and sound for the purpose of picturetelephony on a dataconnection 
with 19200 bps (e.g. a 14000-bps modem-connection with compression). There 
is a hardware for converting a videosignal into a serial datastream. This 
hardware is pluged inbetween the computer and the modem. To switch the 
datastreams from computer or from AVIS to the modem and to decode the 
incoming picture-data you need a software. Until now this software is 
available for DOS, Mac, Amiga, and Windows.

AVIS-Data is transmitted in blocks of variable length. There are two 
different types of blocks, a pictureblock and a datablock.
Both are recognised by a sequence of sync-characters.

Pictureblock:
* At least 20 bytes $0F (picturesync)
    After receiving the first not-pic-sync you have to decide by the number 
    of received pic-syncs if the picture is PAL or NTSC: if it was less 
    than 34 bytes the picture is NTSC, else PAL. You should program a 
    hysteresis by shifting the border to 33 bytes, if the last picture 
    was PAL, or 36 bytes, if the last picture was NTSC.
* 1 to 6 bytes of trash, normally 2. For example, if you receive a NTSC 
    picture with 29 bytes of picsync then you have 3 bytes of trash to 
    fill the column up to 32 bytes (standard column length for NTSC). 
    This Standardoffset of 2 should be adjustable by the user during 
    receiving.
* 96 Columns with 32 (NTSC) oder 38 (PAL) bytes each.
    The columns are build from top down, each byte repesents 2 pixels. 
    The lower nibble of a byte is the upper pixel and the higer nibble 
    is the lower pixel. During decoding of a NTSC-picture every 5th pixel 
    is doubled to result into a correct aspect ratio.

Datablock (old version, must be recognised, but use new version for new software):
* At least 20 bytes, normally 38 bytes $F0 (Datasync)
* 1 byte $00 (Datasync-end)
* 1 byte Datatype (see new version)
* 2 bytes Datalength (High byte first, NOT intel-format)
* n bytes Data
* 2 bytes CRC-sum, high-byte first over all bytes including from datatype 
    to the last databyte

From including the datatype to the checksum some bytes are converted to 
2 bytes, to avoid detecting a false sync.
    $00 -> $00 $00
    $0F -> $00 $FF
    $F0 -> $FF $00
    $FF -> $FF $FF
As this conversion is done after building the header, these doublebytes 
are counted as one in the field "datalength".

 Datablock (new version):
* At least 20 bytes, normally 38 bytes $F0 (Datasync)
* 1 byte $FF (Datasync-end)
* 1 byte Headerlength (in this version 7)
* 1 byte datatype 
    $00 = Testdata, ignore
    $01 = ASCII-Text, end-of-line CR (ASCII 13)
    $02 = First sequence of compressed AVIS-Pictures
    $03 = Further sequence of compressed AVIS-Pictures
    $04 = First sequence of compressed pictures
    $05 = Further sequence of compressed pictures
    $06 = ADPCM-2/8-Bit-Audio, in the datapart first 2 byte
          samplerate in Hz (high-byte first), then datalength-2 byte
          sampledata
    $07 = Drawing-command
    $08 = Control-command
    $09 = Audio, datapart looks as this:
        1 byte compressiontype
            $00 = Audio 8 Bit uncompressed
            $01 = ADPCM-1/8-Bit-Audio (own, not used yet)
            $02 = ADPCM-2/8-Bit-Audio (soundblaster)
            $03 = ADPCM-3/8-Bit-Audio (own)
            $04 = ADPCM-4/12-Bit-Audio (dialogic, normally 8kHz)
            $05 = Code Excited Linear Prediction (CELP) 
            $06 = MACE 6 (Apple Computer)
            $07 = LPC 10 (Linear Prediction Coding, simple CELP)
            $09 = PAC (Ponton Audio Compression)
        1 byte Flags
            Bit 0: 0=first Block; 1=further Block
            Bit 1: 0=further blocks follow; 1=last Block
            Bit 2: 0=further Frame; 1=first Frame, ADPCM init
        2 byte Samplerate (High-byte first) in Hz
        n-4 byte sampledata
     
* 2 bytes datalength (High byte first, not intel-format)
* 2 bytes two's complement of datalength for security (High byte first)
* n bytes data
* 2 bytes CRC-sum, high-byte first over all bytes from including datatype 
    to the last databyte

  Pictureblocks as well as datablocks of the old type can be interrupted by 
a new sync-sequence, the interrupted block is thrown away. All data after 
the 96th column or after the CRC-sum are ignored (except syncs, of course)
  A pictureblock with less than 90 columns is probably defect and should 
not be displayed. Pictureblocks with less than 96 bytes ale filled up to 
the end with black pixels.
  After a datablock, before switching back to AVIS, you should send about 
30 bytes of "trailing trash" to avoid dataerrors in between important data.

 Controlcommands
ECHO ON        local echo of textlines send as datablocks (standard)
ECHO OFF       local echo off.
QVER           query version, send your version and profile.
VERSION        followed by computer/system and versionnumber,
announcement of the possibilities of the software.
    examples:
    VERSION PC1.7B11    PC/MS-DOS with version 1.7 beta 11
    VERSION MAC1.1.1    Macintosh 1.1.1
    VERSION WIN0.1B1    Windows 0.1 beta 1
    VERSION AMIGA1.0B   Amiga 1.0b
NOSOUND        Computer has no possibilities of sound, even if the
softwareversion has (e.g. no soundblaster installed)
NAME           Name of the User. e.g.:
    NAME Peter Graf

Structure of the AVIS-picture
  The videosignal of a connected camera is not digitized in rows, as it is 
produced by the camera, but in columns. The AVIS-digitizer produces a 
sequence of 96 by 76 (64 at NTSC) pixels with 16 grays. Each byte 
transmitted by the digitizer contains two pixels (one per nibble), so 
the size of a (PAL-)AVIS-picture is 96*38=3648 bytes. A completely 
received AVIS-picture contains 96 columns of 38 (PAL) or 32 (NTSC) bytes.
  Since each column is produced during one filed of the camera-videosignal,
the AVIS-pictures have a duration of 1.92 seconds 
for PAL or 1.6 seconds for NTSC.
                                                   columnpixelformat
picture:     96 columns                               PAL:    NTSC:
    0F lu . . . . . . . lu lu                           0L    0L
    0F lu . . . . . . . lu lu                           0H    0H
    .  .  . . . . . . . .  .                            1L    1L
    .  .  . . . . . . . .  .     32 bytes (NTSC)        1H    1H
    .  .  . . . . . . . .  .   o.38 bytes (PAL)         2L    2L
    0F lu . . . . . . . lu lu                           2H    2L
    0F lu . . . . . . . lu lu                           3L    2H
    XX lu . . . . . . . lu lu                           3H    3L
    XX lu lu  . . . . . lu lu                           4L    3H
                                                        4H    4L
lu=lower pixel in high nibble,                          5L    4H
   upper pixel in low nibble                            5H    4H

 The Digitizer-Interface
The camerainterface is connected between modem and computer. 
It contains 2 switches for the datastream:
  - Input :  Modem->Computer | Camera->Computer
  - Output:  Computer->Modem | Camera->Modem
The input-switch has to be switched manually at the interface. It is used 
to see your own transmitting picture instead of the incoming from your partner.
The output-switch is controlled by the computer with the DTR-line on the 
serial port (Pin 20). Only throught this the computer hat the possibility 
to insert datablocks into the AVIS-picture-stream. DTR active means, the 
computer sends to the modem.

Handshaking
If the modem uses CTS-handshaking, the computer should use this as well. 
If the AVIS-digitizer sends to the modem sets CTS to inactive ("I can not 
handle any more data") the computer should rise DTR to active state to stop 
AVIS from sending. After the modem is ready again (CTS active) you should 
wait for about 5 seconds before you reset DTR again, to let the modem empty 
its buffer. If the computer is transmitting anyhow, it should simply hold 
the transmitting during CTS is inactive.
RTS-Handshaking to the modem would be possible, but not useful, or, with 
the mac, dangerous, because inside the mac the RTS and the DTR signals are 
connected and can not be switched seperatly, so the modem would stop
sending data to the computer every time the digitizer sends to the modem.
 Sample Pascal sourcecode for AVIS data-stream-analysis
by Christian Wolff, %  , "

  const
    picSync =$0F;
    dataSync=$F0;
    validsync=20;          {SYNCS at least to be a valid sync}
    cutoff=10;             {max. number of missing columns at end of pict}
    xRez=95;               {95 columns + 1 sync-column}
    yRez=37;               {38 rows}
    xPix=xRez;             {pixelsizes of images}
    yPix=yRez*2+1;
    picsize=(xrez+1)*(yrez+1);  {imagesize in bytes}
    picpixel=picsize*2;    {imagesize in pixels}
    normVertOffs=2;        {standard: 2 bytes trash after sync}

  type
    tPict=array[0..xRez,0..yRez] of byte; {avis-picture}
    tData=record
      null:byte;
      typ:byte;
      length:word;
      data:array[0..16383] of byte;
    end;

  var
    recPic,actPic,tmpPic:^tPict;
    recData,actData,tmpData:^tData;
    picSyncCount,picDelay,dataSyncCount,dataCount,verticalOffset,
      picX,picY,ntscBorder,stretch:integer;
    itsaPic,itsaData,oldheader,doputpic,doshowdata:boolean;

  {function to analyse datastream for avis-pictures and avis-datablocks}
  {input: pointer to byte to return character, when not in graphics mode}
  {output: boolean, true when adat valid character}
  {global variables:
    graphmode            graphics-mode (incoming data plain data or avis-frames)
    actPic               pointer to return-picture
    recPic               pointer to recording structure
    tmpPic               temporary pointer for swapping
    doPutPic             set, if actPic is valid
    actData              pointer to return datablock
    recData              pointer to recording structure
    tmpData              temporary pointer for swapping
    doShowData           set, if actData is valid
    verticalOffset       number of trash-bytes after sync, normally normVertOffs
   global variables only used inside the function:
    itsAPic              receiving picture in progress
    itsAData             receiving datablock in progress
    picX,picY            receiving koordinates in picture
    stretch              variable for expanding NTSC-pixels do PAL-columns
    ntscBorder           hysterese-variable for detecting PAL/NTSC
    dataCount            receiving position in datablock
    oldheader            datablock in old or new format
    usecrc               set, if the partner uses CRC-sum
    crcwrong,crcright    counter for automatic switching of usecrc
    xx,yy                counter}

   function getserial(var adat:byte):boolean;
    var byt:byte;  {incoming byte}
      check:word;  {checksum}
    begin
      getserial:=false;                             {if not otherwise specified, no character returned}
      if head1<>tail1 then begin                    {if character in input-queue then}
        byt:=inbuf1^[tail1];                          {get character from input-queue to byt}
        tail1:=succ(tail1) and bufsize;               {adjust input-queue tail-pointer}
        if byt=picSync then                           {is it a picture-sync ($0F)?}
          inc(picSyncCount)                             {yes: count it!}
        else begin                                    {otherwise it's end of sync}
          if (picSyncCount>validsync)                    {did we had enough syncs for a valid picture...}
          and ((not itsadata) or oldheader) then begin   {... and not the new format and we receive a datablock curently}
            if not graphmode then                          {if we are not already in graphics-mode}
              inscreen.wipe;                                 {then erase screen}
            graphmode:=true;                               {set graphics-mode on}
            if itsapic then begin                          {do we interrupt a picture?}
              if picX>xRez-cutoff then begin                 {did we had enough columns?}
                for yy:=picY to yRez do recPic^[picX,yy]:=0;   {fill rest of this column with black}
                if picX0 then begin                       {waiting for the 2 or 3 garbage-bytes before the picture}
          dec(picDelay);                                 {decrement counter}
          if picDelay=0 then begin                       {start of picture?}
            picX:=0;                                       {reset koordinates}
            picY:=0;
            itsaData:=false;                               {it's not a datablock,}
            itsaPic:=true;                                 {it's a picture!}
          end;
        end;
        if byt=dataSync then                           {is it a dataSync ($F0)?}
          inc(dataSyncCount)                             {count it!}
        else begin                                     {otherwise it's end of datasync}
          if (dataSyncCount>validsync)                   {did we had enough datasyncs...
          and ((not itsadata) or oldheader) then begin   {... and not the new format and we receive a datablock curently}
             oldheader:=byt=$00;                            {is it a old-type datablock, interruptable by sync?}
            if not graphmode then                          {if we are not already in graphics-mode}
              inscreen.wipe;                                 {then erase screen}
            graphmode:=true;                               {set graphics-mode on}
            if itsapic then begin                          {do we interrupt a picture? return it!}
              if picX>xRez-cutoff then begin
                for yy:=picY to yRez do recPic^[picX,yy]:=0;
                if picX0) then begin      {old format, translate}
            if byt=$00 then begin                          {if this is $00}
              if evalin=-1 then evalin:=$00 else             {no last one: store}
              if evalin=$00 then begin evalin:=-1; end else  {last one=$00: return $00}
              if evalin=$FF then begin byt:=$F0; evalin:=-1; end;  {last one=$FF: return $F0}
            end else if byt=$FF then begin                 {if this is $FF}
              if evalin=-1 then evalin:=$FF else             {no last one: store}
              if evalin=$00 then begin byt:=$0F; evalin:=-1; end else  {last one=$00: return $0F}
              if evalin=$FF then begin evalin:=-1; end;      {last one=$FF: return $FF}
            end else evalin:=-1;                           {otherwise reset last one}
          end else evalin:=-1;
          if evalin<0 then begin                       {no last one stored?}
            if dataCount>0 then begin                    {drop first byte}
              if dataCount>=headerlen then begin           {header finished?}
                recData^.data[dataCount-headerlen]:=byt;   {put byte into structure}
                if dataCount-headerlenrecData^.length then begin {else}
                  itsaData:=false;                         {datablock finished}
                  check:=(recData^.data[recData^.length] shl 8) or recData^.data[recData^.length+1]; {read CRC}
                  if usecrc then begin                     {do we care for CRC?}
                    doShowData:=check=incheck;               {compare CRC}
                    if not doShowData then begin             {was it wrong?}
                      inc(crcwrong);                           {count it}
                      if crcwrong>10 then usecrc:=false;       {beyond treshold? disable CRC}
                    end else crcwrong:=0;                    {else reset counter}
                  end else begin                           {we do not care}
                    doshowData:=true;                        {return datablock}
                    if check=incheck then                    {maybe it was ok?}
                      inc(crcright)                            {count it}
                    else crcright:=0;                        {else reset counter}
                    if crcright>10 then usecrc:=true;        {beyond treshold?, enable CRC}
                  end;
                   tmpData:=actData; actData:=recData; recData:=tmpData; {swap pointers (doublebuffering)}
                end;
              end else begin                             {header not finished yet}
                incheck:=updcrc(byt,incheck);              {do crc}
                if oldheader then begin                    {fill old header}
                  if dataCount=1 then recData^.typ:=byt else
                  if dataCount=2 then lastbyte:=byt else
                  if dataCount=3 then recData^.length:=(lastbyte shl 8) or byt;
                end else begin                             {fill new header}
                  if dataCount=1 then headerlen:=byt else    {adjust headersize}
                  if dataCount=2 then recData^.typ:=byt else
                  if dataCount=3 then lastbyte:=byt else
                  if dataCount=4 then recData^.length:=(lastbyte shl 8) or byt else
                  if dataCount=5 then lastbyte:=byt else
                  if dataCount=6 then begin
                    if -recData^.length<>(lastbyte shl 8) or byt then begin
                      itsadata:=false;  {wrong length-check, trash block}
                    end;
                  end;
                end;
              end;
            end;
            inc(dataCount); {increment position}
          end;
        end else if itsaPic then begin {do we receive a picture otherwise?}
          if stretch<0 then              {no stretch, PAL}
            recPic^[picX,picY]:=byt        {put byte into picture}
          else begin                     {stretch, NTSC, double every 5th pixel}
            if stretch=2 then begin
              recPic^[picX,picY]:=(byt and $0F) or ((byt and $0F) shl 4);
              if picY=yRez then begin   {column finished?}
            if picX>=xRez then begin   {picture finished?}
              tmpPic:=actPic; actPic:=recPic; recPic:=tmpPic; {swap pointers}
              doputPic:=true;            {set flag for returning picture}
              itsaPic:=false;            {picture finished}
            end else begin             {not yet, next column}
              picY:=0;                   {to the top}
              if stretch>0 then stretch:=0;  {if stretch, reset stretch-state}
              inc(picX);                 {increment column}
            end;
          end else inc(picY);        {next pixel}
        end else if not graphmode then begin  {otherwise, and if no graphics mode}
          adat:=byt;                 {return byte directly}
          getserial:=true;           {e.g. the answer from the modem}
        end;
      end;
    end;



<< >>

Webmaster: service@ponton.de
Copyright © Ponton European Media Art Lab 1995