/*
* 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;
import net.sourceforge.jaad.aac.syntax.SyntacticElements;
import net.sourceforge.jaad.aac.filterbank.FilterBank;
import net.sourceforge.jaad.aac.transport.ADIFHeader;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
/**
* Main AAC decoder class
* @author in-somnia
*/
public class Decoder implements Constants {
static {
for(Handler h : LOGGER.getHandlers()) {
LOGGER.removeHandler(h);
}
LOGGER.setLevel(Level.WARNING);
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 BitStream 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);
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 new AACException(e);
}
}
}