2011-11-29 78 views
3

所以,我有我想變成一個可執行的JAR的Java應用程序。我在這個應用程序中使用JMF,我似乎無法得到的聲音文件工作的權利......如何從classpath中加載的媒體資源JMF

我創建使用

jar cvfm jarname.jar manifest.txt *.class *.gif *.wav 

所以罐子,所有的聲音文件被放在裏面的罐子,並在代碼中,我使用

Player player = Manager.createPlayer(ClassName.class.getResource("song1.wav")); 

罐子是我的桌面上,當我嘗試運行它,會出現此異常創建玩家:

javax.media.NoPlayerException: Cannot find a Player for :jar:file:/C:/Users/Pojo/ 
Desktop/jarname.jar!/song1.wav 

...它沒有得到的IOExceptions,所以它似乎至少可以找到文件本身沒事。

另外,我用的getResource之前,我曾經有過這樣的:

Player player = Manager.createPlayer(new File("song1.wav").toURL()); 

和它打很好,所以我知道什麼是錯的聲音文件本身。

我想切換到這個方法,而不是文件的方法的原因是,這樣的聲音文件可以被打包的jar本身內部,並沒有成爲它的一個目錄中的兄弟姐妹。

+0

爲什麼使用JMF進行聲音? 'javax.sound。採樣的API自1.3以來一直是J2SE的一部分。 –

+0

@Andrew湯普森 上有什麼'javax.sound.sampled'其他操作除了剪輯?因爲我使用剪輯嘗試,它只是因爲他們太大,剪輯拒絕玩什麼比1MB左右 – Pojo

+0

大不會爲這些文件的工作*「剪輯拒絕玩什麼大於1MB左右」 * a)您可能應該將這些轉換爲MP3格式,所以它們(通常)會比WAV更小。 B)如果你這樣做,那麼需要JMF的'mp3plugin.jar'來解碼MP3,而不是整個JMF。 C)Oracle的'Clip'實現最多可以處理44.1 KHz 16位立體聲的1秒,但是還有其他兩種處理大型音頻的方法。 1)[BigClip](http://stackoverflow.com/questions/5667454/playing-audio-file-in-java-application/5668510#5668510)2)加載流和由組塊播放塊。 –

回答

2

新的解決方案:

首先,定製DataSource類返回實現Seekable一個SourceStream需要:

package com.ziesemer.test; 

import java.io.Closeable; 
import java.io.IOException; 
import java.io.InputStream; 
import java.net.JarURLConnection; 
import java.net.URL; 
import java.util.jar.JarEntry; 
import java.util.jar.JarFile; 

import javax.media.Duration; 
import javax.media.MediaLocator; 
import javax.media.Time; 
import javax.media.protocol.ContentDescriptor; 
import javax.media.protocol.PullDataSource; 
import javax.media.protocol.PullSourceStream; 
import javax.media.protocol.Seekable; 

/** 
* @author Mark A. Ziesemer 
* <a href="http://www.ziesemer.com.">&lt;www.ziesemer.com&gt;</a> 
*/ 
public class JarDataSource extends PullDataSource{ 

    protected JarURLConnection conn; 
    protected ContentDescriptor contentType; 
    protected JarPullSourceStream[] sources; 
    protected boolean connected; 

    public JarDataSource(URL url) throws IOException{ 
     setLocator(new MediaLocator(url)); 
     connected = false; 
    } 

    @Override 
    public PullSourceStream[] getStreams(){ 
     return sources; 
    } 

    @Override 
    public void connect() throws IOException{ 
     conn = (JarURLConnection)getLocator().getURL().openConnection(); 
     conn.connect(); 
     connected = true; 

     JarFile jf = conn.getJarFile(); 
     JarEntry je = jf.getJarEntry(conn.getEntryName()); 

     String mimeType = conn.getContentType(); 
     if(mimeType == null){ 
      mimeType = ContentDescriptor.CONTENT_UNKNOWN; 
     } 
     contentType = new ContentDescriptor(ContentDescriptor.mimeTypeToPackageName(mimeType)); 

     sources = new JarPullSourceStream[1]; 
     sources[0] = new JarPullSourceStream(jf, je, contentType); 
    } 

    @Override 
    public String getContentType(){ 
     return contentType.getContentType(); 
    } 

    @Override 
    public void disconnect(){ 
     if(connected){ 
      try{ 
       sources[0].close(); 
      }catch(IOException e){ 
       e.printStackTrace(); 
      } 
      connected = false; 
     } 
    } 

    @Override 
    public void start() throws IOException{ 
     // Nothing to do. 
    } 

    @Override 
    public void stop() throws IOException{ 
     // Nothing to do. 
    } 

    @Override 
    public Time getDuration(){ 
     return Duration.DURATION_UNKNOWN; 
    } 

    @Override 
    public Object[] getControls(){ 
     return new Object[0]; 
    } 

    @Override 
    public Object getControl(String controlName){ 
     return null; 
    } 

    protected class JarPullSourceStream implements PullSourceStream, Seekable, Closeable{ 

     protected final JarFile jarFile; 
     protected final JarEntry jarEntry; 
     protected final ContentDescriptor type; 

     protected InputStream stream; 
     protected long position; 

     public JarPullSourceStream(JarFile jarFile, JarEntry jarEntry, ContentDescriptor type) throws IOException{ 
      this.jarFile = jarFile; 
      this.jarEntry = jarEntry; 
      this.type = type; 
      this.stream = jarFile.getInputStream(jarEntry); 
     } 

     @Override 
     public ContentDescriptor getContentDescriptor(){ 
      return type; 
     } 

     @Override 
     public long getContentLength(){ 
      return jarEntry.getSize(); 
     } 

     @Override 
     public boolean endOfStream(){ 
      return position < getContentLength(); 
     } 

     @Override 
     public Object[] getControls(){ 
      return new Object[0]; 
     } 

     @Override 
     public Object getControl(String controlType){ 
      return null; 
     } 

     @Override 
     public boolean willReadBlock(){ 
      if(endOfStream()){ 
       return true; 
      } 
      try{ 
       return stream.available() == 0; 
      }catch(IOException e){ 
       return true; 
      } 
     } 

     @Override 
     public int read(byte[] buffer, int offset, int length) throws IOException{ 
      int read = stream.read(buffer, offset, length); 
      position += read; 
      return read; 
     } 

     @Override 
     public long seek(long where){ 
      try{ 
       if(where < position){ 
        stream.close(); 
        stream = jarFile.getInputStream(jarEntry); 
        position = 0; 
       } 
       long skip = where - position; 
       while(skip > 0){ 
        long skipped = stream.skip(skip); 
        skip -= skipped; 
        position += skipped; 
       } 
      }catch(IOException ioe){ 
       // Made a best effort. 
       ioe.printStackTrace(); 
      } 
      return position; 
     } 

     @Override 
     public long tell(){ 
      return position; 
     } 

     @Override 
     public boolean isRandomAccess(){ 
      return true; 
     } 

     @Override 
     public void close() throws IOException{ 
      try{ 
       stream.close(); 
      }finally{ 
       jarFile.close(); 
      } 
     } 

    } 

} 

然後,將上述定製噠TA源用於創建一個播放器,並加入一個ControllerListener使遊戲循環:

package com.ziesemer.test; 

import java.net.URL; 

import javax.media.ControllerEvent; 
import javax.media.ControllerListener; 
import javax.media.EndOfMediaEvent; 
import javax.media.Manager; 
import javax.media.Player; 
import javax.media.Time; 

/** 
* @author Mark A. Ziesemer 
* <a href="http://www.ziesemer.com.">&lt;www.ziesemer.com&gt;</a> 
*/ 
public class JmfTest{ 
    public static void main(String[] args) throws Exception{ 
     URL url = JmfTest.class.getResource("Test.wav"); 
     JarDataSource jds = new JarDataSource(url); 
     jds.connect(); 
     final Player player = Manager.createPlayer(jds); 

     player.addControllerListener(new ControllerListener(){ 
      @Override 
      public void controllerUpdate(ControllerEvent ce){ 
       if(ce instanceof EndOfMediaEvent){ 
        player.setMediaTime(new Time(0)); 
        player.start(); 
       } 
      } 
     }); 
     player.start(); 
    } 
} 

注意,如果沒有自定義數據源,JMF反覆嘗試尋求回到開頭 - 但失敗,最終放棄。這可以從調試同一ControllerListener,將收到每個嘗試的幾個事件可以看出。

或者,用MediaPlayer方法循環(你在我前面的回答中提到):

package com.ziesemer.test; 

import java.net.URL; 

import javax.media.Manager; 
import javax.media.Player; 
import javax.media.bean.playerbean.MediaPlayer; 

/** 
* @author Mark A. Ziesemer 
* <a href="http://www.ziesemer.com.">&lt;www.ziesemer.com&gt;</a> 
*/ 
public class JmfTest{ 
    public static void main(String[] args) throws Exception{ 
     URL url = JmfTest.class.getResource("Test.wav"); 
     JarDataSource jds = new JarDataSource(url); 
     jds.connect(); 
     final Player player = Manager.createPlayer(jds); 

     MediaPlayer mp = new MediaPlayer(); 
     mp.setPlayer(player); 
     mp.setPlaybackLoop(true); 
     mp.start(); 
    } 
} 

同樣,我不會考慮這個產品代碼(可以使用一些更多的Javadoc和記錄等),但它已經過測試和工作(Java 1.6),並且應該很好地滿足您的需求。

聖誕快樂,節日快樂!

+0

謝謝Z先生!你再次爲我而來! (這次是真實的)^ _^ – Pojo

+0

@ziesemer它提升了線程「main」中的一個異常異常java.lang.ClassCastException:sun.net.www.protocol.file.FileURLConnection不能轉換爲java.net.JarURLConnection \t在com.JarDataSource.connect(JarDataSource.java:39) \t在com.JmfTest.main(JmfTest.java:20) – Srivathsan

+0

@Srivathsan - 您使用的代碼提供的,或經過修改的?什麼JDK版本? – ziesemer

0
Manager.createPlayer(this.getClass().getResource("/song1.wav")); 

如果song1.wav是在一個罐子裏它是應用程序的運行時類路徑上的根目錄,將工作。

+0

它必須是'this.getClass()'嗎?我可以不使用類名.class嗎? – Pojo

+1

你可能比添加評論更快,並且等待我問*「當你嘗試時發生了什麼?」*。 –

+0

Tsk。這些天愚蠢的聰明人...呃,我都嘗試過。都沒有工作。 – Pojo

3

這是從生產代碼相去甚遠,但這似乎解決了所有運行時異常(儘管它實際上沒有連接好打任何東西):

import javax.media.Manager; 
import javax.media.Player; 
import javax.media.protocol.URLDataSource; 

// ... 

URL url = JmfTest.class.getResource("song1.wav"); 
System.out.println("url: " + url); 
URLDataSource uds = new URLDataSource(url); 
uds.connect(); 
Player player = Manager.createPlayer(uds); 
+0

啊!再次感謝ziesemer先生。你已經保存了我的項目。我向你們致敬。 <3 – Pojo

+0

等一下,堅持下去。事實證明,即使在MediaPlayer上顯式調用setPlaybackLoop(true),聲音文件也不會循環。他們玩過一次,然後再也沒有聽到過。他們應該永遠循環... – Pojo

+0

請包括您的附加代碼,實際播放和循環聲音,以便我們有一些工作。 – ziesemer