2
* Entagged Audio Tag library
3
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
package org.jaudiotagger.audio.mp4;
21
import org.jaudiotagger.audio.exceptions.CannotReadException;
22
import org.jaudiotagger.audio.exceptions.CannotReadVideoException;
23
import org.jaudiotagger.audio.generic.GenericAudioHeader;
24
import org.jaudiotagger.audio.mp4.atom.*;
25
import org.jaudiotagger.logging.ErrorMessage;
27
import java.io.IOException;
28
import java.io.RandomAccessFile;
29
import java.nio.ByteBuffer;
30
import java.util.logging.Logger;
33
* Read audio info from file.
36
* The info is held in the mvdh and mdhd fields as shown below
43
* |...............|----- mdia
44
* |.......................|---- mdhd
45
* |.......................|---- minf
46
* |..............................|---- smhd
47
* |..............................|---- stbl
48
* |......................................|--- stsd
49
* |.............................................|--- mp4a
55
public class Mp4InfoReader
58
public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.mp4.atom");
60
public GenericAudioHeader read(RandomAccessFile raf) throws CannotReadException, IOException
62
Mp4AudioHeader info = new Mp4AudioHeader();
65
Mp4BoxHeader ftypHeader = Mp4BoxHeader.seekWithinLevel(raf, Mp4NotMetaFieldKey.FTYP.getFieldName());
66
if (ftypHeader == null)
68
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_CONTAINER.getMsg());
70
ByteBuffer ftypBuffer = ByteBuffer.allocate(ftypHeader.getLength() - Mp4BoxHeader.HEADER_LENGTH);
71
raf.getChannel().read(ftypBuffer);
73
Mp4FtypBox ftyp = new Mp4FtypBox(ftypHeader, ftypBuffer);
75
info.setBrand(ftyp.getMajorBrand());
77
//Get to the facts everything we are interested in is within the moov box, so just load data from file
78
//once so no more file I/O needed
79
Mp4BoxHeader moovHeader = Mp4BoxHeader.seekWithinLevel(raf, Mp4NotMetaFieldKey.MOOV.getFieldName());
80
if (moovHeader == null)
82
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
84
ByteBuffer moovBuffer = ByteBuffer.allocate(moovHeader.getLength() - Mp4BoxHeader.HEADER_LENGTH);
85
raf.getChannel().read(moovBuffer);
88
//Level 2-Searching for "mvhd" somewhere within "moov", we make a slice after finding header
89
//so all get() methods will be relative to mvdh positions
90
Mp4BoxHeader boxHeader = Mp4BoxHeader.seekWithinLevel(moovBuffer, Mp4NotMetaFieldKey.MVHD.getFieldName());
91
if (boxHeader == null)
93
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
95
ByteBuffer mvhdBuffer = moovBuffer.slice();
96
Mp4MvhdBox mvhd = new Mp4MvhdBox(boxHeader, mvhdBuffer);
97
info.setLength(mvhd.getLength());
98
//Advance position, TODO should we put this in box code ?
99
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
101
//Level 2-Searching for "trak" within "moov"
102
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.TRAK.getFieldName());
103
int endOfFirstTrackInBuffer = mvhdBuffer.position() + boxHeader.getDataLength();
105
if (boxHeader == null)
107
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
109
//Level 3-Searching for "mdia" within "trak"
110
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDIA.getFieldName());
111
if (boxHeader == null)
113
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
115
//Level 4-Searching for "mdhd" within "mdia"
116
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDHD.getFieldName());
117
if (boxHeader == null)
119
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
121
Mp4MdhdBox mdhd = new Mp4MdhdBox(boxHeader, mvhdBuffer.slice());
122
info.setSamplingRate(mdhd.getSampleRate());
124
//Level 4-Searching for "hdlr" within "mdia"
125
/*We dont currently need to process this because contains nothing we want
126
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
127
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.HDLR.getFieldName());
128
if (boxHeader == null)
130
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
132
Mp4HdlrBox hdlr = new Mp4HdlrBox(boxHeader, mvhdBuffer.slice());
136
//Level 4-Searching for "minf" within "mdia"
137
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
138
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MINF.getFieldName());
139
if (boxHeader == null)
141
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
144
//Level 5-Searching for "smhd" within "minf"
145
//Only an audio track would have a smhd frame
146
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.SMHD.getFieldName());
147
if (boxHeader == null)
149
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
151
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
153
//Level 5-Searching for "stbl within "minf"
154
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.STBL.getFieldName());
155
if (boxHeader == null)
157
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
160
//Level 6-Searching for "stsd within "stbl" and process it direct data, dont think these are mandatory so dont throw
161
//exception if unable to find
162
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.STSD.getFieldName());
163
if (boxHeader != null)
165
Mp4StsdBox stsd = new Mp4StsdBox(boxHeader, mvhdBuffer);
167
int positionAfterStsdHeaderAndData = mvhdBuffer.position();
169
///Level 7-Searching for "mp4a within "stsd"
170
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MP4A.getFieldName());
171
if (boxHeader != null)
173
ByteBuffer mp4aBuffer = mvhdBuffer.slice();
174
Mp4Mp4aBox mp4a = new Mp4Mp4aBox(boxHeader, mp4aBuffer);
176
//Level 8-Searching for "esds" within mp4a to get No Of Channels and bitrate
177
boxHeader = Mp4BoxHeader.seekWithinLevel(mp4aBuffer, Mp4NotMetaFieldKey.ESDS.getFieldName());
178
if (boxHeader != null)
180
Mp4EsdsBox esds = new Mp4EsdsBox(boxHeader, mp4aBuffer.slice());
182
//Set Bitrate in kbps
183
info.setBitrate(esds.getAvgBitrate() / 1000);
185
//Set Number of Channels
186
info.setChannelNumber(esds.getNumberOfChannels());
188
info.setKind(esds.getKind());
189
info.setProfile(esds.getAudioProfile());
191
info.setEncodingType(EncoderType.AAC.getDescription());
196
//Level 7 -Searching for drms within stsd instead (m4p files)
197
mvhdBuffer.position(positionAfterStsdHeaderAndData);
198
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.DRMS.getFieldName());
199
if (boxHeader != null)
201
Mp4DrmsBox drms = new Mp4DrmsBox(boxHeader, mvhdBuffer);
204
//Level 8-Searching for "esds" within drms to get No Of Channels and bitrate
205
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.ESDS.getFieldName());
206
if (boxHeader != null)
208
Mp4EsdsBox esds = new Mp4EsdsBox(boxHeader, mvhdBuffer.slice());
210
//Set Bitrate in kbps
211
info.setBitrate(esds.getAvgBitrate() / 1000);
213
//Set Number of Channels
214
info.setChannelNumber(esds.getNumberOfChannels());
216
info.setKind(esds.getKind());
217
info.setProfile(esds.getAudioProfile());
219
info.setEncodingType(EncoderType.DRM_AAC.getDescription());
222
//Level 7-Searching for alac (Apple Lossless) instead
225
mvhdBuffer.position(positionAfterStsdHeaderAndData);
226
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.ALAC.getFieldName());
227
if (boxHeader != null)
230
Mp4AlacBox alac = new Mp4AlacBox(boxHeader, mvhdBuffer);
233
//Level 8-Searching for 2nd "alac" within box that contains the info we really want
234
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.ALAC.getFieldName());
235
if (boxHeader != null)
237
alac = new Mp4AlacBox(boxHeader, mvhdBuffer);
239
info.setEncodingType(EncoderType.APPLE_LOSSLESS.getDescription());
240
info.setChannelNumber(alac.getChannels());
241
info.setBitrate(alac.getBitRate()/1000);
247
//Set default channels if couldnt calculate it
248
if (info.getChannelNumber() == -1)
250
info.setChannelNumber(2);
253
//Set default bitrate if couldnt calculate it
254
if (info.getBitRateAsNumber() == -1)
256
info.setBitrate(128);
259
//This is the most likley option if cant find a match
260
if (info.getEncodingType().equals(""))
262
info.setEncodingType(EncoderType.AAC.getDescription());
265
logger.info(info.toString());
267
//Level 2-Searching for another "trak" within "moov", if more than one track exists then probably
268
//video so reject it, unless it fits into certain encoding patterns
269
mvhdBuffer.position(endOfFirstTrackInBuffer);
270
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.TRAK.getFieldName());
271
if (boxHeader != null)
273
//We only allow multiple tracks as audio if they follow the format used by the winamp encoder or
274
//are marked as being audio only and if track contains a nmhd atom rather than smhd.
275
//TODO this probably too restrictive but it fixes the test cases we have
276
if (ftyp.getMajorBrand().equals(Mp4FtypBox.Brand.ISO14496_1_VERSION_2.getId())
277
|| ftyp.getMajorBrand().equals(Mp4FtypBox.Brand.APPLE_AUDIO_ONLY.getId())
278
|| ftyp.getMajorBrand().equals(Mp4FtypBox.Brand.APPLE_AUDIO.getId()))
280
//Ok, need to do further checks on this track to ensure it is a scene descriptor
281
//Level 3-Searching for "mdia" within "trak"
282
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDIA.getFieldName());
283
if (boxHeader == null)
285
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
287
//Level 4-Searching for "mdhd" within "mdia"
288
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDHD.getFieldName());
289
if (boxHeader == null)
291
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
293
//Level 4-Searching for "minf" within "mdia"
294
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
295
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MINF.getFieldName());
296
if (boxHeader == null)
298
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
301
//Level 5-Searching for "nmhd" within "minf"
302
//Only an audio track would have a nmhd frame
303
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.NMHD.getFieldName());
304
if (boxHeader == null)
306
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
311
logger.info(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg() + ":" + ftyp.getMajorBrand());
312
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
316
//Build AtomTree to ensure it is valid, this means we can detect any problems early on
317
Mp4AtomTree atomTree = new Mp4AtomTree(raf,false);
2
* Entagged Audio Tag library
3
* Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
package org.jaudiotagger.audio.mp4;
21
import org.jaudiotagger.audio.exceptions.CannotReadException;
22
import org.jaudiotagger.audio.exceptions.CannotReadVideoException;
23
import org.jaudiotagger.audio.generic.GenericAudioHeader;
24
import org.jaudiotagger.audio.mp4.atom.*;
25
import org.jaudiotagger.logging.ErrorMessage;
27
import java.io.IOException;
28
import java.io.RandomAccessFile;
29
import java.nio.ByteBuffer;
30
import java.util.logging.Logger;
33
* Read audio info from file.
36
* The info is held in the mvdh and mdhd fields as shown below
43
* |...............|----- mdia
44
* |.......................|---- mdhd
45
* |.......................|---- minf
46
* |..............................|---- smhd
47
* |..............................|---- stbl
48
* |......................................|--- stsd
49
* |.............................................|--- mp4a
55
public class Mp4InfoReader
58
public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.mp4.atom");
60
public GenericAudioHeader read(RandomAccessFile raf) throws CannotReadException, IOException
62
Mp4AudioHeader info = new Mp4AudioHeader();
65
Mp4BoxHeader ftypHeader = Mp4BoxHeader.seekWithinLevel(raf, Mp4NotMetaFieldKey.FTYP.getFieldName());
66
if (ftypHeader == null)
68
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_CONTAINER.getMsg());
70
ByteBuffer ftypBuffer = ByteBuffer.allocate(ftypHeader.getLength() - Mp4BoxHeader.HEADER_LENGTH);
71
raf.getChannel().read(ftypBuffer);
73
Mp4FtypBox ftyp = new Mp4FtypBox(ftypHeader, ftypBuffer);
75
info.setBrand(ftyp.getMajorBrand());
77
//Get to the facts everything we are interested in is within the moov box, so just load data from file
78
//once so no more file I/O needed
79
Mp4BoxHeader moovHeader = Mp4BoxHeader.seekWithinLevel(raf, Mp4NotMetaFieldKey.MOOV.getFieldName());
80
if (moovHeader == null)
82
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
84
ByteBuffer moovBuffer = ByteBuffer.allocate(moovHeader.getLength() - Mp4BoxHeader.HEADER_LENGTH);
85
raf.getChannel().read(moovBuffer);
88
//Level 2-Searching for "mvhd" somewhere within "moov", we make a slice after finding header
89
//so all get() methods will be relative to mvdh positions
90
Mp4BoxHeader boxHeader = Mp4BoxHeader.seekWithinLevel(moovBuffer, Mp4NotMetaFieldKey.MVHD.getFieldName());
91
if (boxHeader == null)
93
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
95
ByteBuffer mvhdBuffer = moovBuffer.slice();
96
Mp4MvhdBox mvhd = new Mp4MvhdBox(boxHeader, mvhdBuffer);
97
info.setLength(mvhd.getLength());
98
//Advance position, TODO should we put this in box code ?
99
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
101
//Level 2-Searching for "trak" within "moov"
102
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.TRAK.getFieldName());
103
int endOfFirstTrackInBuffer = mvhdBuffer.position() + boxHeader.getDataLength();
105
if (boxHeader == null)
107
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
109
//Level 3-Searching for "mdia" within "trak"
110
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDIA.getFieldName());
111
if (boxHeader == null)
113
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
115
//Level 4-Searching for "mdhd" within "mdia"
116
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDHD.getFieldName());
117
if (boxHeader == null)
119
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
121
Mp4MdhdBox mdhd = new Mp4MdhdBox(boxHeader, mvhdBuffer.slice());
122
info.setSamplingRate(mdhd.getSampleRate());
124
//Level 4-Searching for "hdlr" within "mdia"
125
/*We dont currently need to process this because contains nothing we want
126
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
127
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.HDLR.getFieldName());
128
if (boxHeader == null)
130
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
132
Mp4HdlrBox hdlr = new Mp4HdlrBox(boxHeader, mvhdBuffer.slice());
136
//Level 4-Searching for "minf" within "mdia"
137
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
138
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MINF.getFieldName());
139
if (boxHeader == null)
141
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
144
//Level 5-Searching for "smhd" within "minf"
145
//Only an audio track would have a smhd frame
146
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.SMHD.getFieldName());
147
if (boxHeader == null)
149
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
151
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
153
//Level 5-Searching for "stbl within "minf"
154
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.STBL.getFieldName());
155
if (boxHeader == null)
157
throw new CannotReadException(ErrorMessage.MP4_FILE_NOT_AUDIO.getMsg());
160
//Level 6-Searching for "stsd within "stbl" and process it direct data, dont think these are mandatory so dont throw
161
//exception if unable to find
162
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.STSD.getFieldName());
163
if (boxHeader != null)
165
Mp4StsdBox stsd = new Mp4StsdBox(boxHeader, mvhdBuffer);
167
int positionAfterStsdHeaderAndData = mvhdBuffer.position();
169
///Level 7-Searching for "mp4a within "stsd"
170
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MP4A.getFieldName());
171
if (boxHeader != null)
173
ByteBuffer mp4aBuffer = mvhdBuffer.slice();
174
Mp4Mp4aBox mp4a = new Mp4Mp4aBox(boxHeader, mp4aBuffer);
176
//Level 8-Searching for "esds" within mp4a to get No Of Channels and bitrate
177
boxHeader = Mp4BoxHeader.seekWithinLevel(mp4aBuffer, Mp4NotMetaFieldKey.ESDS.getFieldName());
178
if (boxHeader != null)
180
Mp4EsdsBox esds = new Mp4EsdsBox(boxHeader, mp4aBuffer.slice());
182
//Set Bitrate in kbps
183
info.setBitrate(esds.getAvgBitrate() / 1000);
185
//Set Number of Channels
186
info.setChannelNumber(esds.getNumberOfChannels());
188
info.setKind(esds.getKind());
189
info.setProfile(esds.getAudioProfile());
191
info.setEncodingType(EncoderType.AAC.getDescription());
196
//Level 7 -Searching for drms within stsd instead (m4p files)
197
mvhdBuffer.position(positionAfterStsdHeaderAndData);
198
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.DRMS.getFieldName());
199
if (boxHeader != null)
201
Mp4DrmsBox drms = new Mp4DrmsBox(boxHeader, mvhdBuffer);
204
//Level 8-Searching for "esds" within drms to get No Of Channels and bitrate
205
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.ESDS.getFieldName());
206
if (boxHeader != null)
208
Mp4EsdsBox esds = new Mp4EsdsBox(boxHeader, mvhdBuffer.slice());
210
//Set Bitrate in kbps
211
info.setBitrate(esds.getAvgBitrate() / 1000);
213
//Set Number of Channels
214
info.setChannelNumber(esds.getNumberOfChannels());
216
info.setKind(esds.getKind());
217
info.setProfile(esds.getAudioProfile());
219
info.setEncodingType(EncoderType.DRM_AAC.getDescription());
222
//Level 7-Searching for alac (Apple Lossless) instead
225
mvhdBuffer.position(positionAfterStsdHeaderAndData);
226
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.ALAC.getFieldName());
227
if (boxHeader != null)
230
Mp4AlacBox alac = new Mp4AlacBox(boxHeader, mvhdBuffer);
233
//Level 8-Searching for 2nd "alac" within box that contains the info we really want
234
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.ALAC.getFieldName());
235
if (boxHeader != null)
237
alac = new Mp4AlacBox(boxHeader, mvhdBuffer);
239
info.setEncodingType(EncoderType.APPLE_LOSSLESS.getDescription());
240
info.setChannelNumber(alac.getChannels());
241
info.setBitrate(alac.getBitRate()/1000);
247
//Set default channels if couldnt calculate it
248
if (info.getChannelNumber() == -1)
250
info.setChannelNumber(2);
253
//Set default bitrate if couldnt calculate it
254
if (info.getBitRateAsNumber() == -1)
256
info.setBitrate(128);
259
//This is the most likley option if cant find a match
260
if (info.getEncodingType().equals(""))
262
info.setEncodingType(EncoderType.AAC.getDescription());
265
logger.info(info.toString());
267
//Level 2-Searching for another "trak" within "moov", if more than one track exists then probably
268
//video so reject it, unless it fits into certain encoding patterns
269
mvhdBuffer.position(endOfFirstTrackInBuffer);
270
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.TRAK.getFieldName());
271
if (boxHeader != null)
273
//We only allow multiple tracks as audio if they follow the format used by the winamp encoder or
274
//are marked as being audio only and if track contains a nmhd atom rather than smhd.
275
//TODO this probably too restrictive but it fixes the test cases we have
276
if (ftyp.getMajorBrand().equals(Mp4FtypBox.Brand.ISO14496_1_VERSION_2.getId())
277
|| ftyp.getMajorBrand().equals(Mp4FtypBox.Brand.APPLE_AUDIO_ONLY.getId())
278
|| ftyp.getMajorBrand().equals(Mp4FtypBox.Brand.APPLE_AUDIO.getId()))
280
//Ok, need to do further checks on this track to ensure it is a scene descriptor
281
//Level 3-Searching for "mdia" within "trak"
282
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDIA.getFieldName());
283
if (boxHeader == null)
285
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
287
//Level 4-Searching for "mdhd" within "mdia"
288
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MDHD.getFieldName());
289
if (boxHeader == null)
291
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
293
//Level 4-Searching for "minf" within "mdia"
294
mvhdBuffer.position(mvhdBuffer.position() + boxHeader.getDataLength());
295
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.MINF.getFieldName());
296
if (boxHeader == null)
298
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
301
//Level 5-Searching for "nmhd" within "minf"
302
//Only an audio track would have a nmhd frame
303
boxHeader = Mp4BoxHeader.seekWithinLevel(mvhdBuffer, Mp4NotMetaFieldKey.NMHD.getFieldName());
304
if (boxHeader == null)
306
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
311
logger.info(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg() + ":" + ftyp.getMajorBrand());
312
throw new CannotReadVideoException(ErrorMessage.MP4_FILE_IS_VIDEO.getMsg());
316
//Build AtomTree to ensure it is valid, this means we can detect any problems early on
317
Mp4AtomTree atomTree = new Mp4AtomTree(raf,false);