907 lines
32 KiB
ObjectPascal
907 lines
32 KiB
ObjectPascal
{This unit is part of United Openlibraries of Sound (uos)}
|
|
|
|
{This is the Dynamic loading version of FAAD2 Pascal Wrapper.
|
|
mcwNeAACDec.pas, mcwMP4FF.pas, mcwAAC.pas
|
|
By Franklyn A. Harlow Feb 2016
|
|
License : modified LGPL.
|
|
mcw* merged into uos_aac by Fred van Stappen / fiens@hotmail.com }
|
|
|
|
unit uos_aac;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$PACKRECORDS C}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, ctypes, math,
|
|
dynlibs;
|
|
|
|
const
|
|
|
|
{$IFDEF unix}
|
|
{$IFDEF darwin}
|
|
libaa= 'libfaad.2.dylib';
|
|
libm4= 'libmp4ff.0.dylib';
|
|
{$ELSE}
|
|
libaa= 'libfaad.so.2';
|
|
libm4= 'libmp4ff.so.0.0.0';
|
|
{$ENDIF}
|
|
{$ELSE}
|
|
libaa= 'Faad2.dll';
|
|
libm4= 'mp4ff.dll';
|
|
{$ENDIF}
|
|
|
|
//////////// from mcwMP4FF.pas By Franklyn A. Harlow
|
|
type
|
|
|
|
ArSingle = array of Single;
|
|
PArSingle = ^ArSingle;
|
|
|
|
read_callback_t = function(user_data : Pointer; buffer : pcfloat; length : LongWord) : LongWord; cdecl;
|
|
write_callback_t = function(user_data : Pointer; buffer : pcfloat; length : LongWord) : LongWord; cdecl;
|
|
seek_callback_t = function(user_data : Pointer; Position : cInt64) : LongWord; cdecl;
|
|
truncate_callback_t = function(user_data : Pointer) : LongWord; cdecl;
|
|
|
|
mp4ff_callback_t = record
|
|
read : read_callback_t;
|
|
write : write_callback_t;
|
|
seek : seek_callback_t;
|
|
truncate : truncate_callback_t;
|
|
user_data : Pointer;
|
|
end;
|
|
p_mp4ff_callback_t = ^mp4ff_callback_t;
|
|
|
|
mp4ff_t = pointer;
|
|
int32_t = LongInt;
|
|
int64_t = cInt64;
|
|
uint32_t = LongWord;
|
|
|
|
var
|
|
mp4ff_open_read : function(f : p_mp4ff_callback_t) : mp4ff_t; cdecl;
|
|
mp4ff_open_read_metaonly : function(f : p_mp4ff_callback_t) : mp4ff_t; cdecl;
|
|
mp4ff_close : procedure(f : mp4ff_t); cdecl;
|
|
mp4ff_get_sample_duration : function(f : mp4ff_t; track, sample : int32_t) : int32_t; cdecl;
|
|
mp4ff_get_sample_duration_use_offsets : function(f : mp4ff_t; track, sample : int32_t) : int32_t; cdecl;
|
|
mp4ff_get_sample_position : function(f : mp4ff_t; track, sample : int32_t) : int64_t; cdecl;
|
|
mp4ff_get_sample_offset : function(f : mp4ff_t; track, sample : int32_t) : int32_t; cdecl;
|
|
mp4ff_find_sample : function(f : mp4ff_t; track : int32_t; offset : int64_t; var toskip : int32_t) : int32_t; cdecl;
|
|
mp4ff_find_sample_use_offsets : function(f : mp4ff_t; track : int32_t; offset : int64_t; var toskip : int32_t) : int32_t; cdecl;
|
|
mp4ff_set_sample_position : function(f : mp4ff_t; track : int32_t; sample : int64_t) : int32_t; cdecl;
|
|
mp4ff_read_sample : function(f : mp4ff_t; track, sample : int32_t; var audio_buffer : pcfloat; var bytes : LongWord) : int32_t; cdecl;
|
|
mp4ff_read_sample_v2 : function(f : mp4ff_t; track, sample : int32_t; buffer : pcfloat): int32_t; cdecl; //returns 0 on error, number of bytes read on success, use mp4ff_read_sample_getsize_t = function() to check buffer size needed
|
|
mp4ff_read_sample_getsize : function(f : mp4ff_t; track, sample : Integer) : int32_t; cdecl; //returns 0 on error, buffer size needed for mp4ff_read_sample_v2_t = function() on success
|
|
mp4ff_get_decoder_config : function(f : mp4ff_t; track : Integer; var ppBuf : pcfloat; var pBufSize : LongWord) : int32_t; cdecl;
|
|
mp4ff_free_decoder_config : procedure(Buf : pcfloat); cdecl;
|
|
mp4ff_get_track_type : function(f : mp4ff_t; const track : Integer) : int32_t; cdecl;
|
|
mp4ff_total_tracks : function(f : mp4ff_t) : int32_t; cdecl;
|
|
mp4ff_num_samples : function(f : mp4ff_t; track : Integer) : int32_t; cdecl;
|
|
mp4ff_time_scale : function(f : mp4ff_t; track : Integer) : int32_t; cdecl;
|
|
mp4ff_get_avg_bitrate : function(f : mp4ff_t; track : int32_t) : uint32_t; cdecl;
|
|
mp4ff_get_max_bitrate : function(f : mp4ff_t; track : int32_t) : uint32_t; cdecl;
|
|
mp4ff_get_track_duration : function(f : mp4ff_t; track : int32_t) : int64_t; cdecl; //returns _t = function(-1) if unknown
|
|
mp4ff_get_track_duration_use_offsets : function(f : mp4ff_t; track : int32_t) : Integer; cdecl; //returns _t = function(-1) if unknown
|
|
mp4ff_get_sample_rate : function(f : mp4ff_t; track : int32_t) : uint32_t; cdecl;
|
|
mp4ff_get_channel_count : function(f : mp4ff_t; track : int32_t) : uint32_t; cdecl;
|
|
mp4ff_get_audio_type : function(f : mp4ff_t; track : int32_t) : uint32_t; cdecl;
|
|
mp4ff_meta_get_num_items : function(f : mp4ff_t) : Integer; cdecl;
|
|
mp4ff_meta_get_by_index : function(f : mp4ff_t; index : LongWord; var item, value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_title : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_artist : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_writer : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_album : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_date : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_tool : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_comment : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_genre : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_track : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_disc : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_totaltracks : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_totaldiscs : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_compilation : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_tempo : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
mp4ff_meta_get_coverart : function(f : mp4ff_t; var value : PChar) : Integer; cdecl;
|
|
|
|
function GetAACTrack(infile : mp4ff_t) : Integer;
|
|
procedure Loadmp4ff(mp4ff : AnsiString);
|
|
procedure UnLoadMp4ff;
|
|
Function isMp4ffLoaded : Boolean;
|
|
|
|
//////////// from mcwNeAACDec.pas by Franklyn A. Harlow
|
|
Const
|
|
{ object types for AAC }
|
|
MAIN = 1;
|
|
LC = 2;
|
|
SSR = 3;
|
|
LTP = 4;
|
|
HE_AAC = 5;
|
|
ER_LC = 17;
|
|
ER_LTP = 19;
|
|
LD = 23;
|
|
{ special object type for DRM }
|
|
DRM_ER_LC = 27;
|
|
{ header types }
|
|
RAW = 0;
|
|
ADIF = 1;
|
|
ADTS = 2;
|
|
LATM = 3;
|
|
{ SBR signalling }
|
|
NO_SBR = 0;
|
|
SBR_UPSAMPLED = 1;
|
|
SBR_DOWNSAMPLED = 2;
|
|
NO_SBR_UPSAMPLED = 3;
|
|
{ library output formats }
|
|
FAAD_FMT_16BIT = 1;
|
|
FAAD_FMT_24BIT = 2;
|
|
FAAD_FMT_32BIT = 3;
|
|
FAAD_FMT_FLOAT = 4;
|
|
FAAD_FMT_FIXED = FAAD_FMT_FLOAT;
|
|
FAAD_FMT_DOUBLE = 5;
|
|
{ Capabilities }
|
|
{ Can decode LC }
|
|
LC_DEC_CAP = 1 shl 0;
|
|
{ Can decode MAIN }
|
|
MAIN_DEC_CAP = 1 shl 1;
|
|
{ Can decode LTP }
|
|
LTP_DEC_CAP = 1 shl 2;
|
|
{ Can decode LD }
|
|
LD_DEC_CAP = 1 shl 3;
|
|
{ Can decode ER }
|
|
ERROR_RESILIENCE_CAP = 1 shl 4;
|
|
{ Fixed point }
|
|
FIXED_POINT_CAP = 1 shl 5;
|
|
{ Channel definitions }
|
|
FRONT_CHANNEL_CENTER = 1;
|
|
FRONT_CHANNEL_LEFT = 2;
|
|
FRONT_CHANNEL_RIGHT = 3;
|
|
SIDE_CHANNEL_LEFT = 4;
|
|
SIDE_CHANNEL_RIGHT = 5;
|
|
BACK_CHANNEL_LEFT = 6;
|
|
BACK_CHANNEL_RIGHT = 7;
|
|
BACK_CHANNEL_CENTER = 8;
|
|
LFE_CHANNEL = 9;
|
|
UNKNOWN_CHANNEL = 0;
|
|
{ DRM channel definitions }
|
|
DRMCH_MONO = 1;
|
|
DRMCH_STEREO = 2;
|
|
DRMCH_SBR_MONO = 3;
|
|
DRMCH_SBR_STEREO = 4;
|
|
DRMCH_SBR_PS_STEREO = 5;
|
|
{ A decode call can eat up to FAAD_MIN_STREAMSIZE bytes per decoded channel,
|
|
so at least so much bytes per channel should be available in this stream }
|
|
{ 6144 bits/channel }
|
|
FAAD_MIN_STREAMSIZE = 768;
|
|
|
|
Type
|
|
PNeAACDec = Pointer;
|
|
|
|
NeAAC_byte = {$IFDEF FPC}{$IFDEF CPU64}cuint32 {$ELSE}Byte {$ENDIF}{$ELSE}Byte {$ENDIF};
|
|
NeAAC_word = {$IFDEF FPC}{$IFDEF CPU64}cuint64{$ELSE}Word {$ENDIF}{$ELSE}Word {$ENDIF};
|
|
NeAAC_longword = {$IFDEF FPC}{$IFDEF CPU64}culong {$ELSE}LongWord{$ENDIF}{$ELSE}LongWord{$ENDIF};
|
|
|
|
NeAACDecConfiguration = record
|
|
defObjectType : NeAAC_byte;
|
|
defSampleRate : NeAAC_longword;
|
|
outputFormat : NeAAC_byte;
|
|
downMatrix : NeAAC_byte;
|
|
useOldADTSFormat : NeAAC_byte;
|
|
dontUpSampleImplicitSBR : NeAAC_byte;
|
|
end;
|
|
TNeAACDecConfiguration = NeAACDecConfiguration;
|
|
PNeAACDecConfiguration = ^NeAACDecConfiguration;
|
|
|
|
NeAACDecFrameInfo = record
|
|
bytesconsumed : NeAAC_longword;
|
|
samples : NeAAC_longword;
|
|
channels : NeAAC_byte;
|
|
error : NeAAC_byte;
|
|
samplerate : NeAAC_longword;
|
|
sbr : NeAAC_byte; //* SBR: 0: off, 1: on; upsample, 2: on; downsampled, 3: off; upsampled */
|
|
object_type : NeAAC_byte; //* MPEG-4 ObjectType */
|
|
header_type : NeAAC_byte; //* AAC header type; MP4 will be signalled as RAW also */
|
|
num_front_channels : NeAAC_byte; //* multichannel configuration */
|
|
num_side_channels : NeAAC_byte;
|
|
num_back_channels : NeAAC_byte;
|
|
num_lfe_channels : NeAAC_byte;
|
|
channel_position : array[0..63] of NeAAC_byte;
|
|
ps : NeAAC_byte; //* PS: 0: off, 1: on */
|
|
end;
|
|
TNeAACDecFrameInfo = NeAACDecFrameInfo;
|
|
PNeAACDecFrameInfo = ^NeAACDecFrameInfo;
|
|
|
|
mp4AudioSpecificConfig = record
|
|
objectTypeIndex : NeAAC_byte;
|
|
samplingFrequencyIndex : NeAAC_byte;
|
|
samplingFrequency : NeAAC_longword;
|
|
channelsConfiguration : NeAAC_byte;
|
|
frameLengthFlag : NeAAC_byte; //* GA Specific Info */
|
|
dependsOnCoreCoder : NeAAC_byte;
|
|
coreCoderDelay : NeAAC_word;
|
|
extensionFlag : NeAAC_byte;
|
|
aacSectionDataResilienceFlag : NeAAC_byte;
|
|
aacScalefactorDataResilienceFlag : NeAAC_byte;
|
|
aacSpectralDataResilienceFlag : NeAAC_byte;
|
|
epConfig : NeAAC_byte;
|
|
sbr_present_flag : NeAAC_byte;
|
|
forceUpSampling : NeAAC_byte;
|
|
downSampledSBR : NeAAC_byte;
|
|
end;
|
|
Tmp4AudioSpecificConfig = mp4AudioSpecificConfig;
|
|
Pmp4AudioSpecificConfig = ^mp4AudioSpecificConfig;
|
|
|
|
var
|
|
NeAACDecGetErrorMessage : function(ErrorCode : Byte) : PChar; cdecl;
|
|
NeAACDecGetCapabilities : function : LongWord; cdecl;
|
|
NeAACDecOpen : function : PNeAACDec; cdecl;
|
|
NeAACDecGetCurrentConfiguration : function( hDecoder : PNeAACDec) : PNeAACDecConfiguration; cdecl;
|
|
NeAACDecSetConfiguration : function( hDecoder : PNeAACDec; pConfig : PNeAACDecConfiguration) : Byte; cdecl;
|
|
NeAACDecInit : function( hDecoder : PNeAACDec; pBuffer : pcfloat; lwBufferLength : LongWord; var lwSampleRate : LongWord; var Channels : Byte) : LongInt; cdecl;
|
|
NeAACDecInit2 : function( hDecoder : PNeAACDec; pBuffer : pcfloat; SizeOfDecoderSpecificInfo : LongWord; var lwSampleRate : LongWord; var Channels : Byte) : Byte; cdecl;
|
|
NeAACDecPostSeekReset : procedure(hDecoder : PNeAACDec; Frame : LongInt); cdecl;
|
|
NeAACDecClose : procedure(hDecoder : PNeAACDec); cdecl;
|
|
NeAACDecDecode : function( hDecoder : PNeAACDec; hInfo : PNeAACDecFrameInfo; pBuffer : pcfloat; lwBufferLength : LongWord) : Pointer; cdecl;
|
|
NeAACDecDecode2 : function( hDecoder : PNeAACDec; hInfo : PNeAACDecFrameInfo; pBuffer : pcfloat; lwBufferLength : LongWord; var pSampleBuf : Pointer; lwSampleBufSize : LongWord) : Pointer; cdecl;
|
|
NeAACDecAudioSpecificConfig : function( pBuffer : pcfloat; lwBufferLength : LongWord; var mp4ASC : mp4AudioSpecificConfig) : Byte; cdecl;
|
|
|
|
procedure LoadNeAAC(NeAAC : AnsiString);
|
|
Procedure UnLoadNeAAC;
|
|
Function Is_NeAAC_Loaded : Boolean;
|
|
|
|
//////////////////
|
|
|
|
// from mcwAAC.pas from Franklyn A. Harlow
|
|
Const
|
|
kNeAAC_OK = 1;
|
|
kNeAAC_FAIL = 0;
|
|
|
|
Type
|
|
|
|
TAACIdata = array[0..2024*1024-1] of byte;
|
|
|
|
TAACInfo = class(TObject)
|
|
public
|
|
// libfaad control interface...
|
|
fsStream : TFileStream;
|
|
pDecoder : PNeAACDec;
|
|
hMP4 : mp4ff_t;
|
|
|
|
// output interface setup...
|
|
Channels : longword; // stereo = 2
|
|
BitsPerSample : longword; // ie short/smallint = 16
|
|
SampleRate : longword; // Frequency = 44100
|
|
outputFormat : byte; // See FAAD_FMT_16BIT etc...
|
|
|
|
Bitrate : longword; // 256000 = 256
|
|
|
|
TotalTime : double; // Length in Seconds
|
|
|
|
{$if not defined(fs32bit)}
|
|
TotalSamples : cint64;
|
|
Size : cint64; // in frames
|
|
Position : cint64; // in frames
|
|
{$else}
|
|
TotalSamples : cint;
|
|
Size : cint; // in frames
|
|
Position : cint; // in frames
|
|
{$endif}
|
|
|
|
// UoS interface
|
|
pData : pcfloat;
|
|
lwDataLen : longword;
|
|
|
|
// tag info...
|
|
Artist : AnsiString;
|
|
AlbumArtist : AnsiString;
|
|
Album : AnsiString;
|
|
Title : AnsiString;
|
|
Date : AnsiString;
|
|
Genre : AnsiString;
|
|
Track : AnsiString;
|
|
Comment : AnsiString;
|
|
|
|
// internal data...
|
|
cbs : mp4ff_callback_t;
|
|
_Buf : TAACIdata;
|
|
BufTmp : TAACIdata;
|
|
fTrack : longint;
|
|
fTimescale : longword; // Not actually used
|
|
|
|
fBufStart : longword;
|
|
fBufEnd : longword;
|
|
End;
|
|
|
|
Function aa_load(mp4ff, NeAAC: AnsiString): Boolean;
|
|
Procedure aa_Unload;
|
|
Function isLibAACLoaded : Boolean;
|
|
|
|
Function MP4OpenFile(fName : String; OutputFormat : Byte) : TAACInfo;
|
|
Procedure MP4CloseFile(var AACI: TAACInfo);
|
|
procedure MP4GetData(var AACI: TAACInfo; var Buffer: pcfloat; var Bytes: longword);
|
|
function MP4Seek(var AACI: TAACInfo; var SampleNum: longint) : Boolean;
|
|
|
|
implementation
|
|
|
|
var
|
|
// hNeAAC: {$IFDEF MSWINDOWS}longword{$ELSE}{$IFDEF CPU32}longword{$ELSE}PtrInt{$ENDIF}{$ENDIF};
|
|
hNeAAC:TLibHandle=dynlibs.NilHandle;
|
|
NeAACLoaded : Boolean;
|
|
Mp4ffLoaded : Boolean = False;
|
|
hMp4ff:TLibHandle=dynlibs.NilHandle;
|
|
|
|
//////////// from mcwNeAACDec.pas By Franklyn A. Harlow
|
|
|
|
function CBMP4Read(user_data : Pointer; buffer : pcfloat; length : LongWord) : LongWord; cdecl;
|
|
begin
|
|
try
|
|
Result := LongWord(TAACInfo(user_data).fsStream.Read(buffer^, Integer(length)));
|
|
except
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
function CBMP4Write(user_data : Pointer; buffer : pcfloat; length : LongWord) : LongWord; cdecl;
|
|
begin
|
|
try
|
|
Result := LongWord(TAACInfo(user_data).fsStream.Write(buffer^, Integer(length)));
|
|
TAACInfo(user_data).fsStream.Position:=0;
|
|
except
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
function CBMP4Seek(user_data : Pointer; Position : Int64) : LongWord; cdecl;
|
|
begin
|
|
try
|
|
Result := LongWord(TAACInfo(user_data).fsStream.Seek(Position, soBeginning));
|
|
except
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
function CBMP4Truncate(user_data : Pointer) : LongWord; cdecl;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
//////////// from mcwAAC.pas By Franklyn A. Harlow
|
|
|
|
Function aa_load(mp4ff, NeAAC: AnsiString): Boolean;
|
|
Begin
|
|
// Safe To Call Multiple times, actual load of Lib checks to see if it is already loaded &
|
|
// returns true if it is...
|
|
LoadNeAAC(NeAAC);
|
|
|
|
Loadmp4ff(mp4ff);
|
|
|
|
Result:= Is_NeAAC_Loaded And isMp4ffLoaded;
|
|
End;
|
|
|
|
Procedure aa_Unload;
|
|
Begin
|
|
UnLoadNeAAC();
|
|
UnLoadMp4ff();
|
|
End;
|
|
|
|
Function isLibAACLoaded : Boolean;
|
|
Begin
|
|
Result:= Is_NeAAC_Loaded And isMp4ffLoaded;
|
|
end;
|
|
|
|
Function isFileAAC(fName : AnsiString): Boolean;
|
|
Begin
|
|
Result:= LowerCase(ExtractFileExt(fName)) = '.m4a';
|
|
end;
|
|
|
|
Function MP4OpenFile(fName : String; OutputFormat : Byte) : TAACInfo;
|
|
Var
|
|
pConfig : PNeAACDecConfiguration;
|
|
mp4ASC : mp4AudioSpecificConfig;
|
|
|
|
pBuf : pcfloat;
|
|
lwBufSize : longword;
|
|
lwBufSize2 : longword;
|
|
lwSampleRate : longword;
|
|
bChannels : byte;
|
|
|
|
bRet : Byte;
|
|
iRet : LongInt;
|
|
pID3 : PAnsiChar;
|
|
f : Double;
|
|
Begin
|
|
Result:= nil;
|
|
if not FileExists(fName) then
|
|
Exit;
|
|
if Not isFileAAC(fName) then
|
|
Exit;
|
|
// writeln('MP4OpenFileBegin');
|
|
|
|
Result:= TAACInfo.Create;
|
|
// writeln('MP4OpenFile3');
|
|
|
|
Result.fsStream:= TFileStream.Create(fName, fmOpenRead or fmShareDenyWrite);
|
|
|
|
sleep(1);
|
|
|
|
// writeln('MP4OpenFile4');
|
|
Result.cbs.read := @CBMP4Read;
|
|
Result.cbs.write := @CBMP4Write;
|
|
Result.cbs.seek := @CBMP4Seek;
|
|
Result.cbs.truncate := @CBMP4Truncate;
|
|
Result.cbs.user_data := Pointer(Result);
|
|
// writeln('MP4OpenFile5');
|
|
|
|
Result.pDecoder := NeAACDecOpen();
|
|
// writeln('MP4OpenFile6');
|
|
|
|
pConfig := NeAACDecGetCurrentConfiguration(Result.pDecoder);
|
|
// writeln('MP4OpenFile7');
|
|
pConfig^.defObjectType := LC;
|
|
pConfig^.defSampleRate := 44100;
|
|
pConfig^.outputFormat := OutputFormat; // FAAD_FMT_FLOAT FAAD_FMT_16BIT FAAD_FMT_32BIT
|
|
pConfig^.downMatrix := 1;
|
|
pConfig^.dontUpSampleImplicitSBR := 0;
|
|
|
|
NeAACDecSetConfiguration(Result.pDecoder, pConfig) ;
|
|
|
|
pConfig := NeAACDecGetCurrentConfiguration(Result.pDecoder);
|
|
// writeln('MP4OpenFile9');
|
|
|
|
case pConfig^.outputFormat of
|
|
FAAD_FMT_16BIT : Result.BitsPerSample := 16;
|
|
FAAD_FMT_24BIT : Result.BitsPerSample := 24;
|
|
FAAD_FMT_32BIT : Result.BitsPerSample := 32;
|
|
FAAD_FMT_FLOAT : Result.BitsPerSample := 32;
|
|
FAAD_FMT_DOUBLE: Result.BitsPerSample := 64;
|
|
end;
|
|
// writeln('MP4OpenFile10');
|
|
Result.outputFormat:= pConfig^.outputFormat;
|
|
|
|
Result.hMP4 := mp4ff_open_read(@Result.cbs);
|
|
// writeln('MP4OpenFile11');
|
|
|
|
Result.fTrack := GetAACTrack(Result.hMP4);
|
|
// writeln('MP4OpenFile12');
|
|
|
|
pBuf:= nil;
|
|
iRet:= mp4ff_get_decoder_config(Result.hMP4, Result.fTrack, pBuf, lwBufSize);
|
|
// writeln('MP4OpenFile13');
|
|
|
|
lwBufSize2:= lwBufSize;
|
|
// writeln('MP4OpenFile14');
|
|
|
|
bRet:= NeAACDecInit2(Result.pDecoder, pBuf, lwBufSize, lwSampleRate, bChannels);
|
|
// writeln('MP4OpenFile15');
|
|
|
|
Result.SampleRate := mp4ff_get_sample_rate(Result.hMP4, Result.fTrack);
|
|
Result.Channels := mp4ff_get_channel_count(Result.hMP4, Result.fTrack);
|
|
Result.fTimescale := mp4ff_time_scale(Result.hMP4, Result.fTrack);
|
|
Result.Bitrate := mp4ff_get_avg_bitrate(Result.hMP4, Result.fTrack);
|
|
Result.Size := mp4ff_num_samples(Result.hMP4, Result.fTrack);
|
|
// writeln('MP4OpenFile16');
|
|
|
|
if pBuf <> nil then
|
|
begin
|
|
bRet:= NeAACDecAudioSpecificConfig(pBuf, lwBufSize2, mp4ASC);
|
|
// writeln('MP4OpenFile17');
|
|
|
|
if bRet <> 0 then
|
|
Begin
|
|
// unix x64, NeAACDecAudioSpecificConfig Fails correct read,
|
|
// But if both previous SampleRates match, we'll use them...
|
|
{$IFDEF unix}{$IFDEF CPU64}
|
|
if Result.SampleRate = lwSampleRate then
|
|
Begin
|
|
mp4ASC.sbr_present_flag := 0;
|
|
mp4ASC.samplingFrequency := lwSampleRate;
|
|
End;
|
|
{$ENDIF}{$ENDIF}
|
|
End;
|
|
// writeln('MP4OpenFile18');
|
|
mp4ff_free_decoder_config(pBuf);
|
|
// writeln('MP4OpenFile19');
|
|
end;
|
|
|
|
f := 1024.0;
|
|
if mp4ASC.sbr_present_flag = 1 then
|
|
f := f * 2.0;
|
|
Result.TotalTime := Result.Size * (f-1.0) / mp4ASC.samplingFrequency;
|
|
{ ...End}
|
|
Result.TotalSamples := Trunc(Result.TotalTime * mp4ASC.samplingFrequency);
|
|
// writeln('MP4OpenFile20');
|
|
if mp4ASC.samplingFrequency > Result.SampleRate then
|
|
Result.SampleRate:= mp4ASC.samplingFrequency;
|
|
|
|
Result.Artist:= '';
|
|
iRet:= mp4ff_meta_get_writer(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Artist:= pID3;
|
|
|
|
Result.AlbumArtist:= '';
|
|
iRet:= mp4ff_meta_get_artist(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.AlbumArtist:= pID3;
|
|
|
|
Result.Album:= '';
|
|
iRet:= mp4ff_meta_get_album(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Album:= pID3;
|
|
|
|
Result.Title:= '';
|
|
iRet:= mp4ff_meta_get_title(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Title:= pID3;
|
|
|
|
Result.Date:= '';
|
|
iRet:= mp4ff_meta_get_date(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Date:= pID3;
|
|
|
|
Result.Genre:= '';
|
|
iRet:= mp4ff_meta_get_genre(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Genre:= pID3;
|
|
|
|
Result.Track:= '';
|
|
iRet:= mp4ff_meta_get_track(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Track:= pID3;
|
|
|
|
Result.Comment:= '';
|
|
iRet:= mp4ff_meta_get_comment(Result.hMP4, pID3);
|
|
if iRet = 1 then
|
|
Result.Comment:= pID3;
|
|
|
|
Result.Position := 0;
|
|
Result.fBufStart := 0;
|
|
Result.fBufEnd := 0;
|
|
// writeln('MP4OpenFile End');
|
|
end;
|
|
|
|
Procedure MP4CloseFile(var AACI: TAACInfo);
|
|
Begin
|
|
// writeln('MP4CloseFile1');
|
|
NeAACDecClose(AACI.pDecoder);
|
|
sleep(50);
|
|
// writeln('MP4CloseFile2');
|
|
mp4ff_close(AACI.hMP4);
|
|
// writeln('MP4CloseFile3');
|
|
sleep(50);
|
|
end;
|
|
|
|
procedure MP4GetData(var AACI: TAACInfo; var Buffer: pcfloat; var Bytes: longword);
|
|
var
|
|
ReqBytes : longword;
|
|
CurBufSize : longword;
|
|
NewSampleBuf : Pointer;
|
|
NewBytesRead : longword;
|
|
NewBytesDecoded : longword;
|
|
|
|
Function readNextSample(var audioBuf : pcfloat; var audioSize : longword): longword;
|
|
Begin
|
|
audioSize := 0;
|
|
Result:= 0;
|
|
audioBuf := nil;
|
|
// writeln('readNextSample');
|
|
if AACI.Position > AACI.Size then
|
|
Begin
|
|
Result:= 0;
|
|
Exit;
|
|
end;
|
|
Result := mp4ff_read_sample(AACI.hMP4, AACI.fTrack, AACI.Position, audioBuf, audioSize);
|
|
Inc(AACI.Position);
|
|
end;
|
|
|
|
Function getNextChunk(var SampBuf : Pointer; var NBR : longword) : longword;
|
|
Var
|
|
pAB : pcfloat;
|
|
iAB : longword;
|
|
frameInfo: NeAACDecFrameInfo;
|
|
Begin
|
|
NBR:= 0;
|
|
// writeln('getNextChunk');
|
|
Result:= 0;
|
|
Result:= readNextSample(pAB, iAB);
|
|
if Result = 0 then Exit;
|
|
SampBuf := NeAACDecDecode(AACI.pDecoder, @frameInfo, pAB, iAB);
|
|
if pAB <> nil then
|
|
mp4ff_free_decoder_config(pAB);
|
|
|
|
NBR:= frameInfo.samples * (AACI.BitsPerSample div 8);
|
|
end;
|
|
|
|
begin
|
|
// writeln('MP4GetData1');
|
|
ReqBytes:= Bytes;
|
|
// writeln('MP4GetDataBegin1-2');
|
|
CurBufSize:= AACI.fBufEnd - AACI.fBufStart;
|
|
|
|
While ReqBytes > CurBufSize do
|
|
Begin
|
|
// writeln('MP4GetData31');
|
|
// We need to put more data into Buffer...
|
|
|
|
// If We Have Left Over Data...
|
|
if CurBufSize > 0 then
|
|
Begin
|
|
// Save Existing Left over data..
|
|
Move(AACI._Buf[AACI.fBufStart], AACI.BufTmp[0], CurBufSize);
|
|
// Put Existing Data to Start of buffer...
|
|
Move(AACI.BufTmp[0], AACI._Buf[0], CurBufSize);
|
|
end;
|
|
|
|
// writeln('MP4GetData32');
|
|
// because we reshuffled buffers, AACI.fBufStart is now 0
|
|
AACI.fBufStart:= 0;
|
|
NewSampleBuf := nil;
|
|
// Read next block of data
|
|
|
|
NewBytesRead:= getNextChunk(NewSampleBuf, NewBytesDecoded);
|
|
|
|
// writeln('MP4GetData322');
|
|
if NewBytesRead = 0 then
|
|
begin
|
|
Buffer := nil;
|
|
Bytes := 0;
|
|
Exit;
|
|
end;
|
|
// writeln('MP4GetData33');
|
|
// Append new data to buffer
|
|
Move(NewSampleBuf^, AACI._Buf[CurBufSize], NewBytesDecoded);
|
|
// Update current unprocessed data count
|
|
CurBufSize:= CurBufSize + NewBytesDecoded;
|
|
// writeln('MP4GetData34');
|
|
end;
|
|
|
|
// writeln('MP4GetData4');
|
|
|
|
// set AACI.fBufEnd to last valid data byte in buffer
|
|
AACI.fBufEnd:= CurBufSize;
|
|
|
|
// writeln('MP4GetData5');
|
|
|
|
// If requested Byte count is more tha what we have, reduce requested count..
|
|
if Bytes > AACI.fBufEnd - AACI.fBufStart then
|
|
Bytes := AACI.fBufEnd - AACI.fBufStart;
|
|
|
|
// writeln('MP4GetData6');
|
|
|
|
// pass data back to calling function/procedure
|
|
Buffer := @AACI._Buf[AACI.fBufStart];
|
|
|
|
// writeln('MP4GetData7');
|
|
|
|
Inc(AACI.fBufStart, Bytes);
|
|
|
|
// writeln('MP4GetData8');
|
|
|
|
end;
|
|
|
|
function MP4Seek(var AACI: TAACInfo; var SampleNum: longint) : Boolean;
|
|
begin
|
|
Result := False;
|
|
if (AACI.Size = 0) or (AACI.TotalSamples = 0) then
|
|
Exit;
|
|
|
|
if SampleNum > AACI.TotalSamples then
|
|
SampleNum := AACI.TotalSamples;
|
|
|
|
AACI.Position:= Trunc(SampleNum / AACI.TotalSamples * AACI.Size);
|
|
Result := True;
|
|
end;
|
|
|
|
procedure LoadNeAAC(NeAAC : AnsiString);
|
|
var
|
|
thelib: string;
|
|
Begin
|
|
if Length(NeAAC) = 0 then thelib := libaa else thelib := NeAAC;
|
|
hNeAAC:= DynLibs.SafeLoadLibrary(PChar(thelib));
|
|
NeAACLoaded:= hNeAAC <> dynlibs.NilHandle;
|
|
|
|
Pointer(NeAACDecGetErrorMessage) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecGetErrorMessage'));
|
|
Pointer(NeAACDecGetCapabilities) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecGetCapabilities'));
|
|
Pointer(NeAACDecOpen) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecOpen'));
|
|
Pointer(NeAACDecGetCurrentConfiguration) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecGetCurrentConfiguration'));
|
|
Pointer(NeAACDecSetConfiguration) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecSetConfiguration'));
|
|
Pointer(NeAACDecInit) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecInit'));
|
|
Pointer(NeAACDecInit2) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecInit2'));
|
|
Pointer(NeAACDecPostSeekReset) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecPostSeekReset'));
|
|
Pointer(NeAACDecClose) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecClose'));
|
|
Pointer(NeAACDecDecode) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecDecode'));
|
|
Pointer(NeAACDecDecode2) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecDecode2'));
|
|
Pointer(NeAACDecPostSeekReset) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecPostSeekReset'));
|
|
Pointer(NeAACDecAudioSpecificConfig) :=
|
|
GetProcAddress(hNeAAC, PChar('NeAACDecAudioSpecificConfig'));
|
|
end;
|
|
|
|
Procedure UnLoadNeAAC;
|
|
Begin
|
|
if NeAACLoaded then
|
|
DynLibs.UnloadLibrary(hNeAAC);
|
|
NeAACLoaded:= False;
|
|
end;
|
|
|
|
Function Is_NeAAC_Loaded : Boolean;
|
|
Begin
|
|
Result:= NeAACLoaded;
|
|
end;
|
|
|
|
////////////////////////////// from mcwMP4FF.pas by Franklyn A. Harlow
|
|
|
|
function GetAACTrack(infile : mp4ff_t) : Integer;
|
|
var
|
|
i, rc, numTracks : Integer;
|
|
buff : pcfloat;
|
|
buff_size : LongWord;
|
|
mp4ASC : mp4AudioSpecificConfig;
|
|
begin
|
|
numTracks := mp4ff_total_tracks(infile);
|
|
for i := 0 to numTracks - 1 do
|
|
begin
|
|
buff := nil;
|
|
buff_size:=0;
|
|
mp4ff_get_decoder_config(infile, i, buff, buff_size);
|
|
if buff <> nil then
|
|
begin
|
|
rc := NeAACDecAudioSpecificConfig(buff, buff_size, mp4ASC);
|
|
mp4ff_free_decoder_config(buff);
|
|
if rc < 0 then
|
|
continue;
|
|
Result := i;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure UnLoadMp4ff;
|
|
begin
|
|
if Mp4ffLoaded then
|
|
|
|
DynLibs.UnloadLibrary(hMp4ff);
|
|
|
|
Mp4ffLoaded := False;
|
|
end;
|
|
|
|
Function isMp4ffLoaded : Boolean;
|
|
Begin
|
|
Result:= Mp4ffLoaded;
|
|
end;
|
|
|
|
|
|
procedure Loadmp4ff(mp4ff : AnsiString);
|
|
var
|
|
thelib: string;
|
|
begin
|
|
if Mp4ffLoaded then
|
|
Exit;
|
|
if Length(mp4ff) = 0 then thelib := libm4 else thelib := mp4ff;
|
|
hMp4ff := DynLibs.SafeLoadLibrary(PChar(thelib));
|
|
Mp4ffLoaded := hMp4ff <> dynlibs.NilHandle;
|
|
|
|
// writeln('hMp4ff' + inttostr(hMp4ff));
|
|
|
|
Pointer(mp4ff_open_read) :=
|
|
GetProcAddress(hMp4ff, pchar('mp4ff_open_read'));
|
|
|
|
// if Pointer(mp4ff_open_read) <> nil then
|
|
// writeln('mp4ff_open_read OK') else
|
|
// writeln('mp4ff_open_read NOT OK');
|
|
|
|
Pointer(mp4ff_open_read_metaonly) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_open_read_metaonly'));
|
|
Pointer(mp4ff_close) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_close'));
|
|
Pointer(mp4ff_get_sample_duration) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_duration'));
|
|
Pointer(mp4ff_get_sample_duration_use_offsets) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_duration_use_offsets'));
|
|
Pointer(mp4ff_get_sample_position) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_position'));
|
|
Pointer(mp4ff_get_sample_offset) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_offset'));
|
|
Pointer(mp4ff_find_sample) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_find_sample'));
|
|
|
|
Pointer(mp4ff_find_sample_use_offsets) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_find_sample_use_offsets'));
|
|
Pointer(mp4ff_set_sample_position) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_set_sample_position'));
|
|
Pointer(mp4ff_read_sample) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_read_sample'));
|
|
Pointer(mp4ff_read_sample_v2) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_read_sample_v2'));
|
|
Pointer(mp4ff_read_sample_getsize) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_read_sample_getsize'));
|
|
Pointer(mp4ff_get_sample_position) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_position'));
|
|
Pointer(mp4ff_get_sample_offset) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_offset'));
|
|
Pointer(mp4ff_find_sample) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_find_sample'));
|
|
|
|
Pointer(mp4ff_get_decoder_config) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_decoder_config'));
|
|
Pointer(mp4ff_get_track_type) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_track_type'));
|
|
Pointer(mp4ff_total_tracks) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_total_tracks'));
|
|
Pointer(mp4ff_num_samples) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_num_samples'));
|
|
Pointer(mp4ff_time_scale) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_time_scale'));
|
|
Pointer(mp4ff_get_avg_bitrate) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_avg_bitrate'));
|
|
Pointer(mp4ff_get_max_bitrate) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_max_bitrate'));
|
|
Pointer(mp4ff_get_track_duration) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_track_duration'));
|
|
|
|
Pointer(mp4ff_get_track_duration_use_offsets) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_track_duration_use_offsets'));
|
|
Pointer(mp4ff_get_sample_rate) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_sample_rate'));
|
|
Pointer(mp4ff_get_channel_count) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_channel_count'));
|
|
Pointer(mp4ff_get_audio_type) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_get_audio_type'));
|
|
Pointer(mp4ff_free_decoder_config) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_free_decoder_config'));
|
|
Pointer(mp4ff_meta_get_num_items) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_num_items'));
|
|
Pointer(mp4ff_meta_get_by_index) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_by_index'));
|
|
Pointer(mp4ff_meta_get_title) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_title'));
|
|
|
|
Pointer(mp4ff_meta_get_artist) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_artist'));
|
|
Pointer(mp4ff_meta_get_writer) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_writer'));
|
|
Pointer(mp4ff_meta_get_album) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_album'));
|
|
Pointer(mp4ff_meta_get_date) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_date'));
|
|
Pointer(mp4ff_meta_get_tool) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_tool'));
|
|
Pointer(mp4ff_meta_get_comment) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_comment'));
|
|
Pointer(mp4ff_meta_get_genre) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_genre'));
|
|
Pointer(mp4ff_meta_get_track) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_track'));
|
|
|
|
Pointer(mp4ff_meta_get_disc) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_disc'));
|
|
Pointer(mp4ff_meta_get_totaltracks) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_totaltracks'));
|
|
Pointer(mp4ff_meta_get_totaldiscs) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_totaldiscs'));
|
|
Pointer(mp4ff_meta_get_compilation) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_compilation'));
|
|
Pointer(mp4ff_meta_get_tempo) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_tempo'));
|
|
Pointer(mp4ff_meta_get_coverart) :=
|
|
GetProcAddress(hMp4ff, PChar('mp4ff_meta_get_coverart'));
|
|
|
|
end;
|
|
|
|
initialization
|
|
NeAACLoaded:= False;
|
|
SetExceptionMask(GetExceptionMask + [exZeroDivide] + [exInvalidOp] +
|
|
[exDenormalized] + [exOverflow] + [exPrecision]);
|
|
|
|
finalization
|
|
end.
|