audio - 在Blackberry上具有自定义数据源的播放器

标签 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(黑莓机中的内置媒体播放器说文件长度为4:05)。

我试图在监听器中获取持续时间,但不幸的是在64分钟左右返回了该持续时间,因此我确定datasoruce中的某些内容不好...。

转换的代码
  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; 
} 

最佳答案

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

关于audio - 在Blackberry上具有自定义数据源的播放器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2536200/

相关文章:

ios - 设置 AVPlayerViewController 的背景图片

http - BlackBerry - 为连接设置 HTTP 代理

php - 即时获取 YouTube 音频流

blackberry - 黑莓开发的持续集成服务器? (和证书签名)

java - 在 Java + Linux + eclipse juno 上设置 Blackberry 开发环境

user-interface - BlackBerry-将项目添加到ListField

java - 在 J2ME 中如何从另一个类对象中获取一个 byte[] 对象?

java - J2ME Wireless Toolkits 有什么区别,该选哪个?

Python + winsound - 检查是否正在播放音频文件

ios - 如何在 iOS 中导出具有效果的音频文件