I'm trying to convert aac/wav/wma audio files to mp3 with Xuggler in Java.
Unfortunately, I have a big loss of quality. My input file size is about 7MB and my output file size is only 1,5MB.
The sample rate is set to 44100 Hz, is there other parameters to set?
Thank you for your answers.
if (args.length <= 1)
throw new IllegalArgumentException("must pass an input filename and output filename as argument");
IMediaWriter writer = ToolFactory.makeWriter(args[1]);
String filename = args[0];
// Create a Xuggler container object
IContainer container = IContainer.make();
// Open up the container
if (container.open(filename, IContainer.Type.READ, null) < 0)
throw new IllegalArgumentException("could not open file: " + filename);
// query how many streams the call to open found
int numStreams = container.getNumStreams();
// and iterate through the streams to find the first audio stream
int audioStreamId = -1;
IStreamCoder audioCoder = null;
for(int i = 0; i < numStreams; i++)
{
// Find the stream object
IStream stream = container.getStream(i);
// Get the pre-configured decoder that can decode this stream;
IStreamCoder coder = stream.getStreamCoder();
if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO)
{
audioStreamId = i;
audioCoder = coder;
audioCoder.setBitRate(container.getBitRate());
break;
}
}
if (audioStreamId == -1)
throw new RuntimeException("could not find audio stream in container: "+filename);
/* We read only AAC file for the moment */
if(audioCoder.getCodecID() != ICodec.ID.CODEC_ID_AAC
&& audioCoder.getCodecID() != ICodec.ID.CODEC_ID_WAVPACK
&& audioCoder.getCodecID() != ICodec.ID.CODEC_ID_WMAV1
&& audioCoder.getCodecID() != ICodec.ID.CODEC_ID_WMAV2
&& audioCoder.getCodecID() != ICodec.ID.CODEC_ID_WMAPRO
&& audioCoder.getCodecID() != ICodec.ID.CODEC_ID_WMAVOICE)
{
System.out.println("Read only AAC, WAV or WMA files");
System.exit(1);
}
audioCoder.setSampleFormat(IAudioSamples.Format.FMT_S16);
/*
* Now we have found the audio stream in this file. Let's open up our decoder so it can
* do work.
*/
if (audioCoder.open() < 0)
throw new RuntimeException("could not open audio decoder for container: "+filename);
int streamIndex = writer.addAudioStream(0, 0, audioCoder.getChannels(), audioCoder.getSampleRate());
System.out.println("audio Frame size : "+audioCoder.getAudioFrameSize());
/*
* Now, we start walking through the container looking at each packet.
*/
IPacket packet = IPacket.make();
while(container.readNextPacket(packet) >= 0)
{
/*
* Now we have a packet, let's see if it belongs to our audio stream
*/
if (packet.getStreamIndex() == audioStreamId)
{
/*
* We allocate a set of samples with the same number of channels as the
* coder tells us is in this buffer.
*
* We also pass in a buffer size (1024 in our example), although Xuggler
* will probably allocate more space than just the 1024 (it's not important why).
*/
IAudioSamples samples = IAudioSamples.make(512, audioCoder.getChannels(),IAudioSamples.Format.FMT_S16 );
/*
* A packet can actually contain multiple sets of samples (or frames of samples
* in audio-decoding speak). So, we may need to call decode audio multiple
* times at different offsets in the packet's data. We capture that here.
*/
int offset = 0;
/*
* Keep going until we've processed all data
*/
while(offset < packet.getSize())
{
int bytesDecoded = audioCoder.decodeAudio(samples, packet, offset);
if (bytesDecoded < 0)
throw new RuntimeException("got error decoding audio in: " + filename);
offset += bytesDecoded;
/*
* Some decoder will consume data in a packet, but will not be able to construct
* a full set of samples yet. Therefore you should always check if you
* got a complete set of samples from the decoder
*/
if (samples.isComplete())
{
writer.encodeAudio(streamIndex, samples);
}
}
}
else
{
/*
* This packet isn't part of our audio stream, so we just silently drop it.
*/
do {} while(false);
}
}
I'll do something like this:
public void convertToMP3(File input, File output, int kbps) { //modify on your convenience
// create a media reader
IMediaReader mediaReader = ToolFactory.makeReader(input.getPath());
// create a media writer
IMediaWriter mediaWriter = ToolFactory.makeWriter(output.getPath(), mediaReader);
// add a writer to the reader, to create the output file
mediaReader.addListener(mediaWriter);
// add a IMediaListner to the writer to change bit rate
mediaWriter.addListener(new MediaListenerAdapter() {
@Override
public void onAddStream(IAddStreamEvent event) {
IStreamCoder streamCoder = event.getSource().getContainer().getStream(event.getStreamIndex()).getStreamCoder();
streamCoder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, false);
streamCoder.setBitRate(kbps);
streamCoder.setBitRateTolerance(0);
}
});
// read and decode packets from the source file and
// and dispatch decoded audio and video to the writer
while (mediaReader.readPacket() == null);
}
input is the File (aac/wav/wma) you want to convert and output is a new .mp3 file (Xuggler figure out the conversion by the extension).
You can increase the quality increasing kbps (i.e. for 320 kbps you need to pass in 320000).
Hope that helps :-)
FYI: for Java projects you'll need to import the following if you haven't already done so:
import com.xuggle.mediatool.MediaListenerAdapter;
import com.xuggle.mediatool.event.IAddStreamEvent;
import com.xuggle.xuggler.IStreamCoder;