Merge "MTP: add friend class for fuzz testing"
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 25a81eb..d24cb81 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -28,6 +28,10 @@
#include "ACameraCaptureSession.inc"
+ACameraDevice::~ACameraDevice() {
+ mDevice->stopLooper();
+}
+
namespace android {
namespace acam {
@@ -116,14 +120,10 @@
if (!isClosed()) {
disconnectLocked(session);
}
+ LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
+ "CameraDevice looper should've been stopped before ~CameraDevice");
mCurrentSession = nullptr;
- if (mCbLooper != nullptr) {
- mCbLooper->unregisterHandler(mHandler->id());
- mCbLooper->stop();
- }
}
- mCbLooper.clear();
- mHandler.clear();
}
void
@@ -892,6 +892,16 @@
return;
}
+void CameraDevice::stopLooper() {
+ Mutex::Autolock _l(mDeviceLock);
+ if (mCbLooper != nullptr) {
+ mCbLooper->unregisterHandler(mHandler->id());
+ mCbLooper->stop();
+ }
+ mCbLooper.clear();
+ mHandler.clear();
+}
+
CameraDevice::CallbackHandler::CallbackHandler(const char* id) : mId(id) {
}
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index c92a95f..7a35bf0 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -109,6 +109,9 @@
inline ACameraDevice* getWrapper() const { return mWrapper; };
+ // Stop the looper thread and unregister the handler
+ void stopLooper();
+
private:
friend ACameraCaptureSession;
camera_status_t checkCameraClosedOrErrorLocked() const;
@@ -354,7 +357,7 @@
sp<ACameraMetadata> chars) :
mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {}
- ~ACameraDevice() {};
+ ~ACameraDevice();
/*******************
* NDK public APIs *
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 9ec64e1..4a801a7 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -7696,14 +7696,14 @@
* case, when the application configures a RAW stream, the camera device will make sure
* the active physical camera will remain active to ensure consistent RAW output
* behavior, and not switch to other physical cameras.</p>
- * <p>To maintain backward compatibility, the capture request and result metadata tags
- * required for basic camera functionalities will be solely based on the
- * logical camera capabiltity. Other request and result metadata tags, on the other
- * hand, will be based on current active physical camera. For example, the physical
- * cameras' sensor sensitivity and lens capability could be different from each other.
- * So when the application manually controls sensor exposure time/gain, or does manual
- * focus control, it must checks the current active physical camera's exposure, gain,
- * and focus distance range.</p>
+ * <p>The capture request and result metadata tags required for backward compatible camera
+ * functionalities will be solely based on the logical camera capabiltity. On the other
+ * hand, the use of manual capture controls (sensor or post-processing) with a
+ * logical camera may result in unexpected behavior when the HAL decides to switch
+ * between physical cameras with different characteristics under the hood. For example,
+ * when the application manually sets exposure time and sensitivity while zooming in,
+ * the brightness of the camera images may suddenly change because HAL switches from one
+ * physical camera to the other.</p>
*
* @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index a43d707..35c8355 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -44,6 +44,10 @@
using namespace android;
+ACameraDevice::~ACameraDevice() {
+ mDevice->stopLooper();
+}
+
namespace android {
namespace acam {
@@ -130,13 +134,9 @@
disconnectLocked(session);
}
mCurrentSession = nullptr;
- if (mCbLooper != nullptr) {
- mCbLooper->unregisterHandler(mHandler->id());
- mCbLooper->stop();
- }
+ LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
+ "CameraDevice looper should've been stopped before ~CameraDevice");
}
- mCbLooper.clear();
- mHandler.clear();
}
void
@@ -1400,6 +1400,16 @@
}
}
+void CameraDevice::stopLooper() {
+ Mutex::Autolock _l(mDeviceLock);
+ if (mCbLooper != nullptr) {
+ mCbLooper->unregisterHandler(mHandler->id());
+ mCbLooper->stop();
+ }
+ mCbLooper.clear();
+ mHandler.clear();
+}
+
/**
* Camera service callback implementation
*/
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
index 829b084..9e034c4 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -133,8 +133,12 @@
bool setDeviceMetadataQueues();
inline ACameraDevice* getWrapper() const { return mWrapper; };
+ // Stop the looper thread and unregister the handler
+ void stopLooper();
+
private:
friend ACameraCaptureSession;
+ friend ACameraDevice;
camera_status_t checkCameraClosedOrErrorLocked() const;
@@ -383,8 +387,7 @@
sp<ACameraMetadata> chars) :
mDevice(new android::acam::CameraDevice(id, cb, std::move(chars), this)) {}
- ~ACameraDevice() {};
-
+ ~ACameraDevice();
/*******************
* NDK public APIs *
*******************/
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 8fe029a..f07a1e6 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -57,7 +57,7 @@
#include <algorithm>
using namespace android;
-using ::android::hardware::ICameraServiceDefault;
+using ::android::hardware::ICameraService;
using ::android::hardware::camera2::ICameraDeviceUser;
#define ASSERT_NOT_NULL(x) \
@@ -507,7 +507,7 @@
bool queryStatus;
res = device->isSessionConfigurationSupported(sessionConfiguration, &queryStatus);
EXPECT_TRUE(res.isOk() ||
- (res.serviceSpecificErrorCode() == ICameraServiceDefault::ERROR_INVALID_OPERATION))
+ (res.serviceSpecificErrorCode() == ICameraService::ERROR_INVALID_OPERATION))
<< res;
if (res.isOk()) {
EXPECT_TRUE(queryStatus);
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
index 769895c..0cf277f 100644
--- a/media/codec2/components/aom/C2SoftAomDec.cpp
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -340,6 +340,7 @@
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);
diff --git a/media/codec2/components/gav1/Android.bp b/media/codec2/components/gav1/Android.bp
index da76e9d..0a0545d 100644
--- a/media/codec2/components/gav1/Android.bp
+++ b/media/codec2/components/gav1/Android.bp
@@ -6,4 +6,9 @@
],
srcs: ["C2SoftGav1Dec.cpp"],
+ static_libs: ["libgav1"],
+
+ include_dirs: [
+ "external/libgav1/libgav1/",
+ ],
}
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index f6dd14a..f5321ba 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -27,8 +27,6 @@
namespace android {
-// TODO(vigneshv): This will be changed to c2.android.av1.decoder once this
-// component is fully functional.
constexpr char COMPONENT_NAME[] = "c2.android.gav1.decoder";
class C2SoftGav1Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -256,20 +254,497 @@
const std::shared_ptr<IntfImpl> &intfImpl)
: SimpleC2Component(
std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
- mIntf(intfImpl) {}
+ mIntf(intfImpl),
+ mCodecCtx(nullptr) {
+ gettimeofday(&mTimeStart, nullptr);
+ gettimeofday(&mTimeEnd, nullptr);
+}
-c2_status_t C2SoftGav1Dec::onInit() { return C2_OK; }
-c2_status_t C2SoftGav1Dec::onStop() { return C2_OK; }
-void C2SoftGav1Dec::onReset() {}
-void C2SoftGav1Dec::onRelease(){};
-c2_status_t C2SoftGav1Dec::onFlush_sm() { return C2_OK; }
-void C2SoftGav1Dec::process(const std::unique_ptr<C2Work> & /*work*/,
- const std::shared_ptr<C2BlockPool> & /*pool*/) {}
-c2_status_t C2SoftGav1Dec::drain(
- uint32_t /*drainMode*/, const std::shared_ptr<C2BlockPool> & /*pool*/) {
+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()
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index 40b217c..a7c08bb 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -18,6 +18,13 @@
#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 {
@@ -26,6 +33,7 @@
C2SoftGav1Dec(const char* name, c2_node_id_t id,
const std::shared_ptr<IntfImpl>& intfImpl);
+ ~C2SoftGav1Dec();
// Begin SimpleC2Component overrides.
c2_status_t onInit() override;
@@ -41,6 +49,7 @@
private:
std::shared_ptr<IntfImpl> mIntf;
+ std::unique_ptr<libgav1::Decoder> mCodecCtx;
uint32_t mWidth;
uint32_t mHeight;
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index 6509a88..6dab70b 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -127,14 +127,14 @@
}
switch (mBitrateMode->value) {
- case C2Config::BITRATE_VARIABLE:
- mBitrateControlMode = VPX_VBR;
- break;
case C2Config::BITRATE_CONST:
- default:
mBitrateControlMode = VPX_CBR;
break;
- break;
+ case C2Config::BITRATE_VARIABLE:
+ [[fallthrough]];
+ default:
+ mBitrateControlMode = VPX_VBR;
+ break;
}
setCodecSpecificInterface();
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 90758f9..62ccd1b 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -275,7 +275,7 @@
addParameter(
DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
.withDefault(new C2StreamBitrateModeTuning::output(
- 0u, C2Config::BITRATE_CONST))
+ 0u, C2Config::BITRATE_VARIABLE))
.withFields({
C2F(mBitrateMode, value).oneOf({
C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE })
diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp
index a3ebadb..60ae93c 100644
--- a/media/codec2/components/xaac/C2SoftXaacDec.cpp
+++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp
@@ -1309,69 +1309,84 @@
&ui_exec_done);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DONE_QUERY");
- if (ui_exec_done != 1) {
- VOID* p_array; // ITTIAM:buffer to handle gain payload
- WORD32 buf_size = 0; // ITTIAM:gain payload length
- WORD32 bit_str_fmt = 1;
- WORD32 gain_stream_flag = 1;
-
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
-
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
-
- if (buf_size > 0) {
- /*Set bitstream_split_format */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- memcpy(mDrcInBuf, p_array, buf_size);
- /* Set number of bytes to be processed */
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- /* Execute process */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
-
- mMpegDDRCPresent = 1;
- }
- }
-
- /* How much buffer is used in input buffers */
+ int32_t num_preroll = 0;
err_code = ixheaacd_dec_api(mXheaacCodecHandle,
- IA_API_CMD_GET_CURIDX_INPUT_BUF,
- 0,
- bytesConsumed);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+ IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES,
+ &num_preroll);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES");
- /* Get the output bytes */
- err_code = ixheaacd_dec_api(mXheaacCodecHandle,
- IA_API_CMD_GET_OUTPUT_BYTES,
- 0,
- outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+ {
+ int32_t preroll_frame_offset = 0;
- if (mMpegDDRCPresent == 1) {
- memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+ do {
+ if (ui_exec_done != 1) {
+ VOID* p_array; // ITTIAM:buffer to handle gain payload
+ WORD32 buf_size = 0; // ITTIAM:gain payload length
+ WORD32 bit_str_fmt = 1;
+ WORD32 gain_stream_flag = 1;
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
- RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
- memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+
+ if (buf_size > 0) {
+ /*Set bitstream_split_format */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ memcpy(mDrcInBuf, p_array, buf_size);
+ /* Set number of bytes to be processed */
+ err_code =
+ ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ /* Execute process */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+ IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+ mMpegDDRCPresent = 1;
+ }
+ }
+
+ /* How much buffer is used in input buffers */
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+ IA_API_CMD_GET_CURIDX_INPUT_BUF,
+ 0,
+ bytesConsumed);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+
+ /* Get the output bytes */
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+ IA_API_CMD_GET_OUTPUT_BYTES,
+ 0,
+ outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+
+ if (mMpegDDRCPresent == 1) {
+ memcpy(mDrcInBuf, mOutputBuffer + preroll_frame_offset, *outBytes);
+ preroll_frame_offset += *outBytes;
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+
+ err_code =
+ ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
+ RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+
+ memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+ }
+ num_preroll--;
+ } while (num_preroll > 0);
}
return IA_NO_ERROR;
}
diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h
index cc8c17a..51d417a 100644
--- a/media/codec2/core/include/C2Param.h
+++ b/media/codec2/core/include/C2Param.h
@@ -176,9 +176,9 @@
DIR_INPUT = 0x00000000,
DIR_OUTPUT = 0x10000000,
- IS_STREAM_FLAG = 0x00100000,
- STREAM_ID_MASK = 0x03E00000,
- STREAM_ID_SHIFT = 21,
+ IS_STREAM_FLAG = 0x02000000,
+ STREAM_ID_MASK = 0x01F00000,
+ STREAM_ID_SHIFT = 20,
MAX_STREAM_ID = STREAM_ID_MASK >> STREAM_ID_SHIFT,
STREAM_MASK = IS_STREAM_FLAG | STREAM_ID_MASK,
diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
index 1786c69..50790bc 100644
--- a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
+++ b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
@@ -82,21 +82,14 @@
template <typename BlockProcessor>
void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
- BlockProcessor process,
- bool processInput, bool processOutput) {
+ BlockProcessor process) {
for (const std::unique_ptr<C2Work>& work : workList) {
if (!work) {
continue;
}
- if (processInput) {
- forEachBlock(work->input, process);
- }
- if (processOutput) {
- for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
- if (worklet) {
- forEachBlock(worklet->output,
- process);
- }
+ for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
+ if (worklet) {
+ forEachBlock(worklet->output, process);
}
}
}
@@ -109,8 +102,6 @@
new B2HGraphicBufferProducer(igbp);
}
-} // unnamed namespace
-
status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
const sp<IGraphicBufferProducer>& igbp,
uint32_t generation,
@@ -154,73 +145,221 @@
_C2BlockFactory::GetGraphicBlockPoolData(block),
generation, bqId, bqSlot);
}
+} // unnamed namespace
-bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation) {
- std::shared_ptr<_C2BlockPoolData> data =
- _C2BlockFactory::GetGraphicBlockPoolData(block);
- if (!data) {
- return false;
- }
+class OutputBufferQueue::Impl {
+ std::mutex mMutex;
+ sp<IGraphicBufferProducer> mIgbp;
+ uint32_t mGeneration;
+ uint64_t mBqId;
+ std::shared_ptr<int> mOwner;
+ // To migrate existing buffers
+ sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way
+ std::weak_ptr<_C2BlockPoolData>
+ mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
- uint32_t oldGeneration;
- uint64_t oldId;
- int32_t oldSlot;
- // If the block is not bufferqueue-based, do nothing.
- if (!_C2BlockFactory::GetBufferQueueData(
- data, &oldGeneration, &oldId, &oldSlot) ||
- (oldId == 0)) {
- return false;
- }
+public:
+ Impl(): mGeneration(0), mBqId(0) {}
- // If the block's bqId is the same as the desired bqId, just hold.
- if ((oldId == bqId) && (oldGeneration == generation)) {
- LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
- << " bqId " << oldId
- << ", bqSlot " << oldSlot
- << ", generation " << generation
- << ".";
- _C2BlockFactory::HoldBlockFromBufferQueue(data, getHgbp(igbp));
+ bool configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId) {
+ size_t tryNum = 0;
+ size_t success = 0;
+ sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::weak_ptr<_C2BlockPoolData>
+ poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ {
+ std::scoped_lock<std::mutex> l(mMutex);
+ if (generation == mGeneration) {
+ return false;
+ }
+ mIgbp = igbp;
+ mGeneration = generation;
+ mBqId = bqId;
+ mOwner = std::make_shared<int>(0);
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+ if (mBqId == 0 || !mBuffers[i]) {
+ continue;
+ }
+ std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
+ if (!data ||
+ !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
+ continue;
+ }
+ ++tryNum;
+ int bqSlot;
+ mBuffers[i]->setGenerationNumber(generation);
+ status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
+ if (result != OK) {
+ continue;
+ }
+ bool attach =
+ _C2BlockFactory::EndAttachBlockToBufferQueue(
+ data, mOwner, getHgbp(mIgbp),
+ generation, bqId, bqSlot);
+ if (!attach) {
+ igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
+ continue;
+ }
+ buffers[bqSlot] = mBuffers[i];
+ poolDatas[bqSlot] = data;
+ ++success;
+ }
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+ mBuffers[i] = buffers[i];
+ mPoolDatas[i] = poolDatas[i];
+ }
+ }
+ ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
return true;
}
- // Otherwise, attach to the given igbp, which must not be null.
- if (!igbp) {
+ bool registerBuffer(const C2ConstGraphicBlock& block) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (!data) {
+ return false;
+ }
+ std::scoped_lock<std::mutex> l(mMutex);
+
+ if (!mIgbp) {
+ return false;
+ }
+
+ uint32_t oldGeneration;
+ uint64_t oldId;
+ int32_t oldSlot;
+ // If the block is not bufferqueue-based, do nothing.
+ if (!_C2BlockFactory::GetBufferQueueData(
+ data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
+ return false;
+ }
+ // If the block's bqId is the same as the desired bqId, just hold.
+ if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
+ LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
+ << " bqId " << oldId
+ << ", bqSlot " << oldSlot
+ << ", generation " << mGeneration
+ << ".";
+ _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
+ mPoolDatas[oldSlot] = data;
+ mBuffers[oldSlot] = createGraphicBuffer(block);
+ mBuffers[oldSlot]->setGenerationNumber(mGeneration);
+ return true;
+ }
+ int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
+ LOG(WARNING) << "receiving stale buffer: generation "
+ << mGeneration << " , diff " << d << " : slot "
+ << oldSlot;
return false;
}
- int32_t bqSlot;
- status_t result = attachToBufferQueue(block, igbp, generation, &bqSlot);
+ status_t outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output) {
+ uint32_t generation;
+ uint64_t bqId;
+ int32_t bqSlot;
+ bool display = displayBufferQueueBlock(block);
+ if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+ bqId == 0) {
+ // Block not from bufferqueue -- it must be attached before queuing.
- if (result != OK) {
- LOG(ERROR) << "holdBufferQueueBlock -- fail to attach:"
- << " target bqId " << bqId
- << ", generation " << generation
- << ".";
- return false;
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ mMutex.unlock();
+
+ status_t status = attachToBufferQueue(
+ block, outputIgbp, outputGeneration, &bqSlot);
+ if (status != OK) {
+ LOG(WARNING) << "outputBuffer -- attaching failed.";
+ return INVALID_OPERATION;
+ }
+
+ status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status != OK) {
+ LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+ "on non-bufferqueue-based block. "
+ "Error = " << status << ".";
+ return status;
+ }
+ return OK;
+ }
+
+ mMutex.lock();
+ sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+ uint32_t outputGeneration = mGeneration;
+ uint64_t outputBqId = mBqId;
+ mMutex.unlock();
+
+ if (!outputIgbp) {
+ LOG(VERBOSE) << "outputBuffer -- output surface is null.";
+ return NO_INIT;
+ }
+
+ if (!display) {
+ LOG(WARNING) << "outputBuffer -- cannot display "
+ "bufferqueue-based block to the bufferqueue.";
+ return UNKNOWN_ERROR;
+ }
+ if (bqId != outputBqId || generation != outputGeneration) {
+ int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
+ LOG(WARNING) << "outputBuffer -- buffers from old generation to "
+ << outputGeneration << " , diff: " << diff
+ << " , slot: " << bqSlot;
+ return DEAD_OBJECT;
+ }
+
+ status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+ input, output);
+ if (status != OK) {
+ LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+ "on bufferqueue-based block. "
+ "Error = " << status << ".";
+ return status;
+ }
+ return OK;
}
- LOG(VERBOSE) << "holdBufferQueueBlock -- attached:"
- << " bqId " << bqId
- << ", bqSlot " << bqSlot
- << ", generation " << generation
- << ".";
- _C2BlockFactory::AssignBlockToBufferQueue(
- data, getHgbp(igbp), generation, bqId, bqSlot, true);
- return true;
+ Impl *getPtr() {
+ return this;
+ }
+
+ ~Impl() {}
+};
+
+OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {}
+
+OutputBufferQueue::~OutputBufferQueue() {}
+
+bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId) {
+ return mImpl && mImpl->configure(igbp, generation, bqId);
}
-void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation,
- bool forInput) {
+status_t OutputBufferQueue::outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output) {
+ if (mImpl) {
+ return mImpl->outputBuffer(block, input, output);
+ }
+ return DEAD_OBJECT;
+}
+
+void OutputBufferQueue::holdBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList) {
+ if (!mImpl) {
+ return;
+ }
forEachBlock(workList,
- std::bind(holdBufferQueueBlock,
- std::placeholders::_1, igbp, bqId, generation),
- forInput, !forInput);
+ std::bind(&OutputBufferQueue::Impl::registerBuffer,
+ mImpl->getPtr(), std::placeholders::_1));
}
} // namespace utils
diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp
index 5897dce..a9f20a4 100644
--- a/media/codec2/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hidl/1.0/utils/Component.cpp
@@ -107,19 +107,22 @@
WorkBundle workBundle;
sp<Component> strongComponent = mComponent.promote();
+ beginTransferBufferQueueBlocks(c2workItems, true);
if (!objcpy(&workBundle, c2workItems, strongComponent ?
&strongComponent->mBufferPoolSender : nullptr)) {
LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
<< "received corrupted work items.";
+ endTransferBufferQueueBlocks(c2workItems, false, true);
return;
}
Return<void> transStatus = listener->onWorkDone(workBundle);
if (!transStatus.isOk()) {
LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
<< "transaction failed.";
+ endTransferBufferQueueBlocks(c2workItems, false, true);
return;
}
- yieldBufferQueueBlocks(c2workItems, true);
+ endTransferBufferQueueBlocks(c2workItems, true, true);
}
}
@@ -254,13 +257,14 @@
WorkBundle flushedWorkBundle;
Status res = static_cast<Status>(c2res);
+ beginTransferBufferQueueBlocks(c2flushedWorks, true);
if (c2res == C2_OK) {
if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
res = Status::CORRUPTED;
}
}
_hidl_cb(res, flushedWorkBundle);
- yieldBufferQueueBlocks(c2flushedWorks, true);
+ endTransferBufferQueueBlocks(c2flushedWorks, true, true);
return Void();
}
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
index be429ac..0a2298c 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
@@ -28,62 +28,42 @@
namespace V1_0 {
namespace utils {
-
// BufferQueue-Based Block Operations
// ==================================
-// Create a GraphicBuffer object from a graphic block and attach it to an
-// IGraphicBufferProducer.
-status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint32_t generation,
- int32_t* bqSlot);
+// Manage BufferQueue and graphic blocks for both component and codec.
+// Manage graphic blocks ownership consistently during surface change.
+struct OutputBufferQueue {
-// Return false if block does not come from a bufferqueue-based blockpool.
-// Otherwise, extract generation, bqId and bqSlot and return true.
-bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
- uint32_t* generation,
- uint64_t* bqId,
- int32_t* bqSlot);
+ OutputBufferQueue();
-// Assign the given block to a bufferqueue so that when the block is destroyed,
-// cancelBuffer() will be called.
-//
-// If the block does not come from a bufferqueue-based blockpool, this function
-// returns false.
-//
-// If the block already has a bufferqueue assignment that matches the given one,
-// the function returns true.
-//
-// If the block already has a bufferqueue assignment that does not match the
-// given one, the block will be reassigned to the given bufferqueue. This
-// will call attachBuffer() on the given igbp. The function then returns true on
-// success or false on any failure during the operation.
-//
-// Note: This function should be called after detachBuffer() or dequeueBuffer()
-// is called manually.
-bool holdBufferQueueBlock(const C2ConstGraphicBlock& block,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation);
+ ~OutputBufferQueue();
-// Call holdBufferQueueBlock() on input or output blocks in the given workList.
-// Since the bufferqueue assignment for input and output buffers can be
-// different, this function takes forInput to determine whether the given
-// bufferqueue is for input buffers or output buffers. (The default value of
-// forInput is false.)
-//
-// In the (rare) case that both input and output buffers are bufferqueue-based,
-// this function must be called twice, once for the input buffers and once for
-// the output buffers.
-//
-// Note: This function should be called after WorkBundle has been received from
-// another process.
-void holdBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
- const sp<IGraphicBufferProducer>& igbp,
- uint64_t bqId,
- uint32_t generation,
- bool forInput = false);
+ // Configure a new surface to render graphic blocks.
+ // Graphic blocks from older surface will be migrated to new surface.
+ bool configure(const sp<IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId);
+
+ // Render a graphic block to current surface.
+ status_t outputBuffer(
+ const C2ConstGraphicBlock& block,
+ const BnGraphicBufferProducer::QueueBufferInput& input,
+ BnGraphicBufferProducer::QueueBufferOutput* output);
+
+ // Call holdBufferQueueBlock() on output blocks in the given workList.
+ // The OutputBufferQueue will take the ownership of output blocks.
+ //
+ // Note: This function should be called after WorkBundle has been received
+ // from another process.
+ void holdBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList);
+
+private:
+
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
} // namespace utils
} // namespace V1_0
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
index 0ddfec5..a9928b3 100644
--- a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -299,26 +299,46 @@
// BufferQueue-Based Block Operations
// ==================================
-// Disassociate the given block with its designated bufferqueue so that
-// cancelBuffer() will not be called when the block is destroyed. If the block
-// does not have a designated bufferqueue, the function returns false.
-// Otherwise, it returns true.
+// Call before transferring block to other processes.
//
-// Note: This function should be called after attachBuffer() or queueBuffer() is
-// called manually.
-bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block);
+// The given block is ready to transfer to other processes. This will guarantee
+// the given block data is not mutated by bufferqueue migration.
+bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block);
-// Call yieldBufferQueueBlock() on blocks in the given workList. processInput
-// determines whether input blocks are yielded. processOutput works similarly on
-// output blocks. (The default value of processInput is false while the default
-// value of processOutput is true. This implies that in most cases, only output
-// buffers contain bufferqueue-based blocks.)
+// Call beginTransferBufferQueueBlock() on blocks in the given workList.
+// processInput determines whether input blocks are yielded. processOutput
+// works similarly on output blocks. (The default value of processInput is
+// false while the default value of processOutput is true. This implies that in
+// most cases, only output buffers contain bufferqueue-based blocks.)
+void beginTransferBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList,
+ bool processInput = false,
+ bool processOutput = true);
+
+// Call after transferring block is finished and make sure that
+// beginTransferBufferQueueBlock() is called before.
//
-// Note: This function should be called after WorkBundle has been successfully
-// sent over the Treble boundary to another process.
-void yieldBufferQueueBlocks(const std::list<std::unique_ptr<C2Work>>& workList,
- bool processInput = false,
- bool processOutput = true);
+// The transfer of given block is finished. If transfer is successful the given
+// block is not owned by process anymore. Since transfer is finished the given
+// block data is OK to mutate by bufferqueue migration after this call.
+bool endTransferBufferQueueBlock(const C2ConstGraphicBlock& block,
+ bool transfer);
+
+// Call endTransferBufferQueueBlock() on blocks in the given workList.
+// processInput determines whether input blocks are yielded. processOutput
+// works similarly on output blocks. (The default value of processInput is
+// false while the default value of processOutput is true. This implies that in
+// most cases, only output buffers contain bufferqueue-based blocks.)
+void endTransferBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList,
+ bool transfer,
+ bool processInput = false,
+ bool processOutput = true);
+
+// The given block is ready to be rendered. the given block is not owned by
+// process anymore. If migration is in progress, this returns false in order
+// not to render.
+bool displayBufferQueueBlock(const C2ConstGraphicBlock& block);
} // namespace utils
} // namespace V1_0
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 3fd2f92..07dbf67 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -737,7 +737,15 @@
bufferPoolSender, baseBlocks, baseBlockIndices);
}
case _C2BlockPoolData::TYPE_BUFFERQUEUE:
- // Do the same thing as a NATIVE block.
+ uint32_t gen;
+ uint64_t bqId;
+ int32_t bqSlot;
+ // Update handle if migration happened.
+ if (_C2BlockFactory::GetBufferQueueData(
+ blockPoolData, &gen, &bqId, &bqSlot)) {
+ android::MigrateNativeCodec2GrallocHandle(
+ const_cast<native_handle_t*>(handle), gen, bqId, bqSlot);
+ }
return _addBaseBlock(
index, handle,
baseBlocks, baseBlockIndices);
@@ -1772,20 +1780,53 @@
} // unnamed namespace
-bool yieldBufferQueueBlock(const C2ConstGraphicBlock& block) {
+bool beginTransferBufferQueueBlock(const C2ConstGraphicBlock& block) {
std::shared_ptr<_C2BlockPoolData> data =
_C2BlockFactory::GetGraphicBlockPoolData(block);
if (data && _C2BlockFactory::GetBufferQueueData(data)) {
- _C2BlockFactory::YieldBlockToBufferQueue(data);
+ _C2BlockFactory::BeginTransferBlockToClient(data);
return true;
}
return false;
}
-void yieldBufferQueueBlocks(
+void beginTransferBufferQueueBlocks(
const std::list<std::unique_ptr<C2Work>>& workList,
bool processInput, bool processOutput) {
- forEachBlock(workList, yieldBufferQueueBlock, processInput, processOutput);
+ forEachBlock(workList, beginTransferBufferQueueBlock,
+ processInput, processOutput);
+}
+
+bool endTransferBufferQueueBlock(
+ const C2ConstGraphicBlock& block,
+ bool transfer) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (data && _C2BlockFactory::GetBufferQueueData(data)) {
+ _C2BlockFactory::EndTransferBlockToClient(data, transfer);
+ return true;
+ }
+ return false;
+}
+
+void endTransferBufferQueueBlocks(
+ const std::list<std::unique_ptr<C2Work>>& workList,
+ bool transfer,
+ bool processInput, bool processOutput) {
+ forEachBlock(workList,
+ std::bind(endTransferBufferQueueBlock,
+ std::placeholders::_1, transfer),
+ processInput, processOutput);
+}
+
+bool displayBufferQueueBlock(const C2ConstGraphicBlock& block) {
+ std::shared_ptr<_C2BlockPoolData> data =
+ _C2BlockFactory::GetGraphicBlockPoolData(block);
+ if (data && _C2BlockFactory::GetBufferQueueData(data)) {
+ _C2BlockFactory::DisplayBlockToBufferQueue(data);
+ return true;
+ }
+ return false;
}
} // namespace utils
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
index 687754b..65f0d09 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
@@ -15,19 +15,19 @@
*/
cc_test {
- name: "VtsHidlC2V1_0TargetAudioDecTest",
- defaults: ["VtsMediaC2V1_0Defaults"],
+ name: "VtsHalMediaC2V1_0TargetAudioDecTest",
+ defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: [
- "VtsHidlC2V1_0TargetAudioDecTest.cpp",
+ "VtsHalMediaC2V1_0TargetAudioDecTest.cpp",
//"media_audio_hidl_test_common.cpp"
],
}
cc_test {
- name: "VtsHidlC2V1_0TargetAudioEncTest",
- defaults: ["VtsMediaC2V1_0Defaults"],
+ name: "VtsHalMediaC2V1_0TargetAudioEncTest",
+ defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: [
- "VtsHidlC2V1_0TargetAudioEncTest.cpp",
+ "VtsHalMediaC2V1_0TargetAudioEncTest.cpp",
//"media_audio_hidl_test_common.cpp"
],
}
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioDecTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/functional/audio/VtsHidlC2V1_0TargetAudioEncTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
index da0061a..a011ba3 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
@@ -1,5 +1,5 @@
cc_library_static {
- name: "VtsMediaC2V1_0CommonUtil",
+ name: "VtsHalMediaC2V1_0CommonUtil",
defaults: [
"VtsHalTargetTestDefaults",
"libcodec2-hidl-client-defaults",
@@ -17,14 +17,14 @@
}
cc_defaults {
- name: "VtsMediaC2V1_0Defaults",
+ name: "VtsHalMediaC2V1_0Defaults",
defaults: [
"VtsHalTargetTestDefaults",
"libcodec2-hidl-client-defaults",
],
static_libs: [
- "VtsMediaC2V1_0CommonUtil",
+ "VtsHalMediaC2V1_0CommonUtil",
],
shared_libs: [
diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md
index 3deab10..50e8356 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/README.md
+++ b/media/codec2/hidl/1.0/vts/functional/common/README.md
@@ -3,29 +3,29 @@
#### master :
Functionality of master is to enumerate all the Codec2 components available in C2 media service.
-usage: VtsHidlC2V1\_0TargetMasterTest -I default
+usage: VtsHalMediaC2V1\_0TargetMasterTest -I default
#### 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: VtsHidlC2V1\_0TargetComponentTest -I software -C <comp name>
-example: VtsHidlC2V1\_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 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: VtsHidlC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/res/
-usage: VtsHidlC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/res/
+usage: VtsHalMediaC2V1\_0TargetAudioDecTest -I default -C <comp name> -P /sdcard/media/
+usage: VtsHalMediaC2V1\_0TargetAudioEncTest -I software -C <comp name> -P /sdcard/media/
-example: VtsHidlC2V1\_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /sdcard/res/
-example: VtsHidlC2V1\_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /sdcard/res/
+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/
#### 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: VtsHidlC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/res/
-usage: VtsHidlC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/res/
+usage: VtsHalMediaC2V1\_0TargetVideoDecTest -I default -C <comp name> -P /sdcard/media/
+usage: VtsHalMediaC2V1\_0TargetVideoEncTest -I software -C <comp name> -P /sdcard/media/
-example: VtsHidlC2V1\_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /sdcard/res/
-example: VtsHidlC2V1\_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /sdcard/res/
+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/
diff --git a/media/codec2/hidl/1.0/vts/functional/component/Android.bp b/media/codec2/hidl/1.0/vts/functional/component/Android.bp
index 4b913b6..7ec64ee 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/component/Android.bp
@@ -15,8 +15,8 @@
*/
cc_test {
- name: "VtsHidlC2V1_0TargetComponentTest",
- defaults: ["VtsMediaC2V1_0Defaults"],
- srcs: ["VtsHidlC2V1_0TargetComponentTest.cpp"],
+ name: "VtsHalMediaC2V1_0TargetComponentTest",
+ defaults: ["VtsHalMediaC2V1_0Defaults"],
+ srcs: ["VtsHalMediaC2V1_0TargetComponentTest.cpp"],
}
diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
similarity index 95%
rename from media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
index 74548b5..9dc541c 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/VtsHidlC2V1_0TargetComponentTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
@@ -144,8 +144,7 @@
// Queueing an empty WorkBundle
std::list<std::unique_ptr<C2Work>> workList;
- err = mComponent->queue(&workList);
- ASSERT_EQ(err, C2_OK);
+ mComponent->queue(&workList);
err = mComponent->reset();
ASSERT_EQ(err, C2_OK);
@@ -183,33 +182,23 @@
// Test Multiple Start Stop Reset Test
TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) {
ALOGV("Multiple Start Stop and Reset Test");
- c2_status_t err = C2_OK;
for (size_t i = 0; i < MAX_RETRY; i++) {
- err = mComponent->start();
- ASSERT_EQ(err, C2_OK);
-
- err = mComponent->stop();
- ASSERT_EQ(err, C2_OK);
+ mComponent->start();
+ mComponent->stop();
}
- err = mComponent->start();
- ASSERT_EQ(err, C2_OK);
+ ASSERT_EQ(mComponent->start(), C2_OK);
for (size_t i = 0; i < MAX_RETRY; i++) {
- err = mComponent->reset();
- ASSERT_EQ(err, C2_OK);
+ mComponent->reset();
}
- err = mComponent->start();
- ASSERT_EQ(err, C2_OK);
-
- err = mComponent->stop();
- ASSERT_EQ(err, C2_OK);
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
// Second stop should return error
- err = mComponent->stop();
- ASSERT_NE(err, C2_OK);
+ ASSERT_NE(mComponent->stop(), C2_OK);
}
// Test Component Release API
@@ -233,8 +222,7 @@
ASSERT_EQ(failures.size(), 0u);
for (size_t i = 0; i < MAX_RETRY; i++) {
- err = mComponent->release();
- ASSERT_EQ(err, C2_OK);
+ mComponent->release();
}
}
@@ -332,14 +320,12 @@
timeConsumed = getNowUs() - startTime;
ALOGV("mComponent->queue() timeConsumed=%" PRId64 " us", timeConsumed);
CHECK_TIMEOUT(timeConsumed, QUEUE_TIME_OUT, "queue()");
- ASSERT_EQ(err, C2_OK);
startTime = getNowUs();
err = mComponent->flush(C2Component::FLUSH_COMPONENT, &workList);
timeConsumed = getNowUs() - startTime;
ALOGV("mComponent->flush() timeConsumed=%" PRId64 " us", timeConsumed);
CHECK_TIMEOUT(timeConsumed, FLUSH_TIME_OUT, "flush()");
- ASSERT_EQ(err, C2_OK);
startTime = getNowUs();
err = mComponent->stop();
diff --git a/media/codec2/hidl/1.0/vts/functional/master/Android.bp b/media/codec2/hidl/1.0/vts/functional/master/Android.bp
index e164d68..53e323e 100644
--- a/media/codec2/hidl/1.0/vts/functional/master/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/master/Android.bp
@@ -15,8 +15,8 @@
*/
cc_test {
- name: "VtsHidlC2V1_0TargetMasterTest",
- defaults: ["VtsMediaC2V1_0Defaults"],
- srcs: ["VtsHidlC2V1_0TargetMasterTest.cpp"],
+ name: "VtsHalMediaC2V1_0TargetMasterTest",
+ defaults: ["VtsHalMediaC2V1_0Defaults"],
+ srcs: ["VtsHalMediaC2V1_0TargetMasterTest.cpp"],
}
diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
similarity index 100%
rename from media/codec2/hidl/1.0/vts/functional/master/VtsHidlC2V1_0TargetMasterTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
diff --git a/media/codec2/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
index 6e57ee7..be35b02 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
@@ -15,14 +15,14 @@
*/
cc_test {
- name: "VtsHidlC2V1_0TargetVideoDecTest",
- defaults: ["VtsMediaC2V1_0Defaults"],
- srcs: ["VtsHidlC2V1_0TargetVideoDecTest.cpp"],
+ name: "VtsHalMediaC2V1_0TargetVideoDecTest",
+ defaults: ["VtsHalMediaC2V1_0Defaults"],
+ srcs: ["VtsHalMediaC2V1_0TargetVideoDecTest.cpp"],
}
cc_test {
- name: "VtsHidlC2V1_0TargetVideoEncTest",
- defaults: ["VtsMediaC2V1_0Defaults"],
- srcs: ["VtsHidlC2V1_0TargetVideoEncTest.cpp"],
+ name: "VtsHalMediaC2V1_0TargetVideoEncTest",
+ defaults: ["VtsHalMediaC2V1_0Defaults"],
+ srcs: ["VtsHalMediaC2V1_0TargetVideoEncTest.cpp"],
}
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
similarity index 97%
rename from media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 33fa848..5e28750 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -46,6 +46,10 @@
explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
: C2Buffer(
{block->share(block->offset(), block->size(), ::C2Fence())}) {}
+
+ explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block, size_t size)
+ : C2Buffer(
+ {block->share(block->offset(), size, ::C2Fence())}) {}
};
static ComponentTestEnvironment* gEnv = nullptr;
@@ -120,6 +124,13 @@
mTimestampUs = 0u;
mTimestampDevTest = false;
if (mCompName == unknown_comp) mDisableTest = true;
+
+ C2SecureModeTuning secureModeTuning{};
+ mComponent->query({ &secureModeTuning }, {}, C2_MAY_BLOCK, nullptr);
+ if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
+ mDisableTest = true;
+ }
+
if (mDisableTest) std::cout << "[ WARN ] Test Disabled \n";
}
@@ -371,11 +382,12 @@
ASSERT_EQ(eleStream.gcount(), size);
work->input.buffers.clear();
+ auto alignedSize = ALIGN(size, PAGE_SIZE);
if (size) {
std::shared_ptr<C2LinearBlock> block;
ASSERT_EQ(C2_OK,
linearPool->fetchLinearBlock(
- size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+ alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
&block));
ASSERT_TRUE(block);
@@ -385,13 +397,13 @@
fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
break;
}
- ASSERT_EQ((size_t)size, view.capacity());
+ ASSERT_EQ((size_t)alignedSize, view.capacity());
ASSERT_EQ(0u, view.offset());
- ASSERT_EQ((size_t)size, view.size());
+ ASSERT_EQ((size_t)alignedSize, view.size());
memcpy(view.base(), data, size);
- work->input.buffers.emplace_back(new LinearBuffer(block));
+ work->input.buffers.emplace_back(new LinearBuffer(block, size));
free(data);
}
work->worklets.clear();
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
similarity index 89%
rename from media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
rename to media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index 6bcf840..c1f5a92 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHidlC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -284,15 +284,16 @@
std::list<std::unique_ptr<C2Work>>& workQueue,
std::list<uint64_t>& flushedIndices,
std::shared_ptr<C2BlockPool>& graphicPool,
- std::ifstream& eleStream, uint32_t frameID,
- uint32_t nFrames, uint32_t nWidth, int32_t nHeight,
- bool flushed = false,bool signalEOS = true) {
+ std::ifstream& eleStream, bool& disableTest,
+ uint32_t frameID, uint32_t nFrames, uint32_t nWidth,
+ int32_t nHeight, bool flushed = false, bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
uint32_t maxRetry = 0;
int bytesCount = nWidth * nHeight * 3 >> 1;
int32_t timestampIncr = ENCODER_TIMESTAMP_INCREMENT;
uint64_t timestamp = 0;
+ c2_status_t err = C2_OK;
while (1) {
if (nFrames == 0) break;
uint32_t flags = 0;
@@ -333,16 +334,21 @@
ASSERT_EQ(eleStream.gcount(), bytesCount);
}
std::shared_ptr<C2GraphicBlock> block;
- ASSERT_EQ(
- C2_OK,
- graphicPool->fetchGraphicBlock(
+ err = graphicPool->fetchGraphicBlock(
nWidth, nHeight, HAL_PIXEL_FORMAT_YV12,
- {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
+ {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ if (err != C2_OK) {
+ fprintf(stderr, "fetchGraphicBlock failed : %d\n", err);
+ disableTest = true;
+ break;
+ }
+
ASSERT_TRUE(block);
// Graphic View
C2GraphicView view = block->map().get();
if (view.error() != C2_OK) {
fprintf(stderr, "C2GraphicBlock::map() failed : %d", view.error());
+ disableTest = true;
break;
}
@@ -420,8 +426,16 @@
ASSERT_EQ(mComponent->start(), C2_OK);
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
0, ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS));
+ // mDisableTest will be set if buffer was not fetched properly.
+ // This may happen when resolution is not proper but config suceeded
+ // In this cases, we skip encoding the input stream
+ if (mDisableTest) {
+ std::cout << "[ WARN ] Test Disabled \n";
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+ return;
+ }
// If EOS is not sent, sending empty input with EOS flag
inputFrames = ENC_NUM_FRAMES;
@@ -531,8 +545,17 @@
ALOGV("mURL : %s", mURL);
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
0, numFramesFlushed, nWidth, nHeight));
+ // mDisableTest will be set if buffer was not fetched properly.
+ // This may happen when resolution is not proper but config suceeded
+ // In this cases, we skip encoding the input stream
+ if (mDisableTest) {
+ std::cout << "[ WARN ] Test Disabled \n";
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+ return;
+ }
+
std::list<std::unique_ptr<C2Work>> flushedWork;
c2_status_t err =
mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
@@ -561,10 +584,19 @@
mFlushedIndices.clear();
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
numFramesFlushed, numFrames - numFramesFlushed,
nWidth, nHeight, true));
eleStream.close();
+ // mDisableTest will be set if buffer was not fetched properly.
+ // This may happen when resolution is not proper but config suceeded
+ // In this cases, we skip encoding the input stream
+ if (mDisableTest) {
+ std::cout << "[ WARN ] Test Disabled \n";
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+ return;
+ }
+
err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
ASSERT_NO_FATAL_FAILURE(
@@ -607,19 +639,19 @@
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
0, 1, nWidth, nHeight, false, false));
// Feed larger input buffer.
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
1, 1, nWidth*2, nHeight*2, false, false));
// Feed smaller input buffer.
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
2, 1, nWidth/2, nHeight/2, false, true));
// blocking call to ensures application to Wait till all the inputs are
@@ -629,15 +661,13 @@
waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
if (mFramesReceived != 3) {
- ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGE("framesReceived : %d inputFrames : 3", mFramesReceived);
- ASSERT_TRUE(false);
+ std::cout << "[ WARN ] Component didn't receive all buffers back \n";
+ ALOGW("framesReceived : %d inputFrames : 3", mFramesReceived);
}
if (mFailedWorkReceived == 0) {
- ALOGE("Expected failed frame count mismatch");
- ALOGE("failedFramesReceived : %d", mFailedWorkReceived);
- ASSERT_TRUE(false);
+ std::cout << "[ WARN ] Expected failed frame count mismatch \n";
+ ALOGW("failedFramesReceived : %d", mFailedWorkReceived);
}
ASSERT_EQ(mComponent->stop(), C2_OK);
@@ -665,8 +695,17 @@
ASSERT_NO_FATAL_FAILURE(
encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, 0,
- MAX_INPUT_BUFFERS, nWidth, nHeight));
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
+ 0, MAX_INPUT_BUFFERS, nWidth, nHeight, false, true));
+
+ // mDisableTest will be set if buffer was not fetched properly.
+ // This may happen when resolution is not proper but config suceeded
+ // In this cases, we skip encoding the input stream
+ if (mDisableTest) {
+ std::cout << "[ WARN ] Test Disabled \n";
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+ return;
+ }
ALOGD("Waiting for input consumption");
ASSERT_NO_FATAL_FAILURE(
@@ -676,6 +715,7 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
ASSERT_EQ(mComponent->reset(), C2_OK);
}
+
INSTANTIATE_TEST_CASE_P(NonStdSizes, Codec2VideoEncResolutionTest, ::testing::Values(
std::make_pair(52, 18),
std::make_pair(365, 365),
diff --git a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
index dd45557..e37ca38 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
@@ -23,6 +23,8 @@
#define ENC_DEFAULT_FRAME_HEIGHT 288
#define MAX_ITERATIONS 128
+#define ALIGN(_sz, _align) ((_sz + (_align - 1)) & ~(_align - 1))
+
/*
* Common video utils
*/
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 6aca4a3..5ed54f1 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -959,9 +959,9 @@
std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
char const* serviceName) {
- uint32_t inputSurfaceSetting = ::android::base::GetUintProperty(
- "debug.stagefright.c2inputsurface", uint32_t(0));
- if (inputSurfaceSetting == 0) {
+ int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
+ "debug.stagefright.c2inputsurface", int32_t(0));
+ if (inputSurfaceSetting <= 0) {
return nullptr;
}
size_t index = GetServiceNames().size();
@@ -1080,15 +1080,7 @@
void Codec2Client::Component::handleOnWorkDone(
const std::list<std::unique_ptr<C2Work>> &workItems) {
// Output bufferqueue-based blocks' lifetime management
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> igbp = mOutputIgbp;
- uint64_t bqId = mOutputBqId;
- uint32_t generation = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- if (igbp) {
- holdBufferQueueBlocks(workItems, igbp, bqId, generation);
- }
+ mOutputBufferQueue.holdBufferQueueBlocks(workItems);
}
c2_status_t Codec2Client::Component::queue(
@@ -1151,15 +1143,7 @@
}
// Output bufferqueue-based blocks' lifetime management
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> igbp = mOutputIgbp;
- uint64_t bqId = mOutputBqId;
- uint32_t generation = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- if (igbp) {
- holdBufferQueueBlocks(*flushedWork, igbp, bqId, generation);
- }
+ mOutputBufferQueue.holdBufferQueueBlocks(*flushedWork);
return status;
}
@@ -1239,15 +1223,31 @@
C2BlockPool::local_id_t blockPoolId,
const sp<IGraphicBufferProducer>& surface,
uint32_t generation) {
- sp<HGraphicBufferProducer2> igbp =
- surface->getHalInterface<HGraphicBufferProducer2>();
+ uint64_t bqId = 0;
+ sp<IGraphicBufferProducer> nullIgbp;
+ sp<HGraphicBufferProducer2> nullHgbp;
- if (!igbp) {
+ sp<HGraphicBufferProducer2> igbp = surface ?
+ surface->getHalInterface<HGraphicBufferProducer2>() : nullHgbp;
+ if (surface && !igbp) {
igbp = new B2HGraphicBufferProducer2(surface);
}
+ if (!surface) {
+ mOutputBufferQueue.configure(nullIgbp, generation, 0);
+ } else if (surface->getUniqueId(&bqId) != OK) {
+ LOG(ERROR) << "setOutputSurface -- "
+ "cannot obtain bufferqueue id.";
+ bqId = 0;
+ mOutputBufferQueue.configure(nullIgbp, generation, 0);
+ } else {
+ mOutputBufferQueue.configure(surface, generation, bqId);
+ }
+ ALOGD("generation remote change %u", generation);
+
Return<Status> transStatus = mBase->setOutputSurface(
- static_cast<uint64_t>(blockPoolId), igbp);
+ static_cast<uint64_t>(blockPoolId),
+ bqId == 0 ? nullHgbp : igbp);
if (!transStatus.isOk()) {
LOG(ERROR) << "setOutputSurface -- transaction failed.";
return C2_TRANSACTION_FAILED;
@@ -1256,18 +1256,6 @@
static_cast<c2_status_t>(static_cast<Status>(transStatus));
if (status != C2_OK) {
LOG(DEBUG) << "setOutputSurface -- call failed: " << status << ".";
- } else {
- std::lock_guard<std::mutex> lock(mOutputBufferQueueMutex);
- if (mOutputIgbp != surface) {
- mOutputIgbp = surface;
- if (!surface) {
- mOutputBqId = 0;
- } else if (surface->getUniqueId(&mOutputBqId) != OK) {
- LOG(ERROR) << "setOutputSurface -- "
- "cannot obtain bufferqueue id.";
- }
- }
- mOutputGeneration = generation;
}
return status;
}
@@ -1276,74 +1264,7 @@
const C2ConstGraphicBlock& block,
const QueueBufferInput& input,
QueueBufferOutput* output) {
- uint32_t generation;
- uint64_t bqId;
- int32_t bqSlot;
- if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
- bqId == 0) {
- // Block not from bufferqueue -- it must be attached before queuing.
-
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
- uint32_t outputGeneration = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- status_t status = attachToBufferQueue(block,
- outputIgbp,
- outputGeneration,
- &bqSlot);
- if (status != OK) {
- LOG(WARNING) << "queueToOutputSurface -- attaching failed.";
- return INVALID_OPERATION;
- }
-
- status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
- input, output);
- if (status != OK) {
- LOG(ERROR) << "queueToOutputSurface -- queueBuffer() failed "
- "on non-bufferqueue-based block. "
- "Error = " << status << ".";
- return status;
- }
- return OK;
- }
-
- mOutputBufferQueueMutex.lock();
- sp<IGraphicBufferProducer> outputIgbp = mOutputIgbp;
- uint64_t outputBqId = mOutputBqId;
- uint32_t outputGeneration = mOutputGeneration;
- mOutputBufferQueueMutex.unlock();
-
- if (!outputIgbp) {
- LOG(VERBOSE) << "queueToOutputSurface -- output surface is null.";
- return NO_INIT;
- }
-
- if (bqId != outputBqId || generation != outputGeneration) {
- if (!holdBufferQueueBlock(block, mOutputIgbp, mOutputBqId, mOutputGeneration)) {
- LOG(ERROR) << "queueToOutputSurface -- migration failed.";
- return DEAD_OBJECT;
- }
- if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)) {
- LOG(ERROR) << "queueToOutputSurface -- corrupted bufferqueue assignment.";
- return UNKNOWN_ERROR;
- }
- }
-
- status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
- input, output);
- if (status != OK) {
- LOG(DEBUG) << "queueToOutputSurface -- queueBuffer() failed "
- "on bufferqueue-based block. "
- "Error = " << status << ".";
- return status;
- }
- if (!yieldBufferQueueBlock(block)) {
- LOG(DEBUG) << "queueToOutputSurface -- cannot yield "
- "bufferqueue-based block to the bufferqueue.";
- return UNKNOWN_ERROR;
- }
- return OK;
+ return mOutputBufferQueue.outputBuffer(block, input, output);
}
c2_status_t Codec2Client::Component::connectToInputSurface(
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index 03db515..b8a7fb5 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -369,10 +369,8 @@
::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
mBufferPoolSender;
- std::mutex mOutputBufferQueueMutex;
- sp<IGraphicBufferProducer> mOutputIgbp;
- uint64_t mOutputBqId;
- uint32_t mOutputGeneration;
+ ::android::hardware::media::c2::V1_0::utils::OutputBufferQueue
+ mOutputBufferQueue;
static c2_status_t setDeathListener(
const std::shared_ptr<Component>& component,
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 8ae80ee..9c84c71 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -9,6 +9,7 @@
"CCodecConfig.cpp",
"Codec2Buffer.cpp",
"Codec2InfoBuilder.cpp",
+ "Omx2IGraphicBufferSource.cpp",
"PipelineWatcher.cpp",
"ReflectedParamUpdater.cpp",
"SkipCutBuffer.cpp",
@@ -41,8 +42,10 @@
"libmedia",
"libmedia_omx",
"libsfplugin_ccodec_utils",
+ "libstagefright_bufferqueue_helper",
"libstagefright_codecbase",
"libstagefright_foundation",
+ "libstagefright_omx",
"libstagefright_omx_utils",
"libstagefright_xmlparser",
"libui",
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index 3a93c2a..78d221e 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -36,6 +36,7 @@
#include <media/stagefright/MediaErrors.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <utils/Thread.h>
#include "C2OMXNode.h"
@@ -50,16 +51,128 @@
} // namespace
+class C2OMXNode::QueueThread : public Thread {
+public:
+ QueueThread() : Thread(false) {}
+ ~QueueThread() override = default;
+ void queue(
+ const std::shared_ptr<Codec2Client::Component> &comp,
+ int fenceFd,
+ std::unique_ptr<C2Work> &&work,
+ android::base::unique_fd &&fd0,
+ android::base::unique_fd &&fd1) {
+ Mutexed<Jobs>::Locked jobs(mJobs);
+ auto it = jobs->queues.try_emplace(comp, comp, systemTime()).first;
+ it->second.workList.emplace_back(
+ std::move(work), fenceFd, std::move(fd0), std::move(fd1));
+ jobs->cond.broadcast();
+ }
+
+protected:
+ bool threadLoop() override {
+ constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000; // 10ms
+ constexpr nsecs_t kWaitNs = kIntervalNs * 2;
+ for (int i = 0; i < 2; ++i) {
+ Mutexed<Jobs>::Locked jobs(mJobs);
+ nsecs_t nowNs = systemTime();
+ bool queued = false;
+ for (auto it = jobs->queues.begin(); it != jobs->queues.end(); ) {
+ Queue &queue = it->second;
+ if (queue.workList.empty()
+ || nowNs - queue.lastQueuedTimestampNs < kIntervalNs) {
+ ++it;
+ continue;
+ }
+ std::shared_ptr<Codec2Client::Component> comp = queue.component.lock();
+ if (!comp) {
+ it = jobs->queues.erase(it);
+ continue;
+ }
+ std::list<std::unique_ptr<C2Work>> items;
+ std::vector<int> fenceFds;
+ std::vector<android::base::unique_fd> uniqueFds;
+ while (!queue.workList.empty()) {
+ items.push_back(std::move(queue.workList.front().work));
+ fenceFds.push_back(queue.workList.front().fenceFd);
+ uniqueFds.push_back(std::move(queue.workList.front().fd0));
+ uniqueFds.push_back(std::move(queue.workList.front().fd1));
+ queue.workList.pop_front();
+ }
+
+ jobs.unlock();
+ for (int fenceFd : fenceFds) {
+ sp<Fence> fence(new Fence(fenceFd));
+ fence->waitForever(LOG_TAG);
+ }
+ comp->queue(&items);
+ for (android::base::unique_fd &ufd : uniqueFds) {
+ (void)ufd.release();
+ }
+ jobs.lock();
+
+ it = jobs->queues.upper_bound(comp);
+ queued = true;
+ }
+ if (queued) {
+ return true;
+ }
+ if (i == 0) {
+ jobs.waitForConditionRelative(jobs->cond, kWaitNs);
+ }
+ }
+ return true;
+ }
+
+private:
+ struct WorkFence {
+ WorkFence(std::unique_ptr<C2Work> &&w, int fd) : work(std::move(w)), fenceFd(fd) {}
+
+ WorkFence(
+ std::unique_ptr<C2Work> &&w,
+ int fd,
+ android::base::unique_fd &&uniqueFd0,
+ android::base::unique_fd &&uniqueFd1)
+ : work(std::move(w)),
+ fenceFd(fd),
+ fd0(std::move(uniqueFd0)),
+ fd1(std::move(uniqueFd1)) {}
+
+ std::unique_ptr<C2Work> work;
+ int fenceFd;
+ android::base::unique_fd fd0;
+ android::base::unique_fd fd1;
+ };
+ struct Queue {
+ Queue(const std::shared_ptr<Codec2Client::Component> &comp, nsecs_t timestamp)
+ : component(comp), lastQueuedTimestampNs(timestamp) {}
+ Queue(const Queue &) = delete;
+ Queue &operator =(const Queue &) = delete;
+
+ std::weak_ptr<Codec2Client::Component> component;
+ std::list<WorkFence> workList;
+ nsecs_t lastQueuedTimestampNs;
+ };
+ struct Jobs {
+ std::map<std::weak_ptr<Codec2Client::Component>,
+ Queue,
+ std::owner_less<std::weak_ptr<Codec2Client::Component>>> queues;
+ Condition cond;
+ };
+ Mutexed<Jobs> mJobs;
+};
+
C2OMXNode::C2OMXNode(const std::shared_ptr<Codec2Client::Component> &comp)
: mComp(comp), mFrameIndex(0), mWidth(0), mHeight(0), mUsage(0),
- mAdjustTimestampGapUs(0), mFirstInputFrame(true) {
+ mAdjustTimestampGapUs(0), mFirstInputFrame(true),
+ mQueueThread(new QueueThread) {
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
+ mQueueThread->run("C2OMXNode", PRIORITY_AUDIO);
}
status_t C2OMXNode::freeNode() {
mComp.reset();
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
- return OK;
+ return mQueueThread->requestExitAndWait();
}
status_t C2OMXNode::sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) {
@@ -216,11 +329,6 @@
status_t C2OMXNode::emptyBuffer(
buffer_id buffer, const OMXBuffer &omxBuf,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
- // TODO: better fence handling
- if (fenceFd >= 0) {
- sp<Fence> fence = new Fence(fenceFd);
- fence->waitForever(LOG_TAG);
- }
std::shared_ptr<Codec2Client::Component> comp = mComp.lock();
if (!comp) {
return NO_INIT;
@@ -299,22 +407,8 @@
}
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
- std::list<std::unique_ptr<C2Work>> items;
- uint64_t index = work->input.ordinal.frameIndex.peeku();
- items.push_back(std::move(work));
-
- c2_status_t err = comp->queue(&items);
- if (err != C2_OK) {
- (void)fd0.release();
- (void)fd1.release();
- return UNKNOWN_ERROR;
- }
-
- mBufferIdsInUse.lock()->emplace(index, buffer);
-
- // release ownership of the fds
- (void)fd0.release();
- (void)fd1.release();
+ mBufferIdsInUse.lock()->emplace(work->input.ordinal.frameIndex.peeku(), buffer);
+ mQueueThread->queue(comp, fenceFd, std::move(work), std::move(fd0), std::move(fd1));
return OK;
}
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index 3ca6c0a..1717c96 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -113,6 +113,9 @@
c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame
Mutexed<std::map<uint64_t, buffer_id>> mBufferIdsInUse;
+
+ class QueueThread;
+ sp<QueueThread> mQueueThread;
};
} // namespace android
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index aa7189c..8223273 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -45,6 +45,7 @@
#include "CCodec.h"
#include "CCodecBufferChannel.h"
#include "InputSurfaceWrapper.h"
+#include "Omx2IGraphicBufferSource.h"
extern "C" android::PersistentSurface *CreateInputSurface();
@@ -374,7 +375,11 @@
// consumer usage is queried earlier.
- ALOGD("ISConfig%s", status.str().c_str());
+ if (status.str().empty()) {
+ ALOGD("ISConfig not changed");
+ } else {
+ ALOGD("ISConfig%s", status.str().c_str());
+ }
return err;
}
@@ -1067,6 +1072,7 @@
OmxStatus s;
android::sp<HGraphicBufferProducer> gbp;
android::sp<HGraphicBufferSource> gbs;
+
using ::android::hardware::Return;
Return<void> transStatus = omx->createInputSurface(
[&s, &gbp, &gbs](
@@ -1852,15 +1858,30 @@
// Create Codec 2.0 input surface
extern "C" android::PersistentSurface *CreateInputSurface() {
+ using namespace android;
// Attempt to create a Codec2's input surface.
- std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
- android::Codec2Client::CreateInputSurface();
+ std::shared_ptr<Codec2Client::InputSurface> inputSurface =
+ Codec2Client::CreateInputSurface();
if (!inputSurface) {
- return nullptr;
+ if (property_get_int32("debug.stagefright.c2inputsurface", 0) == -1) {
+ sp<IGraphicBufferProducer> gbp;
+ sp<OmxGraphicBufferSource> gbs = new OmxGraphicBufferSource();
+ status_t err = gbs->initCheck();
+ if (err != OK) {
+ ALOGE("Failed to create persistent input surface: error %d", err);
+ return nullptr;
+ }
+ return new PersistentSurface(
+ gbs->getIGraphicBufferProducer(),
+ sp<IGraphicBufferSource>(
+ new Omx2IGraphicBufferSource(gbs)));
+ } else {
+ return nullptr;
+ }
}
- return new android::PersistentSurface(
+ return new PersistentSurface(
inputSurface->getGraphicBufferProducer(),
- static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
+ static_cast<sp<android::hidl::base::V1_0::IBase>>(
inputSurface->getHalInterface()));
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index eb20b20..1548a89 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -220,7 +220,6 @@
const std::shared_ptr<CCodecCallback> &callback)
: mHeapSeqNum(-1),
mCCodecCallback(callback),
- mDelay(0),
mFrameIndex(0u),
mFirstValidFrameIndex(0u),
mMetaMode(MODE_NONE),
@@ -814,7 +813,6 @@
size_t numInputSlots = inputDelayValue + pipelineDelayValue + kSmoothnessFactor;
size_t numOutputSlots = outputDelayValue + kSmoothnessFactor;
- mDelay = inputDelayValue + pipelineDelayValue + outputDelayValue;
// TODO: get this from input format
bool secure = mComponent->getName().find(".secure") != std::string::npos;
@@ -888,6 +886,8 @@
bool forceArrayMode = false;
Mutexed<Input>::Locked input(mInput);
+ input->inputDelay = inputDelayValue;
+ input->pipelineDelay = pipelineDelayValue;
input->numSlots = numInputSlots;
input->extraBuffers.flush();
input->numExtraSlots = 0u;
@@ -896,6 +896,9 @@
input->buffers.reset(new DummyInputBuffers(mName));
} else if (mMetaMode == MODE_ANW) {
input->buffers.reset(new GraphicMetadataInputBuffers(mName));
+ // This is to ensure buffers do not get released prematurely.
+ // TODO: handle this without going into array mode
+ forceArrayMode = true;
} else {
input->buffers.reset(new GraphicInputBuffers(numInputSlots, mName));
}
@@ -1054,6 +1057,7 @@
}
Mutexed<Output>::Locked output(mOutput);
+ output->outputDelay = outputDelayValue;
output->numSlots = numOutputSlots;
if (graphic) {
if (outputSurface) {
@@ -1075,8 +1079,7 @@
outputGeneration);
}
- if (oStreamFormat.value == C2BufferData::LINEAR
- && mComponentName.find("c2.qti.") == std::string::npos) {
+ if (oStreamFormat.value == C2BufferData::LINEAR) {
// WORKAROUND: if we're using early CSD workaround we convert to
// array mode, to appease apps assuming the output
// buffers to be of the same size.
@@ -1128,8 +1131,9 @@
}
C2StreamBufferTypeSetting::output oStreamFormat(0u);
- c2_status_t err = mComponent->query({ &oStreamFormat }, {}, C2_DONT_BLOCK, nullptr);
- if (err != C2_OK) {
+ C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
+ c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
+ if (err != C2_OK && err != C2_BAD_INDEX) {
return UNKNOWN_ERROR;
}
size_t numInputSlots = mInput.lock()->numSlots;
@@ -1169,7 +1173,7 @@
mName, buffer->capacity(), config->size());
}
} else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0
- && mComponentName.find("c2.qti.") == std::string::npos) {
+ && (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
// WORKAROUND: Some apps expect CSD available without queueing
// any input. Queue an empty buffer to get the CSD.
buffer->setRange(0, 0);
@@ -1377,25 +1381,35 @@
(void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
bool outputBuffersChanged = false;
- Mutexed<Output>::Locked output(mOutput);
- output->outputDelay = outputDelay.value;
- size_t numOutputSlots = outputDelay.value + kSmoothnessFactor;
- if (output->numSlots < numOutputSlots) {
- output->numSlots = numOutputSlots;
- if (output->buffers->isArrayMode()) {
- OutputBuffersArray *array =
- (OutputBuffersArray *)output->buffers.get();
- ALOGV("[%s] onWorkDone: growing output buffer array to %zu",
- mName, numOutputSlots);
- array->grow(numOutputSlots);
- outputBuffersChanged = true;
+ size_t numOutputSlots = 0;
+ {
+ Mutexed<Output>::Locked output(mOutput);
+ output->outputDelay = outputDelay.value;
+ numOutputSlots = outputDelay.value + kSmoothnessFactor;
+ if (output->numSlots < numOutputSlots) {
+ output->numSlots = numOutputSlots;
+ if (output->buffers->isArrayMode()) {
+ OutputBuffersArray *array =
+ (OutputBuffersArray *)output->buffers.get();
+ ALOGV("[%s] onWorkDone: growing output buffer array to %zu",
+ mName, numOutputSlots);
+ array->grow(numOutputSlots);
+ outputBuffersChanged = true;
+ }
}
+ numOutputSlots = output->numSlots;
}
- output.unlock();
if (outputBuffersChanged) {
mCCodecCallback->onOutputBuffersChanged();
}
+
+ uint32_t depth = mReorderStash.lock()->depth();
+ Mutexed<OutputSurface>::Locked output(mOutputSurface);
+ output->maxDequeueBuffers = numOutputSlots + depth + kRenderingDepth;
+ if (output->surface) {
+ output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
+ }
}
}
break;
@@ -1581,6 +1595,7 @@
if (newSurface) {
newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
newSurface->setDequeueTimeout(kDequeueTimeoutNs);
+ newSurface->setMaxDequeuedBufferCount(mOutputSurface.lock()->maxDequeueBuffers);
producer = newSurface->getIGraphicBufferProducer();
producer->setGenerationNumber(generation);
} else {
@@ -1608,7 +1623,6 @@
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
- newSurface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
output->surface = newSurface;
output->generation = generation;
}
@@ -1620,7 +1634,12 @@
// When client pushed EOS, we want all the work to be done quickly.
// Otherwise, component may have stalled work due to input starvation up to
// the sum of the delay in the pipeline.
- size_t n = mInputMetEos ? 0 : mDelay;
+ size_t n = 0;
+ if (!mInputMetEos) {
+ size_t outputDelay = mOutput.lock()->outputDelay;
+ Mutexed<Input>::Locked input(mInput);
+ n = input->inputDelay + input->pipelineDelay + outputDelay;
+ }
return mPipelineWatcher.lock()->elapsed(PipelineWatcher::Clock::now(), n);
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index ae57678..ee3455d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -228,8 +228,6 @@
QueueSync mQueueSync;
std::vector<std::unique_ptr<C2Param>> mParamsToBeSet;
- size_t mDelay;
-
struct Input {
Input();
@@ -306,6 +304,7 @@
const C2WorkOrdinalStruct &ordinal);
void defer(const Entry &entry);
bool hasPending() const;
+ uint32_t depth() const { return mDepth; }
private:
std::list<Entry> mPending;
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 5ebd5bd..26c702d 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -439,6 +439,10 @@
});
}
+size_t BuffersArrayImpl::arraySize() const {
+ return mBuffers.size();
+}
+
// InputBuffersArray
void InputBuffersArray::initialize(
@@ -883,11 +887,24 @@
mAlloc = [format = mFormat, size] {
return new LocalLinearBuffer(format, new ABuffer(size));
};
+ ALOGD("[%s] reallocating with linear buffer of size %u", mName, size);
break;
}
- // TODO: add support
- case C2BufferData::GRAPHIC: [[fallthrough]];
+ case C2BufferData::GRAPHIC: {
+ // This is only called for RawGraphicOutputBuffers.
+ mAlloc = [format = mFormat,
+ lbp = LocalBufferPool::Create(kMaxLinearBufferSize * mImpl.arraySize())] {
+ return ConstGraphicBlockBuffer::AllocateEmpty(
+ format,
+ [lbp](size_t capacity) {
+ return lbp->newBuffer(capacity);
+ });
+ };
+ ALOGD("[%s] reallocating with graphic buffer: format = %s",
+ mName, mFormat->debugString().c_str());
+ break;
+ }
case C2BufferData::INVALID: [[fallthrough]];
case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index e4f2809..2cb6b81 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -478,6 +478,11 @@
*/
size_t numClientBuffers() const;
+ /**
+ * Return the size of the array.
+ */
+ size_t arraySize() const;
+
private:
std::string mImplName; ///< name for debugging
const char *mName; ///< C-string version of name
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 1cfdc19..5adcd94 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -235,7 +235,10 @@
const std::vector<ConfigMapper> &getConfigMappersForSdkKey(std::string key) const {
auto it = mConfigMappers.find(key);
if (it == mConfigMappers.end()) {
- ALOGD("no c2 equivalents for %s", key.c_str());
+ if (mComplained.count(key) == 0) {
+ ALOGD("no c2 equivalents for %s", key.c_str());
+ mComplained.insert(key);
+ }
return NO_MAPPERS;
}
ALOGV("found %zu eqs for %s", it->second.size(), key.c_str());
@@ -304,6 +307,7 @@
private:
std::map<SdkKey, std::vector<ConfigMapper>> mConfigMappers;
+ mutable std::set<std::string> mComplained;
};
const std::vector<ConfigMapper> StandardParams::NO_MAPPERS;
@@ -508,7 +512,8 @@
.limitTo(D::ENCODER & D::VIDEO));
// convert to timestamp base
add(ConfigMapper(KEY_I_FRAME_INTERVAL, C2_PARAMKEY_SYNC_FRAME_INTERVAL, "value")
- .withMappers([](C2Value v) -> C2Value {
+ .limitTo(D::VIDEO & D::ENCODER & D::CONFIG)
+ .withMapper([](C2Value v) -> C2Value {
// convert from i32 to float
int32_t i32Value;
float fpValue;
@@ -518,12 +523,6 @@
return int64_t(c2_min(1000000 * fpValue + 0.5, (double)INT64_MAX));
}
return C2Value();
- }, [](C2Value v) -> C2Value {
- int64_t i64;
- if (v.get(&i64)) {
- return float(i64) / 1000000;
- }
- return C2Value();
}));
// remove when codecs switch to proper coding.gop (add support for calculating gop)
deprecated(ConfigMapper("i-frame-period", "coding.gop", "intra-period")
@@ -1033,7 +1032,25 @@
}
ReflectedParamUpdater::Dict reflected = mParamUpdater->getParams(paramPointers);
- ALOGD("c2 config is %s", reflected.debugString().c_str());
+ std::string config = reflected.debugString();
+ std::set<std::string> configLines;
+ std::string diff;
+ for (size_t start = 0; start != std::string::npos; ) {
+ size_t end = config.find('\n', start);
+ size_t count = (end == std::string::npos)
+ ? std::string::npos
+ : end - start + 1;
+ std::string line = config.substr(start, count);
+ configLines.insert(line);
+ if (mLastConfig.count(line) == 0) {
+ diff.append(line);
+ }
+ start = (end == std::string::npos) ? std::string::npos : end + 1;
+ }
+ if (!diff.empty()) {
+ ALOGD("c2 config diff is %s", diff.c_str());
+ }
+ mLastConfig.swap(configLines);
bool changed = false;
if (domain & mInputDomain) {
diff --git a/media/codec2/sfplugin/CCodecConfig.h b/media/codec2/sfplugin/CCodecConfig.h
index 3bafe3f..a61c8b7 100644
--- a/media/codec2/sfplugin/CCodecConfig.h
+++ b/media/codec2/sfplugin/CCodecConfig.h
@@ -134,6 +134,8 @@
/// For now support a validation function.
std::map<C2Param::Index, LocalParamValidator> mLocalParams;
+ std::set<std::string> mLastConfig;
+
CCodecConfig();
/// initializes the members required to manage the format: descriptors, reflector,
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 702ad6f..5c8ad56 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -25,6 +25,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <nativebase/nativebase.h>
+#include <ui/Fence.h>
#include <C2AllocatorGralloc.h>
#include <C2BlockInternal.h>
@@ -590,7 +591,12 @@
std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
meta->pBuffer = 0;
- // TODO: fence
+ // TODO: wrap this in C2Fence so that the component can wait when it
+ // actually starts processing.
+ if (meta->nFenceFd >= 0) {
+ sp<Fence> fence(new Fence(meta->nFenceFd));
+ fence->waitForever(LOG_TAG);
+ }
return C2Buffer::CreateGraphicBuffer(
block->share(C2Rect(buffer->width, buffer->height), C2Fence()));
#else
diff --git a/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp b/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp
new file mode 100644
index 0000000..764fa00
--- /dev/null
+++ b/media/codec2/sfplugin/Omx2IGraphicBufferSource.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Omx2IGraphicBufferSource"
+#include <android-base/logging.h>
+
+#include "Omx2IGraphicBufferSource.h"
+
+#include <android/BnOMXBufferSource.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+#include <OMX_Component.h>
+#include <OMX_Index.h>
+#include <OMX_IndexExt.h>
+
+namespace android {
+
+namespace /* unnamed */ {
+
+// OmxGraphicBufferSource -> IOMXBufferSource
+
+struct OmxGbs2IOmxBs : public BnOMXBufferSource {
+ sp<OmxGraphicBufferSource> mBase;
+ OmxGbs2IOmxBs(sp<OmxGraphicBufferSource> const& base) : mBase{base} {}
+ BnStatus onOmxExecuting() override {
+ return mBase->onOmxExecuting();
+ }
+ BnStatus onOmxIdle() override {
+ return mBase->onOmxIdle();
+ }
+ BnStatus onOmxLoaded() override {
+ return mBase->onOmxLoaded();
+ }
+ BnStatus onInputBufferAdded(int32_t bufferId) override {
+ return mBase->onInputBufferAdded(bufferId);
+ }
+ BnStatus onInputBufferEmptied(
+ int32_t bufferId,
+ OMXFenceParcelable const& fenceParcel) override {
+ return mBase->onInputBufferEmptied(bufferId, fenceParcel.get());
+ }
+};
+
+struct OmxNodeWrapper : public IOmxNodeWrapper {
+ sp<IOMXNode> mBase;
+ OmxNodeWrapper(sp<IOMXNode> const& base) : mBase{base} {}
+ status_t emptyBuffer(
+ int32_t bufferId, uint32_t flags,
+ const sp<GraphicBuffer> &buffer,
+ int64_t timestamp, int fenceFd) override {
+ return mBase->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd);
+ }
+ void dispatchDataSpaceChanged(
+ int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+ omx_message msg{};
+ msg.type = omx_message::EVENT;
+ msg.fenceFd = -1;
+ msg.u.event_data.event = OMX_EventDataSpaceChanged;
+ msg.u.event_data.data1 = dataSpace;
+ msg.u.event_data.data2 = aspects;
+ msg.u.event_data.data3 = pixelFormat;
+ mBase->dispatchMessage(msg);
+ }
+};
+
+} // unnamed namespace
+
+// Omx2IGraphicBufferSource
+Omx2IGraphicBufferSource::Omx2IGraphicBufferSource(
+ sp<OmxGraphicBufferSource> const& base)
+ : mBase{base},
+ mOMXBufferSource{new OmxGbs2IOmxBs(base)} {
+}
+
+BnStatus Omx2IGraphicBufferSource::setSuspend(
+ bool suspend, int64_t timeUs) {
+ return BnStatus::fromStatusT(mBase->setSuspend(suspend, timeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setRepeatPreviousFrameDelayUs(
+ int64_t repeatAfterUs) {
+ return BnStatus::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setMaxFps(float maxFps) {
+ return BnStatus::fromStatusT(mBase->setMaxFps(maxFps));
+}
+
+BnStatus Omx2IGraphicBufferSource::setTimeLapseConfig(
+ double fps, double captureFps) {
+ return BnStatus::fromStatusT(mBase->setTimeLapseConfig(fps, captureFps));
+}
+
+BnStatus Omx2IGraphicBufferSource::setStartTimeUs(
+ int64_t startTimeUs) {
+ return BnStatus::fromStatusT(mBase->setStartTimeUs(startTimeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setStopTimeUs(
+ int64_t stopTimeUs) {
+ return BnStatus::fromStatusT(mBase->setStopTimeUs(stopTimeUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::getStopTimeOffsetUs(
+ int64_t *stopTimeOffsetUs) {
+ return BnStatus::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::setColorAspects(
+ int32_t aspects) {
+ return BnStatus::fromStatusT(mBase->setColorAspects(aspects));
+}
+
+BnStatus Omx2IGraphicBufferSource::setTimeOffsetUs(
+ int64_t timeOffsetsUs) {
+ return BnStatus::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs));
+}
+
+BnStatus Omx2IGraphicBufferSource::signalEndOfInputStream() {
+ return BnStatus::fromStatusT(mBase->signalEndOfInputStream());
+}
+
+BnStatus Omx2IGraphicBufferSource::configure(
+ const sp<IOMXNode>& omxNode, int32_t dataSpace) {
+ if (omxNode == NULL) {
+ return BnStatus::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ // Do setInputSurface() first, the node will try to enable metadata
+ // mode on input, and does necessary error checking. If this fails,
+ // we can't use this input surface on the node.
+ status_t err = omxNode->setInputSurface(mOMXBufferSource);
+ if (err != NO_ERROR) {
+ ALOGE("Unable to set input surface: %d", err);
+ return BnStatus::fromServiceSpecificError(err);
+ }
+
+ uint32_t consumerUsage;
+ if (omxNode->getParameter(
+ (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
+ &consumerUsage, sizeof(consumerUsage)) != OK) {
+ consumerUsage = 0;
+ }
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = 0; // kPortIndexInput
+
+ err = omxNode->getParameter(
+ OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != NO_ERROR) {
+ ALOGE("Failed to get port definition: %d", err);
+ return BnStatus::fromServiceSpecificError(UNKNOWN_ERROR);
+ }
+
+ return BnStatus::fromStatusT(mBase->configure(
+ new OmxNodeWrapper(omxNode),
+ dataSpace,
+ def.nBufferCountActual,
+ def.format.video.nFrameWidth,
+ def.format.video.nFrameHeight,
+ consumerUsage));
+}
+
+} // namespace android
+
diff --git a/media/codec2/sfplugin/Omx2IGraphicBufferSource.h b/media/codec2/sfplugin/Omx2IGraphicBufferSource.h
new file mode 100644
index 0000000..20fd1ec
--- /dev/null
+++ b/media/codec2/sfplugin/Omx2IGraphicBufferSource.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef OMX_2_IGRAPHICBUFFERSOURCE_H_
+#define OMX_2_IGRAPHICBUFFERSOURCE_H_
+
+#include <android/BnGraphicBufferSource.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
+
+namespace android {
+
+using BnStatus = ::android::binder::Status;
+
+struct Omx2IGraphicBufferSource : public BnGraphicBufferSource {
+ sp<OmxGraphicBufferSource> mBase;
+ sp<IOMXBufferSource> mOMXBufferSource;
+ Omx2IGraphicBufferSource(sp<OmxGraphicBufferSource> const& base);
+ BnStatus configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+ BnStatus setSuspend(bool suspend, int64_t timeUs) override;
+ BnStatus setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
+ BnStatus setMaxFps(float maxFps) override;
+ BnStatus setTimeLapseConfig(double fps, double captureFps) override;
+ BnStatus setStartTimeUs(int64_t startTimeUs) override;
+ BnStatus setStopTimeUs(int64_t stopTimeUs) override;
+ BnStatus getStopTimeOffsetUs(int64_t *stopTimeOffsetUs) override;
+ BnStatus setColorAspects(int32_t aspects) override;
+ BnStatus setTimeOffsetUs(int64_t timeOffsetsUs) override;
+ BnStatus signalEndOfInputStream() override;
+};
+
+} // namespace android
+
+#endif // OMX_2_IGRAPHICBUFFERSOURCE_H_
+
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index 40160c7..7334834 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -629,7 +629,7 @@
// static
std::shared_ptr<C2Mapper::ProfileLevelMapper>
C2Mapper::GetProfileLevelMapper(std::string mediaType) {
- std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower);
+ std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
if (mediaType == MIMETYPE_AUDIO_AAC) {
return std::make_shared<AacProfileLevelMapper>();
} else if (mediaType == MIMETYPE_VIDEO_AVC) {
@@ -657,7 +657,7 @@
// static
std::shared_ptr<C2Mapper::ProfileLevelMapper>
C2Mapper::GetHdrProfileLevelMapper(std::string mediaType, bool isHdr10Plus) {
- std::transform(mediaType.begin(), mediaType.begin(), mediaType.end(), ::tolower);
+ std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
if (mediaType == MIMETYPE_VIDEO_HEVC) {
return std::make_shared<HevcProfileLevelMapper>(true, isHdr10Plus);
} else if (mediaType == MIMETYPE_VIDEO_VP9) {
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index dab4697..af97e61 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -223,6 +223,22 @@
return res;
}
+ static bool MigrateNativeHandle(
+ native_handle_t *handle,
+ uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+ if (handle == nullptr || !isValid(handle)) {
+ return false;
+ }
+ ExtraData *ed = getExtraData(handle);
+ if (!ed) return false;
+ ed->generation = generation;
+ ed->igbp_id_lo = uint32_t(igbp_id & 0xFFFFFFFF);
+ ed->igbp_id_hi = uint32_t(igbp_id >> 32);
+ ed->igbp_slot = igbp_slot;
+ return true;
+ }
+
+
static native_handle_t* UnwrapNativeHandle(
const C2Handle *const handle) {
const ExtraData *xd = getExtraData(handle);
@@ -271,6 +287,13 @@
generation, igbp_id, igbp_slot);
}
+bool MigrateNativeCodec2GrallocHandle(
+ native_handle_t *handle,
+ uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot) {
+ return C2HandleGralloc::MigrateNativeHandle(handle, generation, igbp_id, igbp_slot);
+}
+
+
class C2AllocationGralloc : public C2GraphicAllocation {
public:
virtual ~C2AllocationGralloc() override;
diff --git a/media/codec2/vndk/include/C2AllocatorGralloc.h b/media/codec2/vndk/include/C2AllocatorGralloc.h
index 05d989e..ee7524e 100644
--- a/media/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/codec2/vndk/include/C2AllocatorGralloc.h
@@ -45,6 +45,16 @@
uint32_t generation = 0, uint64_t igbp_id = 0, uint32_t igbp_slot = 0);
/**
+ * When the gralloc handle is migrated to another bufferqueue, update
+ * bufferqueue information.
+ *
+ * @return {@code true} when native_handle is a wrapped codec2 handle.
+ */
+bool MigrateNativeCodec2GrallocHandle(
+ native_handle_t *handle,
+ uint32_t generation, uint64_t igbp_id, uint32_t igbp_slot);
+
+/**
* \todo Get this from the buffer
*/
void _UnwrapNativeCodec2GrallocMetadata(
diff --git a/media/codec2/vndk/internal/C2BlockInternal.h b/media/codec2/vndk/internal/C2BlockInternal.h
index 84ce70a..4ae946a 100644
--- a/media/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/codec2/vndk/internal/C2BlockInternal.h
@@ -206,23 +206,19 @@
*
* - GetBufferQueueData(): Returns generation, bqId and bqSlot.
* - HoldBlockFromBufferQueue(): Sets "held" status to true.
- * - YieldBlockToBufferQueue(): Sets "held" status to false.
- * - AssignBlockToBufferQueue(): Sets the bufferqueue assignment and
- * "held" status.
+ * - BeginTransferBlockToClient()/EndTransferBlockToClient():
+ * Clear "held" status to false if transfer was successful,
+ * otherwise "held" status remains true.
+ * - BeginAttachBlockToBufferQueue()/EndAttachBlockToBufferQueue():
+ * The will keep "held" status true if attach was eligible.
+ * Otherwise, "held" status is cleared to false. In that case,
+ * ownership of buffer should be transferred to bufferqueue.
+ * - DisplayBlockToBufferQueue()
+ * This will clear "held" status to false.
*
* All these functions operate on _C2BlockPoolData, which can be obtained by
* calling GetGraphicBlockPoolData().
*
- * HoldBlockFromBufferQueue() will mark the block as held, while
- * YieldBlockToBufferQueue() will do the opposite. These two functions do
- * not modify the bufferqueue assignment, so it is not wrong to call
- * HoldBlockFromBufferQueue() after YieldBlockToBufferQueue() if it can be
- * guaranteed that the block is not destroyed during the period between the
- * two calls.
- *
- * AssingBlockToBufferQueue() has a "held" status as an optional argument.
- * The default value is true.
- *
* Maintaining Consistency with IGraphicBufferProducer Operations
* ==============================================================
*
@@ -232,16 +228,20 @@
* information for _C2BlockPoolData, with "held" status set to true.
*
* queueBuffer()
- * - After queueBuffer() is called, YieldBlockToBufferQueue() should be
- * called.
+ * - Before queueBuffer() is called, DisplayBlockToBufferQueue() should be
+ * called to test eligibility. If it's not eligible, do not call
+ * queueBuffer().
*
- * attachBuffer()
- * - After attachBuffer() is called, AssignBlockToBufferQueue() should be
- * called with "held" status set to true.
+ * attachBuffer() - remote migration only.
+ * - Local migration on blockpool side will be done automatically by
+ * blockpool.
+ * - Before attachBuffer(), BeginAttachBlockToBufferQueue() should be called
+ * to test eligiblity.
+ * - After attachBuffer() is called, EndAttachBlockToBufferQueue() should
+ * be called. This will set "held" status to true. If it returned
+ * false, cancelBuffer() should be called.
*
- * detachBuffer()
- * - After detachBuffer() is called, HoldBlockFromBufferQueue() should be
- * called.
+ * detachBuffer() - no-op.
*/
/**
@@ -261,43 +261,12 @@
*/
static
bool GetBufferQueueData(
- const std::shared_ptr<_C2BlockPoolData>& poolData,
+ const std::shared_ptr<const _C2BlockPoolData>& poolData,
uint32_t* generation = nullptr,
uint64_t* bqId = nullptr,
int32_t* bqSlot = nullptr);
/**
- * Set bufferqueue assignment and "held" status to a block created by a
- * bufferqueue-based blockpool.
- *
- * \param poolData blockpool data associated to the block.
- * \param igbp \c IGraphicBufferProducer instance from the designated
- * bufferqueue.
- * \param generation Generation number that the buffer belongs to.
- * \param bqId Id of the bufferqueue that will own the buffer (block).
- * \param bqSlot Slot number of the buffer.
- * \param held Whether the block is held. This "held" status can be
- * changed later by calling YieldBlockToBufferQueue() or
- * HoldBlockFromBufferQueue().
- *
- * \return \c true if \p poolData is valid bufferqueue data;
- * \c false otherwise.
- *
- * Note: \p generation should match the latest generation number set on the
- * bufferqueue, and \p bqId should match the unique id for the bufferqueue
- * (obtainable by calling igbp->getUniqueId()).
- */
- static
- bool AssignBlockToBufferQueue(
- const std::shared_ptr<_C2BlockPoolData>& poolData,
- const ::android::sp<::android::hardware::graphics::bufferqueue::
- V2_0::IGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId,
- int32_t bqSlot,
- bool held = true);
-
- /**
* Hold a block from the designated bufferqueue. This causes the destruction
* of the block to trigger a call to cancelBuffer().
*
@@ -305,6 +274,9 @@
* block. It does not check if that is the case.
*
* \param poolData blockpool data associated to the block.
+ * \param owner block owner from client bufferqueue manager.
+ * If this is expired, the block is not owned by client
+ * anymore.
* \param igbp \c IGraphicBufferProducer instance to be assigned to the
* block. This is not needed when the block is local.
*
@@ -313,24 +285,96 @@
static
bool HoldBlockFromBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& poolData,
+ const std::shared_ptr<int>& owner,
const ::android::sp<::android::hardware::graphics::bufferqueue::
V2_0::IGraphicBufferProducer>& igbp = nullptr);
/**
- * Yield a block to the designated bufferqueue. This causes the destruction
- * of the block not to trigger a call to cancelBuffer();
+ * Prepare a block to be transferred to other process. This blocks
+ * bufferqueue migration from happening. The block should be in held.
*
* This function assumes that \p poolData comes from a bufferqueue-based
* block. It does not check if that is the case.
*
* \param poolData blockpool data associated to the block.
*
- * \return The previous held status.
+ * \return true if transfer is eligible, false otherwise.
*/
static
- bool YieldBlockToBufferQueue(
+ bool BeginTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& poolData);
+ /**
+ * Called after transferring the specified block is finished. Make sure
+ * that BeginTransferBlockToClient() was called before this call.
+ *
+ * This will unblock bufferqueue migration. If transfer result was
+ * successful, this causes the destruction of the block not to trigger a
+ * call to cancelBuffer().
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if transfer began before, false otherwise.
+ */
+ static
+ bool EndTransferBlockToClient(
+ const std::shared_ptr<_C2BlockPoolData>& poolData,
+ bool transferred);
+
+ /**
+ * Prepare a block to be migrated to another bufferqueue. This blocks
+ * rendering until migration has been finished. The block should be in
+ * held.
+ *
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if migration is eligible, false otherwise.
+ */
+ static
+ bool BeginAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& poolData);
+
+ /**
+ * Called after migration of the specified block is finished. Make sure
+ * that BeginAttachBlockToBufferQueue() was called before this call.
+ *
+ * This will unblock rendering. if redering is tried during migration,
+ * this returns false. In that case, cancelBuffer() should be called.
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if migration is eligible, false otherwise.
+ */
+ static
+ bool EndAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& poolData,
+ const std::shared_ptr<int>& owner,
+ const ::android::sp<::android::hardware::graphics::bufferqueue::
+ V2_0::IGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId,
+ int32_t bqSlot);
+
+ /**
+ * Indicates a block to be rendered very soon.
+ *
+ * This function assumes that \p poolData comes from a bufferqueue-based
+ * block. It does not check if that is the case.
+ *
+ * \param poolData blockpool data associated to the block.
+ *
+ * \return true if migration is eligible, false otherwise.
+ */
+ static
+ bool DisplayBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& poolData);
};
#endif // ANDROID_STAGEFRIGHT_C2BLOCK_INTERNAL_H_
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 3f40e56..8304f74 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -61,8 +61,13 @@
uint32_t generation;
uint64_t bqId;
int32_t bqSlot;
+ bool transfer; // local transfer to remote
+ bool attach; // attach on remote
+ bool display; // display on remote;
+ std::weak_ptr<int> owner;
sp<HGraphicBufferProducer> igbp;
std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
+ mutable std::mutex lock;
virtual type_t getType() const override {
return TYPE_BUFFERQUEUE;
@@ -71,7 +76,8 @@
// Create a remote BlockPoolData.
C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
- const sp<HGraphicBufferProducer>& producer = nullptr);
+ const std::shared_ptr<int> &owner,
+ const sp<HGraphicBufferProducer>& producer);
// Create a local BlockPoolData.
C2BufferQueueBlockPoolData(
@@ -80,15 +86,19 @@
virtual ~C2BufferQueueBlockPoolData() override;
+ int migrate(const sp<HGraphicBufferProducer>& producer,
+ uint32_t toGeneration, uint64_t toBqId,
+ sp<GraphicBuffer> *buffers, uint32_t oldGeneration);
};
bool _C2BlockFactory::GetBufferQueueData(
- const std::shared_ptr<_C2BlockPoolData>& data,
+ const std::shared_ptr<const _C2BlockPoolData>& data,
uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) {
if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
if (generation) {
- const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
- std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ const std::shared_ptr<const C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<const C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
*generation = poolData->generation;
if (bqId) {
*bqId = poolData->bqId;
@@ -102,32 +112,15 @@
return false;
}
-bool _C2BlockFactory::AssignBlockToBufferQueue(
- const std::shared_ptr<_C2BlockPoolData>& data,
- const sp<HGraphicBufferProducer>& igbp,
- uint32_t generation,
- uint64_t bqId,
- int32_t bqSlot,
- bool held) {
- if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
- const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
- std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- poolData->igbp = igbp;
- poolData->generation = generation;
- poolData->bqId = bqId;
- poolData->bqSlot = bqSlot;
- poolData->held = held;
- return true;
- }
- return false;
-}
-
bool _C2BlockFactory::HoldBlockFromBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data,
+ const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& igbp) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
if (!poolData->local) {
+ poolData->owner = owner;
poolData->igbp = igbp;
}
if (poolData->held) {
@@ -138,12 +131,86 @@
return true;
}
-bool _C2BlockFactory::YieldBlockToBufferQueue(
+bool _C2BlockFactory::BeginTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
- if (!poolData->held) {
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ poolData->transfer = true;
+ return true;
+}
+
+bool _C2BlockFactory::EndTransferBlockToClient(
+ const std::shared_ptr<_C2BlockPoolData>& data,
+ bool transfer) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ poolData->transfer = false;
+ if (transfer) {
poolData->held = false;
+ }
+ return true;
+}
+
+bool _C2BlockFactory::BeginAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& data) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ if (poolData->local || poolData->display ||
+ poolData->attach || !poolData->held) {
+ return false;
+ }
+ if (poolData->bqId == 0) {
+ return false;
+ }
+ poolData->attach = true;
+ return true;
+}
+
+// if display was tried during attach, buffer should be retired ASAP.
+bool _C2BlockFactory::EndAttachBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& data,
+ const std::shared_ptr<int>& owner,
+ const sp<HGraphicBufferProducer>& igbp,
+ uint32_t generation,
+ uint64_t bqId,
+ int32_t bqSlot) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ if (poolData->local || !poolData->attach ) {
+ return false;
+ }
+ if (poolData->display) {
+ poolData->attach = false;
+ poolData->held = false;
+ return false;
+ }
+ poolData->attach = false;
+ poolData->held = true;
+ poolData->owner = owner;
+ poolData->igbp = igbp;
+ poolData->generation = generation;
+ poolData->bqId = bqId;
+ poolData->bqSlot = bqSlot;
+ return true;
+}
+
+bool _C2BlockFactory::DisplayBlockToBufferQueue(
+ const std::shared_ptr<_C2BlockPoolData>& data) {
+ const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
+ std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
+ std::scoped_lock<std::mutex> lock(poolData->lock);
+ if (poolData->local || poolData->display || !poolData->held) {
+ return false;
+ }
+ if (poolData->bqId == 0) {
+ return false;
+ }
+ poolData->display = true;
+ if (poolData->attach) {
return false;
}
poolData->held = false;
@@ -175,7 +242,9 @@
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(generation,
bqId,
- (int32_t)bqSlot);
+ (int32_t)bqSlot,
+ nullptr,
+ nullptr);
block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
} else {
block = _C2BlockFactory::CreateGraphicBlock(alloc);
@@ -186,6 +255,78 @@
return nullptr;
}
+namespace {
+
+int64_t getTimestampNow() {
+ int64_t stamp;
+ struct timespec ts;
+ // TODO: CLOCK_MONOTONIC_COARSE?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stamp = ts.tv_nsec / 1000;
+ stamp += (ts.tv_sec * 1000000LL);
+ return stamp;
+}
+
+bool getGenerationNumber(const sp<HGraphicBufferProducer> &producer,
+ uint32_t *generation) {
+ status_t status{};
+ int slot{};
+ bool bufferNeedsReallocation{};
+ sp<Fence> fence = new Fence();
+
+ using Input = HGraphicBufferProducer::DequeueBufferInput;
+ using Output = HGraphicBufferProducer::DequeueBufferOutput;
+ Return<void> transResult = producer->dequeueBuffer(
+ Input{640, 480, HAL_PIXEL_FORMAT_YCBCR_420_888, 0},
+ [&status, &slot, &bufferNeedsReallocation, &fence]
+ (HStatus hStatus, int32_t hSlot, Output const& hOutput) {
+ slot = static_cast<int>(hSlot);
+ if (!h2b(hStatus, &status) || !h2b(hOutput.fence, &fence)) {
+ status = ::android::BAD_VALUE;
+ } else {
+ bufferNeedsReallocation =
+ hOutput.bufferNeedsReallocation;
+ }
+ });
+ if (!transResult.isOk() || status != android::OK) {
+ return false;
+ }
+ HFenceWrapper hFenceWrapper{};
+ if (!b2h(fence, &hFenceWrapper)) {
+ (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+ ALOGE("Invalid fence received from dequeueBuffer.");
+ return false;
+ }
+ sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
+ // N.B. This assumes requestBuffer# returns an existing allocation
+ // instead of a new allocation.
+ transResult = producer->requestBuffer(
+ slot,
+ [&status, &slotBuffer, &generation](
+ HStatus hStatus,
+ HBuffer const& hBuffer,
+ uint32_t generationNumber){
+ if (h2b(hStatus, &status) &&
+ h2b(hBuffer, &slotBuffer) &&
+ slotBuffer) {
+ *generation = generationNumber;
+ slotBuffer->setGenerationNumber(generationNumber);
+ } else {
+ status = android::BAD_VALUE;
+ }
+ });
+ if (!transResult.isOk()) {
+ return false;
+ } else if (status != android::NO_ERROR) {
+ (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+ return false;
+ }
+ (void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
+ return true;
+}
+
+};
+
class C2BufferQueueBlockPool::Impl
: public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
private:
@@ -227,6 +368,7 @@
});
if (!transResult.isOk() || status != android::OK) {
if (transResult.isOk()) {
+ ++mDqFailure;
if (status == android::INVALID_OPERATION ||
status == android::TIMED_OUT ||
status == android::WOULD_BLOCK) {
@@ -238,6 +380,8 @@
ALOGD("cannot dequeue buffer %d", status);
return C2_BAD_VALUE;
}
+ mDqFailure = 0;
+ mLastDqTs = getTimestampNow();
}
HFenceWrapper hFenceWrapper{};
if (!b2h(fence, &hFenceWrapper)) {
@@ -269,6 +413,7 @@
}
sp<GraphicBuffer> &slotBuffer = mBuffers[slot];
+ uint32_t outGeneration;
if (bufferNeedsReallocation || !slotBuffer) {
if (!slotBuffer) {
slotBuffer = new GraphicBuffer();
@@ -277,7 +422,7 @@
// instead of a new allocation.
Return<void> transResult = mProducer->requestBuffer(
slot,
- [&status, &slotBuffer](
+ [&status, &slotBuffer, &outGeneration](
HStatus hStatus,
HBuffer const& hBuffer,
uint32_t generationNumber){
@@ -285,17 +430,23 @@
h2b(hBuffer, &slotBuffer) &&
slotBuffer) {
slotBuffer->setGenerationNumber(generationNumber);
+ outGeneration = generationNumber;
} else {
status = android::BAD_VALUE;
}
});
if (!transResult.isOk()) {
+ slotBuffer.clear();
return C2_BAD_VALUE;
} else if (status != android::NO_ERROR) {
slotBuffer.clear();
(void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
return C2_BAD_VALUE;
}
+ if (mGeneration == 0) {
+ // getting generation # lazily due to dequeue failure.
+ mGeneration = outGeneration;
+ }
}
if (slotBuffer) {
ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
@@ -319,6 +470,7 @@
slotBuffer->getGenerationNumber(),
mProducerId, slot,
shared_from_this());
+ mPoolDatas[slot] = poolData;
*block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
return C2_OK;
}
@@ -331,7 +483,9 @@
public:
Impl(const std::shared_ptr<C2Allocator> &allocator)
- : mInit(C2_OK), mProducerId(0), mAllocator(allocator) {
+ : mInit(C2_OK), mProducerId(0), mGeneration(0),
+ mDqFailure(0), mLastDqTs(0), mLastDqLogTs(0),
+ mAllocator(allocator) {
}
~Impl() {
@@ -361,6 +515,19 @@
static int kMaxIgbpRetryDelayUs = 10000;
std::unique_lock<std::mutex> lock(mMutex);
+ if (mLastDqLogTs == 0) {
+ mLastDqLogTs = getTimestampNow();
+ } else {
+ int64_t now = getTimestampNow();
+ if (now >= mLastDqLogTs + 5000000) {
+ if (now >= mLastDqTs + 1000000 || mDqFailure > 5) {
+ ALOGW("last successful dequeue was %lld us ago, "
+ "%zu consecutive failures",
+ (long long)(now - mLastDqTs), mDqFailure);
+ }
+ mLastDqLogTs = now;
+ }
+ }
if (mProducerId == 0) {
std::shared_ptr<C2GraphicAllocation> alloc;
c2_status_t err = mAllocator->newGraphicAllocation(
@@ -386,12 +553,14 @@
}
void setRenderCallback(const OnRenderCallback &renderCallback) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::scoped_lock<std::mutex> lock(mMutex);
mRenderCallback = renderCallback;
}
void configureProducer(const sp<HGraphicBufferProducer> &producer) {
uint64_t producerId = 0;
+ uint32_t generation = 0;
+ bool haveGeneration = false;
if (producer) {
Return<uint64_t> transResult = producer->getUniqueId();
if (!transResult.isOk()) {
@@ -399,9 +568,19 @@
return;
}
producerId = static_cast<uint64_t>(transResult);
+ // TODO: provide gneration number from parameter.
+ haveGeneration = getGenerationNumber(producer, &generation);
+ if (!haveGeneration) {
+ ALOGW("get generationNumber failed %llu",
+ (unsigned long long)producerId);
+ }
}
+ int migrated = 0;
{
- std::lock_guard<std::mutex> lock(mMutex);
+ sp<GraphicBuffer> buffers[NUM_BUFFER_SLOTS];
+ std::weak_ptr<C2BufferQueueBlockPoolData>
+ poolDatas[NUM_BUFFER_SLOTS];
+ std::scoped_lock<std::mutex> lock(mMutex);
bool noInit = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
if (!noInit && mProducer) {
@@ -410,46 +589,88 @@
noInit = !transResult.isOk() ||
static_cast<HStatus>(transResult) == HStatus::NO_INIT;
}
- mBuffers[i].clear();
}
+ int32_t oldGeneration = mGeneration;
if (producer) {
mProducer = producer;
mProducerId = producerId;
+ mGeneration = haveGeneration ? generation : 0;
} else {
mProducer = nullptr;
mProducerId = 0;
+ mGeneration = 0;
+ ALOGW("invalid producer producer(%d), generation(%d)",
+ (bool)producer, haveGeneration);
}
+ if (mProducer && haveGeneration) { // migrate buffers
+ for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+ std::shared_ptr<C2BufferQueueBlockPoolData> data =
+ mPoolDatas[i].lock();
+ if (data) {
+ int slot = data->migrate(
+ mProducer, generation,
+ producerId, mBuffers, oldGeneration);
+ if (slot >= 0) {
+ buffers[slot] = mBuffers[i];
+ poolDatas[slot] = data;
+ ++migrated;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
+ mBuffers[i] = buffers[i];
+ mPoolDatas[i] = poolDatas[i];
+ }
+ }
+ if (producer && haveGeneration) {
+ ALOGD("local generation change %u , "
+ "bqId: %llu migrated buffers # %d",
+ generation, (unsigned long long)producerId, migrated);
}
}
private:
friend struct C2BufferQueueBlockPoolData;
- void cancel(uint64_t igbp_id, int32_t igbp_slot) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (igbp_id == mProducerId && mProducer) {
+ void cancel(uint32_t generation, uint64_t igbp_id, int32_t igbp_slot) {
+ bool cancelled = false;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (generation == mGeneration && igbp_id == mProducerId && mProducer) {
(void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk();
+ cancelled = true;
+ }
}
}
c2_status_t mInit;
uint64_t mProducerId;
+ uint32_t mGeneration;
OnRenderCallback mRenderCallback;
+ size_t mDqFailure;
+ int64_t mLastDqTs;
+ int64_t mLastDqLogTs;
+
const std::shared_ptr<C2Allocator> mAllocator;
std::mutex mMutex;
sp<HGraphicBufferProducer> mProducer;
+ sp<HGraphicBufferProducer> mSavedProducer;
sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
+ std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
};
C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
+ const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& producer) :
held(producer && bqId != 0), local(false),
generation(generation), bqId(bqId), bqSlot(bqSlot),
- igbp(producer),
+ transfer(false), attach(false), display(false),
+ owner(owner), igbp(producer),
localPool() {
}
@@ -458,6 +679,7 @@
const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
held(true), local(true),
generation(generation), bqId(bqId), bqSlot(bqSlot),
+ transfer(false), attach(false), display(false),
igbp(pool ? pool->mProducer : nullptr),
localPool(pool) {
}
@@ -466,12 +688,78 @@
if (!held || bqId == 0) {
return;
}
- if (local && localPool) {
- localPool->cancel(bqId, bqSlot);
- } else if (igbp) {
+ if (local) {
+ if (localPool) {
+ localPool->cancel(generation, bqId, bqSlot);
+ }
+ } else if (igbp && !owner.expired()) {
igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk();
}
}
+int C2BufferQueueBlockPoolData::migrate(
+ const sp<HGraphicBufferProducer>& producer,
+ uint32_t toGeneration, uint64_t toBqId,
+ sp<GraphicBuffer> *buffers, uint32_t oldGeneration) {
+ std::scoped_lock<std::mutex> l(lock);
+ if (!held || bqId == 0) {
+ ALOGV("buffer is not owned");
+ return -1;
+ }
+ if (!local || !localPool) {
+ ALOGV("pool is not local");
+ return -1;
+ }
+ if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) {
+ ALOGV("slot is not in effect");
+ return -1;
+ }
+ if (toGeneration == generation && bqId == toBqId) {
+ ALOGV("cannot migrate to same bufferqueue");
+ return -1;
+ }
+ if (oldGeneration != generation) {
+ ALOGV("cannot migrate stale buffer");
+ }
+ if (transfer) {
+ // either transferred or detached.
+ ALOGV("buffer is in transfer");
+ return -1;
+ }
+ sp<GraphicBuffer> const& graphicBuffer = buffers[bqSlot];
+ graphicBuffer->setGenerationNumber(toGeneration);
+
+ HBuffer hBuffer{};
+ uint32_t hGenerationNumber{};
+ if (!b2h(graphicBuffer, &hBuffer, &hGenerationNumber)) {
+ ALOGD("I to O conversion failed");
+ return -1;
+ }
+
+ bool converted{};
+ status_t bStatus{};
+ int slot;
+ int *outSlot = &slot;
+ Return<void> transResult =
+ producer->attachBuffer(hBuffer, hGenerationNumber,
+ [&converted, &bStatus, outSlot](
+ HStatus hStatus, int32_t hSlot, bool releaseAll) {
+ converted = h2b(hStatus, &bStatus);
+ *outSlot = static_cast<int>(hSlot);
+ if (converted && releaseAll && bStatus == android::OK) {
+ bStatus = android::INVALID_OPERATION;
+ }
+ });
+ if (!transResult.isOk() || !converted || bStatus != android::OK) {
+ ALOGD("attach failed %d", static_cast<int>(bStatus));
+ return -1;
+ }
+ ALOGV("local migration from gen %u : %u slot %d : %d",
+ generation, toGeneration, bqSlot, slot);
+ generation = toGeneration;
+ bqId = toBqId;
+ bqSlot = slot;
+ return slot;
+}
C2BufferQueueBlockPool::C2BufferQueueBlockPool(
const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 8004e75..36cab1d 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -5053,15 +5053,15 @@
mCurrentSampleInfoCount = smplcnt;
offset += 4;
size -= 4;
+ if (mCurrentDefaultSampleInfoSize != 0) {
+ ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
+ return OK;
+ }
if(smplcnt > size) {
ALOGW("b/124525515 - smplcnt(%u) > size(%ld)", (unsigned int)smplcnt, (unsigned long)size);
android_errorWriteLog(0x534e4554, "124525515");
return -EINVAL;
}
- if (mCurrentDefaultSampleInfoSize != 0) {
- ALOGV("@@@@ using default sample info size of %d", mCurrentDefaultSampleInfoSize);
- return OK;
- }
if (smplcnt > mCurrentSampleInfoAllocSize) {
uint8_t * newPtr = (uint8_t*) realloc(mCurrentSampleInfoSizes, smplcnt);
if (newPtr == NULL) {
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 4090286..1eedb12 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -73,6 +73,5 @@
"libcutils",
"libutils",
"libbinder",
- "libaudiomanager",
],
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index a6cc45b..366cc87 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -89,7 +89,11 @@
if (mAudioEndpoint.isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
- int64_t estimatedRemoteCounter = mClockModel.convertTimeToPosition(currentNanoTime);
+ // Jitter in the DSP can cause late writes to the FIFO.
+ // This might be caused by resampling.
+ // We want to read the FIFO after the latest possible time
+ // that the DSP could have written the data.
+ int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
// TODO refactor, maybe use setRemoteCounter()
mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
}
@@ -139,7 +143,7 @@
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
- wakeTime = mClockModel.convertPositionToTime(nextPosition);
+ wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
}
break;
default:
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index d26b352..9abdf53 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -19,12 +19,11 @@
#include <log/log.h>
#include <stdint.h>
+#include <algorithm>
#include "utility/AudioClock.h"
#include "IsochronousClockModel.h"
-#define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND)
-
using namespace aaudio;
IsochronousClockModel::IsochronousClockModel()
@@ -32,7 +31,7 @@
, mMarkerNanoTime(0)
, mSampleRate(48000)
, mFramesPerBurst(64)
- , mMaxLatenessInNanos(0)
+ , mMaxMeasuredLatenessNanos(0)
, mState(STATE_STOPPED)
{
}
@@ -41,8 +40,7 @@
}
void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) {
- ALOGV("setPositionAndTime(%lld, %lld)",
- (long long) framePosition, (long long) nanoTime);
+ ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime);
mMarkerFramePosition = framePosition;
mMarkerNanoTime = nanoTime;
}
@@ -54,7 +52,9 @@
}
void IsochronousClockModel::stop(int64_t nanoTime) {
- ALOGV("stop(nanos = %lld)\n", (long long) nanoTime);
+ ALOGD("stop(nanos = %lld) max lateness = %d micros\n",
+ (long long) nanoTime,
+ (int) (mMaxMeasuredLatenessNanos / 1000));
setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
// TODO should we set position?
mState = STATE_STOPPED;
@@ -69,9 +69,10 @@
}
void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
-// ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
-// (long long)framePosition,
-// (long long)nanoTime);
+ mTimestampCount++;
+// Log position and time in CSV format so we can import it easily into spreadsheets.
+ //ALOGD("%s() CSV, %d, %lld, %lld", __func__,
+ //mTimestampCount, (long long)framePosition, (long long)nanoTime);
int64_t framesDelta = framePosition - mMarkerFramePosition;
int64_t nanosDelta = nanoTime - mMarkerNanoTime;
if (nanosDelta < 1000) {
@@ -110,22 +111,54 @@
// Earlier than expected timestamp.
// This data is probably more accurate, so use it.
// Or we may be drifting due to a fast HW clock.
-// int microsDelta = (int) (nanosDelta / 1000);
-// int expectedMicrosDelta = (int) (expectedNanosDelta / 1000);
-// ALOGD("processTimestamp() - STATE_RUNNING - %7d < %7d so %4d micros EARLY",
-// microsDelta, expectedMicrosDelta, (expectedMicrosDelta - microsDelta));
+ //int microsDelta = (int) (nanosDelta / 1000);
+ //int expectedMicrosDelta = (int) (expectedNanosDelta / 1000);
+ //ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY",
+ //__func__, mTimestampCount, expectedMicrosDelta - microsDelta);
setPositionAndTime(framePosition, nanoTime);
- } else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
- // Later than expected timestamp.
-// int microsDelta = (int) (nanosDelta / 1000);
-// int expectedMicrosDeadline = (int) ((expectedNanosDelta + mMaxLatenessInNanos) / 1000);
-// ALOGD("processTimestamp() - STATE_RUNNING - %7d > %7d so %4d micros LATE",
-// microsDelta, expectedMicrosDeadline, (microsDelta - expectedMicrosDeadline));
+ } else if (nanosDelta > (expectedNanosDelta + (2 * mBurstPeriodNanos))) {
+ // In this case we do not update mMaxMeasuredLatenessNanos because it
+ // would force it too high.
+ // mMaxMeasuredLatenessNanos should range from 1 to 2 * mBurstPeriodNanos
+ //int32_t measuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
+ //ALOGD("%s() - STATE_RUNNING - #%d, lateness %d - max %d = %4d micros VERY LATE",
+ //__func__,
+ //mTimestampCount,
+ //measuredLatenessNanos / 1000,
+ //mMaxMeasuredLatenessNanos / 1000,
+ //(measuredLatenessNanos - mMaxMeasuredLatenessNanos) / 1000
+ //);
- // When we are late it may be because of preemption in the kernel or
- // we may be drifting due to a slow HW clock.
- setPositionAndTime(framePosition, nanoTime - mMaxLatenessInNanos);
+ // This typically happens when we are modelling a service instead of a DSP.
+ setPositionAndTime(framePosition, nanoTime - (2 * mBurstPeriodNanos));
+ } else if (nanosDelta > (expectedNanosDelta + mMaxMeasuredLatenessNanos)) {
+ //int32_t previousLatenessNanos = mMaxMeasuredLatenessNanos;
+ mMaxMeasuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
+
+ //ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE",
+ //__func__,
+ //mTimestampCount,
+ //mMaxMeasuredLatenessNanos / 1000,
+ //previousLatenessNanos / 1000,
+ //(mMaxMeasuredLatenessNanos - previousLatenessNanos) / 1000
+ //);
+
+ // When we are late, it may be because of preemption in the kernel,
+ // or timing jitter caused by resampling in the DSP,
+ // or we may be drifting due to a slow HW clock.
+ // We add slight drift value just in case there is actual long term drift
+ // forward caused by a slower clock.
+ // If the clock is faster than the model will get pushed earlier
+ // by the code in the preceding branch.
+ // The two opposing forces should allow the model to track the real clock
+ // over a long time.
+ int64_t driftingTime = mMarkerNanoTime + expectedNanosDelta + kDriftNanos;
+ setPositionAndTime(framePosition, driftingTime);
+ //ALOGD("%s() - #%d, max lateness = %d micros",
+ //__func__,
+ //mTimestampCount,
+ //(int) (mMaxMeasuredLatenessNanos / 1000));
}
break;
default:
@@ -145,9 +178,12 @@
update();
}
+// Update expected lateness based on sampleRate and framesPerBurst
void IsochronousClockModel::update() {
- int64_t nanosLate = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
- mMaxLatenessInNanos = (nanosLate > MIN_LATENESS_NANOS) ? nanosLate : MIN_LATENESS_NANOS;
+ mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
+ // Timestamps may be late by up to a burst because we are randomly sampling the time period
+ // after the DSP position is actually updated.
+ mMaxMeasuredLatenessNanos = mBurstPeriodNanos;
}
int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const {
@@ -190,11 +226,25 @@
return position;
}
+int32_t IsochronousClockModel::getLateTimeOffsetNanos() const {
+ // This will never be < 0 because mMaxLatenessNanos starts at
+ // mBurstPeriodNanos and only gets bigger.
+ return (mMaxMeasuredLatenessNanos - mBurstPeriodNanos) + kExtraLatenessNanos;
+}
+
+int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const {
+ return convertPositionToTime(framePosition) + getLateTimeOffsetNanos();
+}
+
+int64_t IsochronousClockModel::convertLatestTimeToPosition(int64_t nanoTime) const {
+ return convertTimeToPosition(nanoTime - getLateTimeOffsetNanos());
+}
+
void IsochronousClockModel::dump() const {
ALOGD("mMarkerFramePosition = %lld", (long long) mMarkerFramePosition);
ALOGD("mMarkerNanoTime = %lld", (long long) mMarkerNanoTime);
ALOGD("mSampleRate = %6d", mSampleRate);
ALOGD("mFramesPerBurst = %6d", mFramesPerBurst);
- ALOGD("mMaxLatenessInNanos = %6d", mMaxLatenessInNanos);
+ ALOGD("mMaxMeasuredLatenessNanos = %6d", mMaxMeasuredLatenessNanos);
ALOGD("mState = %6d", mState);
}
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 46ca48e..582bf4e 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -18,6 +18,7 @@
#define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
#include <stdint.h>
+#include "utility/AudioClock.h"
namespace aaudio {
@@ -79,6 +80,15 @@
int64_t convertPositionToTime(int64_t framePosition) const;
/**
+ * Calculate the latest estimated time that the stream will be at that position.
+ * The more jittery the clock is then the later this will be.
+ *
+ * @param framePosition
+ * @return time in nanoseconds
+ */
+ int64_t convertPositionToLatestTime(int64_t framePosition) const;
+
+ /**
* Calculate an estimated position where the stream will be at the specified time.
*
* @param nanoTime time of interest
@@ -87,6 +97,18 @@
int64_t convertTimeToPosition(int64_t nanoTime) const;
/**
+ * Calculate the corresponding estimated position based on the specified time being
+ * the latest possible time.
+ *
+ * For the same nanoTime, this may return an earlier position than
+ * convertTimeToPosition().
+ *
+ * @param nanoTime
+ * @return position in frames
+ */
+ int64_t convertLatestTimeToPosition(int64_t nanoTime) const;
+
+ /**
* @param framesDelta difference in frames
* @return duration in nanoseconds
*/
@@ -101,6 +123,9 @@
void dump() const;
private:
+
+ int32_t getLateTimeOffsetNanos() const;
+
enum clock_model_state_t {
STATE_STOPPED,
STATE_STARTING,
@@ -108,13 +133,23 @@
STATE_RUNNING
};
+ // Amount of time to drift forward when we get a late timestamp.
+ // This value was calculated to allow tracking of a clock with 50 ppm error.
+ static constexpr int32_t kDriftNanos = 10 * 1000;
+ // TODO review value of kExtraLatenessNanos
+ static constexpr int32_t kExtraLatenessNanos = 100 * 1000;
+
int64_t mMarkerFramePosition;
int64_t mMarkerNanoTime;
int32_t mSampleRate;
int32_t mFramesPerBurst;
- int32_t mMaxLatenessInNanos;
+ int32_t mBurstPeriodNanos;
+ // Includes mBurstPeriodNanos because we sample randomly over time.
+ int32_t mMaxMeasuredLatenessNanos;
clock_model_state_t mState;
+ int32_t mTimestampCount = 0;
+
void update();
};
diff --git a/media/libaudioclient/include/media/AudioParameter.h b/media/libaudioclient/include/media/AudioParameter.h
index 24837e3..7469976 100644
--- a/media/libaudioclient/include/media/AudioParameter.h
+++ b/media/libaudioclient/include/media/AudioParameter.h
@@ -67,9 +67,9 @@
// keyAudioLanguagePreferred: Preferred audio language
static const char * const keyAudioLanguagePreferred;
- // keyStreamConnect / Disconnect: value is an int in audio_devices_t
- static const char * const keyStreamConnect;
- static const char * const keyStreamDisconnect;
+ // keyDeviceConnect / Disconnect: value is an int in audio_devices_t
+ static const char * const keyDeviceConnect;
+ static const char * const keyDeviceDisconnect;
// For querying stream capabilities. All the returned values are lists.
// keyStreamSupportedFormats: audio_format_t
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index c19fcf6..0a2850f 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -302,6 +302,8 @@
for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
pContext->pBundledContext->bandGaindB[i] = EQNB_5BandSoftPresets[i];
}
+ pContext->pBundledContext->effectProcessCalled = 0;
+ pContext->pBundledContext->effectInDrain = 0;
ALOGV("\tEffectCreate - Calling LvmBundle_init");
ret = LvmBundle_init(pContext);
@@ -394,6 +396,8 @@
// Clear the instantiated flag for the effect
// protect agains the case where an effect is un-instantiated without being disabled
+
+ int &effectInDrain = pContext->pBundledContext->effectInDrain;
if(pContext->EffectType == LVM_BASS_BOOST) {
ALOGV("\tEffectRelease LVM_BASS_BOOST Clearing global intstantiated flag");
pSessionContext->bBassInstantiated = LVM_FALSE;
@@ -418,12 +422,16 @@
} else if(pContext->EffectType == LVM_VOLUME) {
ALOGV("\tEffectRelease LVM_VOLUME Clearing global intstantiated flag");
pSessionContext->bVolumeInstantiated = LVM_FALSE;
- if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE){
+ // There is no samplesToExitCount for volume so we also use the drain flag to check
+ // if we should decrement the effects enabled.
+ if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE
+ || (effectInDrain & 1 << LVM_VOLUME) != 0) {
pContext->pBundledContext->NumberEffectsEnabled--;
}
} else {
ALOGV("\tLVM_ERROR : EffectRelease : Unsupported effect\n\n\n\n\n\n\n");
}
+ effectInDrain &= ~(1 << pContext->EffectType); // no need to drain if released
// Disable effect, in this case ignore errors (return codes)
// if an effect has already been disabled
@@ -3124,8 +3132,9 @@
int Effect_setEnabled(EffectContext *pContext, bool enabled)
{
- ALOGV("\tEffect_setEnabled() type %d, enabled %d", pContext->EffectType, enabled);
-
+ ALOGV("%s effectType %d, enabled %d, currently enabled %d", __func__,
+ pContext->EffectType, enabled, pContext->pBundledContext->NumberEffectsEnabled);
+ int &effectInDrain = pContext->pBundledContext->effectInDrain;
if (enabled) {
// Bass boost or Virtualizer can be temporarily disabled if playing over device speaker due
// to their nature.
@@ -3139,6 +3148,7 @@
if(pContext->pBundledContext->SamplesToExitCountBb <= 0){
pContext->pBundledContext->NumberEffectsEnabled++;
}
+ effectInDrain &= ~(1 << LVM_BASS_BOOST);
pContext->pBundledContext->SamplesToExitCountBb =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bBassEnabled = LVM_TRUE;
@@ -3152,6 +3162,7 @@
if(pContext->pBundledContext->SamplesToExitCountEq <= 0){
pContext->pBundledContext->NumberEffectsEnabled++;
}
+ effectInDrain &= ~(1 << LVM_EQUALIZER);
pContext->pBundledContext->SamplesToExitCountEq =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bEqualizerEnabled = LVM_TRUE;
@@ -3164,6 +3175,7 @@
if(pContext->pBundledContext->SamplesToExitCountVirt <= 0){
pContext->pBundledContext->NumberEffectsEnabled++;
}
+ effectInDrain &= ~(1 << LVM_VIRTUALIZER);
pContext->pBundledContext->SamplesToExitCountVirt =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
@@ -3174,7 +3186,10 @@
ALOGV("\tEffect_setEnabled() LVM_VOLUME is already enabled");
return -EINVAL;
}
- pContext->pBundledContext->NumberEffectsEnabled++;
+ if ((effectInDrain & 1 << LVM_VOLUME) == 0) {
+ pContext->pBundledContext->NumberEffectsEnabled++;
+ }
+ effectInDrain &= ~(1 << LVM_VOLUME);
pContext->pBundledContext->bVolumeEnabled = LVM_TRUE;
break;
default:
@@ -3192,6 +3207,7 @@
return -EINVAL;
}
pContext->pBundledContext->bBassEnabled = LVM_FALSE;
+ effectInDrain |= 1 << LVM_BASS_BOOST;
break;
case LVM_EQUALIZER:
if (pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE) {
@@ -3199,6 +3215,7 @@
return -EINVAL;
}
pContext->pBundledContext->bEqualizerEnabled = LVM_FALSE;
+ effectInDrain |= 1 << LVM_EQUALIZER;
break;
case LVM_VIRTUALIZER:
if (pContext->pBundledContext->bVirtualizerEnabled == LVM_FALSE) {
@@ -3206,6 +3223,7 @@
return -EINVAL;
}
pContext->pBundledContext->bVirtualizerEnabled = LVM_FALSE;
+ effectInDrain |= 1 << LVM_VIRTUALIZER;
break;
case LVM_VOLUME:
if (pContext->pBundledContext->bVolumeEnabled == LVM_FALSE) {
@@ -3213,6 +3231,7 @@
return -EINVAL;
}
pContext->pBundledContext->bVolumeEnabled = LVM_FALSE;
+ effectInDrain |= 1 << LVM_VOLUME;
break;
default:
ALOGV("\tEffect_setEnabled() invalid effect type");
@@ -3283,6 +3302,38 @@
ALOGV("\tLVM_ERROR : Effect_process() ERROR NULL INPUT POINTER OR FRAME COUNT IS WRONG");
return -EINVAL;
}
+
+ int &effectProcessCalled = pContext->pBundledContext->effectProcessCalled;
+ int &effectInDrain = pContext->pBundledContext->effectInDrain;
+ if ((effectProcessCalled & 1 << pContext->EffectType) != 0) {
+ ALOGW("Effect %d already called", pContext->EffectType);
+ const int undrainedEffects = effectInDrain & ~effectProcessCalled;
+ if ((undrainedEffects & 1 << LVM_BASS_BOOST) != 0) {
+ ALOGW("Draining BASS_BOOST");
+ pContext->pBundledContext->SamplesToExitCountBb = 0;
+ --pContext->pBundledContext->NumberEffectsEnabled;
+ effectInDrain &= ~(1 << LVM_BASS_BOOST);
+ }
+ if ((undrainedEffects & 1 << LVM_EQUALIZER) != 0) {
+ ALOGW("Draining EQUALIZER");
+ pContext->pBundledContext->SamplesToExitCountEq = 0;
+ --pContext->pBundledContext->NumberEffectsEnabled;
+ effectInDrain &= ~(1 << LVM_EQUALIZER);
+ }
+ if ((undrainedEffects & 1 << LVM_VIRTUALIZER) != 0) {
+ ALOGW("Draining VIRTUALIZER");
+ pContext->pBundledContext->SamplesToExitCountVirt = 0;
+ --pContext->pBundledContext->NumberEffectsEnabled;
+ effectInDrain &= ~(1 << LVM_VIRTUALIZER);
+ }
+ if ((undrainedEffects & 1 << LVM_VOLUME) != 0) {
+ ALOGW("Draining VOLUME");
+ --pContext->pBundledContext->NumberEffectsEnabled;
+ effectInDrain &= ~(1 << LVM_VOLUME);
+ }
+ }
+ effectProcessCalled |= 1 << pContext->EffectType;
+
if ((pContext->pBundledContext->bBassEnabled == LVM_FALSE)&&
(pContext->EffectType == LVM_BASS_BOOST)){
//ALOGV("\tEffect_process() LVM_BASS_BOOST Effect is not enabled");
@@ -3291,9 +3342,12 @@
//ALOGV("\tEffect_process: Waiting to turn off BASS_BOOST, %d samples left",
// pContext->pBundledContext->SamplesToExitCountBb);
}
- if(pContext->pBundledContext->SamplesToExitCountBb <= 0) {
+ if (pContext->pBundledContext->SamplesToExitCountBb <= 0) {
status = -ENODATA;
- pContext->pBundledContext->NumberEffectsEnabled--;
+ if ((effectInDrain & 1 << LVM_BASS_BOOST) != 0) {
+ pContext->pBundledContext->NumberEffectsEnabled--;
+ effectInDrain &= ~(1 << LVM_BASS_BOOST);
+ }
ALOGV("\tEffect_process() this is the last frame for LVM_BASS_BOOST");
}
}
@@ -3301,7 +3355,10 @@
(pContext->EffectType == LVM_VOLUME)){
//ALOGV("\tEffect_process() LVM_VOLUME Effect is not enabled");
status = -ENODATA;
- pContext->pBundledContext->NumberEffectsEnabled--;
+ if ((effectInDrain & 1 << LVM_VOLUME) != 0) {
+ pContext->pBundledContext->NumberEffectsEnabled--;
+ effectInDrain &= ~(1 << LVM_VOLUME);
+ }
}
if ((pContext->pBundledContext->bEqualizerEnabled == LVM_FALSE)&&
(pContext->EffectType == LVM_EQUALIZER)){
@@ -3311,9 +3368,12 @@
//ALOGV("\tEffect_process: Waiting to turn off EQUALIZER, %d samples left",
// pContext->pBundledContext->SamplesToExitCountEq);
}
- if(pContext->pBundledContext->SamplesToExitCountEq <= 0) {
+ if (pContext->pBundledContext->SamplesToExitCountEq <= 0) {
status = -ENODATA;
- pContext->pBundledContext->NumberEffectsEnabled--;
+ if ((effectInDrain & 1 << LVM_EQUALIZER) != 0) {
+ pContext->pBundledContext->NumberEffectsEnabled--;
+ effectInDrain &= ~(1 << LVM_EQUALIZER);
+ }
ALOGV("\tEffect_process() this is the last frame for LVM_EQUALIZER");
}
}
@@ -3326,9 +3386,12 @@
//ALOGV("\tEffect_process: Waiting for to turn off VIRTUALIZER, %d samples left",
// pContext->pBundledContext->SamplesToExitCountVirt);
}
- if(pContext->pBundledContext->SamplesToExitCountVirt <= 0) {
+ if (pContext->pBundledContext->SamplesToExitCountVirt <= 0) {
status = -ENODATA;
- pContext->pBundledContext->NumberEffectsEnabled--;
+ if ((effectInDrain & 1 << LVM_VIRTUALIZER) != 0) {
+ pContext->pBundledContext->NumberEffectsEnabled--;
+ effectInDrain &= ~(1 << LVM_VIRTUALIZER);
+ }
ALOGV("\tEffect_process() this is the last frame for LVM_VIRTUALIZER");
}
}
@@ -3337,8 +3400,18 @@
pContext->pBundledContext->NumberEffectsCalled++;
}
- if(pContext->pBundledContext->NumberEffectsCalled ==
- pContext->pBundledContext->NumberEffectsEnabled){
+ if (pContext->pBundledContext->NumberEffectsCalled >=
+ pContext->pBundledContext->NumberEffectsEnabled) {
+
+ // We expect the # effects called to be equal to # effects enabled in sequence (including
+ // draining effects). Warn if this is not the case due to inconsistent calls.
+ ALOGW_IF(pContext->pBundledContext->NumberEffectsCalled >
+ pContext->pBundledContext->NumberEffectsEnabled,
+ "%s Number of effects called %d is greater than number of effects enabled %d",
+ __func__, pContext->pBundledContext->NumberEffectsCalled,
+ pContext->pBundledContext->NumberEffectsEnabled);
+ effectProcessCalled = 0; // reset our consistency check.
+
//ALOGV("\tEffect_process Calling process with %d effects enabled, %d called: Effect %d",
//pContext->pBundledContext->NumberEffectsEnabled,
//pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType);
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 6af4554..e4aacd0 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -110,6 +110,14 @@
#ifdef SUPPORT_MC
LVM_INT32 ChMask;
#endif
+
+ /* Bitmask whether drain is in progress due to disabling the effect.
+ The corresponding bit to an effect is set by 1 << lvm_effect_en. */
+ int effectInDrain;
+
+ /* Bitmask whether process() was called for a particular effect.
+ The corresponding bit to an effect is set by 1 << lvm_effect_en. */
+ int effectProcessCalled;
};
/* SessionContext : One session */
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index 1c95e27..060b92b 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -40,8 +40,8 @@
AUDIO_PARAMETER_KEY_AUDIO_LANGUAGE_PREFERRED;
const char * const AudioParameter::keyMonoOutput = AUDIO_PARAMETER_MONO_OUTPUT;
const char * const AudioParameter::keyStreamHwAvSync = AUDIO_PARAMETER_STREAM_HW_AV_SYNC;
-const char * const AudioParameter::keyStreamConnect = AUDIO_PARAMETER_DEVICE_CONNECT;
-const char * const AudioParameter::keyStreamDisconnect = AUDIO_PARAMETER_DEVICE_DISCONNECT;
+const char * const AudioParameter::keyDeviceConnect = AUDIO_PARAMETER_DEVICE_CONNECT;
+const char * const AudioParameter::keyDeviceDisconnect = AUDIO_PARAMETER_DEVICE_DISCONNECT;
const char * const AudioParameter::keyStreamSupportedFormats = AUDIO_PARAMETER_STREAM_SUP_FORMATS;
const char * const AudioParameter::keyStreamSupportedChannels = AUDIO_PARAMETER_STREAM_SUP_CHANNELS;
const char * const AudioParameter::keyStreamSupportedSamplingRates =
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 028bea1..d95bc8e 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -213,15 +213,14 @@
return interface_cast<IMemory>(reply.readStrongBinder());
}
- status_t getFrameAtIndex(std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly)
+ sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly)
{
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d) metaOnly(%d)",
+ index, colorFormat, metaOnly);
Parcel data, reply;
data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
- data.writeInt32(frameIndex);
- data.writeInt32(numFrames);
+ data.writeInt32(index);
data.writeInt32(colorFormat);
data.writeInt32(metaOnly);
#ifndef DISABLE_GROUP_SCHEDULE_HACK
@@ -230,16 +229,9 @@
remote()->transact(GET_FRAME_AT_INDEX, data, &reply);
status_t ret = reply.readInt32();
if (ret != NO_ERROR) {
- return ret;
+ return NULL;
}
- int retNumFrames = reply.readInt32();
- if (retNumFrames < numFrames) {
- numFrames = retNumFrames;
- }
- for (int i = 0; i < numFrames; i++) {
- frames->push_back(interface_cast<IMemory>(reply.readStrongBinder()));
- }
- return OK;
+ return interface_cast<IMemory>(reply.readStrongBinder());
}
sp<IMemory> extractAlbumArt()
@@ -442,24 +434,20 @@
case GET_FRAME_AT_INDEX: {
CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
- int frameIndex = data.readInt32();
- int numFrames = data.readInt32();
+ int index = data.readInt32();
int colorFormat = data.readInt32();
bool metaOnly = (data.readInt32() != 0);
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d), metaOnly(%d)",
+ index, colorFormat, metaOnly);
#ifndef DISABLE_GROUP_SCHEDULE_HACK
setSchedPolicy(data);
#endif
- std::vector<sp<IMemory> > frames;
- status_t err = getFrameAtIndex(
- &frames, frameIndex, numFrames, colorFormat, metaOnly);
- reply->writeInt32(err);
- if (OK == err) {
- reply->writeInt32(frames.size());
- for (size_t i = 0; i < frames.size(); i++) {
- reply->writeStrongBinder(IInterface::asBinder(frames[i]));
- }
+ sp<IMemory> frame = getFrameAtIndex(index, colorFormat, metaOnly);
+ if (frame != nullptr) { // Don't send NULL across the binder interface
+ reply->writeInt32(NO_ERROR);
+ reply->writeStrongBinder(IInterface::asBinder(frame));
+ } else {
+ reply->writeInt32(UNKNOWN_ERROR);
}
#ifndef DISABLE_GROUP_SCHEDULE_HACK
restoreSchedPolicy();
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index cb8d375..2bf0802 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -77,10 +77,13 @@
if (t != 0) {
if (enabled) {
if (t->exitPending()) {
+ mCaptureLock.unlock();
if (t->requestExitAndWait() == WOULD_BLOCK) {
+ mCaptureLock.lock();
ALOGE("Visualizer::enable() called from thread");
return INVALID_OPERATION;
}
+ mCaptureLock.lock();
}
}
t->mLock.lock();
diff --git a/media/libmedia/include/media/IMediaMetadataRetriever.h b/media/libmedia/include/media/IMediaMetadataRetriever.h
index c6f422d..28d2192 100644
--- a/media/libmedia/include/media/IMediaMetadataRetriever.h
+++ b/media/libmedia/include/media/IMediaMetadataRetriever.h
@@ -48,9 +48,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom) = 0;
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
+ virtual sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly) = 0;
virtual sp<IMemory> extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
};
diff --git a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
index 98d300f..37dc401 100644
--- a/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
+++ b/media/libmedia/include/media/MediaMetadataRetrieverInterface.h
@@ -49,9 +49,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail) = 0;
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom) = 0;
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> >* frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) = 0;
+ virtual sp<IMemory> getFrameAtIndex(
+ int frameIndex, int colorFormat, bool metaOnly) = 0;
virtual MediaAlbumArt* extractAlbumArt() = 0;
virtual const char* extractMetadata(int keyCode) = 0;
};
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index d29e97d..138a014 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -98,9 +98,8 @@
int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false, bool thumbnail = false);
sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom);
- status_t getFrameAtIndex(
- std::vector<sp<IMemory> > *frames, int frameIndex, int numFrames = 1,
- int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
+ sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
sp<IMemory> extractAlbumArt();
const char* extractMetadata(int keyCode);
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index e61b04d..2ae76b3 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -179,18 +179,16 @@
index, colorFormat, left, top, right, bottom);
}
-status_t MediaMetadataRetriever::getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> MediaMetadataRetriever::getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly) {
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d) metaOnly(%d)",
+ index, colorFormat, metaOnly);
Mutex::Autolock _l(mLock);
if (mRetriever == 0) {
ALOGE("retriever is not initialized");
- return INVALID_OPERATION;
+ return NULL;
}
- return mRetriever->getFrameAtIndex(
- frames, frameIndex, numFrames, colorFormat, metaOnly);
+ return mRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
}
const char* MediaMetadataRetriever::extractMetadata(int keyCode)
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 40b17bf..4a3c65e 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -242,31 +242,27 @@
sp<IMemory> frame = mRetriever->getImageRectAtIndex(
index, colorFormat, left, top, right, bottom);
if (frame == NULL) {
- ALOGE("failed to extract image");
- return NULL;
+ ALOGE("failed to extract image at index %d", index);
}
return frame;
}
-status_t MetadataRetrieverClient::getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
- frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> MetadataRetrieverClient::getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly) {
+ ALOGV("getFrameAtIndex: index(%d), colorFormat(%d), metaOnly(%d)",
+ index, colorFormat, metaOnly);
Mutex::Autolock lock(mLock);
Mutex::Autolock glock(sLock);
if (mRetriever == NULL) {
ALOGE("retriever is not initialized");
- return INVALID_OPERATION;
+ return NULL;
}
- status_t err = mRetriever->getFrameAtIndex(
- frames, frameIndex, numFrames, colorFormat, metaOnly);
- if (err != OK) {
- frames->clear();
- return err;
+ sp<IMemory> frame = mRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
+ if (frame == NULL) {
+ ALOGE("failed to extract frame at index %d", index);
}
- return OK;
+ return frame;
}
sp<IMemory> MetadataRetrieverClient::extractAlbumArt()
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 272d093..8020441 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -56,9 +56,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail);
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom);
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> > *frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly);
+ virtual sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly);
virtual sp<IMemory> extractAlbumArt();
virtual const char* extractMetadata(int keyCode);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 0f72c0d..4653711 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -404,8 +404,8 @@
sp<DataSource> dataSource = DataSourceFactory::CreateFromURI(
mHTTPService, uri, &mUriHeaders, &contentType,
static_cast<HTTPBase *>(mHttpSource.get()));
- mLock.lock();
mDisconnectLock.lock();
+ mLock.lock();
if (!mDisconnected) {
mDataSource = dataSource;
}
@@ -1578,7 +1578,7 @@
}
if (mPreparing) {
- notifyPreparedAndCleanup(finalStatus);
+ notifyPreparedAndCleanup(finalStatus == ERROR_END_OF_STREAM ? OK : finalStatus);
mPreparing = false;
} else if (mSentPauseOnBuffering) {
sendCacheStats();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 2f0da2d..ee463ce 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -106,16 +106,17 @@
releaseAndResetMediaBuffers();
}
-sp<AMessage> NuPlayer::Decoder::getStats() const {
+sp<AMessage> NuPlayer::Decoder::getStats() {
+ Mutex::Autolock autolock(mStatsLock);
mStats->setInt64("frames-total", mNumFramesTotal);
mStats->setInt64("frames-dropped-input", mNumInputFramesDropped);
mStats->setInt64("frames-dropped-output", mNumOutputFramesDropped);
mStats->setFloat("frame-rate-total", mFrameRateTotal);
- // i'm mutexed right now.
// make our own copy, so we aren't victim to any later changes.
sp<AMessage> copiedStats = mStats->dup();
+
return copiedStats;
}
@@ -362,13 +363,17 @@
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
- mStats->setString("mime", mime.c_str());
- mStats->setString("component-name", mComponentName.c_str());
+ {
+ Mutex::Autolock autolock(mStatsLock);
+ mStats->setString("mime", mime.c_str());
+ mStats->setString("component-name", mComponentName.c_str());
+ }
if (!mIsAudio) {
int32_t width, height;
if (mOutputFormat->findInt32("width", &width)
&& mOutputFormat->findInt32("height", &height)) {
+ Mutex::Autolock autolock(mStatsLock);
mStats->setInt32("width", width);
mStats->setInt32("height", height);
}
@@ -799,6 +804,7 @@
int32_t width, height;
if (format->findInt32("width", &width)
&& format->findInt32("height", &height)) {
+ Mutex::Autolock autolock(mStatsLock);
mStats->setInt32("width", width);
mStats->setInt32("height", height);
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 3da2f0b..4a52b0c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -34,7 +34,7 @@
const sp<Surface> &surface = NULL,
const sp<CCDecoder> &ccDecoder = NULL);
- virtual sp<AMessage> getStats() const;
+ virtual sp<AMessage> getStats();
// sets the output surface of video decoders.
virtual status_t setVideoSurface(const sp<Surface> &surface);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index d44c396..a3e0046 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -47,7 +47,7 @@
void signalResume(bool notifyComplete);
void initiateShutdown();
- virtual sp<AMessage> getStats() const {
+ virtual sp<AMessage> getStats() {
return mStats;
}
@@ -88,6 +88,7 @@
int32_t mBufferGeneration;
bool mPaused;
sp<AMessage> mStats;
+ Mutex mStatsLock;
private:
enum {
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 18a6bd8..7c620a0 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -195,14 +195,14 @@
}
status_t FrameDecoder::init(
- int64_t frameTimeUs, size_t numFrames, int option, int colorFormat) {
+ int64_t frameTimeUs, int option, int colorFormat) {
if (!getDstColorFormat(
(android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
return ERROR_UNSUPPORTED;
}
sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
- frameTimeUs, numFrames, option, &mReadOptions);
+ frameTimeUs, option, &mReadOptions);
if (videoFormat == NULL) {
ALOGE("video format or seek mode not supported");
return ERROR_UNSUPPORTED;
@@ -253,19 +253,7 @@
return NULL;
}
- return mFrames.size() > 0 ? mFrames[0] : NULL;
-}
-
-status_t FrameDecoder::extractFrames(std::vector<sp<IMemory> >* frames) {
- status_t err = extractInternal();
- if (err != OK) {
- return err;
- }
-
- for (size_t i = 0; i < mFrames.size(); i++) {
- frames->push_back(mFrames[i]);
- }
- return OK;
+ return mFrameMemory;
}
status_t FrameDecoder::extractInternal() {
@@ -404,22 +392,20 @@
const sp<MetaData> &trackMeta,
const sp<IMediaSource> &source)
: FrameDecoder(componentName, trackMeta, source),
+ mFrame(NULL),
mIsAvcOrHevc(false),
mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
- mTargetTimeUs(-1LL),
- mNumFrames(0),
- mNumFramesDecoded(0) {
+ mTargetTimeUs(-1LL) {
}
sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
- int64_t frameTimeUs, size_t numFrames, int seekMode, MediaSource::ReadOptions *options) {
+ int64_t frameTimeUs, int seekMode, MediaSource::ReadOptions *options) {
mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
if (mSeekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
mSeekMode > MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
ALOGE("Unknown seek mode: %d", mSeekMode);
return NULL;
}
- mNumFrames = numFrames;
const char *mime;
if (!trackMeta()->findCString(kKeyMIMEType, &mime)) {
@@ -495,7 +481,7 @@
return OK;
}
- *done = (++mNumFramesDecoded >= mNumFrames);
+ *done = true;
if (outputFormat == NULL) {
return ERROR_MALFORMED;
@@ -518,15 +504,18 @@
crop_bottom = height - 1;
}
- sp<IMemory> frameMem = allocVideoFrame(
- trackMeta(),
- (crop_right - crop_left + 1),
- (crop_bottom - crop_top + 1),
- 0,
- 0,
- dstBpp());
- addFrame(frameMem);
- VideoFrame* frame = static_cast<VideoFrame*>(frameMem->pointer());
+ if (mFrame == NULL) {
+ sp<IMemory> frameMem = allocVideoFrame(
+ trackMeta(),
+ (crop_right - crop_left + 1),
+ (crop_bottom - crop_top + 1),
+ 0,
+ 0,
+ dstBpp());
+ mFrame = static_cast<VideoFrame*>(frameMem->pointer());
+
+ setFrame(frameMem);
+ }
ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat());
@@ -547,8 +536,8 @@
(const uint8_t *)videoFrameBuffer->data(),
width, height, stride,
crop_left, crop_top, crop_right, crop_bottom,
- frame->getFlattenedData(),
- frame->mWidth, frame->mHeight, frame->mRowBytes,
+ mFrame->getFlattenedData(),
+ mFrame->mWidth, mFrame->mHeight, mFrame->mRowBytes,
crop_left, crop_top, crop_right, crop_bottom);
return OK;
}
@@ -577,8 +566,7 @@
}
sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
- int64_t frameTimeUs, size_t /*numFrames*/,
- int /*seekMode*/, MediaSource::ReadOptions *options) {
+ int64_t frameTimeUs, int /*seekMode*/, MediaSource::ReadOptions *options) {
sp<MetaData> overrideMeta;
if (frameTimeUs < 0) {
uint32_t type;
@@ -705,7 +693,7 @@
trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
mFrame = static_cast<VideoFrame*>(frameMem->pointer());
- addFrame(frameMem);
+ setFrame(frameMem);
}
int32_t srcFormat;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index e56750b..f579e9d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1110,12 +1110,10 @@
reset();
}
if (!isResourceError(err)) {
- if (err == OK) {
- disableLegacyBufferDropPostQ(surface);
- }
break;
}
}
+
return err;
}
@@ -1178,11 +1176,7 @@
msg->setObject("surface", surface);
sp<AMessage> response;
- status_t result = PostAndAwaitResponse(msg, &response);
- if (result == OK) {
- disableLegacyBufferDropPostQ(surface);
- }
- return result;
+ return PostAndAwaitResponse(msg, &response);
}
status_t MediaCodec::createInputSurface(
@@ -2005,6 +1999,13 @@
CHECK(msg->findMessage("input-format", &mInputFormat));
CHECK(msg->findMessage("output-format", &mOutputFormat));
+
+ // limit to confirming the opt-in behavior to minimize any behavioral change
+ if (mSurface != nullptr && !mAllowFrameDroppingBySurface) {
+ // signal frame dropping mode in the input format as this may also be
+ // meaningful and confusing for an encoder in a transcoder scenario
+ mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface);
+ }
ALOGV("[%s] configured as input format: %s, output format: %s",
mComponentName.c_str(),
mInputFormat->debugString(4).c_str(),
@@ -2437,6 +2438,11 @@
}
if (obj != NULL) {
+ if (!format->findInt32("allow-frame-drop", &mAllowFrameDroppingBySurface)) {
+ // allow frame dropping by surface by default
+ mAllowFrameDroppingBySurface = true;
+ }
+
format->setObject("native-window", obj);
status_t err = handleSetSurface(static_cast<Surface *>(obj.get()));
if (err != OK) {
@@ -2444,6 +2450,9 @@
break;
}
} else {
+ // we are not using surface so this variable is not used, but initialize sensibly anyway
+ mAllowFrameDroppingBySurface = false;
+
handleSetSurface(NULL);
}
@@ -3436,6 +3445,10 @@
if (err != OK) {
ALOGE("nativeWindowConnect returned an error: %s (%d)", strerror(-err), err);
+ } else {
+ if (!mAllowFrameDroppingBySurface) {
+ disableLegacyBufferDropPostQ(surface);
+ }
}
}
// do not return ALREADY_EXISTS unless surfaces are the same
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 3d58d4b..a267f7e 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -170,6 +170,7 @@
sp<IMediaCodecList> MediaCodecList::sRemoteList;
sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
+sp<IBinder> MediaCodecList::sMediaPlayer; // kept since linked to death
void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
Mutex::Autolock _l(sRemoteInitMutex);
@@ -181,15 +182,14 @@
sp<IMediaCodecList> MediaCodecList::getInstance() {
Mutex::Autolock _l(sRemoteInitMutex);
if (sRemoteList == nullptr) {
- sp<IBinder> binder =
- defaultServiceManager()->getService(String16("media.player"));
+ sMediaPlayer = defaultServiceManager()->getService(String16("media.player"));
sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
+ interface_cast<IMediaPlayerService>(sMediaPlayer);
if (service.get() != nullptr) {
sRemoteList = service->getCodecList();
if (sRemoteList != nullptr) {
sBinderDeathObserver = new BinderDeathObserver();
- binder->linkToDeath(sBinderDeathObserver.get());
+ sMediaPlayer->linkToDeath(sBinderDeathObserver.get());
}
}
if (sRemoteList == nullptr) {
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
index 8910463..79ffdeb 100644
--- a/media/libstagefright/OmxInfoBuilder.cpp
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -193,7 +193,7 @@
// treat OMX.google codecs as non-hardware-accelerated and non-vendor
if (!isSoftware) {
attrs |= MediaCodecInfo::kFlagIsVendor;
- if (std::count_if(
+ if (!std::count_if(
node.attributes.begin(), node.attributes.end(),
[](const IOmxStore::Attribute &i) -> bool {
return i.key == "attribute::software-codec";
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index babdc7a..8b6262f 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -36,7 +36,7 @@
using namespace android;
const int64_t kTimeoutWaitForOutputUs = 500000; // 0.5 seconds
-const int64_t kTimeoutWaitForInputUs = 5000; // 5 milliseconds
+const int64_t kTimeoutWaitForInputUs = 0; // don't wait
const int kTimeoutMaxRetries = 20;
//static
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index fa3d372..6f536a9 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -44,7 +44,7 @@
StagefrightMetadataRetriever::StagefrightMetadataRetriever()
: mParsedMetaData(false),
mAlbumArt(NULL),
- mLastImageIndex(-1) {
+ mLastDecodedIndex(-1) {
ALOGV("StagefrightMetadataRetriever()");
}
@@ -143,8 +143,8 @@
FrameRect rect = {left, top, right, bottom};
- if (mImageDecoder != NULL && index == mLastImageIndex) {
- return mImageDecoder->extractFrame(&rect);
+ if (mDecoder != NULL && index == mLastDecodedIndex) {
+ return mDecoder->extractFrame(&rect);
}
return getImageInternal(
@@ -153,6 +153,8 @@
sp<IMemory> StagefrightMetadataRetriever::getImageInternal(
int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect) {
+ mDecoder.clear();
+ mLastDecodedIndex = -1;
if (mExtractor.get() == NULL) {
ALOGE("no extractor.");
@@ -227,14 +229,14 @@
const AString &componentName = matchingCodecs[i];
sp<ImageDecoder> decoder = new ImageDecoder(componentName, trackMeta, source);
int64_t frameTimeUs = thumbnail ? -1 : 0;
- if (decoder->init(frameTimeUs, 1 /*numFrames*/, 0 /*option*/, colorFormat) == OK) {
+ if (decoder->init(frameTimeUs, 0 /*option*/, colorFormat) == OK) {
sp<IMemory> frame = decoder->extractFrame(rect);
if (frame != NULL) {
if (rect != NULL) {
// keep the decoder if slice decoding
- mImageDecoder = decoder;
- mLastImageIndex = index;
+ mDecoder = decoder;
+ mLastDecodedIndex = index;
}
return frame;
}
@@ -242,6 +244,7 @@
ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
}
+ ALOGE("all codecs failed to extract frame.");
return NULL;
}
@@ -250,36 +253,40 @@
ALOGV("getFrameAtTime: %" PRId64 " us option: %d colorFormat: %d, metaOnly: %d",
timeUs, option, colorFormat, metaOnly);
- sp<IMemory> frame;
- status_t err = getFrameInternal(
- timeUs, 1, option, colorFormat, metaOnly, &frame, NULL /*outFrames*/);
- return (err == OK) ? frame : NULL;
+ return getFrameInternal(timeUs, option, colorFormat, metaOnly);
}
-status_t StagefrightMetadataRetriever::getFrameAtIndex(
- std::vector<sp<IMemory> >* frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly) {
- ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d, colorFormat: %d, metaOnly: %d",
- frameIndex, numFrames, colorFormat, metaOnly);
+sp<IMemory> StagefrightMetadataRetriever::getFrameAtIndex(
+ int frameIndex, int colorFormat, bool metaOnly) {
+ ALOGV("getFrameAtIndex: frameIndex %d, colorFormat: %d, metaOnly: %d",
+ frameIndex, colorFormat, metaOnly);
+ if (mDecoder != NULL && frameIndex == mLastDecodedIndex + 1) {
+ sp<IMemory> frame = mDecoder->extractFrame();
+ if (frame != nullptr) {
+ mLastDecodedIndex = frameIndex;
+ }
+ return frame;
+ }
- return getFrameInternal(
- frameIndex, numFrames, MediaSource::ReadOptions::SEEK_FRAME_INDEX,
- colorFormat, metaOnly, NULL /*outFrame*/, frames);
+ return getFrameInternal(frameIndex,
+ MediaSource::ReadOptions::SEEK_FRAME_INDEX, colorFormat, metaOnly);
}
-status_t StagefrightMetadataRetriever::getFrameInternal(
- int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
- sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames) {
+sp<IMemory> StagefrightMetadataRetriever::getFrameInternal(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly) {
+ mDecoder.clear();
+ mLastDecodedIndex = -1;
+
if (mExtractor.get() == NULL) {
ALOGE("no extractor.");
- return NO_INIT;
+ return NULL;
}
sp<MetaData> fileMeta = mExtractor->getMetaData();
if (fileMeta == NULL) {
ALOGE("extractor doesn't publish metadata, failed to initialize?");
- return NO_INIT;
+ return NULL;
}
size_t n = mExtractor->countTracks();
@@ -300,30 +307,24 @@
if (i == n) {
ALOGE("no video track found.");
- return INVALID_OPERATION;
+ return NULL;
}
sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
i, MediaExtractor::kIncludeExtensiveMetaData);
if (!trackMeta) {
- return UNKNOWN_ERROR;
+ return NULL;
}
if (metaOnly) {
- if (outFrame != NULL) {
- *outFrame = FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
- if (*outFrame != NULL) {
- return OK;
- }
- }
- return UNKNOWN_ERROR;
+ return FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
}
sp<IMediaSource> source = mExtractor->getTrack(i);
if (source.get() == NULL) {
ALOGV("unable to instantiate video track.");
- return UNKNOWN_ERROR;
+ return NULL;
}
const void *data;
@@ -350,24 +351,22 @@
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const AString &componentName = matchingCodecs[i];
sp<VideoFrameDecoder> decoder = new VideoFrameDecoder(componentName, trackMeta, source);
- if (decoder->init(timeUs, numFrames, option, colorFormat) == OK) {
- if (outFrame != NULL) {
- *outFrame = decoder->extractFrame();
- if (*outFrame != NULL) {
- return OK;
+ if (decoder->init(timeUs, option, colorFormat) == OK) {
+ sp<IMemory> frame = decoder->extractFrame();
+ if (frame != nullptr) {
+ // keep the decoder if seeking by frame index
+ if (option == MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
+ mDecoder = decoder;
+ mLastDecodedIndex = timeUs;
}
- } else if (outFrames != NULL) {
- status_t err = decoder->extractFrames(outFrames);
- if (err == OK) {
- return OK;
- }
+ return frame;
}
}
ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
}
ALOGE("all codecs failed to extract frame.");
- return UNKNOWN_ERROR;
+ return NULL;
}
MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp
index 48414d7..5880e32 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/packet_util.cpp
@@ -52,7 +52,11 @@
PV_BitstreamByteAlign(stream);
BitstreamReadBits32(stream, resync_marker_length);
- *next_MB = (int) BitstreamReadBits16(stream, nbits);
+ int mbnum = (int) BitstreamReadBits16(stream, nbits);
+ if (mbnum < 0) {
+ return PV_FAIL;
+ }
+ *next_MB = mbnum;
// if (*next_MB <= video->mbnum) /* needs more investigation */
// *next_MB = video->mbnum+1;
diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
index da86758..87e8fd4 100644
--- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
+++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
@@ -1426,75 +1426,90 @@
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
UWORD32 ui_exec_done;
+ WORD32 i_num_preroll = 0;
/* Checking for end of processing */
err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DONE_QUERY,
&ui_exec_done);
RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DONE_QUERY");
-#ifdef ENABLE_MPEG_D_DRC
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES,
+ &i_num_preroll);
+
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES");
{
- if (ui_exec_done != 1) {
- VOID* p_array; // ITTIAM:buffer to handle gain payload
- WORD32 buf_size = 0; // ITTIAM:gain payload length
- WORD32 bit_str_fmt = 1;
- WORD32 gain_stream_flag = 1;
+ int32_t pi_preroll_frame_offset = 0;
+ do {
+#ifdef ENABLE_MPEG_D_DRC
+ if (ui_exec_done != 1) {
+ VOID* p_array; // ITTIAM:buffer to handle gain payload
+ WORD32 buf_size = 0; // ITTIAM:gain payload length
+ WORD32 bit_str_fmt = 1;
+ WORD32 gain_stream_flag = 1;
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
- IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
- RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+ IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+ RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
- if (buf_size > 0) {
- /*Set bitstream_split_format */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ if (buf_size > 0) {
+ /*Set bitstream_split_format */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- memcpy(mDrcInBuf, p_array, buf_size);
- /* Set number of bytes to be processed */
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ memcpy(mDrcInBuf, p_array, buf_size);
+ /* Set number of bytes to be processed */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS,
+ 0, &buf_size);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
- IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+ IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG,
+ &gain_stream_flag);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- /* Execute process */
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
- IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
- RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+ /* Execute process */
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+ IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL);
+ RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
- mMpegDDRCPresent = 1;
+ mMpegDDRCPresent = 1;
+ }
}
- }
- }
#endif
- /* How much buffer is used in input buffers */
- err_code =
- ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, 0, bytesConsumed);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+ /* How much buffer is used in input buffers */
+ err_code =
+ ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF,
+ 0, bytesConsumed);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF");
- /* Get the output bytes */
- err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
+ /* Get the output bytes */
+ err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+ IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES");
#ifdef ENABLE_MPEG_D_DRC
- if (mMpegDDRCPresent == 1) {
- memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
- err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
- RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+ if (mMpegDDRCPresent == 1) {
+ memcpy(mDrcInBuf, mOutputBuffer + pi_preroll_frame_offset, *outBytes);
+ pi_preroll_frame_offset += *outBytes;
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES,
+ 0, outBytes);
+ RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
- err_code =
- ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, NULL);
- RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+ err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE,
+ IA_CMD_TYPE_DO_EXECUTE, NULL);
+ RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
- memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
- }
+ memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+ }
#endif
+ i_num_preroll--;
+ } while (i_num_preroll > 0);
+ }
return IA_NO_ERROR;
}
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index d685321..c7dc415 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -324,8 +324,8 @@
}
#define DECLARE_YUV2RGBFUNC(func, rgb) int (*func)( \
- const uint8*, int, const uint8*, int, \
- const uint8*, int, uint8*, int, int, int) \
+ const uint8_t*, int, const uint8_t*, int, \
+ const uint8_t*, int, uint8_t*, int, int, int) \
= mSrcColorSpace.isBt709() ? libyuv::H420To##rgb \
: mSrcColorSpace.isJpeg() ? libyuv::J420To##rgb \
: libyuv::I420To##rgb
@@ -350,7 +350,7 @@
{
DECLARE_YUV2RGBFUNC(func, RGB565);
(*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
- (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
break;
}
@@ -358,7 +358,7 @@
{
DECLARE_YUV2RGBFUNC(func, ABGR);
(*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
- (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
break;
}
@@ -366,7 +366,7 @@
{
DECLARE_YUV2RGBFUNC(func, ARGB);
(*func)(src_y, src.mStride, src_u, src.mStride / 2, src_v, src.mStride / 2,
- (uint8 *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
+ (uint8_t *)dst_ptr, dst.mStride, src.cropWidth(), src.cropHeight());
break;
}
@@ -391,17 +391,17 @@
switch (mDstFormat) {
case OMX_COLOR_Format16bitRGB565:
- libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+ libyuv::NV12ToRGB565(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
dst.mStride, src.cropWidth(), src.cropHeight());
break;
case OMX_COLOR_Format32bitBGRA8888:
- libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+ libyuv::NV12ToARGB(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
dst.mStride, src.cropWidth(), src.cropHeight());
break;
case OMX_COLOR_Format32BitRGBA8888:
- libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8 *)dst_ptr,
+ libyuv::NV12ToABGR(src_y, src.mStride, src_u, src.mStride, (uint8_t *)dst_ptr,
dst.mStride, src.cropWidth(), src.cropHeight());
break;
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index 04041eb..a07eb8c 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -77,7 +77,7 @@
<Limit name="bitrate" range="1-40000000" />
<Feature name="adaptive-playback" />
</MediaCodec>
- <MediaCodec name="c2.android.av1.decoder" type="video/av01">
+ <MediaCodec name="c2.android.gav1.decoder" type="video/av01">
<Limit name="size" min="96x96" max="1920x1080" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index 67d3f1a..9532ba6 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -182,7 +182,7 @@
</Variant>
<Feature name="adaptive-playback" />
</MediaCodec>
- <MediaCodec name="c2.android.av1.decoder" type="video/av01" variant="!slow-cpu">
+ <MediaCodec name="c2.android.gav1.decoder" type="video/av01" variant="!slow-cpu">
<Limit name="size" min="2x2" max="1920x1080" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="16x16" />
diff --git a/media/libstagefright/exports.lds b/media/libstagefright/exports.lds
index 06c4f19..f5ddf1e 100644
--- a/media/libstagefright/exports.lds
+++ b/media/libstagefright/exports.lds
@@ -395,7 +395,6 @@
ScaleFilterCols_NEON*;
ScaleFilterReduce;
ScaleFilterRows_NEON*;
- ScaleOffset;
ScalePlane;
ScalePlane_16;
ScalePlaneBilinearDown;
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index dc58c15..ce37ae8 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -44,13 +44,10 @@
const sp<MetaData> &trackMeta,
const sp<IMediaSource> &source);
- status_t init(
- int64_t frameTimeUs, size_t numFrames, int option, int colorFormat);
+ status_t init(int64_t frameTimeUs, int option, int colorFormat);
sp<IMemory> extractFrame(FrameRect *rect = NULL);
- status_t extractFrames(std::vector<sp<IMemory> >* frames);
-
static sp<IMemory> getMetadataOnly(
const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail = false);
@@ -59,7 +56,6 @@
virtual sp<AMessage> onGetFormatAndSeekOptions(
int64_t frameTimeUs,
- size_t numFrames,
int seekMode,
MediaSource::ReadOptions *options) = 0;
@@ -80,10 +76,7 @@
sp<MetaData> trackMeta() const { return mTrackMeta; }
OMX_COLOR_FORMATTYPE dstFormat() const { return mDstFormat; }
int32_t dstBpp() const { return mDstBpp; }
-
- void addFrame(const sp<IMemory> &frame) {
- mFrames.push_back(frame);
- }
+ void setFrame(const sp<IMemory> &frameMem) { mFrameMemory = frameMem; }
private:
AString mComponentName;
@@ -91,7 +84,7 @@
sp<IMediaSource> mSource;
OMX_COLOR_FORMATTYPE mDstFormat;
int32_t mDstBpp;
- std::vector<sp<IMemory> > mFrames;
+ sp<IMemory> mFrameMemory;
MediaSource::ReadOptions mReadOptions;
sp<MediaCodec> mDecoder;
sp<AMessage> mOutputFormat;
@@ -112,7 +105,6 @@
protected:
virtual sp<AMessage> onGetFormatAndSeekOptions(
int64_t frameTimeUs,
- size_t numFrames,
int seekMode,
MediaSource::ReadOptions *options) override;
@@ -134,11 +126,10 @@
bool *done) override;
private:
+ VideoFrame *mFrame;
bool mIsAvcOrHevc;
MediaSource::ReadOptions::SeekMode mSeekMode;
int64_t mTargetTimeUs;
- size_t mNumFrames;
- size_t mNumFramesDecoded;
};
struct ImageDecoder : public FrameDecoder {
@@ -150,7 +141,6 @@
protected:
virtual sp<AMessage> onGetFormatAndSeekOptions(
int64_t frameTimeUs,
- size_t numFrames,
int seekMode,
MediaSource::ReadOptions *options) override;
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index c50677a..ee51290 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -26,7 +26,7 @@
namespace android {
class DataSource;
-struct ImageDecoder;
+struct FrameDecoder;
struct FrameRect;
struct StagefrightMetadataRetriever : public MediaMetadataRetrieverBase {
@@ -47,9 +47,8 @@
int index, int colorFormat, bool metaOnly, bool thumbnail);
virtual sp<IMemory> getImageRectAtIndex(
int index, int colorFormat, int left, int top, int right, int bottom);
- virtual status_t getFrameAtIndex(
- std::vector<sp<IMemory> >* frames,
- int frameIndex, int numFrames, int colorFormat, bool metaOnly);
+ virtual sp<IMemory> getFrameAtIndex(
+ int index, int colorFormat, bool metaOnly);
virtual MediaAlbumArt *extractAlbumArt();
virtual const char *extractMetadata(int keyCode);
@@ -62,17 +61,17 @@
KeyedVector<int, String8> mMetaData;
MediaAlbumArt *mAlbumArt;
- sp<ImageDecoder> mImageDecoder;
- int mLastImageIndex;
+ sp<FrameDecoder> mDecoder;
+ int mLastDecodedIndex;
void parseMetaData();
void parseColorAspects(const sp<MetaData>& meta);
// Delete album art and clear metadata.
void clearMetadata();
- status_t getFrameInternal(
- int64_t timeUs, int numFrames, int option, int colorFormat, bool metaOnly,
- sp<IMemory>* outFrame, std::vector<sp<IMemory> >* outFrames);
- virtual sp<IMemory> getImageInternal(
+ sp<IMemory> getFrameInternal(
+ int64_t timeUs, int option, int colorFormat, bool metaOnly);
+
+ sp<IMemory> getImageInternal(
int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect);
StagefrightMetadataRetriever(const StagefrightMetadataRetriever &);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 6cd4265..89cca63 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -340,6 +340,7 @@
int32_t mVideoWidth;
int32_t mVideoHeight;
int32_t mRotationDegrees;
+ int32_t mAllowFrameDroppingBySurface;
// initial create parameters
AString mInitName;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index e44b0a4..e681d25 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -83,6 +83,7 @@
};
static sp<BinderDeathObserver> sBinderDeathObserver;
+ static sp<IBinder> sMediaPlayer;
static sp<IMediaCodecList> sCodecList;
static sp<IMediaCodecList> sRemoteList;
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index e260cae..7d03d98 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -72,7 +72,6 @@
cfi: true,
},
- compile_multilib: "32",
}
cc_library_shared {
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 6160c3c..6adf563 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -966,7 +966,7 @@
(strcmp(name, "/") == 0) || (strcmp(basename(name), name) != 0)) {
char errMsg[80];
- sprintf(errMsg, "Invalid name: %s", (const char *) name);
+ snprintf(errMsg, sizeof(errMsg), "Invalid name: %s", (const char *) name);
ALOGE("%s (b/130656917)", errMsg);
android_errorWriteWithInfoLog(SN_EVENT_LOG_ID, "130656917", -1, errMsg,
strlen(errMsg));
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index baa4fc7..830f752 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -113,12 +113,12 @@
void
AImageReader::FrameListener::onFrameAvailable(const BufferItem& /*item*/) {
- Mutex::Autolock _l(mLock);
sp<AImageReader> reader = mReader.promote();
if (reader == nullptr) {
ALOGW("A frame is available after AImageReader closed!");
return; // reader has been closed
}
+ Mutex::Autolock _l(mLock);
if (mListener.onImageAvailable == nullptr) {
return; // No callback registered
}
@@ -143,12 +143,12 @@
void
AImageReader::BufferRemovedListener::onBufferFreed(const wp<GraphicBuffer>& graphicBuffer) {
- Mutex::Autolock _l(mLock);
sp<AImageReader> reader = mReader.promote();
if (reader == nullptr) {
ALOGW("A frame is available after AImageReader closed!");
return; // reader has been closed
}
+ Mutex::Autolock _l(mLock);
if (mListener.onBufferRemoved == nullptr) {
return; // No callback registered
}
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index e328cb1..19bd704 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -134,7 +134,7 @@
private:
AImageReader_ImageListener mListener = {nullptr, nullptr};
- wp<AImageReader> mReader;
+ const wp<AImageReader> mReader;
Mutex mLock;
};
sp<FrameListener> mFrameListener;
@@ -149,7 +149,7 @@
private:
AImageReader_BufferRemovedListener mListener = {nullptr, nullptr};
- wp<AImageReader> mReader;
+ const wp<AImageReader> mReader;
Mutex mLock;
};
sp<BufferRemovedListener> mBufferRemovedListener;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0b745ac..355d945 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1357,8 +1357,8 @@
String8(AudioParameter::keyFrameCount),
String8(AudioParameter::keyInputSource),
String8(AudioParameter::keyMonoOutput),
- String8(AudioParameter::keyStreamConnect),
- String8(AudioParameter::keyStreamDisconnect),
+ String8(AudioParameter::keyDeviceConnect),
+ String8(AudioParameter::keyDeviceDisconnect),
String8(AudioParameter::keyStreamSupportedFormats),
String8(AudioParameter::keyStreamSupportedChannels),
String8(AudioParameter::keyStreamSupportedSamplingRates),
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 28ad9dd..f81dacf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3956,6 +3956,32 @@
return INVALID_OPERATION;
}
+// For dedicated VoIP outputs, let the HAL apply the stream volume. Track volume is
+// still applied by the mixer.
+// All tracks attached to a mixer with flag VOIP_RX are tied to the same
+// stream type STREAM_VOICE_CALL so this will only change the HAL volume once even
+// if more than one track are active
+status_t AudioFlinger::PlaybackThread::handleVoipVolume_l(float *volume)
+{
+ status_t result = NO_ERROR;
+ if ((mOutput->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) != 0) {
+ if (*volume != mLeftVolFloat) {
+ result = mOutput->stream->setVolume(*volume, *volume);
+ ALOGE_IF(result != OK,
+ "Error when setting output stream volume: %d", result);
+ if (result == NO_ERROR) {
+ mLeftVolFloat = *volume;
+ }
+ }
+ // if stream volume was successfully sent to the HAL, mLeftVolFloat == v here and we
+ // remove stream volume contribution from software volume.
+ if (mLeftVolFloat == *volume) {
+ *volume = 1.0f;
+ }
+ }
+ return result;
+}
+
status_t AudioFlinger::MixerThread::createAudioPatch_l(const struct audio_patch *patch,
audio_patch_handle_t *handle)
{
@@ -4758,22 +4784,25 @@
// no acknowledgement required for newly active tracks
}
sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
+ float volume;
+ if (track->isPlaybackRestricted() || mStreamTypes[track->streamType()].mute) {
+ volume = 0.f;
+ } else {
+ volume = masterVolume * mStreamTypes[track->streamType()].volume;
+ }
+
+ handleVoipVolume_l(&volume);
+
// cache the combined master volume and stream type volume for fast mixer; this
// lacks any synchronization or barrier so VolumeProvider may read a stale value
const float vh = track->getVolumeHandler()->getVolume(
- proxy->framesReleased()).first;
- float volume;
- if (track->isPlaybackRestricted()) {
- volume = 0.f;
- } else {
- volume = masterVolume
- * mStreamTypes[track->streamType()].volume
- * vh;
- }
+ proxy->framesReleased()).first;
+ volume *= vh;
track->mCachedVolume = volume;
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr));
float vrf = volume * float_from_gain(gain_minifloat_unpack_right(vlr));
+
track->setFinalVolume((vlf + vrf) / 2.f);
++fastTracks;
} else {
@@ -4916,20 +4945,22 @@
uint32_t vl, vr; // in U8.24 integer format
float vlf, vrf, vaf; // in [0.0, 1.0] float format
// read original volumes with volume control
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = masterVolume * typeVolume;
+ float v = masterVolume * mStreamTypes[track->streamType()].volume;
// Always fetch volumeshaper volume to ensure state is updated.
const sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
const float vh = track->getVolumeHandler()->getVolume(
track->mAudioTrackServerProxy->framesReleased()).first;
- if (track->isPausing() || mStreamTypes[track->streamType()].mute
- || track->isPlaybackRestricted()) {
+ if (mStreamTypes[track->streamType()].mute || track->isPlaybackRestricted()) {
+ v = 0;
+ }
+
+ handleVoipVolume_l(&v);
+
+ if (track->isPausing()) {
vl = vr = 0;
vlf = vrf = vaf = 0.;
- if (track->isPausing()) {
- track->setPaused();
- }
+ track->setPaused();
} else {
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
@@ -4981,25 +5012,6 @@
track->mHasVolumeController = false;
}
- // For dedicated VoIP outputs, let the HAL apply the stream volume. Track volume is
- // still applied by the mixer.
- if ((mOutput->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) != 0) {
- v = mStreamTypes[track->streamType()].mute ? 0.0f : v;
- if (v != mLeftVolFloat) {
- status_t result = mOutput->stream->setVolume(v, v);
- ALOGE_IF(result != OK, "Error when setting output stream volume: %d", result);
- if (result == OK) {
- mLeftVolFloat = v;
- }
- }
- // if stream volume was successfully sent to the HAL, mLeftVolFloat == v here and we
- // remove stream volume contribution from software volume.
- if (v != 0.0f && mLeftVolFloat == v) {
- vlf = min(1.0f, vlf / v);
- vrf = min(1.0f, vrf / v);
- vaf = min(1.0f, vaf / v);
- }
- }
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(trackId, track);
mAudioMixer->enable(trackId);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 336c2b4..fc8aa13 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -747,6 +747,7 @@
// is safe to do so. That will drop the final ref count and destroy the tracks.
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
void removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
+ status_t handleVoipVolume_l(float *volume);
// StreamOutHalInterfaceCallback implementation
virtual void onWriteReady();
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index 96a8337..1f9b725 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -333,9 +333,10 @@
if (encodedFormat != AUDIO_FORMAT_DEFAULT) {
moduleDevice->setEncodedFormat(encodedFormat);
}
- moduleDevice->setAddress(devAddress);
if (allowToCreate) {
moduleDevice->attach(hwModule);
+ moduleDevice->setAddress(devAddress);
+ moduleDevice->setName(String8(name));
}
return moduleDevice;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a9e687c..edab95a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -94,7 +94,7 @@
{
AudioParameter param(device->address());
const String8 key(state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE ?
- AudioParameter::keyStreamConnect : AudioParameter::keyStreamDisconnect);
+ AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect);
param.addInt(key, device->type());
mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
}
@@ -472,6 +472,10 @@
std::unordered_set<audio_format_t> formatSet;
sp<HwModule> primaryModule =
mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY);
+ if (primaryModule == nullptr) {
+ ALOGE("%s() unable to get primary module", __func__);
+ return NO_INIT;
+ }
DeviceVector declaredDevices = primaryModule->getDeclaredDevices().getDevicesFromTypeMask(
AUDIO_DEVICE_OUT_ALL_A2DP);
for (const auto& device : declaredDevices) {
@@ -836,7 +840,7 @@
// if explicitly requested
static const uint32_t kRelevantFlags =
(AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD |
- AUDIO_OUTPUT_FLAG_VOIP_RX);
+ AUDIO_OUTPUT_FLAG_VOIP_RX | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ);
flags =
(audio_output_flags_t)((flags & kRelevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
}
@@ -2236,16 +2240,22 @@
return status;
}
- // increment activity count before calling getNewInputDevice() below as only active sessions
+ // increment activity count before calling getNewInputDevice() below as only active sessions
// are considered for device selection
inputDesc->setClientActive(client, true);
// indicate active capture to sound trigger service if starting capture from a mic on
// primary HW module
sp<DeviceDescriptor> device = getNewInputDevice(inputDesc);
- setInputDevice(input, device, true /* force */);
+ if (device != nullptr) {
+ status = setInputDevice(input, device, true /* force */);
+ } else {
+ ALOGW("%s no new input device can be found for descriptor %d",
+ __FUNCTION__, inputDesc->getId());
+ status = BAD_VALUE;
+ }
- if (inputDesc->activeCount() == 1) {
+ if (status == NO_ERROR && inputDesc->activeCount() == 1) {
sp<AudioPolicyMix> policyMix = inputDesc->mPolicyMix.promote();
// if input maps to a dynamic policy with an activity listener, notify of state change
if ((policyMix != NULL)
@@ -2276,11 +2286,16 @@
address, "remote-submix", AUDIO_FORMAT_DEFAULT);
}
}
+ } else if (status != NO_ERROR) {
+ // Restore client activity state.
+ inputDesc->setClientActive(client, false);
+ inputDesc->stop();
}
- ALOGV("%s input %d source = %d exit", __FUNCTION__, input, client->source());
+ ALOGV("%s input %d source = %d status = %d exit",
+ __FUNCTION__, input, client->source(), status);
- return NO_ERROR;
+ return status;
}
status_t AudioPolicyManager::stopInput(audio_port_handle_t portId)
@@ -5845,11 +5860,11 @@
if (isVoiceVolSrc || isBtScoVolSrc) {
float voiceVolume;
- // Force voice volume to max for bluetooth SCO as volume is managed by the headset
+ // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
if (isVoiceVolSrc) {
voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
} else {
- voiceVolume = 1.0;
+ voiceVolume = index == 0 ? 0.0 : 1.0;
}
if (voiceVolume != mLastVoiceVolume) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 77f7997..85ea94f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -29,7 +29,6 @@
#include <utils/Log.h>
#include <cutils/properties.h>
#include <binder/IPCThreadState.h>
-#include <binder/ActivityManager.h>
#include <binder/PermissionController.h>
#include <binder/IResultReceiver.h>
#include <utils/String16.h>
@@ -774,28 +773,26 @@
// ----------- AudioPolicyService::UidPolicy implementation ----------
void AudioPolicyService::UidPolicy::registerSelf() {
- ActivityManager am;
- am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+ status_t res = mAm.linkToDeath(this);
+ mAm.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
| ActivityManager::UID_OBSERVER_IDLE
| ActivityManager::UID_OBSERVER_ACTIVE
| ActivityManager::UID_OBSERVER_PROCSTATE,
ActivityManager::PROCESS_STATE_UNKNOWN,
String16("audioserver"));
- status_t res = am.linkToDeath(this);
if (!res) {
Mutex::Autolock _l(mLock);
mObserverRegistered = true;
} else {
ALOGE("UidPolicy::registerSelf linkToDeath failed: %d", res);
- am.unregisterUidObserver(this);
+ mAm.unregisterUidObserver(this);
}
}
void AudioPolicyService::UidPolicy::unregisterSelf() {
- ActivityManager am;
- am.unlinkToDeath(this);
- am.unregisterUidObserver(this);
+ mAm.unlinkToDeath(this);
+ mAm.unregisterUidObserver(this);
Mutex::Autolock _l(mLock);
mObserverRegistered = false;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index e467f70..74aea0d 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -23,6 +23,7 @@
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
+#include <binder/ActivityManager.h>
#include <binder/BinderService.h>
#include <binder/IUidObserver.h>
#include <system/audio.h>
@@ -387,6 +388,7 @@
wp<AudioPolicyService> mService;
Mutex mLock;
+ ActivityManager mAm;
bool mObserverRegistered;
std::unordered_map<uid_t, std::pair<bool, int>> mOverrideUids;
std::unordered_map<uid_t, std::pair<bool, int>> mCachedUids;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 3e62102..a4868bf 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1149,6 +1149,8 @@
clientPid,
states[states.size() - 1]);
+ resource_policy::ClientPriority clientPriority = clientDescriptor->getPriority();
+
// Find clients that would be evicted
auto evicted = mActiveClientManager.wouldEvict(clientDescriptor);
@@ -1166,8 +1168,7 @@
String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
"(PID %d, score %d state %d) due to eviction policy", curTime.string(),
cameraId.string(), packageName.string(), clientPid,
- priorityScores[priorityScores.size() - 1],
- states[states.size() - 1]);
+ clientPriority.getScore(), clientPriority.getState());
for (auto& i : incompatibleClients) {
msg.appendFormat("\n - Blocked by existing device %s client for package %s"
@@ -1212,9 +1213,8 @@
i->getKey().string(), String8{clientSp->getPackageName()}.string(),
i->getOwnerId(), i->getPriority().getScore(),
i->getPriority().getState(), cameraId.string(),
- packageName.string(), clientPid,
- priorityScores[priorityScores.size() - 1],
- states[states.size() - 1]));
+ packageName.string(), clientPid, clientPriority.getScore(),
+ clientPriority.getState()));
// Notify the client of disconnection
clientSp->notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
@@ -1348,14 +1348,19 @@
Status ret = Status::ok();
String8 id = String8(cameraId);
sp<CameraDeviceClient> client = nullptr;
-
+ String16 clientPackageNameAdj = clientPackageName;
+ if (hardware::IPCThreadState::self()->isServingCall()) {
+ std::string vendorClient =
+ StringPrintf("vendor.client.pid<%d>", CameraThreadState::getCallingPid());
+ clientPackageNameAdj = String16(vendorClient.c_str());
+ }
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
/*api1CameraId*/-1,
- CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
+ CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageNameAdj,
clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, /*out*/client);
if(!ret.isOk()) {
- logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageName),
+ logRejected(id, CameraThreadState::getCallingPid(), String8(clientPackageNameAdj),
ret.toString8());
return ret;
}
@@ -2368,11 +2373,7 @@
}
mClientPackageName = packages[0];
}
- if (hardware::IPCThreadState::self()->isServingCall()) {
- std::string vendorClient =
- StringPrintf("vendor.client.pid<%d>", CameraThreadState::getCallingPid());
- mClientPackageName = String16(vendorClient.c_str());
- } else {
+ if (!hardware::IPCThreadState::self()->isServingCall()) {
mAppOpsManager = std::make_unique<AppOpsManager>();
}
}
@@ -2607,14 +2608,13 @@
void CameraService::UidPolicy::registerSelf() {
Mutex::Autolock _l(mUidLock);
- ActivityManager am;
if (mRegistered) return;
- am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+ status_t res = mAm.linkToDeath(this);
+ mAm.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
| ActivityManager::UID_OBSERVER_IDLE
| ActivityManager::UID_OBSERVER_ACTIVE | ActivityManager::UID_OBSERVER_PROCSTATE,
ActivityManager::PROCESS_STATE_UNKNOWN,
String16("cameraserver"));
- status_t res = am.linkToDeath(this);
if (res == OK) {
mRegistered = true;
ALOGV("UidPolicy: Registered with ActivityManager");
@@ -2624,9 +2624,8 @@
void CameraService::UidPolicy::unregisterSelf() {
Mutex::Autolock _l(mUidLock);
- ActivityManager am;
- am.unregisterUidObserver(this);
- am.unlinkToDeath(this);
+ mAm.unregisterUidObserver(this);
+ mAm.unlinkToDeath(this);
mRegistered = false;
mActiveUids.clear();
ALOGV("UidPolicy: Unregistered with ActivityManager");
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 065157d..cf93a41 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -25,6 +25,7 @@
#include <cutils/multiuser.h>
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
+#include <binder/ActivityManager.h>
#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IAppOpsCallback.h>
@@ -564,6 +565,7 @@
Mutex mUidLock;
bool mRegistered;
+ ActivityManager mAm;
wp<CameraService> mService;
std::unordered_set<uid_t> mActiveUids;
// Monitored uid map to cached procState and refCount pair
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 9525ad2..8ebaa2b 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -58,8 +58,8 @@
entry = staticInfo.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
if (entry.count == 5) {
- mInstrinsicCalibration.reserve(5);
- mInstrinsicCalibration.insert(mInstrinsicCalibration.end(), entry.data.f,
+ mIntrinsicCalibration.reserve(5);
+ mIntrinsicCalibration.insert(mIntrinsicCalibration.end(), entry.data.f,
entry.data.f + 5);
} else {
ALOGW("%s: Intrinsic calibration absent from camera characteristics!", __FUNCTION__);
@@ -323,12 +323,12 @@
depthPhoto.mMaxJpegSize = maxDepthJpegSize;
// The camera intrinsic calibration layout is as follows:
// [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew]
- if (mInstrinsicCalibration.size() == 5) {
- memcpy(depthPhoto.mInstrinsicCalibration, mInstrinsicCalibration.data(),
- sizeof(depthPhoto.mInstrinsicCalibration));
- depthPhoto.mIsInstrinsicCalibrationValid = 1;
+ if (mIntrinsicCalibration.size() == 5) {
+ memcpy(depthPhoto.mIntrinsicCalibration, mIntrinsicCalibration.data(),
+ sizeof(depthPhoto.mIntrinsicCalibration));
+ depthPhoto.mIsIntrinsicCalibrationValid = 1;
} else {
- depthPhoto.mIsInstrinsicCalibrationValid = 0;
+ depthPhoto.mIsIntrinsicCalibrationValid = 0;
}
// The camera lens distortion contains the following lens correction coefficients.
// [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5]
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 1bf31f4..975c59b 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -124,7 +124,7 @@
ssize_t mMaxJpegSize;
std::vector<std::tuple<size_t, size_t>> mSupportedDepthSizes;
- std::vector<float> mInstrinsicCalibration, mLensDistortion;
+ std::vector<float> mIntrinsicCalibration, mLensDistortion;
bool mIsLogicalCamera;
void* mDepthPhotoLibHandle;
process_depth_photo_frame mDepthPhotoProcess;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 09638d0..c72029f 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -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 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::~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> ®istered) {
+ 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 @@
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();
@@ -1087,7 +1098,7 @@
return false;
}
-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__,
@@ -1100,13 +1111,9 @@
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);
@@ -2058,6 +2065,13 @@
return OK;
}
bool CameraProviderManager::ProviderInfo::DeviceInfo3::isAPI1Compatible() const {
+ // Do not advertise NIR cameras to API1 camera app.
+ camera_metadata_ro_entry cfa = mCameraCharacteristics.find(
+ ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ if (cfa.count == 1 && cfa.data.u8[0] == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR) {
+ return false;
+ }
+
bool isBackwardCompatible = false;
camera_metadata_ro_entry_t caps = mCameraCharacteristics.find(
ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index a42fb4d..8cdfc24 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -78,6 +78,7 @@
¬ification) = 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 @@
const std::string &serviceName) override {
return hardware::camera::provider::V2_4::ICameraProvider::getService(serviceName);
}
+
+ virtual hardware::hidl_vec<hardware::hidl_string> listServices() override;
};
/**
@@ -567,7 +570,7 @@
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;
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
index fc79150..3c90de0 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.cpp
@@ -61,6 +61,13 @@
using dynamic_depth::Profile;
using dynamic_depth::Profiles;
+template<>
+struct std::default_delete<jpeg_compress_struct> {
+ inline void operator()(jpeg_compress_struct* cinfo) const {
+ jpeg_destroy_compress(cinfo);
+ }
+};
+
namespace android {
namespace camera3 {
@@ -118,16 +125,16 @@
bool mSuccess;
} dmgr;
- jpeg_compress_struct cinfo = {};
+ std::unique_ptr<jpeg_compress_struct> cinfo = std::make_unique<jpeg_compress_struct>();
jpeg_error_mgr jerr;
// Initialize error handling with standard callbacks, but
// then override output_message (to print to ALOG) and
// error_exit to set a flag and print a message instead
// of killing the whole process.
- cinfo.err = jpeg_std_error(&jerr);
+ cinfo->err = jpeg_std_error(&jerr);
- cinfo.err->output_message = [](j_common_ptr cinfo) {
+ cinfo->err->output_message = [](j_common_ptr cinfo) {
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
@@ -135,7 +142,7 @@
ALOGE("libjpeg error: %s", buffer);
};
- cinfo.err->error_exit = [](j_common_ptr cinfo) {
+ cinfo->err->error_exit = [](j_common_ptr cinfo) {
(*cinfo->err->output_message)(cinfo);
if(cinfo->client_data) {
auto & dmgr = *static_cast<CustomJpegDestMgr*>(cinfo->client_data);
@@ -144,12 +151,12 @@
};
// Now that we initialized some callbacks, let's create our compressor
- jpeg_create_compress(&cinfo);
+ jpeg_create_compress(cinfo.get());
dmgr.mBuffer = static_cast<JOCTET*>(out);
dmgr.mBufferSize = maxOutSize;
dmgr.mEncodedSize = 0;
dmgr.mSuccess = true;
- cinfo.client_data = static_cast<void*>(&dmgr);
+ cinfo->client_data = static_cast<void*>(&dmgr);
// These lambdas become C-style function pointers and as per C++11 spec
// may not capture anything.
@@ -171,28 +178,28 @@
dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer;
ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
};
- cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
- cinfo.image_width = width;
- cinfo.image_height = height;
- cinfo.input_components = 1;
- cinfo.in_color_space = JCS_GRAYSCALE;
+ cinfo->dest = static_cast<struct jpeg_destination_mgr*>(&dmgr);
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ cinfo->input_components = 1;
+ cinfo->in_color_space = JCS_GRAYSCALE;
// Initialize defaults and then override what we want
- jpeg_set_defaults(&cinfo);
+ jpeg_set_defaults(cinfo.get());
- jpeg_set_quality(&cinfo, jpegQuality, 1);
- jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE);
- cinfo.raw_data_in = 0;
- cinfo.dct_method = JDCT_IFAST;
+ jpeg_set_quality(cinfo.get(), jpegQuality, 1);
+ jpeg_set_colorspace(cinfo.get(), JCS_GRAYSCALE);
+ cinfo->raw_data_in = 0;
+ cinfo->dct_method = JDCT_IFAST;
- cinfo.comp_info[0].h_samp_factor = 1;
- cinfo.comp_info[1].h_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1;
- cinfo.comp_info[0].v_samp_factor = 1;
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].v_samp_factor = 1;
+ cinfo->comp_info[0].h_samp_factor = 1;
+ cinfo->comp_info[1].h_samp_factor = 1;
+ cinfo->comp_info[2].h_samp_factor = 1;
+ cinfo->comp_info[0].v_samp_factor = 1;
+ cinfo->comp_info[1].v_samp_factor = 1;
+ cinfo->comp_info[2].v_samp_factor = 1;
- jpeg_start_compress(&cinfo, TRUE);
+ jpeg_start_compress(cinfo.get(), TRUE);
if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) {
std::unique_ptr<ExifUtils> utils(ExifUtils::create());
@@ -204,19 +211,19 @@
if (utils->generateApp1()) {
const uint8_t* exifBuffer = utils->getApp1Buffer();
size_t exifBufferSize = utils->getApp1Length();
- jpeg_write_marker(&cinfo, JPEG_APP0 + 1, static_cast<const JOCTET*>(exifBuffer),
+ jpeg_write_marker(cinfo.get(), JPEG_APP0 + 1, static_cast<const JOCTET*>(exifBuffer),
exifBufferSize);
} else {
ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__);
}
}
- for (size_t i = 0; i < cinfo.image_height; i++) {
+ for (size_t i = 0; i < cinfo->image_height; i++) {
auto currentRow = static_cast<JSAMPROW>(in + i*width);
- jpeg_write_scanlines(&cinfo, ¤tRow, /*num_lines*/1);
+ jpeg_write_scanlines(cinfo.get(), ¤tRow, /*num_lines*/1);
}
- jpeg_finish_compress(&cinfo);
+ jpeg_finish_compress(cinfo.get());
actualSize = dmgr.mEncodedSize;
if (dmgr.mSuccess) {
@@ -430,12 +437,12 @@
return BAD_VALUE;
}
- // It is not possible to generate an imaging model without instrinsic calibration.
- if (inputFrame.mIsInstrinsicCalibrationValid) {
+ // It is not possible to generate an imaging model without intrinsic calibration.
+ if (inputFrame.mIsIntrinsicCalibrationValid) {
// The camera intrinsic calibration layout is as follows:
// [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew]
- const dynamic_depth::Point<double> focalLength(inputFrame.mInstrinsicCalibration[0],
- inputFrame.mInstrinsicCalibration[1]);
+ const dynamic_depth::Point<double> focalLength(inputFrame.mIntrinsicCalibration[0],
+ inputFrame.mIntrinsicCalibration[1]);
size_t width = inputFrame.mMainJpegWidth;
size_t height = inputFrame.mMainJpegHeight;
if (switchDimensions) {
@@ -444,9 +451,9 @@
}
const Dimension imageSize(width, height);
ImagingModelParams imagingParams(focalLength, imageSize);
- imagingParams.principal_point.x = inputFrame.mInstrinsicCalibration[2];
- imagingParams.principal_point.y = inputFrame.mInstrinsicCalibration[3];
- imagingParams.skew = inputFrame.mInstrinsicCalibration[4];
+ imagingParams.principal_point.x = inputFrame.mIntrinsicCalibration[2];
+ imagingParams.principal_point.y = inputFrame.mIntrinsicCalibration[3];
+ imagingParams.skew = inputFrame.mIntrinsicCalibration[4];
// The camera lens distortion contains the following lens correction coefficients.
// [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5]
diff --git a/services/camera/libcameraservice/common/DepthPhotoProcessor.h b/services/camera/libcameraservice/common/DepthPhotoProcessor.h
index 6a2fbff..ba5ca9e 100644
--- a/services/camera/libcameraservice/common/DepthPhotoProcessor.h
+++ b/services/camera/libcameraservice/common/DepthPhotoProcessor.h
@@ -39,8 +39,8 @@
size_t mMaxJpegSize;
uint8_t mJpegQuality;
uint8_t mIsLogical;
- float mInstrinsicCalibration[5];
- uint8_t mIsInstrinsicCalibrationValid;
+ float mIntrinsicCalibration[5];
+ uint8_t mIsIntrinsicCalibrationValid;
float mLensDistortion[5];
uint8_t mIsLensDistortionValid;
DepthPhotoOrientation mOrientation;
@@ -57,8 +57,8 @@
mMaxJpegSize(0),
mJpegQuality(100),
mIsLogical(0),
- mInstrinsicCalibration{0.f},
- mIsInstrinsicCalibrationValid(0),
+ mIntrinsicCalibration{0.f},
+ mIsIntrinsicCalibrationValid(0),
mLensDistortion{0.f},
mIsLensDistortionValid(0),
mOrientation(DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES) {}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 9771f9e..4227a3b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -29,6 +29,9 @@
#define CLOGE(fmt, ...) ALOGE("Camera %s: %s: " fmt, mId.string(), __FUNCTION__, \
##__VA_ARGS__)
+#define CLOGW(fmt, ...) ALOGW("Camera %s: %s: " fmt, mId.string(), __FUNCTION__, \
+ ##__VA_ARGS__)
+
// Convenience macros for transitioning to the error state
#define SET_ERR(fmt, ...) setErrorState( \
"%s: " fmt, __FUNCTION__, \
@@ -3267,14 +3270,19 @@
ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
}
- // Sanity check - if we have too many in-flight frames, something has
- // likely gone wrong
- if (!mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() > kInFlightWarnLimit) {
- CLOGE("In-flight list too large: %zu", mInFlightMap.size());
- } else if (mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() >
- kInFlightWarnLimitHighSpeed) {
- CLOGE("In-flight list too large for high speed configuration: %zu",
- mInFlightMap.size());
+ // Sanity check - if we have too many in-flight frames with long total inflight duration,
+ // something has likely gone wrong. This might still be legit only if application send in
+ // a long burst of long exposure requests.
+ if (mExpectedInflightDuration > kMinWarnInflightDuration) {
+ if (!mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() > kInFlightWarnLimit) {
+ CLOGW("In-flight list too large: %zu, total inflight duration %" PRIu64,
+ mInFlightMap.size(), mExpectedInflightDuration);
+ } else if (mIsConstrainedHighSpeedConfiguration && mInFlightMap.size() >
+ kInFlightWarnLimitHighSpeed) {
+ CLOGW("In-flight list too large for high speed configuration: %zu,"
+ "total inflight duration %" PRIu64,
+ mInFlightMap.size(), mExpectedInflightDuration);
+ }
}
}
@@ -4364,7 +4372,7 @@
int overrideFormat = mapToFrameworkFormat(src.v3_2.overrideFormat);
android_dataspace overrideDataSpace = mapToFrameworkDataspace(src.overrideDataSpace);
- if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ if (dstStream->getOriginalFormat() != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
dstStream->setFormatOverride(false);
dstStream->setDataSpaceOverride(false);
if (dst->format != overrideFormat) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 6e8ac84..cae34ce 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -227,6 +227,7 @@
static const size_t kDumpLockAttempts = 10;
static const size_t kDumpSleepDuration = 100000; // 0.10 sec
static const nsecs_t kActiveTimeout = 500000000; // 500 ms
+ static const nsecs_t kMinWarnInflightDuration = 5000000000; // 5 s
static const size_t kInFlightWarnLimit = 30;
static const size_t kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
static const nsecs_t kDefaultExpectedDuration = 100000000; // 100 ms
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 2df084b..fd9b4b0 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -68,7 +68,7 @@
mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX),
mBufferLimitLatency(kBufferLimitLatencyBinSize),
mFormatOverridden(false),
- mOriginalFormat(-1),
+ mOriginalFormat(format),
mDataSpaceOverridden(false),
mOriginalDataSpace(HAL_DATASPACE_UNKNOWN),
mPhysicalCameraId(physicalCameraId),
@@ -125,9 +125,6 @@
void Camera3Stream::setFormatOverride(bool formatOverridden) {
mFormatOverridden = formatOverridden;
- if (formatOverridden && mOriginalFormat == -1) {
- mOriginalFormat = camera3_stream::format;
- }
}
bool Camera3Stream::isFormatOverridden() const {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 533318f..67afd0f 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -582,9 +582,9 @@
static const int32_t kBufferLimitLatencyBinSize = 33; //in ms
CameraLatencyHistogram mBufferLimitLatency;
- //Keep track of original format in case it gets overridden
+ //Keep track of original format when the stream is created in case it gets overridden
bool mFormatOverridden;
- int mOriginalFormat;
+ const int mOriginalFormat;
//Keep track of original dataSpace in case it gets overridden
bool mDataSpaceOverridden;
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index f47e5a5..78d737d 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -205,6 +205,11 @@
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 @@
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";
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 3626ad1..0e7edfd 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -82,7 +82,12 @@
// (0 for either of these disables that threshold)
//
static constexpr nsecs_t kMaxRecordAgeNs = 28 * 3600 * (1000*1000*1000ll);
-static constexpr int kMaxRecords = 0;
+// 2019/6: average daily per device is currently 375-ish;
+// setting this to 2000 is large enough to catch most devices
+// we'll lose some data on very very media-active devices, but only for
+// the gms collection; statsd will have already covered those for us.
+// This also retains enough information to help with bugreports
+static constexpr int kMaxRecords = 2000;
// max we expire in a single call, to constrain how long we hold the
// mutex, which also constrains how long a client might wait.