📄 javasoundcodec.java
字号:
package net.sf.fmj.media.codec;import java.io.BufferedInputStream;import java.io.IOException;import java.util.List;import java.util.logging.Level;import java.util.logging.Logger;import javax.media.Buffer;import javax.media.Format;import javax.media.ResourceUnavailableException;import javax.media.format.AudioFormat;import javax.sound.sampled.AudioInputStream;import javax.sound.sampled.AudioSystem;import javax.sound.sampled.UnsupportedAudioFileException;import javax.sound.sampled.AudioFormat.Encoding;import javax.sound.sampled.spi.FormatConversionProvider;import com.sun.media.sound.JDK13Services;import javazoom.spi.mpeg.sampled.file.MpegEncoding;import javazoom.spi.vorbis.sampled.file.VorbisAudioFormat;import javazoom.spi.vorbis.sampled.file.VorbisEncoding;import net.sf.fmj.codegen.MediaCGUtils;import net.sf.fmj.media.AbstractCodec;import net.sf.fmj.media.BufferQueueInputStream;import net.sf.fmj.media.renderer.audio.JavaSoundRenderer;import net.sf.fmj.utility.FormatUtils;import net.sf.fmj.utility.LoggerSingleton;/** * Converts formats that JavaSound can convert. * * This has to do some tricks because of the delay in getting data from the converted audio output stream. * The streams are designed as streams, not buffer-based, so all our threading and buffer queue tricks * mean that we don't get an output buffer right away for an input one. The missing output buffers build up. * And then we get EOM, and if the graph processing isn't done right, most of the output buffers never make it to the renderer. * * TODO: if this is put ahead of com.ibm.media.codec.audio.PCMToPCM in the registry, there are problems playing the safexmas movie. * @author Ken Larson * */public class JavaSoundCodec extends AbstractCodec{ private static final Logger logger = LoggerSingleton.logger; public JavaSoundCodec() { inputFormats = new Format[] { new AudioFormat(AudioFormat.ULAW), new AudioFormat(AudioFormat.ALAW), new AudioFormat(AudioFormat.LINEAR),// MP3 formats: Only work if MP3 SPI in classpath. TODO: detect dynamically? new AudioFormat(MpegEncoding.MPEG1L1.toString()), new AudioFormat(MpegEncoding.MPEG1L2.toString()), new AudioFormat(MpegEncoding.MPEG1L3.toString()), new AudioFormat(MpegEncoding.MPEG2DOT5L1.toString()), new AudioFormat(MpegEncoding.MPEG2DOT5L2.toString()), new AudioFormat(MpegEncoding.MPEG2DOT5L3.toString()), new AudioFormat(MpegEncoding.MPEG2L1.toString()), new AudioFormat(MpegEncoding.MPEG2L2.toString()), new AudioFormat(MpegEncoding.MPEG2L3.toString()),// OGG formats: Only work if OGG SPI in classpath. TODO: detect dynamically? new AudioFormat(VorbisEncoding.VORBISENC.toString()), }; // TODO: get dynamically from java sound. Not sure if this is possible } //@Override public Format[] getSupportedOutputFormats(Format input) { if (input == null) { return new Format[] {new AudioFormat(AudioFormat.LINEAR)}; // TODO } final javax.sound.sampled.AudioFormat javaSoundFormat = JavaSoundRenderer.convertFormat((AudioFormat) input); // TODO: we could call AudioSystem.getTargetEncodings(javaSoundFormat); rather than hard code the PCM ones: final javax.sound.sampled.AudioFormat[] targets1 = AudioSystem.getTargetFormats(Encoding.PCM_UNSIGNED, javaSoundFormat); final javax.sound.sampled.AudioFormat[] targets2 = AudioSystem.getTargetFormats(Encoding.PCM_SIGNED, javaSoundFormat); final javax.sound.sampled.AudioFormat[] targetsSpecial; // for some reason, AudioSystem.getTargetFormats doesn't return anything for MpegAudioFormat // TODO: check using class name, to avoid class not found exception if javazoom is not in the classpath. if (javaSoundFormat instanceof javazoom.spi.mpeg.sampled.file.MpegAudioFormat) { javax.sound.sampled.AudioFormat decodedFormat = new javax.sound.sampled.AudioFormat(javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED, javaSoundFormat.getSampleRate(), 16, javaSoundFormat.getChannels(), javaSoundFormat.getChannels() * 2, javaSoundFormat.getSampleRate(), false); targetsSpecial = new javax.sound.sampled.AudioFormat[] {decodedFormat}; } else if (javaSoundFormat instanceof VorbisAudioFormat) { // TODO: what is the correct mapping? javax.sound.sampled.AudioFormat decodedFormat = new javax.sound.sampled.AudioFormat(javax.sound.sampled.AudioFormat.Encoding.PCM_SIGNED, javaSoundFormat.getSampleRate(), 16, javaSoundFormat.getChannels(), javaSoundFormat.getChannels() * 2, javaSoundFormat.getSampleRate(), false); targetsSpecial = new javax.sound.sampled.AudioFormat[] {decodedFormat}; } else { targetsSpecial = new javax.sound.sampled.AudioFormat[0]; } final Format[] result = new Format[targets1.length + targets2.length + targetsSpecial.length]; for (int i = 0; i < targets1.length; ++i) { result[i] = JavaSoundRenderer.convertFormat(targets1[i]); logger.finer("getSupportedOutputFormats: " + result[i]); } for (int i = 0; i < targets2.length; ++i) { result[targets1.length + i] = JavaSoundRenderer.convertFormat(targets2[i]); logger.finer("getSupportedOutputFormats: " + result[targets1.length + i]); } for (int i = 0; i < targetsSpecial.length; ++i) { result[targets1.length + targets2.length + i] = JavaSoundRenderer.convertFormat(targetsSpecial[i]); logger.finer("getSupportedOutputFormats: " + result[targets1.length + targets2.length + i]); } // this all really depends on where MP3 decoding is handled in JavaSound with an SPI. for (int i = 0; i < result.length; ++i) { AudioFormat a = ((AudioFormat) result[i]); AudioFormat inputAudioFormat = (AudioFormat) input; // converting from a sound format with a specific sample rate to one without causes problems building filter graphs. // if the input format specifies a sample rate, we are only really interested in output formats with concrete sample // rates. if (FormatUtils.specified(inputAudioFormat.getSampleRate()) && !FormatUtils.specified(a.getSampleRate())) result[i] = null; // TODO: remove from array. } return result; } private BufferQueueInputStream bufferQueueInputStream; private volatile AudioInputStream audioInputStream; // set in one thread and read in another private volatile AudioInputStream audioInputStreamConverted; private AudioInputStreamThread audioInputStreamThread; private class AudioInputStreamThread extends Thread { private final BufferQueueInputStream bufferQueueInputStream; public AudioInputStreamThread(final BufferQueueInputStream bufferQueueInputStream) { super(); this.bufferQueueInputStream = bufferQueueInputStream; } public void run() { // TODO: this could take a while, perhaps it somehow needs to be done in prefetch. try { audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(bufferQueueInputStream));// audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(bufferQueueInputStream) {//// public synchronized int available() throws IOException// {// // TODO Auto-generated method stub// int value = super.available();// if (trace)// logger.fine(this + " available=" + value);// return value;// }//// public synchronized void mark(int readlimit)// {// // TODO Auto-generated method stub// if (trace)// logger.fine(this + " mark");// super.mark(readlimit);// }//// public synchronized int read() throws IOException// {// if (trace)// logger.fine(this + " read");// // TODO Auto-generated method stub// return super.read();// }//// public synchronized int read(byte[] b, int off, int len) throws IOException// {// int result = super.read(b, off, len);// // if (trace)// logger.fine(this + " read " + len + " returning " + result);// // return result;// }//// public synchronized void reset() throws IOException// { // if (trace)// logger.fine(this + " RESET");//// // TODO Auto-generated method stub// super.reset();// }// // }); } catch (UnsupportedAudioFileException e) { logger.log(Level.WARNING, "" + e, e); // TODO //throw new ResourceUnavailableException(e.getMessage()); return; } catch (IOException e) { logger.log(Level.WARNING, "" + e, e); // TODO //throw new ResourceUnavailableException(e.getMessage()); return; } final javax.sound.sampled.AudioFormat javaSoundAudioFormat = JavaSoundRenderer.convertFormat((AudioFormat) outputFormat); logger.fine("javaSoundAudioFormat converted (out)=" + javaSoundAudioFormat); audioInputStreamConverted = getAudioInputStream(javaSoundAudioFormat, audioInputStream); } } // copied and modified from AudioSystem, to handle exceptions. private static AudioInputStream getAudioInputStream(javax.sound.sampled.AudioFormat targetFormat, AudioInputStream sourceStream) { if (sourceStream.getFormat().matches(targetFormat)) { return sourceStream; } List codecs = getFormatConversionProviders(); for(int i = 0; i < codecs.size(); i++) { FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i); if (codec.getClass().getName().equals("org.tritonus.sampled.convert.AlawFormatConversionProvider")) continue; // skip this one. try { if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) { return codec.getAudioInputStream(targetFormat,sourceStream); } } catch (Exception e) { // tritonus throws exceptions like:// Exception in thread "Thread-8" java.lang.IllegalArgumentException: format conversion not supported// at org.tritonus.sampled.convert.AlawFormatConversionProvider.getAudioInputStream(AlawFormatConversionProvider.java:116)// at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:950) logger.log(Level.WARNING, "" + e, e); } } // we ran out of options... throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat()); } private static List getFormatConversionProviders() { return getProviders(FormatConversionProvider.class); } private static List getProviders(Class providerClass) { return JDK13Services.getProviders(providerClass); } private boolean trace; void setTrace(boolean value) { this.trace = value; } // @Override public void open() throws ResourceUnavailableException { super.open(); bufferQueueInputStream = new BufferQueueInputStream(); // TODO: limit should be total bytes, not number of bufers.// bufferQueueInputStream.setTrace(((AudioFormat) inputFormat).getEncoding().equals("LINEAR") && ((AudioFormat) inputFormat).getSampleRate() == 22050.0);// this.setTrace(((AudioFormat) inputFormat).getEncoding().equals("LINEAR") && ((AudioFormat) inputFormat).getSampleRate() == 22050.0); // create fake header (see below) final javax.sound.sampled.AudioFormat javaSoundAudioFormat = JavaSoundRenderer.convertFormat((AudioFormat) inputFormat); logger.fine("javaSoundAudioFormat converted (in)=" + javaSoundAudioFormat); final byte[] header = fakeHeader(javaSoundAudioFormat); if (header == null) throw new ResourceUnavailableException("Unable to reconstruct header for format: " + inputFormat); if (header.length > 0) { Buffer headerBuffer = new Buffer(); headerBuffer.setData(header); headerBuffer.setLength(header.length); bufferQueueInputStream.put(headerBuffer); } audioInputStreamThread = new AudioInputStreamThread(bufferQueueInputStream); audioInputStreamThread.start(); } private int totalIn; private int totalOut; //@Override public int process(Buffer input, Buffer output) { if (!checkInputBuffer(input)) { return BUFFER_PROCESSED_FAILED; } // we can't do this since we need to put the EOM into the bufferQueueInputStream, so it will return EOS to the audio input stream.// if (isEOM(input))// {// propagateEOM(output); // TODO: what about data? can there be any?// return BUFFER_PROCESSED_OK;// } try { if (trace) logger.fine("process: " + MediaCGUtils.bufferToStr(input)); totalIn += input.getLength();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -