File Coverage

blib/lib/Image/ExifTool/AAC.pm
Criterion Covered Total %
statement 46 46 100.0
branch 11 22 50.0
condition 1 3 33.3
subroutine 5 5 100.0
pod 0 1 0.0
total 63 77 81.8


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: AAC.pm
3             #
4             # Description: Read AAC audio files
5             #
6             # Revisions: 2023-12-29 - P. Harvey Created
7             #------------------------------------------------------------------------------
8              
9             package Image::ExifTool::AAC;
10              
11 1     1   7131 use strict;
  1         4  
  1         54  
12 1     1   7 use vars qw($VERSION);
  1         2  
  1         59  
13 1     1   6 use Image::ExifTool qw(:DataAccess :Utils);
  1         4  
  1         316  
14 1     1   1781 use Image::ExifTool::FLAC;
  1         4  
  1         796  
15              
16             $VERSION = '1.00';
17              
18             my %convSampleRate = (
19             0 => 96000, 7 => 22050,
20             1 => 88200, 8 => 16000,
21             2 => 64000, 9 => 12000,
22             3 => 48000, 10 => 11025,
23             4 => 44100, 11 => 8000,
24             5 => 32000, 12 => 7350,
25             6 => 24000,
26             );
27              
28             %Image::ExifTool::AAC::Main = (
29             PROCESS_PROC => \&Image::ExifTool::FLAC::ProcessBitStream,
30             GROUPS => { 2 => 'Audio' },
31             NOTES => 'Tags extracted from Advanced Audio Coding (AAC) files.',
32             # Bit000-011 - sync word (all 1's)
33             # Bit012 - ID (seems to be always 0)
34             # Bit013-014 - layer (00)
35             # Bit015 - CRC absent (0=crc exists, 1=no crc)
36             'Bit016-017' => {
37             Name => 'ProfileType',
38             PrintConv => {
39             0 => 'Main',
40             1 => 'Low Complexity',
41             2 => 'Scalable Sampling Rate',
42             },
43             },
44             'Bit018-021' => {
45             Name => 'SampleRate',
46             ValueConv => \%convSampleRate,
47             },
48             # Bit022 - private
49             'Bit023-025' => {
50             Name => 'Channels',
51             PrintConv => {
52             0 => '?',
53             1 => 1,
54             2 => 2,
55             3 => 3,
56             4 => 4,
57             5 => 5,
58             6 => '5+1',
59             7 => '7+1',
60             },
61             },
62             # Bit026 - original/copy
63             # Bit027 - home
64             # Bit028 - copyright ID
65             # Bit029 - copyright start
66             # Bit030-042 - FrameLength
67             # Bit043-053 - buffer fullness
68             # Bit054-055 - BlocksInFrame (minus 1)
69             # Note: Bitrate for frame = FrameLength * 8 * SampleRate / ((BlocksInFrame+1) * 1024)
70             # - but all frames must be scanned to calculate average bitrate
71             Encoder => {
72             Name => 'Encoder',
73             Notes => 'taken from filler payload of first frame',
74             },
75             );
76              
77             #------------------------------------------------------------------------------
78             # Read information from an AAC file
79             # Inputs: 0) ExifTool object reference, 1) Directory information reference
80             # Returns: 1 on success, 0 if this wasn't a valid AAC file
81             sub ProcessAAC($$)
82             {
83 1     1 0 3 my ($et, $dirInfo) = @_;
84 1         2 my $raf = $$dirInfo{RAF};
85 1         1 my ($buff, $buf2);
86              
87             # format of frame header (7 bytes):
88             # SSSS SSSS SSSS ILLC PPRR RRpC CCoh csff ffff ffff fffb bbbb bbbb bbNN
89             # 1111 1111 1111 0001 0110 0000 0100 0000 0000 0101 0101 1111 1111 1100 (eg.)
90             # S = sync word o = original/copy
91             # I = ID h = home
92             # L = layer (00) c = copyright ID
93             # C = CRC absent s = copyright start
94             # P = profile object type f = frame length
95             # R = sampling rate index b = buffer fullness
96             # p = private N = number of raw data blocks in frame
97             # C = channel configuration
98              
99 1 50       9 $raf->Read($buff, 7) == 7 or return 0;
100 1 50       4 return 0 unless $buff =~ /^\xff[\xf0\xf1]/;
101 1         6 my @t = unpack('NnC', $buff);
102 1 50       3 return 0 if (($t[0] >> 16) & 0x03) == 3; # (reserved profile type)
103 1 50       2 return 0 if (($t[0] >> 12) & 0x0f) > 12; # validate sampling frequency index
104 1         3 my $len = (($t[0] << 11) & 0x1800) | (($t[1] >> 5) & 0x07ff);
105 1 50       2 return 0 if $len < 7;
106              
107 1         5 $et->SetFileType();
108              
109 1         3 my $tagTablePtr = GetTagTable('Image::ExifTool::AAC::Main');
110 1         6 $et->ProcessDirectory({ DataPt => \$buff }, $tagTablePtr);
111              
112             # read the first frame data to check for a filler with the encoder name
113 1   33     7 while ($len > 8 and $raf->Read($buff, $len-7) == $len-7) {
114 1         2 my $noCRC = ($t[0] & 0x00010000);
115 1         1 my $blocks = ($t[2] & 0x03);
116 1         6 my $pos = 0;
117 1 50       2 $pos += 2 + 2 * $blocks unless $noCRC;
118 1 50       3 last if $pos + 2 > length($buff);
119 1         3 my $tmp = unpack("x${pos}n", $buff);
120 1         2 my $id = $tmp >> 13;
121             # read filler payload
122 1 50       3 if ($id == 6) {
123 1         1 my $cnt = ($tmp >> 9) & 0x0f;
124 1         2 ++$pos;
125 1 50       4 if ($cnt == 15) {
126 1         2 $cnt += (($tmp >> 1) & 0xff) - 1;
127 1         2 ++$pos;
128             }
129 1 50       2 if ($pos + $cnt <= length($buff)) {
130 1         3 my $dat = substr($buff, $pos, $cnt);
131 1         5 $dat =~ s/^\0+//;
132 1         3 $dat =~ s/\0+$//;
133 1 50       6 $et->HandleTag($tagTablePtr, Encoder => $dat) if $dat =~ /^[\x20-\x7e]+$/;
134             }
135             }
136 1         3 last;
137             }
138              
139 1         2 return 1;
140             }
141              
142             1; # end
143              
144             __END__