Android 如何播放';它不在url或存储中? 背景

Android 如何播放';它不在url或存储中? 背景,android,android-mediaplayer,inputstream,playback,audiotrack,Android,Android Mediaplayer,Inputstream,Playback,Audiotrack,我已经成功地将音频文件(3gp)上传到谷歌硬盘 现在我希望能够在应用程序中播放文件 GoogleDriveAPI只允许获取存储在那里的文件的输入流 问题 在我的案例中,所有MediaPlayer输入功能都不可用,仅InputSteam: 我知道我可以将文件从谷歌硬盘保存到缓存中并播放,但我想避免存储处理,并动态播放文件 我试过的 我试图搜索这个问题,但只发现使用AudioTrack()可能是可行的。也可以使用新的Jelly Bean特性(显示,从中找到),但我不确定,因为它的级别相当低 遗憾的

我已经成功地将音频文件(3gp)上传到谷歌硬盘

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

GoogleDriveAPI只允许获取存储在那里的文件的输入流

问题 在我的案例中,所有MediaPlayer输入功能都不可用,仅InputSteam:

我知道我可以将文件从谷歌硬盘保存到缓存中并播放,但我想避免存储处理,并动态播放文件

我试过的 我试图搜索这个问题,但只发现使用AudioTrack()可能是可行的。也可以使用新的Jelly Bean特性(显示,从中找到),但我不确定,因为它的级别相当低

遗憾的是,使用AudioTrack播放时,我听到了错误的声音(噪音)

我还注意到MediaPlayer可以选择将数据源设置为MediaDataSource(),但我不仅不确定如何使用它,还需要API 23及更高版本

当然,我尝试使用Google Drive中提供的url,但这只用于其他目的,并且没有指向音频文件,因此不能使用MediaPlayer

问题 给定输入流,是否可以使用AudioTrack或其他方式来播放音频3gp文件


是否有支持库解决方案?

如果您的
minSdkVersion
为23或更高版本,您可以创建并提供自己的
摘要的子类


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

如果您询问media player设置我们的音频路径,此代码可能会有所帮助

File directory=Environment.getExternalStorageDirectory();
File File=新文件(目录+“/AudioRecorder”);
String AudioSavePathInDevice=file.getAbsolutePath()+“/”+“sample.wav”;
mediaPlayer=新的mediaPlayer();
试一试{
mediaPlayer.setDataSource(AudioSavePathInDevice);
mediaPlayer.prepare();
}捕获(IOE异常){
e、 printStackTrace();
}

mediaPlayer.start()对于任何对使用MediaDataSource实现感兴趣的人,我创建了一个可以提前读取和缓存数据缓冲区的实现。它可以从任何InputStream工作,我主要创建它是为了使用JCIFS SmbFile从网络文件中读取


您可以在最简单的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;
    }
}
如果您的文件是google drive上的对象
com.google.api.services.drive.model.file
,并且
com.google.api.services.drive.drive
您可以

InputStream is = drive.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute().getContent();

Build.VERSION.SDK\u INT
的情况下,我必须在Android设备的本地主机上设置HTTP服务器(通过),并通过uri将字节流传输到MediaPlayer-
player.setDataSource(这是mediaUri)

关于MediaDataSource,这是我找到的一个解决方案,但是我没有看到任何关于如何使用它的例子。你能为这两种方式都显示一些代码吗?@androiddeveloper:我从未使用过
MediaDataSource
;前几天我在帮助别人的时候碰到了它。虽然我没有使用
ParcelFileDescriptor
管道来播放媒体,但我已经使用它了,等等。这太糟糕了。谢谢。@commonware我已经检查了
setDataSource(FileDescriptor)
。它生成'W/System.err:java.io.IOException:setDataSourceFD失败。:status=0x8000000 W/System.err:at android.media.MediaPlayer。_setDataSource(本机方法)W/System.err:at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1133)W/System.err:at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1118)'如果FileDescriptor下只有一个InputStream,而Android文件系统上没有真正的文件,请标记:(@isabsent:Media playback需要一个文件或受支持的流服务器协议(HTTP、RTSP等)。当无法倒带时,它无法正常工作,并且管道上有一个流(例如,
ParcelFileDescriptor.createPipe()
)无法重编。你有过解决这个问题的方法吗?@newenglander没有。下面的答案对我来说太笼统了,我没有时间进一步调查。@newenglander我已经为它创建了一个请求:虽然这在理论上可以回答这个问题,但在这里包括答案的基本部分,并提供链接以供参考参考。有关如何编写更好的“基于链接”的答案的说明,请参见。谢谢!很好。谢谢。您能说明如何使用它,以及如何处理API<23(这仍然是很多用户)吗?请说明如何让“mediaUri”与驱动API提供的输入流一起工作。这就是问题所在……我不确定:(我从未在文档中看到ExoPlayer可以在其输入上接收InputsReal。您阅读了吗?我想,有必要查看源代码以了解它。此外,我的要求是支持API>=14,但ExoPlayer只支持API>=16。您必须在应用程序中使用方法
ParcelFileDescriptor openFi输入内容提供者。)le(Uri,字符串模式)
。在这种方法中,您必须使用
ParceFileDescriptor
InputStream
实现@Commonware方案,并使用Uri
content://your.content.provider.name/...
这将识别您的输入流。VLC将抓住这一意图,并要求Android提供具有此类输入流的应用程序tStream(即您的应用程序-通过您的
内容提供商
)。在它之后,VLC将开始输出您提供的InputStream。我没有示例应用程序,它作为
InputStream is = drive.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute().getContent();