使用Xuggler进行音频转换

我正在尝试用Java中的Xuggler将aac / wav / wma音频文件转换为mp3。

不幸的是,我的质量大大降低。 我的输入文件大小约为7MB,输出文件大小仅为1.5MB。

采样率设置为44100 Hz,是否还要设置其他参数?

谢谢您的回答。

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) { /* * 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); } } 

我会做这样的事情:

 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是要转换的File(aac / wav / wma),输出是一个新的.mp3文件(Xuggler通过扩展名找出转换)。

您可以提高质量增加kbps(即320 kbps需要传递320000)。

希望有所帮助:-)

仅供参考:对于Java项目,如果您还没有这样做,则需要导入以下内容:

 import com.xuggle.mediatool.MediaListenerAdapter; import com.xuggle.mediatool.event.IAddStreamEvent; import com.xuggle.xuggler.IStreamCoder; 

我不确定具体选项和它们的作用,但请看一下IStreamCoder的javadoc 。 您可能想要使用其他各种选项。 如果你想要完全控制,你甚至可以使用setFlags()方法直接在ffmpeg上设置标志(Xuggler在下面使用)。

当你有一个带有封面的mp3(png)时要小心你可能会因为尝试将videopng流发送到音频流而导致错误。通过使用ISamples并使用if读取数据包(packet.getStreamIndex()== audioStreamId) {}可以更好地控制您使用的流。 检查我的完整代码:

 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( ); }