Как загрузить медиа-ресурсы из пути к классам в JMF - PullRequest
3 голосов
/ 29 ноября 2011

Итак, у меня есть Java-приложение, которое я хочу превратить в исполняемый файл jar. Я использую JMF в этом приложении, и я не могу заставить звуковые файлы работать правильно ...

Я создаю банку, используя

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

Итак, все звуковые файлы помещаются в банку, а в коде я создаю проигрыватели, используя

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

... Он не получает исключений IOException, поэтому, похоже, он, по крайней мере, находит сам файл в порядке.

Кроме того, до того, как я использовал getResource, у меня было так:

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

и он играл нормально, так что я знаю, что с самим звуковым файлом все в порядке

Причина, по которой я пытаюсь перейти на этот метод вместо метода File, заключается в том, что звуковые файлы могут быть упакованы внутри самого jar-файла и не должны быть его братьями и сестрами в каталоге.

Ответы [ 3 ]

3 голосов
/ 29 ноября 2011

Это далеко не рабочий код, но, похоже, он разрешает любые исключения во время выполнения (хотя на самом деле он еще не настроен на воспроизведение):

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);
2 голосов
/ 24 декабря 2011

Новое решение:

Сначала необходим пользовательский класс 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.">&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, чтобы вызвать зацикливание проигрывателя:

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

Опять же, я бы не стал рассматривать этот готовый к работе код (мог бы использовать еще несколько Javadocs, ведение журналов и т. Д.), Но он протестирован и работает (Java 1.6) и должен хорошо отвечать вашим потребностям.

Счастливого Рождества и счастливых праздников!

0 голосов
/ 29 ноября 2011
Manager.createPlayer(this.getClass().getResource("/song1.wav"));

Это будет работать, если song1.wav находится в корне Jar-файла, который находится в пути к классу приложения во время выполнения.

...