You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编程获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:12:45