Android camera2 jpeg 帧率
Android camera2 jpeg framerate
我正在尝试在具有相机 2 (Galaxy S7) 完整功能的 android 设备上保存具有固定帧率(最好高达 30)的图像序列,但我无法 a) 获得稳定的帧率, b) 甚至达到 20fps(使用 jpeg 编码)。我已经包含了 .
的建议
根据
,JPEG 的最短帧持续时间为 33.33 毫秒(对于低于 1920x1080 的分辨率)
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputMinFrameDuration(ImageFormat.JPEG, size);
并且每种尺寸的停滞持续时间均为 0 毫秒(与 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);
//
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/ANYVALUE,不影响以下次数。
我使用以下命令启动捕获会话:
session.setRepeatingRequest(_capReq.build(), captureListener, mBackgroundHandler);
注意:如果我请求 RepeatingRequest 或 RepeatingBurst 没有区别..
在预览中(仅附加纹理表面),一切都是 30fps。
但是,一旦我附加了图像 reader(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 毫秒 - 66 毫秒 ...
我不明白为什么这些花费的时间是为 jpeg 广告的流配置映射的两倍或三倍?曝光时间远低于 33 毫秒的帧持续时间。是否发生了一些我不知道的其他内部处理?
我对 YUV_420_888 格式进行了同样的尝试,这导致了 33 毫秒的恒定时差。我这里的问题是手机没有足够的带宽来足够快地存储图像(我尝试了中描述的方法)。如果您自己知道任何足够快地压缩或编码这些图像的方法,请告诉我。
编辑:来自 getOutputStallDuration 的文档:"In other words, using a repeating YUV request would result in a steady frame rate (let's say it's 30 FPS). If a single JPEG request is submitted periodically, the frame rate will stay at 30 FPS (as long as we wait for the previous JPEG to return each time). If we try to submit a repeating YUV + JPEG request, then the frame rate will drop from 30 FPS."这是否意味着我需要定期请求单个 capture()?
Edit2:来自 https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html:“给定上述模型,应用程序的必要信息是通过 android.scaler.streamConfigurationMap 字段使用 getOutputMinFrameDuration(int, Size) 提供的。这些用于确定给定流配置可能的最大帧速率/最小帧持续时间。
具体来说,应用程序可以使用以下规则来确定它可以从相机设备请求的最短帧持续时间:
将当前配置的 input/output 组流称为 S。
通过使用 getOutputMinFrameDuration(int, Size)(及其各自的 size/format)在 android.scaler.streamConfigurationMap 中查找,找到 S 中每个流的最小帧持续时间。将这组帧持续时间称为 F。
对于任何给定的请求 R,R 允许的最小帧持续时间是 F 中所有值中的最大值。让 R 中使用的流称为 S_r。
如果 S_r 中的 none 流有一个停顿时间(在 getOutputStallDuration(int, Size) 中使用其各自的 size/format 列出),则 F 中的帧持续时间确定稳态帧速率如果应用程序将 R 用作重复请求,它将获得该应用程序。"
JPEG 输出并不是获取帧的最快方式。通过使用 OpenGL 将帧直接绘制到 Quad 上,您可以更快地完成此操作。
对于突发捕获,更快的解决方案是将图像捕获到 RAM 而不对它们进行编码,然后异步编码和保存它们。
On this website 您通常可以找到很多与 android 多媒体相关的优秀代码。
This specific program 使用 OpenGL 从 MPEG 视频中获取像素数据。使用相机而不是视频作为输入并不难。您基本上可以使用上述程序中 CodecOutputSurface
class 中使用的纹理作为捕获请求的输出纹理。
我找到的一个可能的解决方案包括使用和转储 YUV,而不将其编码为 JPEG,并结合每秒能够节省高达 95Mb 的微型 Sd-card。 (我误以为YUV图会比较大,所以对于完全支持camera2-pipeline的手机,写入速度应该是限制因素。
通过此设置,我能够实现以下稳定速率:
- 1920x1080,15fps(约 4Mb * 15 == 60Mb/秒)
- 960x720,30fps。 (大约 1.5Mb * 30 == 45Mb/秒)
然后我使用 python 脚本将图像从 YUV 离线编码为 PNG。
我正在尝试在具有相机 2 (Galaxy S7) 完整功能的 android 设备上保存具有固定帧率(最好高达 30)的图像序列,但我无法 a) 获得稳定的帧率, b) 甚至达到 20fps(使用 jpeg 编码)。我已经包含了
根据
,JPEG 的最短帧持续时间为 33.33 毫秒(对于低于 1920x1080 的分辨率)characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputMinFrameDuration(ImageFormat.JPEG, size);
并且每种尺寸的停滞持续时间均为 0 毫秒(与 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);
//
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/ANYVALUE,不影响以下次数。
我使用以下命令启动捕获会话:
session.setRepeatingRequest(_capReq.build(), captureListener, mBackgroundHandler);
注意:如果我请求 RepeatingRequest 或 RepeatingBurst 没有区别..
在预览中(仅附加纹理表面),一切都是 30fps。 但是,一旦我附加了图像 reader(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 毫秒 - 66 毫秒 ...
我不明白为什么这些花费的时间是为 jpeg 广告的流配置映射的两倍或三倍?曝光时间远低于 33 毫秒的帧持续时间。是否发生了一些我不知道的其他内部处理?
我对 YUV_420_888 格式进行了同样的尝试,这导致了 33 毫秒的恒定时差。我这里的问题是手机没有足够的带宽来足够快地存储图像(我尝试了
编辑:来自 getOutputStallDuration 的文档:"In other words, using a repeating YUV request would result in a steady frame rate (let's say it's 30 FPS). If a single JPEG request is submitted periodically, the frame rate will stay at 30 FPS (as long as we wait for the previous JPEG to return each time). If we try to submit a repeating YUV + JPEG request, then the frame rate will drop from 30 FPS."这是否意味着我需要定期请求单个 capture()?
Edit2:来自 https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html:“给定上述模型,应用程序的必要信息是通过 android.scaler.streamConfigurationMap 字段使用 getOutputMinFrameDuration(int, Size) 提供的。这些用于确定给定流配置可能的最大帧速率/最小帧持续时间。
具体来说,应用程序可以使用以下规则来确定它可以从相机设备请求的最短帧持续时间:
将当前配置的 input/output 组流称为 S。 通过使用 getOutputMinFrameDuration(int, Size)(及其各自的 size/format)在 android.scaler.streamConfigurationMap 中查找,找到 S 中每个流的最小帧持续时间。将这组帧持续时间称为 F。 对于任何给定的请求 R,R 允许的最小帧持续时间是 F 中所有值中的最大值。让 R 中使用的流称为 S_r。 如果 S_r 中的 none 流有一个停顿时间(在 getOutputStallDuration(int, Size) 中使用其各自的 size/format 列出),则 F 中的帧持续时间确定稳态帧速率如果应用程序将 R 用作重复请求,它将获得该应用程序。"
JPEG 输出并不是获取帧的最快方式。通过使用 OpenGL 将帧直接绘制到 Quad 上,您可以更快地完成此操作。
对于突发捕获,更快的解决方案是将图像捕获到 RAM 而不对它们进行编码,然后异步编码和保存它们。
On this website 您通常可以找到很多与 android 多媒体相关的优秀代码。
This specific program 使用 OpenGL 从 MPEG 视频中获取像素数据。使用相机而不是视频作为输入并不难。您基本上可以使用上述程序中 CodecOutputSurface
class 中使用的纹理作为捕获请求的输出纹理。
我找到的一个可能的解决方案包括使用和转储 YUV,而不将其编码为 JPEG,并结合每秒能够节省高达 95Mb 的微型 Sd-card。 (我误以为YUV图会比较大,所以对于完全支持camera2-pipeline的手机,写入速度应该是限制因素。
通过此设置,我能够实现以下稳定速率:
- 1920x1080,15fps(约 4Mb * 15 == 60Mb/秒)
- 960x720,30fps。 (大约 1.5Mb * 30 == 45Mb/秒)
然后我使用 python 脚本将图像从 YUV 离线编码为 PNG。