Merge "stagefright: distinguish HAL name from name in MediaCodecInfo" into main
diff --git a/media/aconfig/swcodec_flags.aconfig b/media/aconfig/swcodec_flags.aconfig
index 9dd1fdd..cb8a963 100644
--- a/media/aconfig/swcodec_flags.aconfig
+++ b/media/aconfig/swcodec_flags.aconfig
@@ -5,6 +5,16 @@
container: "com.android.media.swcodec"
flag {
+ name: "apexcodecs_base"
+ # ApexCodecs API is getting called early in the boot process, so we need to make
+ # sure that the flag value is stable from the early boot stage.
+ is_fixed_read_only: true
+ namespace: "codec_fwk"
+ description: "Feature flag for base implementation of apexcodecs"
+ bug: "401332082"
+}
+
+flag {
name: "apv_software_codec"
is_exported: true
is_fixed_read_only: true
diff --git a/media/audio/aconfig/audio_framework.aconfig b/media/audio/aconfig/audio_framework.aconfig
index 3a1befa..a4956b8 100644
--- a/media/audio/aconfig/audio_framework.aconfig
+++ b/media/audio/aconfig/audio_framework.aconfig
@@ -289,3 +289,13 @@
bug: "296232417"
is_fixed_read_only: true
}
+
+flag {
+ name: "register_volume_callback_api_hardening"
+ namespace: "media_audio"
+ description:
+ "Add modify audio settings privilege permission to un/register volume group "
+ "callback APIs"
+ bug: "402502314"
+ is_fixed_read_only: true
+}
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h
index 5c2cc2e..8e15778 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h
@@ -17,15 +17,21 @@
#pragma once
#include <aidl/android/hardware/media/c2/BnInputSurface.h>
+#include <utils/RefBase.h>
+#include <C2.h>
+#include <C2Config.h>
#include <codec2/aidl/Configurable.h>
#include <util/C2InterfaceHelper.h>
-#include <C2.h>
-
#include <memory>
+namespace aidl::android::hardware::media::c2::implementation {
+class InputSurfaceSource;
+}
+
namespace aidl::android::hardware::media::c2::utils {
+struct InputSurfaceConnection;
struct InputSurface : public BnInputSurface {
InputSurface();
@@ -40,6 +46,61 @@
const std::shared_ptr<IInputSink>& sink,
std::shared_ptr<IInputSurfaceConnection>* connection) override;
+ // Constant definitions.
+ // Default image size for AImageReader
+ constexpr static uint32_t kDefaultImageWidth = 1280;
+ constexpr static uint32_t kDefaultImageHeight = 720;
+ // Default # of buffers for AImageReader
+ constexpr static uint32_t kDefaultImageBufferCount = 16;
+ constexpr static uint32_t kDefaultImageDataspace = HAL_DATASPACE_BT709;
+
+ // Configs
+ // Config for AImageReader creation
+ struct ImageConfig {
+ int32_t mWidth; // image width
+ int32_t mHeight; // image height
+ int32_t mFormat; // image pixel format
+ int32_t mNumBuffers; // number of max images for AImageReader(consumer)
+ uint64_t mUsage; // image usage
+ uint32_t mDataspace; // image dataspace
+ };
+
+ // Config for InputSurface active buffer stream control
+ struct StreamConfig {
+ // IN PARAMS
+ float mMinFps = 0.0; // minimum fps (repeat frame to achieve this)
+ float mMaxFps = 0.0; // max fps (via frame drop)
+ float mCaptureFps = 0.0; // capture fps
+ float mCodedFps = 0.0; // coded fps
+ bool mSuspended = false; // suspended
+ int64_t mSuspendAtUs = 0; // suspend time
+ int64_t mResumeAtUs = 0; // resume time
+ bool mStopped = false; // stopped
+ int64_t mStopAtUs = 0; // stop time
+ int64_t mStartAtUs = 0; // start time
+ int64_t mTimeOffsetUs = 0; // time offset (input => codec)
+
+ // IN PARAMS (CODEC WRAPPER)
+ C2TimestampGapAdjustmentStruct::mode_t
+ mAdjustedFpsMode = C2TimestampGapAdjustmentStruct::NONE;
+ int64_t mAdjustedGapUs = 0;
+ int mPriority = INT_MAX; // priority of queue thread (if any);
+ // INT_MAX for no-op
+ };
+
+ // TODO: optimize this
+ // The client requests the change of these configurations now.
+ // We can request the change of these configurations from HAL directly
+ // where onWorkDone() callback is called.
+ //
+ // Config for current work status w.r.t input buffers
+ struct WorkStatusConfig {
+ int32_t mLastDoneIndex = -1; // Last work done input buffer index
+ uint32_t mLastDoneCount = 0; // # of work done count
+ uint64_t mEmptyCount = 0; // # of input buffers being emptied
+ };
+
+
protected:
class Interface;
class ConfigurableIntf;
@@ -50,12 +111,29 @@
virtual ~InputSurface() override;
+private:
+ ::android::sp<implementation::InputSurfaceSource> mSource;
+ std::shared_ptr<InputSurfaceConnection> mConnection;
- ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
- static void OnBinderDied(void *cookie);
- static void OnBinderUnlinked(void *cookie);
- struct DeathContext;
- DeathContext *mDeathContext;
+ ImageConfig mImageConfig;
+ StreamConfig mStreamConfig;
+ WorkStatusConfig mWorkStatusConfig;
+
+ std::mutex mLock;
+
+ friend class ConfigurableIntf;
+
+ bool updateConfig(
+ ImageConfig &imageConfig,
+ StreamConfig &streamConfig,
+ WorkStatusConfig &workStatusConfig,
+ int64_t *inputDelayUs);
+
+ void updateImageConfig(ImageConfig &config);
+ bool updateStreamConfig(StreamConfig &config, int64_t *inputDelayUs);
+ void updateWorkStatusConfig(WorkStatusConfig &config);
+
+ void release();
};
} // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h
index 59361e1..7a57f18 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h
@@ -16,17 +16,25 @@
#pragma once
+#include <aidl/android/hardware/media/c2/BnInputSink.h>
#include <aidl/android/hardware/media/c2/BnInputSurfaceConnection.h>
#include <media/NdkImage.h>
+#include <utils/RefBase.h>
#include <C2.h>
#include <memory>
+namespace aidl::android::hardware::media::c2::implementation {
+class InputSurfaceSource;
+}
+
namespace aidl::android::hardware::media::c2::utils {
struct InputSurfaceConnection : public BnInputSurfaceConnection {
- InputSurfaceConnection();
+ InputSurfaceConnection(
+ const std::shared_ptr<IInputSink>& sink,
+ ::android::sp<c2::implementation::InputSurfaceSource> const &source);
c2_status_t status() const;
// Methods from IInputSurfaceConnection follow.
@@ -51,6 +59,10 @@
protected:
virtual ~InputSurfaceConnection() override;
+
+private:
+ std::weak_ptr<IInputSink> mSink;
+ ::android::sp<c2::implementation::InputSurfaceSource> mSource;
};
} // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/inputsurface/InputSurface.cpp b/media/codec2/hal/aidl/inputsurface/InputSurface.cpp
index 5f6d176..ce694ee 100644
--- a/media/codec2/hal/aidl/inputsurface/InputSurface.cpp
+++ b/media/codec2/hal/aidl/inputsurface/InputSurface.cpp
@@ -17,11 +17,29 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2-InputSurface"
#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+
+#include <mutex>
+
+#include <C2Config.h>
#include <codec2/aidl/inputsurface/InputSurface.h>
+#include <codec2/aidl/inputsurface/InputSurfaceConnection.h>
+#include <codec2/aidl/inputsurface/InputSurfaceSource.h>
+
namespace aidl::android::hardware::media::c2::utils {
+using ImageConfig = InputSurface::ImageConfig;
+using StreamConfig = InputSurface::StreamConfig;
+using WorkStatusConfig = InputSurface::WorkStatusConfig;
+
+template <typename T>
+static C2R BasicSetter(bool, C2InterfaceHelper::C2P<T> &) {
+ return C2R::Ok();
+}
+
// Derived class of C2InterfaceHelper
class InputSurface::Interface : public C2InterfaceHelper {
public:
@@ -31,51 +49,524 @@
setDerivedInstance(this);
+ addParameter(
+ DefineParam(mBlockSize, C2_PARAMKEY_BLOCK_SIZE)
+ .withDefault(new C2StreamBlockSizeInfo::output(
+ 0u, kDefaultImageWidth, kDefaultImageHeight))
+ .withFields({
+ C2F(mBlockSize, width).inRange(2, 8192, 2),
+ C2F(mBlockSize, height).inRange(2, 8192, 2),})
+ .withSetter(BlockSizeSetter)
+ .build());
+ addParameter(
+ DefineParam(mBlockCount, C2_PARAMKEY_BLOCK_COUNT)
+ .withDefault(new C2StreamBlockCountInfo::output(
+ 0u, kDefaultImageBufferCount))
+ .withFields({C2F(mBlockCount, value).any()})
+ .withSetter(BlockCountSetter)
+ .build());
+ addParameter(
+ DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+ .withDefault(new C2StreamPixelFormatInfo::output(
+ 0u, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED))
+ .withFields({C2F(mPixelFormat, value).any()})
+ .withSetter(BasicSetter<decltype(mPixelFormat)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mUsage, C2_PARAMKEY_OUTPUT_STREAM_USAGE)
+ .withDefault(new C2StreamUsageTuning::output(0u, 0ULL))
+ .withFields({C2F(mUsage, value).any()})
+ .withSetter(BasicSetter<decltype(mUsage)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mDataspace, C2_PARAMKEY_DATA_SPACE)
+ .withDefault(new C2StreamDataSpaceInfo::output(
+ 0u, kDefaultImageDataspace))
+ .withFields({C2F(mDataspace, value).any()})
+ .withSetter(BasicSetter<decltype(mDataspace)::element_type>)
+ .build());
+
+ addParameter(
+ DefineParam(mMinFps, C2_PARAMKEY_INPUT_SURFACE_MIN_FRAME_RATE)
+ .withDefault(new C2PortMinFrameRateTuning::output(0.0))
+ .withFields({C2F(mMinFps, value).any()})
+ .withSetter(BasicSetter<decltype(mMinFps)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mMaxFps, C2_PARAMKEY_INPUT_SURFACE_MAX_FRAME_RATE)
+ .withDefault(new C2PortMaxFrameRateTuning::output(0.0))
+ .withFields({C2F(mMaxFps, value).any()})
+ .withSetter(BasicSetter<decltype(mMaxFps)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mCaptureFps, C2_PARAMKEY_INPUT_SURFACE_CAPTURE_FRAME_RATE)
+ .withDefault(new C2PortCaptureFrameRateTuning::output(0.0))
+ .withFields({C2F(mCaptureFps, value).any()})
+ .withSetter(BasicSetter<decltype(mCaptureFps)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mCodedFps, C2_PARAMKEY_FRAME_RATE)
+ .withDefault(new C2StreamFrameRateInfo::output(0u, 0.0))
+ .withFields({C2F(mCodedFps, value).any()})
+ .withSetter(BasicSetter<decltype(mCodedFps)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mTimeOffset, C2_PARAMKEY_FRAME_RATE)
+ .withDefault(new C2ComponentTimeOffsetTuning(0ULL))
+ .withFields({C2F(mTimeOffset, value).any()})
+ .withSetter(BasicSetter<decltype(mTimeOffset)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mStarted, C2_PARAMKEY_INPUT_SURFACE_START_AT)
+ .withDefault(new C2PortStartTimestampTuning::output(0ULL))
+ .withFields({
+ C2F(mStarted, enabled).any(),
+ C2F(mStarted, timestamp).any()})
+ .withSetter(BasicSetter<decltype(mStarted)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mStopped, C2_PARAMKEY_INPUT_SURFACE_STOP_AT)
+ .withDefault(new C2PortStopTimestampTuning::output())
+ .withFields({
+ C2F(mStopped, enabled).any(),
+ C2F(mStopped, timestamp).any()})
+ .withSetter(BasicSetter<decltype(mStopped)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mSuspended, C2_PARAMKEY_INPUT_SURFACE_SUSPEND_AT)
+ .withDefault(new C2PortSuspendTimestampTuning::output())
+ .withFields({
+ C2F(mSuspended, enabled).any(),
+ C2F(mSuspended, timestamp).any()})
+ .withSetter(BasicSetter<decltype(mSuspended)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mResumed, C2_PARAMKEY_INPUT_SURFACE_RESUME_AT)
+ .withDefault(new C2PortResumeTimestampTuning::output(0ULL))
+ .withFields({
+ C2F(mResumed, enabled).any(),
+ C2F(mResumed, timestamp).any()})
+ .withSetter(BasicSetter<decltype(mResumed)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mGap, C2_PARAMKEY_INPUT_SURFACE_TIMESTAMP_ADJUSTMENT)
+ .withDefault(new C2PortTimestampGapTuning::output(
+ C2TimestampGapAdjustmentStruct::NONE, 0ULL))
+ .withFields({
+ C2F(mGap, mode)
+ .oneOf({
+ C2TimestampGapAdjustmentStruct::NONE,
+ C2TimestampGapAdjustmentStruct::MIN_GAP,
+ C2TimestampGapAdjustmentStruct::FIXED_GAP}),
+ C2F(mGap, value).any()})
+ .withSetter(BasicSetter<decltype(mGap)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mStopTimeOffset, C2_PARAMKEY_INPUT_SURFACE_STOP_TIME_OFFSET)
+ .withDefault(new C2PortStopTimeOffset::output(0ULL))
+ .withFields({C2F(mStopTimeOffset, value).any()})
+ .withSetter(BasicSetter<decltype(mStopTimeOffset)::element_type>)
+ .build());
+
+ addParameter(
+ DefineParam(mInputDone, C2_PARAMKEY_LAYER_INDEX)
+ .withDefault(new C2StreamLayerIndexInfo::output(0u, UINT32_MAX))
+ .withFields({C2F(mInputDone, value).any()})
+ .withSetter(BasicSetter<decltype(mInputDone)::element_type>)
+ .build());
+ addParameter(
+ DefineParam(mInputDoneCount, C2_PARAMKEY_LAYER_INDEX)
+ .withDefault(new C2StreamLayerCountInfo::input(0u, 0))
+ .withFields({C2F(mInputDoneCount, value).any()})
+ .withSetter(InputDoneCountSetter)
+ .build());
+ addParameter(
+ DefineParam(mEmptyCount, C2_PARAMKEY_LAYER_COUNT)
+ .withDefault(new C2StreamLayerCountInfo::output(0u, 0))
+ .withFields({C2F(mEmptyCount, value).any()})
+ .withSetter(EmptyCountSetter)
+ .build());
+ }
+
+ void getImageConfig(ImageConfig* _Nonnull config) {
+ config->mWidth = mBlockSize->width;
+ config->mHeight = mBlockSize->height;
+ config->mFormat = mPixelFormat->value;
+ config->mNumBuffers = mBlockCount->value;
+ config->mUsage = mUsage->value;
+ config->mDataspace = mDataspace->value;
+ }
+
+ void getStreamConfig(StreamConfig* _Nonnull config) {
+ config->mMinFps = mMinFps->value;
+ config->mMaxFps = mMaxFps->value;
+ config->mCaptureFps = mCaptureFps->value;
+ config->mCodedFps = mCodedFps->value;
+ config->mTimeOffsetUs = mTimeOffset->value;
+
+ bool suspended = mSuspended->enabled;
+ bool resumed = mResumed->enabled;
+ CHECK(resumed != suspended);
+ config->mSuspended = suspended;
+ config->mSuspendAtUs = mSuspended->timestamp;
+ config->mResumeAtUs = mResumed->timestamp;
+ bool stopped = mStopped->enabled;
+ bool started = mStarted->enabled;
+ CHECK(stopped != started);
+ config->mStopped = stopped;
+ config->mStopAtUs = mStopped->timestamp;
+ config->mStartAtUs = mStarted->timestamp;
+
+ config->mAdjustedFpsMode = mGap->mode;
+ config->mAdjustedGapUs = mGap->value;
+ }
+
+ void getWorkStatusConfig(WorkStatusConfig* _Nonnull config) {
+ if (mInputDone->value == UINT32_MAX) {
+ config->mLastDoneIndex = -1;
+ } else {
+ config->mLastDoneIndex = mInputDone->value;
+ }
+ config->mLastDoneCount = mInputDoneCount->value;
+ config->mEmptyCount = mEmptyCount->value;
}
private:
+ // setters
+ static C2R BlockSizeSetter(bool mayBlock,
+ C2InterfaceHelper::C2P<C2StreamBlockSizeInfo::output> &me) {
+ (void)mayBlock;
+ uint32_t width_ = c2_min(me.v.width, 8192u);
+ uint32_t height_ = c2_min(me.v.height, 8192u);
+ if (width_ % 2 != 0) width_++;
+ if (height_ % 2 != 0) height_++;
+ me.set().width = width_;
+ me.set().height = height_;
+ return C2R::Ok();
+ }
+ static C2R BlockCountSetter(bool mayBlock,
+ C2InterfaceHelper::C2P<C2StreamBlockCountInfo::output> &me) {
+ (void)mayBlock;
+ me.set().value = c2_min(me.v.value, kDefaultImageBufferCount);
+ return C2R::Ok();
+ }
+
+ static C2R InputDoneCountSetter(bool mayBlock,
+ C2InterfaceHelper::C2P<C2StreamLayerCountInfo::input> &me) {
+ (void)mayBlock;
+ me.set().value = me.v.value + 1;
+ return C2R::Ok();
+ }
+
+ static C2R EmptyCountSetter(bool mayBlock,
+ C2InterfaceHelper::C2P<C2StreamLayerCountInfo::output> &me) {
+ (void)mayBlock;
+ me.set().value = me.v.value + 1;
+ return C2R::Ok();
+ }
+
+private:
+ // buffer configuraration
+ std::shared_ptr<C2StreamBlockSizeInfo::output> mBlockSize;
+ std::shared_ptr<C2StreamBlockCountInfo::output> mBlockCount;
+ std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+ std::shared_ptr<C2StreamUsageTuning::output> mUsage;
+ std::shared_ptr<C2StreamDataSpaceInfo::output> mDataspace;
+
+ // input surface source configuration
+ std::shared_ptr<C2PortMinFrameRateTuning::output> mMinFps;
+ std::shared_ptr<C2PortMaxFrameRateTuning::output> mMaxFps;
+ std::shared_ptr<C2PortCaptureFrameRateTuning::output> mCaptureFps;
+ std::shared_ptr<C2StreamFrameRateInfo::output> mCodedFps;
+ std::shared_ptr<C2ComponentTimeOffsetTuning> mTimeOffset; // unsigned, but
+ // signed
+ std::shared_ptr<C2PortSuspendTimestampTuning::output> mSuspended;
+ std::shared_ptr<C2PortResumeTimestampTuning::output> mResumed;
+ std::shared_ptr<C2PortStartTimestampTuning::output> mStarted;
+ std::shared_ptr<C2PortStopTimestampTuning::output> mStopped;
+ std::shared_ptr<C2PortTimestampGapTuning::output> mGap;
+ std::shared_ptr<C2PortStopTimeOffset::output> mStopTimeOffset; // query
+
+ // current work status configuration
+ // TODO: remove this and move this to onWorkDone()
+ std::shared_ptr<C2StreamLayerIndexInfo::output> mInputDone;
+ std::shared_ptr<C2StreamLayerCountInfo::input> mInputDoneCount;
+ std::shared_ptr<C2StreamLayerCountInfo::output> mEmptyCount;
};
class InputSurface::ConfigurableIntf : public ConfigurableC2Intf {
public:
+ ConfigurableIntf(
+ const std::shared_ptr<InputSurface::Interface> &intf,
+ const std::shared_ptr<InputSurface> &surface)
+ : ConfigurableC2Intf("input-surface", 0),
+ mIntf(intf), mSurface(surface) {
+ }
+
+ virtual ~ConfigurableIntf() override = default;
+
+ virtual c2_status_t query(
+ const std::vector<C2Param::Index> &indices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const params
+ ) const override {
+ // std::lock_guard<std::mutex> l(mConfigLock);
+ std::lock_guard<std::mutex> l(mConfigLock);
+ return mIntf->query({}, indices, mayBlock, params);
+ }
+
+ virtual c2_status_t config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures
+ ) override {
+ auto surface = mSurface.lock();
+ if (!surface) {
+ return C2_CORRUPTED;
+ }
+
+ c2_status_t err;
+ {
+ ImageConfig imageConfig;
+ StreamConfig streamConfig;
+ WorkStatusConfig workStatusConfig;
+ int64_t inputDelayUs = 0;
+
+ std::lock_guard<std::mutex> l(mConfigLock);
+ err = mIntf->config(params, mayBlock, failures);
+
+ mIntf->getImageConfig(&imageConfig);
+ mIntf->getStreamConfig(&streamConfig);
+ mIntf->getWorkStatusConfig(&workStatusConfig);
+ if (surface->updateConfig(
+ imageConfig, streamConfig, workStatusConfig, &inputDelayUs)) {
+ C2PortStopTimeOffset::output offsetConfig(inputDelayUs);
+ std::vector<std::unique_ptr<C2SettingResult>> fail;
+ c2_status_t updateErr = mIntf->config({&offsetConfig}, mayBlock, &fail);
+ }
+ }
+ return err;
+ }
+
+ virtual c2_status_t querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+ ) const override {
+ std::lock_guard<std::mutex> l(mConfigLock);
+ return mIntf->querySupportedParams(params);
+ }
+
+ virtual c2_status_t querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ c2_blocking_t mayBlock) const override {
+ std::lock_guard<std::mutex> l(mConfigLock);
+ return mIntf->querySupportedValues(fields, mayBlock);
+ }
+
+private:
+ const std::shared_ptr<InputSurface::Interface> mIntf;
+ const std::weak_ptr<InputSurface> mSurface;
+
+ mutable std::mutex mConfigLock;
};
-struct InputSurface::DeathContext {
- // TODO;
-};
+InputSurface::InputSurface() {
+ mIntf = std::make_shared<Interface>(
+ std::make_shared<C2ReflectorHelper>());
-void InputSurface::OnBinderDied(void *cookie) {
- (void) cookie;
-}
-
-void InputSurface::OnBinderUnlinked(void *cookie) {
- (void) cookie;
-}
-
-InputSurface::InputSurface() : mDeathContext(nullptr) {
- mInit = C2_OK;
+ // mConfigurable is initialized lazily.
+ // mInit indicates the initialization status of mConfigurable.
+ mInit = C2_NO_INIT;
}
InputSurface::~InputSurface() {
+ release();
}
::ndk::ScopedAStatus InputSurface::getSurface(::aidl::android::view::Surface* surface) {
- (void) surface;
- return ::ndk::ScopedAStatus::ok();
+ std::lock_guard<std::mutex> l(mLock);
+ ANativeWindow *window = mSource->getNativeWindow();
+ if (window) {
+ surface->reset(window);
+ return ::ndk::ScopedAStatus::ok();
+ }
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(C2_CORRUPTED);
}
::ndk::ScopedAStatus InputSurface::getConfigurable(
std::shared_ptr<IConfigurable>* configurable) {
- *configurable = mConfigurable;
- return ::ndk::ScopedAStatus::ok();
+ if (mInit == C2_NO_INIT) {
+ mConfigurable = SharedRefBase::make<CachedConfigurable>(
+ std::make_unique<ConfigurableIntf>(mIntf, this->ref<InputSurface>()));
+ mInit = C2_OK;
+ }
+ if (mConfigurable) {
+ *configurable = mConfigurable;
+ return ::ndk::ScopedAStatus::ok();
+ }
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(C2_CORRUPTED);
}
::ndk::ScopedAStatus InputSurface::connect(
const std::shared_ptr<IInputSink>& sink,
std::shared_ptr<IInputSurfaceConnection>* connection) {
- (void) sink;
- (void) connection;
+ mConnection = SharedRefBase::make<InputSurfaceConnection>(sink, mSource);
+ *connection = mConnection;
return ::ndk::ScopedAStatus::ok();
}
+void InputSurface::updateImageConfig(ImageConfig &config) {
+ std::unique_lock<std::mutex> l(mLock);
+ if (mImageConfig.mWidth != config.mWidth) {
+ mImageConfig.mWidth = config.mWidth;
+ }
+ if (mImageConfig.mHeight != config.mHeight) {
+ mImageConfig.mHeight = config.mHeight;
+ }
+ if (mImageConfig.mFormat != config.mFormat) {
+ mImageConfig.mFormat = config.mFormat;
+ }
+ if (mImageConfig.mNumBuffers != config.mNumBuffers) {
+ mImageConfig.mNumBuffers = config.mNumBuffers;
+ }
+ if (mImageConfig.mUsage != config.mUsage) {
+ mImageConfig.mUsage = config.mUsage;
+ }
+ if (mImageConfig.mDataspace != config.mDataspace) {
+ mImageConfig.mDataspace = config.mDataspace;
+ }
+}
+
+bool InputSurface::updateStreamConfig(
+ StreamConfig &config, int64_t *inputDelayUs) {
+ std::stringstream status;
+ c2_status_t err = C2_OK;
+ bool inputDelayUpdated = false;
+
+ std::unique_lock<std::mutex> l(mLock);
+ // handle StreamConfig changes.
+ // TRICKY: we do not unset frame delay repeating
+ if (config.mMinFps > 0 && config.mMinFps != mStreamConfig.mMinFps) {
+ int64_t us = 1e6 / config.mMinFps + 0.5;
+ c2_status_t res = mSource->setRepeatPreviousFrameDelayUs(us);
+ status << " minFps=" << config.mMinFps << " => repeatDelayUs=" << us;
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ }
+ mStreamConfig.mMinFps = config.mMinFps;
+ }
+ bool fixedModeUpdate = false;
+ if (config.mAdjustedFpsMode != C2TimestampGapAdjustmentStruct::NONE && (
+ config.mAdjustedFpsMode != mStreamConfig.mAdjustedFpsMode ||
+ config.mAdjustedGapUs != mStreamConfig.mAdjustedGapUs)) {
+ // TODO: configure GapUs to connection
+ // The original codes do not update config, figure out why.
+ mStreamConfig.mAdjustedFpsMode = config.mAdjustedFpsMode;
+ mStreamConfig.mAdjustedGapUs = config.mAdjustedGapUs;
+ fixedModeUpdate = (config.mAdjustedFpsMode == C2TimestampGapAdjustmentStruct::FIXED_GAP);
+ // TODO: update Gap to Connection.
+ }
+ // TRICKY: we do not unset max fps to 0 unless using fixed fps
+ if ((config.mMaxFps > 0 || (fixedModeUpdate && config.mMaxFps == -1))
+ && config.mMaxFps != mStreamConfig.mMaxFps) {
+ c2_status_t res = mSource->setMaxFps(config.mMaxFps);
+ status << " maxFps=" << config.mMaxFps;
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ }
+ mStreamConfig.mMaxFps = config.mMaxFps;
+ }
+ if (config.mTimeOffsetUs != mStreamConfig.mTimeOffsetUs) {
+ c2_status_t res = mSource->setTimeOffsetUs(config.mTimeOffsetUs);
+ status << " timeOffset " << config.mTimeOffsetUs << "us";
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ }
+ mStreamConfig.mTimeOffsetUs = config.mTimeOffsetUs;
+ }
+ if (config.mCaptureFps != mStreamConfig.mCaptureFps ||
+ config.mCodedFps != mStreamConfig.mCodedFps) {
+ c2_status_t res = mSource->setTimeLapseConfig(
+ config.mCodedFps, config.mCaptureFps);
+ status << " timeLapse " << config.mCaptureFps << "fps as "
+ << config.mCodedFps << "fps";
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ }
+ mStreamConfig.mCaptureFps = config.mCaptureFps;
+ mStreamConfig.mCodedFps = config.mCodedFps;
+ }
+ if (config.mStartAtUs != mStreamConfig.mStartAtUs ||
+ (config.mStopped != mStreamConfig.mStopped && !config.mStopped)) {
+ c2_status_t res = mSource->setStartTimeUs(config.mStartAtUs);
+ status << " start at " << config.mStartAtUs << "us";
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ }
+ mStreamConfig.mStartAtUs = config.mStartAtUs;
+ mStreamConfig.mStopped = config.mStopped;
+ }
+ if (config.mSuspended != mStreamConfig.mSuspended) {
+ c2_status_t res = mSource->setSuspend(config.mSuspended, config.mSuspendAtUs);
+ status << " " << (config.mSuspended ? "suspend" : "resume")
+ << " at " << config.mSuspendAtUs << "us";
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ }
+ mStreamConfig.mSuspended = config.mSuspended;
+ mStreamConfig.mSuspendAtUs = config.mSuspendAtUs;
+ }
+ if (config.mStopped != mStreamConfig.mStopped && config.mStopped) {
+ // start time has changed or started from stop.
+ c2_status_t res = mSource->setStopTimeUs(config.mStopAtUs);
+ status << " stop at " << config.mStopAtUs << "us";
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ err = res;
+ } else {
+ status << " delayUs";
+ res = mSource->getStopTimeOffsetUs(inputDelayUs);
+ if (res != C2_OK) {
+ status << " (=> " << asString(res) << ")";
+ } else {
+ status << "=" << *inputDelayUs << "us";
+ inputDelayUpdated = true;
+ }
+ }
+ mStreamConfig.mStopAtUs = config.mStopAtUs;
+ mStreamConfig.mStopped = config.mStopped;
+ }
+ if (status.str().empty()) {
+ ALOGD("StreamConfig not changed");
+ } else {
+ ALOGD("StreamConfig%s", status.str().c_str());
+ }
+ return inputDelayUpdated;
+}
+
+void InputSurface::updateWorkStatusConfig(WorkStatusConfig &config) {
+ (void)config;
+ // TODO
+}
+
+bool InputSurface::updateConfig(
+ ImageConfig &imageConfig, StreamConfig &streamConfig,
+ WorkStatusConfig &workStatusConfig, int64_t *inputDelayUs) {
+ updateImageConfig(imageConfig);
+ bool ret = updateStreamConfig(streamConfig, inputDelayUs);
+ updateWorkStatusConfig(workStatusConfig);
+
+ return ret;
+}
+
+void InputSurface::release() {
+ ALOGD("all refs are gone");
+ // TODO clean up
+}
+
} // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp b/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp
index 44ca924..6a95472 100644
--- a/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp
+++ b/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp
@@ -19,15 +19,24 @@
#include <android-base/logging.h>
#include <codec2/aidl/inputsurface/InputSurfaceConnection.h>
+#include <codec2/aidl/inputsurface/InputSurfaceSource.h>
namespace aidl::android::hardware::media::c2::utils {
-InputSurfaceConnection::InputSurfaceConnection() {
+InputSurfaceConnection::InputSurfaceConnection(
+ const std::shared_ptr<IInputSink>& sink,
+ ::android::sp<c2::implementation::InputSurfaceSource> const &source)
+ : mSink{sink}, mSource{source} {
}
InputSurfaceConnection::~InputSurfaceConnection() {
}
+c2_status_t InputSurfaceConnection::status() const {
+ // TODO;
+ return C2_OK;
+}
+
::ndk::ScopedAStatus InputSurfaceConnection::disconnect() {
return ::ndk::ScopedAStatus::ok();
}
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 693ac4f..8b4e012 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -32,12 +32,14 @@
"audiopolicy-aidl-cpp",
"av-types-aidl-cpp",
"spatializer-aidl-cpp",
+ "volumegroupcallback-aidl-cpp",
],
export_static_lib_headers: [
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
"av-types-aidl-cpp",
"spatializer-aidl-cpp",
+ "volumegroupcallback-aidl-cpp",
],
target: {
darwin: {
@@ -151,6 +153,7 @@
"libutils",
"packagemanager_aidl-cpp",
"spatializer-aidl-cpp",
+ "volumegroupcallback-aidl-cpp",
],
export_shared_lib_headers: [
"audioflinger-aidl-cpp",
@@ -160,6 +163,7 @@
"libmediametrics",
"libmediautils",
"spatializer-aidl-cpp",
+ "volumegroupcallback-aidl-cpp",
],
include_dirs: [
@@ -472,6 +476,7 @@
"capture_state_listener-aidl",
"framework-permission-aidl",
"spatializer-aidl",
+ "volumegroupcallback-aidl",
],
double_loadable: true,
@@ -539,3 +544,30 @@
},
},
}
+
+aidl_interface {
+ name: "volumegroupcallback-aidl",
+ unstable: true,
+ host_supported: true,
+ vendor_available: true,
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/INativeAudioVolumeGroupCallback.aidl",
+ ],
+ double_loadable: true,
+ defaults: [
+ "latest_android_media_audio_common_types_import_interface",
+ ],
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ },
+}
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 3dda043..3ef9225 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -40,6 +40,7 @@
#include <system/audio.h>
#include <android/media/GetInputForAttrResponse.h>
#include <android/media/AudioMixerAttributesInternal.h>
+#include <android/media/audio/common/AudioVolumeGroupChangeEvent.h>
#define VALUE_OR_RETURN_BINDER_STATUS(x) \
({ auto _tmp = (x); \
@@ -66,6 +67,7 @@
using media::audio::common::AudioSource;
using media::audio::common::AudioStreamType;
using media::audio::common::AudioUsage;
+using media::audio::common::AudioVolumeGroupChangeEvent;
using media::audio::common::Int;
std::mutex AudioSystem::gMutex;
@@ -195,6 +197,7 @@
}
if (mValid) return mService;
if (waitMs.count() < 0) waitMs = mWaitMs;
+ auto timepointLimit = std::chrono::steady_clock::now() + waitMs;
ul.unlock();
// mediautils::getService() installs a persistent new service notification.
@@ -205,6 +208,7 @@
ul.lock();
// return the IAudioFlinger interface which is adapted
// from the media::IAudioFlingerService.
+ mCv.wait_until(ul, timepointLimit, isServiceValid_l);
return mService;
}
@@ -289,6 +293,7 @@
mService = service;
client = mClient;
mValid = true;
+ mCv.notify_all();
}
// TODO(b/375280520) consider registerClient() within mMutex lock.
const int64_t token = IPCThreadState::self()->clearCallingIdentity();
@@ -303,7 +308,12 @@
return sp<AudioFlingerClientAdapter>::make(af);
}
+ static bool isServiceValid_l() REQUIRES(mMutex) {
+ return mValid;
+ }
+
static inline constinit std::mutex mMutex;
+ static inline constinit std::condition_variable mCv;
static inline constinit sp<AudioSystem::AudioFlingerClient> mClient GUARDED_BY(mMutex);
static inline constinit sp<IAudioFlinger> mService GUARDED_BY(mMutex);
static inline constinit std::chrono::milliseconds mWaitMs
@@ -1022,6 +1032,7 @@
client = mClient;
mService = aps;
mValid = true;
+ mCv.notify_all();
}
// TODO(b/375280520) consider registerClient() within mMutex lock.
const int64_t token = IPCThreadState::self()->clearCallingIdentity();
@@ -1082,6 +1093,7 @@
}
if (mValid) return mService;
if (waitMs.count() < 0) waitMs = mWaitMs;
+ auto timepointLimit = std::chrono::steady_clock::now() + waitMs;
ul.unlock();
auto service = mediautils::getService<
@@ -1092,6 +1104,7 @@
// (whereupon mService contained the actual local service pointer to use).
// we should always return mService.
ul.lock();
+ mCv.wait_until(ul, timepointLimit, isServiceValid_l);
return mService;
}
@@ -1140,7 +1153,12 @@
}
private:
+ static bool isServiceValid_l() REQUIRES(mMutex) {
+ return mValid;
+ }
+
static inline constinit std::mutex mMutex;
+ static inline constinit std::condition_variable mCv;
static inline constinit sp<AudioSystem::AudioPolicyServiceClient> mClient GUARDED_BY(mMutex);
static inline constinit sp<IAudioPolicyService> mService GUARDED_BY(mMutex);
static inline constinit bool mValid GUARDED_BY(mMutex) = false;
@@ -1965,7 +1983,8 @@
return (ret < 0) ? INVALID_OPERATION : NO_ERROR;
}
-status_t AudioSystem::addAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback) {
+status_t AudioSystem::addAudioVolumeGroupCallback(
+ const sp<media::INativeAudioVolumeGroupCallback>& callback) {
const sp<IAudioPolicyService> aps = get_audio_policy_service();
if (aps == nullptr) return AudioPolicyServiceTraits::getError();
const auto apc = AudioSystem::getAudioPolicyClient();
@@ -1979,7 +1998,8 @@
return (ret < 0) ? INVALID_OPERATION : NO_ERROR;
}
-status_t AudioSystem::removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback) {
+status_t AudioSystem::removeAudioVolumeGroupCallback(
+ const sp<media::INativeAudioVolumeGroupCallback>& callback) {
const sp<IAudioPolicyService> aps = get_audio_policy_service();
if (aps == nullptr) return AudioPolicyServiceTraits::getError();
const auto apc = AudioSystem::getAudioPolicyClient();
@@ -2997,14 +3017,14 @@
// ----------------------------------------------------------------------------
int AudioSystem::AudioPolicyServiceClient::addAudioVolumeGroupCallback(
- const sp<AudioVolumeGroupCallback>& callback) {
+ const sp<media::INativeAudioVolumeGroupCallback>& callback) {
std::lock_guard _l(mMutex);
return mAudioVolumeGroupCallbacks.insert(callback).second
? mAudioVolumeGroupCallbacks.size() : -1;
}
int AudioSystem::AudioPolicyServiceClient::removeAudioVolumeGroupCallback(
- const sp<AudioVolumeGroupCallback>& callback) {
+ const sp<media::INativeAudioVolumeGroupCallback>& callback) {
std::lock_guard _l(mMutex);
return mAudioVolumeGroupCallbacks.erase(callback) > 0
? mAudioVolumeGroupCallbacks.size() : -1;
@@ -3012,13 +3032,12 @@
Status AudioSystem::AudioPolicyServiceClient::onAudioVolumeGroupChanged(int32_t group,
int32_t flags) {
- volume_group_t groupLegacy = VALUE_OR_RETURN_BINDER_STATUS(
- aidl2legacy_int32_t_volume_group_t(group));
- int flagsLegacy = VALUE_OR_RETURN_BINDER_STATUS(convertReinterpret<int>(flags));
-
+ AudioVolumeGroupChangeEvent aidlEvent;
+ aidlEvent.groupId = group;
+ aidlEvent.flags = flags;
std::lock_guard _l(mMutex);
for (const auto& callback : mAudioVolumeGroupCallbacks) {
- callback->onAudioVolumeGroupChanged(groupLegacy, flagsLegacy);
+ callback->onAudioVolumeGroupChanged(aidlEvent);
}
return Status::ok();
}
@@ -3114,9 +3133,6 @@
for (const auto& callback : mAudioPortCallbacks) {
callback->onServiceDied();
}
- for (const auto& callback : mAudioVolumeGroupCallbacks) {
- callback->onServiceDied();
- }
}
ConversionResult<record_client_info_t>
diff --git a/media/libaudioclient/aidl/android/media/INativeAudioVolumeGroupCallback.aidl b/media/libaudioclient/aidl/android/media/INativeAudioVolumeGroupCallback.aidl
new file mode 100644
index 0000000..43c6a65
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/INativeAudioVolumeGroupCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2025 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.
+ */
+
+package android.media;
+
+import android.media.audio.common.AudioVolumeGroupChangeEvent;
+
+/**
+ * The INativeAudioVolumeGroupCallback interface is a callback associated to the
+ * setVolumeGroupVolumeIndex API. The callback is used by the AudioPolicyManager
+ * implementation in native audio server to communicate volume changes.
+ * {@hide}
+ */
+oneway interface INativeAudioVolumeGroupCallback {
+ /**
+ * Called when the index applied by the AudioPolicyManager changes
+ */
+ void onAudioVolumeGroupChanged(in AudioVolumeGroupChangeEvent volumeChangeEvent);
+}
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index db010c8..16c3a7f 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -30,6 +30,7 @@
#include <android/media/BnAudioFlingerClient.h>
#include <android/media/BnAudioPolicyServiceClient.h>
#include <android/media/EffectDescriptor.h>
+#include <android/media/INativeAudioVolumeGroupCallback.h>
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISoundDose.h>
#include <android/media/ISoundDoseCallback.h>
@@ -739,12 +740,12 @@
virtual ~AudioVolumeGroupCallback() {}
virtual void onAudioVolumeGroupChanged(volume_group_t group, int flags) = 0;
- virtual void onServiceDied() = 0;
-
};
- static status_t addAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback);
- static status_t removeAudioVolumeGroupCallback(const sp<AudioVolumeGroupCallback>& callback);
+ static status_t addAudioVolumeGroupCallback(
+ const sp<media::INativeAudioVolumeGroupCallback>& callback);
+ static status_t removeAudioVolumeGroupCallback(
+ const sp<media::INativeAudioVolumeGroupCallback>& callback);
class AudioPortCallback : public virtual RefBase
{
@@ -881,10 +882,10 @@
}
int addAudioVolumeGroupCallback(
- const sp<AudioVolumeGroupCallback>& callback) EXCLUDES(mMutex);
+ const sp<media::INativeAudioVolumeGroupCallback>& callback) EXCLUDES(mMutex);
int removeAudioVolumeGroupCallback(
- const sp<AudioVolumeGroupCallback>& callback) EXCLUDES(mMutex);
+ const sp<media::INativeAudioVolumeGroupCallback>& callback) EXCLUDES(mMutex);
bool isAudioVolumeGroupCbEnabled() const EXCLUDES(mMutex) {
std::lock_guard _l(mMutex);
@@ -914,7 +915,8 @@
private:
mutable std::mutex mMutex;
std::set<sp<AudioPortCallback>> mAudioPortCallbacks GUARDED_BY(mMutex);
- std::set<sp<AudioVolumeGroupCallback>> mAudioVolumeGroupCallbacks GUARDED_BY(mMutex);
+ std::set<sp<media::INativeAudioVolumeGroupCallback>> mAudioVolumeGroupCallbacks
+ GUARDED_BY(mMutex);
};
private:
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ca77a2f..2b37f19 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -48,7 +48,10 @@
#include <binder/IMemory.h>
#include <binder/IServiceManager.h>
#include <binder/MemoryDealer.h>
+#include <com_android_graphics_libgui_flags.h>
#include <cutils/properties.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
#include <hidlmemory/FrameworkUtils.h>
@@ -770,6 +773,42 @@
////////////////////////////////////////////////////////////////////////////////
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+class MediaCodec::ReleaseSurface {
+ public:
+ explicit ReleaseSurface(uint64_t usage) {
+ std::tie(mConsumer, mSurface) = BufferItemConsumer::create(usage);
+
+ struct FrameAvailableListener : public BufferItemConsumer::FrameAvailableListener {
+ FrameAvailableListener(const sp<BufferItemConsumer> &consumer) {
+ mConsumer = consumer;
+ }
+ void onFrameAvailable(const BufferItem&) override {
+ BufferItem buffer;
+ // consume buffer
+ sp<BufferItemConsumer> consumer = mConsumer.promote();
+ if (consumer != nullptr && consumer->acquireBuffer(&buffer, 0) == NO_ERROR) {
+ consumer->releaseBuffer(buffer.mGraphicBuffer, buffer.mFence);
+ }
+ }
+
+ wp<BufferItemConsumer> mConsumer;
+ };
+ mFrameAvailableListener = sp<FrameAvailableListener>::make(mConsumer);
+ mConsumer->setFrameAvailableListener(mFrameAvailableListener);
+ mConsumer->setName(String8{"MediaCodec.release"});
+ }
+
+ const sp<Surface> &getSurface() {
+ return mSurface;
+ }
+
+ private:
+ sp<BufferItemConsumer> mConsumer;
+ sp<Surface> mSurface;
+ sp<BufferItemConsumer::FrameAvailableListener> mFrameAvailableListener;
+ };
+#else
class MediaCodec::ReleaseSurface {
public:
explicit ReleaseSurface(uint64_t usage) {
@@ -807,6 +846,7 @@
sp<IGraphicBufferConsumer> mConsumer;
sp<Surface> mSurface;
};
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libstagefright/MediaSync.cpp b/media/libstagefright/MediaSync.cpp
index 4bd0895..1891954 100644
--- a/media/libstagefright/MediaSync.cpp
+++ b/media/libstagefright/MediaSync.cpp
@@ -18,8 +18,15 @@
#define LOG_TAG "MediaSync"
#include <inttypes.h>
-#include <gui/BufferQueue.h>
+#include <com_android_graphics_libgui_flags.h>
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+#else
#include <gui/IGraphicBufferConsumer.h>
+#endif
+#include <gui/BufferQueue.h>
#include <gui/IGraphicBufferProducer.h>
#include <media/AudioTrack.h>
@@ -74,7 +81,11 @@
MediaSync::~MediaSync() {
if (mInput != NULL) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ mInput->abandon();
+#else
mInput->consumerDisconnect();
+#endif
}
if (mOutput != NULL) {
mOutput->disconnect(NATIVE_WINDOW_API_MEDIA);
@@ -204,6 +215,39 @@
return INVALID_OPERATION;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ int usageFlags = 0;
+ mOutput->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usageFlags);
+
+ auto [newInput, surface] = BufferItemConsumer::create(usageFlags);
+
+ sp<InputListener> listener(new InputListener(this));
+ newInput->setFrameAvailableListener(listener);
+ newInput->setName(String8("MediaSync"));
+ // propagate usage bits from output surface
+ status_t status = newInput->setConsumerUsageBits(usageFlags);
+ if (status != OK) {
+ ALOGE("%s: Unable to set usage bits to %d", __FUNCTION__, usageFlags);
+ return status;
+ }
+
+ // set undequeued buffer count
+ int minUndequeuedBuffers;
+ mOutput->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
+ status = newInput->setMaxAcquiredBufferCount(minUndequeuedBuffers);
+ if (status != OK) {
+ ALOGE("%s: Unable to set setMaxAcquiredBufferCount to %d", __FUNCTION__,
+ minUndequeuedBuffers);
+ return status;
+ }
+
+ mMaxAcquiredBufferCount = minUndequeuedBuffers;
+ mUsageFlagsFromOutput = usageFlags;
+ mInput = newInput;
+ mListener = listener;
+ *outBufferProducer = surface->getIGraphicBufferProducer();
+ return OK;
+#else
sp<IGraphicBufferProducer> bufferProducer;
sp<IGraphicBufferConsumer> bufferConsumer;
BufferQueue::createBufferQueue(&bufferProducer, &bufferConsumer);
@@ -227,6 +271,7 @@
bufferConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBufferCount);
}
return status;
+#endif
}
void MediaSync::resync_l() {
@@ -339,7 +384,15 @@
void MediaSync::setName(const AString &name) {
Mutex::Autolock lock(mMutex);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ if (mInput) {
+ mInput->setName(String8(name.c_str()));
+ } else {
+ ALOGE("%s with name %s called without an mInput set", __FUNCTION__, name.c_str());
+ }
+#else
mInput->setConsumerName(String8(name.c_str()));
+#endif
}
void MediaSync::flush() {
@@ -621,7 +674,11 @@
ALOGV("acquired buffer %#llx from input", (long long)bufferItem.mGraphicBuffer->getId());
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ status = mInput->detachBuffer(bufferItem.mGraphicBuffer);
+#else
status = mInput->detachBuffer(bufferItem.mSlot);
+#endif
if (status != NO_ERROR) {
ALOGE("detaching buffer from input failed (%d)", status);
if (status == NO_INIT) {
@@ -634,7 +691,11 @@
if (mBuffersFromInput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) {
// Something is wrong since this buffer should be at our hands, bail.
ALOGE("received buffer multiple times from input");
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ mInput->abandon();
+#else
mInput->consumerDisconnect();
+#endif
onAbandoned_l(true /* isInput */);
return;
}
@@ -687,7 +748,11 @@
if (mBuffersSentToOutput.indexOfKey(bufferItem.mGraphicBuffer->getId()) >= 0) {
// Something is wrong since this buffer should be held by output now, bail.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ mInput->abandon();
+#else
mInput->consumerDisconnect();
+#endif
onAbandoned_l(true /* isInput */);
return;
}
@@ -748,10 +813,18 @@
// Attach and release the buffer back to the input.
int consumerSlot;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ status_t status = mInput->attachBuffer(oldBuffer);
+#else
status_t status = mInput->attachBuffer(&consumerSlot, oldBuffer);
+#endif
ALOGE_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status);
if (status == NO_ERROR) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ mInput->releaseBuffer(oldBuffer, fence);
+#else
status = mInput->releaseBuffer(consumerSlot, 0 /* frameNumber */, fence);
+#endif
ALOGE_IF(status != NO_ERROR, "releasing buffer to input failed (%d)", status);
}
@@ -770,7 +843,11 @@
if (isInput) {
mOutput->disconnect(NATIVE_WINDOW_API_MEDIA);
} else {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ mInput->abandon();
+#else
mInput->consumerDisconnect();
+#endif
}
mIsAbandoned = true;
}
@@ -815,6 +892,7 @@
mSync->onFrameAvailableFromInput();
}
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
// We don't care about sideband streams, since we won't relay them.
void MediaSync::InputListener::onSidebandStreamChanged() {
ALOGE("onSidebandStreamChanged: got sideband stream unexpectedly.");
@@ -825,6 +903,7 @@
Mutex::Autolock lock(mSync->mMutex);
mSync->onAbandoned_l(true /* isInput */);
}
+#endif
MediaSync::OutputListener::OutputListener(const sp<MediaSync> &sync,
const sp<IGraphicBufferProducer> &output)
diff --git a/media/libstagefright/include/media/stagefright/MediaSync.h b/media/libstagefright/include/media/stagefright/MediaSync.h
index f50fb8f..f6f36bb 100644
--- a/media/libstagefright/include/media/stagefright/MediaSync.h
+++ b/media/libstagefright/include/media/stagefright/MediaSync.h
@@ -17,7 +17,13 @@
#ifndef MEDIA_SYNC_H
#define MEDIA_SYNC_H
+#include <com_android_graphics_libgui_flags.h>
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+#include <gui/BufferItemConsumer.h>
+#else
#include <gui/IConsumerListener.h>
+#endif
#include <gui/IProducerListener.h>
#include <media/AudioResamplerPublic.h>
@@ -34,7 +40,9 @@
class BufferItem;
class Fence;
class GraphicBuffer;
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
class IGraphicBufferConsumer;
+#endif
class IGraphicBufferProducer;
struct MediaClock;
struct VideoFrameScheduler;
@@ -140,13 +148,19 @@
// This is a thin wrapper class that lets us listen to
// IConsumerListener::onFrameAvailable from mInput.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ class InputListener : public BufferItemConsumer::FrameAvailableListener {
+#else
class InputListener : public IConsumerListener, public IBinder::DeathRecipient {
+#endif
public:
InputListener(const sp<MediaSync> &sync);
virtual ~InputListener();
- // From IConsumerListener
- virtual void onFrameAvailable(const BufferItem &item);
+ // From FrameAvailableListener
+ virtual void onFrameAvailable(const BufferItem&) override;
+
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
// From IConsumerListener
// We don't care about released buffers because we detach each buffer as
@@ -160,8 +174,9 @@
// From IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder> &who);
+#endif
- private:
+ private:
sp<MediaSync> mSync;
};
@@ -192,7 +207,12 @@
mutable Mutex mMutex;
Condition mReleaseCondition;
size_t mNumOutstandingBuffers;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_MEDIA_MIGRATION)
+ sp<BufferItemConsumer> mInput;
+ sp<InputListener> mListener; // listener for mInput, so the reference isn't dropped.
+#else
sp<IGraphicBufferConsumer> mInput;
+#endif
sp<IGraphicBufferProducer> mOutput;
int mUsageFlagsFromOutput;
uint32_t mMaxAcquiredBufferCount; // max acquired buffer count
diff --git a/media/module/extractors/mp4/Android.bp b/media/module/extractors/mp4/Android.bp
index effd24a..f0aff32 100644
--- a/media/module/extractors/mp4/Android.bp
+++ b/media/module/extractors/mp4/Android.bp
@@ -52,6 +52,7 @@
shared_libs: [
"server_configurable_flags",
+ "libbase",
],
host_supported: true,
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index 08345f0..0695ceb 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -29,6 +29,10 @@
#include <utils/Log.h>
+#include <android-base/properties.h>
+#ifdef __ANDROID__
+#include <android/api-level.h>
+#endif //__ANDROID__
#include "AC4Parser.h"
#include "MPEG4Extractor.h"
#include "SampleTable.h"
@@ -85,6 +89,22 @@
kMaxAtomSize = 64 * 1024 * 1024,
};
+static bool isAtLeastRelease([[maybe_unused]] int version,
+ [[maybe_unused]] const std::string codeName) {
+#ifdef __ANDROID__
+ static std::once_flag sCheckOnce;
+ static std::string sDeviceCodeName;
+ static int sDeviceApiLevel = 0;
+ std::call_once(sCheckOnce, [&]() {
+ sDeviceCodeName = base::GetProperty("ro.build.version.codename", "");
+ sDeviceApiLevel = android_get_device_api_level();
+ });
+ return sDeviceApiLevel >= version || sDeviceCodeName == codeName;
+#else //__ANDROID__
+ return true;
+#endif //__ANDROID__
+}
+
class MPEG4Source : public MediaTrackHelper {
static const size_t kMaxPcmFrameSize = 8192;
public:
@@ -372,7 +392,9 @@
return MEDIA_MIMETYPE_VIDEO_HEVC;
case FOURCC("apv1"):
- if (!com::android::media::extractor::flags::extractor_mp4_enable_apv()) {
+ // Enable APV codec support from Android Baklava
+ if (!(isAtLeastRelease(36, "Baklava") &&
+ com::android::media::extractor::flags::extractor_mp4_enable_apv())) {
ALOGV("APV support not enabled");
return "application/octet-stream";
}
@@ -2637,7 +2659,9 @@
}
case FOURCC("apvC"): {
- if (!com::android::media::extractor::flags::extractor_mp4_enable_apv()) {
+ // Enable APV codec support from Android Baklava
+ if (!(isAtLeastRelease(36, "Baklava") &&
+ com::android::media::extractor::flags::extractor_mp4_enable_apv())) {
ALOGV("APV support not enabled");
*offset += chunk_size;
break;
@@ -5239,8 +5263,12 @@
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
- mIsAPV = com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
- !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV);
+ // Enable APV codec support from Android Baklava
+ mIsAPV = false;
+ if (isAtLeastRelease(36, "Baklava")) {
+ mIsAPV = com::android::media::extractor::flags::extractor_mp4_enable_apv() &&
+ !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_APV);
+ }
mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
mIsHeif = !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) && mItemTable != NULL;
diff --git a/media/module/libapexcodecs/Android.bp b/media/module/libapexcodecs/Android.bp
index e49230a..27c1d22 100644
--- a/media/module/libapexcodecs/Android.bp
+++ b/media/module/libapexcodecs/Android.bp
@@ -20,6 +20,11 @@
cc_defaults {
name: "libcom.android.media.swcodec.apexcodecs-defaults",
+
+ defaults: [
+ "libcodec2-internal-defaults",
+ ],
+
header_libs: [
"libbase_headers",
],
@@ -31,8 +36,14 @@
"libnativewindow",
],
+ static_libs: [
+ "android.media.swcodec.flags-aconfig-cc",
+ ],
+
export_include_dirs: ["include"],
+ local_include_dirs: ["private"],
+
export_shared_lib_headers: [
"libbase",
"libnativewindow",
@@ -53,6 +64,8 @@
name: "libcom.android.media.swcodec.apexcodecs-testing",
defaults: ["libcom.android.media.swcodec.apexcodecs-defaults"],
+ srcs: ["tests/ApexCodecsStoreTestImpl.cpp"],
+
visibility: [
":__subpackages__",
],
@@ -67,6 +80,8 @@
"//frameworks/av/media/codec2/hal/client",
],
+ srcs: ["ApexCodecsStoreImpl.cpp"],
+
min_sdk_version: "apex_inherit",
version_script: "libcom.android.media.swcodec.apexcodecs.map.txt",
stubs: {
diff --git a/media/module/libapexcodecs/ApexCodecs.cpp b/media/module/libapexcodecs/ApexCodecs.cpp
index 6701e38..8dec439 100644
--- a/media/module/libapexcodecs/ApexCodecs.cpp
+++ b/media/module/libapexcodecs/ApexCodecs.cpp
@@ -14,18 +14,89 @@
* limitations under the License.
*/
+#define LOG_TAG "ApexCodecs"
+// #define LOG_NDEBUG 0
+#include <android-base/logging.h>
+
#include <new>
+#include <map>
+#include <vector>
+
+#include <C2ParamInternal.h>
+#include <android_media_swcodec_flags.h>
#include <android-base/no_destructor.h>
#include <apex/ApexCodecs.h>
+#include <apex/ApexCodecsImpl.h>
#include <apex/ApexCodecsParam.h>
// TODO: remove when we have real implementations
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
+using ::android::apexcodecs::ApexComponentIntf;
+using ::android::apexcodecs::ApexComponentStoreIntf;
+using ::android::base::ERROR;
+
+struct ApexCodec_Component {
+ explicit ApexCodec_Component(std::unique_ptr<ApexComponentIntf> &&comp)
+ : mComponent(std::move(comp)) {
+ }
+
+ ApexCodec_Status start() {
+ return mComponent->start();
+ }
+
+ ApexCodec_Status flush() {
+ return mComponent->flush();
+ }
+
+ ApexCodec_Status reset() {
+ return mComponent->reset();
+ }
+
+private:
+ std::unique_ptr<ApexComponentIntf> mComponent;
+};
+
struct ApexCodec_ComponentStore {
- ApexCodec_ComponentStore() = default;
+ ApexCodec_ComponentStore() : mStore((ApexComponentStoreIntf *)GetApexComponentStore()) {
+ if (mStore == nullptr) {
+ return;
+ }
+ mC2Traits = mStore->listComponents();
+ mTraits.reserve(mC2Traits.size());
+ for (const std::shared_ptr<const C2Component::Traits> &trait : mC2Traits) {
+ mTraits.push_back(ApexCodec_ComponentTraits{
+ trait->name.c_str(), // name
+ trait->mediaType.c_str(), // mediaType
+ (ApexCodec_Kind)trait->kind, // kind
+ (ApexCodec_Domain)trait->domain, // domain
+ });
+ }
+ }
+
+ ApexCodec_ComponentTraits *getTraits(size_t index) {
+ if (mStore == nullptr) {
+ return nullptr;
+ }
+ if (index < mTraits.size()) {
+ return mTraits.data() + index;
+ } else {
+ return nullptr;
+ }
+ }
+
+ std::unique_ptr<ApexComponentIntf> createComponent(const char *name) {
+ if (mStore == nullptr) {
+ return nullptr;
+ }
+ return mStore->createComponent(name);
+ }
+private:
+ ApexComponentStoreIntf *mStore;
+ std::vector<std::shared_ptr<const C2Component::Traits>> mC2Traits;
+ std::vector<ApexCodec_ComponentTraits> mTraits;
};
ApexCodec_ComponentStore *ApexCodec_GetComponentStore() {
@@ -35,27 +106,61 @@
ApexCodec_ComponentTraits *ApexCodec_Traits_get(
ApexCodec_ComponentStore *store, size_t index) {
- return nullptr;
+ if (!android::media::swcodec::flags::apexcodecs_base()) {
+ return nullptr;
+ }
+ return store->getTraits(index);
}
ApexCodec_Status ApexCodec_Component_create(
ApexCodec_ComponentStore *store, const char *name, ApexCodec_Component **comp) {
+ if (!android::media::swcodec::flags::apexcodecs_base()) {
+ return APEXCODEC_STATUS_NOT_FOUND;
+ }
+ if (store == nullptr) {
+ LOG(ERROR) << "ApexCodec_Component_create: store is nullptr";
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (name == nullptr) {
+ LOG(ERROR) << "ApexCodec_Component_create: name is nullptr";
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (comp == nullptr) {
+ LOG(ERROR) << "ApexCodec_Component_create: comp is nullptr";
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
*comp = nullptr;
- return APEXCODEC_STATUS_NOT_FOUND;
+ std::unique_ptr<ApexComponentIntf> compIntf = store->createComponent(name);
+ if (compIntf == nullptr) {
+ return APEXCODEC_STATUS_NOT_FOUND;
+ }
+ *comp = new ApexCodec_Component(std::move(compIntf));
+ return APEXCODEC_STATUS_OK;
}
-void ApexCodec_Component_destroy(ApexCodec_Component *comp) {}
+void ApexCodec_Component_destroy(ApexCodec_Component *comp) {
+ delete comp;
+}
ApexCodec_Status ApexCodec_Component_start(ApexCodec_Component *comp) {
- return APEXCODEC_STATUS_OMITTED;
+ if (comp == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return comp->start();
}
ApexCodec_Status ApexCodec_Component_flush(ApexCodec_Component *comp) {
- return APEXCODEC_STATUS_OMITTED;
+ if (comp == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return comp->flush();
}
ApexCodec_Status ApexCodec_Component_reset(ApexCodec_Component *comp) {
- return APEXCODEC_STATUS_OMITTED;
+ if (comp == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return comp->reset();
}
ApexCodec_Configurable *ApexCodec_Component_getConfigurable(
@@ -63,16 +168,148 @@
return nullptr;
}
+struct ApexCodec_Buffer {
+public:
+ ApexCodec_Buffer()
+ : mType(APEXCODEC_BUFFER_TYPE_EMPTY) {
+ }
+
+ ~ApexCodec_Buffer() {
+ }
+
+ void clear() {
+ mType = APEXCODEC_BUFFER_TYPE_EMPTY;
+ mBufferInfo.reset();
+ mLinearBuffer = {};
+ mGraphicBuffer = nullptr;
+ mConfigUpdates.reset();
+ mOwnedConfigUpdates.reset();
+ }
+
+ ApexCodec_BufferType getType() const {
+ return mType;
+ }
+
+ void setBufferInfo(ApexCodec_BufferFlags flags, uint64_t frameIndex, uint64_t timestampUs) {
+ mBufferInfo.emplace(BufferInfo{flags, frameIndex, timestampUs});
+ }
+
+ ApexCodec_Status setLinearBuffer(const ApexCodec_LinearBuffer *linearBuffer) {
+ if (mType != APEXCODEC_BUFFER_TYPE_EMPTY) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ mType = APEXCODEC_BUFFER_TYPE_LINEAR;
+ if (linearBuffer == nullptr) {
+ mLinearBuffer.data = nullptr;
+ mLinearBuffer.size = 0;
+ } else {
+ mLinearBuffer = *linearBuffer;
+ }
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status setGraphicBuffer(AHardwareBuffer *graphicBuffer) {
+ if (mType != APEXCODEC_BUFFER_TYPE_EMPTY) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ mType = APEXCODEC_BUFFER_TYPE_GRAPHIC;
+ mGraphicBuffer = graphicBuffer;
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status setConfigUpdates(const ApexCodec_LinearBuffer *configUpdates) {
+ if (configUpdates == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (mConfigUpdates.has_value()) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ mOwnedConfigUpdates.reset();
+ mConfigUpdates.emplace(*configUpdates);
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status getBufferInfo(
+ ApexCodec_BufferFlags *outFlags,
+ uint64_t *outFrameIndex,
+ uint64_t *outTimestampUs) const {
+ if (!mBufferInfo.has_value()) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ *outFlags = mBufferInfo->flags;
+ *outFrameIndex = mBufferInfo->frameIndex;
+ *outTimestampUs = mBufferInfo->timestampUs;
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status getLinearBuffer(ApexCodec_LinearBuffer *outLinearBuffer) const {
+ if (mType != APEXCODEC_BUFFER_TYPE_LINEAR) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ *outLinearBuffer = mLinearBuffer;
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status getGraphicBuffer(AHardwareBuffer **outGraphicBuffer) const {
+ if (mType != APEXCODEC_BUFFER_TYPE_GRAPHIC) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ *outGraphicBuffer = mGraphicBuffer;
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status getConfigUpdates(
+ ApexCodec_LinearBuffer *outConfigUpdates,
+ bool *outOwnedByClient) const {
+ if (!mConfigUpdates.has_value()) {
+ return APEXCODEC_STATUS_NOT_FOUND;
+ }
+ *outConfigUpdates = mConfigUpdates.value();
+ *outOwnedByClient = mOwnedConfigUpdates.has_value();
+ return APEXCODEC_STATUS_OK;
+ }
+
+ void setOwnedConfigUpdates(std::vector<uint8_t> &&configUpdates) {
+ mOwnedConfigUpdates = std::move(configUpdates);
+ mConfigUpdates.emplace(
+ ApexCodec_LinearBuffer{ configUpdates.data(), configUpdates.size() });
+ }
+
+private:
+ struct BufferInfo {
+ ApexCodec_BufferFlags flags;
+ uint64_t frameIndex;
+ uint64_t timestampUs;
+ };
+
+ ApexCodec_BufferType mType;
+ std::optional<BufferInfo> mBufferInfo;
+ ApexCodec_LinearBuffer mLinearBuffer;
+ AHardwareBuffer *mGraphicBuffer;
+ std::optional<ApexCodec_LinearBuffer> mConfigUpdates;
+ std::optional<std::vector<uint8_t>> mOwnedConfigUpdates;
+};
+
ApexCodec_Buffer *ApexCodec_Buffer_create() {
- return nullptr;
+ return new ApexCodec_Buffer;
}
-void ApexCodec_Buffer_destroy(ApexCodec_Buffer *buffer) {}
+void ApexCodec_Buffer_destroy(ApexCodec_Buffer *buffer) {
+ delete buffer;
+}
-void ApexCodec_Buffer_clear(ApexCodec_Buffer *buffer) {}
+void ApexCodec_Buffer_clear(ApexCodec_Buffer *buffer) {
+ if (buffer == nullptr) {
+ return;
+ }
+ buffer->clear();
+}
ApexCodec_BufferType ApexCodec_Buffer_getType(ApexCodec_Buffer *buffer) {
- return APEXCODEC_BUFFER_TYPE_EMPTY;
+ if (buffer == nullptr) {
+ return APEXCODEC_BUFFER_TYPE_EMPTY;
+ }
+ return buffer->getType();
}
void ApexCodec_Buffer_setBufferInfo(
@@ -80,24 +317,37 @@
ApexCodec_BufferFlags flags,
uint64_t frameIndex,
uint64_t timestampUs) {
+ if (buffer == nullptr) {
+ return;
+ }
+ buffer->setBufferInfo(flags, frameIndex, timestampUs);
}
ApexCodec_Status ApexCodec_Buffer_setLinearBuffer(
ApexCodec_Buffer *buffer,
const ApexCodec_LinearBuffer *linearBuffer) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->setLinearBuffer(linearBuffer);
}
ApexCodec_Status ApexCodec_Buffer_setGraphicBuffer(
ApexCodec_Buffer *buffer,
AHardwareBuffer *graphicBuffer) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->setGraphicBuffer(graphicBuffer);
}
ApexCodec_Status ApexCodec_Buffer_setConfigUpdates(
ApexCodec_Buffer *buffer,
const ApexCodec_LinearBuffer *configUpdates) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->setConfigUpdates(configUpdates);
}
ApexCodec_Status ApexCodec_Buffer_getBufferInfo(
@@ -105,38 +355,274 @@
ApexCodec_BufferFlags *outFlags,
uint64_t *outFrameIndex,
uint64_t *outTimestampUs) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->getBufferInfo(outFlags, outFrameIndex, outTimestampUs);
}
ApexCodec_Status ApexCodec_Buffer_getLinearBuffer(
ApexCodec_Buffer *buffer,
ApexCodec_LinearBuffer *outLinearBuffer) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->getLinearBuffer(outLinearBuffer);
}
ApexCodec_Status ApexCodec_Buffer_getGraphicBuffer(
ApexCodec_Buffer *buffer,
AHardwareBuffer **outGraphicBuffer) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->getGraphicBuffer(outGraphicBuffer);
}
ApexCodec_Status ApexCodec_Buffer_getConfigUpdates(
ApexCodec_Buffer *buffer,
ApexCodec_LinearBuffer *outConfigUpdates,
bool *outOwnedByClient) {
- return APEXCODEC_STATUS_OMITTED;
+ if (buffer == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return buffer->getConfigUpdates(outConfigUpdates, outOwnedByClient);
}
+struct ApexCodec_SupportedValues {
+public:
+ ApexCodec_SupportedValues(
+ const C2FieldSupportedValues &supportedValues,
+ const C2Value::type_t &numberType) {
+ mType = (ApexCodec_SupportedValuesType)supportedValues.type;
+ mNumberType = (ApexCodec_SupportedValuesNumberType)numberType;
+ switch (supportedValues.type) {
+ case C2FieldSupportedValues::RANGE: {
+ mValues.insert(mValues.end(), 5, ApexCodec_Value{});
+ ToApexCodecValue(supportedValues.range.min, numberType, &mValues[0]);
+ ToApexCodecValue(supportedValues.range.max, numberType, &mValues[1]);
+ ToApexCodecValue(supportedValues.range.step, numberType, &mValues[2]);
+ ToApexCodecValue(supportedValues.range.num, numberType, &mValues[3]);
+ ToApexCodecValue(supportedValues.range.denom, numberType, &mValues[4]);
+ break;
+ }
+ case C2FieldSupportedValues::VALUES:
+ case C2FieldSupportedValues::FLAGS: {
+ for (size_t i = 0; i < supportedValues.values.size(); ++i) {
+ mValues.emplace_back();
+ ToApexCodecValue(supportedValues.values[i], numberType, &mValues[i]);
+ }
+ break;
+ }
+ default:
+ // Unrecognized type; initialize as empty.
+ mType = APEXCODEC_SUPPORTED_VALUES_EMPTY;
+ break;
+ }
+ }
+
+ ~ApexCodec_SupportedValues() {
+ }
+
+ ApexCodec_Status getTypeAndValues(
+ ApexCodec_SupportedValuesType *type,
+ ApexCodec_SupportedValuesNumberType *numberType,
+ ApexCodec_Value **values,
+ uint32_t *numValues) {
+ if (type == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (numberType == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (values == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (numValues == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ *type = mType;
+ *numberType = mNumberType;
+ switch (mType) {
+ case APEXCODEC_SUPPORTED_VALUES_EMPTY: {
+ *values = nullptr;
+ *numValues = 0;
+ break;
+ }
+ case APEXCODEC_SUPPORTED_VALUES_RANGE:
+ case APEXCODEC_SUPPORTED_VALUES_VALUES:
+ case APEXCODEC_SUPPORTED_VALUES_FLAGS: {
+ if (mValues.empty()) {
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ *values = mValues.data();
+ *numValues = mValues.size();
+ break;
+ }
+ default:
+ return APEXCODEC_STATUS_BAD_STATE;
+ }
+ return APEXCODEC_STATUS_OK;
+ }
+
+ static bool ToApexCodecValue(
+ const C2Value::Primitive &value,
+ const C2Value::type_t &type,
+ ApexCodec_Value *outValue) {
+ switch (type) {
+ case C2Value::NO_INIT:
+ return false;
+ case C2Value::INT32:
+ outValue->i32 = value.i32;
+ return true;
+ case C2Value::UINT32:
+ outValue->u32 = value.u32;
+ return true;
+ case C2Value::INT64:
+ outValue->i64 = value.i64;
+ return true;
+ case C2Value::UINT64:
+ outValue->u64 = value.u64;
+ return true;
+ case C2Value::FLOAT:
+ outValue->f = value.fp;
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static C2Value::type_t GetFieldType(
+ const std::shared_ptr<C2ParamReflector> &reflector,
+ const C2ParamField& field) {
+ std::unique_ptr<C2StructDescriptor> desc = reflector->describe(
+ _C2ParamInspector::GetIndex(field));
+
+ for (const C2FieldDescriptor &fieldDesc : *desc) {
+ if (_C2ParamInspector::GetOffset(fieldDesc) == _C2ParamInspector::GetOffset(field)) {
+ if (_C2ParamInspector::GetSize(fieldDesc) != _C2ParamInspector::GetSize(field)) {
+ // Size doesn't match.
+ return C2Value::NO_INIT;
+ }
+ switch (fieldDesc.type()) {
+ case C2FieldDescriptor::INT32:
+ case C2FieldDescriptor::UINT32:
+ case C2FieldDescriptor::INT64:
+ case C2FieldDescriptor::UINT64:
+ case C2FieldDescriptor::FLOAT:
+ return (C2Value::type_t)fieldDesc.type();
+ default:
+ // Unrecognized type.
+ return C2Value::NO_INIT;
+ }
+ }
+ }
+ return C2Value::NO_INIT;
+ }
+
+private:
+ ApexCodec_SupportedValuesType mType;
+ ApexCodec_SupportedValuesNumberType mNumberType;
+ std::vector<ApexCodec_Value> mValues;
+};
+
ApexCodec_Status ApexCodec_SupportedValues_getTypeAndValues(
ApexCodec_SupportedValues *supportedValues,
ApexCodec_SupportedValuesType *type,
ApexCodec_SupportedValuesNumberType *numberType,
ApexCodec_Value **values,
uint32_t *numValues) {
- return APEXCODEC_STATUS_OMITTED;
+ if (supportedValues == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return supportedValues->getTypeAndValues(type, numberType, values, numValues);
}
-void ApexCodec_SupportedValues_destroy(ApexCodec_SupportedValues *values) {}
+void ApexCodec_SupportedValues_destroy(ApexCodec_SupportedValues *values) {
+ delete values;
+}
+
+struct ApexCodec_SettingResults {
+public:
+ explicit ApexCodec_SettingResults(
+ const std::shared_ptr<C2ParamReflector> &reflector,
+ const std::vector<C2SettingResult> &results) : mReflector(reflector) {
+ for (const C2SettingResult &c2Result : results) {
+ mResults.emplace_back();
+ Entry &entry = mResults.back();
+ entry.failure = (ApexCodec_SettingResultFailure)c2Result.failure;
+ entry.field.index = _C2ParamInspector::GetIndex(c2Result.field.paramOrField);
+ entry.field.offset = _C2ParamInspector::GetOffset(c2Result.field.paramOrField);
+ entry.field.size = _C2ParamInspector::GetSize(c2Result.field.paramOrField);
+ if (c2Result.field.values) {
+ entry.fieldValues = std::make_unique<ApexCodec_SupportedValues>(
+ *c2Result.field.values,
+ ApexCodec_SupportedValues::GetFieldType(mReflector,
+ c2Result.field.paramOrField));
+ entry.field.values = entry.fieldValues.get();
+ } else {
+ entry.field.values = nullptr;
+ }
+ for (const C2ParamFieldValues &c2Conflict : c2Result.conflicts) {
+ entry.conflicts.emplace_back();
+ ApexCodec_ParamFieldValues &conflict = entry.conflicts.back();
+ conflict.index = _C2ParamInspector::GetIndex(c2Conflict.paramOrField);
+ conflict.offset = _C2ParamInspector::GetOffset(c2Conflict.paramOrField);
+ conflict.size = _C2ParamInspector::GetSize(c2Conflict.paramOrField);
+ if (c2Conflict.values) {
+ entry.conflictValues.emplace_back(std::make_unique<ApexCodec_SupportedValues>(
+ *c2Conflict.values,
+ ApexCodec_SupportedValues::GetFieldType(mReflector,
+ c2Conflict.paramOrField)));
+ conflict.values = entry.conflictValues.back().get();
+ } else {
+ conflict.values = nullptr;
+ }
+ }
+ }
+ }
+
+ ~ApexCodec_SettingResults() {
+ }
+
+ ApexCodec_Status getResultAtIndex(
+ size_t index,
+ ApexCodec_SettingResultFailure *failure,
+ ApexCodec_ParamFieldValues *field,
+ ApexCodec_ParamFieldValues **conflicts,
+ size_t *numConflicts) {
+ if (failure == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (field == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (conflicts == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (numConflicts == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (index >= mResults.size()) {
+ return APEXCODEC_STATUS_NOT_FOUND;
+ }
+ *failure = mResults[index].failure;
+ *field = mResults[index].field;
+ *conflicts = mResults[index].conflicts.data();
+ *numConflicts = mResults[index].conflicts.size();
+ return APEXCODEC_STATUS_OK;
+ }
+private:
+ std::shared_ptr<C2ParamReflector> mReflector;
+ struct Entry {
+ ApexCodec_SettingResultFailure failure;
+ ApexCodec_ParamFieldValues field;
+ std::vector<ApexCodec_ParamFieldValues> conflicts;
+ std::unique_ptr<ApexCodec_SupportedValues> fieldValues;
+ std::vector<std::unique_ptr<ApexCodec_SupportedValues>> conflictValues;
+ };
+ std::vector<Entry> mResults;
+};
ApexCodec_Status ApexCodec_SettingResults_getResultAtIndex(
ApexCodec_SettingResults *results,
@@ -145,10 +631,15 @@
ApexCodec_ParamFieldValues *field,
ApexCodec_ParamFieldValues **conflicts,
size_t *numConflicts) {
- return APEXCODEC_STATUS_OMITTED;
+ if (results == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return results->getResultAtIndex(index, failure, field, conflicts, numConflicts);
}
-void ApexCodec_SettingResults_destroy(ApexCodec_SettingResults *results) {}
+void ApexCodec_SettingResults_destroy(ApexCodec_SettingResults *results) {
+ delete results;
+}
ApexCodec_Status ApexCodec_Component_process(
ApexCodec_Component *comp,
@@ -175,11 +666,90 @@
return APEXCODEC_STATUS_OMITTED;
}
+struct ApexCodec_ParamDescriptors {
+public:
+ explicit ApexCodec_ParamDescriptors(
+ const std::vector<std::shared_ptr<C2ParamDescriptor>> ¶mDescriptors) {
+ for (const std::shared_ptr<C2ParamDescriptor> &c2Descriptor : paramDescriptors) {
+ if (!c2Descriptor) {
+ continue;
+ }
+ uint32_t index = c2Descriptor->index();
+ Entry &entry = mDescriptors[index];
+ entry.index = index;
+ entry.attr = (ApexCodec_ParamAttribute)_C2ParamInspector::GetAttrib(*c2Descriptor);
+ entry.name = c2Descriptor->name();
+ for (const C2Param::Index &dependency : c2Descriptor->dependencies()) {
+ entry.dependencies.emplace_back((uint32_t)dependency);
+ }
+ mIndices.push_back(entry.index);
+ }
+ }
+
+ ~ApexCodec_ParamDescriptors() {
+ }
+
+ ApexCodec_Status getIndices(uint32_t **indices, size_t *numIndices) {
+ if (indices == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (numIndices == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ *indices = mIndices.data();
+ *numIndices = mIndices.size();
+ return APEXCODEC_STATUS_OK;
+ }
+
+ ApexCodec_Status getDescriptor(
+ uint32_t index,
+ ApexCodec_ParamAttribute *attr,
+ const char **name,
+ uint32_t **dependencies,
+ size_t *numDependencies) {
+ if (attr == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (name == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (dependencies == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ if (numDependencies == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ auto it = mDescriptors.find(index);
+ if (it == mDescriptors.end()) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ const Entry &entry = it->second;
+ *attr = entry.attr;
+ *name = entry.name.c_str();
+ *dependencies = const_cast<uint32_t *>(entry.dependencies.data());
+ *numDependencies = entry.dependencies.size();
+ return APEXCODEC_STATUS_OK;
+ }
+
+private:
+ struct Entry {
+ uint32_t index;
+ ApexCodec_ParamAttribute attr;
+ C2String name;
+ std::vector<uint32_t> dependencies;
+ };
+ std::map<uint32_t, Entry> mDescriptors;
+ std::vector<uint32_t> mIndices;
+};
+
ApexCodec_Status ApexCodec_ParamDescriptors_getIndices(
ApexCodec_ParamDescriptors *descriptors,
uint32_t **indices,
size_t *numIndices) {
- return APEXCODEC_STATUS_OMITTED;
+ if (descriptors == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return descriptors->getIndices(indices, numIndices);
}
ApexCodec_Status ApexCodec_ParamDescriptors_getDescriptor(
@@ -189,10 +759,14 @@
const char **name,
uint32_t **dependencies,
size_t *numDependencies) {
- return APEXCODEC_STATUS_OMITTED;
+ if (descriptors == nullptr) {
+ return APEXCODEC_STATUS_BAD_VALUE;
+ }
+ return descriptors->getDescriptor(index, attr, name, dependencies, numDependencies);
}
void ApexCodec_ParamDescriptors_destroy(ApexCodec_ParamDescriptors *descriptors) {
+ delete descriptors;
}
ApexCodec_Status ApexCodec_Configurable_querySupportedParams(
diff --git a/media/module/libapexcodecs/ApexCodecsImpl.cpp b/media/module/libapexcodecs/ApexCodecsImpl.cpp
new file mode 100644
index 0000000..a737c57
--- /dev/null
+++ b/media/module/libapexcodecs/ApexCodecsImpl.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#include <android-base/no_destructor.h>
+#include <apex/ApexCodecsImpl.h>
+
+namespace android::apexcodecs {
+
+class ApexComponentImpl : public ApexComponentIntf {
+public:
+ ApexComponentImpl(const std::shared_ptr<C2Component> &comp) : mComponent(comp) {}
+ virtual ApexCodec_Status start() = 0;
+ virtual ApexCodec_Status flush() = 0;
+ virtual ApexCodec_Status reset() = 0;
+ virtual ApexCodec_Configurable *getConfigurable() = 0;
+ virtual ApexCodec_Status process(
+ const ApexCodec_Buffer *input,
+ ApexCodec_Buffer *output,
+ size_t *consumed,
+ size_t *produced) = 0;
+private:
+ std::shared_ptr<C2Component> mComponent;
+};
+
+} // namespace android::apexcodecs
\ No newline at end of file
diff --git a/media/module/libapexcodecs/ApexCodecsStoreImpl.cpp b/media/module/libapexcodecs/ApexCodecsStoreImpl.cpp
new file mode 100644
index 0000000..3beb510
--- /dev/null
+++ b/media/module/libapexcodecs/ApexCodecsStoreImpl.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#include <android-base/no_destructor.h>
+#include <apex/ApexCodecsImpl.h>
+
+namespace android::apexcodecs {
+
+class ApexComponentStoreImpl : public ApexComponentStoreIntf {
+public:
+ ApexComponentStoreImpl() = default;
+
+ std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() const override {
+ return {};
+ }
+ virtual std::unique_ptr<ApexComponentIntf> createComponent(const char *name [[maybe_unused]]) {
+ return nullptr;
+ }
+};
+
+} // namespace android::apexcodecs
+
+extern "C" void *GetApexComponentStore() {
+ static ::android::base::NoDestructor<::android::apexcodecs::ApexComponentStoreImpl> sStore;
+ return sStore.get();
+}
\ No newline at end of file
diff --git a/media/module/libapexcodecs/TEST_MAPPING b/media/module/libapexcodecs/TEST_MAPPING
new file mode 100644
index 0000000..6ff6a24
--- /dev/null
+++ b/media/module/libapexcodecs/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "libcom.android.media.swcodec.apexcodecs-tests"
+ }
+ ]
+}
diff --git a/media/module/libapexcodecs/include/apex/ApexCodecs.h b/media/module/libapexcodecs/include/apex/ApexCodecs.h
index 96bd2da..8dfee97 100644
--- a/media/module/libapexcodecs/include/apex/ApexCodecs.h
+++ b/media/module/libapexcodecs/include/apex/ApexCodecs.h
@@ -455,7 +455,7 @@
* input buffer's frame index.
* \param outTimestampUs the timestamp for the buffer in microseconds
* \return APEXCODEC_STATUS_OK if successful
- * \return APEXCODEC_STATUS_BAD_STATE if |buffer| is empty
+ * \return APEXCODEC_STATUS_BAD_STATE if buffer info was never set
*/
ApexCodec_Status ApexCodec_Buffer_getBufferInfo(
ApexCodec_Buffer *_Nonnull buffer,
diff --git a/media/module/libapexcodecs/private/apex/ApexCodecsImpl.h b/media/module/libapexcodecs/private/apex/ApexCodecsImpl.h
new file mode 100644
index 0000000..f01af87
--- /dev/null
+++ b/media/module/libapexcodecs/private/apex/ApexCodecsImpl.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <C2Component.h>
+
+#include <apex/ApexCodecs.h>
+#include <apex/ApexCodecsParam.h>
+
+namespace android::apexcodecs {
+
+class ApexComponentIntf {
+public:
+ virtual ~ApexComponentIntf() = default;
+ virtual ApexCodec_Status start() = 0;
+ virtual ApexCodec_Status flush() = 0;
+ virtual ApexCodec_Status reset() = 0;
+ virtual ApexCodec_Configurable *getConfigurable() = 0;
+ virtual ApexCodec_Status process(
+ const ApexCodec_Buffer *input,
+ ApexCodec_Buffer *output,
+ size_t *consumed,
+ size_t *produced) = 0;
+};
+
+class ApexComponentStoreIntf {
+public:
+ virtual ~ApexComponentStoreIntf() = default;
+ virtual std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() const = 0;
+ virtual std::unique_ptr<ApexComponentIntf> createComponent(const char *name) = 0;
+};
+
+} // namespace android
+
+__BEGIN_DECLS
+
+void *GetApexComponentStore();
+
+__END_DECLS
\ No newline at end of file
diff --git a/media/module/libapexcodecs/tests/Android.bp b/media/module/libapexcodecs/tests/Android.bp
index d13a577..1d444ad 100644
--- a/media/module/libapexcodecs/tests/Android.bp
+++ b/media/module/libapexcodecs/tests/Android.bp
@@ -22,9 +22,16 @@
cc_test {
name: "libcom.android.media.swcodec.apexcodecs-tests",
shared_libs: [
- "libcom.android.media.swcodec.apexcodecs-testing",
+ "libbinder_ndk",
"libcodec2",
+ "libnativewindow",
+ ],
+
+ static_libs: [
+ "libcom.android.media.swcodec.apexcodecs-testing",
],
srcs: ["ApexCodecsTest.cpp"],
+
+ test_suites: ["general-tests"],
}
diff --git a/media/module/libapexcodecs/tests/ApexCodecsStoreTestImpl.cpp b/media/module/libapexcodecs/tests/ApexCodecsStoreTestImpl.cpp
new file mode 100644
index 0000000..fb0e98e
--- /dev/null
+++ b/media/module/libapexcodecs/tests/ApexCodecsStoreTestImpl.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#include <android-base/no_destructor.h>
+#include <apex/ApexCodecsImpl.h>
+
+namespace android::apexcodecs::test {
+
+// This is a test implementation of ApexComponentStoreIntf.
+// It may contain different set of components than the APEX for testing purpose.
+class ApexComponentStoreImpl : public ApexComponentStoreIntf {
+public:
+ ApexComponentStoreImpl() = default;
+
+ std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() const override {
+ return {};
+ }
+ virtual std::unique_ptr<ApexComponentIntf> createComponent(const char *name [[maybe_unused]]) {
+ return nullptr;
+ }
+};
+
+} // namespace android::apexcodecs::test
+
+extern "C" void *GetApexComponentStore() {
+ using ::android::apexcodecs::test::ApexComponentStoreImpl;
+ static ::android::base::NoDestructor<ApexComponentStoreImpl> sStore;
+ return sStore.get();
+}
\ No newline at end of file
diff --git a/media/module/libapexcodecs/tests/ApexCodecsTest.cpp b/media/module/libapexcodecs/tests/ApexCodecsTest.cpp
index f551ce7..3338aff 100644
--- a/media/module/libapexcodecs/tests/ApexCodecsTest.cpp
+++ b/media/module/libapexcodecs/tests/ApexCodecsTest.cpp
@@ -1,8 +1,10 @@
#include <C2.h>
#include <C2Component.h>
+#include <android/hardware_buffer_aidl.h>
#include <apex/ApexCodecs.h>
#include <apex/ApexCodecsParam.h>
+#include <gtest/gtest.h>
// static_asserts for enum values match
static_assert((uint32_t)APEXCODEC_STATUS_OK == (uint32_t)C2_OK);
@@ -98,4 +100,169 @@
static_assert((uint32_t)APEXCODEC_PARAM_IS_READ_ONLY == (uint32_t)C2ParamDescriptor::IS_READ_ONLY);
static_assert((uint32_t)APEXCODEC_PARAM_IS_HIDDEN == (uint32_t)C2ParamDescriptor::IS_HIDDEN);
static_assert((uint32_t)APEXCODEC_PARAM_IS_INTERNAL == (uint32_t)C2ParamDescriptor::IS_INTERNAL);
-static_assert((uint32_t)APEXCODEC_PARAM_IS_CONSTANT == (uint32_t)C2ParamDescriptor::IS_CONST);
\ No newline at end of file
+static_assert((uint32_t)APEXCODEC_PARAM_IS_CONSTANT == (uint32_t)C2ParamDescriptor::IS_CONST);
+
+using ::aidl::android::hardware::HardwareBuffer;
+
+class SpApexCodecBuffer {
+public:
+ SpApexCodecBuffer() {
+ mBuffer = ApexCodec_Buffer_create();
+ }
+
+ ~SpApexCodecBuffer() {
+ ApexCodec_Buffer_destroy(mBuffer);
+ }
+
+ ApexCodec_Buffer* get() const {
+ return mBuffer;
+ }
+
+private:
+ ApexCodec_Buffer* mBuffer;
+};
+
+TEST(ApexCodecsTest, BufferCreateDestroyTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+}
+
+TEST(ApexCodecsTest, BufferInitialStateTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+ ASSERT_EQ(ApexCodec_Buffer_getType(buffer.get()), APEXCODEC_BUFFER_TYPE_EMPTY);
+
+ ApexCodec_BufferFlags flags;
+ uint64_t frameIndex;
+ uint64_t timestampUs;
+ ASSERT_EQ(ApexCodec_Buffer_getBufferInfo(buffer.get(), &flags, &frameIndex, ×tampUs),
+ APEXCODEC_STATUS_BAD_STATE);
+
+ ApexCodec_LinearBuffer linearBuffer;
+ ASSERT_EQ(ApexCodec_Buffer_getLinearBuffer(buffer.get(), &linearBuffer),
+ APEXCODEC_STATUS_BAD_STATE);
+
+ AHardwareBuffer* graphicBuffer;
+ ASSERT_EQ(ApexCodec_Buffer_getGraphicBuffer(buffer.get(), &graphicBuffer),
+ APEXCODEC_STATUS_BAD_STATE);
+
+ ApexCodec_LinearBuffer configUpdates;
+ bool ownedByClient;
+ ASSERT_EQ(ApexCodec_Buffer_getConfigUpdates(buffer.get(), &configUpdates, &ownedByClient),
+ APEXCODEC_STATUS_NOT_FOUND);
+}
+
+TEST(ApexCodecsTest, BufferSetGetInfoTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+
+ ApexCodec_Buffer_setBufferInfo(buffer.get(), APEXCODEC_FLAG_END_OF_STREAM, 123, 456);
+
+ ApexCodec_BufferFlags flags;
+ uint64_t frameIndex;
+ uint64_t timestampUs;
+ ASSERT_EQ(ApexCodec_Buffer_getBufferInfo(buffer.get(), &flags, &frameIndex, ×tampUs),
+ APEXCODEC_STATUS_OK);
+ ASSERT_EQ(flags, APEXCODEC_FLAG_END_OF_STREAM);
+ ASSERT_EQ(frameIndex, 123);
+ ASSERT_EQ(timestampUs, 456);
+}
+
+TEST(ApexCodecsTest, BufferSetGetLinearBufferTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+
+ uint8_t data[10];
+ ApexCodec_LinearBuffer linearBuffer;
+ linearBuffer.data = data;
+ linearBuffer.size = 10;
+ ASSERT_EQ(ApexCodec_Buffer_setLinearBuffer(buffer.get(), &linearBuffer), APEXCODEC_STATUS_OK);
+ ASSERT_EQ(ApexCodec_Buffer_getType(buffer.get()), APEXCODEC_BUFFER_TYPE_LINEAR);
+ // Clear the data to ensure that the buffer owns the data.
+ linearBuffer.data = nullptr;
+ linearBuffer.size = 0;
+ ASSERT_EQ(ApexCodec_Buffer_getLinearBuffer(buffer.get(), &linearBuffer), APEXCODEC_STATUS_OK);
+ ASSERT_EQ(linearBuffer.data, data);
+ ASSERT_EQ(linearBuffer.size, 10);
+
+ ASSERT_EQ(ApexCodec_Buffer_setLinearBuffer(buffer.get(), &linearBuffer),
+ APEXCODEC_STATUS_BAD_STATE);
+}
+
+TEST(ApexCodecsTest, BufferSetGetGraphicBufferTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+
+ HardwareBuffer hardwareBuffer;
+ AHardwareBuffer_Desc desc;
+ desc.width = 100;
+ desc.height = 100;
+ desc.layers = 1;
+ desc.format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420;
+ desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ AHardwareBuffer* graphicBuffer = nullptr;
+ AHardwareBuffer_allocate(&desc, &graphicBuffer);
+ hardwareBuffer.reset(graphicBuffer);
+ ASSERT_NE(graphicBuffer, nullptr);
+ ASSERT_EQ(ApexCodec_Buffer_setGraphicBuffer(buffer.get(), graphicBuffer), APEXCODEC_STATUS_OK);
+ ASSERT_EQ(ApexCodec_Buffer_getType(buffer.get()), APEXCODEC_BUFFER_TYPE_GRAPHIC);
+ graphicBuffer = nullptr;
+ ASSERT_EQ(ApexCodec_Buffer_getGraphicBuffer(buffer.get(), &graphicBuffer), APEXCODEC_STATUS_OK);
+ ASSERT_NE(graphicBuffer, nullptr);
+
+ ASSERT_EQ(ApexCodec_Buffer_setGraphicBuffer(buffer.get(), graphicBuffer),
+ APEXCODEC_STATUS_BAD_STATE);
+}
+
+TEST(ApexCodecsTest, BufferSetGetConfigUpdatesTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+
+ uint8_t configData[20];
+ ApexCodec_LinearBuffer configUpdates;
+ configUpdates.data = configData;
+ configUpdates.size = 20;
+ ASSERT_EQ(ApexCodec_Buffer_setConfigUpdates(buffer.get(), &configUpdates), APEXCODEC_STATUS_OK);
+
+ bool ownedByClient;
+ ASSERT_EQ(ApexCodec_Buffer_getConfigUpdates(buffer.get(), &configUpdates, &ownedByClient),
+ APEXCODEC_STATUS_OK);
+ ASSERT_EQ(configUpdates.data, configData);
+ ASSERT_EQ(configUpdates.size, 20);
+ ASSERT_EQ(ownedByClient, false);
+
+ ASSERT_EQ(ApexCodec_Buffer_setConfigUpdates(buffer.get(), &configUpdates),
+ APEXCODEC_STATUS_BAD_STATE);
+}
+
+TEST(ApexCodecsTest, BufferClearTest) {
+ SpApexCodecBuffer buffer;
+ ASSERT_NE(buffer.get(), nullptr);
+
+ uint8_t data[10];
+ ApexCodec_LinearBuffer linearBuffer;
+ linearBuffer.data = data;
+ linearBuffer.size = 10;
+ ASSERT_EQ(ApexCodec_Buffer_setLinearBuffer(buffer.get(), &linearBuffer), APEXCODEC_STATUS_OK);
+
+ uint8_t configData[20];
+ ApexCodec_LinearBuffer configUpdates;
+ configUpdates.data = configData;
+ configUpdates.size = 20;
+ ASSERT_EQ(ApexCodec_Buffer_setConfigUpdates(buffer.get(), &configUpdates), APEXCODEC_STATUS_OK);
+
+ ApexCodec_Buffer_clear(buffer.get());
+ ASSERT_EQ(ApexCodec_Buffer_getType(buffer.get()), APEXCODEC_BUFFER_TYPE_EMPTY);
+
+ ApexCodec_BufferFlags flags;
+ uint64_t frameIndex;
+ uint64_t timestampUs;
+ ASSERT_EQ(ApexCodec_Buffer_getBufferInfo(buffer.get(), &flags, &frameIndex, ×tampUs),
+ APEXCODEC_STATUS_BAD_STATE);
+ ASSERT_EQ(ApexCodec_Buffer_getLinearBuffer(buffer.get(), &linearBuffer),
+ APEXCODEC_STATUS_BAD_STATE);
+ bool ownedByClient;
+
+ ASSERT_EQ(ApexCodec_Buffer_getConfigUpdates(buffer.get(), &configUpdates, &ownedByClient),
+ APEXCODEC_STATUS_NOT_FOUND);
+}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 49e1422c..1ce9b8e 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1147,7 +1147,9 @@
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
bool directOnly) {
- sp<IOProfile> profile;
+ sp<IOProfile> directOnlyProfile = nullptr;
+ sp<IOProfile> compressOffloadProfile = nullptr;
+ sp<IOProfile> profile = nullptr;
for (const auto& hwModule : hwModules) {
for (const auto& curProfile : hwModule->getOutputProfiles()) {
if (curProfile->getCompatibilityScore(devices,
@@ -1169,19 +1171,21 @@
return curProfile;
}
- // when searching for direct outputs, if several profiles are compatible, give priority
- // to one with offload capability
- if (profile != 0 &&
- ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0)) {
- continue;
- }
profile = curProfile;
- if ((profile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
- break;
+ if ((flags == AUDIO_OUTPUT_FLAG_DIRECT) &&
+ curProfile->getFlags() == AUDIO_OUTPUT_FLAG_DIRECT) {
+ directOnlyProfile = curProfile;
+ }
+
+ if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+ compressOffloadProfile = curProfile;
}
}
}
- return profile;
+
+ return directOnlyProfile ? directOnlyProfile
+ : (compressOffloadProfile ? compressOffloadProfile : profile);
+
}
sp<IOProfile> AudioPolicyManager::getSpatializerOutputProfile(
diff --git a/services/audiopolicy/tests/resources/test_phone_apm_configuration.xml b/services/audiopolicy/tests/resources/test_phone_apm_configuration.xml
index efe1400..98299e6 100644
--- a/services/audiopolicy/tests/resources/test_phone_apm_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_phone_apm_configuration.xml
@@ -43,6 +43,8 @@
</mixPort>
<mixPort name="compressed_offload" role="source"
flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD">
+ <profile name="" format="AUDIO_FORMAT_PCM_FLOAT"
+ samplingRates="48000 96000 384000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<profile name="" format="AUDIO_FORMAT_MP3"
samplingRates="8000 16000 24000 32000 44100 48000 96000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO AUDIO_CHANNEL_OUT_MONO"/>
diff --git a/services/camera/virtualcamera/Android.bp b/services/camera/virtualcamera/Android.bp
index a39229a..c76bb1b 100644
--- a/services/camera/virtualcamera/Android.bp
+++ b/services/camera/virtualcamera/Android.bp
@@ -1,5 +1,5 @@
package {
- default_team: "trendy_team_xr_framework",
+ default_team: "trendy_team_virtual_device_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/camera/virtualcamera/aidl/Android.bp b/services/camera/virtualcamera/aidl/Android.bp
index b3fe3ad..b3c0bce 100644
--- a/services/camera/virtualcamera/aidl/Android.bp
+++ b/services/camera/virtualcamera/aidl/Android.bp
@@ -1,5 +1,5 @@
package {
- default_team: "trendy_team_xr_framework",
+ default_team: "trendy_team_virtual_device_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/camera/virtualcamera/flags/Android.bp b/services/camera/virtualcamera/flags/Android.bp
index 5fa8852..fc72e22 100644
--- a/services/camera/virtualcamera/flags/Android.bp
+++ b/services/camera/virtualcamera/flags/Android.bp
@@ -1,5 +1,5 @@
package {
- default_team: "trendy_team_xr_framework",
+ default_team: "trendy_team_virtual_device_framework",
}
soong_config_module_type {
diff --git a/services/camera/virtualcamera/fuzzer/Android.bp b/services/camera/virtualcamera/fuzzer/Android.bp
index 6a72167..6b8d9cb 100644
--- a/services/camera/virtualcamera/fuzzer/Android.bp
+++ b/services/camera/virtualcamera/fuzzer/Android.bp
@@ -16,7 +16,7 @@
*
*****************************************************************************/
package {
- default_team: "trendy_team_xr_framework",
+ default_team: "trendy_team_virtual_device_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/camera/virtualcamera/tests/Android.bp b/services/camera/virtualcamera/tests/Android.bp
index 543cc10..f67f2b3 100644
--- a/services/camera/virtualcamera/tests/Android.bp
+++ b/services/camera/virtualcamera/tests/Android.bp
@@ -1,5 +1,5 @@
package {
- default_team: "trendy_team_xr_framework",
+ default_team: "trendy_team_virtual_device_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}