Android Kotlin下强制ExoPlayer2使用H264解码RTSP流
解决ExoPlayer 2.18.5强制优先选择H264播放RTSP流的方案
针对RTSP流同时包含H264和H265轨道、且H265因参数缺失导致播放失败的问题,以下是几个可行的解决思路:
1. 自定义RTSP媒体源工厂,在SDP解析阶段过滤H265轨道
ExoPlayer处理RTSP流时会先解析SDP描述,我们可以拦截SDP内容,移除其中的H265媒体轨道信息,让播放器只能识别到H264轨道:
// 自定义RTSPMediaSource.Factory,重写createMediaSource方法 RTSPMediaSource.Factory rtspFactory = new RTSPMediaSource.Factory() { @Override public MediaSource createMediaSource(MediaItem mediaItem) { return new RTSPMediaSource(mediaItem, createDataSourceFactory(), eventListener, looper) { @Override protected SdpParser createSdpParser() { return new DefaultSdpParser() { @Override public Sdp parse(String sdpText) throws IOException { // 修改SDP文本,移除H265相关的媒体描述 String filteredSdp = filterH265FromSdp(sdpText); return super.parse(filteredSdp); } }; } }; } }; // 实现SDP过滤方法 private String filterH265FromSdp(String sdpText) { StringBuilder filtered = new StringBuilder(); String[] lines = sdpText.split("\n"); boolean skipH265Track = false; for (String line : lines) { // 检测是否是H265的媒体轨道(匹配负载类型102或H265标识) if (line.startsWith("m=video") && (line.contains("H265") || line.contains("102"))) { skipH265Track = true; } // 跳过当前H265轨道的所有行,直到遇到下一个媒体轨道定义 if (skipH265Track) { if (line.startsWith("m=") && !line.contains("H265") && !line.contains("102")) { skipH265Track = false; filtered.append(line).append("\n"); } continue; } filtered.append(line).append("\n"); } return filtered.toString(); }
2. 使用自定义TrackSelector强制选择H264轨道
TrackSelector是ExoPlayer负责选择播放轨道的核心组件,通过设置规则可直接限制仅选择H264视频轨道:
// 创建DefaultTrackSelector并添加全局过滤规则 DefaultTrackSelector trackSelector = new DefaultTrackSelector(context); trackSelector.setParameters( trackSelector.buildUponParameters() .setVideoMimeTypeFilter(MimeTypes.VIDEO_H264) // 仅允许H264格式的视频轨道 ); // 初始化播放器时传入该TrackSelector ExoPlayer player = new ExoPlayer.Builder(context) .setTrackSelector(trackSelector) .build();
如果需要更精细的轨道控制,可自定义TrackSelection.Factory:
trackSelector.setTrackSelectionFactory(new TrackSelection.Factory() { @Override public TrackSelection createTrackSelection(TrackSelection.Definition definition) { // 过滤掉所有H265轨道 List<TrackInfo> filteredTracks = new ArrayList<>(); for (TrackInfo track : definition.getTrackInfos()) { if (track.getFormat().sampleMimeType.equals(MimeTypes.VIDEO_H264)) { filteredTracks.add(track); } } if (filteredTracks.isEmpty()) { // 无H264轨道时的 fallback 逻辑,可根据需求调整 return new AdaptiveTrackSelection(definition); } return new AdaptiveTrackSelection(new TrackSelection.Definition( definition.getTrackGroupArray(), filteredTracks.stream().mapToInt(TrackInfo::getTrackIndex).toArray() )); } });
3. 修正自定义MediaCodecSelector的实现
你之前的MediaCodecSelector无效,可能是实现逻辑存在漏洞,确保完全排除H265解码器:
MediaCodecSelector customCodecSelector = new MediaCodecSelector() { @Override public List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder) throws DecoderQueryException { List<MediaCodecInfo> originalList = MediaCodecSelector.DEFAULT.getDecoderInfos(mimeType, requiresSecureDecoder, requiresTunnelingDecoder); // 查询H265解码器时直接返回空列表 if (MimeTypes.VIDEO_H265.equals(mimeType)) { return Collections.emptyList(); } return originalList; } }; // 初始化播放器时设置该CodecSelector ExoPlayer player = new ExoPlayer.Builder(context) .setMediaCodecSelector(customCodecSelector) .build();
建议结合TrackSelector的方法一起使用,避免播放器因H265轨道存在而优先尝试解码。
内容的提问来源于stack exchange,提问作者Lennart Zinn
相关产品推荐
相关产品推荐

