EmulatedFakeCamera2: Add recording support for 320x240, NV21. DO NOT MERGE
- Support 320x240 in addition to 640x480 - Support NV21 (monochrome only right now) - Base simulated time on system time, since stagefright cares about timestamp base - Use emulator magic gralloc format to enable gralloc to pick format based on destination. Bug: 6243944 Change-Id: I3ea56bca726c69b51e03233ce86d4881401a3ffd
This commit is contained in:
@@ -39,6 +39,7 @@ LOCAL_C_INCLUDES += external/jpeg \
|
|||||||
external/skia/include/core/ \
|
external/skia/include/core/ \
|
||||||
frameworks/native/include/media/hardware \
|
frameworks/native/include/media/hardware \
|
||||||
frameworks/base/core/jni/android/graphics \
|
frameworks/base/core/jni/android/graphics \
|
||||||
|
$(LOCAL_PATH)/../../opengl/system/OpenglSystemCommon \
|
||||||
$(call include-path-for, camera)
|
$(call include-path-for, camera)
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "EmulatedCameraFactory.h"
|
#include "EmulatedCameraFactory.h"
|
||||||
#include <ui/Rect.h>
|
#include <ui/Rect.h>
|
||||||
#include <ui/GraphicBufferMapper.h>
|
#include <ui/GraphicBufferMapper.h>
|
||||||
|
#include "gralloc_cb.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -47,8 +48,8 @@ const uint64_t EmulatedFakeCamera2::kAvailableRawMinDurations[1] = {
|
|||||||
Sensor::kFrameDurationRange[0]
|
Sensor::kFrameDurationRange[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32_t EmulatedFakeCamera2::kAvailableProcessedSizes[2] = {
|
const uint32_t EmulatedFakeCamera2::kAvailableProcessedSizes[4] = {
|
||||||
640, 480
|
640, 480, 320, 240
|
||||||
// Sensor::kResolution[0], Sensor::kResolution[1]
|
// Sensor::kResolution[0], Sensor::kResolution[1]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -251,8 +252,9 @@ int EmulatedFakeCamera2::allocateStream(
|
|||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Emulator's opaque format is RGBA
|
// Translate to emulator's magic format.
|
||||||
format = HAL_PIXEL_FORMAT_RGBA_8888;
|
// Note: It is assumed that this is a processed format (not raw or JPEG).
|
||||||
|
format = GRALLOC_EMULATOR_PIXEL_FORMAT_AUTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t *availableSizes;
|
const uint32_t *availableSizes;
|
||||||
@@ -266,6 +268,7 @@ int EmulatedFakeCamera2::allocateStream(
|
|||||||
availableSizes = kAvailableJpegSizes;
|
availableSizes = kAvailableJpegSizes;
|
||||||
availableSizeCount = sizeof(kAvailableJpegSizes)/sizeof(uint32_t);
|
availableSizeCount = sizeof(kAvailableJpegSizes)/sizeof(uint32_t);
|
||||||
break;
|
break;
|
||||||
|
case GRALLOC_EMULATOR_PIXEL_FORMAT_AUTO:
|
||||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||||
case HAL_PIXEL_FORMAT_YV12:
|
case HAL_PIXEL_FORMAT_YV12:
|
||||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||||
@@ -326,7 +329,7 @@ int EmulatedFakeCamera2::allocateStream(
|
|||||||
|
|
||||||
*stream_id = mNextStreamId;
|
*stream_id = mNextStreamId;
|
||||||
if (format_actual) *format_actual = format;
|
if (format_actual) *format_actual = format;
|
||||||
*usage = GRALLOC_USAGE_SW_WRITE_OFTEN;
|
*usage = GRALLOC_USAGE_HW_CAMERA_WRITE;
|
||||||
*max_buffers = 4;
|
*max_buffers = 4;
|
||||||
|
|
||||||
ALOGV("Stream allocated: %d, %d x %d, 0x%x. U: %x, B: %d",
|
ALOGV("Stream allocated: %d, %d x %d, 0x%x. U: %x, B: %d",
|
||||||
@@ -340,9 +343,42 @@ int EmulatedFakeCamera2::registerStreamBuffers(
|
|||||||
uint32_t stream_id,
|
uint32_t stream_id,
|
||||||
int num_buffers,
|
int num_buffers,
|
||||||
buffer_handle_t *buffers) {
|
buffer_handle_t *buffers) {
|
||||||
// Emulator doesn't need to register these with V4L2, etc.
|
Mutex::Autolock l(mMutex);
|
||||||
|
|
||||||
ALOGV("%s: Stream %d registering %d buffers", __FUNCTION__,
|
ALOGV("%s: Stream %d registering %d buffers", __FUNCTION__,
|
||||||
stream_id, num_buffers);
|
stream_id, num_buffers);
|
||||||
|
// Need to find out what the final concrete pixel format for our stream is
|
||||||
|
// Assumes that all buffers have the same format.
|
||||||
|
if (num_buffers < 1) {
|
||||||
|
ALOGE("%s: Stream %d only has %d buffers!",
|
||||||
|
__FUNCTION__, stream_id, num_buffers);
|
||||||
|
return BAD_VALUE;
|
||||||
|
}
|
||||||
|
const cb_handle_t *streamBuffer =
|
||||||
|
reinterpret_cast<const cb_handle_t*>(buffers[0]);
|
||||||
|
|
||||||
|
int finalFormat = streamBuffer->format;
|
||||||
|
|
||||||
|
if (finalFormat == GRALLOC_EMULATOR_PIXEL_FORMAT_AUTO) {
|
||||||
|
ALOGE("%s: Stream %d: Bad final pixel format "
|
||||||
|
"GRALLOC_EMULATOR_PIXEL_FORMAT_AUTO; "
|
||||||
|
"concrete pixel format required!", __FUNCTION__, stream_id);
|
||||||
|
return BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t streamIndex = mStreams.indexOfKey(stream_id);
|
||||||
|
if (streamIndex < 0) {
|
||||||
|
ALOGE("%s: Unknown stream id %d!", __FUNCTION__, stream_id);
|
||||||
|
return BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream &stream = mStreams.editValueAt(streamIndex);
|
||||||
|
|
||||||
|
ALOGV("%s: Stream %d format set to %x, previously %x",
|
||||||
|
__FUNCTION__, stream_id, finalFormat, stream.format);
|
||||||
|
|
||||||
|
stream.format = finalFormat;
|
||||||
|
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,7 +629,14 @@ bool EmulatedFakeCamera2::ConfigureThread::threadLoop() {
|
|||||||
mNextNeedsJpeg = false;
|
mNextNeedsJpeg = false;
|
||||||
ALOGV("Setting up buffers for capture");
|
ALOGV("Setting up buffers for capture");
|
||||||
for (size_t i = 0; i < streams.count; i++) {
|
for (size_t i = 0; i < streams.count; i++) {
|
||||||
const Stream &s = mParent->getStreamInfo(streams.data.u8[i]);
|
int streamId = streams.data.u8[i];
|
||||||
|
const Stream &s = mParent->getStreamInfo(streamId);
|
||||||
|
if (s.format == GRALLOC_EMULATOR_PIXEL_FORMAT_AUTO) {
|
||||||
|
ALOGE("%s: Stream %d does not have a concrete pixel format, but "
|
||||||
|
"is included in a request!", __FUNCTION__, streamId);
|
||||||
|
mParent->signalError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
StreamBuffer b;
|
StreamBuffer b;
|
||||||
b.streamId = streams.data.u8[i];
|
b.streamId = streams.data.u8[i];
|
||||||
b.width = s.width;
|
b.width = s.width;
|
||||||
@@ -708,7 +751,7 @@ bool EmulatedFakeCamera2::ConfigureThread::threadLoop() {
|
|||||||
const Rect rect(s.width, s.height);
|
const Rect rect(s.width, s.height);
|
||||||
|
|
||||||
res = GraphicBufferMapper::get().lock(*(b.buffer),
|
res = GraphicBufferMapper::get().lock(*(b.buffer),
|
||||||
GRALLOC_USAGE_SW_WRITE_OFTEN,
|
GRALLOC_USAGE_HW_CAMERA_WRITE,
|
||||||
rect, (void**)&(b.img) );
|
rect, (void**)&(b.img) );
|
||||||
|
|
||||||
if (res != NO_ERROR) {
|
if (res != NO_ERROR) {
|
||||||
@@ -1220,7 +1263,7 @@ status_t EmulatedFakeCamera2::constructStaticInfo(
|
|||||||
sizeof(exposureCompensationRange)/sizeof(int32_t));
|
sizeof(exposureCompensationRange)/sizeof(int32_t));
|
||||||
|
|
||||||
static const int32_t availableTargetFpsRanges[] = {
|
static const int32_t availableTargetFpsRanges[] = {
|
||||||
5, 30
|
5, 30, 15, 30
|
||||||
};
|
};
|
||||||
ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
|
ADD_OR_SIZE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
|
||||||
availableTargetFpsRanges,
|
availableTargetFpsRanges,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ typedef Vector<StreamBuffer> Buffers;
|
|||||||
struct Stream {
|
struct Stream {
|
||||||
const camera2_stream_ops_t *ops;
|
const camera2_stream_ops_t *ops;
|
||||||
uint32_t width, height;
|
uint32_t width, height;
|
||||||
uint32_t format;
|
int32_t format;
|
||||||
uint32_t stride;
|
uint32_t stride;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,9 @@ bool Sensor::threadLoop() {
|
|||||||
nsecs_t captureTime = 0;
|
nsecs_t captureTime = 0;
|
||||||
|
|
||||||
nsecs_t startRealTime = systemTime();
|
nsecs_t startRealTime = systemTime();
|
||||||
nsecs_t simulatedTime = startRealTime - mStartupTime;
|
// Stagefright cares about system time for timestamps, so base simulated
|
||||||
|
// time on that.
|
||||||
|
nsecs_t simulatedTime = startRealTime;
|
||||||
nsecs_t frameEndRealTime = startRealTime + frameDuration;
|
nsecs_t frameEndRealTime = startRealTime + frameDuration;
|
||||||
nsecs_t frameReadoutEndRealTime = startRealTime +
|
nsecs_t frameReadoutEndRealTime = startRealTime +
|
||||||
kRowReadoutTime * kResolution[1];
|
kRowReadoutTime * kResolution[1];
|
||||||
@@ -312,8 +314,10 @@ bool Sensor::threadLoop() {
|
|||||||
captureRGB(bAux.img, gain, b.stride);
|
captureRGB(bAux.img, gain, b.stride);
|
||||||
mNextCapturedBuffers->push_back(bAux);
|
mNextCapturedBuffers->push_back(bAux);
|
||||||
break;
|
break;
|
||||||
case HAL_PIXEL_FORMAT_YV12:
|
|
||||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||||
|
captureNV21(b.img, gain, b.stride);
|
||||||
|
break;
|
||||||
|
case HAL_PIXEL_FORMAT_YV12:
|
||||||
// TODO:
|
// TODO:
|
||||||
ALOGE("%s: Format %x is TODO", __FUNCTION__, b.format);
|
ALOGE("%s: Format %x is TODO", __FUNCTION__, b.format);
|
||||||
break;
|
break;
|
||||||
@@ -390,11 +394,12 @@ void Sensor::captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride) {
|
|||||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||||
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
||||||
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
||||||
mScene.setReadoutPixel(0,0);
|
uint32_t inc = (stride == 320) ? 2 : 1;
|
||||||
|
|
||||||
for (unsigned int y = 0; y < kResolution[1]; y++ ) {
|
for (unsigned int y = 0, outY = 0; y < kResolution[1]; y+=inc, outY++ ) {
|
||||||
uint8_t *px = img + y * stride * 4;
|
uint8_t *px = img + outY * stride * 4;
|
||||||
for (unsigned int x = 0; x < kResolution[0]; x++) {
|
mScene.setReadoutPixel(0, y);
|
||||||
|
for (unsigned int x = 0; x < kResolution[0]; x+=inc) {
|
||||||
uint32_t rCount, gCount, bCount;
|
uint32_t rCount, gCount, bCount;
|
||||||
// TODO: Perfect demosaicing is a cheat
|
// TODO: Perfect demosaicing is a cheat
|
||||||
const uint32_t *pixel = mScene.getPixelElectrons();
|
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||||
@@ -406,6 +411,7 @@ void Sensor::captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride) {
|
|||||||
*px++ = gCount < 255*64 ? gCount / 64 : 255;
|
*px++ = gCount < 255*64 ? gCount / 64 : 255;
|
||||||
*px++ = bCount < 255*64 ? bCount / 64 : 255;
|
*px++ = bCount < 255*64 ? bCount / 64 : 255;
|
||||||
*px++ = 255;
|
*px++ = 255;
|
||||||
|
if (inc == 2) mScene.getPixelElectrons();
|
||||||
}
|
}
|
||||||
// TODO: Handle this better
|
// TODO: Handle this better
|
||||||
//simulatedTime += kRowReadoutTime;
|
//simulatedTime += kRowReadoutTime;
|
||||||
@@ -417,11 +423,12 @@ void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) {
|
|||||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||||
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
||||||
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
||||||
mScene.setReadoutPixel(0,0);
|
uint32_t inc = (stride == 320) ? 2 : 1;
|
||||||
|
|
||||||
for (unsigned int y = 0; y < kResolution[1]; y++ ) {
|
for (unsigned int y = 0, outY = 0; y < kResolution[1]; y += inc, outY++ ) {
|
||||||
uint8_t *px = img + y * stride * 3;
|
mScene.setReadoutPixel(0, y);
|
||||||
for (unsigned int x = 0; x < kResolution[0]; x++) {
|
uint8_t *px = img + outY * stride * 3;
|
||||||
|
for (unsigned int x = 0; x < kResolution[0]; x += inc) {
|
||||||
uint32_t rCount, gCount, bCount;
|
uint32_t rCount, gCount, bCount;
|
||||||
// TODO: Perfect demosaicing is a cheat
|
// TODO: Perfect demosaicing is a cheat
|
||||||
const uint32_t *pixel = mScene.getPixelElectrons();
|
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||||
@@ -432,6 +439,7 @@ void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) {
|
|||||||
*px++ = rCount < 255*64 ? rCount / 64 : 255;
|
*px++ = rCount < 255*64 ? rCount / 64 : 255;
|
||||||
*px++ = gCount < 255*64 ? gCount / 64 : 255;
|
*px++ = gCount < 255*64 ? gCount / 64 : 255;
|
||||||
*px++ = bCount < 255*64 ? bCount / 64 : 255;
|
*px++ = bCount < 255*64 ? bCount / 64 : 255;
|
||||||
|
if (inc == 2) mScene.getPixelElectrons();
|
||||||
}
|
}
|
||||||
// TODO: Handle this better
|
// TODO: Handle this better
|
||||||
//simulatedTime += kRowReadoutTime;
|
//simulatedTime += kRowReadoutTime;
|
||||||
@@ -439,4 +447,39 @@ void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) {
|
|||||||
ALOGVV("RGB sensor image captured");
|
ALOGVV("RGB sensor image captured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sensor::captureNV21(uint8_t *img, uint32_t gain, uint32_t stride) {
|
||||||
|
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||||
|
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
||||||
|
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
||||||
|
|
||||||
|
// TODO: Make full-color
|
||||||
|
uint32_t inc = (stride == 320) ? 2 : 1;
|
||||||
|
uint32_t outH = kResolution[1] / inc;
|
||||||
|
for (unsigned int y = 0, outY = 0, outUV = outH;
|
||||||
|
y < kResolution[1]; y+=inc, outY++, outUV ) {
|
||||||
|
uint8_t *pxY = img + outY * stride;
|
||||||
|
mScene.setReadoutPixel(0,y);
|
||||||
|
for (unsigned int x = 0; x < kResolution[0]; x+=inc) {
|
||||||
|
uint32_t rCount, gCount, bCount;
|
||||||
|
// TODO: Perfect demosaicing is a cheat
|
||||||
|
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||||
|
rCount = pixel[Scene::R] * scale64x;
|
||||||
|
gCount = pixel[Scene::Gr] * scale64x;
|
||||||
|
bCount = pixel[Scene::B] * scale64x;
|
||||||
|
uint32_t avg = (rCount + gCount + bCount) / 3;
|
||||||
|
*pxY++ = avg < 255*64 ? avg / 64 : 255;
|
||||||
|
if (inc == 2) mScene.getPixelElectrons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned int y = 0, outY = outH; y < kResolution[1]/2; y+=inc, outY++) {
|
||||||
|
uint8_t *px = img + outY * stride;
|
||||||
|
for (unsigned int x = 0; x < kResolution[0]; x+=inc) {
|
||||||
|
// UV to neutral
|
||||||
|
*px++ = 128;
|
||||||
|
*px++ = 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ALOGVV("NV21 sensor image captured");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ class Sensor: private Thread, public virtual RefBase {
|
|||||||
void captureRaw(uint8_t *img, uint32_t gain, uint32_t stride);
|
void captureRaw(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||||
void captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride);
|
void captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||||
void captureRGB(uint8_t *img, uint32_t gain, uint32_t stride);
|
void captureRGB(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||||
|
void captureNV21(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user