@@ -4,6 +4,7 @@ import 'dart:typed_data';
44
55import 'package:audio_metadata_reader/src/metadata/mp4_metadata.dart' ;
66import 'package:audio_metadata_reader/src/parsers/tag_parser.dart' ;
7+ import 'package:audio_metadata_reader/src/utils/buffer.dart' ;
78import 'package:mime/mime.dart' ;
89
910import '../../audio_metadata_reader.dart' ;
@@ -68,24 +69,26 @@ final supportedBox = [
6869///
6970class MP4Parser extends TagParser {
7071 Mp4Metadata tags = Mp4Metadata ();
72+ late final Buffer buffer;
7173
7274 MP4Parser ({fetchImage = false }) : super (fetchImage: fetchImage);
7375
7476 @override
7577 ParserTag parse (RandomAccessFile reader) {
7678 reader.setPositionSync (0 );
79+ buffer = Buffer (randomAccessFile: reader);
7780
7881 final lengthFile = reader.lengthSync ();
7982
80- while (reader. positionSync () < lengthFile) {
81- final box = _readBox (reader );
83+ while (buffer.fileCursor < lengthFile) {
84+ final box = _readBox (buffer );
8285
8386 if (supportedBox.contains (box.type)) {
84- processBox (reader , box);
87+ processBox (buffer , box);
8588 } else {
8689 // We substract 8 to the box size because we already read the data for
8790 // the box header
88- reader. setPositionSync (reader. positionSync () + box.size - 8 );
91+ buffer. skip ( box.size - 8 );
8992 }
9093 }
9194
@@ -100,8 +103,8 @@ class MP4Parser extends TagParser {
100103 /// [0...3] -> box size (header + body)
101104 /// [4...7] -> box name (ASCII)
102105 ///
103- BoxHeader _readBox (RandomAccessFile reader ) {
104- final headerBytes = reader. readSync (8 );
106+ BoxHeader _readBox (Buffer buffer ) {
107+ final headerBytes = buffer. read (8 );
105108 final parser = ByteData .sublistView (headerBytes);
106109
107110 final boxSize = parser.getUint32 (0 );
@@ -119,43 +122,48 @@ class MP4Parser extends TagParser {
119122 return BoxHeader (boxSize, String .fromCharCodes (boxNameBytes));
120123 }
121124
122- ///
123125 /// Parse a box
124126 ///
125127 /// The metadata are inside special boxes. We only read data when we need it
126128 /// otherwise we skip them
127- ///
128- void processBox (RandomAccessFile reader, BoxHeader box) {
129+ void processBox (Buffer buffer, BoxHeader box) {
129130 if (box.type == "moov" ) {
130- parseRecurvise (reader , box);
131+ parseRecurvise (buffer , box);
131132 } else if (box.type == "mvhd" ) {
132- final bytes = reader. readSync (100 );
133+ final bytes = buffer. read (100 );
133134
134135 final timeScale = getUint32 (bytes.sublist (12 , 16 ));
135136 final timeUnit = getUint32 (bytes.sublist (16 , 20 ));
136137
137138 double microseconds = (timeUnit / timeScale) * 1000000 ;
138139 tags.duration = Duration (microseconds: microseconds.toInt ());
139140 } else if (box.type == "udta" ) {
140- parseRecurvise (reader , box);
141+ parseRecurvise (buffer , box);
141142 } else if (box.type == "ilst" ) {
142- parseRecurvise (reader , box);
143+ parseRecurvise (buffer , box);
143144 } else if (["trak" , "mdia" , "minf" , "stbl" , "stsd" ].contains (box.type)) {
144- parseRecurvise (reader , box);
145+ parseRecurvise (buffer , box);
145146 } else if (box.type == "meta" ) {
146- reader. readSync (4 );
147+ buffer. read (4 );
147148
148- parseRecurvise (reader , box);
149+ parseRecurvise (buffer , box);
149150 } else if (box.type[0 ] == "©" ||
150151 ["gnre" , "trkn" , "disk" , "tmpo" , "cpil" , "too" , "covr" , "pgap" , "gen" ]
151152 .contains (box.type)) {
152- final bytes = reader.readSync (box.size - 8 );
153+ final metadataValue = buffer.read (box.size - 8 );
154+
155+ // sometimes the data is stored inside another box called `data`
156+ // we try to find out if the data contains the box type "data" (0:4 is the box size)
157+ // otherwise we just skip the Apple's tag of 4 chars
158+ final data = (String .fromCharCodes (metadataValue.sublist (4 , 8 )) == "data" )
159+ ? metadataValue.sublist (16 )
160+ : metadataValue.sublist (4 );
153161
154162 String value;
155163 try {
156- value = const Utf8Decoder (). convert (bytes. sublist ( 16 ) );
164+ value = utf8. decode (data );
157165 } catch (e) {
158- value = String . fromCharCodes (bytes. sublist ( 16 ) );
166+ value = latin1. decode (data );
159167 }
160168
161169 final boxName = (box.type[0 ] == "©" ) ? box.type.substring (1 ) : box.type;
@@ -190,37 +198,37 @@ class MP4Parser extends TagParser {
190198 case "too" :
191199 break ;
192200 case "disk" :
193- tags.discNumber = getUint16 (bytes .sublist (18 , 20 ));
194- tags.totalDiscs = getUint16 (bytes .sublist (20 , 22 ));
201+ tags.discNumber = getUint16 (data .sublist (2 , 4 ));
202+ tags.totalDiscs = getUint16 (data .sublist (4 , 6 ));
195203 break ;
196204
197205 case "covr" :
198206 if (fetchImage) {
199- final imageData = bytes. sublist ( 16 ) ;
207+ final imageData = data ;
200208 tags.picture = Picture (
201209 imageData,
202210 lookupMimeType ("no path" , headerBytes: imageData) ?? "" ,
203211 PictureType .coverFront);
204212 }
205213 case "trkn" :
206- final a = getUint16 (bytes .sublist (18 , 20 ));
207- final totalTracks = getUint16 (bytes .sublist (20 , 22 ));
214+ final a = getUint16 (data .sublist (2 , 4 ));
215+ final totalTracks = getUint16 (data .sublist (4 , 6 ));
208216 tags.totalTracks = totalTracks;
209217 if (a > 0 ) {
210218 tags.trackNumber = a;
211219 }
212220 break ;
213221 }
214222 } else if (box.type == "----" ) {
215- final mean = _readBox (reader );
216- String .fromCharCodes (reader. readSync (mean.size - 8 )); // mean value
223+ final mean = _readBox (buffer );
224+ String .fromCharCodes (buffer. read (mean.size - 8 )); // mean value
217225
218- final name = _readBox (reader );
226+ final name = _readBox (buffer );
219227
220228 final nameValue =
221- String .fromCharCodes (reader. readSync (name.size - 8 ).sublist (4 ));
222- final dataBox = _readBox (reader );
223- final data = reader. readSync (dataBox.size - 8 );
229+ String .fromCharCodes (buffer. read (name.size - 8 ).sublist (4 ));
230+ final dataBox = _readBox (buffer );
231+ final data = buffer. read (dataBox.size - 8 );
224232 final finalValue = String .fromCharCodes (data.sublist (8 ));
225233
226234 switch (nameValue) {
@@ -233,19 +241,17 @@ class MP4Parser extends TagParser {
233241 default :
234242 }
235243 } else if (box.type == "mp4a" ) {
236- final bytes = reader. readSync (box.size - 8 );
244+ final bytes = buffer. read (box.size - 8 );
237245
238246 // tags.bitrate = timeScale;
239247 tags.sampleRate = getUint32 (bytes.sublist (22 , 26 ));
240248 } else {
241- reader .setPositionSync (reader. positionSync () + box.size - 8 );
249+ buffer .setPositionSync (buffer.fileCursor + box.size - 8 );
242250 }
243251 }
244252
245- ///
246253 /// Parse a box with multiple sub boxes.
247- ///
248- void parseRecurvise (RandomAccessFile reader, BoxHeader box) {
254+ void parseRecurvise (Buffer buffer, BoxHeader box) {
249255 final limit = box.size - 8 ;
250256 int offset = 0 ;
251257
@@ -254,26 +260,24 @@ class MP4Parser extends TagParser {
254260 offset += 4 ;
255261 } else if (box.type == "stsd" ) {
256262 offset += 8 ;
257- reader. readSync (8 );
263+ buffer. read (8 );
258264 }
259265
260266 while (offset < limit) {
261- final newBox = _readBox (reader );
267+ final newBox = _readBox (buffer );
262268
263269 if (supportedBox.contains (newBox.type)) {
264- processBox (reader , newBox);
270+ processBox (buffer , newBox);
265271 } else {
266- reader. setPositionSync (reader. positionSync () + newBox.size - 8 );
272+ buffer. skip ( newBox.size - 8 );
267273 }
268274
269275 offset += newBox.size;
270276 }
271277 }
272278
273- ///
274279 /// To detect if this parser can be used to parse this file, we need to detect
275280 /// the first box. It should be a `ftyp` box
276- ///
277281 static bool canUserParser (RandomAccessFile reader) {
278282 reader.setPositionSync (4 );
279283
0 commit comments