2

自从从 Mediaplayer 切换到简单的实现 Exoplayer 后,我注意到加载时间有了很大改善,但我想知道在流式传输音频时是否有任何内置功能,例如元数据更改侦听器?

我已经使用一个简单的示例实现了 Exoplayer,如下所示:

    Uri uri = Uri.parse(url);
    DefaultSampleSource sampleSource =
            new DefaultSampleSource(new FrameworkSampleExtractor(context, uri, null), 2);
    TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
    mExoPlayerInstance.prepare(audioRenderer);
    mExoPlayerInstance.setPlayWhenReady(true);
4

5 回答 5

6

冰冷的元数据支持现在在 exoplayer 2.10 版中:

ExoPlayerFactory.newSimpleInstance(this).apply {
    setAudioAttributes(
      AudioAttributes.Builder()
        .setContentType(C.CONTENT_TYPE_MUSIC)
        .setUsage(C.USAGE_MEDIA)
        .build(), true
    )
    addMetadataOutput(object : MetadataOutput {
      override fun onMetadata(metadata: Metadata) {
        for (n in 0 until metadata.length()) {
          when (val md = metadata[n]) {
            is com.google.android.exoplayer2.metadata.icy.IcyInfo -> {
              Log.d(TAG, "Title: ${md.title} URL: ${md.url}")
            }
            else -> {
              Log.d(TAG, "Some other sort of metadata: $md")
            }
          }
        }
      }
    })
  }
于 2019-05-27T23:31:45.430 回答
4

我有一个从 IceCast 流启动 ExoPlayer 的 AsyncTask:

OkHttpClient okHttpClient = new OkHttpClient();

UriDataSource uriDataSource = new OkHttpDataSource(okHttpClient, userAgent, null, null, CacheControl.FORCE_NETWORK);
((OkHttpDataSource) uriDataSource).setRequestProperty("Icy-MetaData", "1");
((OkHttpDataSource) uriDataSource).setPlayerCallback(mPlayerCallback);

DataSource dataSource = new DefaultUriDataSource(context, null, uriDataSource);

ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator,
                    BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE);


MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
MediaCodecSelector.DEFAULT, null, true, null, null,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
mPlayerCallback.playerStarted();
exoPlayer.prepare(audioRenderer);

OkHttpDataSource 是使用 OkHttpClient 实现 HttpDataSource 的类。它创建 InputStream 作为请求的响应。我从 AACDecoder 库https://github.com/vbartacek/aacdecoder-android/blob/master/decoder/src/com/spoledge/aacdecoder/IcyInputStream.java中包含了这个类,并根据响应将 InputStream 替换为 IcyInputStream:

(在 OkHttpDataSource 的 open() 中)

try {
  response = okHttpClient.newCall(request).execute();
  responseByteStream = response.body().byteStream();

  String icyMetaIntString = response.header("icy-metaint");
  int icyMetaInt = -1;
  if (icyMetaIntString != null) {
    try {
      icyMetaInt = Integer.parseInt(icyMetaIntString);
      if (icyMetaInt > 0)
        responseByteStream = new IcyInputStream(responseByteStream, icyMetaInt, playerCallback);
    } catch (Exception e) {
      Log.e(TAG, "The icy-metaint '" + icyMetaInt + "' cannot be parsed: '" + e);
    }
  }

} catch (IOException e) {
  throw new HttpDataSourceException("Unable to connect to " + dataSpec.uri.toString(), e,
      dataSpec);
}

现在 IcyInputStream 可以捕获 medatada 并调用回调对象(此处为 playerCallback)。PlayerCallback 也来自 AACDecoder 库:https ://github.com/vbartacek/aacdecoder-android/blob/b58c519a341340a251f3291895c76ff63aef5b94/decoder/src/com/spoledge/aacdecoder/PlayerCallback.java

这样您就不会制作任何重复的流并且它是单数的。如果您不想在项目中包含 AACDecoder 库,那么您可以复制所需的文件并将它们直接包含在您的项目中。

于 2016-10-02T05:39:38.660 回答
1

解析Shoutcast 元数据协议由两部分组成:

  1. 通过发送 HTTP-Header Icy-Metadata:1告诉服务器您的客户端支持元数据,例如:

curl -v -H "Icy-MetaData:1" http://ice1.somafm.com/defcon-128-mp3

  1. 从流中解析元数据

第一部分可以在没有基于 ExoPlayer 2.6.1(在 Kotlin 中)的 OkHttp 的情况下完成:

// Custom HTTP data source factory with IceCast metadata HTTP header set
val defaultHttpDataSourceFactory = DefaultHttpDataSourceFactory(userAgent, null)
defaultHttpDataSourceFactory.setDefaultRequestProperty("Icy-MetaData", "1")

// Produces DataSource instances through which media data is loaded.
val dataSourceFactory = DefaultDataSourceFactory(
    applicationContext, null, defaultHttpDataSourceFactory)

第二部分涉及更多,发布所有代码有点多。您可能想看看我创建的 ExoPlayer2 扩展:

github.com/saschpe/android-exoplayer2-ext-icy

它不依赖于 OkHttp,在我的 Soma FM 流媒体广播应用程序中用于 Android,称为Alpha+ Player

于 2018-01-21T21:54:19.600 回答
0

这将取决于几个因素(如流格式),但简短的回答是否定的。大多数浏览器不公开这一点。不过,有一种带外元数据方法。

如果您从中获取此流的 Icecast 服务器正在运行 2.4.1 或更高版本,那么您可以从其JSON API查询元数据。基本上通过查询http://icecast.example.org/status.json或者如果您只想要一个特定流的信息:http://icecast.example.org/status.json?mount=/stream.ogg

这可以在旧版本的 Icecast 中使用,但是 API 输出需要由托管网页/播放器的网络服务器缓存或使用 CORS ACAO 支持。

于 2015-05-08T14:43:21.630 回答
0

发布以显示对我有用的实现。只是一个带有启动和停止方法的单例,以及一些更新 UI 的意图。

private void startStation(Station station){
if(station!=null) {
  ExoPlayerSingleton.getInstance();
  ExoPlayerSingleton.playStation(station, getApplicationContext());
 }
}


public class ExoPlayerSingleton {

private static ExoPlayer mExoPlayerInstance;
private static MediaCodecAudioTrackRenderer audioRenderer;
private static final int BUFFER_SIZE = 10 * 1024 * 1024;
private static MediaPlayer mediaPlayer;
public static synchronized ExoPlayer getInstance() {


    if (mExoPlayerInstance == null) {
        mExoPlayerInstance = ExoPlayer.Factory.newInstance(1);
    }

    return mExoPlayerInstance;
}

 public static synchronized ExoPlayer getCurrentInstance() {
    return mExoPlayerInstance;
}

public static void  stopExoForStation(Context context){

    if(mExoPlayerInstance!=null) {
        try {

            mExoPlayerInstance.stop();
            mExoPlayerInstance.release();
            mExoPlayerInstance = null;
            Intent intent = new Intent();
            intent.setAction("com.zzz.now_playing_receiver");
            context.sendBroadcast(intent);
        } catch (Exception e) {
            Log.e("Exoplayer Error", e.toString());
        }

    }
}


public static boolean isPlaying(){

    if(mExoPlayerInstance!=null &&(mExoPlayerInstance.getPlaybackState()==       ExoPlayer.STATE_READY )){
        return true;
    }else{
        return false;
    }
}

public static boolean isBuffering(){

    if(mExoPlayerInstance!=null &&(mExoPlayerInstance.getPlaybackState()== ExoPlayer.STATE_BUFFERING)){
        return true;
    }else{
        return false;
    }
}

public static boolean isPreparing(){

    if(mExoPlayerInstance!=null &&( mExoPlayerInstance.getPlaybackState()== ExoPlayer.STATE_PREPARING)){
        return true;
    }else{
        return false;
    }
}

public static void playStation(Station station,final Context context){

    getInstance();
    url = station.getLow_Stream();

    if(url!=null) {
        Uri uri = Uri.parse(url);
        String userAgent = Util.getUserAgent(context, "SomeRadio");
        DataSource audioDataSource = new DefaultUriDataSource(context,userAgent);
        Mp3Extractor extractor = new Mp3Extractor();
                ExtractorSampleSource sampleSource = new ExtractorSampleSource(
                uri, audioDataSource,BUFFER_SIZE, extractor );

        audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);


        mExoPlayerInstance.addListener(new ExoPlayer.Listener() {
            @Override
            public void onPlayerStateChanged(boolean b, int i) {

                if (i == ExoPlayer.STATE_BUFFERING) {


                } else if (i == ExoPlayer.STATE_IDLE) {

                } else if (i == ExoPlayer.STATE_ENDED) {


                } else if (i == ExoPlayer.STATE_READY) {
                    Intent intent = new Intent();
                    intent.setAction("com.zzz.pause_play_update");
                    context.sendBroadcast(intent);

                    Intent progress_intent = new Intent();
                    progress_intent.putExtra("show_dialog", false);
                    progress_intent.setAction("com.zzz.load_progess");
                    context.sendBroadcast(progress_intent);
                }


            }

            @Override
            public void onPlayWhenReadyCommitted() {

                 }

            @Override
            public void onPlayerError(ExoPlaybackException e) {
                String excep =  e.toString();
                Log.e("ExoPlayer Error",excep);

            }
        });
        mExoPlayerInstance.prepare(audioRenderer);
        mExoPlayerInstance.setPlayWhenReady(true);

    }else{
        //send intent to raise no connection dialog
    }


}
于 2016-01-20T19:44:02.837 回答