为什么 Exoplayer 在带宽变化时不切换 HLS 轨道
Why Exoplayer does not switch HLS tracks when bandwidth change
我有这个简单的 .m3u8 文件
#EXTM3U
#EXT-X-VERSION:5
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="English stereo",LANGUAGE="en"
#EXT-X-STREAM-INF:BANDWIDTH=1728000,CODECS="avc1.42c00d,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa_video_360_800000.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=628000,CODECS="avc1.42c00d,mp4a.40.2",RESOLUTION=320x180,AUDIO="audio"
https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa_video_180_250000.m3u8
使用 simpleExoplayer 我像这样加载这个流:
// 1. Create a default TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
// 2. Create the player
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(context, trackSelector);
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "yourApplicationName"), bandwidthMeter);
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(mp4VideoUri);
// Prepare the player with the source.
player.prepare(videoSource);
player.setPlayWhenReady(true);
但即使在高 WIFI 互联网上,它也总是 select 分辨率为 320x180 的第二轨道,并且永远不会切换到分辨率为 640x360 的更高轨道。
如果我用 maxInt 替换 DEFAULT_MAX_INITIAL_BITRATE 那么它会 select 正确的第一个音轨 (640x360) 但是如果在播放过程中我切断了 wifi 让手机只使用3G连接低,那么它永远不会切换到第二首曲目(320x180)并且每次播放都会卡住
也是检查 MediaCodecUtil.getDecoderInfo("video/avc", false).adaptive
并且它是 return TRUE
我做错了什么? hls 在 simpleExoPlayer 中是否正常工作?
DefaultBandwidthMeter
是计量网络容量的组件。 DefaultBandwidthMeter
实现了 BandwidthMeter
和 TransferListener
接口。
您应该只使用 DefaultBandwidthMeter 的一个实例并将其传递给两者,AdaptiveTrackSelection.Factory
(作为 BandwidthMeter)和 DefaultDataSourceFactory
(作为 TransferListener)。这样在数据源中完成的计量在轨道选择器中生效。
使用上面的代码,您传递给 AdaptiveTrackSelection.Factory 的实例不知道计量的是什么,根据您的描述,这显然不起作用。
另请注意,如果您创建一个新的 DefaultBandwidthMeter
实例,那么到目前为止所做的计量将会丢失。因此,您可能希望将 DefaultBandwidthMeter
作为静态变量等,以保留早期播放的计量。
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(DEFAULT_BANDWIDTH_METER);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "yourApplicationName"), DEFAULT_BANDWIDTH_METER);
在我的例子中,带宽变化时它没有切换轨道,所以我将 DefaultHttpDataSourceFactory
替换为 OkHttpDataSourceFactory
并添加了依赖项:
这里是代码片段:
implementation 'com.google.android.exoplayer:extension-okhttp:2.12.1'
val httpCallFactory = OkHttpClient.Builder()
.writeTimeout(httpWriteTimeout, TimeUnit.MILLISECONDS)
.readTimeout(httpReadTimeout, TimeUnit.MILLISECONDS)
.connectTimeout(httpConnectTimeout, TimeUnit.MILLISECONDS)
.build()
val dataSourceFactory = OkHttpDataSourceFactory(
httpCallFactory,
userAgent,
transferListener
)
/* val dataSourceFactory = DefaultHttpDataSourceFactory(
userAgent,
transferListener,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true
)*/
val mediaSource: MediaSource = buildMediaSource(Uri.parse(url), dataSourceFactory)
exoPlayer.setMediaSource(mediaSource, resetPosition)
exoPlayer.prepare()
我有这个简单的 .m3u8 文件
#EXTM3U
#EXT-X-VERSION:5
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="English stereo",LANGUAGE="en"
#EXT-X-STREAM-INF:BANDWIDTH=1728000,CODECS="avc1.42c00d,mp4a.40.2",RESOLUTION=640x360,AUDIO="audio"
https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa_video_360_800000.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=628000,CODECS="avc1.42c00d,mp4a.40.2",RESOLUTION=320x180,AUDIO="audio"
https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa_video_180_250000.m3u8
使用 simpleExoplayer 我像这样加载这个流:
// 1. Create a default TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
// 2. Create the player
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(context, trackSelector);
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "yourApplicationName"), bandwidthMeter);
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(mp4VideoUri);
// Prepare the player with the source.
player.prepare(videoSource);
player.setPlayWhenReady(true);
但即使在高 WIFI 互联网上,它也总是 select 分辨率为 320x180 的第二轨道,并且永远不会切换到分辨率为 640x360 的更高轨道。
如果我用 maxInt 替换 DEFAULT_MAX_INITIAL_BITRATE 那么它会 select 正确的第一个音轨 (640x360) 但是如果在播放过程中我切断了 wifi 让手机只使用3G连接低,那么它永远不会切换到第二首曲目(320x180)并且每次播放都会卡住
也是检查 MediaCodecUtil.getDecoderInfo("video/avc", false).adaptive
并且它是 return TRUE
我做错了什么? hls 在 simpleExoPlayer 中是否正常工作?
DefaultBandwidthMeter
是计量网络容量的组件。 DefaultBandwidthMeter
实现了 BandwidthMeter
和 TransferListener
接口。
您应该只使用 DefaultBandwidthMeter 的一个实例并将其传递给两者,AdaptiveTrackSelection.Factory
(作为 BandwidthMeter)和 DefaultDataSourceFactory
(作为 TransferListener)。这样在数据源中完成的计量在轨道选择器中生效。
使用上面的代码,您传递给 AdaptiveTrackSelection.Factory 的实例不知道计量的是什么,根据您的描述,这显然不起作用。
另请注意,如果您创建一个新的 DefaultBandwidthMeter
实例,那么到目前为止所做的计量将会丢失。因此,您可能希望将 DefaultBandwidthMeter
作为静态变量等,以保留早期播放的计量。
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(DEFAULT_BANDWIDTH_METER);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "yourApplicationName"), DEFAULT_BANDWIDTH_METER);
在我的例子中,带宽变化时它没有切换轨道,所以我将 DefaultHttpDataSourceFactory
替换为 OkHttpDataSourceFactory
并添加了依赖项:
这里是代码片段:
implementation 'com.google.android.exoplayer:extension-okhttp:2.12.1'
val httpCallFactory = OkHttpClient.Builder()
.writeTimeout(httpWriteTimeout, TimeUnit.MILLISECONDS)
.readTimeout(httpReadTimeout, TimeUnit.MILLISECONDS)
.connectTimeout(httpConnectTimeout, TimeUnit.MILLISECONDS)
.build()
val dataSourceFactory = OkHttpDataSourceFactory(
httpCallFactory,
userAgent,
transferListener
)
/* val dataSourceFactory = DefaultHttpDataSourceFactory(
userAgent,
transferListener,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true
)*/
val mediaSource: MediaSource = buildMediaSource(Uri.parse(url), dataSourceFactory)
exoPlayer.setMediaSource(mediaSource, resetPosition)
exoPlayer.prepare()