Merge "camera: Clarify hot pixel map coordinate system when sensor pixel mode is MAXIMUM_RESOLUTION" into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 34ff367..e9b757b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -30,10 +30,8 @@
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
- camera/
media/libaudioclient/tests/
media/libaudiohal/tests/
media/libmediatranscoding/
services/camera/virtualcamera/
services/mediatranscoding/
- services/camera/libcameraservice/
diff --git a/cmds/stagefright/AudioPlayer.cpp b/cmds/stagefright/AudioPlayer.cpp
index 6cddf47..54885ef 100644
--- a/cmds/stagefright/AudioPlayer.cpp
+++ b/cmds/stagefright/AudioPlayer.cpp
@@ -101,6 +101,10 @@
CHECK(mFirstBuffer == NULL);
+ if (!mAudioPlayerWrapper) {
+ mAudioPlayerWrapper = sp<MediaPlayerBase::WeakWrapper<AudioPlayer>>::make(this);
+ }
+
MediaSource::ReadOptions options;
if (mSeeking) {
options.setSeekTo(mSeekTimeUs);
@@ -203,7 +207,7 @@
mSampleRate, numChannels, channelMask, audioFormat,
DEFAULT_AUDIOSINK_BUFFERCOUNT,
&AudioPlayer::AudioSinkCallback,
- this,
+ mAudioPlayerWrapper,
(audio_output_flags_t)flags,
useOffload() ? &offloadInfo : NULL);
@@ -430,10 +434,11 @@
// static
size_t AudioPlayer::AudioSinkCallback(
- MediaPlayerBase::AudioSink * /* audioSink */,
- void *buffer, size_t size, void *cookie,
+ const sp<MediaPlayerBase::AudioSink>& /* audioSink */,
+ void *buffer, size_t size, const wp<RefBase>& cookie,
MediaPlayerBase::AudioSink::cb_event_t event) {
- AudioPlayer *me = (AudioPlayer *)cookie;
+ const auto me = MediaPlayerBase::WeakWrapper<AudioPlayer>::promoteFromRefBase(cookie);
+ if (!me) return 0;
switch(event) {
case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
diff --git a/cmds/stagefright/AudioPlayer.h b/cmds/stagefright/AudioPlayer.h
index 608f54b..877ac13 100644
--- a/cmds/stagefright/AudioPlayer.h
+++ b/cmds/stagefright/AudioPlayer.h
@@ -29,7 +29,7 @@
struct AwesomePlayer;
-class AudioPlayer : AudioTrack::IAudioTrackCallback {
+class AudioPlayer : public AudioTrack::IAudioTrackCallback {
public:
enum {
REACHED_EOS,
@@ -97,14 +97,15 @@
MediaBufferBase *mFirstBuffer;
sp<MediaPlayerBase::AudioSink> mAudioSink;
+ sp<MediaPlayerBase::WeakWrapper<AudioPlayer>> mAudioPlayerWrapper;
bool mPlaying;
int64_t mStartPosUs;
const uint32_t mCreateFlags;
static size_t AudioSinkCallback(
- MediaPlayerBase::AudioSink *audioSink,
- void *data, size_t size, void *me,
+ const sp<MediaPlayerBase::AudioSink>& audioSink,
+ void *data, size_t size, const wp<RefBase>& me,
MediaPlayerBase::AudioSink::cb_event_t event);
size_t fillBuffer(void *data, size_t size);
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index ed1522b..84c0f48 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -103,6 +103,14 @@
}
flag {
+ name: "p210_format_support"
+ is_exported: true
+ namespace: "codec_fwk"
+ description: "Feature flag for Android support for P210 YCbCr format"
+ bug: "368395888"
+}
+
+flag {
name: "region_of_interest"
is_exported: true
namespace: "codec_fwk"
@@ -118,6 +126,13 @@
}
flag {
+ name: "rendering_depth_removal"
+ namespace: "codec_fwk"
+ description: "Feature flag for removing rendering depth"
+ bug: "275527219"
+}
+
+flag {
name: "secure_codecs_require_crypto"
namespace: "codec_fwk"
description: "Bugfix flag for requiring setting crypto for secure codecs"
diff --git a/media/audio/aconfig/Android.bp b/media/audio/aconfig/Android.bp
index a5aeff2..47418cf 100644
--- a/media/audio/aconfig/Android.bp
+++ b/media/audio/aconfig/Android.bp
@@ -172,6 +172,12 @@
}
cc_aconfig_library {
+ name: "android.media.audio-aconfig-cc",
+ aconfig_declarations: "android.media.audio-aconfig",
+ defaults: ["audio-aconfig-cc-defaults"],
+}
+
+cc_aconfig_library {
name: "android.media.audiopolicy-aconfig-cc",
aconfig_declarations: "android.media.audiopolicy-aconfig",
defaults: ["audio-aconfig-cc-defaults"],
diff --git a/media/audio/aconfig/audio_framework.aconfig b/media/audio/aconfig/audio_framework.aconfig
index 411996e..0b434f7 100644
--- a/media/audio/aconfig/audio_framework.aconfig
+++ b/media/audio/aconfig/audio_framework.aconfig
@@ -1,5 +1,6 @@
# Top level framework (android.media) flags
# Only add flags here which must be included in framework.jar
+# Flags used in both framework.jar and native can be added here
#
# Please add flags in alphabetical order.
@@ -22,6 +23,15 @@
bug: "302323921"
}
+flag {
+ name: "concurrent_audio_record_bypass_permission"
+ namespace: "media_audio"
+ description:
+ "New privileged permission to allow bypassing concurrent audio"
+ "capture rules."
+ bug: "374751406"
+}
+
flag{
name: "enable_ringtone_haptics_customization"
namespace: "media_audio"
@@ -144,6 +154,14 @@
is_fixed_read_only: true
}
+flag {
+ name: "routed_device_ids"
+ namespace: "media_audio"
+ description:
+ "Enable Java and native functions to get"
+ "multiple routed device ids"
+ bug: "367816690"
+}
# TODO remove
flag {
diff --git a/media/codec2/components/aom/C2SoftAomEnc.cpp b/media/codec2/components/aom/C2SoftAomEnc.cpp
index 722b13a..93009c4 100644
--- a/media/codec2/components/aom/C2SoftAomEnc.cpp
+++ b/media/codec2/components/aom/C2SoftAomEnc.cpp
@@ -466,6 +466,7 @@
aom_codec_err_t C2SoftAomEnc::setupCodecParameters() {
aom_codec_err_t codec_return = AOM_CODEC_OK;
+ const int maxIntraBitratePct = mBitrateControlMode == AOM_CBR ? 300 : 450;
codec_return = aom_codec_control(mCodecContext, AV1E_SET_TARGET_SEQ_LEVEL_IDX, mAV1EncLevel);
if (codec_return != AOM_CODEC_OK) goto BailOut;
@@ -492,6 +493,10 @@
codec_return = aom_codec_control(mCodecContext, AV1E_SET_AQ_MODE, 3);
if (codec_return != AOM_CODEC_OK) goto BailOut;
+ codec_return = aom_codec_control(mCodecContext, AOME_SET_MAX_INTRA_BITRATE_PCT,
+ maxIntraBitratePct);
+ if (codec_return != AOM_CODEC_OK) goto BailOut;
+
codec_return = aom_codec_control(mCodecContext, AV1E_SET_COEFF_COST_UPD_FREQ, 3);
if (codec_return != AOM_CODEC_OK) goto BailOut;
diff --git a/media/codec2/hal/common/include/codec2/common/BqPoolInvalidateHelper.h b/media/codec2/hal/common/include/codec2/common/BqPoolInvalidateHelper.h
new file mode 100644
index 0000000..859f703
--- /dev/null
+++ b/media/codec2/hal/common/include/codec2/common/BqPoolInvalidateHelper.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 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 <C2BqBufferPriv.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+// filter fn from component's blockpool container to bqpool conatainer
+static inline bool BqPoolFilterFn(
+ std::pair<const uint64_t, std::shared_ptr<C2BlockPool>> pool) {
+ return (pool.second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE);
+}
+
+// convert fn from component's blockpool container to bqpool container
+static inline std::shared_ptr<C2BufferQueueBlockPool> BqPoolConvertFn(
+ std::pair<const uint64_t, std::shared_ptr<C2BlockPool>> pool) {
+ return std::static_pointer_cast<C2BufferQueueBlockPool>(pool.second);
+}
+
+// This is similar to std::transform excpet there is \pred functor parameter.
+// The elements with \pred function value \true only will be transformed and
+// added to the dest container. (For portability std::ranges are not used.)
+template <class InputIt, class OutputIt, class Pred, class Fct>
+void transform_if(InputIt first, InputIt last, OutputIt dest, Pred pred, Fct transform)
+{
+ while (first != last) {
+ if (pred(*first)) {
+ *dest++ = transform(*first);
+ }
+ ++first;
+ }
+}
+
+} // namespace android
diff --git a/media/codec2/hal/hidl/1.0/utils/Component.cpp b/media/codec2/hal/hidl/1.0/utils/Component.cpp
index 62f0e25..162a80e 100644
--- a/media/codec2/hal/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/Component.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "Codec2-Component"
#include <android-base/logging.h>
+#include <codec2/common/BqPoolInvalidateHelper.h>
#include <codec2/hidl/1.0/Component.h>
#include <codec2/hidl/1.0/ComponentStore.h>
#include <codec2/hidl/1.0/InputBufferManager.h>
@@ -30,6 +31,7 @@
#include <utils/Timers.h>
#include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -270,16 +272,17 @@
}
void Component::onDeathReceived() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mClientDied = true;
- for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
- if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
- std::shared_ptr<C2BufferQueueBlockPool> bqPool =
- std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
- bqPool->invalidate();
- }
- }
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ if (!bqPools.empty()) {
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+ std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ bqInvalidateItem->invalidate();
}
release();
}
@@ -549,7 +552,26 @@
}
Return<Status> Component::release() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ if (!mClientDied) {
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ }
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem;
+ if (!bqPools.empty()) {
+ // handling rare cases of process death just after release() called.
+ bqInvalidateItem = std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+ }
Status status = static_cast<Status>(mComponent->release());
+ if (bqInvalidateItem) {
+ // If release is not blocked,
+ // skip invalidation and finish ASAP.
+ bqInvalidateItem->skip();
+ }
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
@@ -637,6 +659,18 @@
}
Component::~Component() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ if (!bqPools.empty()) {
+ LOG(ERROR) << "blockpools are not cleared yet at dtor";
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+ std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+ }
InputBufferManager::unregisterFrameData(mListener);
mStore->reportComponentDeath(this);
}
diff --git a/media/codec2/hal/hidl/1.1/utils/Component.cpp b/media/codec2/hal/hidl/1.1/utils/Component.cpp
index 7f2c4dd..1c2a49a 100644
--- a/media/codec2/hal/hidl/1.1/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.1/utils/Component.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "Codec2-Component@1.1"
#include <android-base/logging.h>
+#include <codec2/common/BqPoolInvalidateHelper.h>
#include <codec2/hidl/1.1/Component.h>
#include <codec2/hidl/1.1/ComponentStore.h>
#include <codec2/hidl/1.1/InputBufferManager.h>
@@ -32,6 +33,7 @@
#include <codec2/common/MultiAccessUnitHelper.h>
#include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -274,16 +276,17 @@
}
void Component::onDeathReceived() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mClientDied = true;
- for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
- if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
- std::shared_ptr<C2BufferQueueBlockPool> bqPool =
- std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
- bqPool->invalidate();
- }
- }
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ if (!bqPools.empty()) {
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+ std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ bqInvalidateItem->invalidate();
}
release();
}
@@ -555,7 +558,26 @@
}
Return<Status> Component::release() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ if (!mClientDied) {
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ }
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem;
+ if (!bqPools.empty()) {
+ // handling rare cases of process death just after release() called.
+ bqInvalidateItem = std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+ }
Status status = static_cast<Status>(mComponent->release());
+ if (bqInvalidateItem) {
+ // If release is not blocked,
+ // skip invalidation and finish ASAP.
+ bqInvalidateItem->skip();
+ }
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
@@ -649,6 +671,18 @@
}
Component::~Component() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ if (!bqPools.empty()) {
+ LOG(ERROR) << "blockpools are not cleared yet at dtor";
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+ std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+ }
InputBufferManager::unregisterFrameData(mListener);
mStore->reportComponentDeath(this);
}
diff --git a/media/codec2/hal/hidl/1.2/utils/Component.cpp b/media/codec2/hal/hidl/1.2/utils/Component.cpp
index 7b0aa9b..a15febe 100644
--- a/media/codec2/hal/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.2/utils/Component.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "Codec2-Component@1.2"
#include <android-base/logging.h>
+#include <codec2/common/BqPoolInvalidateHelper.h>
#include <codec2/hidl/1.2/Component.h>
#include <codec2/hidl/1.2/ComponentStore.h>
#include <codec2/hidl/1.2/InputBufferManager.h>
@@ -30,6 +31,7 @@
#include <utils/Timers.h>
#include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -272,16 +274,17 @@
}
void Component::onDeathReceived() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mClientDied = true;
- for (auto it = mBlockPools.begin(); it != mBlockPools.end(); ++it) {
- if (it->second->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
- std::shared_ptr<C2BufferQueueBlockPool> bqPool =
- std::static_pointer_cast<C2BufferQueueBlockPool>(it->second);
- bqPool->invalidate();
- }
- }
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ if (!bqPools.empty()) {
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+ std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ bqInvalidateItem->invalidate();
}
release();
}
@@ -551,7 +554,26 @@
}
Return<Status> Component::release() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ if (!mClientDied) {
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ }
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem;
+ if (!bqPools.empty()) {
+ // handling rare cases of process death just after release() called.
+ bqInvalidateItem = std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+ }
Status status = static_cast<Status>(mComponent->release());
+ if (bqInvalidateItem) {
+ // If release is not blocked,
+ // skip invalidation and finish ASAP.
+ bqInvalidateItem->skip();
+ }
{
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
@@ -676,6 +698,18 @@
}
Component::~Component() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> bqPools;
+ {
+ std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+ transform_if(mBlockPools.begin(), mBlockPools.end(), std::back_inserter(bqPools),
+ BqPoolFilterFn, BqPoolConvertFn);
+ }
+ if (!bqPools.empty()) {
+ LOG(ERROR) << "blockpools are not cleared yet at dtor";
+ std::shared_ptr<C2BqPoolInvalidateItem> bqInvalidateItem =
+ std::make_shared<C2BqPoolInvalidateItem>(std::move(bqPools));
+ C2BqPoolInvalidator::getInstance().queue(bqInvalidateItem);
+ }
InputBufferManager::unregisterFrameData(mListener);
mStore->reportComponentDeath(this);
}
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index 3c8c1b7..cc5d10c 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -16,6 +16,10 @@
cc_library_shared {
name: "libsfplugin_ccodec",
+ defaults: [
+ "android.hardware.graphics.common-ndk_shared",
+ ],
+
export_include_dirs: ["include"],
srcs: [
@@ -46,6 +50,7 @@
],
static_libs: [
+ "libPlatformProperties",
"libSurfaceFlingerProperties",
"aconfig_mediacodec_flags_c_lib",
"android.media.codec-aconfig-cc",
@@ -56,7 +61,6 @@
"android.hardware.drm@1.0",
"android.hardware.media.c2@1.0",
"android.hardware.media.omx@1.0",
- "android.hardware.graphics.common-V5-ndk",
"graphicbuffersource-aidl-ndk",
"libbase",
"libbinder",
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 3ef2f84..5c46d99 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -38,6 +38,7 @@
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/drm/1.0/types.h>
+#include <android/sysprop/MediaProperties.sysprop.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -207,8 +208,18 @@
Mutexed<BlockPools>::Locked pools(mBlockPools);
pools->outputPoolId = C2BlockPool::BASIC_LINEAR;
}
- std::string value = GetServerConfigurableFlag("media_native", "ccodec_rendering_depth", "3");
- android::base::ParseInt(value, &mRenderingDepth);
+ if (android::media::codec::provider_->rendering_depth_removal()) {
+ constexpr int kAndroidApi202404 = 202404;
+ int vendorVersion = ::android::base::GetIntProperty("ro.vendor.api_level", -1);
+ using ::android::sysprop::MediaProperties::codec2_remove_rendering_depth;
+ if (vendorVersion > kAndroidApi202404 || codec2_remove_rendering_depth().value_or(false)) {
+ mRenderingDepth = 0;
+ }
+ } else {
+ std::string value = GetServerConfigurableFlag(
+ "media_native", "ccodec_rendering_depth", "3");
+ android::base::ParseInt(value, &mRenderingDepth);
+ }
mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + mRenderingDepth;
}
@@ -2052,6 +2063,14 @@
status_t CCodecBufferChannel::requestInitialInputBuffers(
std::map<size_t, sp<MediaCodecBuffer>> &&clientInputBuffers) {
+ std::optional<QueueGuard> guard;
+ if (android::media::codec::provider_->codec_buffer_state_cleanup()) {
+ guard.emplace(mSync);
+ if (!guard->isRunning()) {
+ ALOGD("[%s] skip requestInitialInputBuffers when not running", mName);
+ return OK;
+ }
+ }
C2StreamBufferTypeSetting::output oStreamFormat(0u);
C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr);
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index dc06ee6..9d1cbff 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -53,7 +53,7 @@
],
defaults: [
- "aconfig_lib_cc_static_link.defaults",
+ "aconfig_lib_cc_static_link.defaults",
"libcodec2_hal_selection",
],
@@ -68,6 +68,7 @@
"C2PlatformStorePluginLoader.cpp",
"C2Store.cpp",
"platform/C2BqBuffer.cpp",
+ "platform/C2BqPoolInvalidator.cpp",
"platform/C2SurfaceSyncObj.cpp",
"platform/C2IgbaBuffer.cpp",
"types.cpp",
diff --git a/media/codec2/vndk/include/C2BqPoolInvalidator.h b/media/codec2/vndk/include/C2BqPoolInvalidator.h
new file mode 100644
index 0000000..612d023
--- /dev/null
+++ b/media/codec2/vndk/include/C2BqPoolInvalidator.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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 <android-base/no_destructor.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include <condition_variable>
+#include <deque>
+#include <list>
+#include <memory>
+#include <thread>
+
+class C2BufferQueueBlockPool;
+
+namespace android {
+
+/**
+ * Container class in order to invalidate C2BufferQueueBlockPool(s) and their resources
+ * when the client process is dead abruptly.
+ */
+class C2BqPoolInvalidateItem {
+public:
+
+ /**
+ * invalidate contained C2BufferQueueBlockPool(s) and their resources
+ */
+ void invalidate();
+
+ /**
+ * skip invalidate(), if it is scheduled and not yet invalidated.
+ */
+ void skip();
+
+ /**
+ * returns whether invalidate() is reuqired or not.
+ */
+ bool needsInvalidate();
+
+ C2BqPoolInvalidateItem(std::list<std::shared_ptr<C2BufferQueueBlockPool>> &&pools);
+
+ ~C2BqPoolInvalidateItem() = default;
+private:
+
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> mPools;
+ bool mNeedsInvalidate;
+ std::mutex mLock;
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2BqPoolInvalidateItem);
+};
+
+/**
+ * Asynchronous C2BufferQueueBlockPool invalidator.
+ *
+ * this has C2BqPoolInvalidateItem inside. and call invalidate() from a separate
+ * thread asynchronously.
+ */
+class C2BqPoolInvalidator {
+public:
+ /**
+ * This gets the singleton instance of the class.
+ */
+ static C2BqPoolInvalidator &getInstance();
+
+ /**
+ * queue invalidation items. the item will be invalidated after certain
+ * amount of delay from a separate thread.
+ */
+ void queue(std::shared_ptr<C2BqPoolInvalidateItem> &item);
+
+ ~C2BqPoolInvalidator();
+private:
+
+ C2BqPoolInvalidator();
+
+ void run();
+
+ std::thread mThread;
+ bool mDone;
+
+ std::mutex mMutex;
+ std::condition_variable mCv;
+
+ std::deque<std::pair<int64_t, std::shared_ptr<C2BqPoolInvalidateItem>>> mItems;
+
+ friend class ::android::base::NoDestructor<C2BqPoolInvalidator>;
+
+ DISALLOW_EVIL_CONSTRUCTORS(C2BqPoolInvalidator);
+};
+
+} // namespace android
diff --git a/media/codec2/vndk/platform/C2BqPoolInvalidator.cpp b/media/codec2/vndk/platform/C2BqPoolInvalidator.cpp
new file mode 100644
index 0000000..2666cd3
--- /dev/null
+++ b/media/codec2/vndk/platform/C2BqPoolInvalidator.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 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 "C2BqPoolInvalidator"
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <C2BqBufferPriv.h>
+#include <C2BqPoolInvalidator.h>
+
+namespace android {
+
+namespace {
+ static constexpr int64_t kBqPoolInvalidateDelayMs = 1000;
+} // anonymous namespace
+
+C2BqPoolInvalidateItem::C2BqPoolInvalidateItem(
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> &&pools) : mPools(std::move(pools)) {
+ if (!mPools.empty()) {
+ mNeedsInvalidate = true;
+ } else {
+ mNeedsInvalidate = false;
+ }
+}
+
+void C2BqPoolInvalidateItem::invalidate() {
+ std::list<std::shared_ptr<C2BufferQueueBlockPool>> pools;
+ {
+ std::unique_lock<std::mutex> l(mLock);
+ if (!mNeedsInvalidate) {
+ return;
+ }
+ pools = std::move(mPools);
+ mNeedsInvalidate = false;
+ }
+ for(auto it = pools.begin(); it != pools.end(); ++it) {
+ (*it)->invalidate();
+ }
+}
+
+void C2BqPoolInvalidateItem::skip() {
+ std::unique_lock<std::mutex> l(mLock);
+ mNeedsInvalidate = false;
+ mPools.clear();
+}
+
+bool C2BqPoolInvalidateItem::needsInvalidate() {
+ std::unique_lock<std::mutex> l(mLock);
+ return mNeedsInvalidate;
+}
+
+C2BqPoolInvalidator &C2BqPoolInvalidator::getInstance() {
+ static android::base::NoDestructor<C2BqPoolInvalidator> sInvalidator;
+ return *sInvalidator;
+}
+
+C2BqPoolInvalidator::C2BqPoolInvalidator() : mDone(false) {
+ mThread = std::thread(&C2BqPoolInvalidator::run, this);
+}
+
+C2BqPoolInvalidator::~C2BqPoolInvalidator() {
+ {
+ std::unique_lock<std::mutex> l(mMutex);
+ mDone = true;
+ mCv.notify_one();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void C2BqPoolInvalidator::queue(std::shared_ptr<C2BqPoolInvalidateItem> &item) {
+ std::unique_lock<std::mutex> l(mMutex);
+ std::pair<int64_t, std::shared_ptr<C2BqPoolInvalidateItem>> p =
+ std::make_pair(::android::elapsedRealtime() + kBqPoolInvalidateDelayMs, item);
+ mItems.push_back(p);
+ mCv.notify_one();
+}
+
+void C2BqPoolInvalidator::run() {
+ while(true) {
+ int64_t nowMs = ::android::elapsedRealtime();
+ std::unique_lock<std::mutex> l(mMutex);
+ if (mDone) {
+ break;
+ }
+ std::list<std::shared_ptr<C2BqPoolInvalidateItem>> items;
+ while (!mItems.empty()) {
+ if (mItems.front().first <= nowMs) {
+ items.push_back(mItems.front().second);
+ mItems.pop_front();
+ } else {
+ break;
+ }
+ }
+ if (items.empty()) {
+ if (mItems.empty()) {
+ mCv.wait(l);
+ } else {
+ int64_t nextMs = mItems.front().first;
+ if (nextMs > nowMs) {
+ mCv.wait_for(l, std::chrono::milliseconds(nextMs - nowMs));
+ }
+ }
+ } else {
+ l.unlock();
+ int invalidated = 0;
+ for (auto it = items.begin(); it != items.end(); ++it, ++invalidated) {
+ (*it)->invalidate();
+ }
+ ALOGD("invalidated %d bqpool items", invalidated);
+ }
+ }
+}
+
+} // android
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index a1551f8..f4637e3 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -66,6 +66,7 @@
"libmedia_helper",
"libmediametrics",
"libprocessgroup",
+ "libprocessgroup_util",
"mediametricsservice-aidl-cpp",
"shared-file-region-aidl-cpp",
],
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 5b954f7..b193950 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1159,11 +1159,10 @@
// start of lock scope
AutoMutex lock(mLock);
- uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
- if (newSequence == oldSequence) {
+ if (mSequence == oldSequence) {
if (!audio_is_linear_pcm(mFormat)) {
// If compressed capture, don't attempt to restore the track.
// Return a DEAD_OBJECT error and let the caller recreate.
@@ -1179,7 +1178,7 @@
}
}
}
- oldSequence = newSequence;
+ oldSequence = mSequence;
// Keep the extra references
proxy = mProxy;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index d7c0b5b..e0c5e92 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -2221,11 +2221,10 @@
{ // start of lock scope
AutoMutex lock(mLock);
- uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
- if (newSequence == oldSequence) {
+ if (mSequence == oldSequence) {
status = restoreTrack_l("obtainBuffer");
if (status != NO_ERROR) {
buffer.mFrameCount = 0;
@@ -2235,7 +2234,7 @@
}
}
}
- oldSequence = newSequence;
+ oldSequence = mSequence;
if (status == NOT_ENOUGH_DATA) {
restartIfDisabled();
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
index 61d5ccd..a215c0b 100644
--- a/media/libaudioclient/aidl/fuzzer/Android.bp
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -30,6 +30,7 @@
"libjsoncpp",
"libmediametricsservice",
"libprocessgroup",
+ "libprocessgroup_util",
"shared-file-region-aidl-cpp",
],
shared_libs: [
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index a95c700..8bca8df 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -46,6 +46,7 @@
"libmediametrics",
"libmediametricsservice",
"libprocessgroup",
+ "libprocessgroup_util",
"shared-file-region-aidl-cpp",
],
shared_libs: [
diff --git a/media/libaudioclient/include/media/AudioTimestamp.h b/media/libaudioclient/include/media/AudioTimestamp.h
index e5925dd..45d5595 100644
--- a/media/libaudioclient/include/media/AudioTimestamp.h
+++ b/media/libaudioclient/include/media/AudioTimestamp.h
@@ -154,10 +154,13 @@
std::string toString() const {
std::stringstream ss;
- ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << "\n";
+ ss << "BOOTTIME offset " << mTimebaseOffset[TIMEBASE_BOOTTIME] << ": ExtendedTimestamp: ";
for (int i = 0; i < LOCATION_MAX; ++i) {
- ss << "ExtendedTimestamp[" << i << "] position: "
- << mPosition[i] << " time: " << mTimeNs[i] << "\n";
+ ss << "([" << i << "] position: "
+ << mPosition[i] << " time: " << mTimeNs[i] << ")";
+ if (i != LOCATION_MAX - 1) {
+ ss << ", ";
+ }
}
return ss.str();
}
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 9863fec..c4e4ae8 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -544,7 +544,14 @@
}
void StreamHalAidl::onAsyncTransferReady() {
- if (auto state = getState(); state == StreamDescriptor::State::TRANSFERRING) {
+ StreamDescriptor::State state;
+ {
+ // Use 'mCommandReplyLock' to ensure that 'sendCommand' has finished updating the state
+ // after the reply from the 'burst' command.
+ std::lock_guard l(mCommandReplyLock);
+ state = getState();
+ }
+ if (state == StreamDescriptor::State::TRANSFERRING) {
// Retrieve the current state together with position counters unconditionally
// to ensure that the state on our side gets updated.
sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(),
@@ -555,7 +562,14 @@
}
void StreamHalAidl::onAsyncDrainReady() {
- if (auto state = getState(); state == StreamDescriptor::State::DRAINING) {
+ StreamDescriptor::State state;
+ {
+ // Use 'mCommandReplyLock' to ensure that 'sendCommand' has finished updating the state
+ // after the reply from the 'drain' command.
+ std::lock_guard l(mCommandReplyLock);
+ state = getState();
+ }
+ if (state == StreamDescriptor::State::DRAINING) {
// Retrieve the current state together with position counters unconditionally
// to ensure that the state on our side gets updated.
sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(), nullptr,
diff --git a/media/libmedia/xsd/vts/Android.bp b/media/libmedia/xsd/vts/Android.bp
index 83ab977..add7b51 100644
--- a/media/libmedia/xsd/vts/Android.bp
+++ b/media/libmedia/xsd/vts/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_kernel",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_media_libmedia_license"
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 10a1da7..b267c08 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1816,8 +1816,6 @@
const sp<AudioSystem::AudioDeviceCallback>& deviceCallback)
: mCachedPlayerIId(PLAYER_PIID_INVALID),
mCallback(NULL),
- mCallbackCookie(NULL),
- mCallbackData(NULL),
mStreamType(AUDIO_STREAM_MUSIC),
mLeftVolume(1.0),
mRightVolume(1.0),
@@ -2085,7 +2083,7 @@
status_t MediaPlayerService::AudioOutput::open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
+ AudioCallback cb, const wp<RefBase>& cookie,
audio_output_flags_t flags,
const audio_offload_info_t *offloadInfo,
bool doNotReconnect,
@@ -2514,6 +2512,15 @@
{
Mutex::Autolock lock(mLock);
track = mTrack;
+ }
+
+ // do not hold lock while joining.
+ if (track) {
+ track->stopAndJoinCallbacks();
+ }
+
+ {
+ Mutex::Autolock lock(mLock);
close_l(); // clears mTrack
}
// destruction of the track occurs outside of mutex.
@@ -2705,7 +2712,7 @@
return 0;
}
size_t actualSize = (*me->mCallback)(
- me.get(), buffer.data(), buffer.size(), me->mCallbackCookie,
+ me, buffer.data(), buffer.size(), me->mCallbackCookie,
CB_EVENT_FILL_BUFFER);
// Log when no data is returned from the callback.
@@ -2730,7 +2737,7 @@
return;
}
ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
- (*me->mCallback)(me.get(), NULL /* buffer */, 0 /* size */,
+ (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_STREAM_END);
unlock();
}
@@ -2744,7 +2751,7 @@
return;
}
ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
- (*me->mCallback)(me.get(), NULL /* buffer */, 0 /* size */,
+ (*me->mCallback)(me, nullptr /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
unlock();
}
@@ -2794,7 +2801,7 @@
struct CallbackThread : public Thread {
CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
MediaPlayerBase::AudioSink::AudioCallback cb,
- void *cookie);
+ const wp<RefBase>& cookie);
protected:
virtual ~CallbackThread();
@@ -2804,7 +2811,7 @@
private:
wp<MediaPlayerBase::AudioSink> mSink;
MediaPlayerBase::AudioSink::AudioCallback mCallback;
- void *mCookie;
+ wp<RefBase> mCookie;
void *mBuffer;
size_t mBufferSize;
@@ -2815,7 +2822,7 @@
CallbackThread::CallbackThread(
const wp<MediaPlayerBase::AudioSink> &sink,
MediaPlayerBase::AudioSink::AudioCallback cb,
- void *cookie)
+ const wp<RefBase>& cookie)
: mSink(sink),
mCallback(cb),
mCookie(cookie),
@@ -2842,7 +2849,7 @@
}
size_t actualSize =
- (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie,
+ (*mCallback)(sink, mBuffer, mBufferSize, mCookie,
MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER);
if (actualSize > 0) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index cb544bd..76b7bcf 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -104,14 +104,14 @@
virtual int64_t getBufferDurationInUs() const;
virtual audio_output_flags_t getFlags() const { return mFlags; }
- virtual status_t open(
+ status_t open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format, int bufferCount,
- AudioCallback cb, void *cookie,
+ AudioCallback cb, const wp<RefBase>& cookie,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL,
bool doNotReconnect = false,
- uint32_t suggestedFrameCount = 0);
+ uint32_t suggestedFrameCount = 0) override;
virtual void setPlayerIId(int32_t playerIId);
@@ -164,7 +164,7 @@
sp<AudioOutput> mNextOutput;
int mCachedPlayerIId;
AudioCallback mCallback;
- void * mCallbackCookie;
+ wp<RefBase> mCallbackCookie;
sp<CallbackData> mCallbackData;
audio_stream_type_t mStreamType;
audio_attributes_t * mAttributes;
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index be1aa00..495cf00 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -76,6 +76,33 @@
virtual ~Listener() {}
};
+ // For the AudioCallback, we provide a WeakWrapper class
+ // to wrap a virtual RefBase derived object to pass into the AudioCallback.
+ // This is not used for NuPlayer::Renderer, only for legacy AudioPlayer implementation.
+ template <typename T>
+ class WeakWrapper : public RefBase {
+ public:
+ explicit WeakWrapper(const sp<T>& object)
+ : mObject(object) {}
+
+ sp<T> promote() const {
+ if (mObject == nullptr) return {};
+ return mObject.promote();
+ }
+
+ static sp<T> promoteFromRefBase(const wp<RefBase>& weakWrapper) {
+ if (weakWrapper == nullptr) return {};
+ const auto refBase = weakWrapper.promote();
+ if (!refBase) return {};
+ const auto wrapper = sp<WeakWrapper<T>>::fromExisting(
+ static_cast<WeakWrapper<T>*>(refBase.get()));
+ return wrapper->promote();
+ }
+
+ private:
+ const wp<T> mObject;
+ };
+
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
@@ -89,8 +116,8 @@
// Callback returns the number of bytes actually written to the buffer.
typedef size_t (*AudioCallback)(
- AudioSink *audioSink, void *buffer, size_t size, void *cookie,
- cb_event_t event);
+ const sp<AudioSink>& audioSink, void *buffer, size_t size,
+ const wp<RefBase>& cookie, cb_event_t event);
virtual ~AudioSink() {}
virtual bool ready() const = 0; // audio output is open and ready
@@ -117,7 +144,7 @@
audio_format_t format=AUDIO_FORMAT_PCM_16_BIT,
int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
AudioCallback cb = NULL,
- void *cookie = NULL,
+ const wp<RefBase>& cookie = {},
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL,
bool doNotReconnect = false,
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 899d50e..f6aff83 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -912,12 +912,15 @@
// static
size_t NuPlayer::Renderer::AudioSinkCallback(
- MediaPlayerBase::AudioSink * /* audioSink */,
+ const sp<MediaPlayerBase::AudioSink>& /* audioSink */,
void *buffer,
size_t size,
- void *cookie,
+ const wp<RefBase>& cookie,
MediaPlayerBase::AudioSink::cb_event_t event) {
- NuPlayer::Renderer *me = (NuPlayer::Renderer *)cookie;
+ if (cookie == nullptr) return 0;
+ const auto ref = cookie.promote();
+ if (!ref) return 0;
+ const auto me = static_cast<NuPlayer::Renderer*>(ref.get()); // we already hold a sp.
switch (event) {
case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
diff --git a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
index 574ad3d..cfa742e 100644
--- a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerRenderer.h
@@ -43,8 +43,8 @@
uint32_t flags = 0);
static size_t AudioSinkCallback(
- MediaPlayerBase::AudioSink *audioSink,
- void *data, size_t size, void *me,
+ const sp<MediaPlayerBase::AudioSink>& audioSink,
+ void *data, size_t size, const wp<RefBase>& me,
MediaPlayerBase::AudioSink::cb_event_t event);
void queueBuffer(
diff --git a/media/libstagefright/xmlparser/vts/Android.bp b/media/libstagefright/xmlparser/vts/Android.bp
index 1e36c8f..527230c 100644
--- a/media/libstagefright/xmlparser/vts/Android.bp
+++ b/media/libstagefright/xmlparser/vts/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_media_libstagefright_license"
diff --git a/media/module/aidlpersistentsurface/Android.bp b/media/module/aidlpersistentsurface/Android.bp
index 5c1a010..8b273f3 100644
--- a/media/module/aidlpersistentsurface/Android.bp
+++ b/media/module/aidlpersistentsurface/Android.bp
@@ -3,6 +3,9 @@
unstable: true,
local_include_dir: "aidl",
min_sdk_version: "29",
+ defaults: [
+ "android.hardware.graphics.common-latest"
+ ],
srcs: [
"aidl/android/media/AidlColorAspects.aidl",
"aidl/android/media/IAidlGraphicBufferSource.aidl",
@@ -12,9 +15,6 @@
headers: [
"HardwareBuffer_aidl",
],
- imports: [
- "android.hardware.graphics.common-V5",
- ],
include_dirs: [
"frameworks/native/aidl/gui",
],
@@ -41,6 +41,9 @@
cc_library_shared {
name: "libstagefright_graphicbuffersource_aidl",
min_sdk_version: "29",
+ defaults: [
+ "android.hardware.graphics.common-ndk_shared",
+ ],
srcs: [
"AidlGraphicBufferSource.cpp",
"wrapper/WAidlGraphicBufferSource.cpp",
@@ -56,7 +59,6 @@
"media_plugin_headers",
],
shared_libs: [
- "android.hardware.graphics.common-V5-ndk",
"graphicbuffersource-aidl-ndk",
"libbinder_ndk",
"libcutils",
diff --git a/media/module/codecs/amrwb/enc/Android.bp b/media/module/codecs/amrwb/enc/Android.bp
index 04f36b5..6ca3b6e 100644
--- a/media/module/codecs/amrwb/enc/Android.bp
+++ b/media/module/codecs/amrwb/enc/Android.bp
@@ -96,8 +96,6 @@
"-DARM",
"-DARMV7",
"-DASM_OPT",
- // don't actually generate neon instructions, see bug 26932980
- "-mfpu=vfpv3",
],
local_include_dirs: [
"src/asm/ARMV7",
diff --git a/media/module/extractors/extractor.aconfig b/media/module/extractors/extractor.aconfig
index c61efa4..c9bf694 100644
--- a/media/module/extractors/extractor.aconfig
+++ b/media/module/extractors/extractor.aconfig
@@ -7,6 +7,7 @@
flag {
name: "extractor_sniff_midi_optimizations"
is_exported: true
+ is_fixed_read_only: true
namespace: "media_extractor"
description: "Enable SniffMidi optimizations."
bug: "359920208"
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/BlockModelDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/BlockModelDecoder.java
new file mode 100644
index 0000000..3b3640e
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/BlockModelDecoder.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 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 com.android.media.benchmark.library;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import com.android.media.benchmark.library.Decoder;
+
+public class BlockModelDecoder extends Decoder {
+ private static final String TAG = BlockModelDecoder.class.getSimpleName();
+ private final boolean DEBUG = false;
+ protected final LinearBlockWrapper mLinearInputBlock = new LinearBlockWrapper();
+
+ /**
+ * Wrapper class for {@link MediaCodec.LinearBlock}
+ */
+ public static class LinearBlockWrapper {
+ private MediaCodec.LinearBlock mBlock;
+ private ByteBuffer mBuffer;
+ private int mOffset;
+
+ public MediaCodec.LinearBlock getBlock() {
+ return mBlock;
+ }
+
+ public ByteBuffer getBuffer() {
+ return mBuffer;
+ }
+
+ public int getBufferCapacity() {
+ return mBuffer == null ? 0 : mBuffer.capacity();
+ }
+
+ public int getOffset() {
+ return mOffset;
+ }
+
+ public void setOffset(int size) {
+ mOffset = size;
+ }
+
+ public boolean allocateBlock(String codec, int size) throws RuntimeException{
+ recycle();
+ mBlock = MediaCodec.LinearBlock.obtain(size, new String[]{codec});
+ if (mBlock == null || !mBlock.isMappable()) {
+ throw new RuntimeException("Linear Block not allocated/mapped");
+ }
+ mBuffer = mBlock.map();
+ mOffset = 0;
+ return true;
+ }
+
+ public void recycle() {
+ if (mBlock != null) {
+ mBlock.recycle();
+ mBlock = null;
+ }
+ mBuffer = null;
+ mOffset = 0;
+ }
+ }
+
+ public BlockModelDecoder() {
+ // empty
+ }
+
+ public void tearDown() {
+ mLinearInputBlock.recycle();
+
+ }
+
+ /**
+ * Decodes the given input buffer,
+ * provided valid list of buffer info and format are passed as inputs.
+ *
+ * @param inputBuffer Decode the provided list of ByteBuffers
+ * @param inputBufferInfo List of buffer info corresponding to provided input buffers
+ * @param asyncMode Will run on async implementation if true
+ * @param format For creating the decoder if codec name is empty and configuring it
+ * @param codecName Will create the decoder with codecName
+ * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+ * DECODE_CREATE_ERROR for decoder not created
+ * @throws IOException if the codec cannot be created.
+ */
+ @Override
+ public int decode(@NonNull List<ByteBuffer> inputBuffer,
+ @NonNull List<MediaCodec.BufferInfo> inputBufferInfo, final boolean asyncMode,
+ @NonNull MediaFormat format, String codecName)
+ throws IOException, InterruptedException {
+ setExtraConfigureFlags(MediaCodec.CONFIGURE_FLAG_USE_BLOCK_MODEL);
+ return super.decode(inputBuffer, inputBufferInfo, asyncMode, format, codecName);
+ }
+
+ @Override
+ protected void onInputAvailable(int inputBufferId, MediaCodec mediaCodec) {
+ if (mNumInFramesProvided >= mNumInFramesRequired) {
+ mIndex = mInputBufferInfo.size() - 1;
+ }
+ MediaCodec.BufferInfo bufInfo = mInputBufferInfo.get(mIndex);
+ mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (mLinearInputBlock.getOffset() + bufInfo.size > mLinearInputBlock.getBufferCapacity()) {
+ int requestSize = 8192;
+ requestSize = Math.max(bufInfo.size, requestSize);
+ mLinearInputBlock.allocateBlock(mediaCodec.getCanonicalName(), requestSize);
+ }
+ int codecFlags = 0;
+ if ((bufInfo.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+ }
+ if ((bufInfo.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+ }
+ codecFlags |= mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0;
+ if (DEBUG) {
+ Log.v(TAG, "input: id: " + inputBufferId
+ + " size: " + bufInfo.size
+ + " pts: " + bufInfo.presentationTimeUs
+ + " flags: " + codecFlags);
+ }
+ mLinearInputBlock.getBuffer().put(mInputBuffer.get(mIndex).array());
+ mNumInFramesProvided++;
+ mIndex = mNumInFramesProvided % (mInputBufferInfo.size() - 1);
+ if (mSawInputEOS) {
+ Log.i(TAG, "Saw Input EOS");
+ }
+ mStats.addFrameSize(bufInfo.size);
+ MediaCodec.QueueRequest request = mCodec.getQueueRequest(inputBufferId);
+ request.setLinearBlock(mLinearInputBlock.getBlock(), mLinearInputBlock.getOffset(),
+ bufInfo.size);
+ request.setPresentationTimeUs(bufInfo.presentationTimeUs);
+ request.setFlags(codecFlags);
+ request.queue();
+ if (bufInfo.size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG
+ | MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
+ mLinearInputBlock.setOffset(mLinearInputBlock.getOffset() + bufInfo.size);
+ }
+ }
+
+ @Override
+ protected void onOutputAvailable(
+ MediaCodec mediaCodec, int outputBufferId, MediaCodec.BufferInfo outputBufferInfo) {
+ if (mSawOutputEOS || outputBufferId < 0) {
+ return;
+ }
+ mNumOutputFrame++;
+ if (DEBUG) {
+ Log.d(TAG,
+ "In OutputBufferAvailable ,"
+ + " output frame number = " + mNumOutputFrame
+ + " timestamp = " + outputBufferInfo.presentationTimeUs
+ + " size = " + outputBufferInfo.size);
+ }
+ MediaCodec.OutputFrame outFrame = mediaCodec.getOutputFrame(outputBufferId);
+ ByteBuffer outputBuffer = null;
+ try {
+ if (outFrame.getLinearBlock() != null) {
+ outputBuffer = outFrame.getLinearBlock().map();
+ }
+ } catch(IllegalStateException e) {
+ // buffer may not be linear, this is ok
+ // as we are handling non-linear buffers below.
+ }
+ if (mOutputStream != null) {
+ try {
+ if (outputBuffer != null) {
+ byte[] bytesOutput = new byte[outputBuffer.remaining()];
+ outputBuffer.get(bytesOutput);
+ mOutputStream.write(bytesOutput);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.d(TAG, "Error Dumping File: Exception " + e.toString());
+ }
+ }
+ ByteBuffer copiedBuffer = null;
+ int bytesRemaining = 0;
+ if (outputBuffer != null) {
+ bytesRemaining = outputBuffer.remaining();
+ if (mIBufferSend != null) {
+ copiedBuffer = ByteBuffer.allocate(outputBuffer.remaining());
+ copiedBuffer.put(outputBuffer);
+ }
+ outFrame.getLinearBlock().recycle();
+ outputBuffer = null;
+ }
+ if (mFrameReleaseQueue != null) {
+ if (mMime.startsWith("audio/")) {
+ try {
+ mFrameReleaseQueue.pushFrame(outputBufferId, bytesRemaining);
+ } catch (Exception e) {
+ Log.d(TAG, "Error in getting MediaCodec buffer" + e.toString());
+ }
+ } else {
+ mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
+ outputBufferInfo.presentationTimeUs);
+ }
+
+ } else if (mIBufferSend != null) {
+ IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+ // TODO: may be inefficient;
+ info.buf = copiedBuffer;
+ info.idx = outputBufferId;
+ info.obj = mediaCodec;
+ info.bytesRead = outputBufferInfo.size;
+ info.presentationTimeUs = outputBufferInfo.presentationTimeUs;
+ info.flag = outputBufferInfo.flags;
+ mIBufferSend.sendBuffer(this, info);
+ } else {
+ mediaCodec.releaseOutputBuffer(outputBufferId, mRender);
+ }
+ mSawOutputEOS = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (DEBUG && mSawOutputEOS) {
+ Log.i(TAG, "Saw output EOS");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
index f223242..031817b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
@@ -78,4 +78,21 @@
}
return null;
}
+ /**
+ * Returns compression ratio for a given mediaType.
+ * @param mediaType mime type for which compression ratio is to be returned.
+ */
+ public static float getCompressionRatio(String mediaType) {
+ switch (mediaType) {
+ case MediaFormat.MIMETYPE_AUDIO_FLAC:
+ return 0.7f;
+ case MediaFormat.MIMETYPE_AUDIO_G711_MLAW:
+ case MediaFormat.MIMETYPE_AUDIO_G711_ALAW:
+ case MediaFormat.MIMETYPE_AUDIO_MSGSM:
+ return 0.5f;
+ case MediaFormat.MIMETYPE_AUDIO_RAW:
+ return 1.0f;
+ }
+ return 0.1f;
+ }
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index e9b337d..2ea0ed2 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -18,6 +18,7 @@
import android.view.Surface;
+import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaFormat;
@@ -42,6 +43,7 @@
protected final Object mLock = new Object();
protected MediaCodec mCodec;
+ protected int mExtraFlags = 0;
protected Surface mSurface = null;
protected boolean mRender = false;
protected ArrayList<BufferInfo> mInputBufferInfo;
@@ -58,6 +60,8 @@
protected int mNumOutputFrame;
protected int mIndex;
+ protected boolean mUseFrameReleaseQueue = false;
+
protected ArrayList<ByteBuffer> mInputBuffer;
protected FileOutputStream mOutputStream;
protected FrameReleaseQueue mFrameReleaseQueue = null;
@@ -85,6 +89,11 @@
mIBufferSend = receiver;
return true;
}
+
+ public void setExtraConfigureFlags(int flags) {
+ this.mExtraFlags = flags;
+ }
+
/**
* Setup of decoder
*
@@ -94,17 +103,32 @@
mSignalledError = false;
mOutputStream = outputStream;
}
+
+ /*
+ * This can be used to setup audio decoding, simulating audio playback.
+ */
+ public void setupDecoder(
+ boolean render, boolean useFrameReleaseQueue, int numInFramesRequired) {
+ mRender = render;
+ mUseFrameReleaseQueue = useFrameReleaseQueue;
+ mNumInFramesRequired = numInFramesRequired;
+ mSignalledError = false;
+ setupDecoder(null);
+ }
+
public void setupDecoder(Surface surface, boolean render,
boolean useFrameReleaseQueue, int frameRate) {
setupDecoder(surface, render, useFrameReleaseQueue, frameRate, -1);
}
+
public void setupDecoder(Surface surface, boolean render,
boolean useFrameReleaseQueue, int frameRate, int numInFramesRequired) {
mSignalledError = false;
mOutputStream = null;
mSurface = surface;
mRender = render;
- if (useFrameReleaseQueue) {
+ mUseFrameReleaseQueue = useFrameReleaseQueue;
+ if (mUseFrameReleaseQueue) {
Log.i(TAG, "Using FrameReleaseQueue with frameRate " + frameRate);
mFrameReleaseQueue = new FrameReleaseQueue(mRender, frameRate);
}
@@ -166,6 +190,18 @@
public void onOutputFormatChanged(
@NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
Log.i(TAG, "Output format changed. Format: " + format.toString());
+ if (mUseFrameReleaseQueue
+ && mFrameReleaseQueue == null && mMime.startsWith("audio/")) {
+ // start a frame release thread for this configuration.
+ int bytesPerSample = AudioFormat.getBytesPerSample(
+ format.getInteger(MediaFormat.KEY_PCM_ENCODING,
+ AudioFormat.ENCODING_PCM_16BIT));
+ int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ mFrameReleaseQueue = new FrameReleaseQueue(
+ mRender, sampleRate, channelCount, bytesPerSample);
+ mFrameReleaseQueue.setMediaCodec(mCodec);
+ }
}
@Override
@@ -223,11 +259,10 @@
if (asyncMode) {
setCallback(mCodec);
}
- int isEncoder = 0;
if (DEBUG) {
Log.d(TAG, "Media Format : " + format.toString());
}
- mCodec.configure(format, mSurface, null, isEncoder);
+ mCodec.configure(format, mSurface, null, mExtraFlags);
mCodec.start();
Log.i(TAG, "Codec started async mode ? " + asyncMode);
@@ -395,8 +430,17 @@
}
}
if (mFrameReleaseQueue != null) {
- mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
- outputBufferInfo.presentationTimeUs);
+ if (mMime.startsWith("audio/")) {
+ try {
+ ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferId);
+ mFrameReleaseQueue.pushFrame(outputBufferId, outputBuffer.remaining());
+ } catch (Exception e) {
+ Log.d(TAG, "Error in getting MediaCodec buffer" + e.toString());
+ }
+ } else {
+ mFrameReleaseQueue.pushFrame(mNumOutputFrame, outputBufferId,
+ outputBufferInfo.presentationTimeUs);
+ }
} else if (mIBufferSend != null) {
IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
info.buf = mediaCodec.getOutputBuffer(outputBufferId);
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
index 20a2573..0861c2c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
@@ -29,45 +29,89 @@
public class FrameReleaseQueue {
private static final String TAG = "FrameReleaseQueue";
+ private static final boolean DEBUG = false;
private final String MIME_AV1 = "video/av01";
private final int AV1_SUPERFRAME_DELAY = 6;
private final int THRESHOLD_TIME = 5;
+ private final long HOUR_IN_MS = (60 * 60 * 1000L);
+ private final long MINUTE_IN_MS = (60 * 1000L);
+
private MediaCodec mCodec;
private LinkedBlockingQueue<FrameInfo> mFrameInfoQueue;
private ReleaseThread mReleaseThread;
private AtomicBoolean doFrameRelease = new AtomicBoolean(false);
- private boolean mReleaseJobStarted = false;
+ private AtomicBoolean mReleaseJobStarted = new AtomicBoolean(false);
private boolean mRender = false;
- private int mWaitTime = 40; // milliseconds per frame
+ private long mWaitTime = 40; // milliseconds per frame
private int mWaitTimeCorrection = 0;
private int mCorrectionLoopCount;
- private int firstReleaseTime = -1;
- private int mAllowedDelayTime = THRESHOLD_TIME;
+ protected long firstReleaseTime = -1;
+ private long mAllowedDelayTime = THRESHOLD_TIME;
private int mFrameDelay = 0;
private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
+ public FrameReleaseQueue(boolean render, int frameRate) {
+ this.mFrameInfoQueue = new LinkedBlockingQueue();
+ this.mReleaseThread = new ReleaseThread();
+ this.doFrameRelease.set(true);
+ this.mRender = render;
+ this.mWaitTime = 1000 / frameRate; // wait time in milliseconds per frame
+ int waitTimeRemainder = 1000 % frameRate;
+ int gcd = gcd(frameRate, waitTimeRemainder);
+ this.mCorrectionLoopCount = frameRate / gcd;
+ this.mWaitTimeCorrection = waitTimeRemainder / gcd;
+ Log.i(TAG, "Constructed FrameReleaseQueue with wait time " + this.mWaitTime + " ms");
+ }
+
+ public FrameReleaseQueue(boolean render, int sampleRate, int nChannels, int bytesPerChannel) {
+ this.mFrameInfoQueue = new LinkedBlockingQueue();
+ this.doFrameRelease.set(true);
+ this.mRender = render;
+ this.mReleaseThread = new AudioRendererThread(sampleRate, nChannels, bytesPerChannel);
+ }
private static class FrameInfo {
private int number;
private int bufferId;
private int displayTime;
+ private int bytes;
public FrameInfo(int frameNumber, int frameBufferId, int frameDisplayTime) {
this.number = frameNumber;
this.bufferId = frameBufferId;
this.displayTime = frameDisplayTime;
}
+ public FrameInfo(int frameBufferId, int bytes) {
+ this.bufferId = frameBufferId;
+ this.bytes = bytes;
+ }
}
private class ReleaseThread extends Thread {
private int mLoopCount = 0;
- private int mNextReleaseTime = 0;
+ private long mNextReleaseTime = 0;
+
+ protected void printPlaybackTime() {
+ if (firstReleaseTime == -1) {
+ Log.d(TAG, "Playback Time not initialized");
+ return;
+ }
+ long curTime = getCurSysTime() - firstReleaseTime;
+ long hours = curTime / (HOUR_IN_MS);
+ curTime -= (hours * HOUR_IN_MS);
+ long min = curTime / MINUTE_IN_MS;
+ curTime -= (min * MINUTE_IN_MS);
+ Log.d(TAG, "Playback time: "
+ + hours + "h "
+ + min + "m "
+ + (double)(curTime / (double)1000) +"s");
+ }
@SuppressWarnings("FutureReturnValueIgnored")
public void run() {
/* Check if the release thread wakes up too late */
if (mLoopCount != 0) {
- int delta = getCurSysTime() - mNextReleaseTime;
+ long delta = getCurSysTime() - mNextReleaseTime;
if (delta >= THRESHOLD_TIME) {
Log.d(TAG, "Release thread wake up late by " + delta);
/* For accidental late wake up, we should relax the timestamp
@@ -93,8 +137,8 @@
popAndRelease(false);
} else {
mNextReleaseTime += mWaitTime;
- int curSysTime = getCurSysTime();
- int curMediaTime = curSysTime - firstReleaseTime;
+ long curSysTime = getCurSysTime();
+ long curMediaTime = curSysTime - firstReleaseTime;
while (curFrameInfo != null && curFrameInfo.displayTime > 0 &&
curFrameInfo.displayTime <= curMediaTime) {
if (!((curMediaTime - curFrameInfo.displayTime) <= mAllowedDelayTime)) {
@@ -123,21 +167,86 @@
mNextReleaseTime += mWaitTimeCorrection;
}
mLoopCount += 1;
+ } else {
+ mReleaseJobStarted.set(false);
}
}
}
- public FrameReleaseQueue(boolean render, int frameRate) {
- this.mFrameInfoQueue = new LinkedBlockingQueue();
- this.mReleaseThread = new ReleaseThread();
- this.doFrameRelease.set(true);
- this.mRender = render;
- this.mWaitTime = 1000 / frameRate; // wait time in milliseconds per frame
- int waitTimeRemainder = 1000 % frameRate;
- int gcd = gcd(frameRate, waitTimeRemainder);
- this.mCorrectionLoopCount = frameRate / gcd;
- this.mWaitTimeCorrection = waitTimeRemainder / gcd;
- Log.i(TAG, "Constructed FrameReleaseQueue with wait time " + this.mWaitTime + " ms");
+ private class AudioRendererThread extends ReleaseThread {
+ private final int WAIT_FOR_BUFFER_IN_SEC = 2;
+ private double mTimeAdjustMs = 0;
+ private double mMsForByte = 0;
+ private double mExpectedWakeUpTime = 0;
+ private FrameInfo mCurrentFrameInfo;
+
+ AudioRendererThread(int sampleRate, int nChannels, int bytesPerChannel) {
+ if (DEBUG) {
+ Log.d(TAG, "sampleRate " + sampleRate
+ + " nChannels " + nChannels
+ + " bytesPerChannel " + bytesPerChannel);
+ }
+ this.mMsForByte = 1000 / (double)(sampleRate * nChannels * bytesPerChannel);
+ }
+
+ @Override
+ @SuppressWarnings("FutureReturnValueIgnored")
+ public void run() {
+ long curTime = getCurSysTime();
+ if (DEBUG) {
+ if (firstReleaseTime == -1) {
+ firstReleaseTime = curTime;
+ }
+ printPlaybackTime();
+ }
+ if (mMsForByte == 0) {
+ Log.e(TAG, "Audio rendering not possible, no valid params");
+ return;
+ }
+ if (mCurrentFrameInfo != null) {
+ try {
+ mCodec.releaseOutputBuffer(mCurrentFrameInfo.bufferId, mRender);
+ } catch (IllegalStateException e) {
+ doFrameRelease.set(false);
+ Log.e(TAG, "Threw InterruptedException on releaseOutputBuffer");
+ } finally {
+ mCurrentFrameInfo = null;
+ }
+ }
+ boolean requestedSchedule = false;
+ try {
+ while (doFrameRelease.get() || mFrameInfoQueue.size() > 0) {
+ mCurrentFrameInfo = mFrameInfoQueue.poll(
+ WAIT_FOR_BUFFER_IN_SEC, TimeUnit.SECONDS);
+ if (mCurrentFrameInfo != null) {
+ mTimeAdjustMs = 0;
+ if (mExpectedWakeUpTime != 0) {
+ mTimeAdjustMs = mExpectedWakeUpTime - getCurSysTime();
+ }
+ double sleepTimeUs =
+ (mMsForByte * mCurrentFrameInfo.bytes + mTimeAdjustMs) * 1000;
+ mExpectedWakeUpTime = getCurSysTime() + (sleepTimeUs / 1000);
+ if (DEBUG) {
+ Log.d(TAG, " mExpectedWakeUpTime " + mExpectedWakeUpTime
+ + " Waiting for " + (long)(sleepTimeUs) + "us"
+ + " Now " + getCurSysTime()
+ + " bytes " + mCurrentFrameInfo.bytes
+ + " bufferID " + mCurrentFrameInfo.bufferId);
+ }
+ mScheduler.schedule(
+ mReleaseThread,(long)(sleepTimeUs),TimeUnit.MICROSECONDS);
+ requestedSchedule = true;
+ break;
+ }
+ }
+ } catch(InterruptedException e) {
+ Log.d(TAG, "Interrupted during poll wait");
+ doFrameRelease.set(false);
+ }
+ if (!requestedSchedule) {
+ mReleaseJobStarted.set(false);
+ }
+ }
}
private static int gcd(int a, int b) {
@@ -154,6 +263,19 @@
}
}
+ public boolean pushFrame(int frameBufferId, int bytes) {
+ FrameInfo info = new FrameInfo(frameBufferId, bytes);
+ boolean pushSuccess = mFrameInfoQueue.offer(info);
+ if (!pushSuccess) {
+ Log.e(TAG, "Failed to push frame with buffer id " + info.bufferId);
+ return false;
+ }
+ if (!mReleaseJobStarted.get()) {
+ mScheduler.execute(mReleaseThread);
+ mReleaseJobStarted.set(true);
+ }
+ return true;
+ }
public boolean pushFrame(int frameNumber, int frameBufferId, long frameDisplayTime) {
int frameDisplayTimeMs = (int)(frameDisplayTime/1000);
FrameInfo curFrameInfo = new FrameInfo(frameNumber, frameBufferId, frameDisplayTimeMs);
@@ -163,16 +285,16 @@
return false;
}
- if (!mReleaseJobStarted && frameNumber >= mFrameDelay) {
+ if (!mReleaseJobStarted.get() && frameNumber >= mFrameDelay) {
mScheduler.execute(mReleaseThread);
- mReleaseJobStarted = true;
+ mReleaseJobStarted.set(true);
Log.i(TAG, "Started frame release thread");
}
return true;
}
- private int getCurSysTime() {
- return (int)(System.nanoTime()/1000000);
+ private long getCurSysTime() {
+ return (long)(System.nanoTime() / 1000000L);
}
@SuppressWarnings("FutureReturnValueIgnored")
@@ -196,7 +318,7 @@
public void stopFrameRelease() {
doFrameRelease.set(false);
- while (mFrameInfoQueue.size() > 0) {
+ while (mReleaseJobStarted.get()) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
index c97a35c..bbc3d48 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
@@ -16,6 +16,8 @@
package com.android.media.benchmark.library;
import android.media.MediaCodec;
+
+import java.util.ArrayDeque;
import java.nio.ByteBuffer;
/**
* interfaces that can be used to implement
@@ -26,10 +28,11 @@
public ByteBuffer buf;
public int idx;
public Object obj;
- int flag;
- int bytesRead;
- boolean isComplete = true;
- long presentationTimeUs;
+ public ArrayDeque<MediaCodec.BufferInfo> infos;
+ public int flag;
+ public int bytesRead;
+ public boolean isComplete = true;
+ public long presentationTimeUs;
}
public interface IReceiveBuffer {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
index 3e6cee1..c68ac8a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
@@ -96,10 +96,10 @@
pBuf.info.buf.remaining() +" C:" + cBuf.info.buf.remaining());
}
}
+ cBuf.info.infos = pBuf.info.infos;
cBuf.info.bytesRead = bytesRead;
cBuf.info.presentationTimeUs = pBuf.info.presentationTimeUs;
cBuf.info.flag = pBuf.info.flag;
-
if (pBuf.rIface != null) {
pBuf.rIface.receiveBuffer(pBuf.info);
}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitBlockModelDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitBlockModelDecoder.java
new file mode 100644
index 0000000..ed2defe
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitBlockModelDecoder.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2024 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 com.android.media.benchmark.library;
+
+import android.media.AudioFormat;
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import java.util.ArrayDeque;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.media.benchmark.library.CodecUtils;
+import com.android.media.benchmark.library.BlockModelDecoder;
+
+public class MultiAccessUnitBlockModelDecoder extends BlockModelDecoder {
+ private static final String TAG = MultiAccessUnitBlockModelDecoder.class.getSimpleName();
+ private final ArrayDeque<MediaCodec.BufferInfo> mInputInfos = new ArrayDeque<>();
+ private final boolean DEBUG = false;
+ protected int mMaxInputSize = 0;
+
+ public MultiAccessUnitBlockModelDecoder() {
+ // empty
+ }
+
+ /**
+ * Decodes the given input buffer,
+ * provided valid list of buffer info and format are passed as inputs.
+ *
+ * @param inputBuffer Decode the provided list of ByteBuffers
+ * @param inputBufferInfo List of buffer info corresponding to provided input buffers
+ * @param asyncMode Will run on async implementation if true
+ * @param format For creating the decoder if codec name is empty and configuring it
+ * @param codecName Will create the decoder with codecName
+ * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+ * DECODE_CREATE_ERROR for decoder not created
+ * @throws IOException if the codec cannot be created.
+ */
+ @Override
+ public int decode(@NonNull List<ByteBuffer> inputBuffer,
+ @NonNull List<MediaCodec.BufferInfo> inputBufferInfo, final boolean asyncMode,
+ @NonNull MediaFormat format, String codecName)
+ throws IOException, InterruptedException {
+ setExtraConfigureFlags(MediaCodec.CONFIGURE_FLAG_USE_BLOCK_MODEL);
+ configureMaxInputSize(format);
+ return super.decode(inputBuffer, inputBufferInfo, asyncMode, format, codecName);
+ }
+
+ protected void configureMaxInputSize(MediaFormat format) {
+ final String mime = format.getString(MediaFormat.KEY_MIME);
+ final int maxOutputSize = format.getNumber(
+ MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+ final int maxInputSizeInBytes = format.getInteger(
+ MediaFormat.KEY_MAX_INPUT_SIZE);
+ mMaxInputSize = Math.max(maxInputSizeInBytes,
+ (int) (maxOutputSize * CodecUtils.getCompressionRatio(mime)));
+ }
+
+ @Override
+ public void setCallback(MediaCodec codec) {
+ mCodec.setCallback(new MediaCodec.Callback() {
+ boolean isUsingLargeFrameMode = false;
+
+ @Override
+ public void onInputBufferAvailable(
+ @NonNull MediaCodec mediaCodec, int inputBufferId) {
+ try {
+ mStats.addInputTime();
+ if (isUsingLargeFrameMode) {
+ onInputsAvailable(inputBufferId, mediaCodec);
+ } else {
+ onInputAvailable(inputBufferId, mediaCodec);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ @Override
+ public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
+ int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
+ mStats.addOutputTime();
+ onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
+ if (mSawOutputEOS) {
+ synchronized (mLock) { mLock.notify(); }
+ }
+ }
+
+ @Override
+ public void onOutputBuffersAvailable(
+ @NonNull MediaCodec mediaCodec,
+ int outputBufferId, @NonNull ArrayDeque<MediaCodec.BufferInfo> infos) {
+ int i = 0;
+ while(i++ < infos.size()) {
+ mStats.addOutputTime();
+ }
+ onOutputsAvailable(mediaCodec, outputBufferId, infos);
+ if (mSawOutputEOS) {
+ synchronized (mLock) { mLock.notify(); }
+ }
+ }
+
+ @Override
+ public void onOutputFormatChanged(
+ @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
+ Log.i(TAG, "Output format changed. Format: " + format.toString());
+ final int maxOutputSize = format.getNumber(
+ MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+ isUsingLargeFrameMode = (maxOutputSize > 0);
+ configureMaxInputSize(format);
+ if (mUseFrameReleaseQueue && mFrameReleaseQueue == null) {
+ int bytesPerSample = AudioFormat.getBytesPerSample(
+ format.getInteger(MediaFormat.KEY_PCM_ENCODING,
+ AudioFormat.ENCODING_PCM_16BIT));
+ int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ mFrameReleaseQueue = new FrameReleaseQueue(
+ mRender, sampleRate, channelCount, bytesPerSample);
+ mFrameReleaseQueue.setMediaCodec(mCodec);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
+ mSignalledError = true;
+ Log.e(TAG, "Codec Error: " + e.toString());
+ e.printStackTrace();
+ synchronized (mLock) { mLock.notify(); }
+ }
+ });
+
+ }
+
+ protected void onInputsAvailable(int inputBufferId, MediaCodec mediaCodec) {
+ if (inputBufferId >= 0) {
+ mLinearInputBlock.allocateBlock(mediaCodec.getCanonicalName(), mMaxInputSize);
+ MediaCodec.BufferInfo bufInfo;
+ mInputInfos.clear();
+ int offset = 0;
+ while (mNumInFramesProvided < mNumInFramesRequired) {
+ bufInfo = mInputBufferInfo.get(mIndex);
+ mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ int bufferSizeNeeded = mLinearInputBlock.getOffset() + bufInfo.size;
+ if (bufferSizeNeeded > mLinearInputBlock.getBufferCapacity()) {
+ break;
+ }
+ mLinearInputBlock.getBuffer().put(mInputBuffer.get(mIndex).array());
+ mLinearInputBlock.setOffset(mLinearInputBlock.getOffset() + bufInfo.size);
+ bufInfo.offset = offset; offset += bufInfo.size;
+ mInputInfos.add(bufInfo);
+ mNumInFramesProvided++;
+ mIndex = mNumInFramesProvided % (mInputBufferInfo.size() - 1);
+
+ }
+ if (DEBUG) {
+ Log.d(TAG, "inputsAvailable ID : " + inputBufferId
+ + " queued info size: " + mInputInfos.size()
+ + " Total queued size: " + offset);
+ }
+ if (mNumInFramesProvided >= mNumInFramesRequired) {
+ mIndex = mInputBufferInfo.size() - 1;
+ bufInfo = mInputBufferInfo.get(mIndex);
+ int bufferSizeNeeded = mLinearInputBlock.getOffset() + bufInfo.size;
+ if (bufferSizeNeeded <= mLinearInputBlock.getBufferCapacity()) {
+ if ((bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == 0) {
+ Log.e(TAG, "Error in EOS flag for Decoder");
+ }
+ mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ mLinearInputBlock.getBuffer().put(mInputBuffer.get(mIndex).array());
+ mLinearInputBlock.setOffset(mLinearInputBlock.getOffset() + bufInfo.size);
+ bufInfo.offset = offset; offset += bufInfo.size;
+ //bufInfo.flags = codecFlags;
+ mInputInfos.add(bufInfo);
+ mNumInFramesProvided++;
+ }
+ }
+ if (mInputInfos.size() == 0) {
+ Log.d(TAG, " No inputs to queue");
+ } else {
+ mStats.addFrameSize(offset);
+ MediaCodec.QueueRequest request = mediaCodec.getQueueRequest(inputBufferId);
+ request.setMultiFrameLinearBlock(mLinearInputBlock.getBlock(), mInputInfos);
+ request.queue();
+ }
+ }
+ }
+
+ protected void onOutputsAvailable(MediaCodec mediaCodec, int outputBufferId,
+ ArrayDeque<MediaCodec.BufferInfo> infos) {
+ if (mSawOutputEOS || outputBufferId < 0) {
+ return;
+ }
+ MediaCodec.OutputFrame outFrame = mediaCodec.getOutputFrame(outputBufferId);
+ ByteBuffer outputBuffer = null;
+ try {
+ if (outFrame.getLinearBlock() != null) {
+ outputBuffer = outFrame.getLinearBlock().map();
+ }
+ } catch(IllegalStateException e) {
+ // buffer may not be linear, this is ok
+ // as we are handling non-linear buffers below.
+ }
+ if (mOutputStream != null) {
+ try {
+ if (outputBuffer != null) {
+ byte[] bytesOutput = new byte[outputBuffer.remaining()];
+ outputBuffer.get(bytesOutput);
+ mOutputStream.write(bytesOutput);
+ if (DEBUG) {
+ Log.d(TAG, "Received outputs buffer size : " + outputBuffer.remaining()
+ + " infos size " + infos.size());
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.d(TAG, "Error Dumping File: Exception " + e.toString());
+ }
+ }
+ mNumOutputFrame += infos.size();
+ MediaCodec.BufferInfo last = infos.peekLast();
+ if (last != null) {
+ mSawOutputEOS |= ((last.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
+ }
+ int bytesRemaining = 0;
+ if (outputBuffer != null) {
+ bytesRemaining = outputBuffer.remaining();
+ outFrame.getLinearBlock().recycle();
+ outputBuffer = null;
+ }
+ if (mFrameReleaseQueue != null) {
+ mFrameReleaseQueue.pushFrame(outputBufferId, bytesRemaining);
+ } else if (mIBufferSend == null) {
+ mediaCodec.releaseOutputBuffer(outputBufferId, mRender);
+ }
+ if (mSawOutputEOS) {
+ Log.i(TAG, "Large frame - saw output EOS");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
index cb92f06..fd8859b 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
@@ -18,6 +18,7 @@
import android.view.Surface;
+import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaFormat;
@@ -91,8 +92,18 @@
@NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
Log.i(TAG, "Output format changed. Format: " + format.toString());
final int maxOutputSize = format.getNumber(
- MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+ MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
isUsingLargeFrameMode = (maxOutputSize > 0);
+ if (mUseFrameReleaseQueue && mFrameReleaseQueue == null) {
+ int bytesPerSample = AudioFormat.getBytesPerSample(
+ format.getInteger(MediaFormat.KEY_PCM_ENCODING,
+ AudioFormat.ENCODING_PCM_16BIT));
+ int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ mFrameReleaseQueue = new FrameReleaseQueue(
+ mRender, sampleRate, channelCount, bytesPerSample);
+ mFrameReleaseQueue.setMediaCodec(mCodec);
+ }
}
@Override
@@ -177,30 +188,6 @@
if (mSawOutputEOS || outputBufferId < 0) {
return;
}
- Iterator<BufferInfo> iter = infos.iterator();
- while (iter.hasNext()) {
- BufferInfo bufferInfo = iter.next();
- mNumOutputFrame++;
- if (DEBUG) {
- Log.d(TAG,
- "In OutputBufferAvailable ,"
- + " output frame number = " + mNumOutputFrame
- + " timestamp = " + bufferInfo.presentationTimeUs
- + " size = " + bufferInfo.size);
- }
- if (mIBufferSend != null) {
- IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
- info.buf = mc.getOutputBuffer(outputBufferId);
- info.idx = outputBufferId;
- info.obj = mc;
- info.bytesRead = bufferInfo.size;
- info.presentationTimeUs = bufferInfo.presentationTimeUs;
- info.flag = bufferInfo.flags;
- info.isComplete = iter.hasNext() ? false : true;
- mIBufferSend.sendBuffer(this, info);
- }
- mSawOutputEOS |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
- }
if (mOutputStream != null) {
try {
ByteBuffer outputBuffer = mc.getOutputBuffer(outputBufferId);
@@ -212,12 +199,27 @@
Log.d(TAG, "Error Dumping File: Exception " + e.toString());
}
}
- if (mIBufferSend == null) {
+ mNumOutputFrame += infos.size();
+ MediaCodec.BufferInfo last = infos.peekLast();
+ if (last != null) {
+ mSawOutputEOS |= ((last.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
+ }
+ if (mIBufferSend != null) {
+ IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+ info.buf = mc.getOutputBuffer(outputBufferId);
+ info.idx = outputBufferId;
+ info.obj = mc;
+ info.infos = infos;
+ mIBufferSend.sendBuffer(this, info);
+ } else if (mFrameReleaseQueue != null) {
+ ByteBuffer outputBuffer = mc.getOutputBuffer(outputBufferId);
+ mFrameReleaseQueue.pushFrame(
+ outputBufferId, outputBuffer.remaining());
+ } else {
mc.releaseOutputBuffer(outputBufferId, mRender);
}
if (mSawOutputEOS) {
Log.i(TAG, "Large frame - saw output EOS");
}
- // we don't support frame release queue for large audio frame
}
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 70fa2ca..a1a0634 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -4121,7 +4121,7 @@
frameCountToBeReady,
track->getSpeed(),
1.f /* volume */,
- track->getPortMute() /* muted */);
+ false /* muted */);
status = patchTrack->initCheck();
if (status != NO_ERROR) {
ALOGE("Secondary output patchTrack init failed: %d", status);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 13db5b3..62cb6c7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3556,6 +3556,9 @@
bool enabled,
audio_stream_type_t streamToDriveAbs)
{
+ ALOGI("%s: deviceType 0x%X, enabled %d, streamToDriveAbs %d", __func__, deviceType, enabled,
+ streamToDriveAbs);
+
if (!enabled) {
mAbsoluteVolumeDrivingStreams.erase(deviceType);
return NO_ERROR;
@@ -8312,9 +8315,9 @@
float volumeDb = curves.volIndexToDb(deviceCategory, index);
if (com_android_media_audio_abs_volume_index_fix()) {
- if (mAbsoluteVolumeDrivingStreams.find(volumeDevice) !=
- mAbsoluteVolumeDrivingStreams.end()) {
- audio_attributes_t attributesToDriveAbs = mAbsoluteVolumeDrivingStreams[volumeDevice];
+ const auto it = mAbsoluteVolumeDrivingStreams.find(volumeDevice);
+ if (it != mAbsoluteVolumeDrivingStreams.end()) {
+ audio_attributes_t attributesToDriveAbs = it->second;
auto groupToDriveAbs = mEngine->getVolumeGroupForAttributes(attributesToDriveAbs);
if (groupToDriveAbs == VOLUME_GROUP_NONE) {
ALOGD("%s: no group matching with %s", __FUNCTION__,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 8fbe1cc..0442cae 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -1121,8 +1121,15 @@
Status AudioPolicyService::setDeviceAbsoluteVolumeEnabled(const AudioDevice& deviceAidl,
bool enabled,
AudioStreamType streamToDriveAbsAidl) {
- audio_stream_type_t streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
- aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+ ALOGI("%s: deviceAidl %s, enabled %d, streamToDriveAbsAidl %d", __func__,
+ deviceAidl.toString().c_str(), enabled, streamToDriveAbsAidl);
+
+ audio_stream_type_t streamToDriveAbs = AUDIO_STREAM_DEFAULT;
+ if (enabled) {
+ streamToDriveAbs = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_AudioStreamType_audio_stream_type_t(streamToDriveAbsAidl));
+ }
+
audio_devices_t deviceType;
std::string address;
RETURN_BINDER_STATUS_IF_ERROR(
@@ -1136,9 +1143,7 @@
: settingsAllowed())) {
return binderStatusFromStatusT(PERMISSION_DENIED);
}
- if (uint32_t(streamToDriveAbs) >= AUDIO_STREAM_PUBLIC_CNT) {
- return binderStatusFromStatusT(BAD_VALUE);
- }
+
audio_utils::lock_guard _l(mMutex);
AutoCallerClear acc;
return binderStatusFromStatusT(
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index a35eea1..1fb4e84 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -221,8 +221,8 @@
std::mutex mLock;
std::deque<std::unique_ptr<ProcessCaptureRequestTask>> mQueue GUARDED_BY(mLock);
std::condition_variable mCondVar;
- volatile bool mTextureUpdateRequested GUARDED_BY(mLock);
- volatile bool mPendingExit GUARDED_BY(mLock);
+ volatile bool GUARDED_BY(mLock) mTextureUpdateRequested = false;
+ volatile bool GUARDED_BY(mLock) mPendingExit = false;
// Acquisition timestamp of last frame.
std::atomic<uint64_t> mLastAcquisitionTimestampNanoseconds;