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

API 27中getScaledFrameAtTime返回Bitmap尺寸异常的原因及解决

问题分析与解决办法

我之前在处理视频帧提取的时候也碰到过类似的问题,咱们来一步步拆解原因和解决思路:

可能的异常原因

  • 宽高比自动适配机制:getScaledFrameAtTime方法会严格保持原视频的宽高比进行缩放。如果你的原视频是9:16的竖屏比例,当你指定目标高度为224时,宽度会自动计算为224 * (9/16) = 126,这完全符合方法的设计逻辑,并不是bug。
  • 线程安全问题:MediaMetadataRetriever本身不是线程安全的,如果你多个线程共用同一个实例调用setDataSource或getScaledFrameAtTime,会导致内部状态混乱,可能返回错误尺寸的Bitmap,甚至抛出异常。
  • API 27版本兼容性:在Android 8.1(API 27)中,该方法对某些特殊编码格式的视频(比如部分H.265编码或非常规分辨率的视频)处理可能存在兼容性问题,导致缩放逻辑异常。

预防与处理方案

针对宽高比问题的处理

如果你需要严格得到224x224的Bitmap,可以按以下步骤操作:

  1. 先获取原视频的宽高信息:
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    retriever.setDataSource(uri);
    int videoWidth = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
    int videoHeight = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
    
  2. 计算符合原比例的缩放尺寸,再裁剪或填充到224x224:
    • 方式一:裁剪出中心区域的224x224
      Bitmap scaledBitmap = retriever.getScaledFrameAtTime(timeStamp * 1000, MediaMetadataRetriever.OPTION_CLOSEST, 224, 224);
      if (scaledBitmap != null) {
          int xOffset = (scaledBitmap.getWidth() - 224) / 2;
          int yOffset = (scaledBitmap.getHeight() - 224) / 2;
          Bitmap targetBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, yOffset, 224, 224);
          scaledBitmap.recycle(); // 及时回收避免内存泄漏
      }
      
    • 方式二:将缩放后的Bitmap填充到224x224的画布(补黑边或透明边)
      Bitmap scaledBitmap = retriever.getScaledFrameAtTime(timeStamp * 1000, MediaMetadataRetriever.OPTION_CLOSEST, 224, 224);
      if (scaledBitmap != null) {
          Bitmap targetBitmap = Bitmap.createBitmap(224, 224, Bitmap.Config.ARGB_8888);
          Canvas canvas = new Canvas(targetBitmap);
          canvas.drawColor(Color.BLACK); // 填充背景色
          float scale = Math.max((float)224/scaledBitmap.getWidth(), (float)224/scaledBitmap.getHeight());
          float dx = (224 - scaledBitmap.getWidth()*scale)/2;
          float dy = (224 - scaledBitmap.getHeight()*scale)/2;
          canvas.drawBitmap(scaledBitmap, dx, dy, new Paint());
          scaledBitmap.recycle();
      }
      

解决线程安全问题

每个线程/Runnable都创建独立的MediaMetadataRetriever实例,用完后记得调用release()释放资源:

new Thread(() -> {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    try {
        retriever.setDataSource(uri);
        Bitmap bitmap = retriever.getScaledFrameAtTime(timeStamp * 1000, MediaMetadataRetriever.OPTION_CLOSEST, 224, 224);
        // 处理Bitmap
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        retriever.release();
    }
}).start();

兼容性问题处理

  • 尝试升级目标API版本到28及以上,后续版本对MediaMetadataRetriever的实现做了优化;
  • 如果必须兼容API 27,可以尝试使用getFrameAtTime获取原始尺寸的Bitmap,再手动缩放裁剪到224x224,避免依赖系统的缩放逻辑;
  • 检查视频编码格式,对于非常规编码的视频,可以先转码为H.264等兼容性更好的格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:17:54