Frame Grabber pro Linux - navod na udelani _LONG_ (prominte, ale zdalo se mi to zajimave)
Jaroslav Lukesh
lukesh na seznam.cz
Středa Srpen 18 08:21:58 CEST 1999
> potrebuji nutne nejaky Frame Grabber pro Linux.
>
> Nejradeji bez TV tuneru a podobnejch
> zbytecnosti a nejlepe s nekolika videovstupy.
>
> Z grabberu podporovanych jaderm 2.2.x se mi
> nejvice zamlouva 'Matrix Vision delta'
> (sympaticka mala karta se ctyrmi vstupy).
>
> Bohuzel ani pro tento ani pro zadny jiny grabber
> (krome AVerMedia , ktere obsahuji TV tunery,
> surround a podobny zbytecnosti) se mi nepodarilo
> nikde najit ceskeho prodejce.
>
> Netusite nekdo kde to sehnat ???
To netusim, ale ja bych si ho udelal sam. Napriklad cernobily s naklady pod
100Kc i s konektory vypada asi takto. Docela bomba, ze?
(sorry, nemam URL, jen tento text kdysi stazeny z nejake BBS)
Happy bastling!
Jaroslav Lukesh, K-net
--------------------------------------------------
http://www.k-net.cz
Multimedia, Networking, Communications
Windows terminals, NC
computer hardware and software
--------------------------------------------------
Dirt Cheap Frame Grabber V2.03
by Michael Day
as of 8 Feb 1992
public domain
(DB25P)
DO0 ---330ohm-------------*--->|--*-->|---* IC1 = LM339
pin 2 Vr | red 1N914 | quad
(2.4v) | led | comparator
DO1 ---330ohm-------------* |
pin 3 | | * = connection
330ohm |
1N914 | | ) = no connect
DO6 ---|<---*---1Kohm-----* Vc (2.21v) | (jump over)
pin 8 | |
1N914 | | Vr = ref voltage
DO7 ---|<---*---510ohm----*------* | 2.4 volts
pin 9 | | |
(trimmer) 5Kohm<--* | Vc = 00 = 1.65v
| | 01 = 1.80v
DO2 ----------*-----------)------| |------* 10 = 1.95v
pin 4 | | .1uf | 11 = 2.21v
3| | |
DC3 |/ |6 | | Vz = 00 = 0.74v
(slct) 1 / -|--------* Vz (1.0v) | 01 = 0.81v
DS6 --------|IC1a| | | 10 = 0.88v
(ack) \ +|----* | | 11 = 1.00v
pins 10,17 \ |7 | 360ohm |
| | | Vy = 00 = 0.45v
DC2 / |4 | | | 01 = 0.54v
(init) 2 / -|----)---* Vy (0.66v) | 10 = 0.59v
DS5 --------|IC1b| | | | 11 = 0.66v
(pe) \ +|----* | |
pins 12,16 \ |5 | 220ohm | Vx = 00 = 0.34v
| | | 01 = 0.37v
DC1 / |10 | | | 10 = 0.40v
(afd) 13 / -|----)---* Vx (0.45v) | 11 = 0.45v
DS4 --------|IC1c| | | |
(slct) \ +|----* | | Vs = 00 = 0.086v
pins 13,14 \ |11 | 360ohm | 01 = 0.093v
| | | 10 = 0.101v
DC0 / |8 | | | 11 = 0.115v
(stb) 14 / -|----)---* Vs (0.12v) | Assumes Vz set
DS7 --------|IC1d| | | | for 1.0v using
(busy) \ +|----* | | 5Kohm trimmer
pins 11,1 12|\ |9 | 120ohm | with DO6, DO7
| | | | set high.
Gnd ----------*-------)---*---------------*------*--------*
pins | | | | |
18,19,20,21, | 100Kohm | 75ohm |
22,23,24,25 | | .1uf | | |
*---*-------| |-----)------*------o )
2N2222 | | RCA
*---------C E | phono
| B | jack
pin 5 | | 1N914 |
DO3 ----*--4.7Kohm--*-------->|-----------*
To make it all work, simply plug the connector into a printer
port on the PC, and run the frame grabber program. When running
the program, the LED should turn on. That indicates that the
circuit has enough power to operate. If the LED does not come on,
or it is very dim, there may not be enough power, and you can't
use that printer port with the circuit. Try another port or a
different computer. Note that you may see some minor flickering
on the led when changing the DO6 and DO7 data output lines. This
is normal and indicates that the circuit is working properly.
Changing the DO6 and DO7 data output lines causes the current
flow through the voltage reference ladder to change. This means
that more or less current will be available to the LED, causing
it to change in brightness. The LED should not go out completely
nor become very dim. That would indicate that there is not enough
power available to drive the voltage reference or a problem in
your program.
You can operate in the simple mode by outputting a 0FFH to the
data output port which sets all the output lines high. This
provides power to the comparators, voltage reference, and clamp
transistor. It also disables the gray scale control lines.
To shift the gray scale reference voltage level, send either a
03FH (gray ref 0), 07FH (gray ref 1), 0BFH (gray ref 2), or 0FFH
(gray ref 3) to the data output port.
The resulting detected output from the port is as follows:
76543210
1000xxxx sync
0000xxxx level 0 (black)
0001xxxx level 1 (dark gray)
0011xxxx level 2 (gray)
0111xxxx level 3 (white)
Note that bits 0-3 are unknown and should not be relied on as
being valid.
Video Data Translation:
Once I get the data, I convert it to an intermediate form to be
stored in the interpretation array. This array keeps the gray
level values collected on the previous frames. The display
routine will later interpret the values stored in the
interpretation array into an absolute gray scale level for each
display pixel.
The video data is converted by searching for the horizontal sync
pulse. Once found, we search for the end of the sync pulse.
Following the end of the sync pulse is the actual video data.
Each collected byte in the scan line is read and converted to the
intermediate gray scale level. There are only four levels
involved; 00, 01, 10, and 11. Note that since sync is not video
data, it is not included in the gray scale level. Even if it were
included, it would be a 00 value. Once converted, the byte is
then shifted into it's proper frame position in the interpretation
array byte and stored in the array.
This is repeated until either the full width of the
interpretation array is filled, or a new sync pulse is
encountered. Should a sync pulse be encountered, the rest of the
interpretation array values are filled with 00. Should the full
width of the interpretation array be encountered, then the rest
of the scan line is discarded until an new sync pulse is
encountered. This sequence is continued until the entire frame
has been converted.
Each byte is read from the interpretation array
and translated to an absolute gray level using the translation
array located in the first 256 bytes of the interpretation array.
There are 12 levels of gray available, plus black.
Source code:
{Dirt Cheap Frame Grabber - Version 2.03}
{as of 8 Feb 1992 - by Michael Day}
{public domain}
program DCFG2;
uses crt;
const maxframe = 30000;
maxintrp = 30000;
type frametype = array[0..maxframe] of byte;
frameptr = ^frametype;
intrptype = array[0..maxintrp] of byte;
intrpptr = ^intrptype;
string8 = string[8];
FrameObj = object
fary : array[0..3] of frameptr;
iary : intrpptr;
dary : intrpptr;
inport : word; {frame port data input address (video data)}
outport : word; {frame port data output address (control)}
frameport : word; {printer port number to use for frame grabber}
grabsize : word; {size of data to grab from port}
framenum : byte; {frame sequence number}
IntrpWidth : word; {width of the intrp array (scan width) }
IntrpSize : word; {size of the intrp array (width*lines) }
Filenum:word; {next file frame number to use}
DiskFrameSize:word;
FrameCount:word;
constructor Init;
destructor Done;
procedure SetFramePort(what:string8);
function GrabFrame(inprt,size:word; Fptr:frameptr):boolean;
function GrabOne:boolean;
procedure F2IConvert(Fnum:byte; GSize,IWidth,ISize:word;
Iptr:IntrpPtr; Fptr:FramePtr);
procedure IntrpDisplay(fnum,IWidth,ISize:word; Iptr:IntrpPtr);
procedure MakeDiskArray(fnum,IWidth,ISize:word;
Iptr:IntrpPtr; Dptr:IntrpPtr);
end;
var Frame : FrameObj;
prnarray : array[0..3] of word absolute $40:$08;
screen : array[0..65520] of byte absolute $A000:0;
crtmode : byte absolute $40:$49;
oldmode : byte;
i:word;
ib:byte;
cx:char;
mf:file;
filenum:word;
showframe : boolean;
fns:string;
MovieEnabled:boolean;
{-----------------------------------------------------------}
{ gray level interpretation chart }
{ }
{ frame data }
{gray F3 F2 F1 F0 F3 = frame 3, F2 = frame 2 }
{level: 76 54 32 10 F1 = frame 1, F0 = frame 0 }
{ 12: 11 xx xx xx each group of two bits }
{ 11: <11 11 xx xx represent the video level }
{ 10: <11 <11 11 xx for the frame indicated }
{ 9: <11 <11 <11 11 }
{ 8: 10 <11 <11 <11 xx = any bit pattern }
{ 7: <10 10 <11 <11 <11 = less than 11; (10, 01, 00) }
{ 6: <10 <10 10 <11 <10 = less than 10; (01 or 00) }
{ 5: <10 <10 <10 10 11, 10, 01, or 00 = the indicated }
{ 4: 01 <10 <10 <10 absolute bit pattern }
{ 3: 00 01 <10 <10 }
{ 2: 00 00 01 <10 the gray level for the specified }
{ 1: 00 00 00 01 bit pattern is shown at the left }
{ 0: 00 00 00 00 }
{-----------------------------------------------------------}
{this array is used to translate from the interpretation }
{array data into a gray level for display on the screen }
const IntrpXlat : array[0..255] of byte = (
0,1,5,9,2,2,5,9, 6,6,6,9,10,10,10,10,
3,3,5,9,3,3,5,9, 6,6,6,9,10,10,10,10,
7,7,7,9,7,7,7,9, 7,7,7,9,10,10,10,10,
11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,
4,4,5,9,4,4,5,9, 6,6,6,9,10,10,10,10,
4,4,5,9,4,4,5,9, 6,6,6,9,10,10,10,10,
7,7,7,9,7,7,7,9, 7,7,7,9,10,10,10,10,
11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,
8,8,8,9,8,8,8,9, 8,8,8,9,10,10,10,10,
8,8,8,9,8,8,8,9, 8,8,8,9,10,10,10,10,
8,8,8,9,8,8,8,9, 8,8,8,9,10,10,10,10,
11,11,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,
12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12);
{-----------------------------------------------------------}
{grab a chunk of video from inprt size bytes in length into fary}
function FrameObj.GrabFrame(inprt,size:word; Fptr:frameptr):boolean;
assembler;
asm
mov bx,17000 {timeout if we go over 50ms without sync}
mov dx,[inprt]
les di,[Fptr] {now collect a frame}
mov cx,0
@vsloop1:
mov ah,8 {[vsyncslice]} {if we are in a vert sync, get out of it
first}
@vsloop2:
dec bx
jz @vdone
in al,dx
shl al,1
jc @vsloop1
dec ah
jnz @vsloop2
@vsloop3:
mov ah,8 {[vsyncslice]} {find the start of a vert sync}
@vsloop4:
dec bx
jz @vdone
in al,dx
shl al,1
jnc @vsloop3
dec ah
jnz @vsloop4
cld
mov cx,[size] {start collecting data}
rep
db 6ch
@vdone:
xor al,al {return error code}
or bh,bl {one = all ok}
jz @vexit {zero = no sync}
inc al
@vexit:
end;
Constructor FrameObj.Init;
var i:byte;
begin
for i := 0 to 3 do
begin
new(fary[i]);
fillchar(fary[i]^,sizeof(fary[i]^),0);
end;
new(iary);
fillchar(iary^,sizeof(iary^),0);
move(IntrpXlat,iary^,256);
new(dary);
fillchar(dary^,sizeof(dary^),0);
move(IntrpXlat,dary^,256);
end;
Destructor FrameObj.Done;
var i:byte;
begin
for i := 0 to 3 do
begin
dispose(fary[i]);
end;
dispose(iary);
dispose(dary);
end;
procedure FrameObj.SetFramePort(what:string8);
begin
frameport := 0;
if length(what) > 0 then
begin
case what[1] of
'2': frameport := 1;
'3': frameport := 2;
'4': frameport := 3;
end;
end;
outport := prnarray[frameport]; {- $378} {get port base addr}
inport := outport+1; {- $379}
port[outport+2] := $04; {- $37A} {init output control lines}
port[outport] := $ff; {init data lines}
grabsize := 20000; {default grab size}
framenum := 0;
IntrpWidth := 70;
IntrpSize := IntrpWidth*(262-12);
framecount := 0;
end;
function FrameObj.GrabOne:boolean;
var Fptr : framePtr;
begin
inc(framenum);
framenum := framenum and 3;
port[frame.outport] := (framenum shl 6) or $3f;
Fptr := fary[framenum];
asm CLI; end;
GrabOne := GrabFrame(inport,grabsize,Fptr);
asm STI; end;
port[frame.outport] := $3f;
end;
{==================================================================}
{note: this assumes that the frame grab array has been preformated}
{with starting with a valid scan line at the top of the screen}
procedure FrameObj.F2Iconvert(Fnum:byte; GSize,IWidth,ISize:word;
Iptr:IntrpPtr; Fptr:FramePtr);
var Bottom:word;
begin
asm
cld
mov cl,ss:[Fnum] {get gray scale frame number}
and cl,03H
add cl,cl {*2 = shifter count}
mov ch,0FCH {create intrp data mask}
rol ch,cl
mov dx,ss:[GSize] {get size of grabbed data to convert}
inc dx
les di,ss:[Iptr] {get intrp array pointer}
add di,256 {first 256 bytes has xlat array}
mov ax,di
add ax,ss:[ISize] {compute intrp bottom address offset}
mov ss:[Bottom],ax {and save it}
mov bx,ss:[IWidth] {put intrp right edge offset into bx}
push ds {save current data segment}
lds si,ss:[Fptr] {get video frame pointer to DS:SI}
add si,500 {ignore the vertical sync}
{data conversion loop starts here}
@loop1:
dec dx {did we run out of data?}
jz @done
lodsb {get a frame scan byte}
shl al,1 {if it is a sync, try again}
jc @loop1
@loop2:
dec dx {did we run out of data?}
jz @done
lodsb {get a frame scan byte}
shl al,1 {if it is a sync, we are}
jc @loop4 {done with the scan line}
{convert scan input data to intrp level reference}
xor ah,ah {init to zero level}
shl al,1 {if highest level on}
adc ah,0 {add one to level count}
shl al,1 {if next high level on}
adc ah,0 {add one to level count}
shl al,1 {if lowest level on}
adc ah,0 {add one to level count}
shl ah,cl {adjust result to position}
mov al,es:[di] {get current intrp value}
and al,ch {strip old intrp value}
or al,ah {insert new intrp value}
mov es:[di],al {save the new intrp value}
inc di
dec bx {if not at end of intrp line}
jnz @loop2 {go process the next byte}
{ran against right edge of intrp window}
{so throw away rest of the scan data}
@loop3: {suck up extra scan data}
dec dx {did we run out of data?}
jz @done
lodsb {get a frame scan byte}
shl al,1 {if it is not a sync, }
jnc @loop3 {keep looping}
jmp @loopd
@loop4: {fill out rest of intrp data}
and es:[di],ch {strip old intrp value to 0}
inc di
dec bx {loop until right edge reached}
jnz @loop4
@loopd:
mov bx,ss:[IWidth] {restore width to reg BX}
cmp di,ss:[Bottom] {are we at bottom?}
jc @loop1 {do more if not at bottom}
@done:
pop ds {restore DS and we are done}
end;
end;
{=====================================================================}
{now we are gonna display the video on the screen}
procedure FrameObj.IntrpDisplay(fnum,IWidth,ISize:word; Iptr:IntrpPtr);
var Bottom:word;
begin
asm
cld
push ds
lds si,ss:[Iptr] {get intrp array pointer}
mov bx,si {point bx at the start of the array}
add si,256 {first 256 bytes has intpr array}
mov ax,ss:[ISize] {compute intrp bottom address offset}
add ax,si
mov ss:[Bottom],ax {and save it}
mov ax,0A000h {point es to the display segment}
mov es,ax
mov cx,ss:[IWidth] {put intrp right edge offset}
mov di,fnum {start at top left corner of screen}
and di,1 {offset by frame number count (even/odd)}
jz @dlp1
add si,cx {use odd scan lines on odd video frames}
@dlp1:
push di
@dlp2:
lodsb {get a intrp byte}
xlat {translate it to gray scale number}
stosb {display it}
inc di {skip a display pixel (we get it next time)}
dec cx {end of the scan line?}
jnz @dlp2 {loop until done}
pop di {restore original display start offset}
add di,320 {add display width to it}
mov cx,ss:[IWidth] {restore Iwidth to cx}
add si,cx
add si,cx {skip three video scan lines}
add si,cx
cmp si,ss:[Bottom] {are we at the bottom?}
jc @dlp1 {keep going if not}
@done:
pop ds {ok, we're done}
end;
end;
{=====================================================================}
{now we are gonna display the video on the screen}
procedure FrameObj.MakeDiskArray(fnum,IWidth,ISize:word;
Iptr:IntrpPtr; Dptr:IntrpPtr);
var Bottom:word;
begin
asm
cld
push ds
lds si,ss:[Iptr] {get intrp array pointer}
mov bx,si {point bx at the start of the array}
add si,256 {first 256 bytes has intpr array}
mov ax,ss:[ISize] {compute intrp bottom address offset}
add ax,si
mov ss:[Bottom],ax {and save it}
les di,Dptr {point es:di at the disk array}
mov cx,ss:[IWidth] {put intrp right edge offset}
mov dx,si
add dx,cx
@dlp1:
lodsb {get a intrp byte}
xlat {translate it to gray scale number}
mov ah,al
xchg si,dx
lodsb
xlat
xchg ah,al
xchg si,dx
stosw {save it in the array}
dec cx {end of the scan line?}
jnz @dlp1 {loop until done}
@dlp3:
mov cx,ss:[IWidth] {restore Iwidth to cx}
add si,cx
add si,cx {skip three video scan lines}
add si,cx
mov dx,si
add dx,cx
cmp si,ss:[Bottom] {are we at the bottom?}
jc @dlp1 {keep going if not}
@done:
pop ds {ok, we're done}
end;
end;
procedure DisplayMovieFrame(DWidth,DSize:word; Dptr:IntrpPtr);
var Bottom:word;
begin
asm
cld
push ds
lds si,ss:[Dptr] {get intrp array pointer}
mov ax,ss:[DSize] {compute intrp bottom address offset}
add ax,si
mov ss:[Bottom],ax {and save it}
mov ax,0A000h {point es to the display segment}
mov es,ax
mov di,0
mov cx,ss:[DWidth] {put intrp right edge offset}
@dlp1:
push di
rep movsb {get a movie byte and display it}
pop di {restore original display start offset}
add di,320 {add display width to it}
mov cx,ss:[DWidth] {restore Iwidth to cx}
cmp si,ss:[Bottom] {are we at the bottom?}
jc @dlp1 {keep going if not}
@done:
pop ds {ok, we're done}
end;
end;
{================================================================}
function fstr(W:word):string8;
var s:string8;
begin
str(W,S);
fstr := S;
end;
{------------------------------------------------------------}
{format of disk file is: }
{ number of frames : word }
{ frame size in bytes : word }
{ frame width in bytes : word }
{ video frame data : array[0..frames] of dary^ }
{------------------------------------------------------------}
procedure OpenMovie;
var MovieWidth : word;
MovieSize : word;
MovieCount : word;
begin
Frame.FrameCount := 0;
if Frame.filenum > 9 then frame.filenum := 0;
MovieWidth := Frame.IntrpWidth*2;
MovieSize := (Frame.IntrpSize*2) div 3;
MovieCount := Frame.Framecount;
fns := 'DCFG'+fstr(Frame.filenum)+'.MOV';
Assign(mf,fns);
inc(Frame.filenum);
rewrite(mf,1);
blockwrite(mf,MovieCount,2);
blockwrite(mf,MovieSize,2);
blockwrite(mf,MovieWidth,2);
end;
procedure WriteMovie;
var MovieSize:word;
begin
MovieSize := (Frame.IntrpSize*2) div 3;
inc(frame.framecount);
Frame.MakeDiskArray(Frame.framenum,Frame.IntrpWidth,
Frame.IntrpSize, Frame.Iary, Frame.Dary);
blockwrite(mf,Frame.Dary^,MovieSize);
end;
procedure CloseMovie;
begin
reset(mf,1);
dec(Frame.FrameCount);
blockwrite(mf,Frame.framecount,2);
close(mf);
end;
procedure ShowMovie(what:char; Rep:boolean);
var MovieWidth:word;
MovieSize:word;
MovieCount:word;
done:boolean;
begin
showframe := false;
done := false;
While not(done) do
begin
if not(Rep) then fns := 'DCFG'+what+'.MOV';
Assign(mf,fns);
reset(mf,1);
blockread(mf,MovieCount,2);
blockread(mf,MovieSize,2);
blockread(mf,MovieWidth,2);
inc(MovieCount);
i := 0;
while i < MovieCount do
begin
blockread(mf,Frame.Dary^,MovieSize);
DisplayMovieFrame(MovieWidth,MovieSize,Frame.Dary);
if keypressed then i := MovieCount;
gotoxy(1,24);
write('Showing Movie:',fns,' Frame:',i,' ');
inc(i);
delay(50);
end;
close(mf);
if not(Rep) then done := true;
if keypressed then done := true;
end;
gotoxy(1,24);
write(' ');
end;
procedure SaveToFrame;
begin
OpenMovie;
WriteMovie;
CloseMovie;
end;
{ ************************************************************** }
{ program start }
begin
writeln;
cx := #255;
filenum := 0;
showframe := true;
MovieEnabled := false;
directvideo := false;
OldMode := CrtMode;
asm
mov ax,$0013 {switch to vga graphics mode}
mov bx,0
int $10
end;
ib := 0;
while ib < 15 do {load palettes with gray levels}
begin
asm
mov ax,1010h
mov ch,[ib] {green}
add ch,ch
add ch,ch
mov cl,ch {blue}
mov dh,ch {red}
mov bl,[ib]
mov bh,0
int 10h
end;
inc(ib);
end;
fillchar(screen,sizeof(screen),0);
Frame.Init;
if ParamCount > 0 then
Frame.SetFramePort(ParamStr(1))
else
Frame.SetFramePort('1');
gotoxy(1,20);
write('X:',Frame.IntrpWidth * 2,' Y:',Frame.IntrpSize div
(Frame.Intrpwidth *2),' ');
repeat
if Frame.GrabOne then
begin
Frame.F2Iconvert(Frame.Framenum,Frame.GrabSize,
Frame.IntrpWidth,Frame.IntrpSize,
Frame.Iary, Frame.Fary[Frame.framenum]);
if MovieEnabled then
begin
WriteMovie;
gotoxy(1,24);
write('Movie:',fns,' Frame:',Frame.framecount,' ');
gotoxy(1,25);
write('Movie on ');
end
else
begin
gotoxy(1,25);
write('Movie off ');
end;
gotoxy(1,22);
write(' ');
end
else
begin
gotoxy(1,22);
write('Lost Sync');
end;
if ShowFrame then
Frame.IntrpDisplay(Frame.framenum,Frame.IntrpWidth,
Frame.IntrpSize,Frame.Iary);
if keypressed then {key pressed? If so, process it}
begin
cx := readkey;
if cx = #0 then cx := char($80+ord(readkey));
if MovieEnabled then
begin
MovieEnabled := false;
CloseMovie;
end;
if upcase(cx) = 'F'then SaveToFrame
else if upcase(cx) = 'M' then begin OpenMovie; MovieEnabled :=
true; end
else if upcase(cx) = 'R' then ShowMovie(cx,true)
else if upcase(cx) = 'S' then Showframe := false
else if (cx >= '0') and (cx <= '9') then ShowMovie(cx,false)
else showframe := true;
gotoxy(1,20);
write('X:',Frame.IntrpWidth * 2,' Y:',Frame.IntrpSize div
(Frame.Intrpwidth *2),' ');
end;
until cx < #32;
asm
mov ah,$00 {restore original display mode}
mov al,[oldmode]
mov bx,0
int $10
end;
end.
-----------------------------------------------------------------
Program Description
This program requires a 286 or better computer. A printer port
with the attached device described above with a video signal
driving it. And a MCGA or VGA type display system. You start the
program up by entering its name at the DOS prompt followed by the
printer port number you wish to use.
Examples: DCFG2 1 <-- loads program for use with LPT1.
DCFG2 2 <-- loads program for use with LPT2.
If you don't give it a number, the program will default to LPT1.
Grabbing a Frame:
The program works by grabbing samples from the printer port in
frames. Once each time through the loop, it sucks in a video
frame from the video input.
Actually, more than a frame is grabbed. How much is grabbed
depends on the speed of access to the printer port on your
computer. On a typical computer this is 1.5us per byte. At the
1.5us access rate, 63.5us per scan line, and 265.5 scan lines per
video frame, that results in 11240 bytes per frame required to be
collected. The collection size is currently set to 20,000 bytes.
That provides enough space to handle buss speeds up to 12MHz which
should be adequate for most computers.
The down side is that we collect 210 extra scan lines on a normal
computer. In most situations this isn't a problem since three to
four frames will typically be lost while the collected video data
is being processed anyway. The extra partial frame has only a
slight effect on the over all collection time and results. The
effect is minimized on a fast machine since nearly the entire
array is filled anyway.
The maximum sample size allowed is 65520. More than that and we
run out of segment space in memory. Also during this period I
keep the system interrupts off so as to not have distortions of
the collected data caused by the system deciding to go off and do
something else.
In addition to the collection time, there is a pre-configure time
while the loop is waiting for the vertical sync pulse to come in.
I don't collect any data prior to the vertical sync pulse since
it would be ignored anyway.
The vertical sync is determined by watching the sync line. When
it goes on for longer than 10us, you are in the vertical sync
time. Less then 10us, and it is a horizontal sync pulse.
Once the video data is collected, I release the interrupts to
allow the computer to return to normal operation. Should no sync
come in during the vertical sync detection period, I terminate
the loop after a while to allow the computer to continue to
operate, and return an error condition so that the program will
know that no data was collected.
If you have an O'scope, you can verify the video collection time
by observing data output line DO7 (pin 9). This line should never
go high for longer than 50ms. Typically it should be around 20 to
40ms in length. This should be the same whether you have a signal
going into the video line or not. Though there may be a
difference between the timing when a signal is present verses
when it is not present.
Další informace o konferenci Linux