2011-11-29 67 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

相关问题