Log in

View Full Version : QPel, GMC and B-VOP


marnix88
8th May 2006, 15:03
Hello, I'm not really sure where to post this, but this seems to be the most suitable forum.

I've been reading some information about how AVI videos work... the main header starts with "avih", the index starts with "idx1", a video frame starts with "00dc", etc. This is visible when I open a small AVI (XviD) with for example notepad.

This information can be easily found on the internet, but I can't find information about how to identify QPel, GMC and B-VOP. Programs like GSpot, AVICheck and MPEG4 Modifier can show whether an AVI was encoded with these options, but how do they check that? What are they exactly looking for? I'm not talking about software source code, but I assume that there must be something within a frame that tells you if one (or more) of these options were used.

Thanks.

Manao
8th May 2006, 16:56
Yes, there is. It's rather technical though. Either you get your hands on the MPEG4 part 2 standard, which explains how the bitstream is built, or you read XviD or ffmpeg's source code - which might actually be easier and clearer ( bitstream/bitstream.c in XviD )

marnix88
9th May 2006, 00:56
Thanks Manao, I'll have a look at the MPEG4 part 2 standard. Maybe that'll give me the information I'm looking for.

Looking at the source code of XviD and ffmpeg won't help much, because then I need to have full knowledge of the C language (which I don't have) in order to understand what the code exactly does.

marnix88
2nd June 2006, 15:40
Ok, I found a way to get B-VOP and S-VOP. I need to search for four bytes 00 00 01 B6. The next two bits are the frame type 00 (0), 01 (1), 10 (2), 11 (3). B-VOP is 2 and S-VOP is 3.

I wrote a function to get this information. Now I only need to find out if a video is encoded with QPel. Is there also a byte/bit(s) somewhere in an AVI video that tells you if it was encoded with QPel?

Thanks.

foxyshadis
2nd June 2006, 22:08
You should pick up a copy of the mpeg4modifier source, it has a very easy-to-understand parser in MPEG4FrameModifier.cs ParseVOL(). I find it the best bitstream description around. (To answer your question, it's a bool stored in the header as a byte right after the quant matrix, if any.)

marnix88
3rd June 2006, 20:20
Thank you, foxyshadis.

mrmarman
15th June 2006, 19:23
Hi-

- I'm studying computer programming and want to produce a tiny application. I'm using Delphi Programming language.

- I have a Hardware DivX player that not supports GMC and QPEL.

- I want to check QPEL and GMC from AVI files and send result to a Database that I createted automatically.

- I made an AVI file Info Structure viewer with OpenDML AVI Info Structure pdf documentation, but didn't see any information about determining these specs from an avi.

- I have a couple of DivX AVI that contains QPEL and/or GMC.

- MPEG4 Modifier says below

Packed bitstream: Yes
QPel: Yes
GMC: Yes (2 warp points)
Interlaced: No
Aspect ratio: Square pixels
Quant type: H.263

I-VOPs: 1 (0,72%)
P-VOPs: 41 (29,71%)
B-VOPs: 68 (49,28%)
S-VOPs: 28 (20,29%)
N-VOPs: 0 (0,00%)

Max consecutive B-VOPs: 1

Warp points used:
1: 100,00%
- I can access a file with binary mode and made an tiny utility for this operation and cropped an AVI file until QuarterPixel result No.

- MPEG4 Modifier does not support truncated AVI's so that I tried other two program AVICheck and GSPOT.

- The result; size limitation of detection for AVICheck is 38.427 bytes. This test for GSpot program was 121.675 bytes.

- So, I understood that the QPEL record is not in AVI header chunk, in the data chunks.

- Now, please give me a trick about how to find and detect QPEL and GMC from AVI. Which part of AVI chunk ( for example you say : first 00dc (Video Frame) you must pass nn bytes and read or search blabla hex Flag and read next bit )

- I check MPEG4 Modifier Source but I lost between "BitStream Class" and "ParseVOP". Java is foreign for me very much. :(

- I can read and report AVI Info like below..

[AVI]
AvihSize=56
dwMicroSecPerFrame=40000
dwMaxBytesPerFrame=0
dwPaddingGranularity=0
dwFlags=0x00000110
dwTotalFrames=33156
dwInitialFrames=0
dwStreams=2
dwSuggestedBufferSize=0
dwWidth=480
dwHeight=352


[STRL_01]
StrlSize=4244
StrhSize=56
fccType=vids
fccHandler=divx
dwFlags=0x00000000
wPriority=0
wLanguage=0
dwInitialFrames=0
dwScale=1000
dwRate=25000
dwStart=0
dwLength=33156
dwSuggestedBufferSize=75292
dwQuality=10000
dwSampleSize=0
rcFrame_wX1=0
rcFrame_wY1=0
rcFrame_wX2=480
rcFrame_wY2=352


[STRL_01_Video]
StrfSize=40
biSize=40
biWidth=480
biHeight=352
biPlanes=1
biBitCount=24
biCompression=DX50
biSizeImage=506880
biXPelsPerMeter=0
biYPelsPerMeter=0
biClrUsed=0
biClrImportant=0


[STRL_02]
StrlSize=4234
StrhSize=56
fccType=auds
fccHandler=
dwFlags=0x00000000
wPriority=0
wLanguage=0
dwInitialFrames=1
dwScale=1152
dwRate=48000
dwStart=0
dwLength=55260
dwSuggestedBufferSize=768
dwQuality=-1
dwSampleSize=0
rcFrame_wX1=0
rcFrame_wY1=0
rcFrame_wX2=1136
rcFrame_wY2=0


[STRL_02_Audio]
StrfSize=30
wFormatTag=0x0055
nChannels=2
nSamplesPerSec=48000
nAvgBytesPerSec=16000
nBlockAlign=1152
wBitsPerSample=0
cbSize=12

- I try to make a function below.. I'm seeking 00dc chunk and starting GMC and/or QPEL flag detection.

Function FrameType(InFile:String) : Integer;
Var
AVIFile : File;
Chunk1,
Chunk2 : string[4];
Position,
Size : dWord;
FlagResult: Byte;
Two : Word;
begin
Result := -1;
AssignFile( AVIFile, InFile );
FileMode := 0; // read only
Reset(AVIFile, 1);

Position := 12;
While (Chunk2 <> 'movi') AND (Position < 50000) do
begin // 50000 is precaution for infinite loop
Chunk1[0] := #4;
Chunk2[0] := #4;
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk1[1], 4);
BlockRead(AVIFile, Size, SizeOf(Size));
BlockRead(AVIFile, Chunk2[1], 4);
if Chunk2 <> 'movi' then Inc(Position, Size + 8);
end;
If Chunk2 <> 'movi' then
begin
ShowMessage('File Truncated');
Exit;
end;

Inc(Position, 8); // Start Position -> 'movi'
Inc(Position, 4); // Start Position -> '01wb'
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk1[1], 4);
BlockRead(AVIFile, Size, SizeOf(Size));

Inc(Position, Size + 8); // NOW Start Position ->'00dc' (Video Frame)
While (Chunk1 <> '00dc') AND (Position < 50000) do
begin // 50000 is precaution for infinite loop
Chunk1[0] := #4; // Reset variable
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk1[1], 4);
BlockRead(AVIFile, Size, SizeOf(Size));
if Chunk1 <> '00dc' then Inc(Position, Size + 8);
end;

If Chunk1 <> '00dc' then
begin
ShowMessage('!File Truncated');
Exit;
end;

// Message for trace: are we really positioned on '00dc'
// and chunk size what we read.
ShowMessageFmt('%xh: %s -> %x', [Position, Chunk1, Size]);
//_____________________________________
// These section is not complete...
Two := 0;
FlagResult := 0;
Inc(Position, 8 + 38); // one file can find 'B601' but its changing place
Seek(AVIFile, Position);
BlockRead(AVIFile, Two, SizeOf(Two));
BlockRead(AVIFile, FlagResult, SizeOf(FlagResult));
ShowMessageFmt('%xh: Founded Flag -> %.4x and Result : %x', [Position, Two, FlagResult]);
//_____________________________________
Case FlagResult of
$00 : Result := 0;
$01 : Result := 1;
$10 : Result := 2;
$11 : Result := 3;
end; // Case
CloseFile(AVIFile);
end;
- Please help ...

- Best regards Muharrem ARMAN

sysKin
16th June 2006, 09:31
- Please help ...
I know Delphi just like you know Java (it seems) but I don't see any bit-wise operations that would identify the two flags?

You're reading bytes, but you need bits, and I don't see the code that extracts the two.

Anyway, to search for qpel, you first need to find VOL startcode (00 00 01 20 in hex) and then you need to parse it on bitstream level. No other way, since the following data are variable-length fields. The actual qpel flag can be reasonably close on some streams, but waaaay later if, for example, quantization matrices are present (they are transmitted first).

mrmarman
17th June 2006, 13:22
@sysKin thank you for this opinion.

- I really light up now and take the your route.

Sample GMC + QPEL http://img265.imageshack.us/img265/8387/qpelgmcsamplevw7.png

- I trace the 0x00000120 and found the VOL (Video Object Layer) programmatically and get and next four bytes converted to the binary format. The result is below.

[00]00dc Offset: 000085D4-0000D163
VOLoffset : 85E0
0000000000000000
0000000001001000
0000000000001000
0000000010111010


Function CheckIt( InFile:String; MaxBytes:dWord ) : Integer;
Function HexToBin(const S: string): string; // sub function needed for bit op.
var I: integer; T: string;
begin
Result:='';
for I:=1 to Length(S) do
begin
case S[I] of
'0': T:='0000';
'1': T:='0001';
'2': T:='0010';
'3': T:='0011';
'4': T:='0100';
'5': T:='0101';
'6': T:='0110';
'7': T:='0111';
'8': T:='1000';
'9': T:='1001';
'A': T:='1010';
'B': T:='1011';
'C': T:='1100';
'D': T:='1101';
'E': T:='1110';
'F': T:='1111';
else T:= 'xxxx';
end; // Case
Result:=Concat(Result,T)
end
end;

Const
VOLStart = $20010000; //0x00000120 Video Object Layer (VOL)
UserData = $B2010000; //0x000001B2 User Data
VOPStart = $B6010000; //0x000001B6 Video Object Plane (VOP)
Var
AVIFile : File;
Chunk : string[4];
dValue,
Position,
dSize : dWord;
bByte : Byte;
dcCounter : Integer;

dcStart,
dcEnd : dWord;
MaxSize : dWord;
begin
Form1.Memo1.Lines.Clear;
Result := -1;
Position := 12;
dcCounter := 00;

AssignFile( AVIFile, InFile );
FileMode := 0; // read only
Reset(AVIFile, 1);
If MaxBytes = 0 then MaxSize := FileSize(AVIFile)
else MaxSize := MaxBytes;

While (Chunk <> 'movi') AND (Position < MaxSize) do
begin
Chunk[0] := #4;
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk[1], 4);
BlockRead(AVIFile, dSize, SizeOf(dSize));
Chunk[0] := #4;
BlockRead(AVIFile, Chunk[1], 4);
if Chunk <> 'movi' then Inc(Position, dSize + 8);
end;

If Chunk <> 'movi' then
begin
ShowMessage('File Truncated');
Exit;
end;

Inc(Position, 8); // Position 'movi'
Inc(Position, 4); // Position '01wb'
Seek(AVIFile, Position);
Chunk[0] := #4;
BlockRead(AVIFile, Chunk[1], 4);
BlockRead(AVIFile, dSize, SizeOf(dSize));
Inc(Position, dSize + 8); // now we are prior position of "00dc" (Video Frame)

Repeat
Chunk := '';
While (Chunk <> '00dc') AND (Position < MaxSize) do
begin
Chunk[0] := #4; // Reset variable
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk[1], 4);
BlockRead(AVIFile, dSize, SizeOf(dSize));
if Chunk <> '00dc' then Inc(Position, dSize + 8);
end;

If Chunk <> '00dc' then
begin
//ShowMessage('!File Truncated');
Exit;
end;

dcStart := Position;
dcEnd := Position + dSize + 8;

Form1.Memo1.Lines.Add('');
Form1.Memo1.Lines.Add( Format('[%.2d]00dc Offset: %.8x-%.8x', [dcCounter, dcStart, dcEnd]) );

While ( dWord(FilePos(AVIFile)) < dcEnd ) do
begin
BlockRead(AVIFile, dValue, SizeOf(dValue));
If ( dValue = VOLStart ) AND (NOT EOF(AVIFile) ) then
begin
Form1.Memo1.Lines.Add( Format('VOLoffset : %.4x', [FilePos(AVIFile)-4]) );
// Byte-1
BlockRead(AVIFile, bByte, SizeOf(bByte));
Form1.Memo1.Lines.Add( HexToBin(Format('%.4x', [bByte])) );
// Byte-2
BlockRead(AVIFile, bByte, SizeOf(bByte));
Form1.Memo1.Lines.Add( HexToBin(Format('%.4x', [bByte])) );
// Byte-3
BlockRead(AVIFile, bByte, SizeOf(bByte));
Form1.Memo1.Lines.Add( HexToBin(Format('%.4x', [bByte])) );
// Byte-4
BlockRead(AVIFile, bByte, SizeOf(bByte));
Form1.Memo1.Lines.Add( HexToBin(Format('%.4x', [bByte])) );
end
else Seek(AVIFile, FilePos(AVIFile)-3); // three bytes rewind
end; // While
Inc(dcCounter);
If dcEnd MOD 2 = 0 then Position := dcEnd
else Position := dcEnd + 1;
Until ( dcCounter > 0 ) or ( Position >= MaxSize );
CloseFile(AVIFile);
end;

- How many bytes must I get. Is four bytes right otherwise only the next one byte.

Regards, Muharrem

sysKin
17th June 2006, 18:05
After reading the 32 bits which indicate VOL start code, you need to skip another 9 bits (random_accessible_vol bit and video_object_type_indication) and read one bit.

This bit will tell you if object_layer_identifier is present. If yes, the next four bits will tell you the type of VOL it is (vol_ver_id) which you need to remember for later. Then, you skip three more bits (VOL priority).

Then, you read four bits which indicate pixel aspect ratio. If this aspect ratio is "external", you need to skip another 16 bits in which external PAR is signaled (as two 8-bit numbers). Otherwise, they are not there.

The following bit will tell you if "VOL control parameters" is present. If yes, you need to read gazzilion parameters which indicate bitrate and VBV characterstics, and also whether decoder should or should not expect b-frames. If not, you don't.

Then, you read four bits which indicate what shape the video is. Let's say you can assume "rectangular" here.

Then, you read 16 bits of time resolution, you use it to calculate number of bits N for time increment, and you read N bits of time increment.

...


Hopefully you see the picture. All I've just written is a translation to English of BitstreamReadHeaders() function from xvid/src/bitrstream.c

- How many bytes must I get. Is four bytes right otherwise only the next one byte.
If quantization matrices are present, they alone might take up to 128 bytes before you get to a qpel flag. You need to stop thinking in bytes.

mrmarman
20th June 2006, 04:23
Hello...

Thanks again @sysKin my brain lights up again...

- I studied my lesson... :sly: I started to learn C language to solve this problem.

- There are a few good Web Sites that teaching and/or reference for C language that helped me like this C# Programmer's Reference (http://www.nedcomp.nl/support/origdocs/dotnetsdk/csref/vclrfconditionaloperator.htm)

- I started to convert VOLParse function C to delphi. There was a hard work for me. Result is below...But there is a little problem. Does not work perfect. :confused:

- I want to share the code. May be another delphi programmer want to look and may be give his/her opinion.

- I checked code a lot of times but did not find what the wrong is... Some files finds GMC - some files not. QPEL detection is awful.

Function CheckIt( InFile:String; MaxBytes:dWord ) : Integer;
Function Backwise(Bin:String):String;
begin
Result := '';
While Length(Bin) > 0 do
begin
Result := Result + Copy(Bin, Length(Bin)-1, 2);
Delete(Bin, Length(Bin)-1, 2);
end;
end;

Function HexToBin(const S: string): string; // sub function needed for bit op.
var I: integer; T: string;
begin
Result:='';
for I:=1 to Length(S) do
begin
case S[I] of
'0': T:='0000';
'1': T:='0001';
'2': T:='0010';
'3': T:='0011';
'4': T:='0100';
'5': T:='0101';
'6': T:='0110';
'7': T:='0111';
'8': T:='1000';
'9': T:='1001';
'A': T:='1010';
'B': T:='1011';
'C': T:='1100';
'D': T:='1101';
'E': T:='1110';
'F': T:='1111';
else T:= 'xxxx';
end; // Case
Result:=Concat(Result,T)
end
end;

function Binary2Int( const Value: String ) : Integer;
var I: Integer;
begin
Result := 0;
for I := 1 to Length( Value ) do
begin
if Value[ I ] in [ '0'..'1' ] then
Result := Result * 2 + Ord( Value[ I ] ) - Ord( '0' )
else break;
end;
end;

Function ReadBit(BitStream:String; Var Pos:Int64; BitCount:Int64):Integer;
begin
// Read and Step to Next number of BitCount Bits
Result := Binary2Int( Copy(BitStream, Pos, BitCount) );
Inc(Pos, BitCount);
end;

Function BitsUsed(x:Integer): Integer;
Var
numBits : Integer;
begin
numBits := 0;
While NOT (x = 0) do
begin
x:= x shr 1;
Inc(numBits);
end;
Result := numBits;
end;

Function ToBool(x:Integer): Boolean;
begin
Result := Boolean(x <> 0);
end;

Const _zigzag_scan : Array [1..64] of word = (
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63 );

_ar_desc : Array [0..15] of String = (
'Invalid aspect_ratio setting',
'Square pixels',
'4:3 PAL pixel shape',
'4:3 NTSC pixel shape',
'16:9 PAL pixel shape',
'16:9 NTSC pixel shape',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Invalid aspect_ratio setting',
'Custom pixel shape' );

Function CopyQuantMatrix(BitStream:String; Var BitPos:Int64) : dWord;
Var
Val : dWord;
qm : Array [1..64] of Integer Absolute Val;
last : Integer;
coef : Integer;
i : Integer;
begin
Last := 0;
For i := 1 to 64 do
begin
coef := ReadBit(BitStream, BitPos, 8);
If coef = 0 then break;
last := coef;
qm[_zigzag_scan[i]] := coef;
end;
qm[_zigzag_scan[i]] := coef;

while (i < 64) do begin
Inc(i);
qm[_zigzag_scan[i]] := last;
end;
ShowMessageFmt('%d', [Val]);
Result := Val;
end;

Type TVOLInfo = Packed Record
ver_id,
aspect_ratio,
par_width,
par_height,
shape,
time_inc_res : dWord;
time_inc_bits : Integer;
interlaced : Boolean;
sprite_enable,
sprite_warping_points : dWord;
mpeg_quant,
load_intra_quant_mat,
load_inter_quant_mat : Boolean;
intra_quant_mat,
inter_quant_mat : dWord;
quarterpel,
newpred_enable,
reduced_resolution_enable : Boolean;
end;

Type TVOPType = ( I_VOP, P_VOP, B_VOP, S_VOP, N_VOP, N_VOP_D );

Type TVOLShape = Packed Record
Rectangular,
Binary,
BinaryOnly,
Grayscale : Word;
end;

Type TPARType = Packed Record
VGA_1_1,
PAL_4_3,
NTSC_4_3,
PAL_16_9,
NTSC_16_9,
Custom : Word;
end;

Type TSprite = Packed Record
None,
Static,
GMC : Word;
end;

Type TVOPInfo = Packed Record
frame_num : Integer;
coding_type : dWord;
coded : Boolean;
vop_type : TVOPType;
is_reference_vop : Boolean;
modulo_time_base,
time_inc : dWord;
disp_time_ticks : Int64;
disp_time_secs : Double;
top_field_first : Boolean;
warping_points_used : dWord;
end;

Const
VOLstart = $20010000; //0x00000120 Video Object Layer (VOL)
UserData = $B2010000; //0x000001B2 User Data
VOPstart = $B6010000; //0x000001B6 Video Object Plane (VOP)
Var
AVIFile : File;
Chunk : string[4];

d32Bits : dWord;
s32Bits : String;
d64Bits : Int64; // 8 bytes - 64 bits
sType : String;
First : Boolean;

dValue,
Position,
dSize : dWord;
dcCounter : Integer;

dcEnd : dWord;
MaxSize : dWord;
VOL : TVOLinfo;
VOLshape : TVOLshape;
PARtype : TPARtype;
Sprite : TSprite;
BitStream : String;
BitPos : Int64;
begin
VolShape.Rectangular := 0;
VolShape.Binary := 1;
VolShape.BinaryOnly := 2;
VolShape.Rectangular := 3;

PARtype.VGA_1_1 := 1;
PARtype.PAL_4_3 := 2;
PARtype.NTSC_4_3 := 3;
PARtype.PAL_16_9 := 4;
PARtype.NTSC_16_9 := 5;
PARtype.Custom :=15;

Sprite.None := 0;
Sprite.Static := 1;
Sprite.GMC := 3;

BitPos := 0;
FillChar(VOl, SizeOf(Vol), #0);
Form1.Memo1.Lines.Clear;
Result := -1;
Position := 12;
dcCounter := 00;

AssignFile( AVIFile, InFile );
FileMode := fmOpenRead;
Reset(AVIFile, 1);
If MaxBytes = 0 then MaxSize := FileSize(AVIFile)
else MaxSize := MaxBytes;

While (Chunk <> 'movi') AND (Position < MaxSize) do
begin
Chunk[0] := #4;
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk[1], 4);
BlockRead(AVIFile, dSize, SizeOf(dSize));
Chunk[0] := #4;
BlockRead(AVIFile, Chunk[1], 4);
if Chunk <> 'movi' then Inc(Position, dSize + 8);
end;

If Chunk <> 'movi' then
begin
ShowMessage('File Truncated');
Exit;
end;

Inc(Position, 8); // Position 'movi'
Inc(Position, 4); // Position '01wb'
Seek(AVIFile, Position);
Chunk[0] := #4;
BlockRead(AVIFile, Chunk[1], 4);
BlockRead(AVIFile, dSize, SizeOf(dSize));
Inc(Position, dSize + 8); // now we are prior position of "00dc" (Video Frame)

Repeat
Chunk := '';
While (Chunk <> '00dc') AND (Position < MaxSize) do
begin
Chunk[0] := #4; // Reset variable
Seek(AVIFile, Position);
BlockRead(AVIFile, Chunk[1], 4);
BlockRead(AVIFile, dSize, SizeOf(dSize));
if Chunk <> '00dc' then Inc(Position, dSize + 8);
end;

If Chunk <> '00dc' then
begin
//ShowMessage('!File Truncated');
Exit;
end;

//dcStart := Position;
dcEnd := Position + dSize + 8;
First := True;
While ( dWord(FilePos(AVIFile)) < dcEnd ) do
begin
BlockRead(AVIFile, dValue, SizeOf(dValue));
If ( dValue = VOLstart ) then //VOLstart:0x00000120 VideoObjectLayer(VOL)
begin //_ParseVOL_//
// 3x8 byte = 24 byte (192 bits will be read)
BlockRead(AVIFile, d64Bits, SizeOf(d64Bits));
BitStream := Backwise(Format('%.16x', [d64Bits]));
BlockRead(AVIFile, d64Bits, SizeOf(d64Bits));
BitStream := BitStream + Backwise(Format('%.16x', [d64Bits]));
BlockRead(AVIFile, d64Bits, SizeOf(d64Bits));
BitStream := BitStream + Backwise(Format('%.16x', [d64Bits]));
BitStream := HexToBin(BitStream);

BitPos := 1;
Inc(BitPos, 9); // first 9 bits random_accessible_vol bit and video_object_type_indication
// 10th bit -> if Object Layer Identifier is present
If ReadBit(BitStream, BitPos, 1) = 1 then begin
Vol.ver_id := ReadBit(BitStream, BitPos, 4); // type of VOL
Inc(BitPos, 3); // Skip VOL priority
end
else Vol.ver_id := 1;

// PAR info
Vol.aspect_ratio := ReadBit(BitStream, BitPos, 4);
If Vol.aspect_ratio = PARType.Custom then begin // Custom PARType
Vol.par_width := ReadBit(BitStream, BitPos, 8);
Vol.par_height := ReadBit(BitStream, BitPos, 8);
end;

If ReadBit(BitStream, BitPos, 1) = 1 then begin
Inc(BitPos, 3);
If ReadBit(BitStream, BitPos, 1) = 1 then begin
//79 bits
Inc(BitPos, 32);
Inc(BitPos, 32);
Inc(BitPos, 15);
end;
end;
vol.shape := ReadBit(BitStream, BitPos, 2);
if ( (vol.ver_id <> 1) AND (vol.shape = VOLShape.Grayscale) ) then begin
Inc(BitPos, 4);
end;
Inc(BitPos, 1);

vol.time_inc_res := ReadBit(BitStream, BitPos, 16);
vol.time_inc_bits := Max(BitsUsed(vol.time_inc_res - 1), 1); // Uses Math
// 412, 9
Inc(BitPos, 1);

if ReadBit(BitStream, BitPos, 1) = 1 then
begin
Inc(BitPos, vol.time_inc_bits);
end;

if ( vol.shape <> VOLShape.BinaryOnly) then begin
if (vol.shape = VOLShape.Rectangular) then begin
Inc(BitPos, 29);
end;
vol.interlaced := ToBool(ReadBit(BitStream, BitPos, 1));
Inc(BitPos, 1);
// Alternative for --> vol.sprite_enable = bits.Copy( vol.ver_id == 1 ? 1 : 2 );
If vol.ver_id = 1
then vol.sprite_enable := ReadBit(BitStream, BitPos, 1)
else vol.sprite_enable := ReadBit(BitStream, BitPos, 2);

if (vol.sprite_enable <> Sprite.None) then begin
if (vol.sprite_enable = Sprite.Static) then begin
// 56 bits
Inc(BitPos, 32);
Inc(BitPos, 24);
end;

vol.sprite_warping_points := ReadBit(BitStream, BitPos, 6);
if (vol.sprite_warping_points > 4) then begin
ShowMessage('Invalid VOL (too many warping points)');
end;

Inc(BitPos, 3);
if (vol.sprite_enable = Sprite.Static) then begin
Inc(BitPos, 1);
end;
end;

if ((vol.ver_id <> 1) AND (vol.shape <> VOLShape.Rectangular)) then begin
Inc(BitPos, 1);
end;

if ReadBit(BitStream, BitPos, 1) = 1 then begin
Inc(BitPos, 8);
end;

if (vol.shape = VOLShape.Grayscale) then begin
Inc(BitPos, 3);
end;

vol.mpeg_quant := ToBool(ReadBit(BitStream, BitPos, 1));
if (vol.mpeg_quant) then begin
vol.load_intra_quant_mat := ToBool(ReadBit(BitStream, BitPos, 1));
if (vol.load_intra_quant_mat) then begin
vol.intra_quant_mat := CopyQuantMatrix(BitStream, BitPos);
end;

vol.load_inter_quant_mat := ToBool(ReadBit(BitStream, BitPos, 1));
if (vol.load_inter_quant_mat) then begin
vol.inter_quant_mat := CopyQuantMatrix(BitStream, BitPos);
end;

if (vol.shape = VOLShape.Grayscale) then begin
ShowMessage('Grayscale matrix isn''t supported');
end;
end;
if (vol.ver_id <> 1) then begin
vol.quarterpel := ToBool(ReadBit(BitStream, BitPos, 1));
end;

if ReadBit(BitStream, BitPos, 1) = 0 then begin
ShowMessage('Complexity estimation isn''t supported');
end;
Inc(BitPos, 1);

if ReadBit(BitStream, BitPos, 1) = 1 then begin
Inc(BitPos, 1);
end;

if (vol.ver_id <> 1) then begin
vol.newpred_enable := ToBool(ReadBit(BitStream, BitPos, 1));
if (vol.newpred_enable) then begin
Inc(BitPos, 3);
end;
vol.reduced_resolution_enable := ToBool(ReadBit(BitStream, BitPos, 1));
end;
if ReadBit(BitStream, BitPos, 1) = 1 then begin
ShowMessage('Scalability isn''t supported');
end;
end
else begin
if (vol.ver_id <> 1) then begin
if ReadBit(BitStream, BitPos, 1) = 1 then begin
ShowMessage('Scalability isn''t supported');
end;
end;
Inc(BitPos, 1);
end;
// Check padding (padding should be present even if the data already ends on
// a byte boundary, but if there's no padding we will just ignore it)
// if ((bits.Remaining < 0) || (bits.Remaining > 8)) {
// throw new Exception("Invalid VOL");
//}
//bits.Copy(bits.Remaining);

//_vol = vol;
//return modify ? bitsOut.GetBytes(bitsOut.Position / 8) : null;

// QPEL
If Vol.QuarterPel then ShowMessage('QPEL : Yes');

// GMC
if (vol.sprite_enable = Sprite.GMC) then ShowMessage('GMC : Yes');

// Quant type
If (vol.mpeg_quant) then begin
If (vol.load_intra_quant_mat OR vol.load_inter_quant_mat)
then ShowMessage('Quant Type : MPEG Custom')
else ShowMessage('Quant Type : MPEG')
end
else begin
ShowMessage('Quant Type : H.263');
end;
end else
If ( dValue = VOPstart ) then
begin // VOPstart = $B6010000; //0x000001B6 Video Object Plane (VOP)
break;
//____ !!! ____//
BlockRead(AVIFile, d32Bits, SizeOf(d32Bits));
s32Bits := HexToBin(Backwise(Format('%.8x', [d32Bits])));
sType := '';
If s32Bits[1]+s32Bits[2] = '00' then sType := 'I-VOP';
If s32Bits[1]+s32Bits[2] = '10' then sType := 'B-VOP';
If s32Bits[1]+s32Bits[2] = '11' then sType := 'S-VOP';
If s32Bits[1]+s32Bits[2] = '01' then sType := 'P-VOP';
If First then Form1.Memo1.Lines.Add( Format('[%.3d] %s-%.8s (%x)', [dcCounter, s32Bits, sType, d32Bits] ) )
else Form1.Memo1.Lines.Add( Format(' %s-%.8s (%x)', [ s32Bits, sType, d32Bits] ) );
First := False;
end
else Seek(AVIFile, FilePos(AVIFile)-3); // three bytes rewind
end; // While
Inc(dcCounter);
If dcEnd MOD 2 = 0 then Position := dcEnd
else Position := dcEnd + 1;
Until ( dcCounter > 3 ) or ( Position >= MaxSize );
CloseFile(AVIFile);
end;


- Best regards Muharrem