package net.sourceforge.jaad.aac; import static net.sourceforge.jaad.aac.Profile.*; import net.sourceforge.jaad.aac.syntax.BitStream; import net.sourceforge.jaad.aac.syntax.SyntaxConstants; import net.sourceforge.jaad.aac.syntax.IBitStream; import net.sourceforge.jaad.aac.syntax.PCE; /** * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed * under the Public Domain license. Code changes provided by the JCodec project * are distributed under FreeBSD license. * * DecoderConfig that must be passed to the * <code>Decoder</code> constructor. Typically it is created via one of the * static parsing methods. * * @author in-somnia */ public class DecoderConfig implements SyntaxConstants { private Profile profile, extProfile; private SampleFrequency sampleFrequency; private ChannelConfiguration channelConfiguration; private boolean frameLengthFlag; private boolean dependsOnCoreCoder; private int coreCoderDelay; private boolean extensionFlag; //extension: SBR private boolean sbrPresent, downSampledSBR, sbrEnabled; //extension: error resilience private boolean sectionDataResilience, scalefactorResilience, spectralDataResilience; private DecoderConfig() { profile = Profile.AAC_MAIN; extProfile = Profile.UNKNOWN; sampleFrequency = SampleFrequency.SAMPLE_FREQUENCY_NONE; channelConfiguration = ChannelConfiguration.CHANNEL_CONFIG_UNSUPPORTED; frameLengthFlag = false; sbrPresent = false; downSampledSBR = false; sbrEnabled = true; sectionDataResilience = false; scalefactorResilience = false; spectralDataResilience = false; } /* ========== gets/sets ========== */ public ChannelConfiguration getChannelConfiguration() { return channelConfiguration; } public void setChannelConfiguration(ChannelConfiguration channelConfiguration) { this.channelConfiguration = channelConfiguration; } public int getCoreCoderDelay() { return coreCoderDelay; } public void setCoreCoderDelay(int coreCoderDelay) { this.coreCoderDelay = coreCoderDelay; } public boolean isDependsOnCoreCoder() { return dependsOnCoreCoder; } public void setDependsOnCoreCoder(boolean dependsOnCoreCoder) { this.dependsOnCoreCoder = dependsOnCoreCoder; } public Profile getExtObjectType() { return extProfile; } public void setExtObjectType(Profile extObjectType) { this.extProfile = extObjectType; } public int getFrameLength() { return frameLengthFlag ? WINDOW_SMALL_LEN_LONG : WINDOW_LEN_LONG; } public boolean isSmallFrameUsed() { return frameLengthFlag; } public void setSmallFrameUsed(boolean shortFrame) { this.frameLengthFlag = shortFrame; } public Profile getProfile() { return profile; } public void setProfile(Profile profile) { this.profile = profile; } public SampleFrequency getSampleFrequency() { return sampleFrequency; } public void setSampleFrequency(SampleFrequency sampleFrequency) { this.sampleFrequency = sampleFrequency; } //=========== SBR ============= public boolean isSBRPresent() { return sbrPresent; } public boolean isSBRDownSampled() { return downSampledSBR; } public boolean isSBREnabled() { return sbrEnabled; } public void setSBREnabled(boolean enabled) { sbrEnabled = enabled; } //=========== ER ============= public boolean isScalefactorResilienceUsed() { return scalefactorResilience; } public boolean isSectionDataResilienceUsed() { return sectionDataResilience; } public boolean isSpectralDataResilienceUsed() { return spectralDataResilience; } /* ======== static builder ========= */ /** * Parses the input arrays as a DecoderSpecificInfo, as used in MP4 * containers. * * @return a DecoderConfig */ public static DecoderConfig parseMP4DecoderSpecificInfo(byte[] data) throws AACException { final IBitStream _in = BitStream.createBitStream(data); final DecoderConfig config = new DecoderConfig(); try { config.profile = readProfile(_in); int sf = _in.readBits(4); if(sf==0xF) config.sampleFrequency = SampleFrequency.forFrequency(_in.readBits(24)); else config.sampleFrequency = SampleFrequency.forInt(sf); config.channelConfiguration = ChannelConfiguration.forInt(_in.readBits(4)); Profile cp = config.profile; if (AAC_SBR == cp) { config.extProfile = cp; config.sbrPresent = true; sf = _in.readBits(4); //TODO: 24 bits already read; read again? //if(sf==0xF) config.sampleFrequency = SampleFrequency.forFrequency(_in.readBits(24)); //if sample frequencies are the same: downsample SBR config.downSampledSBR = config.sampleFrequency.getIndex()==sf; config.sampleFrequency = SampleFrequency.forInt(sf); config.profile = readProfile(_in); } else if (AAC_MAIN == cp || AAC_LC == cp || AAC_SSR == cp || AAC_LTP == cp || ER_AAC_LC == cp || ER_AAC_LTP == cp || ER_AAC_LD == cp ) { //ga-specific info: config.frameLengthFlag = _in.readBool(); if(config.frameLengthFlag) throw new AACException("config uses 960-sample frames, not yet supported"); //TODO: are 960-frames working yet? config.dependsOnCoreCoder = _in.readBool(); if(config.dependsOnCoreCoder) config.coreCoderDelay = _in.readBits(14); else config.coreCoderDelay = 0; config.extensionFlag = _in.readBool(); if(config.extensionFlag) { if(cp.isErrorResilientProfile()) { config.sectionDataResilience = _in.readBool(); config.scalefactorResilience = _in.readBool(); config.spectralDataResilience = _in.readBool(); } //extensionFlag3 _in.skipBit(); } if(config.channelConfiguration==ChannelConfiguration.CHANNEL_CONFIG_NONE) { //TODO: is this working correct? -> ISO 14496-3 part 1: 1.A.4.3 _in.skipBits(3); //PCE PCE pce = new PCE(); pce.decode(_in); config.profile = pce.getProfile(); config.sampleFrequency = pce.getSampleFrequency(); config.channelConfiguration = ChannelConfiguration.forInt(pce.getChannelCount()); } if(_in.getBitsLeft()>10) readSyncExtension(_in, config); } else { throw new AACException("profile not supported: "+cp.getIndex()); } return config; } finally { _in.destroy(); } } private static Profile readProfile(IBitStream _in) throws AACException { int i = _in.readBits(5); if(i==31) i = 32+_in.readBits(6); return Profile.forInt(i); } private static void readSyncExtension(IBitStream _in, DecoderConfig config) throws AACException { final int type = _in.readBits(11); switch(type) { case 0x2B7: final Profile profile = Profile.forInt(_in.readBits(5)); if(profile.equals(Profile.AAC_SBR)) { config.sbrPresent = _in.readBool(); if(config.sbrPresent) { config.profile = profile; int tmp = _in.readBits(4); if(tmp==config.sampleFrequency.getIndex()) config.downSampledSBR = true; if(tmp==15) { throw new AACException("sample rate specified explicitly, not supported yet!"); //tmp = _in.readBits(24); } } } break; } } }