Android端如何实现视频流分辨率实时无缝切换?
解决ExoPlayer强制切换指定分辨率/码率的问题
嘿,我来帮你搞定这个无缝切换视频分辨率的需求!你现在用的AdaptiveTrackSelection.Factory是让ExoPlayer自动根据带宽选轨道,但要手动指定分辨率的话,关键在于用DefaultTrackSelector的轨道覆盖(TrackSelectionOverride)功能,而且完全能做到无缝切换不中断播放。
第一步:把TrackSelector改成成员变量
首先,你需要把DefaultTrackSelector改成类的成员变量,这样后续切换时能方便调用它的方法:
// 定义成成员变量,不要只在初始化时用局部变量 private DefaultTrackSelector trackSelector; // 初始化ExoPlayer的时候 private void initPlayer(Context context) { BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); // 把trackSelector传给ExoPlayer SimpleExoPlayer player = new SimpleExoPlayer.Builder(context) .setTrackSelector(trackSelector) .build(); // 后续的player.prepare()等逻辑... }
第二步:实现手动切换分辨率的方法
接下来写一个方法,根据目标分辨率(比如720p、144p)找到对应的轨道,然后强制切换。核心是创建TrackSelectionOverride并更新轨道选择参数:
/** * 切换到指定高度的分辨率轨道 * @param targetHeight 目标分辨率高度,比如720(720p)、144(144p) */ private void switchToTargetResolution(int targetHeight) { // 先确保player已经准备好,否则无法获取轨道信息 if (trackSelector.getCurrentMappedTrackInfo() == null) { return; } // 获取当前的轨道选择参数 DefaultTrackSelector.Parameters currentParams = trackSelector.getParameters(); // 获取所有视频轨道组 TrackGroupArray videoTrackGroups = trackSelector.getCurrentMappedTrackInfo() .getTrackGroups(C.TRACK_TYPE_VIDEO); TrackSelectionOverride targetOverride = null; // 遍历所有视频轨道组,找到匹配目标分辨率的轨道 for (int groupIndex = 0; groupIndex < videoTrackGroups.length; groupIndex++) { TrackGroup trackGroup = videoTrackGroups.get(groupIndex); for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { Format trackFormat = trackGroup.getFormat(trackIndex); // 匹配目标高度(你也可以根据bitrate码率来匹配,看你的流数据结构) if (trackFormat.height == targetHeight) { // 创建轨道覆盖,强制选择这个轨道 targetOverride = new TrackSelectionOverride( new TrackGroupArray(trackGroup), new int[]{trackIndex}); break; } } if (targetOverride != null) { break; } } if (targetOverride != null) { // 更新参数,应用轨道覆盖 DefaultTrackSelector.Parameters newParams = currentParams .buildUpon() .setOverrideForType(targetOverride) .build(); trackSelector.setParameters(newParams); } else { // 如果没找到目标分辨率,恢复自动自适应选择 DefaultTrackSelector.Parameters newParams = currentParams .buildUpon() .clearOverridesOfType(C.TRACK_TYPE_VIDEO) .build(); trackSelector.setParameters(newParams); } }
第三步:绑定UI事件
把你的分辨率选择按钮(比如Spinner、菜单选项)的点击事件绑定到这个方法就行:
// 比如点击720p按钮时 btn720p.setOnClickListener(v -> switchToTargetResolution(720)); // 点击144p按钮时 btn144p.setOnClickListener(v -> switchToTargetResolution(144)); // 恢复自动选择时 btnAuto.setOnClickListener(v -> { if (trackSelector.getCurrentMappedTrackInfo() != null) { DefaultTrackSelector.Parameters newParams = trackSelector.getParameters() .buildUpon() .clearOverridesOfType(C.TRACK_TYPE_VIDEO) .build(); trackSelector.setParameters(newParams); } });
关键注意点
- 必须在
player.prepare()之后调用切换方法,否则getCurrentMappedTrackInfo()会返回null,拿不到轨道信息。 - 无缝切换的前提是你的流媒体是DASH/HLS这种多码率格式,服务器支持切换轨道。
- 除了用
height匹配分辨率,你也可以根据bitrate(码率)来选择,比如某些流的分辨率标识不清晰,用码率更准确:if (trackFormat.bitrate == targetBitrate)。
这样操作后,ExoPlayer会在后台加载目标轨道的内容,切换时完全不会中断当前播放,完美实现无缝切换的需求!
内容的提问来源于stack exchange,提问作者imi
相关产品推荐
相关产品推荐

