package org.jaudiotagger.audio.mp4.atom;
import org.jaudiotagger.audio.generic.Utils;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
/**
* EsdsBox ( stream specific description box), usually holds the Bitrate/No of Channels
* <p/>
* It contains a number of (possibly optional?) sections (section 3 - 6) (containing optional filler) with
* differeent info in each section.
* <p/>
* <p/>
* -> 4 bytes version/flags = 8-bit hex version + 24-bit hex flags
* (current = 0)
* <p/>
* Section 3
* -> 1 byte ES descriptor type tag = 8-bit hex value 0x03
* -> 3 bytes optional extended descriptor type tag string = 3 * 8-bit hex value
* - types are 0x80,0x81,0xFE
* -> 1 byte descriptor type length = 8-bit unsigned length
* -> 2 bytes ES ID = 16-bit unsigned value
* -> 1 byte stream priority = 8-bit unsigned value
* - Defaults to 16 and ranges from 0 through to 31
* <p/>
* Section 4
* -> 1 byte decoder config descriptor type tag = 8-bit hex value 0x04
* -> 3 bytes optional extended descriptor type tag string = 3 * 8-bit hex value
* - types are 0x80,0x81,0xFE
* -> 1 byte descriptor type length = 8-bit unsigned length *
* -> 1 byte object type ID = 8-bit unsigned value
* -> 6 bits stream type = 3/4 byte hex value
* - type IDs are object descript. = 1 ; clock ref. = 2
* - type IDs are scene descript. = 4 ; visual = 4
* - type IDs are audio = 5 ; MPEG-7 = 6 ; IPMP = 7
* - type IDs are OCI = 8 ; MPEG Java = 9
* - type IDs are user private = 32
* -> 1 bit upstream flag = 1/8 byte hex value
* -> 1 bit reserved flag = 1/8 byte hex value set to 1
* -> 3 bytes buffer size = 24-bit unsigned value
* -> 4 bytes maximum bit rate = 32-bit unsigned value
* -> 4 bytes average bit rate = 32-bit unsigned value
* <p/>
* Section 5
* -> 1 byte decoder specific descriptor type tag 8-bit hex value 0x05
* -> 3 bytes optional extended descriptor type tag string = 3 * 8-bit hex value
* - types are 0x80,0x81,0xFE
* -> 1 byte descriptor type length = 8-bit unsigned length
* -> 1 byte Audio profile Id
* - 5 bits Profile Id
* - 3 bits Unknown
* -> 8 bits other flags
* - 3 bits unknown
* - 2 bits is No of Channels
* - 3 bits unknown
* <p/>
* Section 6
* <p/>
* -> 1 byte SL config descriptor type tag = 8-bit hex value 0x06
* -> 3 bytes optional extended descriptor type tag string = 3 * 8-bit hex value
* - types are 0x80,0x81,0xFE
* -> 1 byte descriptor type length = 8-bit unsigned length
* -> 1 byte SL value = 8-bit hex value set to 0x02
*/
public class Mp4EsdsBox extends AbstractMp4Box
{
public static final int VERSION_FLAG_LENGTH = 1;
public static final int OTHER_FLAG_LENGTH = 3;
public static final int DESCRIPTOR_TYPE_LENGTH = 1;
public static final int ES_ID_LENGTH = 2;
public static final int STREAM_PRIORITY_LENGTH = 1;
public static final int CONFIG_TYPE_LENGTH = 1;
public static final int OBJECT_TYPE_LENGTH = 1;
public static final int STREAM_TYPE_LENGTH = 1;
public static final int BUFFER_SIZE_LENGTH = 3;
public static final int MAX_BITRATE_LENGTH = 4;
public static final int AVERAGE_BITRATE_LENGTH = 4;
public static final int DESCRIPTOR_OBJECT_TYPE_LENGTH = 1;
public static final int CHANNEL_FLAGS_LENGTH = 1;
//Data we are tring to extract
private Kind kind;
private AudioProfile audioProfile;
private int numberOfChannels;
private int maxBitrate;
private int avgBitrate;
//Section indentifiers
private static final int SECTION_THREE = 0x03;
private static final int SECTION_FOUR = 0x04;
private static final int SECTION_FIVE = 0x05;
private static final int SECTION_SIX = 0x06;
//Possible Section Filler values
private static final int FILLER_START = 0x80;
private static final int FILLER_OTHER = 0x81;
private static final int FILLER_END = 0xFE;
private static Map<Integer, Kind> kindMap;
private static Map<Integer, AudioProfile> audioProfileMap;
static
{
//Create maps to speed up lookup from raw value to enum
kindMap = new HashMap<Integer, Kind>();
for (Kind next : Kind.values())
{
kindMap.put(next.getId(), next);
}
audioProfileMap = new HashMap<Integer, AudioProfile>();
for (AudioProfile next : AudioProfile.values())
{
audioProfileMap.put(next.getId(), next);
}
}
/**
* DataBuffer must start from from the start of the body
*
* @param header header info
* @param dataBuffer data of box (doesnt include header data)
*/
public Mp4EsdsBox(Mp4BoxHeader header, ByteBuffer dataBuffer)
{
this.header = header;
//Not currently used, as lengths can extend over more than one section i think
int sectionThreeLength;
int sectionFourLength;
int sectionFiveLength;
int sectionSixLength;
//As explained earlier the length of this atom is not fixed so processing is a bit more difficult
//Process Flags
dataBuffer.position(dataBuffer.position() + VERSION_FLAG_LENGTH + OTHER_FLAG_LENGTH);
//Process Section 3 if exists
if (dataBuffer.get() == SECTION_THREE)
{
sectionThreeLength = processSectionHeader(dataBuffer);
//Skip Other Section 3 data
dataBuffer.position(dataBuffer.position() + ES_ID_LENGTH + STREAM_PRIORITY_LENGTH);
}
//Process Section 4 (to getFields type and bitrate)
if (dataBuffer.get() == SECTION_FOUR)
{
sectionFourLength = processSectionHeader(dataBuffer);
//kind (in iTunes)
kind = kindMap.get((int) dataBuffer.get());
//Skip Other Section 4 data
dataBuffer.position(dataBuffer.position() + STREAM_TYPE_LENGTH + BUFFER_SIZE_LENGTH);
//Bit rates
this.maxBitrate = Utils.getIntBE(dataBuffer, dataBuffer.position(), (dataBuffer.position() + MAX_BITRATE_LENGTH - 1));
dataBuffer.position(dataBuffer.position() + MAX_BITRATE_LENGTH);
this.avgBitrate = Utils.getIntBE(dataBuffer, dataBuffer.position(), (dataBuffer.position() + AVERAGE_BITRATE_LENGTH - 1));
dataBuffer.position(dataBuffer.position() + AVERAGE_BITRATE_LENGTH);
}
//Process Section 5,(to getFields no of channels and audioprofile(profile in itunes))
if (dataBuffer.get() == SECTION_FIVE)
{
sectionFiveLength = processSectionHeader(dataBuffer);
//Audio Profile
audioProfile = audioProfileMap.get((dataBuffer.get() >> 3));
//Channels
byte channelByte = dataBuffer.get();
numberOfChannels = (channelByte << 1) >> 4;
}
//Process Section 6, not needed ...
}
public int getNumberOfChannels()
{
return numberOfChannels;
}
/**
* @return maximum bit rate (bps)
*/
public int getMaxBitrate()
{
return maxBitrate;
}
/**
* @return average bit rate (bps)
*/
public int getAvgBitrate()
{
return avgBitrate;
}
/**
* Process header, skipping filler bytes and calculating size
*
* @param dataBuffer
* @return section header
*/
public int processSectionHeader(ByteBuffer dataBuffer)
{
int datalength;
byte nextByte = dataBuffer.get();
if (((nextByte & 0xFF) == FILLER_START) || ((nextByte & 0xFF) == FILLER_OTHER) || ((nextByte & 0xFF) == FILLER_END))
{
dataBuffer.get();
dataBuffer.get();
datalength = dataBuffer.get();
}
else
{
datalength = nextByte;
}
return datalength;
}
/**
* Only expext MPG_Audio,
* TODO shouldnt matter if another type of audio, but something gone wrong if type of video
*
* @return the file type for the track
*/
public Kind getKind()
{
return kind;
}
/**
* Get audio profile, usually AAC Low Complexity
*
* @return the audio profile
*/
public AudioProfile getAudioProfile()
{
return audioProfile;
}
/**
* File type, held in Section 4 , only really expecting type 0x64 (AAC)
*/
public static enum Kind
{
V1(1),
V2(2),
MPEG4_VIDEO(32),
MPEG4_AVC_SPS(33),
MPEG4_AVC_PPS(34),
MPEG4_AUDIO(64),
MPEG2_SIMPLE_VIDEO(96),
MPEG2_MAIN_VIDEO(97),
MPEG2_SNR_VIDEO(98),
MPEG2_SPATIAL_VIDEO(99),
MPEG2_HIGH_VIDEO(100),
MPEG2_422_VIDEO(101),
MPEG4_ADTS_MAIN(102),
MPEG4_ADTS_LOW_COMPLEXITY(103),
MPEG4_ADTS_SCALEABLE_SAMPLING(104),
MPEG2_ADTS_MAIN(105),
MPEG1_VIDEO(106),
MPEG1_ADTS(107),
JPEG_VIDEO(108),
PRIVATE_AUDIO(192),
PRIVATE_VIDEO(208),
PCM_LITTLE_ENDIAN_AUDIO(224),
VORBIS_AUDIO(225),
DOLBY_V3_AUDIO(226),
ALAW_AUDIO(227),
MULAW_AUDIO(228),
ADPCM_AUDIO(229),
PCM_BIG_ENDIAN_AUDIO(230),
YV12_VIDEO(240),
H264_VIDEO(241),
H263_VIDEO(242),
H261_VIDEO(243);
private int id;
Kind(int id)
{
this.id = id;
}
public int getId()
{
return id;
}
}
/**
* Audio profile, held in Section 5 this is usually type LOW_COMPLEXITY
*/
public static enum AudioProfile
{
MAIN(1, "Main"),
LOW_COMPLEXITY(2, "Low Complexity"),
SCALEABLE(3, "Scaleable Sample rate"),
T_F(4, "T/F"),
T_F_MAIN(5, "T/F Main"),
T_F_LC(6, "T/F LC"),
TWIN_VQ(7, "TWIN"),
CELP(8, "CELP"),
HVXC(9, "HVXC"),
HILN(10, "HILN"),
TTSI(11, "TTSI"),
MAIN_SYNTHESIS(12, "MAIN_SYNTHESIS"),
WAVETABLE(13, "WAVETABLE"),;
private int id;
private String description;
/**
* @param id it is stored as in file
* @param description human readable description
*/
AudioProfile(int id, String description)
{
this.id = id;
this.description = description;
}
public int getId()
{
return id;
}
public String getDescription()
{
return description;
}
}
}
|