如何从JMF中的类路径加载媒体资源 [英] How to load media resources from the classpath in JMF

查看:83
本文介绍了如何从JMF中的类路径加载媒体资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我有一个Java应用程序,我想变成一个可执行jar。我在这个应用程序中使用JMF,我似乎无法使声音文件正常工作...

So, I have a Java application that I want to turn into an executable jar. I am using JMF in this application, and I can't seem to get the sound files working right...

我使用

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

因此,所有声音文件都放在jar中,在代码中,我正在创建玩家

So, all the sound files get put inside the jar, and in the code, I am creating the Players using

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

jar在我的桌面上,当我尝试运行它时,会发生以下异常:

The jar is on my desktop, and when I attempt to run it, this exception occurs:

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

...它没有得到IOExceptions,所以看来至少可以找到文件本身。

...It's not getting IOExceptions, so it seems to at least be finding the file itself all right.

另外,在我使用getResource之前,我曾经这样做过:

Also, before I used the getResource, I used to have it like this:

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

并且播放正常,所以我知道声音文件本身没有任何问题。

and it was playing fine, so I know nothing is wrong with the sound file itself.

我试图切换到此方法而不是File方法的原因是声音文件可以打包在jar本身内而不必是它的兄弟姐妹在目录中。

The reason I am trying to switch to this method instead of the File method is so that the sound files can be packaged inside the jar itself and not have to be its siblings in a directory.

推荐答案

新解决方案:

首先,a自定义 DataSource 返回实现 Seekable SourceStream 的类是需要:

First, a custom DataSource class that returns a SourceStream that implements Seekable is needed:

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

    }

}

然后,上面的自定义数据源用于创建播放器,并添加 ControllerListener 以使播放器循环:

Then, the above custom data source is used to create a player, and a ControllerListener is added to cause the player to loop:

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 看出,每次尝试都会收到几个事件。

Note that without the custom data source, JMF tries repeatedly to seek back to the beginning - but fails, and eventually gives up. This can be seen from debugging the same ControllerListener, which will receive a several events for each attempt.

或者,使用 MediaPlayer 循环方法(你在我之前的回答中提到过):

Or, using the MediaPlayer approach to loop (that you mentioned on my previous answer):

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),并且应该很好地满足您的需求。

Again, I would not consider this production-ready code (could use some more Javadocs and logging, etc.), but it is tested and working (Java 1.6), and should meet your needs nicely.

圣诞快乐,和节日快乐!

Merry Christmas, and happy holidays!

这篇关于如何从JMF中的类路径加载媒体资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆