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 fi
Be carefull when you have a mp3 with cover (png) you could end up with errors because you are trying to send video png stream to audio stream.. By using ISamples and reading packet with if ( packet.getStreamIndex( ) == audioStreamId ) {} gives a better control over the stream that you use. check my full code:
private static void streamToSource( OutputStream source, Path path ) throws IOException {
byte[] buffer = new byte[4096];
PipedInputStream pis = new PipedInputStream( );
PipedOutputStream pos = new PipedOutputStream( pis );
convertToMP3Xuggler( path, pos );
System.out.println( "start streaming" );
int nRead = 0;
while ( ( nRead = pis.read( buffer ) ) != -1 ) {
source.write( buffer,0 , nRead );
}
pis.close( );
System.out.println( "end : " + path );
}
private static void convertToMP3Xuggler( Path path, PipedOutputStream pos ) throws FileNotFoundException {
// create a media reader
// final IMediaReader mediaReader = ToolFactory.makeReader( XugglerIO.map( new FileInputStream( path.toFile( ) ) ) );
// create a media writer
// IMediaWriter mediaWriter = ToolFactory.makeWriter( XugglerIO.map( XugglerIO.generateUniqueName( os, ".mp3" ), os ), mediaReader );
IMediaWriter mediaWriter = ToolFactory.makeWriter( XugglerIO.map( pos ) );
// manually set the container format (because it can't detect it by filename anymore)
IContainerFormat containerFormat = IContainerFormat.make( );
containerFormat.setOutputFormat( "mp3", null, "audio/mp3" );
mediaWriter.getContainer( ).setFormat( containerFormat );
System.out.println( "file = " + path.toFile( ).toString( ) );
IContainer audioContainer = IContainer.make( );
audioContainer.open( path.toFile( ).toString( ), IContainer.Type.READ, null );
System.out.println( "streams= " + audioContainer.getNumStreams( ) );
System.out.println( "# Duration (ms): " + ( ( audioContainer.getDuration( ) == Global.NO_PTS ) ? "unknown" : "" + audioContainer.getDuration( ) / 1000 ) );
System.out.println( "# File size (bytes): " + audioContainer.getFileSize( ) );
System.out.println( "# Bit rate: " + audioContainer.getBitRate( ) );
int audioStreamId = -1;
for ( int i = 0; i < audioContainer.getNumStreams( ); i++ ) {
// Find the stream object
IStream stream = audioContainer.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;
break;
}
}
if ( audioStreamId < 0 ) {
throw new IllegalArgumentException( "cannot find audio stream in the current file : " + path.toString( ) );
}
System.out.println( "found audio stream = " + audioStreamId );
IStreamCoder coderAudio = audioContainer.getStream( audioStreamId ).getStreamCoder( );
if ( coderAudio.open( null, null ) < 0 ) {
throw new RuntimeException( "Cant open audio coder" );
}
coderAudio.setSampleFormat( IAudioSamples.Format.FMT_S16 );
System.out.println( "bitrate from reading = " + audioContainer.getBitRate( ) );
System.out.println( "bitrate from reading = " + coderAudio.getBitRate( ) );
int streamIndex = mediaWriter.addAudioStream( 0, 0, coderAudio.getChannels( ), coderAudio.getSampleRate( ) );
IStreamCoder writerCoder = mediaWriter.getContainer( ).getStream( streamIndex ).getStreamCoder( );
writerCoder.setFlag( IStreamCoder.Flags.FLAG_QSCALE, false );
writerCoder.setBitRate( BITRATE * 1000 );
writerCoder.setBitRateTolerance( 0 );
System.out.println( "bitrate for output = " + writerCoder.getBitRate( ) );
IPacket packet = IPacket.make( );
runInThread( path, pos, mediaWriter, audioContainer, audioStreamId, coderAudio, streamIndex, packet );
}
private static void runInThread( Path path, PipedOutputStream pos, IMediaWriter mediaWriter, IContainer audioContainer, int audioStreamId, IStreamCoder coderAudio, int streamIndex, IPacket packet ) {
new Thread( ) {
@Override
public void run( ) {
while ( audioContainer.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 (4096 in our example), although Xuggler
* will probably allocate more space than just the 4096 (it's not important why).
*/
IAudioSamples samples = IAudioSamples.make( 4096, coderAudio.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 = coderAudio.decodeAudio( samples, packet, offset );
if ( bytesDecoded < 0 ) {
System.out.println( "decode error in : " + path + " bytesDecoded =" + bytesDecoded + " offset=" + offset + " packet=" + packet );
break;
// throw new RuntimeException( "got error decoding audio in: " + path );
}
offset += bytesDecoded;
// System.out.println( "pktSize = " + packet.getSize( ) + " offset = " + offset + " samplesComplete = " + samples.isComplete( ) );
/*
* 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( ) ) {
mediaWriter.encodeAudio( streamIndex, samples );
}
}
}
}
coderAudio.close( );
audioContainer.close( );
mediaWriter.close( );
try {
pos.close( );
} catch ( IOException e ) {
e.printStackTrace( );
}
}
}.start( );
}