You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何从网络URL获取视频宽高并适配Feed列表的SurfaceView显示

从网络URL获取视频宽高并适配SurfaceView到Feed列表

嘿,这个需求我做Feed流视频模块的时候刚好碰到过,给你分享两个靠谱的实现思路,顺便说说怎么适配SurfaceView的显示参数:

一、用MediaMetadataRetriever快速解析视频元数据

这是Android系统提供的原生工具,不用引入额外依赖,适合快速获取视频宽高:

实现步骤

  1. 先添加网络权限(如果是HTTP URL,还要在Manifest里配置android:usesCleartextTraffic="true"
  2. 异步解析视频元数据(绝对不能在主线程做,否则会触发ANR)
  3. 提取宽高并判断视频方向

代码示例

private void getVideoSizeFromUrl(String videoUrl) {
    new AsyncTask<Void, Void, int[]>() {
        @Override
        protected int[] doInBackground(Void... voids) {
            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
            try {
                retriever.setDataSource(videoUrl, new HashMap<>());
                // 获取原始宽高
                String widthStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
                String heightStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
                // 获取视频旋转角度
                String rotationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
                
                int width = Integer.parseInt(widthStr);
                int height = Integer.parseInt(heightStr);
                int rotation = Integer.parseInt(rotationStr);
                
                // 旋转90/270度时,实际显示的宽高需要互换
                if (rotation == 90 || rotation == 270) {
                    return new int[]{height, width};
                }
                return new int[]{width, height};
            } catch (Exception e) {
                e.printStackTrace();
                // 解析失败返回默认值
                return new int[]{0, 0};
            } finally {
                retriever.release();
            }
        }

        @Override
        protected void onPostExecute(int[] size) {
            int videoWidth = size[0];
            int videoHeight = size[1];
            if (videoWidth > 0 && videoHeight > 0) {
                // 拿到有效宽高后,开始适配SurfaceView
                adaptSurfaceView(videoWidth, videoHeight);
            }
        }
    }.execute();
}

二、用ExoPlayer解析(更适合播放场景)

如果你的Feed流已经用ExoPlayer播放视频,直接通过ExoPlayer的API获取宽高更高效,还能避免重复解析视频资源:

实现步骤

  1. 初始化ExoPlayer实例
  2. 设置MediaItem并监听播放器的加载完成事件
  3. 从TrackInfo中提取视频宽高

代码示例

private void getVideoSizeWithExoPlayer(String videoUrl) {
    SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build();
    MediaItem mediaItem = MediaItem.fromUri(videoUrl);
    player.setMediaItem(mediaItem);
    
    player.addListener(new Player.Listener() {
        @Override
        public void onMediaItemTransition(MediaItem mediaItem, int reason) {
            // 视频加载完成后遍历轨道信息,找到视频轨道
            TrackGroupArray trackGroups = player.getCurrentTrackGroups();
            for (int i = 0; i < trackGroups.length; i++) {
                TrackGroup trackGroup = trackGroups.get(i);
                for (int j = 0; j < trackGroup.length; j++) {
                    Format format = trackGroup.getFormat(j);
                    if (format.sampleMimeType.startsWith("video/")) {
                        int videoWidth = format.width;
                        int videoHeight = format.height;
                        int rotation = format.rotationDegrees;
                        
                        // 处理旋转后的宽高互换
                        if (rotation == 90 || rotation == 270) {
                            int temp = videoWidth;
                            videoWidth = videoHeight;
                            videoHeight = temp;
                        }
                        
                        // 适配SurfaceView
                        adaptSurfaceView(videoWidth, videoHeight);
                        break;
                    }
                }
            }
        }
    });
    
    player.prepare();
}

三、适配SurfaceView到Feed列表

拿到视频的实际宽高后,就可以根据Feed列表的布局需求动态调整SurfaceView的尺寸:

核心思路

  • 先计算视频的宽高比:float ratio = (float) videoWidth / videoHeight;
  • 根据Feed列表的item宽度(比如父容器的宽度)计算对应高度;或针对竖屏Feed固定高度后计算宽度
  • 动态设置SurfaceView的LayoutParams

代码示例

private void adaptSurfaceView(int videoWidth, int videoHeight) {
    SurfaceView surfaceView = findViewById(R.id.surface_view);
    ViewGroup.LayoutParams params = surfaceView.getLayoutParams();
    
    // 假设Feed列表的item宽度为屏幕宽度
    int parentWidth = getResources().getDisplayMetrics().widthPixels;
    float videoRatio = (float) videoWidth / videoHeight;
    
    if (videoWidth > videoHeight) {
        // 横屏视频:宽度填充满父容器,高度按比例计算
        params.width = parentWidth;
        params.height = (int) (parentWidth / videoRatio);
    } else {
        // 竖屏视频:固定Feed item高度(比如200dp),宽度按比例适配
        int fixedHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
        params.height = fixedHeight;
        params.width = (int) (fixedHeight * videoRatio);
    }
    
    surfaceView.setLayoutParams(params);
}

注意事项

  • 所有视频元数据解析操作必须异步执行,避免阻塞主线程
  • 要处理异常场景:比如URL无效、视频损坏、格式不支持等,需设置默认宽高
  • 务必考虑视频旋转角度,否则显示会出现拉伸变形
  • 使用ExoPlayer时,记得在页面销毁时释放播放器资源,避免内存泄漏

内容的提问来源于stack exchange,提问作者Harsh Bhavsar

火山引擎 最新活动