DO NOT MERGE - qt-qpr1-dev-plus-aosp-without-vendor@5915889 into stage-aosp-master

Bug: 142003500
Change-Id: I0de886061b2d75f9c37e8edbd4e7e08225d0186b
gugelfrei
Xin Li 5 years ago
commit 6fcb941084

@ -34,6 +34,7 @@ int main(int argc __unused, char** argv __unused)
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
CameraService::instantiate();
ALOGI("ServiceManager: %p done instantiate", sm.get());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}

@ -62,10 +62,8 @@ Return<void> CryptoPlugin::decrypt(
secure, keyId, iv, mode, pattern, subSamples, source, offset, destination,
[&](Status_V1_2 hStatus, uint32_t hBytesWritten, hidl_string hDetailedError) {
status = toStatus_1_0(hStatus);
if (status == Status::OK) {
bytesWritten = hBytesWritten;
detailedError = hDetailedError;
}
bytesWritten = hBytesWritten;
detailedError = hDetailedError;
}
);
@ -109,6 +107,10 @@ Return<void> CryptoPlugin::decrypt_1_2(
"destination decrypt buffer base not set");
return Void();
}
} else {
_hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
"destination type not supported");
return Void();
}
sp<IMemory> sourceBase = mSharedBufferMap[source.bufferId];
@ -126,24 +128,20 @@ Return<void> CryptoPlugin::decrypt_1_2(
(static_cast<void *>(sourceBase->getPointer()));
uint8_t* srcPtr = static_cast<uint8_t *>(base + source.offset + offset);
void* destPtr = NULL;
if (destination.type == BufferType::SHARED_MEMORY) {
const SharedBuffer& destBuffer = destination.nonsecureMemory;
sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId];
if (destBase == nullptr) {
_hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr");
return Void();
}
// destination.type == BufferType::SHARED_MEMORY
const SharedBuffer& destBuffer = destination.nonsecureMemory;
sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId];
if (destBase == nullptr) {
_hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr");
return Void();
}
if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
return Void();
}
destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
} else if (destination.type == BufferType::NATIVE_HANDLE) {
native_handle_t *handle = const_cast<native_handle_t *>(
destination.secureMemory.getNativeHandle());
destPtr = static_cast<void *>(handle);
if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
return Void();
}
destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
// Calculate the output buffer size and determine if any subsamples are
// encrypted.
@ -151,13 +149,24 @@ Return<void> CryptoPlugin::decrypt_1_2(
bool haveEncryptedSubsamples = false;
for (size_t i = 0; i < subSamples.size(); i++) {
const SubSample &subSample = subSamples[i];
destSize += subSample.numBytesOfClearData;
destSize += subSample.numBytesOfEncryptedData;
if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize)) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample clear size overflow");
return Void();
}
if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize)) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample encrypted size overflow");
return Void();
}
if (subSample.numBytesOfEncryptedData > 0) {
haveEncryptedSubsamples = true;
}
}
if (destSize > destBuffer.size) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample sum too large");
return Void();
}
if (mode == Mode::UNENCRYPTED) {
if (haveEncryptedSubsamples) {
_hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,

@ -1,9 +1,5 @@
cc_library {
name: "libstagefright_bufferpool@2.0",
vendor_available: true,
vndk: {
enabled: true,
},
cc_defaults {
name: "libstagefright_bufferpool@2.0-default",
srcs: [
"Accessor.cpp",
"AccessorImpl.cpp",
@ -29,3 +25,23 @@ cc_library {
"android.hardware.media.bufferpool@2.0",
],
}
cc_library {
name: "libstagefright_bufferpool@2.0.1",
defaults: ["libstagefright_bufferpool@2.0-default"],
vendor_available: true,
cflags: [
"-DBUFFERPOOL_CLONE_HANDLES",
],
}
// Deprecated. Do not use. Use libstagefright_bufferpool@2.0.1 instead.
cc_library {
name: "libstagefright_bufferpool@2.0",
defaults: ["libstagefright_bufferpool@2.0-default"],
vendor_available: true,
vndk: {
enabled: true,
},
}

@ -351,7 +351,21 @@ ResultStatus ClientManager::Impl::allocate(
}
client = it->second;
}
#ifdef BUFFERPOOL_CLONE_HANDLES
native_handle_t *origHandle;
ResultStatus res = client->allocate(params, &origHandle, buffer);
if (res != ResultStatus::OK) {
return res;
}
*handle = native_handle_clone(origHandle);
if (handle == NULL) {
buffer->reset();
return ResultStatus::NO_MEMORY;
}
return ResultStatus::OK;
#else
return client->allocate(params, handle, buffer);
#endif
}
ResultStatus ClientManager::Impl::receive(
@ -367,7 +381,22 @@ ResultStatus ClientManager::Impl::receive(
}
client = it->second;
}
#ifdef BUFFERPOOL_CLONE_HANDLES
native_handle_t *origHandle;
ResultStatus res = client->receive(
transactionId, bufferId, timestampUs, &origHandle, buffer);
if (res != ResultStatus::OK) {
return res;
}
*handle = native_handle_clone(origHandle);
if (handle == NULL) {
buffer->reset();
return ResultStatus::NO_MEMORY;
}
return ResultStatus::OK;
#else
return client->receive(transactionId, bufferId, timestampUs, handle, buffer);
#endif
}
ResultStatus ClientManager::Impl::postSend(

@ -104,7 +104,9 @@ struct ClientManager : public IClientManager {
ResultStatus flush(ConnectionId connectionId);
/**
* Allocates a buffer from the specified connection.
* Allocates a buffer from the specified connection. The output parameter
* handle is cloned from the internal handle. So it is safe to use directly,
* and it should be deleted and destroyed after use.
*
* @param connectionId The id of the connection.
* @param params The allocation parameters.
@ -123,7 +125,9 @@ struct ClientManager : public IClientManager {
std::shared_ptr<BufferPoolData> *buffer);
/**
* Receives a buffer for the transaction.
* Receives a buffer for the transaction. The output parameter handle is
* cloned from the internal handle. So it is safe to use directly, and it
* should be deleted and destoyed after use.
*
* @param connectionId The id of the receiving connection.
* @param transactionId The id for the transaction.

@ -1,10 +1,16 @@
cc_library_shared {
name: "libcodec2_soft_av1dec",
name: "libcodec2_soft_av1dec_aom",
defaults: [
"libcodec2_soft-defaults",
"libcodec2_soft_sanitize_all-defaults",
],
// coordinated with frameworks/av/media/codec2/components/gav1/Android.bp
// so only 1 of them has the official c2.android.av1.decoder name
cflags: [
"-DCODECNAME=\"c2.android.av1-aom.decoder\"",
],
srcs: ["C2SoftAomDec.cpp"],
static_libs: ["libaom"],

@ -29,7 +29,8 @@
namespace android {
constexpr char COMPONENT_NAME[] = "c2.android.av1.decoder";
// codecname set and passed in as a compile flag from Android.bp
constexpr char COMPONENT_NAME[] = CODECNAME;
class C2SoftAomDec::IntfImpl : public SimpleInterface<void>::BaseParams {
public:
@ -340,6 +341,7 @@ status_t C2SoftAomDec::initDecoder() {
aom_codec_flags_t flags;
memset(&flags, 0, sizeof(aom_codec_flags_t));
ALOGV("Using libaom AV1 software decoder.");
aom_codec_err_t err;
if ((err = aom_codec_dec_init(mCodecCtx, aom_codec_av1_dx(), &cfg, 0))) {
ALOGE("av1 decoder failed to initialize. (%d)", err);

@ -33,7 +33,8 @@ namespace android {
namespace {
constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
constexpr uint32_t kDefaultOutputDelay = 8;
constexpr uint32_t kMaxOutputDelay = 16;
} // namespace
class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@ -54,7 +55,9 @@ public:
// TODO: Proper support for reorder depth.
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
.withConstValue(new C2PortActualDelayTuning::output(8u))
.withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
.withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
.withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
// TODO: output latency and reordering
@ -196,7 +199,6 @@ public:
0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
.build());
}
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
C2P<C2StreamPictureSizeInfo::output> &me) {
(void)mayBlock;
@ -333,6 +335,7 @@ C2SoftAvcDec::C2SoftAvcDec(
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
mIvColorFormat(IV_YUV_420P),
mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
mHeaderDecoded(false),
@ -882,6 +885,26 @@ void C2SoftAvcDec::process(
work->result = C2_CORRUPTED;
return;
}
if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
mOutputDelay = s_decode_op.i4_reorder_depth;
ALOGV("New Output delay %d ", mOutputDelay);
C2PortActualDelayTuning::output outputDelay(mOutputDelay);
std::vector<std::unique_ptr<C2SettingResult>> failures;
c2_status_t err =
mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
if (err == OK) {
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(outputDelay));
} else {
ALOGE("Cannot set output delay");
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
continue;
}
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;

@ -157,7 +157,7 @@ private:
size_t mNumCores;
IV_COLOR_FORMAT_T mIvColorFormat;
uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mStride;

@ -73,7 +73,7 @@ public:
addParameter(
DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
.withDefault(new C2StreamChannelCountInfo::output(0u, 1))
.withDefault(new C2StreamChannelCountInfo::output(0u, 6))
.withFields({C2F(mChannelCount, value).equalTo(1)})
.withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
.build());

@ -0,0 +1,20 @@
cc_library_shared {
name: "libcodec2_soft_av1dec_gav1",
defaults: [
"libcodec2_soft-defaults",
"libcodec2_soft_sanitize_all-defaults",
],
// coordinated with frameworks/av/media/codec2/components/aom/Android.bp
// so only 1 of them has the official c2.android.av1.decoder name
cflags: [
"-DCODECNAME=\"c2.android.av1.decoder\"",
],
srcs: ["C2SoftGav1Dec.cpp"],
static_libs: ["libgav1"],
include_dirs: [
"external/libgav1/libgav1/",
],
}

@ -0,0 +1,792 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "C2SoftGav1Dec"
#include "C2SoftGav1Dec.h"
#include <C2Debug.h>
#include <C2PlatformSupport.h>
#include <SimpleC2Interface.h>
#include <log/log.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/MediaDefs.h>
namespace android {
// codecname set and passed in as a compile flag from Android.bp
constexpr char COMPONENT_NAME[] = CODECNAME;
class C2SoftGav1Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
public:
explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
: SimpleInterface<void>::BaseParams(
helper, COMPONENT_NAME, C2Component::KIND_DECODER,
C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_AV1) {
noPrivateBuffers(); // TODO: account for our buffers here.
noInputReferences();
noOutputReferences();
noInputLatency();
noTimeStretch();
addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
.withConstValue(new C2ComponentAttributesSetting(
C2Component::ATTRIB_IS_TEMPORAL))
.build());
addParameter(
DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
.withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
.withFields({
C2F(mSize, width).inRange(2, 2048, 2),
C2F(mSize, height).inRange(2, 2048, 2),
})
.withSetter(SizeSetter)
.build());
addParameter(DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
.withDefault(new C2StreamProfileLevelInfo::input(
0u, C2Config::PROFILE_AV1_0, C2Config::LEVEL_AV1_2_1))
.withFields({C2F(mProfileLevel, profile)
.oneOf({C2Config::PROFILE_AV1_0,
C2Config::PROFILE_AV1_1}),
C2F(mProfileLevel, level)
.oneOf({
C2Config::LEVEL_AV1_2,
C2Config::LEVEL_AV1_2_1,
C2Config::LEVEL_AV1_2_2,
C2Config::LEVEL_AV1_3,
C2Config::LEVEL_AV1_3_1,
C2Config::LEVEL_AV1_3_2,
})})
.withSetter(ProfileLevelSetter, mSize)
.build());
mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0);
addParameter(
DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO)
.withDefault(mHdr10PlusInfoInput)
.withFields({
C2F(mHdr10PlusInfoInput, m.value).any(),
})
.withSetter(Hdr10PlusInfoInputSetter)
.build());
mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0);
addParameter(
DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO)
.withDefault(mHdr10PlusInfoOutput)
.withFields({
C2F(mHdr10PlusInfoOutput, m.value).any(),
})
.withSetter(Hdr10PlusInfoOutputSetter)
.build());
addParameter(
DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
.withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
.withFields({
C2F(mSize, width).inRange(2, 2048, 2),
C2F(mSize, height).inRange(2, 2048, 2),
})
.withSetter(MaxPictureSizeSetter, mSize)
.build());
addParameter(DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
.withDefault(new C2StreamMaxBufferSizeInfo::input(
0u, 320 * 240 * 3 / 4))
.withFields({
C2F(mMaxInputSize, value).any(),
})
.calculatedAs(MaxInputSizeSetter, mMaxSize)
.build());
C2ChromaOffsetStruct locations[1] = {C2ChromaOffsetStruct::ITU_YUV_420_0()};
std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */,
C2Color::YUV_420);
memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
defaultColorInfo = C2StreamColorInfo::output::AllocShared(
{C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */,
C2Color::YUV_420);
helper->addStructDescriptors<C2ChromaOffsetStruct>();
addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
.withConstValue(defaultColorInfo)
.build());
addParameter(
DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
.withDefault(new C2StreamColorAspectsTuning::output(
0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
.withFields(
{C2F(mDefaultColorAspects, range)
.inRange(C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
C2F(mDefaultColorAspects, primaries)
.inRange(C2Color::PRIMARIES_UNSPECIFIED,
C2Color::PRIMARIES_OTHER),
C2F(mDefaultColorAspects, transfer)
.inRange(C2Color::TRANSFER_UNSPECIFIED,
C2Color::TRANSFER_OTHER),
C2F(mDefaultColorAspects, matrix)
.inRange(C2Color::MATRIX_UNSPECIFIED,
C2Color::MATRIX_OTHER)})
.withSetter(DefaultColorAspectsSetter)
.build());
// TODO: support more formats?
addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
.withConstValue(new C2StreamPixelFormatInfo::output(
0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
.build());
}
static C2R SizeSetter(bool mayBlock,
const C2P<C2StreamPictureSizeInfo::output> &oldMe,
C2P<C2StreamPictureSizeInfo::output> &me) {
(void)mayBlock;
C2R res = C2R::Ok();
if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
me.set().width = oldMe.v.width;
}
if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
me.set().height = oldMe.v.height;
}
return res;
}
static C2R MaxPictureSizeSetter(
bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output> &me,
const C2P<C2StreamPictureSizeInfo::output> &size) {
(void)mayBlock;
// TODO: get max width/height from the size's field helpers vs.
// hardcoding
me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
return C2R::Ok();
}
static C2R MaxInputSizeSetter(
bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
(void)mayBlock;
// assume compression ratio of 2
me.set().value =
(((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072);
return C2R::Ok();
}
static C2R DefaultColorAspectsSetter(
bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
(void)mayBlock;
if (me.v.range > C2Color::RANGE_OTHER) {
me.set().range = C2Color::RANGE_OTHER;
}
if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
me.set().primaries = C2Color::PRIMARIES_OTHER;
}
if (me.v.transfer > C2Color::TRANSFER_OTHER) {
me.set().transfer = C2Color::TRANSFER_OTHER;
}
if (me.v.matrix > C2Color::MATRIX_OTHER) {
me.set().matrix = C2Color::MATRIX_OTHER;
}
return C2R::Ok();
}
static C2R ProfileLevelSetter(
bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
const C2P<C2StreamPictureSizeInfo::output> &size) {
(void)mayBlock;
(void)size;
(void)me; // TODO: validate
return C2R::Ok();
}
std::shared_ptr<C2StreamColorAspectsTuning::output>
getDefaultColorAspects_l() {
return mDefaultColorAspects;
}
static C2R Hdr10PlusInfoInputSetter(bool mayBlock,
C2P<C2StreamHdr10PlusInfo::input> &me) {
(void)mayBlock;
(void)me; // TODO: validate
return C2R::Ok();
}
static C2R Hdr10PlusInfoOutputSetter(bool mayBlock,
C2P<C2StreamHdr10PlusInfo::output> &me) {
(void)mayBlock;
(void)me; // TODO: validate
return C2R::Ok();
}
private:
std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
};
C2SoftGav1Dec::C2SoftGav1Dec(const char *name, c2_node_id_t id,
const std::shared_ptr<IntfImpl> &intfImpl)
: SimpleC2Component(
std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
mIntf(intfImpl),
mCodecCtx(nullptr) {
gettimeofday(&mTimeStart, nullptr);
gettimeofday(&mTimeEnd, nullptr);
}
C2SoftGav1Dec::~C2SoftGav1Dec() { onRelease(); }
c2_status_t C2SoftGav1Dec::onInit() {
return initDecoder() ? C2_OK : C2_CORRUPTED;
}
c2_status_t C2SoftGav1Dec::onStop() {
mSignalledError = false;
mSignalledOutputEos = false;
return C2_OK;
}
void C2SoftGav1Dec::onReset() {
(void)onStop();
c2_status_t err = onFlush_sm();
if (err != C2_OK) {
ALOGW("Failed to flush the av1 decoder. Trying to hard reset.");
destroyDecoder();
if (!initDecoder()) {
ALOGE("Hard reset failed.");
}
}
}
void C2SoftGav1Dec::onRelease() { destroyDecoder(); }
c2_status_t C2SoftGav1Dec::onFlush_sm() {
Libgav1StatusCode status =
mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
/*user_private_data=*/0);
if (status != kLibgav1StatusOk) {
ALOGE("Failed to flush av1 decoder. status: %d.", status);
return C2_CORRUPTED;
}
// Dequeue frame (if any) that was enqueued previously.
const libgav1::DecoderBuffer *buffer;
status = mCodecCtx->DequeueFrame(&buffer);
if (status != kLibgav1StatusOk) {
ALOGE("Failed to dequeue frame after flushing the av1 decoder. status: %d",
status);
return C2_CORRUPTED;
}
mSignalledError = false;
mSignalledOutputEos = false;
return C2_OK;
}
static int GetCPUCoreCount() {
int cpuCoreCount = 1;
#if defined(_SC_NPROCESSORS_ONLN)
cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
#else
// _SC_NPROC_ONLN must be defined...
cpuCoreCount = sysconf(_SC_NPROC_ONLN);
#endif
CHECK(cpuCoreCount >= 1);
ALOGV("Number of CPU cores: %d", cpuCoreCount);
return cpuCoreCount;
}
bool C2SoftGav1Dec::initDecoder() {
mSignalledError = false;
mSignalledOutputEos = false;
mCodecCtx.reset(new libgav1::Decoder());
if (mCodecCtx == nullptr) {
ALOGE("mCodecCtx is null");
return false;
}
libgav1::DecoderSettings settings = {};
settings.threads = GetCPUCoreCount();
ALOGV("Using libgav1 AV1 software decoder.");
Libgav1StatusCode status = mCodecCtx->Init(&settings);
if (status != kLibgav1StatusOk) {
ALOGE("av1 decoder failed to initialize. status: %d.", status);
return false;
}
return true;
}
void C2SoftGav1Dec::destroyDecoder() { mCodecCtx = nullptr; }
void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
flags |= C2FrameData::FLAG_END_OF_STREAM;
ALOGV("signalling eos");
}
work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.ordinal = work->input.ordinal;
work->workletsProcessed = 1u;
}
void C2SoftGav1Dec::finishWork(uint64_t index,
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2GraphicBlock> &block) {
std::shared_ptr<C2Buffer> buffer =
createGraphicBuffer(block, C2Rect(mWidth, mHeight));
auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
(c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
flags |= C2FrameData::FLAG_END_OF_STREAM;
ALOGV("signalling eos");
}
work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
work->workletsProcessed = 1u;
};
if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
fillWork(work);
} else {
finish(index, fillWork);
}
}
void C2SoftGav1Dec::process(const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
work->result = C2_OK;
work->workletsProcessed = 0u;
work->worklets.front()->output.configUpdate.clear();
work->worklets.front()->output.flags = work->input.flags;
if (mSignalledError || mSignalledOutputEos) {
work->result = C2_BAD_VALUE;
return;
}
size_t inOffset = 0u;
size_t inSize = 0u;
C2ReadView rView = mDummyReadView;
if (!work->input.buffers.empty()) {
rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
inSize = rView.capacity();
if (inSize && rView.error()) {
ALOGE("read view map failed %d", rView.error());
work->result = C2_CORRUPTED;
return;
}
}
bool codecConfig =
((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0);
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x", inSize,
(int)work->input.ordinal.timestamp.peeku(),
(int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
if (codecConfig) {
fillEmptyWork(work);
return;
}
int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
if (inSize) {
uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
int32_t decodeTime = 0;
int32_t delay = 0;
GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
const Libgav1StatusCode status =
mCodecCtx->EnqueueFrame(bitstream, inSize, frameIndex);
GETTIME(&mTimeEnd, nullptr);
TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
ALOGV("decodeTime=%4d delay=%4d\n", decodeTime, delay);
if (status != kLibgav1StatusOk) {
ALOGE("av1 decoder failed to decode frame. status: %d.", status);
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
mSignalledError = true;
return;
}
} else {
const Libgav1StatusCode status =
mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
/*user_private_data=*/0);
if (status != kLibgav1StatusOk) {
ALOGE("Failed to flush av1 decoder. status: %d.", status);
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
mSignalledError = true;
return;
}
}
(void)outputBuffer(pool, work);
if (eos) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
mSignalledOutputEos = true;
} else if (!inSize) {
fillEmptyWork(work);
}
}
static void copyOutputBufferToYV12Frame(uint8_t *dst, const uint8_t *srcY,
const uint8_t *srcU,
const uint8_t *srcV, size_t srcYStride,
size_t srcUStride, size_t srcVStride,
uint32_t width, uint32_t height) {
const size_t dstYStride = align(width, 16);
const size_t dstUVStride = align(dstYStride / 2, 16);
uint8_t *const dstStart = dst;
for (size_t i = 0; i < height; ++i) {
memcpy(dst, srcY, width);
srcY += srcYStride;
dst += dstYStride;
}
dst = dstStart + dstYStride * height;
for (size_t i = 0; i < height / 2; ++i) {
memcpy(dst, srcV, width / 2);
srcV += srcVStride;
dst += dstUVStride;
}
dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
for (size_t i = 0; i < height / 2; ++i) {
memcpy(dst, srcU, width / 2);
srcU += srcUStride;
dst += dstUVStride;
}
}
static void convertYUV420Planar16ToY410(uint32_t *dst, const uint16_t *srcY,
const uint16_t *srcU,
const uint16_t *srcV, size_t srcYStride,
size_t srcUStride, size_t srcVStride,
size_t dstStride, size_t width,
size_t height) {
// Converting two lines at a time, slightly faster
for (size_t y = 0; y < height; y += 2) {
uint32_t *dstTop = (uint32_t *)dst;
uint32_t *dstBot = (uint32_t *)(dst + dstStride);
uint16_t *ySrcTop = (uint16_t *)srcY;
uint16_t *ySrcBot = (uint16_t *)(srcY + srcYStride);
uint16_t *uSrc = (uint16_t *)srcU;
uint16_t *vSrc = (uint16_t *)srcV;
uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1;
size_t x = 0;
for (; x < width - 3; x += 4) {
u01 = *((uint32_t *)uSrc);
uSrc += 2;
v01 = *((uint32_t *)vSrc);
vSrc += 2;
y01 = *((uint32_t *)ySrcTop);
ySrcTop += 2;
y23 = *((uint32_t *)ySrcTop);
ySrcTop += 2;
y45 = *((uint32_t *)ySrcBot);
ySrcBot += 2;
y67 = *((uint32_t *)ySrcBot);
ySrcBot += 2;
uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
uv1 = (u01 >> 16) | ((v01 >> 16) << 20);
*dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0;
*dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0;
*dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1;
*dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1;
*dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0;
*dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0;
*dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1;
*dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1;
}
// There should be at most 2 more pixels to process. Note that we don't
// need to consider odd case as the buffer is always aligned to even.
if (x < width) {
u01 = *uSrc;
v01 = *vSrc;
y01 = *((uint32_t *)ySrcTop);
y45 = *((uint32_t *)ySrcBot);
uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
*dstTop++ = ((y01 & 0x3FF) << 10) | uv0;
*dstTop++ = ((y01 >> 16) << 10) | uv0;
*dstBot++ = ((y45 & 0x3FF) << 10) | uv0;
*dstBot++ = ((y45 >> 16) << 10) | uv0;
}
srcY += srcYStride * 2;
srcU += srcUStride;
srcV += srcVStride;
dst += dstStride * 2;
}
}
static void convertYUV420Planar16ToYUV420Planar(
uint8_t *dst, const uint16_t *srcY, const uint16_t *srcU,
const uint16_t *srcV, size_t srcYStride, size_t srcUStride,
size_t srcVStride, size_t dstStride, size_t width, size_t height) {
uint8_t *dstY = (uint8_t *)dst;
size_t dstYSize = dstStride * height;
size_t dstUVStride = align(dstStride / 2, 16);
size_t dstUVSize = dstUVStride * height / 2;
uint8_t *dstV = dstY + dstYSize;
uint8_t *dstU = dstV + dstUVSize;
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
dstY[x] = (uint8_t)(srcY[x] >> 2);
}
srcY += srcYStride;
dstY += dstStride;
}
for (size_t y = 0; y < (height + 1) / 2; ++y) {
for (size_t x = 0; x < (width + 1) / 2; ++x) {
dstU[x] = (uint8_t)(srcU[x] >> 2);
dstV[x] = (uint8_t)(srcV[x] >> 2);
}
srcU += srcUStride;
srcV += srcVStride;
dstU += dstUVStride;
dstV += dstUVStride;
}
}
bool C2SoftGav1Dec::outputBuffer(const std::shared_ptr<C2BlockPool> &pool,
const std::unique_ptr<C2Work> &work) {
if (!(work && pool)) return false;
const libgav1::DecoderBuffer *buffer;
const Libgav1StatusCode status = mCodecCtx->DequeueFrame(&buffer);
if (status != kLibgav1StatusOk) {
ALOGE("av1 decoder DequeueFrame failed. status: %d.", status);
return false;
}
// |buffer| can be NULL if status was equal to kLibgav1StatusOk. This is not
// an error. This could mean one of two things:
// - The EnqueueFrame() call was either a flush (called with nullptr).
// - The enqueued frame did not have any displayable frames.
if (!buffer) {
return false;
}
const int width = buffer->displayed_width[0];
const int height = buffer->displayed_height[0];
if (width != mWidth || height != mHeight) {
mWidth = width;
mHeight = height;
C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
std::vector<std::unique_ptr<C2SettingResult>> failures;
c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
if (err == C2_OK) {
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(size));
} else {
ALOGE("Config update size failed");
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
return false;
}
}
// TODO(vigneshv): Add support for monochrome videos since AV1 supports it.
CHECK(buffer->image_format == libgav1::kImageFormatYuv420);
std::shared_ptr<C2GraphicBlock> block;
uint32_t format = HAL_PIXEL_FORMAT_YV12;
if (buffer->bitdepth == 10) {
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamColorAspectsTuning::output> defaultColorAspects =
mIntf->getDefaultColorAspects_l();
if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
defaultColorAspects->matrix == C2Color::MATRIX_BT2020 &&
defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) {
format = HAL_PIXEL_FORMAT_RGBA_1010102;
}
}
C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format,
usage, &block);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
work->result = err;
return false;
}
C2GraphicView wView = block->map().get();
if (wView.error()) {
ALOGE("graphic view map failed %d", wView.error());
work->result = C2_CORRUPTED;
return false;
}
ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d", block->width(),
block->height(), mWidth, mHeight, (int)buffer->user_private_data);
uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
size_t srcYStride = buffer->stride[0];
size_t srcUStride = buffer->stride[1];
size_t srcVStride = buffer->stride[2];
if (buffer->bitdepth == 10) {
const uint16_t *srcY = (const uint16_t *)buffer->plane[0];
const uint16_t *srcU = (const uint16_t *)buffer->plane[1];
const uint16_t *srcV = (const uint16_t *)buffer->plane[2];
if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
convertYUV420Planar16ToY410(
(uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2,
srcVStride / 2, align(mWidth, 16), mWidth, mHeight);
} else {
convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
srcUStride / 2, srcVStride / 2,
align(mWidth, 16), mWidth, mHeight);
}
} else {
const uint8_t *srcY = (const uint8_t *)buffer->plane[0];
const uint8_t *srcU = (const uint8_t *)buffer->plane[1];
const uint8_t *srcV = (const uint8_t *)buffer->plane[2];
copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, srcYStride, srcUStride,
srcVStride, mWidth, mHeight);
}
finishWork(buffer->user_private_data, work, std::move(block));
block = nullptr;
return true;
}
c2_status_t C2SoftGav1Dec::drainInternal(
uint32_t drainMode, const std::shared_ptr<C2BlockPool> &pool,
const std::unique_ptr<C2Work> &work) {
if (drainMode == NO_DRAIN) {
ALOGW("drain with NO_DRAIN: no-op");
return C2_OK;
}
if (drainMode == DRAIN_CHAIN) {
ALOGW("DRAIN_CHAIN not supported");
return C2_OMITTED;
}
Libgav1StatusCode status =
mCodecCtx->EnqueueFrame(/*data=*/nullptr, /*size=*/0,
/*user_private_data=*/0);
if (status != kLibgav1StatusOk) {
ALOGE("Failed to flush av1 decoder. status: %d.", status);
return C2_CORRUPTED;
}
while (outputBuffer(pool, work)) {
}
if (drainMode == DRAIN_COMPONENT_WITH_EOS && work &&
work->workletsProcessed == 0u) {
fillEmptyWork(work);
}
return C2_OK;
}
c2_status_t C2SoftGav1Dec::drain(uint32_t drainMode,
const std::shared_ptr<C2BlockPool> &pool) {
return drainInternal(drainMode, pool, nullptr);
}
class C2SoftGav1Factory : public C2ComponentFactory {
public:
C2SoftGav1Factory()
: mHelper(std::static_pointer_cast<C2ReflectorHelper>(
GetCodec2PlatformComponentStore()->getParamReflector())) {}
virtual c2_status_t createComponent(
c2_node_id_t id, std::shared_ptr<C2Component> *const component,
std::function<void(C2Component *)> deleter) override {
*component = std::shared_ptr<C2Component>(
new C2SoftGav1Dec(COMPONENT_NAME, id,
std::make_shared<C2SoftGav1Dec::IntfImpl>(mHelper)),
deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *const interface,
std::function<void(C2ComponentInterface *)> deleter) override {
*interface = std::shared_ptr<C2ComponentInterface>(
new SimpleInterface<C2SoftGav1Dec::IntfImpl>(
COMPONENT_NAME, id,
std::make_shared<C2SoftGav1Dec::IntfImpl>(mHelper)),
deleter);
return C2_OK;
}
virtual ~C2SoftGav1Factory() override = default;
private:
std::shared_ptr<C2ReflectorHelper> mHelper;
};
} // namespace android
extern "C" ::C2ComponentFactory *CreateCodec2Factory() {
ALOGV("in %s", __func__);
return new ::android::C2SoftGav1Factory();
}
extern "C" void DestroyCodec2Factory(::C2ComponentFactory *factory) {
ALOGV("in %s", __func__);
delete factory;
}

@ -0,0 +1,77 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_C2_SOFT_GAV1_DEC_H_
#define ANDROID_C2_SOFT_GAV1_DEC_H_
#include <SimpleC2Component.h>
#include "libgav1/src/decoder.h"
#include "libgav1/src/decoder_settings.h"
#define GETTIME(a, b) gettimeofday(a, b);
#define TIME_DIFF(start, end, diff) \
diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
((end).tv_usec - (start).tv_usec);
namespace android {
struct C2SoftGav1Dec : public SimpleC2Component {
class IntfImpl;
C2SoftGav1Dec(const char* name, c2_node_id_t id,
const std::shared_ptr<IntfImpl>& intfImpl);
~C2SoftGav1Dec();
// Begin SimpleC2Component overrides.
c2_status_t onInit() override;
c2_status_t onStop() override;
void onReset() override;
void onRelease() override;
c2_status_t onFlush_sm() override;
void process(const std::unique_ptr<C2Work>& work,
const std::shared_ptr<C2BlockPool>& pool) override;
c2_status_t drain(uint32_t drainMode,
const std::shared_ptr<C2BlockPool>& pool) override;
// End SimpleC2Component overrides.
private:
std::shared_ptr<IntfImpl> mIntf;
std::unique_ptr<libgav1::Decoder> mCodecCtx;
uint32_t mWidth;
uint32_t mHeight;
bool mSignalledOutputEos;
bool mSignalledError;
struct timeval mTimeStart; // Time at the start of decode()
struct timeval mTimeEnd; // Time at the end of decode()
bool initDecoder();
void destroyDecoder();
void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
const std::shared_ptr<C2GraphicBlock>& block);
bool outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
const std::unique_ptr<C2Work>& work);
c2_status_t drainInternal(uint32_t drainMode,
const std::shared_ptr<C2BlockPool>& pool,
const std::unique_ptr<C2Work>& work);
C2_DO_NOT_COPY(C2SoftGav1Dec);
};
} // namespace android
#endif // ANDROID_C2_SOFT_GAV1_DEC_H_

@ -33,7 +33,8 @@ namespace android {
namespace {
constexpr char COMPONENT_NAME[] = "c2.android.hevc.decoder";
constexpr uint32_t kDefaultOutputDelay = 8;
constexpr uint32_t kMaxOutputDelay = 16;
} // namespace
class C2SoftHevcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@ -54,7 +55,9 @@ public:
// TODO: Proper support for reorder depth.
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
.withConstValue(new C2PortActualDelayTuning::output(8u))
.withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
.withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
.withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
addParameter(
@ -327,6 +330,7 @@ C2SoftHevcDec::C2SoftHevcDec(
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
mIvColorformat(IV_YUV_420P),
mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
mHeaderDecoded(false),
@ -877,6 +881,26 @@ void C2SoftHevcDec::process(
work->result = C2_CORRUPTED;
return;
}
if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
mOutputDelay = s_decode_op.i4_reorder_depth;
ALOGV("New Output delay %d ", mOutputDelay);
C2PortActualDelayTuning::output outputDelay(mOutputDelay);
std::vector<std::unique_ptr<C2SettingResult>> failures;
c2_status_t err =
mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
if (err == OK) {
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(outputDelay));
} else {
ALOGE("Cannot set output delay");
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
continue;
}
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;

@ -115,7 +115,7 @@ struct C2SoftHevcDec : public SimpleC2Component {
size_t mNumCores;
IV_COLOR_FORMAT_T mIvColorformat;
uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mStride;

@ -24,7 +24,7 @@ cc_library {
"libgui",
"libhidlbase",
"liblog",
"libstagefright_bufferpool@2.0",
"libstagefright_bufferpool@2.0.1",
"libui",
"libutils",
],
@ -37,7 +37,7 @@ cc_library {
"android.hardware.media.c2@1.0",
"libcodec2",
"libgui",
"libstagefright_bufferpool@2.0",
"libstagefright_bufferpool@2.0.1",
"libui",
],
}
@ -81,7 +81,7 @@ cc_library {
"libcutils",
"libhidlbase",
"liblog",
"libstagefright_bufferpool@2.0",
"libstagefright_bufferpool@2.0.1",
"libstagefright_bufferqueue_helper",
"libui",
"libutils",
@ -96,7 +96,7 @@ cc_library {
"libcodec2",
"libcodec2_vndk",
"libhidlbase",
"libstagefright_bufferpool@2.0",
"libstagefright_bufferpool@2.0.1",
"libui",
],
}

@ -1434,6 +1434,11 @@ bool objcpy(C2BaseBlock* d, const BaseBlock& s) {
d->type = C2BaseBlock::GRAPHIC;
return true;
}
if (cHandle) {
// Though we got cloned handle, creating block failed.
native_handle_close(cHandle);
native_handle_delete(cHandle);
}
LOG(ERROR) << "Unknown handle type in BaseBlock::pooledBlock.";
return false;

@ -547,6 +547,10 @@ TEST_P(Codec2AudioDecDecodeTest, DecodeTest) {
if (mCompName == raw) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else if (mCompName == g711alaw || mCompName == g711mlaw) {
// g711 test data is all 1-channel and has no embedded config info.
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
ASSERT_NO_FATAL_FAILURE(
getInputChannelInfo(mComponent, mCompName, bitStreamInfo));

@ -1,31 +1,36 @@
## Codec2 VTS Hal @ 1.0 tests ##
---
#### master :
# Codec2 VTS Hal @ 1.0 tests #
## master :
Functionality of master is to enumerate all the Codec2 components available in C2 media service.
usage: VtsHalMediaC2V1\_0TargetMasterTest -I default
usage: `VtsHalMediaC2V1_0TargetMasterTest -I default`
#### component :
## component :
Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass.
usage: VtsHalMediaC2V1\_0TargetComponentTest -I software -C <comp name>
example: VtsHalMediaC2V1\_0TargetComponentTest -I software -C c2.android.vorbis.decoder
usage: `VtsHalMediaC2V1_0TargetComponentTest -I software -C <comp name>`
example: `VtsHalMediaC2V1_0TargetComponentTest -I software -C c2.android.vorbis.decoder`
## audio :
Functionality of audio test is to validate audio specific functionality of Codec2 components. The resource files for this test are taken from `frameworks/av/media/codec2/hidl/1.0/vts/functional/res`. The path to these files on the device can be specified with `-P`. (If the device path is omitted, `/data/local/tmp/media/` is the default value.)
usage: `VtsHalMediaC2V1_0TargetAudioDecTest -I default -C <comp name> -P <path to resource files>`
usage: `VtsHalMediaC2V1_0TargetAudioEncTest -I software -C <comp name> -P <path to resource files>`
example: `VtsHalMediaC2V1_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /data/local/tmp/media/`
#### audio :
Functionality of audio test is to validate audio specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
example: `VtsHalMediaC2V1_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /data/local/tmp/media/`
usage: VtsHalMediaC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/media/
usage: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/media/
## video :
Functionality of video test is to validate video specific functionality of Codec2 components. The resource files for this test are taken from `frameworks/av/media/codec2/hidl/1.0/vts/functional/res`. The path to these files on the device can be specified with `-P`. (If the device path is omitted, `/data/local/tmp/media/` is the default value.)
example: VtsHalMediaC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/media/
example: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/media/
usage: `VtsHalMediaC2V1_0TargetVideoDecTest -I default -C <comp name> -P <path to resource files>`
#### video :
Functionality of video test is to validate video specific functionality Codec2 components. The resource files for this test are taken from media/codec2/hidl/1.0/vts/functional/res. The path to these files on the device is required to be given for bitstream tests.
usage: `VtsHalMediaC2V1_0TargetVideoEncTest -I software -C <comp name> -P <path to resource files>`
usage: VtsHalMediaC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/media/
usage: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/media/
example: `VtsHalMediaC2V1_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /data/local/tmp/media/`
example: VtsHalMediaC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/media/
example: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/media/
example: `VtsHalMediaC2V1_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /data/local/tmp/media/`

@ -118,7 +118,7 @@ class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
registerTestService<IComponentStore>();
}
ComponentTestEnvironment() : res("/sdcard/media/") {}
ComponentTestEnvironment() : res("/data/local/tmp/media/") {}
void setComponent(const char* _component) { component = _component; }

@ -18,7 +18,7 @@ cc_library {
"libgui",
"libhidlbase",
"liblog",
"libstagefright_bufferpool@2.0",
"libstagefright_bufferpool@2.0.1",
"libui",
"libutils",
],

@ -883,66 +883,72 @@ std::shared_ptr<Codec2Client::Component>
Codec2Client::CreateComponentByName(
const char* componentName,
const std::shared_ptr<Listener>& listener,
std::shared_ptr<Codec2Client>* owner) {
std::shared_ptr<Component> component;
c2_status_t status = ForAllServices(
componentName,
[owner, &component, componentName, &listener](
const std::shared_ptr<Codec2Client> &client)
-> c2_status_t {
c2_status_t status = client->createComponent(componentName,
listener,
&component);
if (status == C2_OK) {
if (owner) {
*owner = client;
std::shared_ptr<Codec2Client>* owner,
size_t numberOfAttempts) {
while (true) {
std::shared_ptr<Component> component;
c2_status_t status = ForAllServices(
componentName,
[owner, &component, componentName, &listener](
const std::shared_ptr<Codec2Client> &client)
-> c2_status_t {
c2_status_t status = client->createComponent(componentName,
listener,
&component);
if (status == C2_OK) {
if (owner) {
*owner = client;
}
} else if (status != C2_NOT_FOUND) {
LOG(DEBUG) << "IComponentStore("
<< client->getServiceName()
<< ")::createComponent(\"" << componentName
<< "\") returned status = "
<< status << ".";
}
} else if (status != C2_NOT_FOUND) {
LOG(DEBUG) << "IComponentStore("
<< client->getServiceName()
<< ")::createComponent(\"" << componentName
<< "\") returned status = "
<< status << ".";
}
return status;
});
if (status != C2_OK) {
LOG(DEBUG) << "Could not create component \"" << componentName << "\". "
"Status = " << status << ".";
return status;
});
if (numberOfAttempts > 0 && status == C2_TRANSACTION_FAILED) {
--numberOfAttempts;
continue;
}
return component;
}
return component;
}
std::shared_ptr<Codec2Client::Interface>
Codec2Client::CreateInterfaceByName(
const char* interfaceName,
std::shared_ptr<Codec2Client>* owner) {
std::shared_ptr<Interface> interface;
c2_status_t status = ForAllServices(
interfaceName,
[owner, &interface, interfaceName](
const std::shared_ptr<Codec2Client> &client)
-> c2_status_t {
c2_status_t status = client->createInterface(interfaceName,
&interface);
if (status == C2_OK) {
if (owner) {
*owner = client;
std::shared_ptr<Codec2Client>* owner,
size_t numberOfAttempts) {
while (true) {
std::shared_ptr<Interface> interface;
c2_status_t status = ForAllServices(
interfaceName,
[owner, &interface, interfaceName](
const std::shared_ptr<Codec2Client> &client)
-> c2_status_t {
c2_status_t status = client->createInterface(interfaceName,
&interface);
if (status == C2_OK) {
if (owner) {
*owner = client;
}
} else if (status != C2_NOT_FOUND) {
LOG(DEBUG) << "IComponentStore("
<< client->getServiceName()
<< ")::createInterface(\"" << interfaceName
<< "\") returned status = "
<< status << ".";
}
} else if (status != C2_NOT_FOUND) {
LOG(DEBUG) << "IComponentStore("
<< client->getServiceName()
<< ")::createInterface(\"" << interfaceName
<< "\") returned status = "
<< status << ".";
}
return status;
});
if (status != C2_OK) {
LOG(DEBUG) << "Could not create interface \"" << interfaceName << "\". "
"Status = " << status << ".";
return status;
});
if (numberOfAttempts > 0 && status == C2_TRANSACTION_FAILED) {
--numberOfAttempts;
continue;
}
return interface;
}
return interface;
}
std::vector<C2Component::Traits> const& Codec2Client::ListComponents() {

@ -179,17 +179,21 @@ struct Codec2Client : public Codec2ConfigurableClient {
static std::vector<std::shared_ptr<Codec2Client>> CreateFromAllServices();
// Try to create a component with a given name from all known
// IComponentStore services.
// IComponentStore services. numberOfAttempts determines the number of times
// to retry the HIDL call if the transaction fails.
static std::shared_ptr<Component> CreateComponentByName(
char const* componentName,
std::shared_ptr<Listener> const& listener,
std::shared_ptr<Codec2Client>* owner = nullptr);
std::shared_ptr<Codec2Client>* owner = nullptr,
size_t numberOfAttempts = 10);
// Try to create a component interface with a given name from all known
// IComponentStore services.
// IComponentStore services. numberOfAttempts determines the number of times
// to retry the HIDL call if the transaction fails.
static std::shared_ptr<Interface> CreateInterfaceByName(
char const* interfaceName,
std::shared_ptr<Codec2Client>* owner = nullptr);
std::shared_ptr<Codec2Client>* owner = nullptr,
size_t numberOfAttempts = 10);
// List traits from all known IComponentStore services.
static std::vector<C2Component::Traits> const& ListComponents();

@ -814,9 +814,17 @@ void CCodec::configure(const sp<AMessage> &msg) {
}
{
double value;
if (msg->findDouble("time-lapse-fps", &value)) {
config->mISConfig->mCaptureFps = value;
bool captureFpsFound = false;
double timeLapseFps;
float captureRate;
if (msg->findDouble("time-lapse-fps", &timeLapseFps)) {
config->mISConfig->mCaptureFps = timeLapseFps;
captureFpsFound = true;
} else if (msg->findAsFloat(KEY_CAPTURE_RATE, &captureRate)) {
config->mISConfig->mCaptureFps = captureRate;
captureFpsFound = true;
}
if (captureFpsFound) {
(void)msg->findAsFloat(KEY_FRAME_RATE, &config->mISConfig->mCodedFps);
}
}

@ -224,7 +224,7 @@ CCodecBufferChannel::CCodecBufferChannel(
mFirstValidFrameIndex(0u),
mMetaMode(MODE_NONE),
mInputMetEos(false) {
mOutputSurface.lock()->maxDequeueBuffers = 2 * kSmoothnessFactor + kRenderingDepth;
mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth;
{
Mutexed<Input>::Locked input(mInput);
input->buffers.reset(new DummyInputBuffers(""));
@ -948,8 +948,11 @@ status_t CCodecBufferChannel::start(
uint32_t outputGeneration;
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
output->maxDequeueBuffers = numOutputSlots + numInputSlots +
output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + kRenderingDepth;
if (!secure) {
output->maxDequeueBuffers += numInputSlots;
}
outputSurface = output->surface ?
output->surface->getIGraphicBufferProducer() : nullptr;
if (outputSurface) {
@ -1329,14 +1332,18 @@ bool CCodecBufferChannel::handleWork(
case C2PortReorderBufferDepthTuning::CORE_INDEX: {
C2PortReorderBufferDepthTuning::output reorderDepth;
if (reorderDepth.updateFrom(*param)) {
bool secure = mComponent->getName().find(".secure") != std::string::npos;
mReorderStash.lock()->setDepth(reorderDepth.value);
ALOGV("[%s] onWorkDone: updated reorder depth to %u",
mName, reorderDepth.value);
size_t numOutputSlots = mOutput.lock()->numSlots;
size_t numInputSlots = mInput.lock()->numSlots;
Mutexed<OutputSurface>::Locked output(mOutputSurface);
output->maxDequeueBuffers = numOutputSlots + numInputSlots +
output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + kRenderingDepth;
if (!secure) {
output->maxDequeueBuffers += numInputSlots;
}
if (output->surface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}
@ -1380,6 +1387,7 @@ bool CCodecBufferChannel::handleWork(
if (outputDelay.updateFrom(*param)) {
ALOGV("[%s] onWorkDone: updating output delay %u",
mName, outputDelay.value);
bool secure = mComponent->getName().find(".secure") != std::string::npos;
(void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
bool outputBuffersChanged = false;
@ -1409,8 +1417,10 @@ bool CCodecBufferChannel::handleWork(
uint32_t depth = mReorderStash.lock()->depth();
Mutexed<OutputSurface>::Locked output(mOutputSurface);
output->maxDequeueBuffers = numOutputSlots + numInputSlots +
depth + kRenderingDepth;
output->maxDequeueBuffers = numOutputSlots + depth + kRenderingDepth;
if (!secure) {
output->maxDequeueBuffers += numInputSlots;
}
if (output->surface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}

@ -66,7 +66,7 @@ cc_library_shared {
"liblog",
"libnativewindow",
"libstagefright_foundation",
"libstagefright_bufferpool@2.0",
"libstagefright_bufferpool@2.0.1",
"libui",
"libutils",
],

@ -413,17 +413,14 @@ std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
std::shared_ptr<C2LinearAllocation> alloc;
if (C2AllocatorIon::isValid(cHandle)) {
native_handle_t *handle = native_handle_clone(cHandle);
if (handle) {
c2_status_t err = sAllocator->priorLinearAllocation(handle, &alloc);
const std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(data);
if (err == C2_OK && poolData) {
// TODO: config params?
std::shared_ptr<C2LinearBlock> block =
_C2BlockFactory::CreateLinearBlock(alloc, poolData);
return block;
}
c2_status_t err = sAllocator->priorLinearAllocation(cHandle, &alloc);
const std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(data);
if (err == C2_OK && poolData) {
// TODO: config params?
std::shared_ptr<C2LinearBlock> block =
_C2BlockFactory::CreateLinearBlock(alloc, poolData);
return block;
}
}
return nullptr;
@ -674,17 +671,14 @@ public:
ResultStatus status = mBufferPoolManager->allocate(
mConnectionId, params, &cHandle, &bufferPoolData);
if (status == ResultStatus::OK) {
native_handle_t *handle = native_handle_clone(cHandle);
if (handle) {
std::shared_ptr<C2LinearAllocation> alloc;
std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(bufferPoolData);
c2_status_t err = mAllocator->priorLinearAllocation(handle, &alloc);
if (err == C2_OK && poolData && alloc) {
*block = _C2BlockFactory::CreateLinearBlock(alloc, poolData, 0, capacity);
if (*block) {
return C2_OK;
}
std::shared_ptr<C2LinearAllocation> alloc;
std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(bufferPoolData);
c2_status_t err = mAllocator->priorLinearAllocation(cHandle, &alloc);
if (err == C2_OK && poolData && alloc) {
*block = _C2BlockFactory::CreateLinearBlock(alloc, poolData, 0, capacity);
if (*block) {
return C2_OK;
}
}
return C2_NO_MEMORY;
@ -710,19 +704,16 @@ public:
ResultStatus status = mBufferPoolManager->allocate(
mConnectionId, params, &cHandle, &bufferPoolData);
if (status == ResultStatus::OK) {
native_handle_t *handle = native_handle_clone(cHandle);
if (handle) {
std::shared_ptr<C2GraphicAllocation> alloc;
std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(bufferPoolData);
c2_status_t err = mAllocator->priorGraphicAllocation(
handle, &alloc);
if (err == C2_OK && poolData && alloc) {
*block = _C2BlockFactory::CreateGraphicBlock(
alloc, poolData, C2Rect(width, height));
if (*block) {
return C2_OK;
}
std::shared_ptr<C2GraphicAllocation> alloc;
std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(bufferPoolData);
c2_status_t err = mAllocator->priorGraphicAllocation(
cHandle, &alloc);
if (err == C2_OK && poolData && alloc) {
*block = _C2BlockFactory::CreateGraphicBlock(
alloc, poolData, C2Rect(width, height));
if (*block) {
return C2_OK;
}
}
return C2_NO_MEMORY;
@ -1117,17 +1108,14 @@ std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
std::shared_ptr<C2GraphicAllocation> alloc;
if (C2AllocatorGralloc::isValid(cHandle)) {
native_handle_t *handle = native_handle_clone(cHandle);
if (handle) {
c2_status_t err = sAllocator->priorGraphicAllocation(handle, &alloc);
const std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(data);
if (err == C2_OK && poolData) {
// TODO: config setup?
std::shared_ptr<C2GraphicBlock> block =
_C2BlockFactory::CreateGraphicBlock(alloc, poolData);
return block;
}
c2_status_t err = sAllocator->priorGraphicAllocation(cHandle, &alloc);
const std::shared_ptr<C2PooledBlockPoolData> poolData =
std::make_shared<C2PooledBlockPoolData>(data);
if (err == C2_OK && poolData) {
// TODO: config setup?
std::shared_ptr<C2GraphicBlock> block =
_C2BlockFactory::CreateGraphicBlock(alloc, poolData);
return block;
}
}
return nullptr;

@ -848,7 +848,8 @@ C2PlatformComponentStore::C2PlatformComponentStore()
emplace("libcodec2_soft_amrnbenc.so");
emplace("libcodec2_soft_amrwbdec.so");
emplace("libcodec2_soft_amrwbenc.so");
emplace("libcodec2_soft_av1dec.so");
//emplace("libcodec2_soft_av1dec_aom.so"); // deprecated for the gav1 implementation
emplace("libcodec2_soft_av1dec_gav1.so");
emplace("libcodec2_soft_avcdec.so");
emplace("libcodec2_soft_avcenc.so");
emplace("libcodec2_soft_flacdec.so");

@ -32,6 +32,7 @@ enum {
CONFIG = IBinder::FIRST_CALL_TRANSACTION,
ADD_RESOURCE,
REMOVE_RESOURCE,
REMOVE_CLIENT,
RECLAIM_RESOURCE,
};
@ -72,12 +73,14 @@ public:
virtual void addResource(
int pid,
int uid,
int64_t clientId,
const sp<IResourceManagerClient> client,
const Vector<MediaResource> &resources) {
Parcel data, reply;
data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
data.writeInt32(pid);
data.writeInt32(uid);
data.writeInt64(clientId);
data.writeStrongBinder(IInterface::asBinder(client));
writeToParcel(&data, resources);
@ -85,15 +88,25 @@ public:
remote()->transact(ADD_RESOURCE, data, &reply);
}
virtual void removeResource(int pid, int64_t clientId) {
virtual void removeResource(int pid, int64_t clientId, const Vector<MediaResource> &resources) {
Parcel data, reply;
data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
data.writeInt32(pid);
data.writeInt64(clientId);
writeToParcel(&data, resources);
remote()->transact(REMOVE_RESOURCE, data, &reply);
}
virtual void removeClient(int pid, int64_t clientId) {
Parcel data, reply;
data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
data.writeInt32(pid);
data.writeInt64(clientId);
remote()->transact(REMOVE_CLIENT, data, &reply);
}
virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources) {
Parcel data, reply;
data.writeInterfaceToken(IResourceManagerService::getInterfaceDescriptor());
@ -129,6 +142,7 @@ status_t BnResourceManagerService::onTransact(
case ADD_RESOURCE: {
CHECK_INTERFACE(IResourceManagerService, data, reply);
int pid = data.readInt32();
int uid = data.readInt32();
int64_t clientId = data.readInt64();
sp<IResourceManagerClient> client(
interface_cast<IResourceManagerClient>(data.readStrongBinder()));
@ -137,7 +151,7 @@ status_t BnResourceManagerService::onTransact(
}
Vector<MediaResource> resources;
readFromParcel(data, &resources);
addResource(pid, clientId, client, resources);
addResource(pid, uid, clientId, client, resources);
return NO_ERROR;
} break;
@ -145,7 +159,17 @@ status_t BnResourceManagerService::onTransact(
CHECK_INTERFACE(IResourceManagerService, data, reply);
int pid = data.readInt32();
int64_t clientId = data.readInt64();
removeResource(pid, clientId);
Vector<MediaResource> resources;
readFromParcel(data, &resources);
removeResource(pid, clientId, resources);
return NO_ERROR;
} break;
case REMOVE_CLIENT: {
CHECK_INTERFACE(IResourceManagerService, data, reply);
int pid = data.readInt32();
int64_t clientId = data.readInt64();
removeClient(pid, clientId);
return NO_ERROR;
} break;

@ -39,11 +39,15 @@ public:
virtual void addResource(
int pid,
int uid,
int64_t clientId,
const sp<IResourceManagerClient> client,
const Vector<MediaResource> &resources) = 0;
virtual void removeResource(int pid, int64_t clientId) = 0;
virtual void removeResource(int pid, int64_t clientId,
const Vector<MediaResource> &resources) = 0;
virtual void removeClient(int pid, int64_t clientId) = 0;
virtual bool reclaimResource(
int callingPid,

@ -31,12 +31,13 @@ public:
kNonSecureCodec,
kGraphicMemory,
kCpuBoost,
kBattery,
};
enum SubType {
kUnspecifiedSubType = 0,
kAudioCodec,
kVideoCodec
kVideoCodec,
};
MediaResource();
@ -62,6 +63,8 @@ inline static const char *asString(MediaResource::Type i, const char *def = "??"
case MediaResource::kSecureCodec: return "secure-codec";
case MediaResource::kNonSecureCodec: return "non-secure-codec";
case MediaResource::kGraphicMemory: return "graphic-memory";
case MediaResource::kCpuBoost: return "cpu-boost";
case MediaResource::kBattery: return "battery";
default: return def;
}
}

@ -1826,6 +1826,23 @@ status_t ACodec::configureCodec(
mRepeatFrameDelayUs = -1LL;
}
if (!msg->findDouble("time-lapse-fps", &mCaptureFps)) {
float captureRate;
if (msg->findAsFloat(KEY_CAPTURE_RATE, &captureRate)) {
mCaptureFps = captureRate;
} else {
mCaptureFps = -1.0;
}
}
if (!msg->findInt32(
KEY_CREATE_INPUT_SURFACE_SUSPENDED,
(int32_t*)&mCreateInputBuffersSuspended)) {
mCreateInputBuffersSuspended = false;
}
}
if (encoder && (mIsVideo || mIsImage)) {
// only allow 32-bit value, since we pass it as U32 to OMX.
if (!msg->findInt64(KEY_MAX_PTS_GAP_TO_ENCODER, &mMaxPtsGapUs)) {
mMaxPtsGapUs = 0LL;
@ -1842,16 +1859,6 @@ status_t ACodec::configureCodec(
if (mMaxPtsGapUs < 0LL) {
mMaxFps = -1;
}
if (!msg->findDouble("time-lapse-fps", &mCaptureFps)) {
mCaptureFps = -1.0;
}
if (!msg->findInt32(
KEY_CREATE_INPUT_SURFACE_SUSPENDED,
(int32_t*)&mCreateInputBuffersSuspended)) {
mCreateInputBuffersSuspended = false;
}
}
// NOTE: we only use native window for video decoders

@ -48,6 +48,7 @@
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BatteryChecker.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
@ -57,7 +58,6 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/PersistentSurface.h>
#include <media/stagefright/SurfaceUtils.h>
#include <mediautils/BatteryNotifier.h>
#include <private/android_filesystem_config.h>
#include <utils/Singleton.h>
@ -166,8 +166,9 @@ private:
DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient);
};
MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy(pid_t pid)
: mPid(pid) {
MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy(
pid_t pid, uid_t uid)
: mPid(pid), mUid(uid) {
if (mPid == MediaCodec::kNoPid) {
mPid = IPCThreadState::self()->getCallingPid();
}
@ -204,15 +205,25 @@ void MediaCodec::ResourceManagerServiceProxy::addResource(
if (mService == NULL) {
return;
}
mService->addResource(mPid, clientId, client, resources);
mService->addResource(mPid, mUid, clientId, client, resources);
}
void MediaCodec::ResourceManagerServiceProxy::removeResource(int64_t clientId) {
void MediaCodec::ResourceManagerServiceProxy::removeResource(
int64_t clientId,
const Vector<MediaResource> &resources) {
Mutex::Autolock _l(mLock);
if (mService == NULL) {
return;
}
mService->removeResource(mPid, clientId, resources);
}
void MediaCodec::ResourceManagerServiceProxy::removeClient(int64_t clientId) {
Mutex::Autolock _l(mLock);
if (mService == NULL) {
return;
}
mService->removeResource(mPid, clientId);
mService->removeClient(mPid, clientId);
}
bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
@ -517,9 +528,6 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
mStickyError(OK),
mSoftRenderer(NULL),
mAnalyticsItem(NULL),
mResourceManagerClient(new ResourceManagerClient(this)),
mResourceManagerService(new ResourceManagerServiceProxy(pid)),
mBatteryStatNotified(false),
mIsVideo(false),
mVideoWidth(0),
mVideoHeight(0),
@ -537,13 +545,15 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
} else {
mUid = uid;
}
mResourceManagerClient = new ResourceManagerClient(this);
mResourceManagerService = new ResourceManagerServiceProxy(pid, mUid);
initAnalyticsItem();
}
MediaCodec::~MediaCodec() {
CHECK_EQ(mState, UNINITIALIZED);
mResourceManagerService->removeResource(getId(mResourceManagerClient));
mResourceManagerService->removeClient(getId(mResourceManagerClient));
flushAnalyticsItem();
}
@ -742,6 +752,12 @@ void MediaCodec::statsBufferSent(int64_t presentationUs) {
return;
}
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCodecActivity([this] () {
addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
});
}
const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
BufferFlightTiming_t startdata = { presentationUs, nowNs };
@ -776,6 +792,12 @@ void MediaCodec::statsBufferReceived(int64_t presentationUs) {
return;
}
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCodecActivity([this] () {
addResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
});
}
BufferFlightTiming_t startdata;
bool valid = false;
while (mBuffersInFlight.size() > 0) {
@ -964,6 +986,10 @@ status_t MediaCodec::init(const AString &name) {
mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
}
if (mIsVideo) {
mBatteryChecker = new BatteryChecker(new AMessage(kWhatCheckBatteryStats, this));
}
status_t err;
Vector<MediaResource> resources;
MediaResource::Type type =
@ -1221,6 +1247,13 @@ void MediaCodec::addResource(
getId(mResourceManagerClient), mResourceManagerClient, resources);
}
void MediaCodec::removeResource(
MediaResource::Type type, MediaResource::SubType subtype, uint64_t value) {
Vector<MediaResource> resources;
resources.push_back(MediaResource(type, subtype, value));
mResourceManagerService->removeResource(getId(mResourceManagerClient), resources);
}
status_t MediaCodec::start() {
sp<AMessage> msg = new AMessage(kWhatStart, this);
@ -1682,6 +1715,59 @@ void MediaCodec::requestCpuBoostIfNeeded() {
}
}
BatteryChecker::BatteryChecker(const sp<AMessage> &msg, int64_t timeoutUs)
: mTimeoutUs(timeoutUs)
, mLastActivityTimeUs(-1ll)
, mBatteryStatNotified(false)
, mBatteryCheckerGeneration(0)
, mIsExecuting(false)
, mBatteryCheckerMsg(msg) {}
void BatteryChecker::onCodecActivity(std::function<void()> batteryOnCb) {
if (!isExecuting()) {
// ignore if not executing
return;
}
if (!mBatteryStatNotified) {
batteryOnCb();
mBatteryStatNotified = true;
sp<AMessage> msg = mBatteryCheckerMsg->dup();
msg->setInt32("generation", mBatteryCheckerGeneration);
// post checker and clear last activity time
msg->post(mTimeoutUs);
mLastActivityTimeUs = -1ll;
} else {
// update last activity time
mLastActivityTimeUs = ALooper::GetNowUs();
}
}
void BatteryChecker::onCheckBatteryTimer(
const sp<AMessage> &msg, std::function<void()> batteryOffCb) {
// ignore if this checker already expired because the client resource was removed
int32_t generation;
if (!msg->findInt32("generation", &generation)
|| generation != mBatteryCheckerGeneration) {
return;
}
if (mLastActivityTimeUs < 0ll) {
// timed out inactive, do not repost checker
batteryOffCb();
mBatteryStatNotified = false;
} else {
// repost checker and clear last activity time
msg->post(mTimeoutUs + mLastActivityTimeUs - ALooper::GetNowUs());
mLastActivityTimeUs = -1ll;
}
}
void BatteryChecker::onClientRemoved() {
mBatteryStatNotified = false;
mBatteryCheckerGeneration++;
}
////////////////////////////////////////////////////////////////////////////////
void MediaCodec::cancelPendingDequeueOperations() {
@ -2318,7 +2404,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
mFlags &= ~kFlagIsComponentAllocated;
mResourceManagerService->removeResource(getId(mResourceManagerClient));
// off since we're removing all resources including the battery on
if (mBatteryChecker != nullptr) {
mBatteryChecker->onClientRemoved();
}
mResourceManagerService->removeClient(getId(mResourceManagerClient));
(new AMessage)->postReply(mReplyID);
break;
@ -3029,6 +3120,16 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
case kWhatCheckBatteryStats:
{
if (mBatteryChecker != nullptr) {
mBatteryChecker->onCheckBatteryTimer(msg, [this] () {
removeResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1);
});
}
break;
}
default:
TRESPASS();
}
@ -3125,9 +3226,11 @@ void MediaCodec::setState(State newState) {
mState = newState;
cancelPendingDequeueOperations();
if (mBatteryChecker != nullptr) {
mBatteryChecker->setExecuting(isExecuting());
}
updateBatteryStat();
cancelPendingDequeueOperations();
}
void MediaCodec::returnBuffersToCodec(bool isReclaim) {
@ -3631,20 +3734,6 @@ status_t MediaCodec::amendOutputFormatWithCodecSpecificData(
return OK;
}
void MediaCodec::updateBatteryStat() {
if (!mIsVideo) {
return;
}
if (mState == CONFIGURED && !mBatteryStatNotified) {
BatteryNotifier::getInstance().noteStartVideo(mUid);
mBatteryStatNotified = true;
} else if (mState == UNINITIALIZED && mBatteryStatNotified) {
BatteryNotifier::getInstance().noteStopVideo(mUid);
mBatteryStatNotified = false;
}
}
std::string MediaCodec::stateString(State state) {
const char *rval = NULL;
char rawbuffer[16]; // room for "%d"

@ -7,6 +7,9 @@
"include-annotation": "android.platform.test.annotations.RequiresDevice"
}
]
},
{
"name": "BatteryChecker_test"
}
]
}

@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BATTERY_CHECKER_H_
#define BATTERY_CHECKER_H_
#include <media/stagefright/foundation/AMessage.h>
namespace android {
struct BatteryChecker : public RefBase {
BatteryChecker(const sp<AMessage> &msg, int64_t timeout = 3000000ll);
void setExecuting(bool executing) { mIsExecuting = executing; }
void onCodecActivity(std::function<void()> batteryOnCb);
void onCheckBatteryTimer(const sp<AMessage>& msg, std::function<void()> batteryOffCb);
void onClientRemoved();
private:
const int64_t mTimeoutUs;
int64_t mLastActivityTimeUs;
bool mBatteryStatNotified;
int32_t mBatteryCheckerGeneration;
bool mIsExecuting;
sp<AMessage> mBatteryCheckerMsg;
bool isExecuting() { return mIsExecuting; }
DISALLOW_EVIL_CONSTRUCTORS(BatteryChecker);
};
} // namespace android
#endif // BATTERY_CHECKER_H_

@ -36,6 +36,7 @@ struct ABuffer;
struct AMessage;
struct AReplyToken;
struct AString;
struct BatteryChecker;
class BufferChannelBase;
struct CodecBase;
class IBatteryStats;
@ -257,6 +258,7 @@ private:
kWhatSetCallback = 'setC',
kWhatSetNotification = 'setN',
kWhatDrmReleaseCrypto = 'rDrm',
kWhatCheckBatteryStats = 'chkB',
};
enum {
@ -283,7 +285,7 @@ private:
};
struct ResourceManagerServiceProxy : public IBinder::DeathRecipient {
ResourceManagerServiceProxy(pid_t pid);
ResourceManagerServiceProxy(pid_t pid, uid_t uid);
~ResourceManagerServiceProxy();
void init();
@ -296,7 +298,11 @@ private:
const sp<IResourceManagerClient> &client,
const Vector<MediaResource> &resources);
void removeResource(int64_t clientId);
void removeResource(
int64_t clientId,
const Vector<MediaResource> &resources);
void removeClient(int64_t clientId);
bool reclaimResource(const Vector<MediaResource> &resources);
@ -304,6 +310,7 @@ private:
Mutex mLock;
sp<IResourceManagerService> mService;
pid_t mPid;
uid_t mUid;
};
State mState;
@ -335,7 +342,6 @@ private:
sp<IResourceManagerClient> mResourceManagerClient;
sp<ResourceManagerServiceProxy> mResourceManagerService;
bool mBatteryStatNotified;
bool mIsVideo;
int32_t mVideoWidth;
int32_t mVideoHeight;
@ -425,11 +431,11 @@ private:
status_t onSetParameters(const sp<AMessage> &params);
status_t amendOutputFormatWithCodecSpecificData(const sp<MediaCodecBuffer> &buffer);
void updateBatteryStat();
bool isExecuting() const;
uint64_t getGraphicBufferSize();
void addResource(MediaResource::Type type, MediaResource::SubType subtype, uint64_t value);
void removeResource(MediaResource::Type type, MediaResource::SubType subtype, uint64_t value);
void requestCpuBoostIfNeeded();
bool hasPendingBuffer(int portIndex);
@ -458,6 +464,8 @@ private:
Mutex mLatencyLock;
int64_t mLatencyUnknown; // buffers for which we couldn't calculate latency
sp<BatteryChecker> mBatteryChecker;
void statsBufferSent(int64_t presentationUs);
void statsBufferReceived(int64_t presentationUs);

@ -27,3 +27,21 @@ cc_test {
"-Wall",
],
}
cc_test {
name: "BatteryChecker_test",
srcs: ["BatteryChecker_test.cpp"],
test_suites: ["device-tests"],
shared_libs: [
"libstagefright",
"libstagefright_foundation",
"libutils",
"liblog",
],
cflags: [
"-Werror",
"-Wall",
],
}

@ -0,0 +1,242 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// #define LOG_NDEBUG 0
#define LOG_TAG "BatteryChecker_test"
#include <utils/Log.h>
#include <gtest/gtest.h>
#include <media/stagefright/BatteryChecker.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AHandler.h>
#include <vector>
namespace android {
static const int kBatteryTimeoutUs = 1000000ll; // 1 seconds
static const int kTestMarginUs = 50000ll; // 50ms
static const int kWaitStatusChangeUs = kBatteryTimeoutUs + kTestMarginUs;
static const int kSparseFrameIntervalUs = kBatteryTimeoutUs - kTestMarginUs;
class BatteryCheckerTestHandler : public AHandler {
enum EventType {
// Events simulating MediaCodec
kWhatStart = 0, // codec entering executing state
kWhatStop, // codec exiting executing state
kWhatActivity, // codec queue input or dequeue output
kWhatReleased, // codec released
kWhatCheckpoint, // test checkpoing with expected values on On/Off
// Message for battery checker monitor (not for testing through runTest())
kWhatBatteryChecker,
};
struct Operation {
int32_t event;
int64_t delay = 0;
uint32_t repeatCount = 0;
int32_t expectedOnCounter = 0;
int32_t expectedOffCounter = 0;
};
std::vector<Operation> mOps;
sp<BatteryChecker> mBatteryChecker;
int32_t mOnCounter;
int32_t mOffCounter;
Condition mDone;
Mutex mLock;
BatteryCheckerTestHandler() : mOnCounter(0), mOffCounter(0) {}
void runTest(const std::vector<Operation> &ops, int64_t timeoutUs) {
mOps = ops;
mBatteryChecker = new BatteryChecker(
new AMessage(kWhatBatteryChecker, this), kBatteryTimeoutUs);
(new AMessage(ops[0].event, this))->post();
// wait for done
AutoMutex lock(mLock);
EXPECT_NE(TIMED_OUT, mDone.waitRelative(mLock, timeoutUs * 1000ll));
}
virtual void onMessageReceived(const sp<AMessage> &msg);
friend class BatteryCheckerTest;
};
class BatteryCheckerTest : public ::testing::Test {
public:
BatteryCheckerTest()
: mLooper(new ALooper)
, mHandler(new BatteryCheckerTestHandler()) {
mLooper->setName("BatterCheckerLooper");
mLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mLooper->registerHandler(mHandler);
}
protected:
using EventType = BatteryCheckerTestHandler::EventType;
using Operation = BatteryCheckerTestHandler::Operation;
virtual ~BatteryCheckerTest() {
mLooper->stop();
mLooper->unregisterHandler(mHandler->id());
}
void runTest(const std::vector<Operation> &ops, int64_t timeoutUs) {
mHandler->runTest(ops, timeoutUs);
}
sp<ALooper> mLooper;
sp<BatteryCheckerTestHandler> mHandler;
};
void BatteryCheckerTestHandler::onMessageReceived(const sp<AMessage> &msg) {
switch(msg->what()) {
case kWhatStart:
mBatteryChecker->setExecuting(true);
break;
case kWhatStop:
mBatteryChecker->setExecuting(false);
break;
case kWhatActivity:
mBatteryChecker->onCodecActivity([this] () { mOnCounter++; });
break;
case kWhatReleased:
mBatteryChecker->onClientRemoved();
break;
case kWhatBatteryChecker:
mBatteryChecker->onCheckBatteryTimer(msg, [this] () { mOffCounter++; });
break;
case kWhatCheckpoint:
// verify ON/OFF state and total events
EXPECT_EQ(mOnCounter, mOps[0].expectedOnCounter);
EXPECT_EQ(mOffCounter, mOps[0].expectedOffCounter);
break;
default:
TRESPASS();
}
if (msg->what() != kWhatBatteryChecker) {
EXPECT_EQ(msg->what(), mOps[0].event);
// post next message
if (!mOps[0].repeatCount) {
mOps.erase(mOps.begin());
} else {
mOps[0].repeatCount--;
}
int64_t duration = mOps[0].delay;
if (!mOps.empty()) {
(new AMessage(mOps[0].event, this))->post(duration);
} else {
AutoMutex lock(mLock);
mDone.signal();
}
}
}
TEST_F(BatteryCheckerTest, testNormalOperations) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0}, // ON
{EventType::kWhatActivity, 33333ll, 2*kWaitStatusChangeUs/33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 1, 1}, // OFF
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testPauseResume) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0}, // ON
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 1, 1}, // OFF
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 2, 1}, // ON
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 2, 2}, // OFF
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testClientRemovedAndRestart) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatActivity, 33333ll, kWaitStatusChangeUs/33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// stop executing state itself shouldn't trigger any calls
{EventType::kWhatStop, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// release shouldn't trigger any calls either,
// client resource will be removed entirely
{EventType::kWhatReleased, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 1, 0},
// start pushing buffers again, On should be received without any Off
{EventType::kWhatStart, 0ll},
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 2, 0},
// double check that only new checker msg triggers OFF,
// left-over checker msg from stale generate discarded
{EventType::kWhatCheckpoint, kWaitStatusChangeUs, 0, 2, 1},
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testActivityWhileNotExecuting) {
runTest({
// activity before start shouldn't trigger
{EventType::kWhatActivity, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
// activity after start before stop should trigger
{EventType::kWhatActivity, 33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// stop executing state itself shouldn't trigger any calls
{EventType::kWhatStop, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
// keep pushing another 3 seconds after stop, expected to OFF
{EventType::kWhatActivity, 33333ll, kWaitStatusChangeUs/33333ll},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 1},
}, 10000000ll);
}
TEST_F(BatteryCheckerTest, testSparseActivity) {
runTest({
{EventType::kWhatStart, 0ll},
{EventType::kWhatCheckpoint, 0ll, 0, 0, 0},
// activity arrives sparsely with interval only slightly small than timeout
// should only trigger 1 ON
{EventType::kWhatActivity, kSparseFrameIntervalUs, 2},
{EventType::kWhatCheckpoint, 0ll, 0, 1, 0},
{EventType::kWhatCheckpoint, kSparseFrameIntervalUs, 0, 1, 0},
{EventType::kWhatCheckpoint, kTestMarginUs, 0, 1, 1}, // OFF
}, 10000000ll);
}
} // namespace android

@ -138,7 +138,12 @@ package media.codecs {
public class Variant {
ctor public Variant();
method public java.util.List<media.codecs.Alias> getAlias_optional();
method public java.util.List<media.codecs.Quirk> getAttribute_optional();
method public java.util.List<media.codecs.Feature> getFeature_optional();
method public java.util.List<media.codecs.Limit> getLimit_optional();
method public String getName();
method public java.util.List<media.codecs.Quirk> getQuirk_optional();
method public void setName(String);
}

@ -107,6 +107,13 @@
<xs:attribute name="value" type="xs:string"/>
</xs:complexType>
<xs:complexType name="Variant">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Quirk" type="Quirk"/>
<xs:element name="Attribute" type="Quirk"/>
<xs:element name="Alias" type="Alias"/>
<xs:element name="Limit" type="Limit"/>
<xs:element name="Feature" type="Feature"/>
</xs:choice>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
<xs:complexType name="Setting">

@ -221,7 +221,13 @@ void CodecHandler::onMessageReceived(const sp<AMessage> &msg) {
break;
}
AMediaFormat *aMediaFormat = AMediaFormat_fromMsg(&format);
// Here format is MediaCodec's internal copy of output format.
// Make a copy since the client might modify it.
sp<AMessage> copy;
if (format != nullptr) {
copy = format->dup();
}
AMediaFormat *aMediaFormat = AMediaFormat_fromMsg(&copy);
Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
if (mCodec->mAsyncCallbackUserData != NULL

@ -89,7 +89,7 @@ struct AMediaDrm {
};
void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
if (!mEventListener && !mExpirationUpdateListener && !mKeysChangeListener) {
if (!mEventListener || !mExpirationUpdateListener || !mKeysChangeListener) {
ALOGE("No listeners are specified");
return;
}

@ -30,6 +30,7 @@ cc_library {
],
shared_libs: [
"libbinder",
"libcutils",
"liblog",
"libutils",
"libhidlbase",

@ -63,7 +63,10 @@ static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
uid_t uid, bool start) {
// Okay to not track in app ops as audio server is us and if
// device is rooted security model is considered compromised.
if (isAudioServerOrRootUid(uid)) return true;
// system_server loses its RECORD_AUDIO permission when a secondary
// user is active, but it is a core system service so let it through.
// TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0
if (isAudioServerOrSystemServerOrRootUid(uid)) return true;
// We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
// may open a record track on behalf of a client. Note that pid may be a tid.

@ -58,6 +58,12 @@ static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
}
// used for calls that should come from system_server or audio_server and
// include AID_ROOT for command-line tests.
static inline bool isAudioServerOrSystemServerOrRootUid(uid_t uid) {
return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER || uid == AID_ROOT;
}
// Mediaserver may forward the client PID and UID as part of a binder interface call;
// otherwise the calling UID must be equal to the client UID.
static inline bool isAudioServerOrMediaServerUid(uid_t uid) {

@ -725,11 +725,11 @@ status_t AudioPolicyService::shellCommand(int in, int out, int err, Vector<Strin
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
if (args.size() == 3 && args[0] == String16("set-uid-state")) {
if (args.size() >= 3 && args[0] == String16("set-uid-state")) {
return handleSetUidState(args, err);
} else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
} else if (args.size() >= 2 && args[0] == String16("reset-uid-state")) {
return handleResetUidState(args, err);
} else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
} else if (args.size() >= 2 && args[0] == String16("get-uid-state")) {
return handleGetUidState(args, out, err);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
@ -739,14 +739,32 @@ status_t AudioPolicyService::shellCommand(int in, int out, int err, Vector<Strin
return BAD_VALUE;
}
status_t AudioPolicyService::handleSetUidState(Vector<String16>& args, int err) {
static status_t getUidForPackage(String16 packageName, int userId, /*inout*/uid_t& uid, int err) {
if (userId < 0) {
ALOGE("Invalid user: %d", userId);
dprintf(err, "Invalid user: %d\n", userId);
return BAD_VALUE;
}
PermissionController pc;
int uid = pc.getPackageUid(args[1], 0);
uid = pc.getPackageUid(packageName, 0);
if (uid <= 0) {
ALOGE("Unknown package: '%s'", String8(args[1]).string());
dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
ALOGE("Unknown package: '%s'", String8(packageName).string());
dprintf(err, "Unknown package: '%s'\n", String8(packageName).string());
return BAD_VALUE;
}
uid = multiuser_get_uid(userId, uid);
return NO_ERROR;
}
status_t AudioPolicyService::handleSetUidState(Vector<String16>& args, int err) {
// Valid arg.size() is 3 or 5, args.size() is 5 with --user option.
if (!(args.size() == 3 || args.size() == 5)) {
printHelp(err);
return BAD_VALUE;
}
bool active = false;
if (args[2] == String16("active")) {
active = true;
@ -754,30 +772,59 @@ status_t AudioPolicyService::handleSetUidState(Vector<String16>& args, int err)
ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
return BAD_VALUE;
}
int userId = 0;
if (args.size() >= 5 && args[3] == String16("--user")) {
userId = atoi(String8(args[4]));
}
uid_t uid;
if (getUidForPackage(args[1], userId, uid, err) == BAD_VALUE) {
return BAD_VALUE;
}
mUidPolicy->addOverrideUid(uid, active);
return NO_ERROR;
}
status_t AudioPolicyService::handleResetUidState(Vector<String16>& args, int err) {
PermissionController pc;
int uid = pc.getPackageUid(args[1], 0);
if (uid < 0) {
ALOGE("Unknown package: '%s'", String8(args[1]).string());
dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
// Valid arg.size() is 2 or 4, args.size() is 4 with --user option.
if (!(args.size() == 2 || args.size() == 4)) {
printHelp(err);
return BAD_VALUE;
}
int userId = 0;
if (args.size() >= 4 && args[2] == String16("--user")) {
userId = atoi(String8(args[3]));
}
uid_t uid;
if (getUidForPackage(args[1], userId, uid, err) == BAD_VALUE) {
return BAD_VALUE;
}
mUidPolicy->removeOverrideUid(uid);
return NO_ERROR;
}
status_t AudioPolicyService::handleGetUidState(Vector<String16>& args, int out, int err) {
PermissionController pc;
int uid = pc.getPackageUid(args[1], 0);
if (uid < 0) {
ALOGE("Unknown package: '%s'", String8(args[1]).string());
dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
// Valid arg.size() is 2 or 4, args.size() is 4 with --user option.
if (!(args.size() == 2 || args.size() == 4)) {
printHelp(err);
return BAD_VALUE;
}
int userId = 0;
if (args.size() >= 4 && args[2] == String16("--user")) {
userId = atoi(String8(args[3]));
}
uid_t uid;
if (getUidForPackage(args[1], userId, uid, err) == BAD_VALUE) {
return BAD_VALUE;
}
if (mUidPolicy->isUidActive(uid)) {
return dprintf(out, "active\n");
} else {
@ -787,9 +834,9 @@ status_t AudioPolicyService::handleGetUidState(Vector<String16>& args, int out,
status_t AudioPolicyService::printHelp(int out) {
return dprintf(out, "Audio policy service commands:\n"
" get-uid-state <PACKAGE> gets the uid state\n"
" set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
" reset-uid-state <PACKAGE> clears the uid state override\n"
" get-uid-state <PACKAGE> [--user USER_ID] gets the uid state\n"
" set-uid-state <PACKAGE> <active|idle> [--user USER_ID] overrides the uid state\n"
" reset-uid-state <PACKAGE> [--user USER_ID] clears the uid state override\n"
" help print this message\n");
}

@ -153,8 +153,6 @@ void CameraService::onFirstRef()
mInitialized = true;
}
CameraService::pingCameraServiceProxy();
mUidPolicy = new UidPolicy(this);
mUidPolicy->registerSelf();
mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
@ -164,6 +162,11 @@ void CameraService::onFirstRef()
ALOGE("%s: Failed to register default android.frameworks.cameraservice.service@1.0",
__FUNCTION__);
}
// This needs to be last call in this function, so that it's as close to
// ServiceManager::addService() as possible.
CameraService::pingCameraServiceProxy();
ALOGI("CameraService pinged cameraservice proxy");
}
status_t CameraService::enumerateProviders() {

@ -1671,8 +1671,13 @@ void HeicCompositeStream::CodecCallbackHandler::onMessageReceived(const sp<AMess
ALOGE("CB_OUTPUT_FORMAT_CHANGED: format is expected.");
break;
}
parent->onHeicFormatChanged(format);
// Here format is MediaCodec's internal copy of output format.
// Make a copy since onHeicFormatChanged() might modify it.
sp<AMessage> formatCopy;
if (format != nullptr) {
formatCopy = format->dup();
}
parent->onHeicFormatChanged(formatCopy);
break;
}

@ -29,6 +29,7 @@
#include <future>
#include <inttypes.h>
#include <hardware/camera_common.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/ServiceManagement.h>
#include <functional>
#include <camera_metadata_hidden.h>
@ -47,10 +48,6 @@ using namespace ::android::hardware::camera::common::V1_0;
using std::literals::chrono_literals::operator""s;
namespace {
// Hardcoded name for the passthrough HAL implementation, since it can't be discovered via the
// service manager
const std::string kLegacyProviderName("legacy/0");
const std::string kExternalProviderName("external/0");
const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false));
} // anonymous namespace
@ -62,6 +59,19 @@ CameraProviderManager::sHardwareServiceInteractionProxy{};
CameraProviderManager::~CameraProviderManager() {
}
hardware::hidl_vec<hardware::hidl_string>
CameraProviderManager::HardwareServiceInteractionProxy::listServices() {
hardware::hidl_vec<hardware::hidl_string> ret;
auto manager = hardware::defaultServiceManager1_2();
if (manager != nullptr) {
manager->listManifestByInterface(provider::V2_4::ICameraProvider::descriptor,
[&ret](const hardware::hidl_vec<hardware::hidl_string> &registered) {
ret = registered;
});
}
return ret;
}
status_t CameraProviderManager::initialize(wp<CameraProviderManager::StatusListener> listener,
ServiceInteractionProxy* proxy) {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
@ -84,9 +94,10 @@ status_t CameraProviderManager::initialize(wp<CameraProviderManager::StatusListe
return INVALID_OPERATION;
}
// See if there's a passthrough HAL, but let's not complain if there's not
addProviderLocked(kLegacyProviderName, /*expected*/ false);
addProviderLocked(kExternalProviderName, /*expected*/ false);
for (const auto& instance : mServiceProxy->listServices()) {
this->addProviderLocked(instance);
}
IPCThreadState::self()->flushCommands();
@ -1116,7 +1127,7 @@ CameraProviderManager::isHiddenPhysicalCameraInternal(const std::string& cameraI
return falseRet;
}
status_t CameraProviderManager::addProviderLocked(const std::string& newProvider, bool expected) {
status_t CameraProviderManager::addProviderLocked(const std::string& newProvider) {
for (const auto& providerInfo : mProviders) {
if (providerInfo->mProviderName == newProvider) {
ALOGW("%s: Camera provider HAL with name '%s' already registered", __FUNCTION__,
@ -1129,13 +1140,9 @@ status_t CameraProviderManager::addProviderLocked(const std::string& newProvider
interface = mServiceProxy->getService(newProvider);
if (interface == nullptr) {
if (expected) {
ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
newProvider.c_str());
return BAD_VALUE;
} else {
return OK;
}
ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
newProvider.c_str());
return BAD_VALUE;
}
sp<ProviderInfo> providerInfo = new ProviderInfo(newProvider, this);

@ -78,6 +78,7 @@ public:
&notification) = 0;
virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
const std::string &serviceName) = 0;
virtual hardware::hidl_vec<hardware::hidl_string> listServices() = 0;
virtual ~ServiceInteractionProxy() {}
};
@ -95,6 +96,8 @@ public:
const std::string &serviceName) override {
return hardware::camera::provider::V2_4::ICameraProvider::getService(serviceName);
}
virtual hardware::hidl_vec<hardware::hidl_string> listServices() override;
};
/**
@ -270,6 +273,7 @@ public:
bool isLogicalCamera(const std::string& id, std::vector<std::string>* physicalCameraIds);
bool isPublicallyHiddenSecureCamera(const std::string& id) const;
bool isHiddenPhysicalCamera(const std::string& cameraId) const;
static const float kDepthARTolerance;
@ -567,7 +571,7 @@ private:
hardware::hidl_version minVersion = hardware::hidl_version{0,0},
hardware::hidl_version maxVersion = hardware::hidl_version{1000,0}) const;
status_t addProviderLocked(const std::string& newProvider, bool expected = true);
status_t addProviderLocked(const std::string& newProvider);
status_t removeProvider(const std::string& provider);
sp<StatusListener> getStatusListener() const;

@ -205,6 +205,11 @@ struct TestInteractionProxy : public CameraProviderManager::ServiceInteractionPr
return mTestCameraProvider;
}
virtual hardware::hidl_vec<hardware::hidl_string> listServices() override {
hardware::hidl_vec<hardware::hidl_string> ret = {"test/0"};
return ret;
}
};
struct TestStatusListener : public CameraProviderManager::StatusListener {
@ -231,37 +236,24 @@ TEST(CameraProviderManagerTest, InitializeTest) {
vendorSection);
serviceProxy.setProvider(provider);
int numProviders = static_cast<int>(serviceProxy.listServices().size());
res = providerManager->initialize(statusListener, &serviceProxy);
ASSERT_EQ(res, OK) << "Unable to initialize provider manager";
// Check that both "legacy" and "external" providers (really the same object) are called
// once for all the init methods
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], 2) <<
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], numProviders) <<
"Only one call to setCallback per provider expected during init";
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], 2) <<
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], numProviders) <<
"Only one call to getVendorTags per provider expected during init";
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED], 2) <<
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED],
numProviders) <<
"Only one call to isSetTorchModeSupported per provider expected during init";
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], 2) <<
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], numProviders) <<
"Only one call to getCameraIdList per provider expected during init";
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], 2) <<
EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], numProviders) <<
"Only one call to notifyDeviceState per provider expected during init";
std::string legacyInstanceName = "legacy/0";
std::string externalInstanceName = "external/0";
bool gotLegacy = false;
bool gotExternal = false;
EXPECT_EQ(2u, serviceProxy.mLastRequestedServiceNames.size()) <<
"Only two service queries expected to be seen by hardware service manager";
for (auto& serviceName : serviceProxy.mLastRequestedServiceNames) {
if (serviceName == legacyInstanceName) gotLegacy = true;
if (serviceName == externalInstanceName) gotExternal = true;
}
ASSERT_TRUE(gotLegacy) <<
"Legacy instance not requested from service manager";
ASSERT_TRUE(gotExternal) <<
"External instance not requested from service manager";
hardware::hidl_string testProviderFqInterfaceName =
"android.hardware.camera.provider@2.4::ICameraProvider";
hardware::hidl_string testProviderInstanceName = "test/0";

@ -42,7 +42,8 @@ cc_library_shared {
"libcodec2_soft_opusenc",
"libcodec2_soft_vp8dec",
"libcodec2_soft_vp9dec",
"libcodec2_soft_av1dec",
// "libcodec2_soft_av1dec_aom", // replaced by the gav1 implementation
"libcodec2_soft_av1dec_gav1",
"libcodec2_soft_vp8enc",
"libcodec2_soft_vp9enc",
"libcodec2_soft_rawdec",

@ -21,8 +21,11 @@
#include <binder/IMediaResourceMonitor.h>
#include <binder/IServiceManager.h>
#include <cutils/sched_policy.h>
#include <dirent.h>
#include <media/stagefright/ProcessInfo.h>
#include <mediautils/BatteryNotifier.h>
#include <mediautils/SchedulingPolicyService.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -31,8 +34,7 @@
#include "ResourceManagerService.h"
#include "ServiceLog.h"
#include "mediautils/SchedulingPolicyService.h"
#include <cutils/sched_policy.h>
namespace android {
namespace {
@ -69,9 +71,9 @@ static String8 getString(const Vector<T> &items) {
return itemsStr;
}
static bool hasResourceType(MediaResource::Type type, const Vector<MediaResource>& resources) {
for (size_t i = 0; i < resources.size(); ++i) {
if (resources[i].mType == type) {
static bool hasResourceType(MediaResource::Type type, const ResourceList& resources) {
for (auto it = resources.begin(); it != resources.end(); it++) {
if (it->second.mType == type) {
return true;
}
}
@ -101,20 +103,22 @@ static ResourceInfos& getResourceInfosForEdit(
}
static ResourceInfo& getResourceInfoForEdit(
uid_t uid,
int64_t clientId,
const sp<IResourceManagerClient>& client,
ResourceInfos& infos) {
for (size_t i = 0; i < infos.size(); ++i) {
if (infos[i].clientId == clientId) {
return infos.editItemAt(i);
}
ssize_t index = infos.indexOfKey(clientId);
if (index < 0) {
ResourceInfo info;
info.uid = uid;
info.clientId = clientId;
info.client = client;
index = infos.add(clientId, info);
}
ResourceInfo info;
info.clientId = clientId;
info.client = client;
info.cpuBoost = false;
infos.push_back(info);
return infos.editItemAt(infos.size() - 1);
return infos.editValueAt(index);
}
static void notifyResourceGranted(int pid, const Vector<MediaResource> &resources) {
@ -181,10 +185,10 @@ status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */
snprintf(buffer, SIZE, " Name: %s\n", infos[j].client->getName().string());
result.append(buffer);
Vector<MediaResource> resources = infos[j].resources;
const ResourceList &resources = infos[j].resources;
result.append(" Resources:\n");
for (size_t k = 0; k < resources.size(); ++k) {
snprintf(buffer, SIZE, " %s\n", resources[k].toString().string());
for (auto it = resources.begin(); it != resources.end(); it++) {
snprintf(buffer, SIZE, " %s\n", it->second.toString().string());
result.append(buffer);
}
}
@ -196,15 +200,45 @@ status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */
return OK;
}
struct SystemCallbackImpl :
public ResourceManagerService::SystemCallbackInterface {
SystemCallbackImpl() {}
virtual void noteStartVideo(int uid) override {
BatteryNotifier::getInstance().noteStartVideo(uid);
}
virtual void noteStopVideo(int uid) override {
BatteryNotifier::getInstance().noteStopVideo(uid);
}
virtual void noteResetVideo() override {
BatteryNotifier::getInstance().noteResetVideo();
}
virtual bool requestCpusetBoost(
bool enable, const sp<IInterface> &client) override {
return android::requestCpusetBoost(enable, client);
}
protected:
virtual ~SystemCallbackImpl() {}
private:
DISALLOW_EVIL_CONSTRUCTORS(SystemCallbackImpl);
};
ResourceManagerService::ResourceManagerService()
: ResourceManagerService(new ProcessInfo()) {}
: ResourceManagerService(new ProcessInfo(), new SystemCallbackImpl()) {}
ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo)
ResourceManagerService::ResourceManagerService(
const sp<ProcessInfoInterface> &processInfo,
const sp<SystemCallbackInterface> &systemResource)
: mProcessInfo(processInfo),
mSystemCB(systemResource),
mServiceLog(new ServiceLog()),
mSupportsMultipleSecureCodecs(true),
mSupportsSecureWithNonSecureCodec(true),
mCpuBoostCount(0) {}
mCpuBoostCount(0) {
mSystemCB->noteResetVideo();
}
ResourceManagerService::~ResourceManagerService() {}
@ -224,8 +258,41 @@ void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies)
}
}
void ResourceManagerService::onFirstAdded(
const MediaResource& resource, const ResourceInfo& clientInfo) {
// first time added
if (resource.mType == MediaResource::kCpuBoost
&& resource.mSubType == MediaResource::kUnspecifiedSubType) {
// Request it on every new instance of kCpuBoost, as the media.codec
// could have died, if we only do it the first time subsequent instances
// never gets the boost.
if (mSystemCB->requestCpusetBoost(true, this) != OK) {
ALOGW("couldn't request cpuset boost");
}
mCpuBoostCount++;
} else if (resource.mType == MediaResource::kBattery
&& resource.mSubType == MediaResource::kVideoCodec) {
mSystemCB->noteStartVideo(clientInfo.uid);
}
}
void ResourceManagerService::onLastRemoved(
const MediaResource& resource, const ResourceInfo& clientInfo) {
if (resource.mType == MediaResource::kCpuBoost
&& resource.mSubType == MediaResource::kUnspecifiedSubType
&& mCpuBoostCount > 0) {
if (--mCpuBoostCount == 0) {
mSystemCB->requestCpusetBoost(false, this);
}
} else if (resource.mType == MediaResource::kBattery
&& resource.mSubType == MediaResource::kVideoCodec) {
mSystemCB->noteStopVideo(clientInfo.uid);
}
}
void ResourceManagerService::addResource(
int pid,
int uid,
int64_t clientId,
const sp<IResourceManagerClient> client,
const Vector<MediaResource> &resources) {
@ -239,20 +306,15 @@ void ResourceManagerService::addResource(
return;
}
ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos);
// TODO: do the merge instead of append.
info.resources.appendVector(resources);
ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos);
for (size_t i = 0; i < resources.size(); ++i) {
if (resources[i].mType == MediaResource::kCpuBoost && !info.cpuBoost) {
info.cpuBoost = true;
// Request it on every new instance of kCpuBoost, as the media.codec
// could have died, if we only do it the first time subsequent instances
// never gets the boost.
if (requestCpusetBoost(true, this) != OK) {
ALOGW("couldn't request cpuset boost");
}
mCpuBoostCount++;
const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
if (info.resources.find(resType) == info.resources.end()) {
onFirstAdded(resources[i], info);
info.resources[resType] = resources[i];
} else {
info.resources[resType].mValue += resources[i].mValue;
}
}
if (info.deathNotifier == nullptr) {
@ -262,7 +324,48 @@ void ResourceManagerService::addResource(
notifyResourceGranted(pid, resources);
}
void ResourceManagerService::removeResource(int pid, int64_t clientId) {
void ResourceManagerService::removeResource(int pid, int64_t clientId,
const Vector<MediaResource> &resources) {
String8 log = String8::format("removeResource(pid %d, clientId %lld, resources %s)",
pid, (long long) clientId, getString(resources).string());
mServiceLog->add(log);
Mutex::Autolock lock(mLock);
if (!mProcessInfo->isValidPid(pid)) {
ALOGE("Rejected removeResource call with invalid pid.");
return;
}
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
return;
}
ResourceInfos &infos = mMap.editValueAt(index);
index = infos.indexOfKey(clientId);
if (index < 0) {
ALOGV("removeResource: didn't find clientId %lld", (long long) clientId);
return;
}
ResourceInfo &info = infos.editValueAt(index);
for (size_t i = 0; i < resources.size(); ++i) {
const auto resType = std::make_pair(resources[i].mType, resources[i].mSubType);
// ignore if we don't have it
if (info.resources.find(resType) != info.resources.end()) {
MediaResource &resource = info.resources[resType];
if (resource.mValue > resources[i].mValue) {
resource.mValue -= resources[i].mValue;
} else {
onLastRemoved(resources[i], info);
info.resources.erase(resType);
}
}
}
}
void ResourceManagerService::removeClient(int pid, int64_t clientId) {
removeResource(pid, clientId, true);
}
@ -282,24 +385,22 @@ void ResourceManagerService::removeResource(int pid, int64_t clientId, bool chec
ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
return;
}
bool found = false;
ResourceInfos &infos = mMap.editValueAt(index);
for (size_t j = 0; j < infos.size(); ++j) {
if (infos[j].clientId == clientId) {
if (infos[j].cpuBoost && mCpuBoostCount > 0) {
if (--mCpuBoostCount == 0) {
requestCpusetBoost(false, this);
}
}
IInterface::asBinder(infos[j].client)->unlinkToDeath(infos[j].deathNotifier);
j = infos.removeAt(j);
found = true;
break;
}
index = infos.indexOfKey(clientId);
if (index < 0) {
ALOGV("removeResource: didn't find clientId %lld", (long long) clientId);
return;
}
if (!found) {
ALOGV("didn't find client");
const ResourceInfo &info = infos[index];
for (auto it = info.resources.begin(); it != info.resources.end(); it++) {
onLastRemoved(it->second, info);
}
IInterface::asBinder(info.client)->unlinkToDeath(info.deathNotifier);
infos.removeItemsAt(index);
}
void ResourceManagerService::getClientForResource_l(
@ -410,7 +511,7 @@ bool ResourceManagerService::reclaimResource(
ResourceInfos &infos = mMap.editValueAt(i);
for (size_t j = 0; j < infos.size();) {
if (infos[j].client == failedClient) {
j = infos.removeAt(j);
j = infos.removeItemsAt(j);
found = true;
} else {
++j;
@ -538,11 +639,12 @@ bool ResourceManagerService::getBiggestClient_l(
uint64_t largestValue = 0;
const ResourceInfos &infos = mMap.valueAt(index);
for (size_t i = 0; i < infos.size(); ++i) {
Vector<MediaResource> resources = infos[i].resources;
for (size_t j = 0; j < resources.size(); ++j) {
if (resources[j].mType == type) {
if (resources[j].mValue > largestValue) {
largestValue = resources[j].mValue;
const ResourceList &resources = infos[i].resources;
for (auto it = resources.begin(); it != resources.end(); it++) {
const MediaResource &resource = it->second;
if (resource.mType == type) {
if (resource.mValue > largestValue) {
largestValue = resource.mValue;
clientTemp = infos[i].client;
}
}

@ -33,15 +33,17 @@ namespace android {
class ServiceLog;
struct ProcessInfoInterface;
typedef std::map<std::pair<MediaResource::Type, MediaResource::SubType>, MediaResource> ResourceList;
struct ResourceInfo {
int64_t clientId;
uid_t uid;
sp<IResourceManagerClient> client;
sp<IBinder::DeathRecipient> deathNotifier;
Vector<MediaResource> resources;
bool cpuBoost;
ResourceList resources;
};
typedef Vector<ResourceInfo> ResourceInfos;
// TODO: convert these to std::map
typedef KeyedVector<int64_t, ResourceInfo> ResourceInfos;
typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap;
class ResourceManagerService
@ -49,23 +51,37 @@ class ResourceManagerService
public BnResourceManagerService
{
public:
struct SystemCallbackInterface : public RefBase {
virtual void noteStartVideo(int uid) = 0;
virtual void noteStopVideo(int uid) = 0;
virtual void noteResetVideo() = 0;
virtual bool requestCpusetBoost(
bool enable, const sp<IInterface> &client) = 0;
};
static char const *getServiceName() { return "media.resource_manager"; }
virtual status_t dump(int fd, const Vector<String16>& args);
ResourceManagerService();
explicit ResourceManagerService(sp<ProcessInfoInterface> processInfo);
explicit ResourceManagerService(
const sp<ProcessInfoInterface> &processInfo,
const sp<SystemCallbackInterface> &systemResource);
// IResourceManagerService interface
virtual void config(const Vector<MediaResourcePolicy> &policies);
virtual void addResource(
int pid,
int uid,
int64_t clientId,
const sp<IResourceManagerClient> client,
const Vector<MediaResource> &resources);
virtual void removeResource(int pid, int64_t clientId);
virtual void removeResource(int pid, int64_t clientId,
const Vector<MediaResource> &resources);
virtual void removeClient(int pid, int64_t clientId);
// Tries to reclaim resource from processes with lower priority than the calling process
// according to the requested resources.
@ -107,8 +123,12 @@ private:
void getClientForResource_l(
int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients);
void onFirstAdded(const MediaResource& res, const ResourceInfo& clientInfo);
void onLastRemoved(const MediaResource& res, const ResourceInfo& clientInfo);
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
sp<SystemCallbackInterface> mSystemCB;
sp<ServiceLog> mServiceLog;
PidResourceInfosMap mMap;
bool mSupportsMultipleSecureCodecs;

@ -0,0 +1,10 @@
{
"presubmit": [
{
"name": "ResourceManagerService_test"
},
{
"name": "ServiceLog_test"
}
]
}

@ -2,6 +2,7 @@
cc_test {
name: "ResourceManagerService_test",
srcs: ["ResourceManagerService_test.cpp"],
test_suites: ["device-tests"],
shared_libs: [
"libbinder",
"liblog",
@ -23,6 +24,7 @@ cc_test {
cc_test {
name: "ServiceLog_test",
srcs: ["ServiceLog_test.cpp"],
test_suites: ["device-tests"],
shared_libs: [
"liblog",
"libmedia",

@ -52,13 +52,69 @@ private:
DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo);
};
struct TestSystemCallback :
public ResourceManagerService::SystemCallbackInterface {
TestSystemCallback() :
mLastEvent({EventType::INVALID, 0}), mEventCount(0) {}
enum EventType {
INVALID = -1,
VIDEO_ON = 0,
VIDEO_OFF = 1,
VIDEO_RESET = 2,
CPUSET_ENABLE = 3,
CPUSET_DISABLE = 4,
};
struct EventEntry {
EventType type;
int arg;
};
virtual void noteStartVideo(int uid) override {
mLastEvent = {EventType::VIDEO_ON, uid};
mEventCount++;
}
virtual void noteStopVideo(int uid) override {
mLastEvent = {EventType::VIDEO_OFF, uid};
mEventCount++;
}
virtual void noteResetVideo() override {
mLastEvent = {EventType::VIDEO_RESET, 0};
mEventCount++;
}
virtual bool requestCpusetBoost(
bool enable, const sp<IInterface> &/*client*/) override {
mLastEvent = {enable ? EventType::CPUSET_ENABLE : EventType::CPUSET_DISABLE, 0};
mEventCount++;
return true;
}
size_t eventCount() { return mEventCount; }
EventType lastEventType() { return mLastEvent.type; }
EventEntry lastEvent() { return mLastEvent; }
protected:
virtual ~TestSystemCallback() {}
private:
EventEntry mLastEvent;
size_t mEventCount;
DISALLOW_EVIL_CONSTRUCTORS(TestSystemCallback);
};
struct TestClient : public BnResourceManagerClient {
TestClient(int pid, sp<ResourceManagerService> service)
: mReclaimed(false), mPid(pid), mService(service) {}
virtual bool reclaimResource() {
sp<IResourceManagerClient> client(this);
mService->removeResource(mPid, (int64_t) client.get());
mService->removeClient(mPid, (int64_t) client.get());
mReclaimed = true;
return true;
}
@ -86,16 +142,26 @@ private:
};
static const int kTestPid1 = 30;
static const int kTestUid1 = 1010;
static const int kTestPid2 = 20;
static const int kTestUid2 = 1011;
static const int kLowPriorityPid = 40;
static const int kMidPriorityPid = 25;
static const int kHighPriorityPid = 10;
using EventType = TestSystemCallback::EventType;
using EventEntry = TestSystemCallback::EventEntry;
bool operator== (const EventEntry& lhs, const EventEntry& rhs) {
return lhs.type == rhs.type && lhs.arg == rhs.arg;
}
class ResourceManagerServiceTest : public ::testing::Test {
public:
ResourceManagerServiceTest()
: mService(new ResourceManagerService(new TestProcessInfo)),
: mSystemCB(new TestSystemCallback()),
mService(new ResourceManagerService(new TestProcessInfo, mSystemCB)),
mTestClient1(new TestClient(kTestPid1, mService)),
mTestClient2(new TestClient(kTestPid2, mService)),
mTestClient3(new TestClient(kTestPid2, mService)) {
@ -103,20 +169,21 @@ public:
protected:
static bool isEqualResources(const Vector<MediaResource> &resources1,
const Vector<MediaResource> &resources2) {
if (resources1.size() != resources2.size()) {
return false;
}
const ResourceList &resources2) {
// convert resource1 to ResourceList
ResourceList r1;
for (size_t i = 0; i < resources1.size(); ++i) {
if (resources1[i] != resources2[i]) {
return false;
}
const auto resType = std::make_pair(resources1[i].mType, resources1[i].mSubType);
r1[resType] = resources1[i];
}
return true;
return r1 == resources2;
}
static void expectEqResourceInfo(const ResourceInfo &info, sp<IResourceManagerClient> client,
static void expectEqResourceInfo(const ResourceInfo &info,
int uid,
sp<IResourceManagerClient> client,
const Vector<MediaResource> &resources) {
EXPECT_EQ(uid, info.uid);
EXPECT_EQ(client, info.client);
EXPECT_TRUE(isEqualResources(resources, info.resources));
}
@ -153,24 +220,24 @@ protected:
// kTestPid1 mTestClient1
Vector<MediaResource> resources1;
resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
mService->addResource(kTestPid1, getId(mTestClient1), mTestClient1, resources1);
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
resources1.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
Vector<MediaResource> resources11;
resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
mService->addResource(kTestPid1, getId(mTestClient1), mTestClient1, resources11);
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
// kTestPid2 mTestClient2
Vector<MediaResource> resources2;
resources2.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
resources2.push_back(MediaResource(MediaResource::kGraphicMemory, 300));
mService->addResource(kTestPid2, getId(mTestClient2), mTestClient2, resources2);
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
// kTestPid2 mTestClient3
Vector<MediaResource> resources3;
mService->addResource(kTestPid2, getId(mTestClient3), mTestClient3, resources3);
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources3);
resources3.push_back(MediaResource(MediaResource::kSecureCodec, 1));
resources3.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
mService->addResource(kTestPid2, getId(mTestClient3), mTestClient3, resources3);
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient3), mTestClient3, resources3);
const PidResourceInfosMap &map = mService->mMap;
EXPECT_EQ(2u, map.size());
@ -178,14 +245,14 @@ protected:
ASSERT_GE(index1, 0);
const ResourceInfos &infos1 = map[index1];
EXPECT_EQ(1u, infos1.size());
expectEqResourceInfo(infos1[0], mTestClient1, resources1);
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, resources1);
ssize_t index2 = map.indexOfKey(kTestPid2);
ASSERT_GE(index2, 0);
const ResourceInfos &infos2 = map[index2];
EXPECT_EQ(2u, infos2.size());
expectEqResourceInfo(infos2[0], mTestClient2, resources2);
expectEqResourceInfo(infos2[1], mTestClient3, resources3);
expectEqResourceInfo(infos2.valueFor(getId(mTestClient2)), kTestUid2, mTestClient2, resources2);
expectEqResourceInfo(infos2.valueFor(getId(mTestClient3)), kTestUid2, mTestClient3, resources3);
}
void testConfig() {
@ -219,10 +286,84 @@ protected:
EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec);
}
void testCombineResource() {
// kTestPid1 mTestClient1
Vector<MediaResource> resources1;
resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
Vector<MediaResource> resources11;
resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
const PidResourceInfosMap &map = mService->mMap;
EXPECT_EQ(1u, map.size());
ssize_t index1 = map.indexOfKey(kTestPid1);
ASSERT_GE(index1, 0);
const ResourceInfos &infos1 = map[index1];
EXPECT_EQ(1u, infos1.size());
// test adding existing types to combine values
resources1.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
Vector<MediaResource> expected;
expected.push_back(MediaResource(MediaResource::kSecureCodec, 2));
expected.push_back(MediaResource(MediaResource::kGraphicMemory, 300));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
// test adding new types (including types that differs only in subType)
resources11.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
resources11.push_back(MediaResource(MediaResource::kSecureCodec, MediaResource::kVideoCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
expected.clear();
expected.push_back(MediaResource(MediaResource::kSecureCodec, 2));
expected.push_back(MediaResource(MediaResource::kNonSecureCodec, 1));
expected.push_back(MediaResource(MediaResource::kSecureCodec, MediaResource::kVideoCodec, 1));
expected.push_back(MediaResource(MediaResource::kGraphicMemory, 500));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
}
void testRemoveResource() {
// kTestPid1 mTestClient1
Vector<MediaResource> resources1;
resources1.push_back(MediaResource(MediaResource::kSecureCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
Vector<MediaResource> resources11;
resources11.push_back(MediaResource(MediaResource::kGraphicMemory, 200));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources11);
const PidResourceInfosMap &map = mService->mMap;
EXPECT_EQ(1u, map.size());
ssize_t index1 = map.indexOfKey(kTestPid1);
ASSERT_GE(index1, 0);
const ResourceInfos &infos1 = map[index1];
EXPECT_EQ(1u, infos1.size());
// test partial removal
resources11.editItemAt(0).mValue = 100;
mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
Vector<MediaResource> expected;
expected.push_back(MediaResource(MediaResource::kSecureCodec, 1));
expected.push_back(MediaResource(MediaResource::kGraphicMemory, 100));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
// test complete removal with overshoot value
resources11.editItemAt(0).mValue = 1000;
mService->removeResource(kTestPid1, getId(mTestClient1), resources11);
expected.clear();
expected.push_back(MediaResource(MediaResource::kSecureCodec, 1));
expectEqResourceInfo(infos1.valueFor(getId(mTestClient1)), kTestUid1, mTestClient1, expected);
}
void testRemoveClient() {
addResource();
mService->removeResource(kTestPid2, getId(mTestClient2));
mService->removeClient(kTestPid2, getId(mTestClient2));
const PidResourceInfosMap &map = mService->mMap;
EXPECT_EQ(2u, map.size());
@ -231,6 +372,7 @@ protected:
EXPECT_EQ(1u, infos1.size());
EXPECT_EQ(1u, infos2.size());
// mTestClient2 has been removed.
// (OK to use infos2[0] as there is only 1 entry)
EXPECT_EQ(mTestClient3, infos2[0].client);
}
@ -246,6 +388,7 @@ protected:
EXPECT_TRUE(mService->getAllClients_l(kHighPriorityPid, type, &clients));
EXPECT_EQ(2u, clients.size());
// (OK to require ordering in clients[], as the pid map is sorted)
EXPECT_EQ(mTestClient3, clients[0]);
EXPECT_EQ(mTestClient1, clients[1]);
}
@ -438,7 +581,7 @@ protected:
verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// clean up client 3 which still left
mService->removeResource(kTestPid2, getId(mTestClient3));
mService->removeClient(kTestPid2, getId(mTestClient3));
}
}
@ -498,6 +641,84 @@ protected:
EXPECT_TRUE(mService->isCallingPriorityHigher_l(99, 100));
}
void testBatteryStats() {
// reset should always be called when ResourceManagerService is created (restarted)
EXPECT_EQ(1u, mSystemCB->eventCount());
EXPECT_EQ(EventType::VIDEO_RESET, mSystemCB->lastEventType());
// new client request should cause VIDEO_ON
Vector<MediaResource> resources1;
resources1.push_back(MediaResource(MediaResource::kBattery, MediaResource::kVideoCodec, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
EXPECT_EQ(2u, mSystemCB->eventCount());
EXPECT_EQ(EventEntry({EventType::VIDEO_ON, kTestUid1}), mSystemCB->lastEvent());
// each client should only cause 1 VIDEO_ON
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
EXPECT_EQ(2u, mSystemCB->eventCount());
// new client request should cause VIDEO_ON
Vector<MediaResource> resources2;
resources2.push_back(MediaResource(MediaResource::kBattery, MediaResource::kVideoCodec, 2));
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
EXPECT_EQ(3u, mSystemCB->eventCount());
EXPECT_EQ(EventEntry({EventType::VIDEO_ON, kTestUid2}), mSystemCB->lastEvent());
// partially remove mTestClient1's request, shouldn't be any VIDEO_OFF
mService->removeResource(kTestPid1, getId(mTestClient1), resources1);
EXPECT_EQ(3u, mSystemCB->eventCount());
// remove mTestClient1's request, should be VIDEO_OFF for kTestUid1
// (use resource2 to test removing more instances than previously requested)
mService->removeResource(kTestPid1, getId(mTestClient1), resources2);
EXPECT_EQ(4u, mSystemCB->eventCount());
EXPECT_EQ(EventEntry({EventType::VIDEO_OFF, kTestUid1}), mSystemCB->lastEvent());
// remove mTestClient2, should be VIDEO_OFF for kTestUid2
mService->removeClient(kTestPid2, getId(mTestClient2));
EXPECT_EQ(5u, mSystemCB->eventCount());
EXPECT_EQ(EventEntry({EventType::VIDEO_OFF, kTestUid2}), mSystemCB->lastEvent());
}
void testCpusetBoost() {
// reset should always be called when ResourceManagerService is created (restarted)
EXPECT_EQ(1u, mSystemCB->eventCount());
EXPECT_EQ(EventType::VIDEO_RESET, mSystemCB->lastEventType());
// new client request should cause CPUSET_ENABLE
Vector<MediaResource> resources1;
resources1.push_back(MediaResource(MediaResource::kCpuBoost, 1));
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
EXPECT_EQ(2u, mSystemCB->eventCount());
EXPECT_EQ(EventType::CPUSET_ENABLE, mSystemCB->lastEventType());
// each client should only cause 1 CPUSET_ENABLE
mService->addResource(kTestPid1, kTestUid1, getId(mTestClient1), mTestClient1, resources1);
EXPECT_EQ(2u, mSystemCB->eventCount());
// new client request should cause CPUSET_ENABLE
Vector<MediaResource> resources2;
resources2.push_back(MediaResource(MediaResource::kCpuBoost, 2));
mService->addResource(kTestPid2, kTestUid2, getId(mTestClient2), mTestClient2, resources2);
EXPECT_EQ(3u, mSystemCB->eventCount());
EXPECT_EQ(EventType::CPUSET_ENABLE, mSystemCB->lastEventType());
// remove mTestClient2 should not cause CPUSET_DISABLE, mTestClient1 still active
mService->removeClient(kTestPid2, getId(mTestClient2));
EXPECT_EQ(3u, mSystemCB->eventCount());
// remove 1 cpuboost from mTestClient1, should not be CPUSET_DISABLE (still 1 left)
mService->removeResource(kTestPid1, getId(mTestClient1), resources1);
EXPECT_EQ(3u, mSystemCB->eventCount());
// remove 2 cpuboost from mTestClient1, should be CPUSET_DISABLE
// (use resource2 to test removing more than previously requested)
mService->removeResource(kTestPid1, getId(mTestClient1), resources2);
EXPECT_EQ(4u, mSystemCB->eventCount());
EXPECT_EQ(EventType::CPUSET_DISABLE, mSystemCB->lastEventType());
}
sp<TestSystemCallback> mSystemCB;
sp<ResourceManagerService> mService;
sp<IResourceManagerClient> mTestClient1;
sp<IResourceManagerClient> mTestClient2;
@ -512,10 +733,18 @@ TEST_F(ResourceManagerServiceTest, addResource) {
addResource();
}
TEST_F(ResourceManagerServiceTest, combineResource) {
testCombineResource();
}
TEST_F(ResourceManagerServiceTest, removeResource) {
testRemoveResource();
}
TEST_F(ResourceManagerServiceTest, removeClient) {
testRemoveClient();
}
TEST_F(ResourceManagerServiceTest, reclaimResource) {
testReclaimResourceSecure();
testReclaimResourceNonSecure();
@ -541,4 +770,12 @@ TEST_F(ResourceManagerServiceTest, isCallingPriorityHigher_l) {
testIsCallingPriorityHigher();
}
TEST_F(ResourceManagerServiceTest, testBatteryStats) {
testBatteryStats();
}
TEST_F(ResourceManagerServiceTest, testCpusetBoost) {
testCpusetBoost();
}
} // namespace android

Loading…
Cancel
Save