代码之家  ›  专栏  ›  技术社区  ›  TobiasWeis

Android camera2 jpeg帧率

  •  7
  • TobiasWeis  · 技术社区  · 7 年前

    我正在尝试在具有camera2(Galaxy S7)全功能的android设备上以固定帧速率(最好高达30)保存图像序列,但我无法a)获得稳定的帧速率,b)甚至达到20fps(使用jpeg编码)。我已经包括了来自 Android camera2 capture burst is too slow .

    JPEG的最小帧持续时间为33.33毫秒(分辨率低于1920x1080),根据

    characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputMinFrameDuration(ImageFormat.JPEG, size);
    

    每种尺寸的失速持续时间为0ms(类似于YUV_420_888)。

    我的捕获生成器如下所示:

    captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
    captureBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, _exp_time);
    captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
    
    captureBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, _iso_value);
    
    captureBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, _foc_dist);
    captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CONTROL_AF_MODE_OFF);
    
    captureBuilder.set(CaptureRequest.CONTROL_AWB_MODE, _wb_value);
    
    // https://stackoverflow.com/questions/29265126/android-camera2-capture-burst-is-too-slow
    captureBuilder.set(CaptureRequest.EDGE_MODE,CaptureRequest.EDGE_MODE_OFF); 
    captureBuilder.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE_OFF);
    captureBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
    captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
    
    // Orientation
    int rotation = getWindowManager().getDefaultDisplay().getRotation();           
    captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,ORIENTATIONS.get(rotation));
    

    焦距设置为0.0(inf),iso设置为100,曝光时间为5ms。白平衡可以设置为关闭/自动/任何值,它不会影响以下时间。

    我使用以下命令启动捕获会话:

    session.setRepeatingRequest(_capReq.build(), captureListener, mBackgroundHandler);
    

    注意:如果我请求RepeatingRequest或RepeatingBurst,则没有区别。。

    在预览(仅附加纹理表面)中,所有内容均为30fps。 然而,只要我附加了一个图像读取器(在HandlerThread上运行的侦听器),我就会像下面这样实例化(不保存,只测量帧之间的时间):

    reader = ImageReader.newInstance(_img_width, _img_height, ImageFormat.JPEG, 2);
    reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
    

    带有时间测量代码:

    ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader myreader) {
            Image image = null;
    
            image = myreader.acquireNextImage();
            if (image == null) {
                return;
            }
            long curr = image.getTimestamp();
            Log.d("curr- _last_ts", "" + ((curr - last_ts) / 1000000) + " ms");
            last_ts = curr;
            image.close();
        }
    }
    

    我周期性地重复时间差,如下所示:

    99毫秒-66毫秒-66毫秒-99毫秒-66 ms-66毫秒。。。

    我不明白为什么流配置映射为jpeg广告所花费的时间是原来的两倍或三倍?曝光时间远低于33ms的帧持续时间。是否有其他我不知道的内部处理发生?

    我对YUV_420_888格式也尝试了同样的方法,这导致了33ms的恒定时间差。我在这里遇到的问题是,手机缺乏足够快的带宽来存储图像(我尝试了中描述的方法) How to save a YUV_420_888 image? ). 如果你知道任何方法来压缩或编码这些图像足够快自己,请让我知道。

    编辑:来自getOutputStallDuration的文档:“换句话说,使用重复的YUV请求将导致稳定的帧速率(假设为30 FPS)。如果定期提交单个JPEG请求,帧速率将保持在30 FPS(只要我们每次等待上一个JPEG返回)如果我们尝试提交重复的YUV+JPEG请求,则帧速率将从30fps下降。“这是否意味着我需要定期请求单个捕获()?

    编辑2:来自 https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html :“给定上述模型,应用程序的必要信息通过android.scaler.streamConfigurationMap字段使用getOutputMinFrameDuration(int,Size)提供。这些信息用于确定给定流配置可能的最大帧速率/最小帧速率。

    具体而言,应用程序可以使用以下规则来确定其可以从摄像机设备请求的最小帧持续时间:

    将当前配置的输入/输出流集合称为S。 通过在android.scaler中查找每个流的最小帧持续时间,以S为单位。使用getOutputMinFrameDuration(int,Size)的streamConfigurationMap(具有各自的大小/格式)。让这组帧持续时间称为F。 对于任何给定的请求R,R允许的最小帧持续时间是F中所有值中的最大值。将R中使用的流称为S_R。 如果S_r中的所有流都没有暂停时间(使用各自的大小/格式在getOutputStallDuration(int,Size)中列出),则F中的帧持续时间决定了应用程序在使用r作为重复请求时将获得的稳态帧速率。”

    2 回复  |  直到 7 年前
        1
  •  2
  •   Emiswelt    7 年前

    JPEG输出不是获取帧的最快方式。通过使用OpenGL将帧直接绘制到四边形上,可以更快地完成此操作。

    对于突发捕获,一个更快的解决方案是将图像捕获到RAM中,而不进行编码,然后进行编码并异步保存。

    On this website 你可以找到很多与android多媒体相关的优秀代码。

    This specific program 使用OpenGL从MPEG视频中提取像素数据。使用摄像机作为输入而不是视频并不困难。基本上可以使用中使用的纹理 CodecOutputSurface 类作为捕获请求的输出纹理。

        2
  •  2
  •   TobiasWeis    7 年前

    我发现的一个可能的解决方案是使用和转储YUV,而不将其编码为JPEG,并结合一个能够每秒保存高达95Mb的微型Sd卡。(我错误地认为YUV图像会更大,因此对于完全支持camera2管道的手机,写入速度应该是限制因素。

    通过此设置,我能够实现以下稳定速率:

    • 1920x1080,15fps(约4Mb*15==60Mb/秒)
    • 960x720,30fps。(约1.5Mb*30==45Mb/秒)

    然后,我使用python脚本将图像从YUV脱机编码为PNG。