/*
* Copyright (C) 2011 in-somnia
*
* This file is part of JAAD.
*
* JAAD is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* JAAD is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.jaad.aac;
import net.sourceforge.jaad.aac.syntax.BitStream;
import net.sourceforge.jaad.aac.syntax.Constants;
import net.sourceforge.jaad.aac.syntax.PCE;
/**
* 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 Constants {
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;
//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;
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 void setSBRPresent(boolean sbrPresent) {
this.sbrPresent = sbrPresent;
}
public boolean isSBRDownSampled() {
return downSampledSBR;
}
//=========== 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
*/
static DecoderConfig parseMP4DecoderSpecificInfo(byte[] data) throws AACException {
final BitStream in = new BitStream(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));
switch(config.profile) {
case AAC_SBR:
config.extProfile = config.profile;
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);
break;
case AAC_MAIN:
case AAC_LC:
case AAC_SSR:
case AAC_LTP:
case ER_AAC_LC:
case ER_AAC_LTP:
case ER_AAC_LD:
//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(config.profile.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);
break;
default:
throw new AACException("profile not supported: "+config.profile.getIndex());
}
return config;
}
finally {
in.destroy();
}
}
private static Profile readProfile(BitStream in) throws AACException {
int i = in.readBits(5);
if(i==31) i = 32+in.readBits(6);
return Profile.forInt(i);
}
private static void readSyncExtension(BitStream 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;
}
}
}