如何从网络URL获取视频宽高并适配Feed列表的SurfaceView显示
从网络URL获取视频宽高并适配SurfaceView到Feed列表
嘿,这个需求我做Feed流视频模块的时候刚好碰到过,给你分享两个靠谱的实现思路,顺便说说怎么适配SurfaceView的显示参数:
一、用MediaMetadataRetriever快速解析视频元数据
这是Android系统提供的原生工具,不用引入额外依赖,适合快速获取视频宽高:
实现步骤
- 先添加网络权限(如果是HTTP URL,还要在Manifest里配置
android:usesCleartextTraffic="true") - 异步解析视频元数据(绝对不能在主线程做,否则会触发ANR)
- 提取宽高并判断视频方向
代码示例
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获取宽高更高效,还能避免重复解析视频资源:
实现步骤
- 初始化ExoPlayer实例
- 设置MediaItem并监听播放器的加载完成事件
- 从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




