如何播放不在网址或存储空间内的音频文件的InputStream?

时间:2016-04-11 15:02:45

标签: android android-mediaplayer inputstream playback audiotrack

背景

我已成功将音频文件(3gp)上传到Google-Drive。

现在我希望能够在应用程序中播放该文件。

Google Drive API仅允许获取存储在那里的文件的输入流。

问题

输入的所有MediaPlayer功能在我的情况下都不可用,只有InputSteam:

http://developer.android.com/reference/android/media/MediaPlayer.html#setDataSource(java.io.FileDescriptor)

我知道我可以将文件从Google-Drive保存到缓存并播放,但我想避免存储处理,并动态播放文件。

我尝试了什么

我尝试搜索此问题,但发现可能使用AudioTrack(here)。也许可以使用新的Jelly-Bean功能(显示here,来自here),但我不确定它的级别是否很低。

可悲的是,使用AudioTrack我听错了声音(噪音)。

我还注意到MediaPlayer可以选择将dataSource设置为MediaDataSource(here),但不仅我不确定如何使用它,还需要API 23及更高版本。< / p>

当然,我尝试使用Google-Drive中提供的网址,但这仅用于其他目的,并且不会定向到音频文件,因此无法使用MediaPlayer。

问题

给定一个InputStream,是否可以使用AudioTrack或其他东西来播放音频3gp文件?

是否可能有支持库解决方案?

4 个答案:

答案 0 :(得分:2)

如果您的minSdkVersion为23或更高,则可以use setDataSource(MediaDataSource)并提供您自己的abstract MediaDataSource class的子类。

对于旧设备,您应该能够使用从ParcelFileDescriptor创建的管道。您将拥有一个将数据写入管道末端的线程,并将FileDescriptor(从getFileDescriptor())传递给玩家端setDataSource(FileDescriptor)

答案 1 :(得分:1)

最简单的MediaDataSource实现示例:

import android.media.MediaDataSource;
import android.os.Build;
import android.support.annotation.RequiresApi;

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

@RequiresApi(api = Build.VERSION_CODES.M)
public class InputStreamMediaDataSource extends MediaDataSource {
    private InputStream is;
    private long streamLength = -1, lastReadEndPosition;

    public InputStreamMediaDataSource(InputStream is, long streamLength) {
        this.is = is;
        this.streamLength = streamLength;
        if (streamLength <= 0){
            try {
                this.streamLength = is.available(); //Correct value of InputStream#available() method not always supported by InputStream implementation!
            } catch (IOException e) {
                e.printStackTrace();
            }
        }                 
    }

    @Override
    public synchronized void close() throws IOException {
        is.close();
    }

    @Override
    public synchronized int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
        if (position >= streamLength)
            return -1;

        if (position + size > streamLength)
            size -= (position + size) - streamLength;

        if (position < lastReadEndPosition) {
            is.close();
            lastReadEndPosition = 0;
            is = getNewCopyOfInputStreamSomeHow();//new FileInputStream(mediaFile) for example.
        }

        long skipped = is.skip(position - lastReadEndPosition);
        if (skipped == position - lastReadEndPosition) {
            int bytesRead = is.read(buffer, offset, size);
            lastReadEndPosition = position + bytesRead;
            return bytesRead;
        } else {
            return -1;
        }
    }

    @Override
    public synchronized long getSize() throws IOException {
        return streamLength;
    }
}

要使用API​​&gt; = 23,您必须提供streamLength值,如果(当)MediaPlayer返回 - 即position < lastReadEndPosition,您必须知道如何创建{的新副本{1}}。

用法示例:

你必须创建Activity,初始化InputStream类(有很多文件播放的例子)并且放置以下代码而不是旧的MediaPlayer

player.setDataSource("/path/to/media/file.3gp")

如果您的文件是Google云端硬盘上的对象if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { File file = new File("/path/to/media/file.3gp");//It is just an example! If you have real file on the phone memory, you don't need to wrap it to the InputStream to play it in MediaPlayer! player.setDataSource(new InputStreamMediaDataSource(new FileInputStream(file), file.length())); } else player.setDataSource(this, mediaUri); com.google.api.services.drive.model.File则可以

com.google.api.services.drive.Drive drive

InputStream is = drive.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute().getContent(); 的情况下,我必须在Android设备的本地主机上设置HTTP服务器(通过NanoHTTPd)并通过uri传输通过此服务器到MediaPlayer的字节流 - { {1}}

答案 2 :(得分:0)

对于任何对使用MediaDataSource实现感兴趣的人,我创建了一个可以预读并缓存数据缓冲区的人。它适用于任何InputStream,我主要创建它用于使用JCIFS SmbFile从网络文件中读取。

您可以在https://github.com/SteveGreatApe/BufferedMediaDataSource

找到它

答案 3 :(得分:-1)

如果您询问媒体播放器设置我们的音频路径,这段代码可能会有所帮助。

&#13;
&#13;
File directory = Environment.getExternalStorageDirectory();
		File file = new File( directory + "/AudioRecorder" );
		String AudioSavePathInDevice = file.getAbsolutePath() + "/" + "sample.wav" ;
    
     mediaPlayer = new MediaPlayer();
     
      try {
            mediaPlayer.setDataSource(AudioSavePathInDevice);
            mediaPlayer.prepare();
          } catch (IOException e) {
              e.printStackTrace();
          }

      mediaPlayer.start();
&#13;
&#13;
&#13;