1006 lines
28 KiB
ObjectPascal
1006 lines
28 KiB
ObjectPascal
unit PrecompMedia;
|
|
|
|
{$POINTERMATH ON}
|
|
|
|
interface
|
|
|
|
uses
|
|
BrunsliDLL, FLACDLL, PackJPGDLL, JoJpegDLL, XDeltaDLL,
|
|
Utils,
|
|
PrecompUtils,
|
|
System.SysUtils, System.Classes, System.Math;
|
|
|
|
var
|
|
Codec: TPrecompressor;
|
|
|
|
implementation
|
|
|
|
const
|
|
MediaCodecs: array of PChar = ['flac', 'packjpg', 'brunsli', 'jojpeg'];
|
|
CODEC_COUNT = 4;
|
|
FLAC_CODEC = 0;
|
|
PACKJPG_CODEC = 1;
|
|
BRUNSLI_CODEC = 2;
|
|
JOJPEG_CODEC = 3;
|
|
|
|
const
|
|
FLAC_LEVEL = 5;
|
|
J_WORKMEM = 262144;
|
|
|
|
type
|
|
PFlacEncCD = ^TFlacEncCD;
|
|
|
|
TFlacEncCD = record
|
|
Output: TMemoryStreamEx;
|
|
Error: Boolean;
|
|
end;
|
|
|
|
PFlacDecCD = ^TFlacDecCD;
|
|
|
|
TFlacDecCD = record
|
|
Input, Output: TMemoryStreamEx;
|
|
end;
|
|
|
|
var
|
|
cctx, dctx: array of Pointer;
|
|
ccd: array of TFlacEncCD;
|
|
dcd: array of TFlacDecCD;
|
|
JJInst: TArray<Pointer>;
|
|
FlacLevel: Integer = FLAC_LEVEL;
|
|
CodecAvailable, CodecEnabled: TArray<Boolean>;
|
|
RestoreOut: TArray<_PrecompOutput>;
|
|
RestoreSize: TArray<Integer>;
|
|
|
|
const
|
|
RIFF_SIGN = $46464952;
|
|
WAVE_SIGN = $45564157;
|
|
fmt_SIGN = $20746D66;
|
|
data_SIGN = $61746164;
|
|
|
|
WAVE_FORMAT_PCM = 1;
|
|
WAVE_FORMAT_EXTENSIBLE = $FFFE;
|
|
|
|
type
|
|
PRIFF_hdr = ^TRIFF_hdr;
|
|
|
|
TRIFF_hdr = packed record
|
|
chunk_id, chunk_size, form_type: Cardinal;
|
|
end;
|
|
|
|
PWAVE_hdr = ^TWAVE_hdr;
|
|
|
|
TWAVE_hdr = packed record
|
|
audio_format, num_channels: Word;
|
|
sample_rate, byte_rate: Cardinal;
|
|
block_align, bits_per_sample: Word;
|
|
end;
|
|
|
|
PWAVE_subchunk_hdr = ^TWAVE_subchunk_hdr;
|
|
|
|
TWAVE_subchunk_hdr = packed record
|
|
subchunk_id, subchunk_size: Cardinal;
|
|
end;
|
|
|
|
PWAVE_subformat = ^TWAVE_subformat;
|
|
|
|
TWAVE_subformat = packed record
|
|
f1: Cardinal;
|
|
f2, f3: Word;
|
|
f4: array [0 .. 7] of Byte;
|
|
end;
|
|
|
|
PWAVE_ext_hdr = ^TWAVE_ext_hdr;
|
|
|
|
TWAVE_ext_hdr = packed record
|
|
cb_size, valid_bits: Word;
|
|
ch_mask: Cardinal;
|
|
est: TWAVE_subformat;
|
|
end;
|
|
|
|
function GetWAVEInfo(InBuff: PByte; InSize: NativeInt; wave_hdr: PWAVE_hdr;
|
|
chunk_size, header_size: PCardinal): Boolean;
|
|
var
|
|
Pos, LastPos: NativeInt;
|
|
riffhdr: TRIFF_hdr;
|
|
subchunk_hdr: TWAVE_subchunk_hdr;
|
|
wave_hdr_ex: TWAVE_ext_hdr;
|
|
begin
|
|
Result := False;
|
|
Pos := 0;
|
|
if InSize - Pos < SizeOf(TRIFF_hdr) then
|
|
exit;
|
|
Move((InBuff + Pos)^, riffhdr, SizeOf(TRIFF_hdr));
|
|
Inc(Pos, SizeOf(TRIFF_hdr));
|
|
if (riffhdr.chunk_id <> RIFF_SIGN) or (riffhdr.form_type <> WAVE_SIGN) then
|
|
exit;
|
|
while True do
|
|
begin
|
|
if InSize - Pos < SizeOf(TWAVE_subchunk_hdr) then
|
|
exit;
|
|
Move((InBuff + Pos)^, subchunk_hdr, SizeOf(TWAVE_subchunk_hdr));
|
|
Inc(Pos, SizeOf(TWAVE_subchunk_hdr));
|
|
if subchunk_hdr.subchunk_id = fmt_SIGN then
|
|
begin
|
|
LastPos := Pos;
|
|
if InSize - Pos < SizeOf(TWAVE_hdr) then
|
|
exit;
|
|
Move((InBuff + Pos)^, wave_hdr^, SizeOf(TWAVE_hdr));
|
|
Inc(Pos, SizeOf(TWAVE_hdr));
|
|
if wave_hdr^.audio_format = WAVE_FORMAT_EXTENSIBLE then
|
|
begin
|
|
if InSize - Pos < SizeOf(TWAVE_ext_hdr) then
|
|
exit;
|
|
Move((InBuff + Pos)^, wave_hdr_ex, SizeOf(TWAVE_ext_hdr));
|
|
Inc(Pos, SizeOf(TWAVE_ext_hdr));
|
|
wave_hdr^.audio_format := wave_hdr_ex.est.f1;
|
|
end
|
|
else if wave_hdr^.audio_format <> WAVE_FORMAT_PCM then
|
|
exit;
|
|
Pos := LastPos + subchunk_hdr.subchunk_size;
|
|
end
|
|
else if subchunk_hdr.subchunk_id = data_SIGN then
|
|
begin
|
|
Result := True;
|
|
chunk_size^ := subchunk_hdr.subchunk_size;
|
|
header_size^ := Pos;
|
|
exit;
|
|
end
|
|
else
|
|
Inc(Pos, subchunk_hdr.subchunk_size);
|
|
end;
|
|
end;
|
|
|
|
function FlacEncoderWriteCB(encoder: Pointer; const Buffer; bytes: NativeUInt;
|
|
samples, current_frame: Cardinal; client_data: PFlacEncCD): Integer cdecl;
|
|
begin
|
|
try
|
|
client_data^.Output.WriteBuffer(Buffer, bytes);
|
|
except
|
|
client_data^.Error := True;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
function FlacDecoderReadCB(decoder: Pointer; var Buffer; bytes: PNativeUInt;
|
|
client_data: PFlacDecCD): Integer cdecl;
|
|
begin
|
|
bytes^ := client_data^.Input.Read(Buffer, bytes^);
|
|
if bytes^ > 0 then
|
|
Result := 0
|
|
else
|
|
Result := 1;
|
|
end;
|
|
|
|
function FlacDecoderWriteCB(decoder: Pointer; frame: PFLAC__Frame;
|
|
Buffer: Pointer; client_data: PFlacDecCD): Integer cdecl;
|
|
var
|
|
I, J, X: Integer;
|
|
begin
|
|
for I := 0 to frame^.header.blocksize - 1 do
|
|
for J := 0 to frame^.header.channels - 1 do
|
|
begin
|
|
X := PInteger(PNativeInt(Buffer)[J])[I];
|
|
client_data^.Output.WriteBuffer(X, frame^.header.bits_per_sample div 8);
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure FlacDecoderErrorCB(decoder: Pointer; Status: Integer;
|
|
client_data: Pointer)cdecl;
|
|
begin
|
|
|
|
end;
|
|
|
|
function FlacEncode(ctx: Pointer; cd: PFlacEncCD; InBuff: PByte;
|
|
InSize: Integer; OutBuff: PByte; OutSize: Integer;
|
|
Level: Integer = 5): Integer;
|
|
const
|
|
READSIZE = 1000;
|
|
var
|
|
I, J, X: Integer;
|
|
Ptr: PByte;
|
|
wave_hdr: TWAVE_hdr;
|
|
byte_per_sample: Word;
|
|
data_size, hdr_size, smp_size: Cardinal;
|
|
ok: Boolean;
|
|
pcm: TArray<Integer>;
|
|
begin
|
|
Result := 0;
|
|
ok := False;
|
|
if GetWAVEInfo(InBuff, InSize, @wave_hdr, @data_size, @hdr_size) then
|
|
begin
|
|
ok := True;
|
|
byte_per_sample := (wave_hdr.bits_per_sample + 7) div 8;
|
|
smp_size := wave_hdr.num_channels * byte_per_sample;
|
|
ok := ok and FLAC__stream_encoder_set_verify(ctx, True);
|
|
ok := ok and FLAC__stream_encoder_set_compression_level(ctx, Level);
|
|
ok := ok and FLAC__stream_encoder_set_channels(ctx, wave_hdr.num_channels);
|
|
ok := ok and FLAC__stream_encoder_set_bits_per_sample(ctx,
|
|
wave_hdr.bits_per_sample);
|
|
ok := ok and FLAC__stream_encoder_set_sample_rate(ctx,
|
|
wave_hdr.sample_rate);
|
|
end;
|
|
if ok then
|
|
try
|
|
cd^.Error := False;
|
|
cd^.Output.Update(OutBuff, OutSize);
|
|
cd^.Output.Size := 0;
|
|
if FLAC__stream_encoder_init_stream(ctx, @FlacEncoderWriteCB, nil, nil,
|
|
nil, cd) = 0 then
|
|
begin
|
|
Ptr := InBuff + hdr_size;
|
|
SetLength(pcm, READSIZE * wave_hdr.num_channels);
|
|
while True do
|
|
begin
|
|
X := 0;
|
|
for I := 0 to READSIZE - 1 do
|
|
begin
|
|
if NativeInt(Ptr + smp_size - InBuff) > hdr_size + data_size then
|
|
break;
|
|
Inc(X);
|
|
for J := 0 to wave_hdr.num_channels - 1 do
|
|
begin
|
|
if NativeInt(Ptr - InBuff) > hdr_size + data_size then
|
|
break;
|
|
pcm[I * wave_hdr.num_channels + J] :=
|
|
GetBits(PInteger(Ptr)^, 0, Pred(wave_hdr.bits_per_sample)) -
|
|
IfThen(GetBits(PInteger(Ptr)^, Pred(wave_hdr.bits_per_sample),
|
|
1) = 0, 0, 1 shl Pred(wave_hdr.bits_per_sample));
|
|
Inc(Ptr, byte_per_sample);
|
|
end;
|
|
end;
|
|
ok := ok and FLAC__stream_encoder_process_interleaved(ctx, pcm[0], X);
|
|
if (NativeInt(Ptr + smp_size - InBuff) > hdr_size + data_size) or
|
|
(ok = False) then
|
|
break;
|
|
end;
|
|
end;
|
|
finally
|
|
FLAC__stream_encoder_finish(ctx);
|
|
if ok and (cd^.Error = False) then
|
|
Result := cd^.Output.Size;
|
|
end;
|
|
end;
|
|
|
|
function FlacDecode(ctx: Pointer; cd: PFlacDecCD; InBuff: PByte;
|
|
InSize: Integer; OutBuff: PByte; OutSize: Integer): Integer;
|
|
var
|
|
ok: Boolean;
|
|
begin
|
|
Result := 0;
|
|
ok := False;
|
|
cd^.Input.Update(InBuff, InSize);
|
|
cd^.Input.Position := 0;
|
|
cd^.Input.Size := InSize;
|
|
cd^.Output.Update(OutBuff, OutSize);
|
|
cd^.Output.Size := 0;
|
|
try
|
|
if FLAC__stream_decoder_init_stream(ctx, @FlacDecoderReadCB, nil, nil, nil,
|
|
nil, @FlacDecoderWriteCB, nil, @FlacDecoderErrorCB, cd) = 0 then
|
|
begin
|
|
ok := True;
|
|
ok := ok and FLAC__stream_decoder_process_until_end_of_stream(ctx);
|
|
end;
|
|
finally
|
|
FLAC__stream_decoder_finish(ctx);
|
|
if ok then
|
|
Result := cd^.Output.Size;
|
|
end;
|
|
end;
|
|
|
|
const
|
|
JPG_HEADER = $D8FF;
|
|
JPG_FOOTER = $D9FF;
|
|
|
|
function GetJPEGInfo(InBuff: PByte; InSize: NativeInt; ImageSize: PCardinal;
|
|
IsProgressive: PBoolean): Boolean;
|
|
// https://github.com/schnaader/precomp-cpp/blob/master/precomp.cpp
|
|
var
|
|
Pos: NativeInt;
|
|
hasQuantTable: Boolean;
|
|
done, found: Boolean;
|
|
length: Integer;
|
|
isMarker: Boolean;
|
|
bytesRead: Cardinal;
|
|
begin
|
|
Result := False;
|
|
Pos := 0;
|
|
done := False;
|
|
found := False;
|
|
if (PWord(InBuff)^ = JPG_HEADER) and ((InBuff + 2)^ = $FF) then
|
|
begin
|
|
Pos := 3;
|
|
if (InBuff + Pos)^ in [$C0, $C2, $C4, $DB .. $FE] then
|
|
begin
|
|
hasQuantTable := (InBuff + Pos)^ = $DB;
|
|
IsProgressive^ := (InBuff + Pos)^ = $C2;
|
|
Pos := 2;
|
|
repeat
|
|
if (Pos + 5 > InSize) or ((InBuff + Pos)[0] <> $FF) then
|
|
break;
|
|
length := Integer((InBuff + Pos)[2]) * 256 + Integer((InBuff + Pos)[3]);
|
|
case (InBuff + Pos)[1] of
|
|
$DB:
|
|
begin
|
|
if (length <= 262) and (((length - 2) mod 65) = 0) and
|
|
((InBuff + Pos)[4] <= 3) then
|
|
begin
|
|
hasQuantTable := True;
|
|
Inc(Pos, length + 2);
|
|
end
|
|
else
|
|
done := True;
|
|
end;
|
|
$C4:
|
|
begin
|
|
done := (((InBuff + Pos)[4] and $F) > 3) or
|
|
(((InBuff + Pos)[4] shr 4) > 1);
|
|
Inc(Pos, length + 2);
|
|
end;
|
|
$DA:
|
|
begin
|
|
found := hasQuantTable;
|
|
Inc(Pos, length + 2);
|
|
end;
|
|
$D9:
|
|
done := True;
|
|
$C2:
|
|
begin
|
|
IsProgressive^ := True;
|
|
Inc(Pos, length + 2);
|
|
end;
|
|
$C0:
|
|
begin
|
|
done := (InBuff + Pos)[4] <> $08;
|
|
Inc(Pos, length + 2);
|
|
end;
|
|
else
|
|
Inc(Pos, length + 2);
|
|
end;
|
|
until done;
|
|
if found then
|
|
while Pos < InSize - 1 do
|
|
begin
|
|
if PWord(InBuff + Pos)^ = JPG_FOOTER then
|
|
begin
|
|
ImageSize^ := Pos + 2;
|
|
Result := True;
|
|
break;
|
|
end;
|
|
Inc(Pos);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function BrunsliWriter(cd: Pointer; data: Pointer; Size: NativeUInt)
|
|
: Integer cdecl;
|
|
begin
|
|
RestoreOut[PInteger(cd)^](PInteger(cd)^, data, Size);
|
|
Inc(RestoreSize[PInteger(cd)^], Size);
|
|
Result := Size;
|
|
end;
|
|
|
|
function MediaInit(Command: PChar; Count: Integer;
|
|
Funcs: PPrecompFuncs): Boolean;
|
|
var
|
|
I: Integer;
|
|
Options: TArray<Integer>;
|
|
S: String;
|
|
X, Y: Integer;
|
|
begin
|
|
Result := True;
|
|
SetLength(RestoreOut, Count);
|
|
SetLength(RestoreSize, Count);
|
|
for X := Low(CodecAvailable) to High(CodecAvailable) do
|
|
begin
|
|
CodecAvailable[X] := False;
|
|
CodecEnabled[X] := False;
|
|
end;
|
|
CodecAvailable[FLAC_CODEC] := FLACDLL.DLLLoaded;
|
|
CodecAvailable[PACKJPG_CODEC] := PackJPGDLL.DLLLoaded;
|
|
CodecAvailable[BRUNSLI_CODEC] := BrunsliDLL.DLLLoaded;
|
|
CodecAvailable[JOJPEG_CODEC] := JoJpegDLL.DLLLoaded;
|
|
X := 0;
|
|
while Funcs^.GetCodec(Command, X, False) <> '' do
|
|
begin
|
|
S := Funcs^.GetCodec(Command, X, False);
|
|
if (CompareText(S, MediaCodecs[FLAC_CODEC]) = 0) and FLACDLL.DLLLoaded then
|
|
begin
|
|
CodecEnabled[FLAC_CODEC] := True;
|
|
if Funcs^.GetParam(Command, X, 'l') <> '' then
|
|
FlacLevel := StrToInt(Funcs^.GetParam(Command, X, 'l'));
|
|
end
|
|
else if (CompareText(S, MediaCodecs[PACKJPG_CODEC]) = 0) and PackJPGDLL.DLLLoaded
|
|
then
|
|
CodecEnabled[PACKJPG_CODEC] := True
|
|
else if (CompareText(S, MediaCodecs[BRUNSLI_CODEC]) = 0) and BrunsliDLL.DLLLoaded
|
|
then
|
|
CodecEnabled[BRUNSLI_CODEC] := True
|
|
else if (CompareText(S, MediaCodecs[JOJPEG_CODEC]) = 0) and JoJpegDLL.DLLLoaded
|
|
then
|
|
CodecEnabled[JOJPEG_CODEC] := True;;
|
|
Inc(X);
|
|
end;
|
|
if CodecAvailable[FLAC_CODEC] then
|
|
begin
|
|
SetLength(cctx, Count);
|
|
SetLength(dctx, Count);
|
|
SetLength(ccd, Count);
|
|
SetLength(dcd, Count);
|
|
for X := Low(cctx) to High(cctx) do
|
|
begin
|
|
cctx[X] := FLAC__stream_encoder_new;
|
|
dctx[X] := FLAC__stream_decoder_new;
|
|
ccd[X].Output := TMemoryStreamEx.Create(False);
|
|
dcd[X].Input := TMemoryStreamEx.Create(False);
|
|
dcd[X].Output := TMemoryStreamEx.Create(False);
|
|
end;
|
|
end;
|
|
if CodecAvailable[JOJPEG_CODEC] then
|
|
begin
|
|
SetLength(JJInst, Count);
|
|
for X := Low(JJInst) to High(JJInst) do
|
|
JJInst[X] := GetMemory(jojpeg_Size);
|
|
end;
|
|
end;
|
|
|
|
procedure MediaFree(Funcs: PPrecompFuncs);
|
|
var
|
|
X: Integer;
|
|
begin
|
|
if CodecAvailable[FLAC_CODEC] then
|
|
begin
|
|
for X := Low(cctx) to High(cctx) do
|
|
begin
|
|
FLAC__stream_encoder_delete(cctx[X]);
|
|
FLAC__stream_decoder_delete(dctx[X]);
|
|
ccd[X].Output.Free;
|
|
dcd[X].Input.Free;
|
|
dcd[X].Output.Free;
|
|
end;
|
|
end;
|
|
if CodecAvailable[JOJPEG_CODEC] then
|
|
for X := Low(JJInst) to High(JJInst) do
|
|
FreeMemory(JJInst[X]);
|
|
end;
|
|
|
|
function MediaParse(Command: PChar; Option: PInteger;
|
|
Funcs: PPrecompFuncs): Boolean;
|
|
var
|
|
S: String;
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
Option^ := 0;
|
|
I := 0;
|
|
while Funcs^.GetCodec(Command, I, False) <> '' do
|
|
begin
|
|
S := Funcs^.GetCodec(Command, I, False);
|
|
if (CompareText(S, MediaCodecs[FLAC_CODEC]) = 0) and FLACDLL.DLLLoaded then
|
|
begin
|
|
SetBits(Option^, FLAC_CODEC, 0, 5);
|
|
Result := True;
|
|
end
|
|
else if (CompareText(S, MediaCodecs[PACKJPG_CODEC]) = 0) and PackJPGDLL.DLLLoaded
|
|
then
|
|
begin
|
|
SetBits(Option^, PACKJPG_CODEC, 0, 5);
|
|
Result := True;
|
|
end
|
|
else if (CompareText(S, MediaCodecs[BRUNSLI_CODEC]) = 0) and BrunsliDLL.DLLLoaded
|
|
then
|
|
begin
|
|
SetBits(Option^, BRUNSLI_CODEC, 0, 5);
|
|
Result := True;
|
|
end
|
|
else if (CompareText(S, MediaCodecs[JOJPEG_CODEC]) = 0) and JoJpegDLL.DLLLoaded
|
|
then
|
|
begin
|
|
SetBits(Option^, JOJPEG_CODEC, 0, 5);
|
|
Result := True;
|
|
end;
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
procedure MediaScan1(Instance, Depth: Integer; Input: PByte;
|
|
Size, SizeEx: NativeInt; Output: _PrecompOutput; Add: _PrecompAdd;
|
|
Funcs: PPrecompFuncs);
|
|
var
|
|
Buffer: PByte;
|
|
Pos: NativeInt;
|
|
X, Y, Z: Integer;
|
|
SI: _StrInfo1;
|
|
DI1, DI2: TDepthInfo;
|
|
DS: TPrecompStr;
|
|
wave_hdr: TWAVE_hdr;
|
|
data_size, hdr_size: Cardinal;
|
|
progressive: Boolean;
|
|
begin
|
|
DI1 := Funcs^.GetDepthInfo(Instance);
|
|
DS := Funcs^.GetCodec(DI1.Codec, 0, False);
|
|
if DS <> '' then
|
|
begin
|
|
X := IndexTextW(@DS[0], MediaCodecs);
|
|
if (X < 0) or (DI1.OldSize <> SizeEx) then
|
|
exit;
|
|
if not CodecAvailable[X] then
|
|
exit;
|
|
Y := DI1.OldSize;
|
|
case X of
|
|
FLAC_CODEC:
|
|
begin
|
|
if GetWAVEInfo(Input, Y, @wave_hdr, @data_size, @hdr_size) then
|
|
Y := data_size + hdr_size
|
|
else
|
|
Y := 0;
|
|
end;
|
|
PACKJPG_CODEC, BRUNSLI_CODEC, JOJPEG_CODEC:
|
|
begin
|
|
if GetJPEGInfo(Input, Y, @data_size, @progressive) then
|
|
Y := data_size
|
|
else
|
|
Y := 0;
|
|
end;
|
|
else
|
|
Y := 0;
|
|
end;
|
|
if Y > 0 then
|
|
begin
|
|
Z := data_size;
|
|
if X = FLAC_CODEC then
|
|
begin
|
|
Buffer := Funcs^.Allocator(Instance, hdr_size);
|
|
Move(Input^, Buffer^, hdr_size);
|
|
Inc(PInteger(Buffer)^);
|
|
Output(Instance, @hdr_size, hdr_size.Size);
|
|
Output(Instance, Buffer, hdr_size);
|
|
Output(Instance, Input + hdr_size, Z);
|
|
Inc(Z, hdr_size.Size);
|
|
Inc(Z, hdr_size);
|
|
end
|
|
else
|
|
Output(Instance, Input, Z);
|
|
SI.Position := 0;
|
|
SI.OldSize := Y;
|
|
SI.NewSize := Z;
|
|
SI.Option := 0;
|
|
SetBits(SI.Option, X, 0, 5);
|
|
if System.Pos(SPrecompSep2, DI1.Codec) > 0 then
|
|
SI.Status := TStreamStatus.Predicted
|
|
else
|
|
SI.Status := TStreamStatus.None;
|
|
DS := Funcs^.GetDepthCodec(DI1.Codec);
|
|
Move(DS[0], DI2.Codec, SizeOf(DI2.Codec));
|
|
DI2.OldSize := SI.NewSize;
|
|
DI2.NewSize := SI.NewSize;
|
|
Funcs^.LogScan1(MediaCodecs[GetBits(SI.Option, 0, 5)], SI.Position,
|
|
SI.OldSize, -1);
|
|
Add(Instance, @SI, DI1.Codec, @DI2);
|
|
end;
|
|
exit;
|
|
end;
|
|
if BoolArray(CodecEnabled, False) then
|
|
exit;
|
|
Pos := 0;
|
|
while Pos < Size do
|
|
begin
|
|
if CodecEnabled[FLAC_CODEC] and (PCardinal(Input + Pos)^ = RIFF_SIGN) then
|
|
begin
|
|
Y := SizeEx - Pos;
|
|
if GetWAVEInfo(Input + Pos, Y, @wave_hdr, @data_size, @hdr_size) then
|
|
Y := data_size + hdr_size
|
|
else
|
|
Y := 0;
|
|
if Y > 0 then
|
|
begin
|
|
Z := data_size;
|
|
Buffer := Funcs^.Allocator(Instance, hdr_size);
|
|
Move((Input + Pos)^, Buffer^, hdr_size);
|
|
Inc(PInteger(Buffer)^);
|
|
Output(Instance, @hdr_size, hdr_size.Size);
|
|
Output(Instance, Buffer, hdr_size);
|
|
Output(Instance, Input + Pos + hdr_size, Z);
|
|
Inc(Z, hdr_size.Size);
|
|
Inc(Z, hdr_size);
|
|
SI.Position := Pos;
|
|
SI.OldSize := Y;
|
|
SI.NewSize := Z;
|
|
SI.Option := 0;
|
|
SetBits(SI.Option, FLAC_CODEC, 0, 5);
|
|
SI.Status := TStreamStatus.None;
|
|
Funcs^.LogScan1(MediaCodecs[GetBits(SI.Option, 0, 5)], SI.Position,
|
|
SI.OldSize, -1);
|
|
Add(Instance, @SI, nil, nil);
|
|
Inc(Pos, SI.OldSize);
|
|
continue;
|
|
end;
|
|
end;
|
|
if (CodecEnabled[PACKJPG_CODEC] or CodecEnabled[BRUNSLI_CODEC] or
|
|
CodecEnabled[JOJPEG_CODEC]) and (PWord(Input + Pos)^ = JPG_HEADER) then
|
|
begin
|
|
Y := SizeEx - Pos;
|
|
if GetJPEGInfo(Input + Pos, Y, @data_size, @progressive) then
|
|
Y := data_size
|
|
else
|
|
Y := 0;
|
|
if Y > 0 then
|
|
begin
|
|
Z := data_size;
|
|
Output(Instance, Input + Pos, Z);
|
|
SI.Position := Pos;
|
|
SI.OldSize := Y;
|
|
SI.NewSize := Z;
|
|
SI.Option := 0;
|
|
if CodecEnabled[BRUNSLI_CODEC] then
|
|
SetBits(SI.Option, BRUNSLI_CODEC, 0, 5)
|
|
else if CodecEnabled[JOJPEG_CODEC] then
|
|
SetBits(SI.Option, JOJPEG_CODEC, 0, 5)
|
|
else if CodecEnabled[PACKJPG_CODEC] then
|
|
SetBits(SI.Option, PACKJPG_CODEC, 0, 5);
|
|
SI.Status := TStreamStatus.None;
|
|
Funcs^.LogScan1(MediaCodecs[GetBits(SI.Option, 0, 5)], SI.Position,
|
|
SI.OldSize, -1);
|
|
Add(Instance, @SI, nil, nil);
|
|
Inc(Pos, SI.OldSize);
|
|
continue;
|
|
end;
|
|
end;
|
|
Inc(Pos);
|
|
end;
|
|
end;
|
|
|
|
function MediaScan2(Instance, Depth: Integer; Input: Pointer; Size: NativeInt;
|
|
StreamInfo: PStrInfo2; Offset: PInteger; Output: _PrecompOutput;
|
|
Funcs: PPrecompFuncs): Boolean;
|
|
var
|
|
Buffer: PByte;
|
|
X, Y, Z: Integer;
|
|
wave_hdr: TWAVE_hdr;
|
|
data_size, hdr_size: Cardinal;
|
|
progressive: Boolean;
|
|
begin
|
|
Result := False;
|
|
X := GetBits(StreamInfo^.Option, 0, 5);
|
|
if StreamInfo^.OldSize <= 0 then
|
|
exit;
|
|
Y := StreamInfo^.OldSize;
|
|
case X of
|
|
FLAC_CODEC:
|
|
begin
|
|
if GetWAVEInfo(Input, Y, @wave_hdr, @data_size, @hdr_size) then
|
|
Y := data_size + hdr_size
|
|
else
|
|
Y := 0;
|
|
end;
|
|
PACKJPG_CODEC, BRUNSLI_CODEC, JOJPEG_CODEC:
|
|
begin
|
|
if GetJPEGInfo(Input, Y, @data_size, @progressive) then
|
|
Y := data_size
|
|
else
|
|
Y := 0;
|
|
end;
|
|
else
|
|
Y := 0;
|
|
end;
|
|
if Y > 0 then
|
|
begin
|
|
Z := data_size;
|
|
if X = FLAC_CODEC then
|
|
begin
|
|
Buffer := Funcs^.Allocator(Instance, hdr_size);
|
|
Move(Input^, Buffer^, hdr_size);
|
|
Inc(PInteger(Buffer)^);
|
|
Output(Instance, @hdr_size, hdr_size.Size);
|
|
Output(Instance, Buffer, hdr_size);
|
|
Output(Instance, PByte(Input) + hdr_size, Z);
|
|
Inc(Z, hdr_size.Size);
|
|
Inc(Z, hdr_size);
|
|
end
|
|
else
|
|
Output(Instance, Input, Z);
|
|
StreamInfo^.NewSize := Z;
|
|
Funcs^.LogScan2(MediaCodecs[GetBits(StreamInfo^.Option, 0, 5)],
|
|
StreamInfo^.OldSize, -1);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function MediaProcess(Instance, Depth: Integer; OldInput, NewInput: Pointer;
|
|
StreamInfo: PStrInfo2; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean;
|
|
var
|
|
Buffer, Ptr: PByte;
|
|
Params: String;
|
|
ctx: Pointer;
|
|
I, J: Integer;
|
|
X, Y: Integer;
|
|
Res: Cardinal;
|
|
Res1, Res2: Integer;
|
|
begin
|
|
Result := False;
|
|
X := GetBits(StreamInfo^.Option, 0, 5);
|
|
if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then
|
|
exit;
|
|
Params := '';
|
|
case X of
|
|
FLAC_CODEC:
|
|
begin
|
|
Params := 'l' + FlacLevel.ToString;
|
|
Y := Integer.Size + PInteger(NewInput)^;
|
|
Res := StreamInfo.NewSize - Y;
|
|
Res := FlacEncode(cctx[Instance], @ccd[Instance], OldInput,
|
|
StreamInfo^.OldSize, PByte(NewInput) + Y, Res, FlacLevel);
|
|
if (Res > 0) and (Res + Y < StreamInfo^.NewSize) then
|
|
begin
|
|
StreamInfo^.NewSize := Res + Y;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
PACKJPG_CODEC:
|
|
begin
|
|
Buffer := nil;
|
|
Res := StreamInfo.OldSize;
|
|
pjglib_init_streams(OldInput, pjglib_memory, StreamInfo^.OldSize,
|
|
Buffer, pjglib_memory);
|
|
if pjglib_convert_stream2mem(@Buffer, @Res, nil) and
|
|
(Res < StreamInfo^.NewSize) then
|
|
begin
|
|
Move(Buffer^, NewInput^, Res);
|
|
StreamInfo^.NewSize := Res;
|
|
Result := True;
|
|
end;
|
|
ShowMessage('');
|
|
end;
|
|
BRUNSLI_CODEC:
|
|
begin
|
|
ctx := brunsli_alloc_JPEGData;
|
|
try
|
|
if brunsli_ReadJpeg(ctx, OldInput, StreamInfo^.OldSize) = Integer(True)
|
|
then
|
|
begin
|
|
Res := brunsli_GetMaximumEncodedSize(ctx);
|
|
Buffer := Funcs^.Allocator(Instance, Res);
|
|
Res := brunsli_EncodeJpeg(ctx, Buffer, Res);
|
|
if Res < StreamInfo^.NewSize then
|
|
begin
|
|
Move(Buffer^, NewInput^, Res);
|
|
StreamInfo^.NewSize := Res;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
finally
|
|
brunsli_free_JPEGData(ctx);
|
|
end;
|
|
end;
|
|
JOJPEG_CODEC:
|
|
begin
|
|
ctx := JJInst[Instance];
|
|
Buffer := Funcs^.Allocator(Instance, J_WORKMEM * 2);
|
|
I := 0;
|
|
J := 0;
|
|
Y := 0;
|
|
jojpeg_Init(ctx, jojpeg_Compress);
|
|
try
|
|
jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer, J_WORKMEM,
|
|
jojpeg_enc_Output1);
|
|
jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer + J_WORKMEM, J_WORKMEM,
|
|
jojpeg_enc_Output2);
|
|
while True do
|
|
begin
|
|
Res1 := jojpeg_Loop(ctx, jojpeg_Compress);
|
|
if (Res1 = jojpeg_enc_Input) then
|
|
begin
|
|
Res2 := Min(StreamInfo^.OldSize - I, J_WORKMEM);
|
|
jojpeg_Addbuf(ctx, jojpeg_Compress, PByte(OldInput) + I, Res2,
|
|
jojpeg_enc_Input);
|
|
Inc(I, Res2);
|
|
end;
|
|
if (Res1 in [0, jojpeg_enc_Output1]) then
|
|
begin
|
|
Res2 := jojpeg_Getvalue(ctx, jojpeg_Compress, jojpeg_enc_Output1);
|
|
Move(Buffer^, (PByte(NewInput) + J)^, Res2);
|
|
Inc(J, Res2);
|
|
jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer, J_WORKMEM,
|
|
jojpeg_enc_Output1);
|
|
end;
|
|
if (Res1 = jojpeg_enc_Output2) or (Res1 = 0) then
|
|
begin
|
|
Res2 := jojpeg_Getvalue(ctx, jojpeg_Compress, jojpeg_enc_Output2);
|
|
Output(Instance, Buffer + J_WORKMEM, Res2);
|
|
Inc(Y, Res2);
|
|
jojpeg_Addbuf(ctx, jojpeg_Compress, Buffer + J_WORKMEM, J_WORKMEM,
|
|
jojpeg_enc_Output2);
|
|
if Res1 = 0 then
|
|
break;
|
|
end;
|
|
end;
|
|
if I = StreamInfo^.OldSize then
|
|
begin
|
|
StreamInfo^.NewSize := J;
|
|
Result := True;
|
|
end;
|
|
finally
|
|
jojpeg_Quit(ctx, jojpeg_Compress);
|
|
end;
|
|
end;
|
|
end;
|
|
Funcs^.LogProcess(MediaCodecs[GetBits(StreamInfo^.Option, 0, 5)],
|
|
PChar(Params), StreamInfo^.OldSize, StreamInfo^.NewSize, -1, Result);
|
|
end;
|
|
|
|
function MediaRestore(Instance, Depth: Integer; Input, InputExt: Pointer;
|
|
StreamInfo: _StrInfo3; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean;
|
|
var
|
|
Buffer: PByte;
|
|
Params: String;
|
|
ctx, writer: Pointer;
|
|
I, J: Integer;
|
|
X, Y: Integer;
|
|
Res: Cardinal;
|
|
Res1, Res2: Integer;
|
|
begin
|
|
Result := False;
|
|
X := GetBits(StreamInfo.Option, 0, 5);
|
|
if BoolArray(CodecAvailable, False) or (CodecAvailable[X] = False) then
|
|
exit;
|
|
Params := '';
|
|
case X of
|
|
FLAC_CODEC:
|
|
begin
|
|
Buffer := Funcs^.Allocator(Instance, StreamInfo.OldSize);
|
|
Y := Integer.Size + PInteger(Input)^;
|
|
Res := StreamInfo.OldSize;
|
|
Res := FlacDecode(dctx[Instance], @dcd[Instance], PByte(Input) + Y,
|
|
StreamInfo.NewSize - Y, Buffer + PInteger(Input)^, Res);
|
|
if Res > 0 then
|
|
begin
|
|
Move((PByte(Input) + Integer.Size)^, Buffer^, PInteger(Input)^);
|
|
Inc(Res, PInteger(Input)^);
|
|
Dec(PInteger(Buffer)^);
|
|
Result := True;
|
|
end;
|
|
if Result then
|
|
Output(Instance, Buffer, Res);
|
|
end;
|
|
PACKJPG_CODEC:
|
|
begin
|
|
Buffer := nil;
|
|
pjglib_init_streams(Input, pjglib_memory, StreamInfo.NewSize, Buffer,
|
|
pjglib_memory);
|
|
Res := StreamInfo.OldSize;
|
|
Result := pjglib_convert_stream2mem(@Buffer, @Res, nil);
|
|
if Result then
|
|
Output(Instance, Buffer, Res);
|
|
end;
|
|
BRUNSLI_CODEC:
|
|
begin
|
|
RestoreOut[Instance] := Output;
|
|
RestoreSize[Instance] := 0;
|
|
Res := RestoreSize[Instance];
|
|
ctx := brunsli_alloc_JPEGData;
|
|
try
|
|
if brunsli_DecodeJpeg(ctx, Input, StreamInfo.NewSize) = BRUNSLI_OK
|
|
then
|
|
begin
|
|
writer := brunsli_alloc_JPEGOutput(BrunsliWriter, @Instance);
|
|
try
|
|
Result := Boolean(brunsli_WriteJpeg(ctx, writer));
|
|
finally
|
|
brunsli_free_JPEGOutput(writer);
|
|
end;
|
|
end;
|
|
finally
|
|
brunsli_free_JPEGData(ctx);
|
|
end;
|
|
Res := RestoreSize[Instance];
|
|
end;
|
|
JOJPEG_CODEC:
|
|
begin
|
|
ctx := JJInst[Instance];
|
|
Buffer := Funcs^.Allocator(Instance, J_WORKMEM);
|
|
I := 0;
|
|
J := 0;
|
|
Y := 0;
|
|
jojpeg_Init(ctx, jojpeg_Decompress);
|
|
try
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, Buffer, J_WORKMEM,
|
|
jojpeg_dec_Output);
|
|
while True do
|
|
begin
|
|
Res1 := jojpeg_Loop(ctx, jojpeg_Decompress);
|
|
if (Res1 = jojpeg_dec_Input1) then
|
|
begin
|
|
Res2 := Min(StreamInfo.NewSize - I, J_WORKMEM);
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(Input) + I, Res2,
|
|
jojpeg_dec_Input1);
|
|
Inc(I, Res2);
|
|
end;
|
|
if (Res1 = jojpeg_dec_Input2) then
|
|
begin
|
|
Res2 := Min(StreamInfo.ExtSize - J, J_WORKMEM);
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(InputExt) + J, Res2,
|
|
jojpeg_dec_Input2);
|
|
Inc(J, Res2);
|
|
end;
|
|
if (Res1 in [0, jojpeg_dec_Output]) then
|
|
begin
|
|
Res2 := jojpeg_Getvalue(ctx, jojpeg_Decompress,
|
|
jojpeg_dec_Output);
|
|
Output(Instance, Buffer, Res2);
|
|
Inc(Y, Res2);
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, Buffer, J_WORKMEM,
|
|
jojpeg_dec_Output);
|
|
if Res1 = 0 then
|
|
break;
|
|
end;
|
|
end;
|
|
{ while True do
|
|
begin
|
|
Res1 := jojpeg_Loop(ctx, jojpeg_Decompress);
|
|
if Res1 = 0 then
|
|
break;
|
|
if (Res1 = jojpeg_dec_Input1) then
|
|
begin
|
|
Res2 := Min(StreamInfo.NewSize - I, J_WORKMEM);
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(Input) + I, Res2,
|
|
jojpeg_dec_Input1);
|
|
Inc(I, Res2);
|
|
end;
|
|
if (Res1 = jojpeg_dec_Input2) then
|
|
begin
|
|
Res2 := Min(StreamInfo.ExtSize - J, J_WORKMEM);
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, PByte(InputExt) + J, Res2,
|
|
jojpeg_dec_Input2);
|
|
Inc(J, Res2);
|
|
end;
|
|
if (Res1 = jojpeg_dec_Output) then
|
|
begin
|
|
Output(Instance, Buffer, J_WORKMEM);
|
|
Inc(Y, J_WORKMEM);
|
|
jojpeg_Addbuf(ctx, jojpeg_Decompress, Buffer, J_WORKMEM,
|
|
jojpeg_dec_Output);
|
|
end;
|
|
end;
|
|
if (Y <> StreamInfo.OldSize) and (StreamInfo.OldSize - Y < J_WORKMEM)
|
|
then
|
|
begin
|
|
Res2 := StreamInfo.OldSize - Y;
|
|
Output(Instance, Buffer, Res2);
|
|
Inc(Y, Res2);
|
|
end; }
|
|
Res := Y;
|
|
Result := Y = StreamInfo.OldSize;
|
|
finally
|
|
jojpeg_Quit(ctx, jojpeg_Decompress);
|
|
end;
|
|
end;
|
|
end;
|
|
Funcs^.LogRestore(MediaCodecs[GetBits(StreamInfo.Option, 0, 5)],
|
|
PChar(Params), StreamInfo.NewSize, Res, -1, Result);
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
|
|
initialization
|
|
|
|
Codec.Names := [];
|
|
for I := Low(MediaCodecs) to High(MediaCodecs) do
|
|
begin
|
|
Codec.Names := Codec.Names + [MediaCodecs[I]];
|
|
StockMethods.Add(MediaCodecs[I]);
|
|
end;
|
|
Codec.Initialised := False;
|
|
Codec.Init := @MediaInit;
|
|
Codec.Free := @MediaFree;
|
|
Codec.Parse := @MediaParse;
|
|
Codec.Scan1 := @MediaScan1;
|
|
Codec.Scan2 := @MediaScan2;
|
|
Codec.Process := @MediaProcess;
|
|
Codec.Restore := @MediaRestore;
|
|
SetLength(CodecAvailable, length(Codec.Names));
|
|
SetLength(CodecEnabled, length(Codec.Names));
|
|
|
|
end.
|