自从从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);
答案 0 :(得分:4)
我有一个从IceCast Stream启动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库,那么您只需复制所需的文件并将其直接包含在项目中。
答案 1 :(得分:0)
这取决于几个因素(如流格式),但简短的答案是否定的。大多数浏览器都不公开这个。但是有一种带外元数据方法。
如果您从中获取此流的Icecast服务器运行的是2.4.1或更高版本,那么您可以从其JSON API though查询元数据。基本上通过查询http://icecast.example.org/status.json
或者您只想要一个特定流的信息:http://icecast.example.org/status.json?mount=/stream.ogg
这可以用于旧版本的Icecast,但是API输出需要由托管网页/播放器的网络服务器或CORS ACAO支持进行缓存。
答案 2 :(得分: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
}
}
答案 3 :(得分:0)
解析Shoutcast Metadata Protocol由两部分组成:
curl -v -H“Icy-MetaData:1”http://ice1.somafm.com/defcon-128-mp3
第一部分可以在没有基于ExoPlayer 2.6.1的OkHttp的情况下完成(在Kotlin中):
// 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,而是用于我的称为Alpha+ Player的Android Soma FM流媒体广播应用程序。
答案 4 :(得分:0)
冰冷的元数据支持现已在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")
}
}
}
}
})
}