MODULE MPEGVideoDecoder;
IMPORT
SYSTEM, Codec := Codecs, Raster, Streams, KernelLog, Files, WMGraphics, MPEGTables,
WM := WMWindowManager, Rectangles := WMRectangles, Kernel, Commands,
Util := MPEGUtilities;
CONST
SCPicture* = CHR(000H);
SCUserData* = CHR(0B2H);
SCSequenceHeader* = CHR(0B3H);
SCSequenceError* = CHR(0B4H);
SCExtension* = CHR(0B5H);
SCSequenceEnd* = CHR(0B7H);
SCGOP* = CHR(0B8H);
SCSystemEnd* = CHR(0B9H);
SCPack* = CHR(0BAH);
SCSystemHeader* = CHR(0BBH);
SCReservedStream* = CHR(0BCH);
SCPrivateStream* = CHR(0BDH);
SCPaddingStream* = CHR(0BEH);
SCPrivateStream2* = CHR(0BFH);
PicStructReserved* = 0;
PicStructTopField* = 1;
PicStructBottomField* = 2;
PicStructFrame* = 3;
FMTReserved* = 0;
FMTField* = 1;
FMTFrame* = 2;
FMTDualPrime* = 3;
forward = 0;
backward = 1;
horizontal = 0;
vertical = 1;
mv1 = 0;
mv2 = 1;
TYPE
StreamType = RECORD
stream*: Codec.DemuxStream;
idByte*: CHAR;
pos: LONGINT;
bytesLeftInPacket: LONGINT;
eos: BOOLEAN;
END;
TYPE PW* = OBJECT(WM.DoubleBufferWindow)
PROCEDURE & InitNew*(w, h:LONGINT; alpha:BOOLEAN);
BEGIN
Init(w, h, alpha);
manager := WM.GetDefaultManager();
WM.DefaultAddWindow(SELF);
END InitNew;
PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
BEGIN
Draw^(canvas, w, h, 0)
END Draw;
PROCEDURE Close;
BEGIN
Close^;
END Close;
END PW;
MPEGVideoDecoder* = OBJECT(Codec.VideoDecoder)
VAR
videoWidth, videoHeight: LONGINT;
videoWidthDiv2, videoHeightDiv2: LONGINT;
videoWidthDiv16, videoHeightDiv16: LONGINT;
aspectRatioIndex, frameRateIndex: LONGINT;
bitRate: LONGINT;
stream*: Util.BitStream;
reader: Util.StreamReader;
idct: Util.IDCT;
yuv2rgb: Util.ColorSpace;
dequantizer: Util.Dequantizer;
blocks: Util.BlockActions;
intraQM: Util.PointerToArrayOfLONGINT;
nonintraQM: Util.PointerToArrayOfLONGINT;
curFrame: Util.Frame;
prevRef, nextRef: Util.Frame;
nextFrameToRender: Util.Frame;
mvinfos: Util.MotionVectorInfos;
frameNr: LONGINT;
realFrameNr: LONGINT;
time: LONGINT;
mspf: LONGINT;
hasMoreFrames: BOOLEAN;
MPEG2: BOOLEAN;
MainProfile: BOOLEAN;
LevelID: LONGINT;
ChromaFormat: LONGINT;
picExt: Util.PicCodingExt;
mbSkipped: BOOLEAN;
dcY, dcCb, dcCr: LONGINT;
mbMotionForwOld, mbMotionBackOld: BOOLEAN;
mbIntraOld: BOOLEAN;
mbAddress: LONGINT;
mbAddressLast: LONGINT;
mbAddressLastIntra: LONGINT;
macroblockNr: INTEGER;
frameMotionType: LONGINT;
dctType: BOOLEAN;
block: Util.PointerToArrayOfLONGINT;
frametype: LONGINT;
PROCEDURE &Init*;
VAR
i: SHORTINT;
BEGIN
NEW(idct);
NEW(yuv2rgb);
NEW(dequantizer);
NEW(picExt);
NEW(mvinfos);
NEW(blocks);
NEW(block, 64);
hasMoreFrames := TRUE;
realFrameNr := -1;
NEW(intraQM, 64);
NEW(nonintraQM, 64);
FOR i := 0 TO 63 DO
intraQM[i] := MPEGTables.IQM[i];
nonintraQM[i] := 16;
END;
END Init;
PROCEDURE Open*(in : Streams.Reader; VAR res : LONGINT);
VAR
marker: CHAR;
BEGIN
res := Codec.ResFailed;
IF ~(in IS Codec.DemuxStream) THEN RETURN END;
NEW(stream, in(Codec.DemuxStream));
NEW(reader, stream);
frameNr := -1;
IF ~GotoNextMarker(stream, marker) THEN
KernelLog.String("this is not a legal MPEG video stream (no startcode found)"); KernelLog.Ln;
RETURN;
END;
IF marker # SCSequenceHeader THEN
IF marker = CHR(0BAH) THEN
KernelLog.String("This is a multiplexed (audio & video) MPEG stream. Use the demultiplexer."); KernelLog.Ln;
ELSE
KernelLog.String("This is not a valid Video Stream (Marker="); KernelLog.Hex(ORD(marker), -1);
KernelLog.String(")"); KernelLog.Ln;
END;
RETURN;
END;
stream.SkipBits(32);
IF ParseSequenceHeader() THEN
videoWidthDiv2 := videoWidth DIV 2;
videoHeightDiv2 := videoHeight DIV 2;
videoWidthDiv16 := videoWidth DIV 16;
videoHeightDiv16 := videoHeight DIV 16;
NEW(curFrame);
NEW(curFrame.buffer, videoHeight * videoWidth + 2 * (videoHeightDiv2 * videoWidthDiv2));
curFrame.cbOffset := videoHeight * videoWidth;
curFrame.crOffset := videoHeight * videoWidth + videoHeightDiv2 * videoWidthDiv2;
curFrame.frameNr := -1;
NEW(prevRef);
NEW(prevRef.buffer, videoHeight * videoWidth + 2 * (videoHeightDiv2 * videoWidthDiv2));
prevRef.cbOffset := videoHeight * videoWidth;
prevRef.crOffset := videoHeight * videoWidth + videoHeightDiv2 * videoWidthDiv2;
prevRef.frameNr := -1;
NEW(nextRef);
NEW(nextRef.buffer, videoHeight * videoWidth + 2 * (videoHeightDiv2 * videoWidthDiv2));
nextRef.cbOffset := videoHeight * videoWidth;
nextRef.crOffset := videoHeight * videoWidth + videoHeightDiv2 * videoWidthDiv2;
nextRef.frameNr := -1;
res := Codec.ResOk;
RETURN;
ELSE
KernelLog.String("Failed parsing (first) Sequence Header"); KernelLog.Ln;
END;
END Open;
PROCEDURE HasMoreData*(): BOOLEAN;
BEGIN
RETURN hasMoreFrames;
END HasMoreData;
PROCEDURE ParseSequenceHeader(): BOOLEAN;
VAR
marker: CHAR;
BEGIN
videoWidth := stream.GetBits(12);
videoHeight := stream.GetBits(12);
IF videoWidth MOD 16 # 0 THEN
videoWidth := ((videoWidth DIV 16) + 1) * 16;
END;
IF videoHeight MOD 16 # 0 THEN
videoHeight := ((videoHeight DIV 16) + 1) * 16;
END;
aspectRatioIndex := stream.GetBits(4);
frameRateIndex := stream.GetBits(4);
CASE frameRateIndex OF
1: mspf := 42
| 2: mspf := 42
| 3: mspf := 40
| 4: mspf := 33
| 5: mspf := 33
| 6: mspf := 20
| 7: mspf := 17
| 8: mspf := 17
ELSE
mspf := 40;
KernelLog.String("Unknown Framerate Index: "); KernelLog.Int(frameRateIndex, 0); KernelLog.Ln;
END;
bitRate := stream.GetBits(18);
stream.SkipBits(1);
stream.SkipBits(10);
stream.SkipBits(1);
IF stream.GetBits(1) = 1 THEN
reader.ReadQuantizerMatrix(intraQM);
IF reader.eof THEN RETURN FALSE END;
END;
IF stream.GetBits(1) = 1 THEN
reader.ReadQuantizerMatrix(nonintraQM);
IF reader.eof THEN RETURN FALSE END;
END;
IF ~stream.HasMoreData() THEN hasMoreFrames := FALSE; RETURN FALSE END;
IF ~GotoNextMarker(stream, marker) THEN
RETURN FALSE;
END;
IF marker = SCExtension THEN
MPEG2 := TRUE;
REPEAT
IF marker = SCExtension THEN
stream.SkipBits(32);
IF ~ReadExtension() THEN RETURN FALSE END;
IF ~GotoNextMarker(stream, marker) THEN RETURN FALSE END;
ELSE
stream.SkipBits(32);
IF ~GotoNextMarker(stream, marker) THEN RETURN FALSE END;
END;
UNTIL (marker # SCExtension) & (marker # SCUserData);
ELSE
MPEG2 := FALSE;
WHILE marker = SCUserData DO
stream.SkipBits(32);
IF ~GotoNextMarker(stream, marker) THEN
RETURN FALSE;
END;
END;
END;
RETURN TRUE;
END ParseSequenceHeader;
PROCEDURE ReadExtension(): BOOLEAN;
VAR
fourbits: LONGINT;
tmp: BOOLEAN;
BEGIN
fourbits := stream.GetBits(4);
CASE fourbits OF
0, 5, 6, 9..15:
KernelLog.String("Extension not supported: "); KernelLog.Int(stream.ShowBits(4), 0); KernelLog.Ln;
RETURN FALSE;
| 1:
tmp := reader.ReadSequenceExtension(MainProfile, LevelID, ChromaFormat, videoWidth, videoHeight);
IF reader.eof THEN
RETURN FALSE;
ELSE
RETURN tmp;
END;
| 2:
tmp := reader.ReadSequenceDisplayExtension();
IF reader.eof THEN
RETURN FALSE;
ELSE
RETURN tmp;
END;
| 3:
tmp := reader.ReadQuantMatrixExtension();
IF reader.eof THEN
RETURN FALSE;
ELSE
RETURN tmp;
END;
| 4:
tmp := reader.ReadCopyrightExtension();
IF reader.eof THEN
RETURN FALSE;
ELSE
RETURN tmp;
END;
| 7:
tmp := reader.ReadPictureDisplayExtension();
IF reader.eof THEN
RETURN FALSE;
ELSE
RETURN tmp;
END;
| 8:
tmp := reader.ReadPictureCodingExtension(picExt, mvinfos);
IF reader.eof THEN
RETURN FALSE;
ELSE
RETURN tmp;
END;
ELSE
hasMoreFrames := FALSE;
RETURN FALSE;
END;
RETURN FALSE;
END ReadExtension;
PROCEDURE ReadTimecode;
VAR
h, min, sec, frames: LONGINT;
BEGIN
stream.SkipBits(1);
h := stream.GetBits(5);
min := stream.GetBits(6);
stream.SkipBits(1);
sec := stream.GetBits(6);
frames := stream.GetBits(6);
IF ~stream.HasMoreData() THEN
hasMoreFrames := FALSE;
END;
END ReadTimecode;
PROCEDURE GetVideoInfo*(VAR width, height, millisecondsPerFrame : LONGINT);
BEGIN
width := videoWidth;
height := videoHeight;
millisecondsPerFrame := mspf;
END GetVideoInfo;
PROCEDURE CanSeek*() : BOOLEAN;
BEGIN
RETURN TRUE
END CanSeek;
PROCEDURE GetCurrentFrame*() : LONGINT;
BEGIN
RETURN realFrameNr;
END GetCurrentFrame;
PROCEDURE GetCurrentTime*() : LONGINT;
BEGIN
RETURN time;
END GetCurrentTime;
PROCEDURE SeekFrame*(frame : LONGINT; goKeyFrame : BOOLEAN; VAR res : LONGINT);
VAR
i: LONGINT;
code: CHAR;
lastIntraPosOld, lastIntraNrOld, lastIntraPos, lastIntraNr, lastFramePos: LONGINT;
nrB, nrBOld: LONGINT;
countB: BOOLEAN;
type: LONGINT;
BEGIN
res := Codec.ResFailed;
stream.Reset;
IF ~GotoNextMarker(stream, code) THEN RETURN END;
stream.SkipBits(32);
IF ~ParseSequenceHeader() THEN RETURN END;
lastIntraPos := stream.Pos();
lastFramePos := stream.Pos();
FOR i := 0 TO frame DO
type := SkipNext();
IF type = 1 THEN
lastIntraPosOld := lastIntraPos;
lastIntraNrOld := lastIntraNr;
lastIntraPos := lastFramePos;
lastIntraNr := i;
countB := TRUE;
nrBOld := nrB;
nrB := 0;
ELSE
IF countB THEN
IF type = 3 THEN
INC(nrB);
ELSE
countB := FALSE;
END;
END;
END;
lastFramePos := stream.Pos();
END;
stream.SetPos(lastIntraPosOld);
realFrameNr := lastIntraNrOld;
frameNr := 10000;
Next();
FOR i := 1 TO nrBOld DO
type := SkipNext();
END;
DEC(frameNr, nrBOld);
FOR i := lastIntraNrOld+1 TO lastIntraNr-1 DO
Next();
END;
IF ~goKeyFrame THEN
FOR i := lastIntraNr TO frame DO
Next();
END;
END;
res := Codec.ResOk;
END SeekFrame;
PROCEDURE SeekMillisecond*(millisecond : LONGINT; goKeyFrame : BOOLEAN; VAR res : LONGINT);
VAR
newframe: LONGINT;
BEGIN
newframe := millisecond DIV mspf;
SeekFrame(newframe, goKeyFrame, res);
time := newframe * mspf;
END SeekMillisecond;
PROCEDURE SkipNext(): LONGINT;
VAR
marker: CHAR;
picType: LONGINT;
tempRef: LONGINT;
nextCode: LONGINT;
tmpFrame: Util.Frame;
BEGIN
IF ~hasMoreFrames THEN RETURN -1 END;
INC(frameNr);
INC(realFrameNr);
IF frameNr = nextRef.frameNr THEN
tmpFrame := curFrame;
curFrame := nextRef;
nextRef := tmpFrame;
ELSE
REPEAT
mbAddress := -1;
mbAddressLast := -1;
IF ~GotoNextMarker(stream, marker) THEN RETURN -1 END;
WHILE marker # SCPicture DO
IF marker = SCSequenceHeader THEN
stream.SkipBits(32);
IF ~ParseSequenceHeader() THEN
hasMoreFrames := FALSE;
RETURN -1;
END;
ELSIF marker = SCGOP THEN
stream.SkipBits(32);
ReadTimecode;
stream.SkipBits(1);
stream.SkipBits(1);
stream.SkipBits(5);
frameNr := 0;
prevRef.frameNr := -1;
nextRef.frameNr := -1;
ELSE
KernelLog.String("Unexpected marker found: "); KernelLog.Hex(ORD(marker), -1); KernelLog.Ln;
RETURN -1;
END;
IF ~GotoNextMarker(stream, marker) THEN RETURN -1 END;
END;
stream.SkipBits(32);
tempRef := stream.GetBits(10);
curFrame.frameNr := tempRef;
picType := stream.GetBits(3);
frametype := picType;
curFrame.picType := picType;
stream.SkipBits(16);
IF (picType = 2) OR (picType = 3) THEN
stream.SkipBits(4);
END;
IF picType = 3 THEN
stream.SkipBits(4);
END;
WHILE stream.ShowBits(1) = 1 DO
stream.SkipBits(1);
stream.SkipBits(8);
END;
stream.SkipBits(1);
IF ~stream.HasMoreData() THEN
hasMoreFrames := FALSE;
RETURN -1;
END;
IF ~GotoNextMarker(stream, marker) THEN RETURN -1 END;
IF marker = SCExtension THEN
IF ~MPEG2 THEN
HALT(1234);
END;
stream.SkipBits(32);
IF stream.ShowBits(4) # 8 THEN
RETURN -1;
ELSE
stream.SkipBits(4);
END;
IF ~reader.ReadPictureCodingExtension(picExt, mvinfos) THEN RETURN -1 END;
IF reader.eof THEN hasMoreFrames := FALSE; RETURN 0 END;
IF picExt.framePredFrameDct THEN frameMotionType := FMTFrame END;
IF ~GotoNextMarker(stream, marker) THEN RETURN -1 END;
IF ~GotoNextMarker(stream, marker) THEN RETURN -1 END;
ELSIF MPEG2 THEN
KernelLog.String("MPEG-2 picture extension not found"); KernelLog.Ln;
HALT(1234);
END;
IF marker = SCUserData THEN
stream.SkipBits(32);
WHILE stream.ShowBits(24) # 1 DO
stream.SkipBits(8);
END;
IF ~GotoNextMarker(stream, marker) THEN RETURN -1 END;
END;
IF (picType = 1) OR (picType = 2) OR (picType = 3) THEN
REPEAT
stream.SkipBits(32);
WHILE stream.ShowBits(24) # 1 DO
stream.SkipBits(8);
END;
nextCode := stream.ShowBits(32);
UNTIL ~(nextCode > 100H) & (nextCode <= 1AFH);
ELSIF picType = 4 THEN
RETURN -1;
ELSE
RETURN -1;
END;
IF tempRef > frameNr THEN
tmpFrame := nextRef;
nextRef := curFrame;
curFrame := tmpFrame;
END;
UNTIL tempRef <= frameNr;
END;
IF (curFrame.picType = 1) OR (curFrame.picType = 2) THEN
tmpFrame := prevRef;
prevRef := curFrame;
curFrame := tmpFrame;
nextFrameToRender := prevRef;
ELSE
nextFrameToRender := curFrame;
END;
RETURN picType;
END SkipNext;
PROCEDURE Next*;
VAR
marker: CHAR;
picType: LONGINT;
tempRef: LONGINT;
res: BOOLEAN;
nextCode: LONGINT;
tmpFrame: Util.Frame;
BEGIN
IF ~hasMoreFrames THEN RETURN END;
INC(frameNr);
INC(realFrameNr);
INC(time, mspf);
IF frameNr = nextRef.frameNr THEN
tmpFrame := curFrame;
curFrame := nextRef;
nextRef := tmpFrame;
ELSE
REPEAT
mbAddress := -1;
mbAddressLast := -1;
IF ~GotoNextMarker(stream, marker) THEN RETURN END;
WHILE marker # SCPicture DO
IF marker = SCSequenceHeader THEN
stream.SkipBits(32);
IF ~ParseSequenceHeader() THEN
hasMoreFrames := FALSE;
RETURN END;
ELSIF marker = SCGOP THEN
stream.SkipBits(32);
ReadTimecode;
stream.SkipBits(1);
stream.SkipBits(1);
stream.SkipBits(5);
frameNr := 0;
prevRef.frameNr := -1;
nextRef.frameNr := -1;
ELSIF marker = SCSequenceEnd THEN
hasMoreFrames := FALSE;
RETURN;
ELSE
KernelLog.String("Unexpected marker found: "); KernelLog.Hex(ORD(marker), -1); KernelLog.Ln;
RETURN;
END;
IF ~GotoNextMarker(stream, marker) THEN RETURN END;
END;
stream.SkipBits(32);
tempRef := stream.GetBits(10);
curFrame.frameNr := tempRef;
picType := stream.GetBits(3);
frametype := picType;
curFrame.picType := picType;
stream.SkipBits(16);
IF tempRef < frameNr THEN
frameNr := tempRef;
END;
IF (picType = 2) OR (picType = 3) THEN
IF stream.ShowBits(1) = 1 THEN
mvinfos.fullPel[mv1][forward] := TRUE;
ELSE
mvinfos.fullPel[mv1][forward] := FALSE;
END;
stream.SkipBits(1);
mvinfos.fCode[mv1][forward] := stream.GetBits(3);
mvinfos.rSize[mv1][forward] := mvinfos.fCode[mv1][forward] - 1;
mvinfos.f[mv1][forward] := SYSTEM.VAL(LONGINT, {mvinfos.rSize[mv1][forward]});
END;
IF picType = 3 THEN
IF stream.ShowBits(1) = 1 THEN
mvinfos.fullPel[mv1][backward] := TRUE;
ELSE
mvinfos.fullPel[mv1][backward] := FALSE;
END;
stream.SkipBits(1);
mvinfos.fCode[mv1][backward] := stream.GetBits(3);
mvinfos.rSize[mv1][backward] := mvinfos.fCode[mv1][backward] - 1;
mvinfos.f[mv1][backward] := SYSTEM.VAL(LONGINT, {mvinfos.rSize[mv1][backward]});
END;
WHILE stream.ShowBits(1) = 1 DO
stream.SkipBits(1);
stream.SkipBits(8);
END;
stream.SkipBits(1);
IF ~stream.HasMoreData() OR reader.eof THEN
hasMoreFrames := FALSE;
RETURN;
END;
IF ~GotoNextMarker(stream, marker) THEN RETURN END;
IF marker = SCExtension THEN
IF ~MPEG2 THEN
HALT(1234);
END;
stream.SkipBits(32);
IF stream.ShowBits(4) # 8 THEN
RETURN;
ELSE
stream.SkipBits(4);
END;
IF ~reader.ReadPictureCodingExtension(picExt, mvinfos) THEN RETURN END;
IF reader.eof THEN hasMoreFrames := FALSE; RETURN END;
IF picExt.framePredFrameDct THEN frameMotionType := FMTFrame END;
IF ~GotoNextMarker(stream, marker) THEN RETURN END;
IF ~GotoNextMarker(stream, marker) THEN RETURN END;
ELSIF MPEG2 THEN
KernelLog.String("MPEG-2 picture extension not found"); KernelLog.Ln;
HALT(1234);
END;
IF marker = SCUserData THEN
stream.SkipBits(32);
WHILE stream.ShowBits(24) # 1 DO
stream.SkipBits(8);
END;
IF ~GotoNextMarker(stream, marker) THEN RETURN END;
END;
IF (picType = 1) OR (picType = 2) OR (picType = 3) THEN
REPEAT
res := DecodeSlice(picType);
IF ~res THEN hasMoreFrames := FALSE; RETURN END;
nextCode := stream.ShowBits(32);
UNTIL ~(res & (nextCode > 100H) & (nextCode <= 1AFH));
ELSIF picType = 4 THEN
RETURN;
ELSE
RETURN;
END;
IF tempRef > frameNr THEN
tmpFrame := nextRef;
nextRef := curFrame;
curFrame := tmpFrame;
END;
UNTIL tempRef <= frameNr;
END;
IF (curFrame.picType = 1) OR (curFrame.picType = 2) THEN
tmpFrame := prevRef;
prevRef := curFrame;
curFrame := tmpFrame;
nextFrameToRender := prevRef;
ELSE
nextFrameToRender := curFrame;
END;
END Next;
PROCEDURE DecodeSlice(type: LONGINT):BOOLEAN;
VAR
quantScale: LONGINT;
marker: CHAR;
BEGIN
IF MPEG2 THEN
dcY := MPEGTables.DCP[picExt.dcPrecision];
dcCb := MPEGTables.DCP[picExt.dcPrecision];
dcCr := MPEGTables.DCP[picExt.dcPrecision];
ELSE
dcY := 8 * 128;
dcCb := 8 * 128;
dcCr := 8 * 128;
END;
mvinfos.pmv[mv1][forward][horizontal] := 0;
mvinfos.pmv[mv1][forward][vertical] := 0;
mvinfos.pmv[mv1][backward][horizontal] := 0;
mvinfos.pmv[mv1][backward][vertical] := 0;
mvinfos.pmv[mv2][forward][horizontal] := 0;
mvinfos.pmv[mv2][forward][vertical] := 0;
mvinfos.pmv[mv2][backward][horizontal] := 0;
mvinfos.pmv[mv2][backward][vertical] := 0;
mvinfos.motionVerticalFieldSelect[mv1][forward] := FALSE;
mvinfos.motionVerticalFieldSelect[mv1][backward] := FALSE;
mvinfos.motionVerticalFieldSelect[mv2][forward] := TRUE;
mvinfos.motionVerticalFieldSelect[mv2][backward] := TRUE;
macroblockNr := 0;
stream.SkipBits(24);
mbAddress := ((stream.GetBits(8)-1) * videoWidthDiv16) - 1;
mbAddressLast := mbAddress;
quantScale := stream.GetBits(5);
IF quantScale < 0 THEN hasMoreFrames := FALSE; RETURN FALSE END;
IF MPEG2 THEN
IF picExt.qScaleType THEN
quantScale := MPEGTables.QS1[quantScale];
ELSE
quantScale := MPEGTables.QS0[quantScale];
END;
END;
WHILE stream.ShowBits(1) = 1 DO
stream.SkipBits(9);
END;
stream.SkipBits(1);
WHILE stream.ShowBits(23) # 0 DO
IF ~DecodeMacroBlock(type, quantScale) THEN hasMoreFrames := FALSE; RETURN FALSE END;
END;
RETURN GotoNextMarker(stream, marker);
END DecodeSlice;
PROCEDURE DecodeMacroBlock(type: LONGINT; VAR quantScale: LONGINT):BOOLEAN;
VAR
tmp: LONGINT;
cbp: LONGINT;
cbpBits: LONGINT;
mbIntra, mbPattern, mbMotionBack, mbMotionForw, mbQuant: BOOLEAN;
i: LONGINT;
offsetX, offsetY, offsetXDiv2, offsetYDiv2: LONGINT;
first: BOOLEAN;
fpmf, fpmb: LONGINT;
yoffs, yincr: LONGINT;
BEGIN
INC(macroblockNr);
WHILE stream.ShowBits(11) = 15 DO
stream.SkipBits(11);
END;
WHILE stream.ShowBits(11) = 8 DO
stream.SkipBits(11);
INC(mbAddress, 33);
END;
tmp := reader.ReadAddressIncrement();
IF reader.eof THEN hasMoreFrames := FALSE END;
IF tmp # -1 THEN
INC(mbAddress, tmp);
ELSE
RETURN FALSE;
END;
mbSkipped := (mbAddress - mbAddressLast) > 1;
IF mbSkipped THEN
CASE type OF
1:
HALT(1234);
| 2:
FOR i := mbAddressLast + 1 TO (mbAddress - 1) DO
InsertPrediction(TRUE, FALSE, i, 0, 0, 0, 0);
END;
| 3:
IF mvinfos.fullPel[mv1][forward] THEN
fpmf := 2;
ELSE
fpmf := 1;
END;
IF mvinfos.fullPel[mv1][backward] THEN
fpmb := 2;
ELSE
fpmb := 1;
END;
FOR i := mbAddressLast + 1 TO (mbAddress - 1) DO
InsertPrediction(
mbMotionForwOld,
mbMotionBackOld,
i,
mvinfos.mv[mv1][forward][horizontal] * fpmf,
mvinfos.mv[mv1][forward][vertical] * fpmf,
mvinfos.mv[mv1][backward][horizontal] * fpmb,
mvinfos.mv[mv1][backward][vertical] * fpmb);
END;
END;
END;
IF ~reader.ReadMacroBlockType(type, mbIntra, mbPattern, mbMotionBack, mbMotionForw, mbQuant) THEN
RETURN FALSE;
END;
IF reader.eof THEN
RETURN FALSE;
END;
IF MPEG2 THEN
IF mbMotionForw OR mbMotionBack THEN
IF picExt.picStructure = PicStructFrame THEN
IF ~picExt.framePredFrameDct THEN
frameMotionType := stream.GetBits(2);
END;
ELSE
HALT(1234);
END;
END;
IF (picExt.picStructure = PicStructFrame) & (~picExt.framePredFrameDct) & (mbIntra OR mbPattern) THEN
dctType := (stream.GetBits(1) = 1);
ELSE
dctType := FALSE;
END;
END;
IF picExt.concealmentMV THEN
HALT(1234);
END;
IF mbIntraOld OR
((type = 2) & mbSkipped) OR
((type = 2) & ~mbMotionForw) THEN
mvinfos.pmv[0][0][0] := 0;
mvinfos.pmv[0][0][1] := 0;
mvinfos.pmv[0][1][0] := 0;
mvinfos.pmv[0][1][1] := 0;
mvinfos.pmv[1][0][0] := 0;
mvinfos.pmv[1][0][1] := 0;
mvinfos.pmv[1][1][0] := 0;
mvinfos.pmv[1][1][1] := 0;
mvinfos.mv[0][0][0] := 0;
mvinfos.mv[0][0][1] := 0;
mvinfos.mv[0][1][0] := 0;
mvinfos.mv[0][1][1] := 0;
mvinfos.mv[1][0][0] := 0;
mvinfos.mv[1][0][1] := 0;
mvinfos.mv[1][1][0] := 0;
mvinfos.mv[1][1][1] := 0;
mvinfos.motionVerticalFieldSelect[mv1][forward] := FALSE;
mvinfos.motionVerticalFieldSelect[mv1][backward] := FALSE;
mvinfos.motionVerticalFieldSelect[mv2][forward] := TRUE;
mvinfos.motionVerticalFieldSelect[mv2][backward] := TRUE;
END;
IF ~stream.HasMoreData() THEN
hasMoreFrames := FALSE;
RETURN FALSE;
END;
IF mbQuant THEN
quantScale := stream.GetBits(5);
IF quantScale < 0 THEN hasMoreFrames := FALSE; RETURN FALSE END;
IF MPEG2 THEN
IF picExt.qScaleType THEN
quantScale := MPEGTables.QS1[quantScale];
ELSE
quantScale := MPEGTables.QS0[quantScale];
END;
END;
END;
IF mbMotionForw OR (MPEG2 & mbIntra & picExt.concealmentMV) THEN
IF ~MPEG2 THEN
mvinfos.motionCode[mv1][forward][horizontal] := reader.ReadMotionCode();
IF (mvinfos.fCode[mv1][forward] # 1) & (mvinfos.motionCode[mv1][forward][horizontal] # 0) THEN
mvinfos.motionResidual[mv1][forward][horizontal] := stream.GetBits(mvinfos.fCode[mv1][forward]-1);
END;
mvinfos.motionCode[mv1][forward][vertical] := reader.ReadMotionCode();
IF (mvinfos.fCode[mv1][forward] # 1) & (mvinfos.motionCode[mv1][forward][vertical] # 0) THEN
mvinfos.motionResidual[mv1][forward][vertical] := stream.GetBits(mvinfos.fCode[mv1][forward]-1);
END;
ELSE
reader.ReadMotionVectors(0, mvinfos, frameMotionType);
END;
END;
IF mbMotionBack OR (MPEG2 & mbIntra & picExt.concealmentMV) THEN
IF ~MPEG2 THEN
mvinfos.motionCode[mv1][backward][horizontal] := reader.ReadMotionCode();
IF (mvinfos.fCode[mv1][backward] # 1) & (mvinfos.motionCode[mv1][backward][horizontal] # 0) THEN
mvinfos.motionResidual[mv1][backward][horizontal] := stream.GetBits(mvinfos.fCode[mv1][backward]-1);
END;
mvinfos.motionCode[mv1][backward][vertical] := reader.ReadMotionCode();
IF (mvinfos.fCode[mv1][backward] # 1) & (mvinfos.motionCode[mv1][backward][vertical] # 0) THEN
mvinfos.motionResidual[mv1][backward][vertical] := stream.GetBits(mvinfos.fCode[mv1][backward]-1);
END;
ELSE
reader.ReadMotionVectors(1, mvinfos, frameMotionType);
END;
END;
IF reader.eof OR ~stream.HasMoreData() THEN
hasMoreFrames := FALSE;
RETURN FALSE;
END;
IF mbPattern THEN
IF stream.ShowBits(3) = 0 THEN
cbpBits := stream.ShowBits(9);
cbp := MPEGTables.CBP9[cbpBits][0];
stream.SkipBits(MPEGTables.CBP9[cbpBits][1]);
ELSE
cbpBits := stream.ShowBits(7)-16;
cbp := MPEGTables.CBP7[cbpBits][0];
stream.SkipBits(MPEGTables.CBP7[cbpBits][1]);
END;
ELSE
IF mbIntra THEN
cbp := 63;
ELSE
cbp := 0;
END;
END;
IF ~MPEG2 THEN
IF mbMotionForw THEN
MotionDisplacement(forward, horizontal);
MotionDisplacement(forward, vertical);
END;
IF mbMotionBack THEN
MotionDisplacement(backward, horizontal);
MotionDisplacement(backward, vertical);
END;
ELSE
IF mbMotionForw THEN
DecodeMotionVectors(mv1, forward, horizontal);
DecodeMotionVectors(mv1, forward, vertical);
END;
IF mbMotionBack THEN
DecodeMotionVectors(mv1, backward, horizontal);
DecodeMotionVectors(mv1, backward, vertical);
END;
IF frameMotionType = FMTField THEN
IF mbMotionForw THEN
DecodeMotionVectors(mv2, forward, horizontal);
DecodeMotionVectors(mv2, forward, vertical);
END;
IF mbMotionBack THEN
DecodeMotionVectors(mv2, backward, horizontal);
DecodeMotionVectors(mv2, backward, vertical);
END;
END;
IF frameMotionType = FMTFrame THEN
IF mbMotionForw THEN
mvinfos.pmv[mv2][forward][horizontal] := mvinfos.pmv[mv1][forward][horizontal];
mvinfos.pmv[mv2][forward][vertical] := mvinfos.pmv[mv1][forward][vertical];
END;
IF mbMotionBack THEN
mvinfos.pmv[mv2][backward][horizontal] := mvinfos.pmv[mv1][backward][horizontal];
mvinfos.pmv[mv2][backward][vertical] := mvinfos.pmv[mv1][backward][vertical];
END;
END;
END;
IF MPEG2 THEN
mvinfos.fullPel[mv1][forward] := FALSE;
mvinfos.fullPel[mv1][backward] := FALSE;
END;
IF (type # 1) & ~mbIntra THEN
IF frameMotionType = FMTField THEN
InsertInterlacedPrediction(
(type = 2) OR ((type = 3) & mbMotionForw),
mbMotionBack,
mbAddress,
mvinfos);
ELSE
IF mvinfos.fullPel[mv1][forward] THEN
fpmf := 2;
ELSE
fpmf := 1;
END;
IF mvinfos.fullPel[mv1][backward] THEN
fpmb := 2;
ELSE
fpmb := 1;
END;
InsertPrediction(
(type = 2) OR ((type = 3) & mbMotionForw),
mbMotionBack,
mbAddress,
mvinfos.mv[mv1][forward][horizontal] * fpmf,
mvinfos.mv[mv1][forward][vertical] * fpmf,
mvinfos.mv[mv1][backward][horizontal] * fpmb,
mvinfos.mv[mv1][backward][vertical] * fpmb);
END;
END;
offsetX := (mbAddress MOD (videoWidthDiv16)) * 16;
offsetY := (mbAddress DIV (videoWidthDiv16)) * 16;
offsetXDiv2 := offsetX DIV 2;
offsetYDiv2 := offsetY DIV 2;
IF ~MPEG2 THEN
first := TRUE;
IF 5 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock(0, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, offsetY*videoWidth + offsetX, videoWidth);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, offsetY*videoWidth + offsetX, videoWidth);
END;
first := FALSE;
END;
IF 4 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock(1, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, offsetY*videoWidth + offsetX + 8, videoWidth);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, offsetY*videoWidth + offsetX + 8, videoWidth);
END;
first := FALSE;
END;
IF 3 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock(2, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, (offsetY+8)*videoWidth + offsetX, videoWidth);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, (offsetY+8)*videoWidth + offsetX, videoWidth);
END;
first := FALSE;
END;
IF 2 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock(3, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, (offsetY+8)*videoWidth + offsetX+8, videoWidth);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, (offsetY+8)*videoWidth + offsetX+8, videoWidth);
END;
first := FALSE;
END;
IF 1 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock(4, block, mbIntra, quantScale, TRUE, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, videoWidth*videoHeight + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, videoWidth*videoHeight + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
END;
END;
IF 0 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock(5, block, mbIntra, quantScale, TRUE, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, videoWidth*videoHeight + videoWidthDiv2*videoHeightDiv2 + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, videoWidth*videoHeight + videoWidthDiv2*videoHeightDiv2 + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
END;
END;
ELSE
IF dctType THEN
yincr := 2;
yoffs := 1;
ELSE
yincr := 1;
yoffs := 8;
END;
first := TRUE;
IF 5 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock2(0, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, offsetY*videoWidth + offsetX, videoWidth * yincr);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, offsetY*videoWidth + offsetX, videoWidth * yincr);
END;
first := FALSE;
END;
IF 4 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock2(1, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, offsetY*videoWidth + offsetX + 8, videoWidth * yincr);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, offsetY*videoWidth + offsetX + 8, videoWidth * yincr);
END;
first := FALSE;
END;
IF 3 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock2(2, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, (offsetY+yoffs)*videoWidth + offsetX, videoWidth * yincr);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, (offsetY+yoffs)*videoWidth + offsetX, videoWidth * yincr);
END;
first := FALSE;
END;
IF 2 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock2(3, block, mbIntra, quantScale, first, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, (offsetY+yoffs)*videoWidth + offsetX + 8, videoWidth * yincr);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, (offsetY+yoffs)*videoWidth + offsetX + 8, videoWidth * yincr);
END;
END;
IF 1 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock2(4, block, mbIntra, quantScale, TRUE, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, videoWidth*videoHeight + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, videoWidth*videoHeight + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
END;
END;
IF 0 IN SYSTEM.VAL(SET, cbp) THEN
IF ~DecodeBlock2(5, block, mbIntra, quantScale, TRUE, type) THEN RETURN FALSE END;
IF mbIntra THEN
blocks.TransferIDCTCopy(block, curFrame.buffer, videoWidth*videoHeight + videoWidthDiv2*videoHeightDiv2 + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
ELSE
blocks.TransferIDCTAdd(block, curFrame.buffer, videoWidth*videoHeight + videoWidthDiv2*videoHeightDiv2 + offsetYDiv2*videoWidthDiv2 + offsetXDiv2, videoWidthDiv2);
END;
END;
END;
IF type = 4 THEN
IF stream.ShowBits(1) # 1 THEN
RETURN FALSE;
ELSE
stream.SkipBits(1);
END;
END;
mbMotionForwOld := mbMotionForw;
mbMotionBackOld := mbMotionBack;
mbIntraOld := mbIntra;
mbAddressLast := mbAddress;
IF mbIntra THEN
mbAddressLastIntra := mbAddress;
END;
RETURN TRUE;
END DecodeMacroBlock;
PROCEDURE InsertInterlacedPrediction(forw, back: BOOLEAN; address: LONGINT; VAR mvi: Util.MotionVectorInfos);
VAR
yOffs, cbOffs, crOffs: LONGINT;
mvfx1, mvfy1, mvbx1, mvby1: LONGINT;
mvfx2, mvfy2, mvbx2, mvby2: LONGINT;
mvfx1c, mvfy1c, mvbx1c, mvby1c: LONGINT;
mvfx2c, mvfy2c, mvbx2c, mvby2c: LONGINT;
BEGIN
yOffs := (address DIV videoWidthDiv16) * videoWidth * 16 + (address MOD videoWidthDiv16) * 16;
cbOffs := videoWidth * videoHeight + (address DIV videoWidthDiv16) * videoWidthDiv2 * 8 + (address MOD videoWidthDiv16) * 8;
crOffs := cbOffs + videoHeightDiv2 * videoWidthDiv2;
mvfx1 := mvi.mv[mv1][forward][horizontal];
mvfy1 := mvi.mv[mv1][forward][vertical];
mvbx1 := mvi.mv[mv1][backward][horizontal];
mvby1 := mvi.mv[mv1][backward][vertical];
mvfx2 := mvi.mv[mv2][forward][horizontal];
mvfy2 := mvi.mv[mv2][forward][vertical];
mvbx2 := mvi.mv[mv2][backward][horizontal];
mvby2 := mvi.mv[mv2][backward][vertical];
mvfx1c := mvfx1 DIV 2;
mvfy1c := mvfy1 DIV 2;
mvbx1c := mvbx1 DIV 2;
mvby1c := mvby1 DIV 2;
mvfx2c := mvfx2 DIV 2;
mvfy2c := mvfy2 DIV 2;
mvbx2c := mvbx2 DIV 2;
mvby2c := mvby2 DIV 2;
IF mvi.motionVerticalFieldSelect[mv1][forward] THEN INC(mvfy1); INC(mvfy1c) END;
IF mvi.motionVerticalFieldSelect[mv1][backward] THEN INC(mvby1); INC(mvby1c) END;
IF ~mvi.motionVerticalFieldSelect[mv2][forward] THEN DEC(mvfy2); DEC(mvfy2c) END;
IF ~mvi.motionVerticalFieldSelect[mv2][backward] THEN DEC(mvby2); DEC(mvby2c) END;
IF forw THEN
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, yOffs, mvfx1, mvfy1, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, yOffs+8, mvfx1, mvfy1, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, yOffs+videoWidth, mvfx2, mvfy2, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, yOffs+videoWidth+8, mvfx2, mvfy2, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, cbOffs, mvfx1c, mvfy1c, videoWidth, videoWidth, 4);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, cbOffs+videoWidthDiv2, mvfx2c, mvfy2c, videoWidth, videoWidth, 4);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, crOffs, mvfx1c, mvfy1c, videoWidth, videoWidth, 4);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, crOffs+videoWidthDiv2, mvfx2c, mvfy2c, videoWidth, videoWidth, 4);
END;
IF back THEN
IF forw THEN
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, yOffs, mvbx1, mvby1, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, yOffs+8, mvbx1, mvby1, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, yOffs+videoWidth, mvbx2, mvby2, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, yOffs+videoWidth+8, mvbx2, mvby2, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, cbOffs, mvbx1c, mvby1c, videoWidth, videoWidth, 4);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, cbOffs+videoWidthDiv2, mvbx2c, mvby2c, videoWidth, videoWidth, 4);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, crOffs, mvbx1c, mvby1c, videoWidth, videoWidth, 4);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, crOffs+videoWidthDiv2, mvbx2c, mvby2c, videoWidth, videoWidth, 4);
ELSE
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, yOffs, mvbx1, mvby1, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, yOffs+8, mvbx1, mvby1, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, yOffs+videoWidth, mvbx2, mvby2, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, yOffs+videoWidth+8, mvbx2, mvby2, 2*videoWidth, 2*videoWidth, 8);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, cbOffs, mvbx1c, mvby1c, videoWidth, videoWidth, 4);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, cbOffs+videoWidthDiv2, mvbx2c, mvby2c, videoWidth, videoWidth, 4);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, crOffs, mvbx1c, mvby1c, videoWidth, videoWidth, 4);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, crOffs+videoWidthDiv2, mvbx2c, mvby2c, videoWidth, videoWidth, 4);
END;
END;
END InsertInterlacedPrediction;
PROCEDURE InsertPrediction(forward, backward: BOOLEAN; address, mvfx, mvfy, mvbx, mvby: LONGINT);
VAR
yOffs, cbOffs, crOffs: LONGINT;
BEGIN
yOffs := (address DIV videoWidthDiv16) * videoWidth * 16 + (address MOD videoWidthDiv16) * 16;
cbOffs := videoWidth * videoHeight + (address DIV videoWidthDiv16) * videoWidthDiv2 * 8 + (address MOD videoWidthDiv16) * 8;
crOffs := cbOffs + videoHeightDiv2 * videoWidthDiv2;
IF forward THEN
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, yOffs, mvfx, mvfy, videoWidth, videoWidth, 16);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, yOffs+8, mvfx, mvfy, videoWidth, videoWidth, 16);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, cbOffs, mvfx DIV 2, mvfy DIV 2, videoWidthDiv2, videoWidthDiv2, 8);
blocks.MoveBlockOverwrite(prevRef.buffer, curFrame.buffer, crOffs, mvfx DIV 2, mvfy DIV 2, videoWidthDiv2, videoWidthDiv2, 8);
END;
IF backward THEN
IF forward THEN
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, yOffs, mvbx, mvby, videoWidth, videoWidth, 16);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, yOffs+8, mvbx, mvby, videoWidth, videoWidth, 16);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, cbOffs, mvbx DIV 2, mvby DIV 2, videoWidthDiv2, videoWidthDiv2, 8);
blocks.MoveBlockInterp(nextRef.buffer, curFrame.buffer, crOffs, mvbx DIV 2, mvby DIV 2, videoWidthDiv2, videoWidthDiv2, 8);
ELSE
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, yOffs, mvbx, mvby, videoWidth, videoWidth, 16);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, yOffs+8, mvbx, mvby, videoWidth, videoWidth, 16);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, cbOffs, mvbx DIV 2, mvby DIV 2, videoWidthDiv2, videoWidthDiv2, 8);
blocks.MoveBlockOverwrite(nextRef.buffer, curFrame.buffer, crOffs, mvbx DIV 2, mvby DIV 2, videoWidthDiv2, videoWidthDiv2, 8);
END;
END;
END InsertPrediction;
PROCEDURE DecodeMotionVectors(r, s, t: LONGINT);
VAR
rSize: LONGINT;
f: LONGINT;
high, low, range: LONGINT;
delta: LONGINT;
prediction: LONGINT;
DEBUG: BOOLEAN;
BEGIN
DEBUG := (realFrameNr = -1) & (mbAddress > 1155) & (mbAddress < 1159);
IF DEBUG THEN
KernelLog.String("Macroblock "); KernelLog.Int(mbAddress, 0); KernelLog.Ln;
KernelLog.String("+++ INPUT +++"); KernelLog.Ln;
mvinfos.Dump(r, s, t);
END;
rSize := mvinfos.fCode[s][t] - 1;
f := SYSTEM.VAL(LONGINT, {rSize});
high := 16 * f - 1;
low := (-16) * f ;
range := 32 * f;
IF (f = 1) OR (mvinfos.motionCode[r][s][t] = 0) THEN
delta := mvinfos.motionCode[r][s][t];
ELSE
IF mvinfos.motionCode[r][s][t] > 0 THEN
delta := (mvinfos.motionCode[r][s][t] - 1) * f + mvinfos.motionResidual[r][s][t] + 1;
ELSE
delta := - ((- mvinfos.motionCode[r][s][t] - 1) * f + mvinfos.motionResidual[r][s][t] + 1);
END;
END;
IF (frameMotionType # FMTFrame) & (t = 1) & (picExt.picStructure = PicStructFrame) THEN
prediction := mvinfos.pmv[r][s][t] DIV 2;
ELSE
prediction := mvinfos.pmv[r][s][t];
END;
mvinfos.mv[r][s][t] := prediction + delta;
IF mvinfos.mv[r][s][t] < low THEN
INC(mvinfos.mv[r][s][t], range);
ELSIF mvinfos.mv[r][s][t] > high THEN
DEC(mvinfos.mv[r][s][t], range);
END;
IF (frameMotionType # FMTFrame) & (t = 1) & (picExt.picStructure = PicStructFrame) THEN
mvinfos.pmv[r][s][t] := mvinfos.mv[r][s][t] * 2;
ELSE
mvinfos.pmv[r][s][t] := mvinfos.mv[r][s][t];
END;
END DecodeMotionVectors;
PROCEDURE MotionDisplacement(fb, hv: LONGINT);
VAR
delta: LONGINT;
range: LONGINT;
motionCode, motionResidual, f, prediction: LONGINT;
BEGIN
motionCode := mvinfos.motionCode[mv1][fb][hv];
motionResidual := mvinfos.motionResidual[mv1][fb][hv];
f := mvinfos.f[mv1][fb];
prediction := mvinfos.pmv[mv1][fb][hv];
IF (f = 1) OR (motionCode = 0) THEN
delta := motionCode;
ELSE
delta := 1 + f * (motionCode * Sign(motionCode) - 1) + motionResidual;
IF motionCode < 0 THEN
delta := -delta;
END;
END;
INC(prediction, delta);
range := f * 32;
IF prediction > (f * 16 - 1) THEN
DEC(prediction, range);
ELSIF prediction < -(f * 16) THEN
INC(prediction, range);
END;
mvinfos.mv[mv1][fb][hv] := prediction;
mvinfos.pmv[mv1][fb][hv] := prediction;
END MotionDisplacement;
PROCEDURE DecodeBlock(
nr: SHORTINT;
coeffs: Util.PointerToArrayOfLONGINT;
intra: BOOLEAN;
VAR qScale: LONGINT;
first: BOOLEAN;
type: LONGINT): BOOLEAN;
VAR
bits: LONGINT;
size: LONGINT;
dcDiff: LONGINT;
tmp: LONGINT;
cur: LONGINT;
dummy: BOOLEAN;
i: LONGINT;
intraSkipped: BOOLEAN;
BEGIN
FOR i := 0 TO 63 DO
coeffs[i] := 0;
END;
cur := 0;
IF intra THEN
bits := stream.ShowBits(3);
IF nr < 4 THEN
IF bits = 7 THEN
stream.SkipBits(3);
size := 4;
WHILE stream.ShowBits(1) = 1 DO
INC(size);
stream.SkipBits(1);
END;
INC(size);
stream.SkipBits(1);
ELSE
size := MPEGTables.DCL3[bits][0];
stream.SkipBits(MPEGTables.DCL3[bits][1]);
END;
ELSE
IF bits = 7 THEN
stream.SkipBits(3);
size := 3;
WHILE stream.ShowBits(1) = 1 DO
INC(size);
stream.SkipBits(1);
END;
INC(size);
stream.SkipBits(1);
ELSE
size := MPEGTables.DCC3[bits][0];
stream.SkipBits(MPEGTables.DCC3[bits][1]);
END;
END;
IF size # 0 THEN
IF stream.ShowBits(1) = 0 THEN
tmp := stream.GetBits(size);
IF tmp < 0 THEN hasMoreFrames := FALSE; RETURN FALSE END;
dcDiff := -(SYSTEM.VAL(INTEGER, ((-SYSTEM.VAL(SET, tmp)) * {0..(size-1)})));
ELSE
dcDiff := stream.GetBits(size);
IF dcDiff < 0 THEN hasMoreFrames := FALSE; RETURN FALSE END;
END;
END;
coeffs[0] := dcDiff;
cur := 1;
ELSE
IF stream.ShowBits(1) = 1 THEN
IF stream.ShowBits(2) = 2 THEN
coeffs[0] := 1;
ELSE
coeffs[0] := -1;
END;
stream.SkipBits(2);
INC(cur);
ELSE
dummy := reader.ReadRunLevelCode(coeffs, cur, MPEG2);
IF reader.eof THEN hasMoreFrames := FALSE; RETURN FALSE END;
END;
END;
IF (~MPEG2) OR (~picExt.intraVlcFormat OR ~intra) THEN
WHILE ~reader.ReadRunLevelCode(coeffs, cur, MPEG2) DO END;
ELSE
WHILE ~reader.ReadRunLevelCode2(coeffs, cur) DO END;
END;
IF reader.eof THEN hasMoreFrames := FALSE; RETURN FALSE END;
intraSkipped := (mbAddress - mbAddressLastIntra) > 1;
IF intra THEN
CASE nr OF
0..3:
IF ~dequantizer.DequantizeIntraCoeffs(coeffs, intraQM, qScale, dcY, first, intraSkipped) THEN RETURN FALSE END;
| 4:
IF ~dequantizer.DequantizeIntraCoeffs(coeffs, intraQM, qScale, dcCb, first, intraSkipped) THEN RETURN FALSE END;
| 5:
IF ~dequantizer.DequantizeIntraCoeffs(coeffs, intraQM, qScale, dcCr, first, intraSkipped) THEN RETURN FALSE END;
END;
ELSE
IF ~dequantizer.DequantizeNonintraCoeffs(coeffs, nonintraQM, qScale) THEN RETURN FALSE END;
END;
idct.PerformIDCT(coeffs);
IF macroblockNr = -1 THEN KernelLog.String("Block decoded"); KernelLog.Ln; END;
RETURN TRUE;
END DecodeBlock;
PROCEDURE DecodeBlock2(
nr: SHORTINT;
coeffs:Util.PointerToArrayOfLONGINT;
intra: BOOLEAN;
VAR qScale: LONGINT;
first: BOOLEAN;
type: LONGINT): BOOLEAN;
VAR
bits: LONGINT;
size: LONGINT;
dcDiff: LONGINT;
dcPrediction: LONGINT;
coeffsPos: LONGINT;
dummy: BOOLEAN;
BEGIN
blocks.ClearBlockLongint(coeffs);
IF intra THEN
bits := stream.ShowBits(3);
IF nr < 4 THEN
IF bits = 7 THEN
stream.SkipBits(3);
size := 4;
WHILE stream.ShowBits(1) = 1 DO
INC(size);
stream.SkipBits(1);
END;
INC(size);
stream.SkipBits(1);
ELSE
size := MPEGTables.DCL3[bits][0];
stream.SkipBits(MPEGTables.DCL3[bits][1]);
END;
ELSE
IF bits = 7 THEN
stream.SkipBits(3);
size := 3;
WHILE stream.ShowBits(1) = 1 DO
INC(size);
stream.SkipBits(1);
END;
INC(size);
stream.SkipBits(1);
ELSE
size := MPEGTables.DCC3[bits][0];
stream.SkipBits(MPEGTables.DCC3[bits][1]);
END;
END;
IF size = 0 THEN
dcDiff := 0;
ELSE
IF stream.ShowBits(1) = 0 THEN
bits := stream.GetBits(size);
IF bits < 0 THEN hasMoreFrames := FALSE; RETURN FALSE END;
dcDiff := -(SYSTEM.VAL(INTEGER, ((-SYSTEM.VAL(SET, bits)) * {0..(size-1)})));
ELSE
dcDiff := stream.GetBits(size);
IF dcDiff < 0 THEN hasMoreFrames := FALSE; RETURN FALSE END;
END;
END;
IF (mbSkipped OR ((mbAddress - mbAddressLastIntra) > 1)) & first THEN
CASE nr OF
0..3:
dcY := MPEGTables.DCP[picExt.dcPrecision];
| 4:
dcCb := MPEGTables.DCP[picExt.dcPrecision];
| 5:
dcCr := MPEGTables.DCP[picExt.dcPrecision];
END;
END;
CASE nr OF
0..3:
dcPrediction := dcY;
| 4:
dcPrediction := dcCb;
| 5:
dcPrediction := dcCr;
END;
coeffs[0] := dcPrediction + dcDiff;
CASE nr OF
0..3:
dcY := coeffs[0];
| 4:
dcCb := coeffs[0];
| 5:
dcCr := coeffs[0];
END;
coeffsPos := 1;
ELSE
IF stream.ShowBits(1) = 1 THEN
IF stream.ShowBits(2) = 2 THEN
coeffs[0] := 1;
ELSE
coeffs[0] := -1;
END;
stream.SkipBits(2);
coeffsPos := 1;
ELSE
dummy := reader.ReadRunLevelCode(coeffs, coeffsPos, MPEG2);
IF reader.eof THEN hasMoreFrames := FALSE; RETURN FALSE END;
END;
END;
IF ~picExt.intraVlcFormat OR ~intra THEN
WHILE ~reader.ReadRunLevelCode(coeffs, coeffsPos, MPEG2) DO END;
ELSE
WHILE ~reader.ReadRunLevelCode2(coeffs, coeffsPos) DO END;
END;
IF reader.eof THEN hasMoreFrames := FALSE; RETURN FALSE END;
IF picExt.alternateScan THEN
HALT(1234);
END;
IF intra THEN
dequantizer.DequantizeIntraCoeffs2(coeffs, intraQM, qScale, picExt.dcPrecision);
ELSE
dequantizer.DequantizeNonintraCoeffs2(coeffs, nonintraQM, qScale);
END;
idct.PerformIDCT(coeffs);
RETURN TRUE;
END DecodeBlock2;
PROCEDURE Render*(img : Raster.Image);
BEGIN
yuv2rgb.Convert(
nextFrameToRender.buffer, 0, videoWidth, videoWidth * videoHeight,
videoWidth * videoHeight + videoWidthDiv2 * videoHeightDiv2, videoWidthDiv2, img, videoWidth, videoHeight,
videoWidth);
END Render;
END MPEGVideoDecoder;
MPEGDemultiplexer* = OBJECT(Codec.AVDemultiplexer);
VAR
input: Streams.Reader;
bytesRead: LONGINT;
streams: ARRAY 64 OF POINTER TO StreamType;
nextStreamNr: LONGINT;
singleStream: BOOLEAN;
PROCEDURE &Init*;
BEGIN
nextStreamNr := 0;
END Init;
PROCEDURE Open*(in : Streams.Reader; VAR res : LONGINT);
VAR
oldPos, i: LONGINT;
startCode: CHAR;
BEGIN
input := in;
res := Codec.ResFailed;
IF ~in.CanSetPos() THEN RETURN END;
IF ~GotoNextStartCode() THEN RETURN END;
startCode := input.Get();
IF startCode = SCSequenceHeader THEN
singleStream := TRUE;
NEW(streams[0]);
NEW(streams[0].stream, SELF, 0);
streams[0].idByte := startCode;
streams[0].pos := 0;
streams[0].bytesLeftInPacket := -1;
streams[0].eos := FALSE;
nextStreamNr := -1;
input.SetPos(0);
res := Codec.ResOk;
RETURN;
ELSIF startCode # SCPack THEN
RETURN;
END;
IF ~ReadPackHeader() THEN RETURN END;
IF ~GotoNextStartCode() OR (input.Get() # SCSystemHeader) THEN RETURN END;
IF ~ReadSystemHeader() THEN RETURN END;
oldPos := input.Pos();
FOR i := 0 TO 20 DO
IF ~GotoNextStartCode() THEN HALT(1234) END;
startCode := input.Get();
IF startCode = SCSystemHeader THEN
IF ~ReadSystemHeader() THEN HALT(1234) END;
ELSIF startCode = SCPack THEN
IF ~ReadPackHeader() THEN HALT(1234) END;
ELSIF ((ORD(startCode) >= 0BCH) & (ORD(startCode) <= 0FFH)) THEN
input.SkipBytes(ORD(input.Get()) * 100H + ORD(input.Get()));
ELSE
HALT(1234);
END;
END;
input.SetPos(oldPos);
res := Codec.ResOk;
END Open;
PROCEDURE GotoNextStartCode(): BOOLEAN;
BEGIN
IF SkipZeros() < 2 THEN RETURN FALSE END;
RETURN input.Get() = CHR(1);
END GotoNextStartCode;
PROCEDURE SkipZeros(): LONGINT;
VAR
count: LONGINT;
BEGIN
WHILE (input.Peek() = CHR(0)) & ~(input.res = Streams.EOF) DO
input.SkipBytes(1);
INC(count);
END;
IF input.res = Streams.EOF THEN
RETURN -1;
ELSE
RETURN count;
END;
END SkipZeros;
PROCEDURE ReadPackHeader(): BOOLEAN;
VAR
peek: LONGINT;
stuffBytes: LONGINT;
buffer: ARRAY 8 OF CHAR;
BEGIN
peek := ORD(input.Peek());
IF (peek >= 32) & (peek < 48) THEN
input.Bytes(buffer, 0, 8, bytesRead);
IF (input.res # Streams.Ok) OR (bytesRead < 8) THEN RETURN FALSE END;
RETURN TRUE;
ELSIF (peek >= 64) & (peek < 128) THEN
input.SkipBytes(9);
stuffBytes := ORD(input.Get()) MOD 8;
input.SkipBytes(stuffBytes);
RETURN (input.res = Streams.Ok);
ELSE
RETURN FALSE;
END;
END ReadPackHeader;
PROCEDURE ReadSystemHeader(): BOOLEAN;
VAR
headerLength: LONGINT;
buffer: ARRAY 8 OF CHAR;
BEGIN
input.Bytes(buffer, 0, 8, bytesRead);
IF (input.res # Streams.Ok) OR (bytesRead < 8) THEN RETURN FALSE END;
headerLength := ORD(buffer[0]) * 256 + ORD(buffer[1]) - 6;
WHILE ORD(input.Peek()) > 127 DO
input.Bytes(buffer, 0, 3, bytesRead);
IF (input.res # Streams.Ok) OR (bytesRead < 3) THEN RETURN FALSE END;
IF isNewStream(buffer[0]) THEN
NEW(streams[nextStreamNr]);
NEW(streams[nextStreamNr].stream, SELF, nextStreamNr);
streams[nextStreamNr].idByte := buffer[0];
streams[nextStreamNr].pos := -1;
streams[nextStreamNr].eos := FALSE;
INC(nextStreamNr);
END;
END;
RETURN TRUE;
END ReadSystemHeader;
PROCEDURE isNewStream(id: CHAR): BOOLEAN;
VAR
i: LONGINT;
BEGIN
FOR i := 0 TO (nextStreamNr-1) DO
IF streams[i].idByte = id THEN RETURN FALSE END;
END;
RETURN TRUE;
END isNewStream;
PROCEDURE GetNumberOfStreams*() : LONGINT;
BEGIN
IF singleStream THEN
RETURN 1;
ELSE
RETURN nextStreamNr;
END;
END GetNumberOfStreams;
PROCEDURE GetStreamType*(streamNr : LONGINT): LONGINT;
VAR
streamid: LONGINT;
BEGIN
IF streams[streamNr] # NIL THEN
streamid := ORD(streams[streamNr].idByte);
CASE streamid OF
0BCH..0BFH:
RETURN Codec.STUnknown;
| 0C0H..0DFH:
RETURN Codec.STAudio;
| 0E0H..0EFH:
RETURN Codec.STVideo;
| 0F0H..0FFH:
KernelLog.String("Stream-ID: "); KernelLog.Hex(streamid, 0); KernelLog.Ln;
RETURN Codec.STUnknown;
ELSE
KernelLog.String("Stream-ID: "); KernelLog.Hex(streamid, 0); KernelLog.Ln;
RETURN Codec.STUnknown;
END;
ELSE
RETURN Codec.STError;
END;
END GetStreamType;
PROCEDURE GetStream*(streamNr: LONGINT): Codec.DemuxStream;
BEGIN
IF streams[streamNr] # NIL THEN
RETURN streams[streamNr].stream;
ELSE
RETURN NIL;
END;
END GetStream;
PROCEDURE GetStreamInfo*(streamNr : LONGINT): Codec.AVStreamInfo;
VAR si : Codec.AVStreamInfo;
BEGIN
CASE GetStreamType(streamNr) OF
Codec.STAudio: COPY("MPEGAUDIO", si.contentType);
| Codec.STVideo: COPY("MPEG", si.contentType);
| Codec.STUnknown: COPY("UNKNOWN", si.contentType);
ELSE COPY("UNKNOWN", si.contentType);
END;
RETURN si
END GetStreamInfo;
PROCEDURE GetData*(streamNr : LONGINT; VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT);
VAR
cur: POINTER TO StreamType;
length: LONGINT;
offset: LONGINT;
BEGIN
IF singleStream THEN
IF streams[0].eos = TRUE THEN
RETURN;
END;
input.Bytes(buf, ofs, size, len);
res := input.res;
IF input.res = Streams.EOF THEN
streams[0].eos := TRUE;
END;
INC(streams[0].pos, len);
RETURN;
END;
cur := streams[streamNr];
len := size;
offset := ofs;
IF cur.eos = TRUE THEN
RETURN;
END;
IF cur.pos = -1 THEN
input.SetPos(-1);
IF ~GotoNextPacket(cur^) THEN res := Codec.ResFailed; RETURN END;
END;
input.SetPos(cur.pos);
WHILE (cur.bytesLeftInPacket < size) & ~cur.eos DO
input.Bytes(buf, offset, cur.bytesLeftInPacket, length);
INC(cur.pos, length);
DEC(size, cur.bytesLeftInPacket);
INC(offset, cur.bytesLeftInPacket);
IF ~GotoNextPacket(cur^) THEN
cur.eos := TRUE;
DEC(len, size);
RETURN;
END;
END;
res := Codec.ResOk;
IF cur.eos THEN
DEC(len, size);
ELSE
input.Bytes(buf, offset, size, length);
cur.pos := input.Pos();
DEC(cur.bytesLeftInPacket, length);
END;
END GetData;
PROCEDURE SkipData(streamNr : LONGINT; size: LONGINT; VAR len, res: LONGINT);
VAR
cur: POINTER TO StreamType;
BEGIN
IF singleStream THEN
input.SkipBytes(size);
res := Codec.ResOk;
INC(streams[0].pos, len);
RETURN;
END;
cur := streams[streamNr];
len := size;
IF cur.pos = -1 THEN
input.SetPos(cur.pos);
IF ~GotoNextPacket(cur^) THEN res := Codec.ResFailed; RETURN END;
END;
input.SetPos(cur.pos);
WHILE cur.bytesLeftInPacket < size DO
input.SkipBytes(cur.bytesLeftInPacket);
INC(cur.pos, cur.bytesLeftInPacket);
DEC(size, cur.bytesLeftInPacket);
IF ~GotoNextPacket(cur^) THEN
res := Codec.ResFailed;
RETURN;
END;
END;
input.SkipBytes(size);
cur.pos := input.Pos();
DEC(cur.bytesLeftInPacket, size);
END SkipData;
PROCEDURE GetPosInMuxedStream*(streamNr: LONGINT): LONGINT;
BEGIN
RETURN streams[streamNr].pos;
END GetPosInMuxedStream;
PROCEDURE GotoNextPacket(VAR stream: StreamType): BOOLEAN;
VAR
nextStartCode: CHAR;
length: LONGINT;
peekByte: CHAR;
flags: LONGINT;
optionsLength: LONGINT;
BEGIN
IF ~GotoNextStartCode() THEN RETURN FALSE END;
nextStartCode := input.Get();
WHILE (nextStartCode # stream.idByte) & (input.res # Streams.EOF) DO
IF nextStartCode = SCPack THEN
IF ~ReadPackHeader() THEN RETURN FALSE END;
ELSIF nextStartCode = SCSystemHeader THEN
IF ~ReadSystemHeader() THEN RETURN FALSE END;
ELSE
length := ORD(input.Get()) * 100H + ORD(input.Get());
input.SkipBytes(length);
END;
IF ~GotoNextStartCode() THEN RETURN FALSE END;
nextStartCode := input.Get();
END;
IF input.res = Streams.EOF THEN
RETURN FALSE;
END;
length := ORD(input.Get()) * 100H + ORD(input.Get());
IF nextStartCode # SCPrivateStream2 THEN
IF (ORD(input.Peek()) >= 128) & (ORD(input.Peek()) < 192) THEN
input.SkipBytes(1);
flags := ORD(input.Get());
optionsLength := ORD(input.Get());
input.SkipBytes(optionsLength);
DEC(length, optionsLength + 3);
ELSE
WHILE ORD(input.Peek()) = 0FFH DO
input.SkipBytes(1);
DEC(length);
END;
peekByte := input.Peek();
IF (ORD(peekByte) > 63) & (ORD(peekByte) <128) THEN
input.SkipBytes(2);
DEC(length, 2);
peekByte := input.Peek();
END;
IF (ORD(peekByte) > 31) & (ORD(peekByte) < 48) THEN
input.SkipBytes(5);
DEC(length, 5);
peekByte := input.Peek();
ELSIF (ORD(peekByte) > 47) & (ORD(peekByte) < 64) THEN
input.SkipBytes(10);
DEC(length, 10);
ELSE
input.SkipBytes(1);
DEC(length);
END;
END;
END;
stream.pos := input.Pos() + 1;
stream.bytesLeftInPacket := length;
RETURN TRUE;
END GotoNextPacket;
PROCEDURE SetStreamPos*(streamNr : LONGINT; seekType : LONGINT; pos : LONGINT; VAR itemSize : LONGINT; VAR res : LONGINT);
VAR
cur: POINTER TO StreamType;
len: LONGINT;
BEGIN
res := Codec.ResFailed;
IF seekType # Codec.SeekByte THEN
RETURN;
END;
itemSize := 1;
IF singleStream THEN
IF streamNr # 0 THEN RETURN END;
input.SetPos(pos);
streams[0].pos := pos;
res := Codec.ResOk;
RETURN;
END;
IF streamNr >= nextStreamNr THEN
RETURN
END;
cur := streams[streamNr];
IF (cur.stream.Pos()+cur.stream.Available()) > pos THEN
input.SetPos(-1);
IF ~GotoNextPacket(cur^) THEN HALT(1234); res := Codec.ResFailed; RETURN END;
SkipData(streamNr, pos, len, res);
ELSE
SkipData(streamNr, pos - (cur.stream.Pos()+cur.stream.Available()), len, res);
END;
END SetStreamPos;
PROCEDURE HasMoreData(streamNr: LONGINT): BOOLEAN;
BEGIN
RETURN ~streams[streamNr].eos;
END HasMoreData;
END MPEGDemultiplexer;
PROCEDURE GotoNextMarker(VAR stream: Util.BitStream; VAR marker: CHAR): BOOLEAN;
VAR
i: INTEGER;
DEBUG: BOOLEAN;
BEGIN
DEBUG := FALSE;
i := 0;
stream.ByteAlign();
WHILE (stream.ShowBits(24) # 1) DO
stream.SkipBits(8);
INC(i);
END;
marker := CHR(stream.ShowBits(32) MOD 256);
RETURN TRUE;
END GotoNextMarker;
PROCEDURE Sign(value: LONGINT): LONGINT;
BEGIN
IF value > 0 THEN
RETURN 1;
ELSIF value < 0 THEN
RETURN -1;
ELSE
RETURN 0;
END;
END Sign;
PROCEDURE DecoderFactory*() : Codec.VideoDecoder;
VAR p: MPEGVideoDecoder;
BEGIN
NEW(p);
RETURN p;
END DecoderFactory;
PROCEDURE DemuxFactory*() : Codec.AVDemultiplexer;
VAR d: MPEGDemultiplexer;
BEGIN
NEW(d);
RETURN d
END DemuxFactory;
PROCEDURE Test*(context : Commands.Context);
VAR
demux: MPEGDemultiplexer;
decoder: MPEGVideoDecoder;
file: Files.File;
fileinputstream: Codec.FileInputStream;
vstream: Codec.DemuxStream;
result: LONGINT;
i: LONGINT;
w, h, ms: LONGINT;
wnd: PW;
timer: Kernel.Timer;
milliTimer : Kernel.MilliTimer;
ticks: LONGINT;
filename:ARRAY 100 OF CHAR;
min, max, total: LONGINT;
minFrame, maxFrame: LONGINT;
BEGIN
context.arg.SkipWhitespace; context.arg.String(filename);
file := Files.Old(filename);
IF file = NIL THEN
context.error.String("Couldn't open File "); context.error.String(filename);
context.error.Ln();
RETURN;
END;
NEW(timer);
NEW(fileinputstream, file, 0);
NEW(demux);
demux.Open(fileinputstream, result);
IF result # Codec.ResOk THEN
context.error.String("error opening the demultiplexer"); context.error.Ln;
END;
vstream := demux.GetStream(0);
NEW(decoder);
decoder.Open(vstream, result);
IF result = Codec.ResOk THEN
decoder.GetVideoInfo(w, h, ms);
NEW(wnd, w, h, FALSE);
wnd.SetTitle(WM.NewString("Simple MPEG Player"));
FOR i := 0 TO 50 DO
Kernel.SetTimer(milliTimer, 0);
decoder.Next();
decoder.Render(wnd.backImg);
wnd.Swap();
wnd.Invalidate(Rectangles.MakeRect( 0, 0, wnd.backImg.width, wnd.backImg.height ) );
ticks := Kernel.Elapsed(milliTimer);
IF ticks < min THEN
min := ticks;
minFrame := i;
ELSIF ticks > max THEN
max := ticks;
maxFrame := i;
END;
INC(total, ticks);
END;
context.out.String("Finished decoding "); context.out.Int(i, 0); context.out.String(" Frames (min/avg/max): ");
context.out.Int(min, 0); context.out.String(" (Frame "); context.out.Int(minFrame, 0); context.out.String(") / ");
context.out.Int(total DIV i, 0); context.out.String(" /"); context.out.Int(max, 0); context.out.String(" (Frame "); context.out.Int(maxFrame, 0);
context.out.String(")"); context.out.Ln;
END;
END Test;
END MPEGVideoDecoder.
SystemTools.Free MPEGVideoDecoder ~
MPEGVideoDecoder.Test beauty.mpg~
(* end of file *)