Android camera2 jpeg帧率

时间:2017-01-07 14:50:19

标签: android image jpeg yuv camera2

我试图在Android设备上保存具有固定帧率(最好高达30)的图像序列,具有相同功能的camera2(Galaxy S7),但我无法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。白平衡可以设置为OFF / AUTO / ANY VALUE,不会影响下面的时间。

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

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 ms - 66 ms - 66 ms - 99 ms - 66 ms - 66 ms ...

我不明白为什么这些为流配置图为jpeg做广告的时间增加了两倍或三倍?曝光时间远低于33ms的帧持续时间。是否还有一些我不知道的其他内部处理?

我为YUV_420_888格式尝试了相同的操作,导致33ms的恒定时差。我在这里遇到的问题是手机没有足够的带宽来快速存储图像(我尝试了How to save a YUV_420_888 image?中描述的方法)。如果您知道有任何方法可以自己快速压缩或编码这些图像,请告诉我。

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

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

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

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

2 个答案:

答案 0 :(得分:1)

JPEG输出不是获取帧的最快方法。通过使用OpenGL将帧直接绘制到Quad上,您可以更快地完成此任务。

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

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

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

答案 1 :(得分:1)

我找到的一个可能的解决方案包括使用和转储YUV而不将其编码为JPEG,结合微型Sd卡,每秒可以节省高达95Mb。 (我误以为YUV图像会更大,因此对于完全支持camera2-pipeline的手机,写入速度应该是限制因素。

通过这种设置,我能够达到以下稳定的速率:

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

然后我使用python脚本将图像从YUV离线编码为PNG。