如何编程获取Android ExoPlayer的视频帧率(解决返回-1问题)
ExoPlayer获取直播流视频帧率返回-1的解决方法
问题复现
尝试通过AnalyticsListener监听轨道变化读取帧率,代码如下:
exo_player.addAnalyticsListener(new AnalyticsListener() { @Override public void onTracksChanged(EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { if(trackGroups!=null && !trackGroups.isEmpty()){ for(int i=0;i<trackGroups.length;i++){ for(int j=0;i<trackGroups.get(i).length;j++){ float videFrame = trackGroups.get(i).getFormat(j).frameRate; Log.e("videoFrame",String.valueOf(videFrame)); } } } } });
运行后获取到的帧率值恒为-1,不符合预期。
问题原因
- 原代码遍历了所有轨道组的所有格式,包含音频、字幕等非视频轨道,这类轨道本身无有效帧率字段,默认返回-1
Format.frameRate字段读取的是直播流清单/容器元数据中预先声明的标称帧率,大量动态码率直播流不会在元数据中写入固定帧率值,此时该字段默认返回-1,不代表实际播放帧率- 原代码内层循环条件存在笔误,将
j < trackGroups.get(i).length错写为i < trackGroups.get(i).length,会触发数组索引越界异常
实现方案
根据需求不同,分两种场景实现:
1. 获取当前选中视频轨道的标称帧率
不要遍历全量轨道,直接读取当前播放选中的视频轨道格式,过滤非视频轨道:
exoPlayer.addAnalyticsListener(new AnalyticsListener() { @Override public void onTracksChanged(EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { for (TrackSelection trackSelection : trackSelections.getAll()) { if (trackSelection == null) continue; Format selectedFormat = trackSelection.getSelectedFormat(); // 仅处理视频类轨道 if (MimeTypes.isVideo(selectedFormat.sampleMimeType)) { float declaredFps = selectedFormat.frameRate; Log.e("declaredVideoFrame", String.valueOf(declaredFps)); // *注意:若流元数据未声明帧率,该值仍可能返回-1* } } } });
2. 获取实际播放的实时帧率(适配无元数据的直播流场景)
如果标称帧率返回-1,可通过VideoFrameMetadataListener监听每帧的渲染时间戳,自行计算实时帧率,该方式不依赖流元数据声明,适配所有直播场景:
// 帧率计算临时变量 private long lastFramePresentTimeUs = -1; private float currentPlayFps = 0f; // 给播放器设置帧渲染监听 exoPlayer.setVideoFrameMetadataListener((presentationTimeUs, releaseTimeNs, format, mediaCodec) -> { if (lastFramePresentTimeUs != -1) { long intervalUs = presentationTimeUs - lastFramePresentTimeUs; if (intervalUs > 0) { // 计算瞬时帧率,如需数值更稳定可对最近5-10帧的结果做滑动平均 currentPlayFps = 1_000_000f / intervalUs; Log.e("realTimeVideoFrame", String.format("%.1f", currentPlayFps)); } } lastFramePresentTimeUs = presentationTimeUs; });
提示:如果需要更稳定的帧率输出,可以维护一个容量为5~10的队列存储最近帧的间隔值,取平均后输出,避免单帧解码/渲染波动导致数值跳变。
内容的提问来源于stack exchange,提问作者YeJong Choe
相关产品推荐
相关产品推荐

