2011-08-01 71 views
3

我試圖用Java中的Xuggler將aac/wav/wma音頻文件轉換爲mp3。使用Xuggler轉換音頻

不幸的是,我有一個很大的質量損失。我的輸入文件大小約爲7MB,輸出文件大小隻有1.5MB。

採樣率設置爲44100Hz,還有其他參數需要設置嗎?

謝謝你的回答。

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); 
     } 
    } 
+0

我做過類似的東西,我有很多的,當我有1024分之512樣本大小,一些測試後,我切換到4096,音頻質量是好的,問題沒有任何問題(與512/1024一樣) –

回答

0

我不知道確切的選項和他們做了什麼,但看看javadoc for IStreamCoder。還有其他各種可供選擇的選項。如果您想完全控制,您甚至可以直接在ffmpeg上設置標誌(Xuggler在下面使用),方法是setFlags()

6

我會做這樣的事情:

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

輸入要轉換和輸出是一個新的.MP3文件(Xuggler弄不清轉換由文件(AAC/WAV/WMA)延期)。

您可以提高質量提高kbps(即對於320 kbps,您需要傳入320000)。

希望幫助:-)

FYI:對於Java項目,您需要導入以下的,如果你還沒有這樣做:

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

如何將mp3轉換爲wav?這是我的問題http://stackoverflow.com/questions/15945387/convert-mp3-to-wav-using-xuggle謝謝 – Felix

0

要當心,當你有一個MP3與封面(PNG),你可能最終會出現錯誤,因爲你試圖發送視頻PNG流音頻流..通過使用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(); 
}