黑莓自定义数据源的播放器

时间:2010-03-29 06:49:55

标签: audio blackberry java-me streaming mmapi

我必须在应用程序中创建一个支持mp3和wav文件的自定义媒体播放器。我在没有自定义数据源的文档中读到了我无法查找或获取媒体文件的持续时间。

我检查了JDE 4.6中的演示,但我仍然有问题...我无法获得持续时间,它返回的远远超过预期所以我确定我在修改代码时搞砸了一些内容来自文件系统的本地mp3文件。

有人可以告诉我我做错了什么吗? (我可以听到mp3,所以播放器从头到尾正确播放)

我必须支持操作系统> = 4.6。

这是我修改过的数据源:

/* LimitedRateStreaminSource.java
 *
 * Copyright © 1998-2009 Research In Motion Ltd.
 * 
 * Note: For the sake of simplicity, this sample application may not leverage
 * resource bundles and resource strings.  However, it is STRONGLY recommended
 * that application developers make use of the localization features available
 * within the BlackBerry development platform to ensure a seamless application
 * experience across a variety of languages and geographies.  
 * For more information on localizing your application, please refer to the 
 * BlackBerry Java Development Environment Development Guide associated with 
 * this release.
 */

package com.halcyon.tawkwidget.model;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.media.Control;
import javax.microedition.media.protocol.ContentDescriptor;
import javax.microedition.media.protocol.DataSource;
import javax.microedition.media.protocol.SourceStream;

import net.rim.device.api.io.SharedInputStream;

/**
 * The data source used by the BufferedPlayback's media player.
 */
public final class LimitedRateStreamingSource extends DataSource
{
    /** The max size to be read from the stream at one time. */
    private static final int READ_CHUNK = 512; // bytes

    /** A reference to the field which displays the load status. */
    //private TextField _loadStatusField;

    /** A reference to the field which displays the player status. */
    //private TextField _playStatusField;

    /**
     * The minimum number of bytes that must be buffered before the media file
     * will begin playing.
     */
    private int _startBuffer = 200000;

    /** The maximum size (in bytes) of a single read. */
    private int _readLimit = 32000;

    /**
     * The minimum forward byte buffer which must be maintained in order for
     * the video to keep playing. If the forward buffer falls below this
     * number, the playback will pause until the buffer increases.
     */
    private int _pauseBytes = 64000;

    /**
     * The minimum forward byte buffer required to resume
     * playback after a pause.
     */
    private int _resumeBytes = 128000;

    /** The stream connection over which media content is passed. */
    //private ContentConnection _contentConnection;

    private FileConnection _fileConnection;

    /** An input stream shared between several readers. */
    private SharedInputStream _readAhead;

    /** A stream to the buffered resource. */
    private LimitedRateSourceStream _feedToPlayer;

    /** The MIME type of the remote media file. */
    private String _forcedContentType;

    /** A counter for the total number of buffered bytes */
    private volatile int _totalRead;

    /** A flag used to tell the connection thread to stop */
    private volatile boolean _stop;

    /**
     * A flag used to indicate that the initial buffering is complete. In
     * other words, that the current buffer is larger than the defined start
     * buffer size.
     */
    private volatile boolean _bufferingComplete;

    /** A flag used to indicate that the remote file download is complete. */
    private volatile boolean _downloadComplete;

    /** The thread which retrieves the remote media file. */
    private ConnectionThread _loaderThread;

    /** The local save file into which the remote file is written. */
    private FileConnection _saveFile;

    /** A stream for the local save file. */
    private OutputStream _saveStream;

    /**
     * Constructor.
     * @param locator The locator that describes the DataSource.
     */
    public LimitedRateStreamingSource(String locator)
    {
        super(locator);
    }

    /**
     * Open a connection to the locator.
     * @throws IOException
     */
    public void connect() throws IOException
    {
        //Open the connection to the remote file.

     _fileConnection = (FileConnection)Connector.open(getLocator(), 
            Connector.READ);
        //Cache a reference to the locator.
        String locator = getLocator();

        //Report status.
        System.out.println("Loading: " + locator);
        //System.out.println("Size: " + _contentConnection.getLength());
        System.out.println("Size: " + _fileConnection.totalSize());

        //The name of the remote file begins after the last forward slash.
        int filenameStart = locator.lastIndexOf('/');

        //The file name ends at the first instance of a semicolon.
        int paramStart = locator.indexOf(';');

        //If there is no semicolon, the file name ends at the end of the line.
        if (paramStart < 0)
        {
            paramStart = locator.length();
        }

        //Extract the file name.
        String filename = locator.substring(filenameStart, paramStart);
        System.out.println("Filename: " + filename);

        //Open a local save file with the same name as the remote file.
        _saveFile = (FileConnection) Connector.open("file:///SDCard"+
            "/blackberry/music" + filename, Connector.READ_WRITE);

        //If the file doesn't already exist, create it.
        if (!_saveFile.exists())
        {
            _saveFile.create();
        }
        System.out.println("---------- 1");
        //Open the file for writing.
        _saveFile.setReadable(true);

        //Open a shared input stream to the local save file to
        //allow many simultaneous readers.
        SharedInputStream fileStream = SharedInputStream.getSharedInputStream(
            _saveFile.openInputStream());

        //Begin reading at the beginning of the file.
        fileStream.setCurrentPosition(0);
        System.out.println("---------- 2");
        //If the local file is smaller than the remote file...
        if (_saveFile.fileSize() < _fileConnection.totalSize())
        {
         System.out.println("---------- 3");
            //Did not get the entire file, set the system to try again.
            _saveFile.setWritable(true);
            System.out.println("---------- 4");
            //A non-null save stream is used as a flag later to indicate that
            //the file download was incomplete.
            _saveStream = _saveFile.openOutputStream();
            System.out.println("---------- 5");
            //Use a new shared input stream for buffered reading.
            _readAhead = SharedInputStream.getSharedInputStream(
                _fileConnection.openInputStream());
            System.out.println("---------- 6");
        }
        else
        {
            //The download is complete.
         System.out.println("---------- 7");
         _downloadComplete = true;

            //We can use the initial input stream to read the buffered media.
            _readAhead = fileStream;
            System.out.println("---------- 8");
            //We can close the remote connection.
            _fileConnection.close();
            System.out.println("---------- 9");
        }

        if (_forcedContentType != null)
        {
            //Use the user-defined content type if it is set.
         System.out.println("---------- 10");
            _feedToPlayer = new LimitedRateSourceStream(_readAhead, 
                _forcedContentType);
            System.out.println("---------- 11");
        }
        else
        {
         System.out.println("---------- 12");
            //Otherwise, use the MIME types of the remote file.
           // _feedToPlayer = new LimitedRateSourceStream(_readAhead, 
                  _fileConnection));
        }
        System.out.println("---------- 13");
    }

    /**
     * Destroy and close all existing connections.
     */
    public void disconnect() {
        try 
        {
            if (_saveStream != null)
            {
                //Destroy the stream to the local save file.
                _saveStream.close();
                _saveStream = null;
            }

            //Close the local save file.
            _saveFile.close();

            if (_readAhead != null)
            {
                //Close the reader stream.
                _readAhead.close();
                _readAhead = null;   
            }

            //Close the remote file connection.
            _fileConnection.close();

            //Close the stream to the player.
            _feedToPlayer.close();
        }
        catch (Exception e)
        {
            System.err.println(e.getMessage());
        }
    }

    /**
     * Returns the content type of the remote file.
     * @return The content type of the remote file.
     */
    public String getContentType()
    {
        return _feedToPlayer.getContentDescriptor().getContentType();
    }

    /**
     * Returns a stream to the buffered resource.
     * @return A stream to the buffered resource.
     */
    public SourceStream[] getStreams()
    {
        return new SourceStream[] { _feedToPlayer };
    }

    /**
     * Starts the connection thread used to download the remote file.
     */
    public void start() throws IOException
    {
        //If the save stream is null, we have already completely downloaded
        //the file.
        if (_saveStream != null)
        {
            //Open the connection thread to finish downloading the file.
            _loaderThread = new ConnectionThread();
            _loaderThread.start();
        }
    }

    /**
     * Stop the connection thread.
     */
    public void stop() throws IOException
    {
        //Set the boolean flag to stop the thread.
        _stop = true;
    }

    /**
     * @see javax.microedition.media.Controllable#getControl(String)
     */
    public Control getControl(String controlType)
    {
        // No implemented Controls.
        return null;
    }

    /**
     * @see javax.microedition.media.Controllable#getControls()
     */
    public Control[] getControls()
    {
        // No implemented Controls.
        return null;
    }

    /**
     * Force the lower level stream to a given content type. Must be called
     * before the connect function in order to work.
     * @param contentType The content type to use.
     */
    public void setContentType(String contentType)
    {
        _forcedContentType = contentType;
    }

    /**
     * A stream to the buffered media resource.
     */
    private final class LimitedRateSourceStream implements SourceStream
    {
        /** A stream to the local copy of the remote resource. */
        private SharedInputStream _baseSharedStream;

        /** Describes the content type of the media file. */
        private ContentDescriptor _contentDescriptor;

        /**
         * Constructor. Creates a LimitedRateSourceStream from
         * the given InputStream.
         * @param inputStream The input stream used to create a new reader.
         * @param contentType The content type of the remote file.
         */
        LimitedRateSourceStream(InputStream inputStream, String contentType)
        {
         System.out.println("[LimitedRateSoruceStream]---------- 1");
            _baseSharedStream = SharedInputStream.getSharedInputStream(
                inputStream);
            System.out.println("[LimitedRateSoruceStream]---------- 2");
            _contentDescriptor = new ContentDescriptor(contentType);
            System.out.println("[LimitedRateSoruceStream]---------- 3");
        }

        /**
         * Returns the content descriptor for this stream.
         * @return The content descriptor for this stream.
         */
        public ContentDescriptor getContentDescriptor()
        {
            return _contentDescriptor;
        }

        /**
         * Returns the length provided by the connection.
         * @return long The length provided by the connection.
         */
        public long getContentLength()
        {
            return _fileConnection.totalSize();
        }

        /**
         * Returns the seek type of the stream.
         */
        public int getSeekType()
        {
         return RANDOM_ACCESSIBLE;
            //return SEEKABLE_TO_START;
        }

        /**
         * Returns the maximum size (in bytes) of a single read.
         */
        public int getTransferSize() 
        {
            return _readLimit;
        }

       /**
        * Writes bytes from the buffer into a byte array for playback.
        * @param bytes The buffer into which the data is read.
        * @param off The start offset in array b at which the data is written.
        * @param len The maximum number of bytes to read.
        * @return the total number of bytes read into the buffer, or -1 if
        * there is no more data because the end of the stream has been reached.
        * @throws IOException 
        */
        public int read(byte[] bytes, int off, int len) throws IOException
        {
         System.out.println("[LimitedRateSoruceStream]---------- 5");
            System.out.println("Read Request for: " + len + " bytes");

            //Limit bytes read to our readLimit.
            int readLength = len;
            System.out.println("[LimitedRateSoruceStream]---------- 6");
            if (readLength > getReadLimit())
            {
                readLength = getReadLimit();
            }

            //The number of available byes in the buffer.
            int available;

            //A boolean flag indicating that the thread should pause
            //until the buffer has increased sufficiently.
            boolean paused = false;
            System.out.println("[LimitedRateSoruceStream]---------- 7");
            for (;;)
            {
                available = _baseSharedStream.available();

                System.out.println("[LimitedRateSoruceStream]---------- 8");
                if (_downloadComplete)
                {
                    //Ignore all restrictions if downloading is complete.
                    System.out.println("Complete, Reading: " + len + 
                        " - Available: " + available);
                    return _baseSharedStream.read(bytes, off, len);
                }
                else if(_bufferingComplete)
                {
                    if (paused && available > getResumeBytes())
                    {
                        //If the video is paused due to buffering, but the
                        //number of available byes is sufficiently high,
                        //resume playback of the media.
                        System.out.println("Resuming - Available: " + 
                            available);
                        paused = false;
                        return _baseSharedStream.read(bytes, off, readLength);
                    }
                    else if(!paused && (available > getPauseBytes() || 
                        available > readLength))
                    {
                        //We have enough information for this media playback.

                        if (available < getPauseBytes())
                        {
                            //If the buffer is now insufficient, set the
                            //pause flag.
                            paused = true;
                        }

                        System.out.println("Reading: " + readLength + 
                            " - Available: " + available);
                        return _baseSharedStream.read(bytes, off, readLength);
                    }
                    else if(!paused)
                    {
                        //Set pause until loaded enough to resume.
                        paused = true;
                    }
                }
                else
                {
                    //We are not ready to start yet, try sleeping to allow the
                    //buffer to increase.
                    try
                    {
                        Thread.sleep(500);
                    }
                    catch (Exception e)
                    {
                        System.err.println(e.getMessage());
                    }
                }
            }
        }

        /**
         * @see javax.microedition.media.protocol.SourceStream#seek(long)
         */
        public long seek(long where) throws IOException
        {
            _baseSharedStream.setCurrentPosition((int) where);
            return _baseSharedStream.getCurrentPosition();
        }

        /**
         * @see javax.microedition.media.protocol.SourceStream#tell()
         */
        public long tell()
        {
            return _baseSharedStream.getCurrentPosition();
        }

        /**
         * Close the stream.
         * @throws IOException
         */
        void close() throws IOException
        {
            _baseSharedStream.close();
        }

        /**
         * @see javax.microedition.media.Controllable#getControl(String)
         */
        public Control getControl(String controlType)
        {
            // No implemented controls.
            return null;
        }

        /**
         * @see javax.microedition.media.Controllable#getControls()
         */
        public Control[] getControls()
        {
            // No implemented controls.
            return null;
        }
    }

   /**
    * A thread which downloads the remote file and writes it to the local file.
    */
    private final class ConnectionThread extends Thread
    {
        /**
         * Download the remote media file, then write it to the local
         * file.
         * @see java.lang.Thread#run()
         */
        public void run()
        {
            try
            {
                byte[] data = new byte[READ_CHUNK];
                int len = 0;

                //Until we reach the end of the file.
                while (-1 != (len = _readAhead.read(data)))
                {
                    _totalRead += len;

                    if (!_bufferingComplete && _totalRead > getStartBuffer())
                    {
                        //We have enough of a buffer to begin playback.
                        _bufferingComplete = true;
                        System.out.println("Initial Buffering Complete");
                    }

                    if (_stop)
                    {
                        //Stop reading.
                        return;
                    }

                }

                System.out.println("Downloading Complete");
                System.out.println("Total Read: " + _totalRead);

                //If the downloaded data is not the same size
                //as the remote file, something is wrong.
                if (_totalRead != _fileConnection.totalSize())
                {
                    System.err.println("* Unable to Download entire file *");
                }

                _downloadComplete = true;
                _readAhead.setCurrentPosition(0);

                //Write downloaded data to the local file.
                while (-1 != (len = _readAhead.read(data)))
                {
                    _saveStream.write(data);
                }

            }
            catch (Exception e)
            {
                System.err.println(e.toString());
            }
        }
    }

    /**
     * Gets the minimum forward byte buffer which must be maintained in
     * order for the video to keep playing.
     * @return The pause byte buffer.
     */
    int getPauseBytes()
    {
        return _pauseBytes;
    }

    /**
     * Sets the minimum forward buffer which must be maintained in order
     * for the video to keep playing.
     * @param pauseBytes The new pause byte buffer.
     */
    void setPauseBytes(int pauseBytes)
    {
        _pauseBytes = pauseBytes;
    }

    /**
     * Gets the maximum size (in bytes) of a single read.
     * @return The maximum size (in bytes) of a single read.
     */ 
    int getReadLimit()
    {
        return _readLimit;
    }

    /**
     * Sets the maximum size (in bytes) of a single read.
     * @param readLimit The new maximum size (in bytes) of a single read.
     */
    void setReadLimit(int readLimit)
    {
        _readLimit = readLimit;
    }

    /**
     * Gets the minimum forward byte buffer required to resume
     * playback after a pause.
     * @return The resume byte buffer.
     */
    int getResumeBytes()
    {
        return _resumeBytes;
    }

    /**
     * Sets the minimum forward byte buffer required to resume
     * playback after a pause.
     * @param resumeBytes The new resume byte buffer.
     */
    void setResumeBytes(int resumeBytes)
    {
        _resumeBytes = resumeBytes;
    }

    /**
     * Gets the minimum number of bytes that must be buffered before the
     * media file will begin playing.
     * @return The start byte buffer.
     */
    int getStartBuffer()
    {
        return _startBuffer;
    }

    /**
     * Sets the minimum number of bytes that must be buffered before the
     * media file will begin playing.
     * @param startBuffer The new start byte buffer.
     */
    void setStartBuffer(int startBuffer)
    {
        _startBuffer = startBuffer;
    }
}

通过这种方式我使用它:

LimitedRateStreamingSource source = new                                     
    LimitedRateStreamingSource("file:///SDCard/music3.mp3");
source.setContentType("audio/mpeg");
mediaPlayer = javax.microedition.media.Manager.createPlayer(source);
mediaPlayer.addPlayerListener(this);
mediaPlayer.realize();
mediaPlayer.prefetch();

开始后我使用mediaPlayer.getDuration它返回让我们说24:22左右(黑莓中的inbuild媒体播放器说文件长度为4:05)

我试图在监听器中获得持续时间,但不幸的是它在64分钟左右返回,所以我确信数据流中的内容不好......

转换

的代码
  String getElapsedTimeMinutesSeconds(long elapsedTime) {
  long Seconds=(elapsedTime/1000)%60; 
  long Minutes=(elapsedTime/(1000*60))%60;
  long Hours=(elapsedTime/(1000*60*60))%24;
  return ""+Minutes + ":"+Seconds; 
} 

2 个答案:

答案 0 :(得分:1)

Player.setMediaTime()和Player.getMediaTime()都以微秒为单位,而不是毫秒。因此,要获得经过的秒数,您需要除以1000000而不是1000。

答案 1 :(得分:0)

前一段时间我遇到了播放无限大小音频的问题。我修理它。 这是链接:

http://supportforums.blackberry.com/t5/Java-Development/Use-custom-DataSource-to-play-audio/m-p/1373247#M178928