Merge changes from topic "inputsurfaceconnection" into main * changes: InputSurface: implement InputSurfaceConnection InputSurface: add FrameQueueThread InputSurface: input buffer work status config update
diff --git a/media/codec2/hal/aidl/Android.bp b/media/codec2/hal/aidl/Android.bp index c85df825..b2db705 100644 --- a/media/codec2/hal/aidl/Android.bp +++ b/media/codec2/hal/aidl/Android.bp
@@ -162,6 +162,7 @@ "InputBufferManager.cpp", "ParamTypes.cpp", "inputsurface/FrameDropper.cpp", + "inputsurface/FrameQueueThread.cpp", "inputsurface/InputSurface.cpp", "inputsurface/InputSurfaceConnection.cpp", "inputsurface/InputSurfaceSource.cpp", @@ -186,6 +187,7 @@ "liblog", "libnativewindow", "libmediandk", + "libsfplugin_ccodec_utils", "libstagefright_aidl_bufferpool2", "libstagefright_bufferpool@2.0.1", "libstagefright_foundation",
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/FrameQueueThread.h b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/FrameQueueThread.h new file mode 100644 index 0000000..d8af97b --- /dev/null +++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/FrameQueueThread.h
@@ -0,0 +1,86 @@ +/* + * + * 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 <condition_variable> +#include <deque> +#include <thread> + +#include <aidl/android/hardware/media/c2/BnInputSink.h> +#include <utils/Timers.h> + +#include <C2Config.h> +#include <C2Work.h> + +namespace aidl::android::hardware::media::c2::implementation { + +/** + * This class runs a thread which receives encoder frames and queues them + * to an encoder component. Frames queued in a specific short duration can be + * batched for queueing to an encoder component. + */ +class FrameQueueThread { +public: + FrameQueueThread(const std::shared_ptr<IInputSink> &sink); + + ~FrameQueueThread(); + + /** + * Queue a frame for an encoder. + */ + void queue(std::unique_ptr<C2Work> &&work, int fenceFd); + + /** + * Set/update a dataspace for upcoming frames. + */ + void setDataspace(android_dataspace dataspace); + + /** + * Set/update thread priority for the frame queueing thread. + */ + void setPriority(int priority); + +private: + bool mDone = false; + std::thread mThread; + std::weak_ptr<IInputSink> mSink; + + std::mutex mLock; + std::condition_variable mCv; + struct Item { + Item(std::unique_ptr<C2Work> &&w, int fd) : work(std::move(w)), fenceFd(fd) {} + + void updateConfig(std::deque<std::unique_ptr<C2Param>> &newConfig) { + configUpdate = std::move(newConfig); + } + + std::unique_ptr<C2Work> work; + int fenceFd; + std::deque<std::unique_ptr<C2Param>> configUpdate; + }; + std::deque<Item> mItems; + std::deque<std::unique_ptr<C2Param>> mConfigUpdate; + nsecs_t mLastQueuedTimestampNs = 0; + +private: + void run(); + + void queueItems(std::deque<Item> &items); +}; + +} // namespace aidl::android::hardware::media::c2::implementation
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 8e15778..47063b9 100644 --- a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h +++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurface.h
@@ -95,7 +95,7 @@ // // Config for current work status w.r.t input buffers struct WorkStatusConfig { - int32_t mLastDoneIndex = -1; // Last work done input buffer index + uint64_t mLastDoneIndex = UINT64_MAX; // Last work done buffer frame index uint32_t mLastDoneCount = 0; // # of work done count uint64_t mEmptyCount = 0; // # of input buffers being emptied };
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 7a57f18..61fa83f 100644 --- a/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h +++ b/media/codec2/hal/aidl/include/codec2/aidl/inputsurface/InputSurfaceConnection.h
@@ -23,12 +23,17 @@ #include <C2.h> +#include <list> +#include <map> #include <memory> namespace aidl::android::hardware::media::c2::implementation { class InputSurfaceSource; +class FrameQueueThread; } +class C2Allocator; + namespace aidl::android::hardware::media::c2::utils { struct InputSurfaceConnection : public BnInputSurfaceConnection { @@ -57,12 +62,55 @@ void dispatchDataSpaceChanged( int32_t dataSpace, int32_t aspects, int32_t pixelFormat); + void release(); + + // InputSurface config + void setAdjustTimestampGapUs(int32_t gapUs); + + void onInputBufferDone(c2_cntr64_t index); + + void onInputBufferEmptied(); + protected: virtual ~InputSurfaceConnection() override; private: + c2_status_t mInit; + std::atomic<bool> mReleased; + std::weak_ptr<IInputSink> mSink; - ::android::sp<c2::implementation::InputSurfaceSource> mSource; + ::android::wp<c2::implementation::InputSurfaceSource> mSource; + std::shared_ptr<c2::implementation::FrameQueueThread> mQueueThread; + + std::atomic_uint64_t mFrameIndex; + + // WORKAROUND: timestamp adjustment + + // if >0: this is the max timestamp gap, if <0: this is -1 times the fixed timestamp gap + // if 0: no timestamp adjustment is made + // note that C2OMXNode can be recycled between encoding sessions. + int32_t mAdjustTimestampGapUs; + bool mFirstInputFrame; // true for first input + c2_cntr64_t mPrevInputTimestamp; // input timestamp for previous frame + c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame + + // Tracks the status of buffers + struct BuffersTracker { + BuffersTracker() = default; + + // For synchronization of data accesses and/or modifications. + std::mutex mMutex; + // Keeps track of buffers that are used by the component. Maps timestamp -> ID + std::map<uint64_t, uint32_t> mIdsInUse; + // Keeps track of the buffer IDs that are available after being released from the component. + std::list<uint32_t> mAvailableIds; + }; + BuffersTracker mBuffersTracker; + + c2_status_t submitBufferInternal( + int32_t bufferId, const AImage *buffer, int64_t timestamp, int fenceFd, bool eos); + + void notifyInputBufferEmptied(int32_t bufferId); }; } // namespace aidl::android::hardware::media::c2::utils
diff --git a/media/codec2/hal/aidl/inputsurface/FrameQueueThread.cpp b/media/codec2/hal/aidl/inputsurface/FrameQueueThread.cpp new file mode 100644 index 0000000..b6cb8b4 --- /dev/null +++ b/media/codec2/hal/aidl/inputsurface/FrameQueueThread.cpp
@@ -0,0 +1,137 @@ +/* + * + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Codec2-InputSurface" + +#include <sys/types.h> + +#include <chrono> + +#include <android-base/logging.h> +#include <codec2/aidl/BufferTypes.h> +#include <codec2/aidl/inputsurface/FrameQueueThread.h> +#include <media/stagefright/foundation/ColorUtils.h> +#include <ui/Fence.h> +#include <utils/AndroidThreads.h> + +#include <Codec2Mapper.h> + +namespace aidl::android::hardware::media::c2::implementation { + +FrameQueueThread::FrameQueueThread(const std::shared_ptr<IInputSink> &sink) + : mSink{sink} { + mThread = std::thread(&FrameQueueThread::run, this); +} + +FrameQueueThread::~FrameQueueThread() { + { + std::unique_lock<std::mutex> l(mLock); + mDone = true; + mCv.notify_all(); + } + if (mThread.joinable()) { + mThread.join(); + } +} + +void FrameQueueThread::queue(std::unique_ptr<C2Work> &&work, int fenceFd) { + { + std::unique_lock<std::mutex> l(mLock); + mItems.emplace_back(std::move(work), fenceFd); + if (!mConfigUpdate.empty()) { + mItems.back().updateConfig(mConfigUpdate); + } + } + mCv.notify_all(); +} + +void FrameQueueThread::setDataspace(android_dataspace dataspace) { + std::unique_lock<std::mutex> l(mLock); + ::android::ColorUtils::convertDataSpaceToV0(dataspace); + mConfigUpdate.emplace_back(new C2StreamDataSpaceInfo::input(0u, dataspace)); + int32_t standard; + int32_t transfer; + int32_t range; + ::android::ColorUtils::getColorConfigFromDataSpace(dataspace, &range, &standard, &transfer); + std::unique_ptr<C2StreamColorAspectsInfo::input> colorAspects = + std::make_unique<C2StreamColorAspectsInfo::input>(0u); + if (::android::C2Mapper::map(standard, &colorAspects->primaries, &colorAspects->matrix) + && ::android::C2Mapper::map(transfer, &colorAspects->transfer) + && ::android::C2Mapper::map(range, &colorAspects->range)) { + mConfigUpdate.push_back(std::move(colorAspects)); + } +} + +void FrameQueueThread::setPriority(int priority) { + androidSetThreadPriority(gettid(), priority); +} + +void FrameQueueThread::run() { + constexpr nsecs_t kIntervalNs = nsecs_t(10) * 1000 * 1000; // 10ms + constexpr nsecs_t kWaitNs = kIntervalNs * 2; + + std::unique_lock<std::mutex> lock(mLock); + while (!mDone) { + nsecs_t nowNs = systemTime(); + nsecs_t diffNs = nowNs - mLastQueuedTimestampNs; + if (mItems.empty() || (mLastQueuedTimestampNs != 0 && diffNs < kIntervalNs)) { + mCv.wait_for(lock, std::chrono::nanoseconds(kIntervalNs - diffNs)); + continue; + } + std::deque<Item> items = std::move(mItems); + lock.unlock(); + queueItems(items); + lock.lock(); + mLastQueuedTimestampNs = nowNs; + mCv.wait_for(lock, std::chrono::nanoseconds(kWaitNs)); + } +} + +void FrameQueueThread::queueItems(std::deque<Item> &items) { + std::shared_ptr<IInputSink> sink = mSink.lock(); + if (!sink) { + ALOGE("queueItems: sink is not valid"); + return; + } + + std::list<std::unique_ptr<C2Work>> c2Items; + std::vector<int> fenceFds; + while (!items.empty()) { + c2Items.push_back(std::move(items.front().work)); + fenceFds.push_back(items.front().fenceFd); + for (const std::unique_ptr<C2Param> ¶m: items.front().configUpdate) { + c2Items.back()->input.configUpdate.emplace_back(C2Param::Copy(*param)); + } + items.pop_front(); + } + // TODO: Pass fence if an encoder supports receiving fences + // along with a block. + for (int fenceFd : fenceFds) { + ::android::sp<::android::Fence> fence(new ::android::Fence(fenceFd)); + fence->waitForever(LOG_TAG); + } + + WorkBundle workBundle; + if (!utils::ToAidl(&workBundle, c2Items, nullptr)) { + ALOGE("queueItems: conversion from C2Work to workBundle failed"); + return; + } + sink->queue(workBundle); +} + +} // namespace aidl::android::hardware::media::c2::implementation
diff --git a/media/codec2/hal/aidl/inputsurface/InputSurface.cpp b/media/codec2/hal/aidl/inputsurface/InputSurface.cpp index ce694ee..eeb1f0b 100644 --- a/media/codec2/hal/aidl/inputsurface/InputSurface.cpp +++ b/media/codec2/hal/aidl/inputsurface/InputSurface.cpp
@@ -169,8 +169,8 @@ .build()); addParameter( - DefineParam(mInputDone, C2_PARAMKEY_LAYER_INDEX) - .withDefault(new C2StreamLayerIndexInfo::output(0u, UINT32_MAX)) + DefineParam(mInputDone, C2_PARAMKEY_OUTPUT_COUNTER) + .withDefault(new C2PortConfigCounterTuning::output(UINT64_MAX)) .withFields({C2F(mInputDone, value).any()}) .withSetter(BasicSetter<decltype(mInputDone)::element_type>) .build()); @@ -178,13 +178,13 @@ DefineParam(mInputDoneCount, C2_PARAMKEY_LAYER_INDEX) .withDefault(new C2StreamLayerCountInfo::input(0u, 0)) .withFields({C2F(mInputDoneCount, value).any()}) - .withSetter(InputDoneCountSetter) + .withSetter(BasicSetter<decltype(mInputDoneCount)::element_type>) .build()); addParameter( DefineParam(mEmptyCount, C2_PARAMKEY_LAYER_COUNT) .withDefault(new C2StreamLayerCountInfo::output(0u, 0)) .withFields({C2F(mEmptyCount, value).any()}) - .withSetter(EmptyCountSetter) + .withSetter(BasicSetter<decltype(mEmptyCount)::element_type>) .build()); } @@ -222,11 +222,7 @@ } void getWorkStatusConfig(WorkStatusConfig* _Nonnull config) { - if (mInputDone->value == UINT32_MAX) { - config->mLastDoneIndex = -1; - } else { - config->mLastDoneIndex = mInputDone->value; - } + config->mLastDoneIndex = mInputDone->value; config->mLastDoneCount = mInputDoneCount->value; config->mEmptyCount = mEmptyCount->value; } @@ -251,20 +247,6 @@ 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; @@ -289,7 +271,7 @@ // current work status configuration // TODO: remove this and move this to onWorkDone() - std::shared_ptr<C2StreamLayerIndexInfo::output> mInputDone; + std::shared_ptr<C2PortConfigCounterTuning::output> mInputDone; std::shared_ptr<C2StreamLayerCountInfo::input> mInputDoneCount; std::shared_ptr<C2StreamLayerCountInfo::output> mEmptyCount; }; @@ -409,6 +391,7 @@ ::ndk::ScopedAStatus InputSurface::connect( const std::shared_ptr<IInputSink>& sink, std::shared_ptr<IInputSurfaceConnection>* connection) { + std::unique_lock<std::mutex> l(mLock); mConnection = SharedRefBase::make<InputSurfaceConnection>(sink, mSource); *connection = mConnection; return ::ndk::ScopedAStatus::ok(); @@ -459,12 +442,12 @@ 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. + if (mConnection) { + mConnection->setAdjustTimestampGapUs(mStreamConfig.mAdjustedGapUs); + } } // TRICKY: we do not unset max fps to 0 unless using fixed fps if ((config.mMaxFps > 0 || (fixedModeUpdate && config.mMaxFps == -1)) @@ -550,8 +533,22 @@ } void InputSurface::updateWorkStatusConfig(WorkStatusConfig &config) { - (void)config; - // TODO + std::unique_lock<std::mutex> l(mLock); + if (!mConnection) { + ALOGE("work status is updated though there is no connection."); + return; + } + if (mWorkStatusConfig.mLastDoneIndex != config.mLastDoneIndex) { + mWorkStatusConfig.mLastDoneIndex = config.mLastDoneIndex; + mConnection->onInputBufferDone(mWorkStatusConfig.mLastDoneIndex); + } + if (mWorkStatusConfig.mLastDoneCount != config.mLastDoneCount) { + mWorkStatusConfig.mLastDoneCount = config.mLastDoneCount; + } + if (mWorkStatusConfig.mEmptyCount != config.mEmptyCount) { + mWorkStatusConfig.mEmptyCount = config.mEmptyCount; + mConnection->onInputBufferEmptied(); + } } bool InputSurface::updateConfig(
diff --git a/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp b/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp index 6a95472..1250531 100644 --- a/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp +++ b/media/codec2/hal/aidl/inputsurface/InputSurfaceConnection.cpp
@@ -16,54 +16,235 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Codec2-InputSurface" -#include <android-base/logging.h> +#include <android_media_codec.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> + +#include <codec2/aidl/inputsurface/FrameQueueThread.h> #include <codec2/aidl/inputsurface/InputSurfaceConnection.h> #include <codec2/aidl/inputsurface/InputSurfaceSource.h> +#include <C2AllocatorGralloc.h> +#include <C2BlockInternal.h> + namespace aidl::android::hardware::media::c2::utils { InputSurfaceConnection::InputSurfaceConnection( const std::shared_ptr<IInputSink>& sink, ::android::sp<c2::implementation::InputSurfaceSource> const &source) - : mSink{sink}, mSource{source} { + : mSink{sink}, mSource{source}, + mQueueThread{std::make_shared<implementation::FrameQueueThread>(sink)}, mFrameIndex(0), + mAdjustTimestampGapUs(0), mFirstInputFrame(true) { + auto component = mSink.lock(); + if (!component) { + mInit = C2_NO_INIT; + return; + } + mInit = C2_OK; } InputSurfaceConnection::~InputSurfaceConnection() { } c2_status_t InputSurfaceConnection::status() const { - // TODO; - return C2_OK; + return mInit; } ::ndk::ScopedAStatus InputSurfaceConnection::disconnect() { + auto source = mSource.promote(); + if (!source) { + return ::ndk::ScopedAStatus::fromServiceSpecificError(C2_CORRUPTED); + } + (void)source->stop(); + (void)source->release(); + return ::ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus InputSurfaceConnection::signalEndOfStream() { + auto source = mSource.promote(); + if (!source) { + return ::ndk::ScopedAStatus::fromServiceSpecificError(C2_CORRUPTED); + } + + c2_status_t status = source->signalEndOfInputStream(); + if (status != C2_OK) { + return ::ndk::ScopedAStatus::fromServiceSpecificError(status); + } return ::ndk::ScopedAStatus::ok(); } c2_status_t InputSurfaceConnection::submitBuffer( int32_t bufferId, const AImage *buffer, int64_t timestamp, int fenceFd) { - (void)bufferId; - (void)buffer; - (void)timestamp; - (void)fenceFd; - return C2_OK; + return submitBufferInternal(bufferId, buffer, timestamp, fenceFd, false); } c2_status_t InputSurfaceConnection::submitEos(int32_t bufferId) { - (void)bufferId; - return C2_OK; + return submitBufferInternal(bufferId, nullptr, 0, -1, true); } void InputSurfaceConnection::dispatchDataSpaceChanged( int32_t dataSpace, int32_t aspects, int32_t pixelFormat) { - (void)dataSpace; (void)aspects; (void)pixelFormat; + android_dataspace d = (android_dataspace)dataSpace; + mQueueThread->setDataspace(d); +} + +void InputSurfaceConnection::setAdjustTimestampGapUs(int32_t gapUs) { + mAdjustTimestampGapUs = gapUs; +} + + +void InputSurfaceConnection::onInputBufferDone(c2_cntr64_t index) { + if (::android::media::codec::provider_->input_surface_throttle()) { + std::unique_lock<std::mutex> l(mBuffersTracker.mMutex); + auto it = mBuffersTracker.mIdsInUse.find(index.peeku()); + if (it == mBuffersTracker.mIdsInUse.end()) { + ALOGV("Untracked input index %llu (maybe already removed)", index.peekull()); + return; + } + int32_t bufferId = it->second; + (void)mBuffersTracker.mIdsInUse.erase(it); + mBuffersTracker.mAvailableIds.push_back(bufferId); + } else { + { + auto source = mSource.promote(); + if (!source) { + return; + } + } + int32_t bufferId = 0; + { + std::unique_lock<std::mutex> l(mBuffersTracker.mMutex); + auto it = mBuffersTracker.mIdsInUse.find(index.peeku()); + if (it == mBuffersTracker.mIdsInUse.end()) { + ALOGV("Untracked input index %llu (maybe already removed)", index.peekull()); + return; + } + bufferId = it->second; + (void)mBuffersTracker.mIdsInUse.erase(it); + } + notifyInputBufferEmptied(bufferId); + } +} + +void InputSurfaceConnection::onInputBufferEmptied() { + if (!::android::media::codec::provider_->input_surface_throttle()) { + ALOGE("onInputBufferEmptied should not be called " + "when input_surface_throttle is false"); + return; + } + { + auto source = mSource.promote(); + if (!source) { + return; + } + } + int32_t bufferId = 0; + { + std::unique_lock<std::mutex> l(mBuffersTracker.mMutex); + if (mBuffersTracker.mAvailableIds.empty()) { + ALOGV("The codec is ready to take more input buffers " + "but no input buffers are ready yet."); + return; + } + bufferId = mBuffersTracker.mAvailableIds.front(); + mBuffersTracker.mAvailableIds.pop_front(); + } +} + +c2_status_t InputSurfaceConnection::submitBufferInternal( + int32_t bufferId, const AImage *buffer, int64_t timestamp, int fenceFd, bool eos) { + // close fenceFd on returning an error. + ::android::base::unique_fd ufd(fenceFd); + std::shared_ptr<IInputSink> sink = mSink.lock(); + if (!sink) { + ALOGE("inputsurface does not have valid sink"); + return C2_BAD_STATE; + } + + uint32_t c2Flags = (eos == true) ? C2FrameData::FLAG_END_OF_STREAM : 0; + AHardwareBuffer *hwBuffer = nullptr; + + if (buffer) { + if (AImage_getHardwareBuffer(buffer, &hwBuffer) != AMEDIA_OK) { + ALOGE("cannot get AHardwareBuffer form AImage"); + return C2_CORRUPTED; + } + } else if (!eos) { + ALOGE("buffer should be submitted, but was nullptr"); + return C2_BAD_VALUE; + } + + std::shared_ptr<C2GraphicBlock> block; + if (hwBuffer) { + block = _C2BlockFactory::CreateGraphicBlock(hwBuffer); + } + + std::unique_ptr<C2Work> work(new C2Work); + work->input.flags = (C2FrameData::flags_t)c2Flags; + work->input.ordinal.timestamp = timestamp; + { + work->input.ordinal.customOrdinal = timestamp; // save input timestamp + if (mFirstInputFrame) { + // grab timestamps on first frame + mPrevInputTimestamp = timestamp; + mPrevCodecTimestamp = timestamp; + mFirstInputFrame = false; + } else if (mAdjustTimestampGapUs > 0) { + work->input.ordinal.timestamp = + mPrevCodecTimestamp + + c2_min((timestamp - mPrevInputTimestamp).peek(), mAdjustTimestampGapUs); + } else if (mAdjustTimestampGapUs < 0) { + work->input.ordinal.timestamp = mPrevCodecTimestamp - mAdjustTimestampGapUs; + } + mPrevInputTimestamp = work->input.ordinal.customOrdinal; + mPrevCodecTimestamp = work->input.ordinal.timestamp; + ALOGV("adjusting %lld to %lld (gap=%lld)", + work->input.ordinal.customOrdinal.peekll(), + work->input.ordinal.timestamp.peekll(), + (long long)mAdjustTimestampGapUs); + } + + work->input.ordinal.frameIndex = mFrameIndex++; + work->input.buffers.clear(); + if (block) { + std::shared_ptr<C2Buffer> c2Buffer( + C2Buffer::CreateGraphicBuffer(block->share( + C2Rect(block->width(), block->height()), ::C2Fence()))); + work->input.buffers.push_back(c2Buffer); + std::shared_ptr<C2StreamHdrStaticInfo::input> staticInfo; + std::shared_ptr<C2StreamHdrDynamicMetadataInfo::input> dynamicInfo; + ::android::GetHdrMetadataFromGralloc4Handle( + block->handle(), + &staticInfo, + &dynamicInfo); + if (staticInfo && *staticInfo) { + c2Buffer->setInfo(staticInfo); + } + if (dynamicInfo && *dynamicInfo) { + c2Buffer->setInfo(dynamicInfo); + } + } + work->worklets.clear(); + work->worklets.emplace_back(new C2Worklet); + { + std::unique_lock<std::mutex> l(mBuffersTracker.mMutex); + mBuffersTracker.mIdsInUse.emplace(work->input.ordinal.frameIndex.peeku(), bufferId); + } + mQueueThread->queue(std::move(work), ufd.release()); + + return C2_OK; +} + +void InputSurfaceConnection::notifyInputBufferEmptied(int32_t bufferId) { + auto source = mSource.promote(); + if (!source) { + return; + } + source->onInputBufferEmptied(bufferId, -1); } } // namespace aidl::android::hardware::media::c2::utils