package net.sourceforge.jaad.aac;
import java.lang.IllegalArgumentException;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import net.sourceforge.jaad.aac.filterbank.FilterBank;
import net.sourceforge.jaad.aac.syntax.BitStream;
import net.sourceforge.jaad.aac.syntax.IBitStream;
import net.sourceforge.jaad.aac.syntax.PCE;
import net.sourceforge.jaad.aac.syntax.SyntacticElements;
import net.sourceforge.jaad.aac.syntax.SyntaxConstants;
import net.sourceforge.jaad.aac.transport.ADIFHeader;
/**
* 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.
*
* Main AAC decoder class
*
* @author in-somnia
*/
public class Decoder implements SyntaxConstants {
static {
for (Handler h : LOGGER.getHandlers()) {
LOGGER.removeHandler(h);
}
LOGGER.setLevel(Level.ALL);
final ConsoleHandler h = new ConsoleHandler();
h.setLevel(Level.ALL);
LOGGER.addHandler(h);
}
private final DecoderConfig config;
private final SyntacticElements syntacticElements;
private final FilterBank filterBank;
private IBitStream _in;
private ADIFHeader adifHeader;
/**
* The methods returns true, if a profile is supported by the decoder.
*
* @param profile
* an AAC profile
* @return true if the specified profile can be decoded
* @see Profile#isDecodingSupported()
*/
public static boolean canDecode(Profile profile) {
return profile.isDecodingSupported();
}
/**
* Initializes the decoder with a MP4 decoder specific info.
*
* After this the MP4 frames can be passed to the
* <code>decodeFrame(byte[], SampleBuffer)</code> method to decode them.
*
* @param decoderSpecificInfo
* a byte array containing the decoder specific info from an MP4
* container
* @throws AACException
* if the specified profile is not supported
*/
public Decoder(byte[] decoderSpecificInfo) throws AACException {
config = DecoderConfig.parseMP4DecoderSpecificInfo(decoderSpecificInfo);
if (config == null)
throw new IllegalArgumentException("illegal MP4 decoder specific info");
if (!canDecode(config.getProfile()))
throw new AACException("unsupported profile: " + config.getProfile().getDescription());
syntacticElements = new SyntacticElements(config);
filterBank = new FilterBank(config.isSmallFrameUsed(), config.getChannelConfiguration().getChannelCount());
_in = new BitStream();
LOGGER.log(Level.FINE, "profile: {0}", config.getProfile());
LOGGER.log(Level.FINE, "sf: {0}", config.getSampleFrequency().getFrequency());
LOGGER.log(Level.FINE, "channels: {0}", config.getChannelConfiguration().getDescription());
}
public DecoderConfig getConfig() {
return config;
}
/**
* Decodes one frame of AAC data in frame mode and returns the raw PCM data.
*
* @param frame
* the AAC frame
* @param buffer
* a buffer to hold the decoded PCM data
* @throws AACException
* if decoding fails
*/
public void decodeFrame(byte[] frame, SampleBuffer buffer) throws AACException {
if (frame != null)
_in.setData(frame);
LOGGER.finest("bits left " + _in.getBitsLeft());
try {
decode(buffer);
} catch (AACException e) {
if (!e.isEndOfStream())
throw e;
else
LOGGER.warning("unexpected end of frame");
}
}
private void decode(SampleBuffer buffer) throws AACException {
if (ADIFHeader.isPresent(_in)) {
adifHeader = ADIFHeader.readHeader(_in);
final PCE pce = adifHeader.getFirstPCE();
config.setProfile(pce.getProfile());
config.setSampleFrequency(pce.getSampleFrequency());
config.setChannelConfiguration(ChannelConfiguration.forInt(pce.getChannelCount()));
}
if (!canDecode(config.getProfile()))
throw new AACException("unsupported profile: " + config.getProfile().getDescription());
syntacticElements.startNewFrame();
try {
//1: bitstream parsing and noiseless coding
syntacticElements.decode(_in);
//2: spectral processing
syntacticElements.process(filterBank);
//3: send to output buffer
syntacticElements.sendToOutput(buffer);
} catch (AACException e) {
buffer.setData(new byte[0], 0, 0, 0, 0);
throw e;
} catch (Exception e) {
buffer.setData(new byte[0], 0, 0, 0, 0);
throw AACException.wrap(e);
}
}
}