Audio converting with Xuggler

后端 未结 3 1549
生来不讨喜
生来不讨喜 2021-02-11 00:05

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

相关标签:
3条回答
  • 2021-02-11 00:48

    I'm not sure of the exact options and what they do, but take a look at the javadoc for IStreamCoder. There's various other options there you might want to play with. You can even set flags on ffmpeg directly (which Xuggler uses underneath) with the setFlags() method if you want complete control.

    0 讨论(0)
  • 2021-02-11 00:49

    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( );
    }
    
    0 讨论(0)
  • 2021-02-11 01:04

    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;
    
    0 讨论(0)
提交回复
热议问题