如何将自定义 android.media.MediaDataSource 与 android.media.MediaPlayer 一起使用?

How do I use custom android.media.MediaDataSource along with android.media.MediaPlayer?

我知道 Android 的 MediaPlayer 很棒。它允许我们播放本地文件和媒体流。而且它非常容易使用(仅作为示例):

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource("http://streaming.shoutcast.com/80sPlanet"); // this steam broadcasts audio/mpeg
mediaPlayer.prepareAsync();
mediaPlayer.start();

可以通过使用不同的参数集调用重载 setDataSource() 来设置不同类型的数据源。 这个函数有一个有趣的原型:

void setDataSource(MediaDataSource dataSource) 

看起来可以用您自己的实现完全覆盖 DataSource。它确实有效:

import android.media.MediaDataSource;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.HttpURLConnection;

public class UrlMediaDataSource extends MediaDataSource {
    URL url;
    HttpURLConnection connection;
    BufferedInputStream stream;

    public UrlMediaDataSource(URL url) throws IOException {
        this.url = url;
        connection = (HttpURLConnection) url.openConnection();
    }

    @Override
    public long getSize() {
        return 0;
    }

    @Override
    public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
        if (stream == null)
            stream = new BufferedInputStream(connection.getInputStream());
        return stream.read(buffer, offset, size);
    }

    @Override
    public void close() throws IOException {
        stream.close();
        stream = null;
        connection.disconnect();
        connection = null;
    }
}

在主代码中:

UrlMediaDataSource dataSource = new UrlMediaDataSource(new URL("http://streaming.shoutcast.com/80sPlanet"));
mediaPlayer.setDataSource(dataSource);

是的,这很好用。但是如果我尝试 audio/aacp 广播流(例如:“http://111.223.51.8:8005” - 它是 "COOLfahrenheit 93" 广播),播放器不会播放。 Logcat 追踪:

06-07 23:26:01.680 1352-1147/? E/GenericSource: Failed to init from data source!
06-07 23:26:01.681 1352-1093/? D/NuPlayerDriver: notifyListener_l(0xf3e051e0), (100, 1, -2147483648)
06-07 23:26:01.735 1352-2013/? D/NuPlayerDriver: reset(0xf3e051e0)
06-07 23:26:01.735 1352-2013/? D/NuPlayerDriver: notifyListener_l(0xf3e051e0), (8, 0, 0)
06-07 23:26:01.736 1352-1093/? D/NuPlayerDriver: notifyResetComplete(0xf3e051e0)

不过,URL 在没有使用自定义 MediaDataSource 时工作正常(音乐播放):

mediaPlayer.setDataSource("http://111.223.51.8:8005");

有人知道正确的管理方法吗? 只是不要建议我直接使用 URL - 我需要自定义 MediaDataSource 来访问流的原始数据。

The main point is that the MediaPlayer does playback audio/mpeg (both ways - through URL and through custom MediaDataSource), but audio/aacp streams could be played back only via URL as DataSource.

那么,让我们了解幕后发生的事情。

当您将 URL 作为数据源传递时,将执行 this 检查:


    if ("file".equals(scheme)) {
        path = uri.getPath();
    } else if (scheme != null) {
        // handle non-file sources
        nativeSetDataSource(
            MediaHTTPService.createHttpServiceBinderIfNecessary(path),
            path,
            keys,
            values);
        return;
    }

MediaPlayer uses MediaHTTPService class, which is responsible for providing data from http, https and widevine protocols. MediaHTTPService internally uses MediaHTTPConnection, which takes all the heavy lifting for working with that type of streams. Unfortunately, these APIs are not public (yet), but you can see how connection establishing is done in MediaHTTPConnection sources (particularly seekTo method). So, the custom data source that you provide to MediaPlayer should depict approximately the logics, that MediaHTTPConnection class 实施。