要更多地了解为什么您的ExoPlayer没有像您预期的那样适应网络条件,您可以从在曲目 Select 器中包含带宽计量器开始.这有助于ExoPlayer做出适应性决策.
AdaptiveTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
虽然DefaultLoadControl
在大多数情况下应该足够了,但您可能希望设置缓冲区大小、缓冲区持续时间等的自定义值,以微调自适应行为.
您需要使用曲目 Select 中包含的带宽测量器更新initialisePlayer
和playAudio
方法:
private void initialisePlayer(){
TrackSelector trackSelector = new DefaultTrackSelector(this);
LoadControl loadControl = new DefaultLoadControl();
exoPlayer = new ExoPlayer.Builder(this)
.setTrackSelector(trackSelector)
.setLoadControl(loadControl)
.build();
exoPlayer.addListener(listener);
exoPlayer.addAnalyticsListener(new EventLogger(trackSelector));
exoPlayer.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ true);
exoPlayer.setHandleAudioBecomingNoisy(true);
}
private void playAudio(){
DefaultBandwidthMeter bandwidthMeter = DefaultBandwidthMeter.getSingletonInstance(this);
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this)
.setTransferListener(bandwidthMeter);
HlsMediaSource audioSource = new HlsMediaSource.Factory(dataSourceFactory)
.setLoadErrorHandlingPolicy(new CustomPolicy())
.createMediaSource(MediaItem.fromUri("my-hls-master-url"));
exoPlayer.setMediaSource(audioSource);
exoPlayer.prepare();
exoPlayer.setPlayWhenReady(true);
}
The DefaultBandwidthMeter
should provide you with bandwidth estimates. You could log these to see if they change when you switch networks. If the bandwidth estimates do not change, that might be the issue.
Even though you can't inject the BandwidthMeter
into the AdaptiveTrackSelection.Factory
, you can still add an event listener to it to log changes in estimated bandwidth.
bandwidthMeter.addEventListener(new Handler(Looper.getMainLooper()), new BandwidthMeter.EventListener() {
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
Log.d("BANDWIDTH_SAMPLE", "bitrate = " + bitrate);
}
});
I have added the bandwidthMeter.addEventListener
and have seen that bitrate logged is always returning a higher number like - 3200000 or 3099619 or 2929804 and even when I change to 2G network the bitrate is something between 117245 and 165935.
But exoplayer is still not switching the audio variants.
您在2G网络上的带宽估计值显示为117245和165935,因此...ExoPlayer认为带宽足以播放您的主播放列表中可用的最高比特率,根据您发布的内容,该列表的最大比特率为320000.
测试您是否可以手动设置曲目,以确认问题是否与ExoPlayer的自适应机制或其他什么有关.
MappingTrackSelector.MappedTrackInfo mappedTrackInfo = ((DefaultTrackSelector) trackSelector).getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
int rendererIndex = 0; // For audio. Change it as per your needs
int trackIndexToBeUsed = 1; // Use 0, 1, 2... based on available tracks
DefaultTrackSelector.SelectionOverride override = new DefaultTrackSelector.SelectionOverride(trackIndexToBeUsed, 0);
DefaultTrackSelector.ParametersBuilder parametersBuilder = trackSelector.buildUponParameters();
parametersBuilder.setSelectionOverride(rendererIndex, mappedTrackInfo.getTrackGroups(rendererIndex), override);
trackSelector.setParameters(parametersBuilder);
}
将上面的代码片段添加到准备了exoPlayer.prepare();
的播放器之后,手动设置曲目.
我用了exoplayer.prepare();
后面的代码MappingTrackSelector.MappedTrackInfo .... trackSelector.setParameters(parametersBuilder);
,但还是找不到任何区别.
ExoPlayer仍然在初始加载中使用第一个音频变量,然后立即移动到最高变量音频,并且无论带宽如何,永远不会更改音频变量.
我可以看到,在2G网络中,比特率最初被带宽计多次记录为-990000,然后缓慢变化,然后在同一网络中下降到更低的比特率,如39000到189000.
有时,曲目 Select 的问题可能与您调用某些方法的确切时间有关.确保您拨打的是手动曲目 Select 代码after,exoPlayer.prepare()
呼叫已完成.当播放器状态更改为Player.STATE_READY
时,您可能希望将曲目 Select 代码放在Player.Listener
回调中.
exoPlayer.addListener(new Player.Listener() {
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_READY) {
// Manual track selection code here
}
}
});
虽然ExoPlayer本身应该处理这一点,但出于调试目的,您可以显式设置一些自适应参数,例如开始或继续播放时需要缓冲的媒体的最短持续时间.ExoPlayer允许您将这样的阈值设置为DefaultLoadControl
.
现在我可以手动 Select 曲目,但它现在只 Select 音频曲目的较低变体,即使在5G网络中也是如此.
如果您的手动曲目 Select 成功,但仅导致播放器即使在5G这样的高速网络上也使用较低比特率的曲目,这表明曲目 Select 覆盖优先于ExoPlayer自己的自适应机制.当您设置明确的曲目 Select 覆盖时,ExoPlayer将坚持该 Select ,不会根据网络条件进行调整.
因此,删除用于自适应的手动覆盖,并try 有条件的手动覆盖:实现基于特定条件手动切换轨道的逻辑,例如带宽是否低于特定阈值.通过这种方式,您可以混合使用手动和自适应行为.
bandwidthMeter.addEventListener(new Handler(Looper.getMainLooper()), new BandwidthMeter.EventListener() {
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
if (bitrate > SOME_THRESHOLD) {
// Revert to adaptive track selection
DefaultTrackSelector.ParametersBuilder parametersBuilder = trackSelector.buildUponParameters();
parametersBuilder.clearSelectionOverrides(/* rendererIndex= */ 0);
trackSelector.setParameters(parametersBuilder);
}
}
});
再说一次,这是为了测试:虽然可以混合手动和自适应曲目 Select ,但这样做会使玩家的行为更难预测和调试.
每当使用我的手动音轨 Select 的比特率较低时, Select 最低比特率,当比特率高于我的阈值时,自适应行为只 Select 最高比特率,但不 Select 介于两者之间的比特率.
问题是自适应曲目 Select 仍然不能正常工作,我有时看到比特率为102773336,网速为32兆比特.我认为onBandwidthSample
%的比特率也是错误的.
另外,如果自适应音轨 Select 不起作用,那么我认为唯一的方法就是手动完成所有的音轨 Select ,基于BandwidthMeter.EventListener
中的从onBandwidthSample
开始的比特率方法.但该方法返回的错误比特率也是这个问题的另一个问题.
作为最后的手段,如果ExoPlayer的自适应算法不能满足您的需求,并且您也无法获得可靠的带宽估计,您可能确实必须接管对曲目 Select 的完全控制.
在这种情况下,您可以编写将带宽估计映射到特定磁道的逻辑.但是,请确保对带宽估计应用某种形式的平滑或平均,以避免快速切换轨道,这可能会对用户造成干扰.
bandwidthMeter.addEventListener(new Handler(Looper.getMainLooper()), new BandwidthMeter.EventListener() {
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
// Your own logic to select a track based on the current bitrate
int trackIndexToBeUsed = mapBitrateToTrack(bitrate); // Implement this function based on your requirements
DefaultTrackSelector.SelectionOverride override = new DefaultTrackSelector.SelectionOverride(trackIndexToBeUsed, 0);
DefaultTrackSelector.ParametersBuilder parametersBuilder = trackSelector.buildUponParameters();
parametersBuilder.setSelectionOverride(0, trackGroups, override);
trackSelector.setParameters(parametersBuilder);
}
});
作为另一种 Select ,你可以try 使用Android的网络功能:Android中的ConnectivityManager
类可以提供有关当前网络连接类型和速度的一般信息.此信息不如带宽估计精确,但仍可用于在高、中、低质量流之间进行 Select .
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkCapabilities nc = cm.getNetworkCapabilities(cm.getActiveNetwork());
int downSpeed = nc.getLinkDownstreamBandwidthKbps();
int upSpeed = nc.getLinkUpstreamBandwidthKbps();
或者使用第三方网络测量库,如square/okhttp
.
You could also implement your own bandwidth estimation logic.
One approach is to download a small file at the beginning of the application and measure the download speed. Be cautious with this approach as it introduces latency and could vary based on server speeds, concurrent network operations, or other factors.