mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
ADD: Simple m4a parser
This commit is contained in:
parent
bcb56b41f7
commit
6ea2eb3494
3 changed files with 234 additions and 2 deletions
|
|
@ -10,7 +10,7 @@ const
|
|||
DETECT_STRING: String = '(EXT="MP3") | (EXT="MP2") | (EXT="MP1") | (EXT="OGG") | (EXT="WMA") | ' +
|
||||
'(EXT="WAV") | (EXT="VQF") | (EXT="AAC") | (EXT="APE") | (EXT="MPC") | ' +
|
||||
'(EXT="FLAC") | (EXT="CDA") | (EXT="TTA") | (EXT="AC3") | (EXT="DTS") | ' +
|
||||
'(EXT="WV") | (EXT="WVC") | (EXT="OFR") | (EXT="OFS")';
|
||||
'(EXT="WV") | (EXT="WVC") | (EXT="OFR") | (EXT="OFS") | (EXT="M4A")';
|
||||
|
||||
const
|
||||
FIELD_COUNT = 20;
|
||||
|
|
|
|||
211
plugins/wdx/audioinfo/src/atl/MP4file.pas
Normal file
211
plugins/wdx/audioinfo/src/atl/MP4file.pas
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
{
|
||||
Double commander
|
||||
-------------------------------------------------------------------------
|
||||
Class TMP4file - for manipulating with M4A audio file information
|
||||
|
||||
Copyright (C) 2016 Alexander Koblov (alexx2000@mail.ru)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
}
|
||||
|
||||
unit MP4file;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, DCClassesUtf8;
|
||||
|
||||
type
|
||||
TAtomName = Array [0..3] of AnsiChar;
|
||||
|
||||
{ TMP4file }
|
||||
|
||||
TMP4file = class
|
||||
private
|
||||
FFileSize: Int64;
|
||||
FStream: TStream;
|
||||
function GetValid: Boolean;
|
||||
private
|
||||
FChannels: Word;
|
||||
FBitRate: Double;
|
||||
FDuration: Double;
|
||||
FSampleSize: Word;
|
||||
FSampleRate: LongWord;
|
||||
protected
|
||||
procedure ResetData;
|
||||
procedure ReadMovieHeader;
|
||||
procedure ReadSampleDescription;
|
||||
function FindAtomHeader(const AName: TAtomName; ASize: PInt64 = nil): Boolean;
|
||||
function LoadAtomHeader(out AtomName: TAtomName; out AtomSize: Int64): Boolean;
|
||||
public
|
||||
constructor Create; { Create object }
|
||||
destructor Destroy; override; { Destroy object }
|
||||
function ReadFromFile(const FileName: String): Boolean; { Load header }
|
||||
property FileSize: Int64 read FFileSize; { File size (bytes) }
|
||||
property Channels: Word read FChannels; { Number of channels }
|
||||
property SampleRate: LongWord read FSampleRate; { Sample rate (hz) }
|
||||
property BitRate: Double read FBitRate; { Bit rate (bit/s) }
|
||||
property Duration: Double read FDuration; { Duration (seconds) }
|
||||
property Valid: Boolean read GetValid; { True if data valid }
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TMP4file }
|
||||
|
||||
function TMP4file.FindAtomHeader(const AName: TAtomName; ASize: PInt64): Boolean;
|
||||
var
|
||||
AtomSize: Int64;
|
||||
APosition: Int64;
|
||||
AtomName: TAtomName;
|
||||
begin
|
||||
repeat
|
||||
if not LoadAtomHeader(AtomName, AtomSize) then
|
||||
Break;
|
||||
if SameText(AtomName, AName) then
|
||||
begin
|
||||
if Assigned(ASize) then ASize^:= AtomSize;
|
||||
Exit(True);
|
||||
end
|
||||
else begin
|
||||
APosition:= FStream.Seek(AtomSize, soCurrent);
|
||||
end;
|
||||
until (APosition >= FFileSize);
|
||||
Result:= False;
|
||||
end;
|
||||
|
||||
function TMP4file.GetValid: Boolean;
|
||||
begin
|
||||
Result:= (FDuration > 0.0) and (FBitRate > 0.0);
|
||||
end;
|
||||
|
||||
function TMP4file.LoadAtomHeader(out AtomName: TAtomName; out AtomSize: Int64): Boolean;
|
||||
begin
|
||||
AtomSize:= SwapEndian(FStream.ReadDWord);
|
||||
FillChar(AtomName, SizeOf(TAtomName), #0);
|
||||
FStream.Read(AtomName, SizeOf(TAtomName));
|
||||
if AtomSize <> 1 then
|
||||
AtomSize:= AtomSize - 8
|
||||
else begin
|
||||
AtomSize:= Int64(SwapEndian(FStream.ReadQWord)) - 16;
|
||||
end;
|
||||
Result:= not ((AtomSize < 0) or (AtomSize > FFileSize));
|
||||
end;
|
||||
|
||||
procedure TMP4file.ResetData;
|
||||
begin
|
||||
FBitRate:= 0;
|
||||
FChannels:= 0;
|
||||
FDuration:= 0.0;
|
||||
FSampleSize:= 0;
|
||||
FSampleRate:= 0;
|
||||
end;
|
||||
|
||||
procedure TMP4file.ReadMovieHeader;
|
||||
var
|
||||
AVersion: Byte;
|
||||
MediaSize: Int64;
|
||||
ADuration: QWord;
|
||||
TimeScale: LongWord = 0;
|
||||
begin
|
||||
FStream.Seek(0, soBeginning);
|
||||
if FindAtomHeader('moov') and FindAtomHeader('mvhd') then
|
||||
begin
|
||||
AVersion:= FStream.ReadByte;
|
||||
FStream.Seek(3, soCurrent);
|
||||
if AVersion = 0 then
|
||||
begin
|
||||
FStream.Seek(8, soCurrent);
|
||||
TimeScale:= SwapEndian(FStream.ReadDWord);
|
||||
ADuration:= SwapEndian(FStream.ReadDWord);
|
||||
end
|
||||
else if AVersion = 1 then
|
||||
begin
|
||||
FStream.Seek(16, soCurrent);
|
||||
TimeScale:= SwapEndian(FStream.ReadDWord);
|
||||
ADuration:= SwapEndian(FStream.ReadQWord);
|
||||
end;
|
||||
if TimeScale > 0 then FDuration:= ADuration / TimeScale;
|
||||
end;
|
||||
FStream.Seek(0, soBeginning);
|
||||
if (FDuration > 0) and FindAtomHeader('mdat', @MediaSize) then
|
||||
begin
|
||||
FBitRate:= MediaSize * 8 / FDuration / 1000;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMP4file.ReadSampleDescription;
|
||||
var
|
||||
Number: LongWord;
|
||||
begin
|
||||
if not FindAtomHeader('moov') then Exit;
|
||||
if not FindAtomHeader('trak') then Exit;
|
||||
if not FindAtomHeader('mdia') then Exit;
|
||||
if not FindAtomHeader('minf') then Exit;
|
||||
if not FindAtomHeader('stbl') then Exit;
|
||||
|
||||
if FindAtomHeader('stsd') then
|
||||
begin
|
||||
FStream.Seek(4, soCurrent);
|
||||
Number:= SwapEndian(FStream.ReadDWord);
|
||||
if Number = 1 then
|
||||
begin
|
||||
if FindAtomHeader('mp4a') then
|
||||
begin
|
||||
FStream.Seek(16, soCurrent);
|
||||
FChannels:= SwapEndian(FStream.ReadWord);
|
||||
FSampleSize:= SwapEndian(FStream.ReadWord);
|
||||
FStream.Seek(2, soCurrent);
|
||||
FSampleRate:= SwapEndian(FStream.ReadDWord);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TMP4file.Create;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
destructor TMP4file.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TMP4file.ReadFromFile(const FileName: String): Boolean;
|
||||
var
|
||||
AtomSize: Int64;
|
||||
AtomName: TAtomName;
|
||||
begin
|
||||
ResetData;
|
||||
FStream:= TFileStreamEx.Create(FileName, fmOpenRead or fmShareDenyNone);
|
||||
try
|
||||
FFileSize:= FStream.Size;
|
||||
Result:= LoadAtomHeader(AtomName, AtomSize) and SameText(AtomName, 'ftyp');
|
||||
if Result then
|
||||
begin
|
||||
FStream.Seek(AtomSize, soCurrent);
|
||||
ReadSampleDescription;
|
||||
ReadMovieHeader;
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(FStream);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ interface
|
|||
uses
|
||||
Classes, SysUtils, DCStrUtils, MPEGaudio, Musepack, OggVorbis, ID3v1, ID3v2,
|
||||
APEtag, FLACfile, Monkey, AACfile, CDAtrack, WMAfile, WAVfile, TTA, TwinVQ,
|
||||
AC3, DTS, WAVPackfile, OptimFROG;
|
||||
AC3, DTS, WAVPackfile, OptimFROG, MP4file;
|
||||
|
||||
type
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ type
|
|||
FTTA: TTTA;
|
||||
FTwinVQ: TTwinVQ;
|
||||
FMonkey: TMonkey;
|
||||
FMP4file: TMP4file;
|
||||
FAACfile: TAACfile;
|
||||
FWMAfile: TWMAfile;
|
||||
FWAVfile: TWAVfile;
|
||||
|
|
@ -69,6 +70,7 @@ type
|
|||
function ReadTTA: Boolean;
|
||||
function ReadTwinVQ: Boolean;
|
||||
function ReadMonkey: Boolean;
|
||||
function ReadMP4file: Boolean;
|
||||
function ReadAACfile: Boolean;
|
||||
function ReadWMAfile: Boolean;
|
||||
function ReadWAVfile: Boolean;
|
||||
|
|
@ -298,6 +300,19 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TAudioData.ReadMP4file: Boolean;
|
||||
begin
|
||||
Result:= FMP4file.ReadFromFile(FFileName) and FMP4file.Valid;
|
||||
if Result then
|
||||
begin
|
||||
SampleRate:= FMP4file.SampleRate;
|
||||
BitRate:= Round(FMP4file.BitRate);
|
||||
Duration:= Round(FMP4file.Duration);
|
||||
DurationHMS:= FormatDuration(Duration);
|
||||
Channels:= FormatChannels(FMP4file.Channels);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TAudioData.ReadAACfile: Boolean;
|
||||
begin
|
||||
Result:= FAACfile.ReadFromFile(FFileName) and FAACfile.Valid;
|
||||
|
|
@ -484,6 +499,7 @@ begin
|
|||
FTTA:= TTTA.Create;
|
||||
FTwinVQ:= TTwinVQ.Create;
|
||||
FMonkey:= TMonkey.Create;
|
||||
FMP4file:= TMP4file.Create;
|
||||
FAACfile:= TAACfile.Create;
|
||||
FWMAfile:= TWMAfile.Create;
|
||||
FWAVfile:= TWAVfile.Create;
|
||||
|
|
@ -503,6 +519,7 @@ begin
|
|||
FTTA.Free;
|
||||
FTwinVQ.Free;
|
||||
FMonkey.Free;
|
||||
FMP4file.Free;
|
||||
FAACfile.Free;
|
||||
FWMAfile.Free;
|
||||
FWAVfile.Free;
|
||||
|
|
@ -585,6 +602,10 @@ begin
|
|||
begin
|
||||
Result:= ReadOptimFrog;
|
||||
end
|
||||
else if (FileExt = 'm4a') then
|
||||
begin
|
||||
Result:= ReadMP4file;
|
||||
end
|
||||
else Result:= False;
|
||||
|
||||
if Result then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue