所以,我有一个Java应用程序,我想变成一个可执行jar。我在这个应用程序中使用JMF,我似乎无法使声音文件正常工作......
我使用
创建jarjar cvfm jarname.jar manifest.txt *.class *.gif *.wav
因此,所有声音文件都放在jar中,在代码中,我正在使用
创建播放器Player player = Manager.createPlayer(ClassName.class.getResource("song1.wav"));
jar在我的桌面上,当我尝试运行它时,会发生以下异常:
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());
它播放得很好,所以我知道声音文件本身没有任何问题。
我尝试切换到此方法而不是File方法的原因是声音文件可以打包在jar本身内,而不必是目录中的兄弟节点。
答案 0 :(得分: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);
答案 1 :(得分:2)
新解决方案:
首先,需要一个返回DataSource
的自定义SourceStream
类来实现Seekable
:
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."><www.ziesemer.com></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();
}
}
}
}
然后,上面的自定义数据源用于创建播放器,并添加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."><www.ziesemer.com></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."><www.ziesemer.com></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),并且应该很好地满足您的需求。
圣诞快乐,节日快乐!答案 2 :(得分:0)
Manager.createPlayer(this.getClass().getResource("/song1.wav"));
如果song1.wav
位于应用程序的运行时类路径上的Jar的根目录中,那么这将有效。