Merge "Move basic tests from postsubmit to presubmit" into tm-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index aa9fd89..b9b9bde 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -124,6 +124,26 @@
// modified by the Soong or platform compat team.
hidden_api: {
max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // The following packages contain classes from other modules on the
+ // bootclasspath. That means that the hidden API flags for this module
+ // has to explicitly list every single class this module provides in
+ // that package to differentiate them from the classes provided by other
+ // modules. That can include private classes that are not part of the
+ // API.
+ split_packages: [
+ "android.media",
+ ],
+
+ // The following packages and all their subpackages currently only
+ // contain classes from this bootclasspath_fragment. Listing a package
+ // here won't prevent other bootclasspath modules from adding classes in
+ // any of those packages but it will prevent them from adding those
+ // classes into an API surface, e.g. public, system, etc.. Doing so will
+ // result in a build failure due to inconsistent flags.
+ package_prefixes: [
+ "android.media.internal",
+ ],
},
}
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index aa40793..c394d5a 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -286,4 +286,11 @@
return mDrmHalHidl->getLogMessages(logs);
}
+status_t DrmHal::getSupportedSchemes(std::vector<uint8_t> &schemes) const {
+ status_t statusResult;
+ statusResult = mDrmHalAidl->getSupportedSchemes(schemes);
+ if (statusResult == OK) return statusResult;
+ return mDrmHalHidl->getSupportedSchemes(schemes);
+}
+
} // namespace android
diff --git a/drm/libmediadrm/DrmHalAidl.cpp b/drm/libmediadrm/DrmHalAidl.cpp
index 284abd5..bdd83e9 100644
--- a/drm/libmediadrm/DrmHalAidl.cpp
+++ b/drm/libmediadrm/DrmHalAidl.cpp
@@ -1189,6 +1189,25 @@
return serializedMetrics;
}
+status_t DrmHalAidl::getSupportedSchemes(std::vector<uint8_t> &schemes) const {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mFactories.empty()) return UNKNOWN_ERROR;
+ for (ssize_t i = mFactories.size() - 1; i >= 0; i--) {
+ CryptoSchemes curSchemes{};
+ auto err = mFactories[i]->getSupportedCryptoSchemes(&curSchemes);
+ if (!err.isOk()) {
+ continue;
+ }
+
+ for (auto uuidObj : curSchemes.uuids) {
+ schemes.insert(schemes.end(), uuidObj.uuid.begin(), uuidObj.uuid.end());
+ }
+ }
+
+ return OK;
+}
+
void DrmHalAidl::cleanup() {
closeOpenSessions();
diff --git a/drm/libmediadrm/DrmHalHidl.cpp b/drm/libmediadrm/DrmHalHidl.cpp
index c83b52b..c38dbef 100644
--- a/drm/libmediadrm/DrmHalHidl.cpp
+++ b/drm/libmediadrm/DrmHalHidl.cpp
@@ -20,6 +20,7 @@
#include <aidl/android/media/BnResourceManagerClient.h>
#include <android/binder_manager.h>
#include <android/hardware/drm/1.2/types.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/ServiceManagement.h>
#include <media/EventMetric.h>
@@ -1514,4 +1515,23 @@
return DrmUtils::GetLogMessages<drm::V1_4::IDrmPlugin>(mPlugin, logs);
}
+status_t DrmHalHidl::getSupportedSchemes(std::vector<uint8_t> &schemes) const {
+ Mutex::Autolock autoLock(mLock);
+ for (auto &factory : mFactories) {
+ sp<drm::V1_3::IDrmFactory> factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory);
+ if (factoryV1_3 == nullptr) {
+ continue;
+ }
+
+ factoryV1_3->getSupportedCryptoSchemes(
+ [&](const hardware::hidl_vec<hardware::hidl_array<uint8_t, 16>>& schemes_hidl) {
+ for (const auto &scheme : schemes_hidl) {
+ schemes.insert(schemes.end(), scheme.data(), scheme.data() + scheme.size());
+ }
+ });
+ }
+
+ return OK;
+}
+
} // namespace android
diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h
index f5e75ac..eab597b 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHal.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHal.h
@@ -117,6 +117,7 @@
Vector<uint8_t> const &sessionId,
const char *playbackId);
virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const;
+ virtual status_t getSupportedSchemes(std::vector<uint8_t> &schemes) const;
private:
sp<IDrm> mDrmHalHidl;
diff --git a/drm/libmediadrm/include/mediadrm/DrmHalAidl.h b/drm/libmediadrm/include/mediadrm/DrmHalAidl.h
index e35140e..0f51ce9 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHalAidl.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHalAidl.h
@@ -105,6 +105,7 @@
bool* required) const;
virtual status_t setPlaybackId(Vector<uint8_t> const& sessionId, const char* playbackId);
virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage>& logs) const;
+ virtual status_t getSupportedSchemes(std::vector<uint8_t> &schemes) const;
::ndk::ScopedAStatus onEvent(EventTypeAidl in_eventType,
const std::vector<uint8_t>& in_sessionId,
diff --git a/drm/libmediadrm/include/mediadrm/DrmHalHidl.h b/drm/libmediadrm/include/mediadrm/DrmHalHidl.h
index 94ef285..11f0608 100644
--- a/drm/libmediadrm/include/mediadrm/DrmHalHidl.h
+++ b/drm/libmediadrm/include/mediadrm/DrmHalHidl.h
@@ -184,6 +184,7 @@
const char *playbackId);
virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const;
+ virtual status_t getSupportedSchemes(std::vector<uint8_t> &schemes) const;
// Methods of IDrmPluginListener
Return<void> sendEvent(EventType eventType,
diff --git a/drm/libmediadrm/include/mediadrm/IDrm.h b/drm/libmediadrm/include/mediadrm/IDrm.h
index a88784d..ee2be6a 100644
--- a/drm/libmediadrm/include/mediadrm/IDrm.h
+++ b/drm/libmediadrm/include/mediadrm/IDrm.h
@@ -165,6 +165,8 @@
virtual status_t getLogMessages(Vector<drm::V1_4::LogMessage> &logs) const = 0;
+ virtual status_t getSupportedSchemes(std::vector<uint8_t> &schemes) const = 0;
+
protected:
IDrm() {}
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
index b7a5686..4f5caec 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
@@ -123,7 +123,7 @@
// matches size limits in codec library
addParameter(
DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
- .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+ .withDefault(new C2StreamPictureSizeInfo::input(0u, 64, 64))
.withFields({
C2F(mSize, width).inRange(2, 1920, 2),
C2F(mSize, height).inRange(2, 1088, 2),
@@ -133,7 +133,7 @@
addParameter(
DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
- .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+ .withDefault(new C2StreamFrameRateInfo::output(0u, 1.))
.withFields({C2F(mFrameRate, value).greaterThan(0.)})
.withSetter(
Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index e0cc5bf..eccbf46 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1127,15 +1127,15 @@
void *data;
size_t size;
- if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0,
+ if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2,
&data, &size)
- && size >= 24) {
- const uint8_t *ptr = (const uint8_t *)data + (size - 24);
+ && size >= 5) {
+ const uint8_t *ptr = (const uint8_t *)data;
const uint8_t profile = ptr[2] >> 1;
- const uint8_t bl_compatibility_id = (ptr[4]) >> 4;
+ const uint8_t blCompatibilityId = (ptr[4]) >> 4;
bool create_two_tracks = false;
- if (bl_compatibility_id && bl_compatibility_id != 15) {
+ if (blCompatibilityId && blCompatibilityId != 15) {
create_two_tracks = true;
}
@@ -1168,11 +1168,11 @@
mLastTrack->next = track_b;
track_b->next = NULL;
- // we want to remove the csd-0 key from the metadata, but
+ // we want to remove the csd-2 key from the metadata, but
// don't have an AMediaFormat_* function to do so. Settle
- // for replacing this csd-0 with an empty csd-0.
+ // for replacing this csd-2 with an empty csd-2.
uint8_t emptybuffer[8] = {};
- AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_0,
+ AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_2,
emptybuffer, 0);
if (4 == profile || 7 == profile || 8 == profile ) {
@@ -1184,8 +1184,6 @@
} else if (10 == profile) {
AMediaFormat_setString(track_b->meta,
AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1);
- AMediaFormat_setBuffer(track_b->meta, AMEDIAFORMAT_KEY_CSD_0,
- data, size - 24);
} // Should never get to else part
mLastTrack = track_b;
@@ -2618,22 +2616,8 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
- void *data = nullptr;
- size_t size = 0;
- if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
- //if csd-0 is already present, then append dvcc
- auto csd0_dvcc = heapbuffer<uint8_t>(size + chunk_data_size);
-
- memcpy(csd0_dvcc.get(), data, size);
- memcpy(csd0_dvcc.get() + size, buffer.get(), chunk_data_size);
-
- AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0,
- csd0_dvcc.get(), size + chunk_data_size);
- } else {
- //if not set csd-0 directly
- AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_0,
+ AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2,
buffer.get(), chunk_data_size);
- }
AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME,
MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
@@ -4511,12 +4495,12 @@
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
void *data;
size_t size;
- if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)
- || size < 24) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)
+ || size != 24) {
return NULL;
}
- const uint8_t *ptr = (const uint8_t *)data + (size - 24);
+ const uint8_t *ptr = (const uint8_t *)data;
// dv_major.dv_minor Should be 1.0 or 2.1
if ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)) {
return NULL;
@@ -4596,7 +4580,7 @@
return ERROR_MALFORMED;
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
- if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
+ if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
return ERROR_MALFORMED;
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
@@ -5172,11 +5156,11 @@
ALOGV("%s DolbyVision stream detected", __FUNCTION__);
void *data;
size_t size;
- CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_0, &data, &size));
+ CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size));
- const uint8_t *ptr = (const uint8_t *)data + (size - 24);
+ const uint8_t *ptr = (const uint8_t *)data;
- CHECK(size >= 24);
+ CHECK(size == 24);
// dv_major.dv_minor Should be 1.0 or 2.1
CHECK(!((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)));
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index 6afe023..10da028 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -227,4 +227,9 @@
int getAAudioHardwareBurstMinUsec();
void setDeviceConnectedState(in AudioPort devicePort, boolean connected);
+
+ // When adding a new method, please review and update
+ // IAudioFlinger.h AudioFlingerServerAdapter::Delegate::TransactionCode
+ // AudioFlinger.cpp AudioFlinger::onTransactWrapper()
+ // AudioFlinger.cpp IAUDIOFLINGER_BINDER_METHOD_MACRO_LIST
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index e2ef772..8ac89a8 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -390,4 +390,8 @@
* for the specified audio attributes.
*/
AudioProfile[] getDirectProfilesForAttributes(in AudioAttributesInternal attr);
+
+ // When adding a new method, please review and update
+ // AudioPolicyService.cpp AudioPolicyService::onTransact()
+ // AudioPolicyService.cpp IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST
}
diff --git a/media/libaudioclient/aidl/android/media/IEffect.aidl b/media/libaudioclient/aidl/android/media/IEffect.aidl
index 813cd5c..6ec0405 100644
--- a/media/libaudioclient/aidl/android/media/IEffect.aidl
+++ b/media/libaudioclient/aidl/android/media/IEffect.aidl
@@ -62,4 +62,8 @@
* TODO(ytai): Explain how this should be used exactly.
*/
SharedFileRegion getCblk();
+
+ // When adding a new method, please review and update
+ // Effects.cpp AudioFlinger::EffectHandle::onTransact()
+ // Effects.cpp IEFFECT_BINDER_METHOD_MACRO_LIST
}
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index e047378..3c3715d 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -482,9 +482,9 @@
* Legacy server should implement this interface in order to be wrapped.
*/
class Delegate : public IAudioFlinger {
- protected:
friend class AudioFlingerServerAdapter;
-
+ public:
+ // expose the TransactionCode enum for TimeCheck purposes.
enum class TransactionCode {
CREATE_TRACK = media::BnAudioFlingerService::TRANSACTION_createTrack,
CREATE_RECORD = media::BnAudioFlingerService::TRANSACTION_createRecord,
@@ -553,6 +553,7 @@
SET_DEVICE_CONNECTED_STATE = media::BnAudioFlingerService::TRANSACTION_setDeviceConnectedState,
};
+ protected:
/**
* And optional hook, called on every transaction, allowing additional operations to be
* performed before/after the unparceling ofthe data and dispatching to the respective
diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp
index dd435fe..4002fbf 100644
--- a/media/libaudiohal/impl/Android.bp
+++ b/media/libaudiohal/impl/Android.bp
@@ -143,8 +143,9 @@
":audio_core_hal_client_sources",
":audio_effect_hal_client_sources",
],
- shared_libs: [
+ static_libs: [
"android.hardware.audio.common@7.0",
+ "android.hardware.audio.common@7.0-enums",
"android.hardware.audio.common@7.0-util",
"android.hardware.audio.effect@7.0",
"android.hardware.audio.effect@7.0-util",
@@ -164,8 +165,9 @@
srcs: [
":audio_core_hal_client_sources",
],
- shared_libs: [
+ static_libs: [
"android.hardware.audio.common@7.0",
+ "android.hardware.audio.common@7.1-enums",
"android.hardware.audio.common@7.1-util",
"android.hardware.audio@7.0",
"android.hardware.audio@7.1",
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index 4247375..90472eb 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -140,6 +140,8 @@
#define AMEDIAMETRICS_PROP_INTERVALCOUNT "intervalCount" // int32
#define AMEDIAMETRICS_PROP_LATENCYMS "latencyMs" // double value
#define AMEDIAMETRICS_PROP_LOGSESSIONID "logSessionId" // hex string, "" none
+#define AMEDIAMETRICS_PROP_METHODCODE "methodCode" // int64_t an int indicating method
+#define AMEDIAMETRICS_PROP_METHODNAME "methodName" // string method name
#define AMEDIAMETRICS_PROP_NAME "name" // string value
#define AMEDIAMETRICS_PROP_ORIGINALFLAGS "originalFlags" // int32
#define AMEDIAMETRICS_PROP_OUTPUTDEVICES "outputDevices" // string value
@@ -224,6 +226,7 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME "setVolume" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_START "start" // AudioTrack, AudioRecord
#define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP "stop" // AudioTrack, AudioRecord
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_TIMEOUT "timeout" // AudioFlinger, AudioPolicy
#define AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN "underrun" // from Thread
// Possible values for AMEDIAMETRICS_PROP_CALLERNAME
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f9e6c7b..63d3180 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -36,6 +36,7 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ALookup.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
@@ -44,6 +45,7 @@
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
#include <media/mediarecorder.h>
@@ -372,9 +374,7 @@
uint8_t mProfileCompatible;
uint8_t mLevelIdc;
- uint8_t mDoviProfile;
- void *mDoviConfigData;
- size_t mDoviConfigDataSize;
+ int32_t mDoviProfile;
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
@@ -428,7 +428,7 @@
status_t parseHEVCCodecSpecificData(
const uint8_t *data, size_t size, HevcParameterSets ¶mSets);
- status_t makeDoviCodecSpecificData();
+ status_t getDolbyVisionProfile();
// Track authoring progress status
void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -628,14 +628,14 @@
}
const char *MPEG4Writer::Track::getDoviFourCC() const {
- if (mDoviProfile == 5) {
+ if (mDoviProfile == DolbyVisionProfileDvheStn) {
return "dvh1";
- } else if (mDoviProfile == 8) {
+ } else if (mDoviProfile == DolbyVisionProfileDvheSt) {
return "hvc1";
- } else if (mDoviProfile == 9 || mDoviProfile == 32) {
+ } else if (mDoviProfile == DolbyVisionProfileDvavSe) {
return "avc1";
}
- return (const char*)NULL;
+ return nullptr;
}
// static
@@ -693,6 +693,11 @@
}
if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+ // For MEDIA_MIMETYPE_VIDEO_DOLBY_VISION,
+ // getFourCCForMime() requires profile information
+ // to decide the final FourCC codes.
+ // So we let the creation of the new track now and
+ // assign FourCC codes later using getDoviFourCC()
ALOGV("Add source mime '%s'", mime);
} else if (Track::getFourCCForMime(mime) == NULL) {
ALOGE("Unsupported mime '%s'", mime);
@@ -2173,8 +2178,7 @@
mMinCttsOffsetTimeUs(0),
mMinCttsOffsetTicks(0),
mMaxCttsOffsetTicks(0),
- mDoviConfigData(NULL),
- mDoviConfigDataSize(0),
+ mDoviProfile(0),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
mGotAllCodecSpecificData(false),
@@ -2636,7 +2640,7 @@
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
mMeta->findData(kKeyHVCC, &type, &data, &size);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
- makeDoviCodecSpecificData();
+ getDolbyVisionProfile();
if (!mMeta->findData(kKeyAVCC, &type, &data, &size) &&
!mMeta->findData(kKeyHVCC, &type, &data, &size)) {
ALOGE("Failed: No HVCC/AVCC for Dolby Vision ..\n");
@@ -2683,10 +2687,6 @@
mCodecSpecificData = NULL;
}
- if (mDoviConfigData != NULL) {
- free(mDoviConfigData);
- mDoviConfigData = NULL;
- }
}
void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
@@ -3365,34 +3365,37 @@
return OK;
}
-status_t MPEG4Writer::Track::makeDoviCodecSpecificData() {
+status_t MPEG4Writer::Track::getDolbyVisionProfile() {
uint32_t type;
const void *data = NULL;
size_t size = 0;
- if (mDoviConfigData != NULL) {
- ALOGE("Already have Dolby Vision codec specific data");
- return OK;
+ if (!mMeta->findData(kKeyDVCC, &type, &data, &size) &&
+ !mMeta->findData(kKeyDVVC, &type, &data, &size) &&
+ !mMeta->findData(kKeyDVWC, &type, &data, &size)) {
+ ALOGE("Failed getting Dovi config for Dolby Vision %d", (int)size);
+ return ERROR_MALFORMED;
}
+ static const ALookup<uint8_t, int32_t> dolbyVisionProfileMap = {
+ {1, DolbyVisionProfileDvavPen},
+ {3, DolbyVisionProfileDvheDen},
+ {4, DolbyVisionProfileDvheDtr},
+ {5, DolbyVisionProfileDvheStn},
+ {6, DolbyVisionProfileDvheDth},
+ {7, DolbyVisionProfileDvheDtb},
+ {8, DolbyVisionProfileDvheSt},
+ {9, DolbyVisionProfileDvavSe},
+ {10, DolbyVisionProfileDvav110}
+ };
- if (!mMeta->findData(kKeyDVCC, &type, &data, &size)
- && !mMeta->findData(kKeyDVVC, &type, &data, &size)
- && !mMeta->findData(kKeyDVWC, &type, &data, &size)) {
- ALOGE("Failed getting Dovi config for Dolby Vision %d", (int)size);
- return ERROR_MALFORMED;
+ // Dolby Vision profile information is extracted as per
+ // https://dolby.my.salesforce.com/sfc/p/#700000009YuG/a/4u000000l6FB/076wHYEmyEfz09m0V1bo85_25hlUJjaiWTbzorNmYY4
+ uint8_t dv_profile = ((((uint8_t *)data)[2] >> 1) & 0x7f);
+
+ if (!dolbyVisionProfileMap.map(dv_profile, &mDoviProfile)) {
+ ALOGE("Failed to get Dolby Profile from DV Config data");
+ return ERROR_MALFORMED;
}
-
- mDoviConfigData = malloc(size);
- if (mDoviConfigData == NULL) {
- ALOGE("Failed allocating Dolby Vision config data");
- return ERROR_MALFORMED;
- }
-
- mDoviConfigDataSize = size;
- memcpy(mDoviConfigData, data, size);
-
- mDoviProfile = (((char *)data)[2] >> 1) & 0x7f; //getting profile info
-
return OK;
}
@@ -3542,24 +3545,26 @@
buffer->range_length());
}
if (mIsDovi) {
- err = makeDoviCodecSpecificData();
-
- const void *data = NULL;
- size_t size = 0;
-
- uint32_t type = 0;
- if (mDoviProfile == 9){
- mMeta->findData(kKeyAVCC, &type, &data, &size);
- } else if (mDoviProfile < 9) {
- mMeta->findData(kKeyHVCC, &type, &data, &size);
- }
-
- if (data != NULL && copyCodecSpecificData((uint8_t *)data, size) == OK) {
- mGotAllCodecSpecificData = true;
+ err = getDolbyVisionProfile();
+ if(err == OK) {
+ const void *data = NULL;
+ size_t size = 0;
+ uint32_t type = 0;
+ if (mDoviProfile == DolbyVisionProfileDvavSe) {
+ mMeta->findData(kKeyAVCC, &type, &data, &size);
+ } else if (mDoviProfile < DolbyVisionProfileDvavSe) {
+ mMeta->findData(kKeyHVCC, &type, &data, &size);
+ } else {
+ ALOGW("DV Profiles > DolbyVisionProfileDvavSe are not supported");
+ err = ERROR_MALFORMED;
+ }
+ if (err == OK && data != NULL &&
+ copyCodecSpecificData((uint8_t *)data, size) == OK) {
+ mGotAllCodecSpecificData = true;
+ }
}
}
}
-
buffer->release();
buffer = NULL;
if (OK != err) {
@@ -4429,10 +4434,12 @@
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
writeHvccBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime)) {
- if (mDoviProfile <= 8) {
+ if (mDoviProfile <= DolbyVisionProfileDvheSt) {
writeHvccBox();
- } else if (mDoviProfile == 9 || mDoviProfile == 32) {
+ } else if (mDoviProfile == DolbyVisionProfileDvavSe) {
writeAvccBox();
+ } else {
+ TRESPASS("Unsupported Dolby Vision profile");
}
writeDoviConfigBox();
}
@@ -4994,21 +5001,29 @@
}
void MPEG4Writer::Track::writeDoviConfigBox() {
- CHECK(mDoviConfigData);
- CHECK_EQ(mDoviConfigDataSize, 24u);
+ CHECK_NE(mDoviProfile, 0u);
- uint8_t *ptr = (uint8_t *)mDoviConfigData;
- uint8_t profile = (ptr[2] >> 1) & 0x7f;
+ uint32_t type = 0;
+ const void *data = nullptr;
+ size_t size = 0;
+ // check to see which key has the configuration box.
+ if (mMeta->findData(kKeyDVCC, &type, &data, &size) ||
+ mMeta->findData(kKeyDVVC, &type, &data, &size) ||
+ mMeta->findData(kKeyDVWC, &type, &data, &size)) {
- if (profile > 10) {
- mOwner->beginBox("dvwC");
- } else if (profile > 7) {
- mOwner->beginBox("dvvC");
- } else {
- mOwner->beginBox("dvcC");
+ // if this box is present we write the box, or
+ // this mp4 will be interpreted as a backward
+ // compatible stream.
+ if (mDoviProfile > DolbyVisionProfileDvav110) {
+ mOwner->beginBox("dvwC");
+ } else if (mDoviProfile > DolbyVisionProfileDvheDtb) {
+ mOwner->beginBox("dvvC");
+ } else {
+ mOwner->beginBox("dvcC");
+ }
+ mOwner->write(data, size);
+ mOwner->endBox(); // dvwC/dvvC/dvcC
}
- mOwner->write(mDoviConfigData, mDoviConfigDataSize);
- mOwner->endBox(); // dvwC/dvvC/dvcC
}
void MPEG4Writer::Track::writeD263Box() {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 1854588..4b6470a 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -243,6 +243,39 @@
}
}
+static const ALookup<uint8_t, int32_t>& getDolbyVisionProfileTable() {
+ static const ALookup<uint8_t, int32_t> profileTable = {
+ {1, DolbyVisionProfileDvavPen},
+ {3, DolbyVisionProfileDvheDen},
+ {4, DolbyVisionProfileDvheDtr},
+ {5, DolbyVisionProfileDvheStn},
+ {6, DolbyVisionProfileDvheDth},
+ {7, DolbyVisionProfileDvheDtb},
+ {8, DolbyVisionProfileDvheSt},
+ {9, DolbyVisionProfileDvavSe},
+ {10, DolbyVisionProfileDvav110},
+ };
+ return profileTable;
+}
+
+static const ALookup<uint8_t, int32_t>& getDolbyVisionLevelsTable() {
+ static const ALookup<uint8_t, int32_t> levelsTable = {
+ {0, DolbyVisionLevelUnknown},
+ {1, DolbyVisionLevelHd24},
+ {2, DolbyVisionLevelHd30},
+ {3, DolbyVisionLevelFhd24},
+ {4, DolbyVisionLevelFhd30},
+ {5, DolbyVisionLevelFhd60},
+ {6, DolbyVisionLevelUhd24},
+ {7, DolbyVisionLevelUhd30},
+ {8, DolbyVisionLevelUhd48},
+ {9, DolbyVisionLevelUhd60},
+ {10, DolbyVisionLevelUhd120},
+ {11, DolbyVisionLevel8k30},
+ {12, DolbyVisionLevel8k60},
+ };
+ return levelsTable;
+}
static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) {
// dv_major.dv_minor Should be 1.0 or 2.1
if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
@@ -262,33 +295,9 @@
// All Dolby Profiles will have profile and level info in MediaFormat
// Profile 8 and 9 will have bl_compatibility_id too.
- const static ALookup<uint8_t, int32_t> profiles{
- {1, DolbyVisionProfileDvavPen},
- {3, DolbyVisionProfileDvheDen},
- {4, DolbyVisionProfileDvheDtr},
- {5, DolbyVisionProfileDvheStn},
- {6, DolbyVisionProfileDvheDth},
- {7, DolbyVisionProfileDvheDtb},
- {8, DolbyVisionProfileDvheSt},
- {9, DolbyVisionProfileDvavSe},
- {10, DolbyVisionProfileDvav110},
- };
+ const ALookup<uint8_t, int32_t> &profiles = getDolbyVisionProfileTable();
+ const ALookup<uint8_t, int32_t> &levels = getDolbyVisionLevelsTable();
- const static ALookup<uint8_t, int32_t> levels{
- {0, DolbyVisionLevelUnknown},
- {1, DolbyVisionLevelHd24},
- {2, DolbyVisionLevelHd30},
- {3, DolbyVisionLevelFhd24},
- {4, DolbyVisionLevelFhd30},
- {5, DolbyVisionLevelFhd60},
- {6, DolbyVisionLevelUhd24},
- {7, DolbyVisionLevelUhd30},
- {8, DolbyVisionLevelUhd48},
- {9, DolbyVisionLevelUhd60},
- {10, DolbyVisionLevelUhd120},
- {11, DolbyVisionLevel8k30},
- {12, DolbyVisionLevel8k60},
- };
// set rpuAssoc
if (rpu_present_flag && el_present_flag && !bl_present_flag) {
format->setInt32("rpuAssoc", 1);
@@ -1516,30 +1525,18 @@
if (meta->findData(kKeyDVCC, &type, &data, &size)
|| meta->findData(kKeyDVVC, &type, &data, &size)
|| meta->findData(kKeyDVWC, &type, &data, &size)) {
- sp<ABuffer> buffer, csdOrg;
- if (msg->findBuffer("csd-0", &csdOrg)) {
- buffer = new (std::nothrow) ABuffer(size + csdOrg->size());
- if (buffer.get() == NULL || buffer->base() == NULL) {
- return NO_MEMORY;
- }
-
- memcpy(buffer->data(), csdOrg->data(), csdOrg->size());
- memcpy(buffer->data() + csdOrg->size(), data, size);
- } else {
- buffer = new (std::nothrow) ABuffer(size);
- if (buffer.get() == NULL || buffer->base() == NULL) {
- return NO_MEMORY;
- }
- memcpy(buffer->data(), data, size);
- }
-
- buffer->meta()->setInt32("csd", true);
- buffer->meta()->setInt64("timeUs", 0);
- msg->setBuffer("csd-0", buffer);
-
const uint8_t *ptr = (const uint8_t *)data;
ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size);
parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg);
+ sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+ if (buffer.get() == nullptr || buffer->base() == nullptr) {
+ return NO_MEMORY;
+ }
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-2", buffer);
}
*format = msg;
@@ -2041,133 +2038,146 @@
mime == MEDIA_MIMETYPE_IMAGE_AVIF) {
meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
- int32_t needCreateDoviCSD = 0;
- int32_t profile = 0;
- uint8_t bl_compatibility = 0;
- if (msg->findInt32("profile", &profile)) {
- if (profile == DolbyVisionProfileDvheSt) {
- profile = 8;
- bl_compatibility = 4;
- } else if (profile == DolbyVisionProfileDvavSe) {
- profile = 9;
- bl_compatibility = 2;
- }
- if (profile == 8 || profile == 9) {
- needCreateDoviCSD = 1;
- }
- } else {
- ALOGW("did not find dolby vision profile");
- }
- // No dovi csd data, need to create it
- if (needCreateDoviCSD) {
- uint8_t dvcc[24];
- int32_t level = 0;
- uint8_t level_val = 0;
+ int32_t profile = -1;
+ uint8_t blCompatibilityId = -1;
+ int32_t level = 0;
+ uint8_t profileVal = -1;
+ uint8_t profileVal1 = -1;
+ uint8_t profileVal2 = -1;
+ constexpr size_t dvccSize = 24;
- if (msg->findInt32("level", &level)) {
- const static ALookup<int32_t, uint8_t> levels {
- {DolbyVisionLevelUnknown, 0},
- {DolbyVisionLevelHd24, 1},
- {DolbyVisionLevelHd30, 2},
- {DolbyVisionLevelFhd24, 3},
- {DolbyVisionLevelFhd30, 4},
- {DolbyVisionLevelFhd60, 5},
- {DolbyVisionLevelUhd24, 6},
- {DolbyVisionLevelUhd30, 7},
- {DolbyVisionLevelUhd48, 8},
- {DolbyVisionLevelUhd60, 9},
- {DolbyVisionLevelUhd120, 10},
- {DolbyVisionLevel8k30, 11},
- {DolbyVisionLevel8k60, 12},
- };
- levels.map(level, &level_val);
- ALOGV("found dolby vision level: %d, value: %d", level, level_val);
+ const ALookup<uint8_t, int32_t> &profiles =
+ getDolbyVisionProfileTable();
+ const ALookup<uint8_t, int32_t> &levels =
+ getDolbyVisionLevelsTable();
+
+ if (!msg->findBuffer("csd-2", &csd2)) {
+ // MP4 extractors are expected to generate csd buffer
+ // some encoders might not be generating it, in which
+ // case we populate the track metadata dv (cc|vc|wc)
+ // from the 'profile' and 'level' info.
+ // This is done according to Dolby Vision ISOBMFF spec
+
+ if (!msg->findInt32("profile", &profile)) {
+ ALOGE("Dolby Vision profile not found");
+ return BAD_VALUE;
}
+ msg->findInt32("level", &level);
+
+ if (profile == DolbyVisionProfileDvheSt) {
+ if (!profiles.rlookup(DolbyVisionProfileDvheSt, &profileVal)) { // dvhe.08
+ ALOGE("Dolby Vision profile lookup error");
+ return BAD_VALUE;
+ }
+ blCompatibilityId = 4;
+ } else if (profile == DolbyVisionProfileDvavSe) {
+ if (!profiles.rlookup(DolbyVisionProfileDvavSe, &profileVal)) { // dvav.09
+ ALOGE("Dolby Vision profile lookup error");
+ return BAD_VALUE;
+ }
+ blCompatibilityId = 2;
+ } else {
+ ALOGE("Dolby Vision profile look up error");
+ return BAD_VALUE;
+ }
+
+ profile = (int32_t) profileVal;
+
+ uint8_t level_val = 0;
+ if (!levels.map(level, &level_val)) {
+ ALOGE("Dolby Vision level lookup error");
+ return BAD_VALUE;
+ }
+
+ std::vector<uint8_t> dvcc(dvccSize);
dvcc[0] = 1; // major version
dvcc[1] = 0; // minor version
- dvcc[2] = (uint8_t)((profile & 0x7f) << 1);// dolby vision profile
+ dvcc[2] = (uint8_t)((profile & 0x7f) << 1); // dolby vision profile
dvcc[2] = (uint8_t)((dvcc[2] | (uint8_t)((level_val >> 5) & 0x1)) & 0xff);
dvcc[3] = (uint8_t)((level_val & 0x1f) << 3); // dolby vision level
dvcc[3] = (uint8_t)(dvcc[3] | (1 << 2)); // rpu_present_flag
dvcc[3] = (uint8_t)(dvcc[3] | (1)); // bl_present_flag
- dvcc[4] = (uint8_t)(bl_compatibility << 4);// bl_compatibility id
+ dvcc[4] = (uint8_t)(blCompatibilityId << 4); // bl_compatibility id
- std::vector<uint8_t> dvcc_data(24);
- memcpy(dvcc_data.data(), dvcc, 24);
- if (profile > 10) {
- meta->setData(kKeyDVWC, kTypeDVWC, dvcc_data.data(), 24);
- } else if (profile > 7) {
- meta->setData(kKeyDVVC, kTypeDVVC, dvcc_data.data(), 24);
+ profiles.rlookup(DolbyVisionProfileDvav110, &profileVal);
+ profiles.rlookup(DolbyVisionProfileDvheDtb, &profileVal1);
+ if (profile > (int32_t) profileVal) {
+ meta->setData(kKeyDVWC, kTypeDVWC, dvcc.data(), dvccSize);
+ } else if (profile > (int32_t) profileVal1) {
+ meta->setData(kKeyDVVC, kTypeDVVC, dvcc.data(), dvccSize);
} else {
- meta->setData(kKeyDVCC, kTypeDVCC, dvcc_data.data(), 24);
+ meta->setData(kKeyDVCC, kTypeDVCC, dvcc.data(), dvccSize);
}
- } else if (csd0size >= 24) { // have dovi csd, just send it out...
- uint8_t *dvconfig = csd0->data() + (csd0size -24);
- profile = dvconfig[2] >> 1;
- if (profile > 10) {
- meta->setData(kKeyDVWC, kTypeDVWC, dvconfig, 24);
- } else if (profile > 7) {
- meta->setData(kKeyDVVC, kTypeDVVC, dvconfig, 24);
- } else {
- meta->setData(kKeyDVCC, kTypeDVCC, dvconfig, 24);
- }
+
} else {
- return BAD_VALUE;
+ // we have csd-2, just use that to populate dvcc
+ if (csd2->size() == dvccSize) {
+ uint8_t *dvcc = csd2->data();
+ profile = dvcc[2] >> 1;
+
+ profiles.rlookup(DolbyVisionProfileDvav110, &profileVal);
+ profiles.rlookup(DolbyVisionProfileDvheDtb, &profileVal1);
+ if (profile > (int32_t) profileVal) {
+ meta->setData(kKeyDVWC, kTypeDVWC, csd2->data(), csd2->size());
+ } else if (profile > (int32_t) profileVal1) {
+ meta->setData(kKeyDVVC, kTypeDVVC, csd2->data(), csd2->size());
+ } else {
+ meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
+ }
+
+ } else {
+ ALOGE("Convert MessageToMetadata csd-2 is present but not valid");
+ return BAD_VALUE;
+ }
}
-
- // Send the avc/hevc/av1 csd data...
- if (csd0size >= 24) {
- sp<ABuffer> csd;
- if ( profile > 1 && profile < 9) {
- if (msg->findBuffer("csd-hevc", &csd)) {
- meta->setData(kKeyHVCC, kTypeHVCC, csd->data(), csd->size());
- } else if (csd0size > 24) {
- std::vector<uint8_t> hvcc(csd0size + 1024);
- size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
- meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
- }
- } else if (profile == 9) {
- sp<ABuffer> csd1;
- if (msg->findBuffer("csd-avc", &csd)) {
- meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
- } else if (msg->findBuffer("csd-1", &csd1)) {
- std::vector<char> avcc(csd0size + csd1->size() + 1024);
- size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
- meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
- } else { // for dolby vision avc, csd0 also holds csd1
- size_t i = 0;
- int csd0realsize = 0;
- do {
- i = findNextNalStartCode(csd0->data() + i,
- csd0->size() - i) - csd0->data();
- if (i > 0) {
- csd0realsize = i;
- break;
- }
- i += 4;
- } while(i < csd0->size());
- // buffer0 -> csd0
- sp<ABuffer> buffer0 = new (std::nothrow) ABuffer(csd0realsize);
- if (buffer0.get() == NULL || buffer0->base() == NULL) {
- return NO_MEMORY;
+ profiles.rlookup(DolbyVisionProfileDvavPen, &profileVal);
+ profiles.rlookup(DolbyVisionProfileDvavSe, &profileVal1);
+ profiles.rlookup(DolbyVisionProfileDvav110, &profileVal2);
+ if ((profile > (int32_t) profileVal) && (profile < (int32_t) profileVal1)) {
+ std::vector<uint8_t> hvcc(csd0size + 1024);
+ size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
+ meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
+ } else if (profile == (int32_t) profileVal2) {
+ meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
+ } else {
+ sp<ABuffer> csd1;
+ if (msg->findBuffer("csd-1", &csd1)) {
+ std::vector<char> avcc(csd0size + csd1->size() + 1024);
+ size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
+ meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
+ }
+ else {
+ // for dolby vision avc, csd0 also holds csd1
+ size_t i = 0;
+ int csd0realsize = 0;
+ do {
+ i = findNextNalStartCode(csd0->data() + i,
+ csd0->size() - i) - csd0->data();
+ if (i > 0) {
+ csd0realsize = i;
+ break;
}
- memcpy(buffer0->data(), csd0->data(), csd0realsize);
- // buffer1 -> csd1
- sp<ABuffer> buffer1 = new (std::nothrow)
- ABuffer(csd0->size() - csd0realsize);
- if (buffer1.get() == NULL || buffer1->base() == NULL) {
- return NO_MEMORY;
- }
- memcpy(buffer1->data(), csd0->data()+csd0realsize,
- csd0->size() - csd0realsize);
-
- std::vector<char> avcc(csd0->size() + 1024);
- size_t outsize = reassembleAVCC(buffer0, buffer1, avcc.data());
- meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
+ i += 4;
+ } while(i < csd0->size());
+ // buffer0 -> csd0
+ sp<ABuffer> buffer0 = new (std::nothrow) ABuffer(csd0realsize);
+ if (buffer0.get() == NULL || buffer0->base() == NULL) {
+ return NO_MEMORY;
}
- } else if (profile == 10) {
- meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size() - 24);
+ memcpy(buffer0->data(), csd0->data(), csd0realsize);
+ // buffer1 -> csd1
+ sp<ABuffer> buffer1 = new (std::nothrow)
+ ABuffer(csd0->size() - csd0realsize);
+ if (buffer1.get() == NULL || buffer1->base() == NULL) {
+ return NO_MEMORY;
+ }
+ memcpy(buffer1->data(), csd0->data()+csd0realsize,
+ csd0->size() - csd0realsize);
+
+ std::vector<char> avcc(csd0->size() + 1024);
+ size_t outsize = reassembleAVCC(buffer0, buffer1, avcc.data());
+ meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
}
}
} else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
@@ -2216,6 +2226,17 @@
meta->setData(kKeyStreamHeader, 'mdat', csd0->data(), csd0->size());
} else if (msg->findBuffer("d263", &csd0)) {
meta->setData(kKeyD263, kTypeD263, csd0->data(), csd0->size());
+ } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION && msg->findBuffer("csd-2", &csd2)) {
+ meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
+
+ // Remove CSD-2 from the data here to avoid duplicate data in meta
+ meta->remove(kKeyOpaqueCSD2);
+
+ if (msg->findBuffer("csd-avc", &csd0)) {
+ meta->setData(kKeyAVCC, kTypeAVCC, csd0->data(), csd0->size());
+ } else if (msg->findBuffer("csd-hevc", &csd0)) {
+ meta->setData(kKeyHVCC, kTypeHVCC, csd0->data(), csd0->size());
+ }
}
// XXX TODO add whatever other keys there are
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index ba8f199..569ea2a 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -43,7 +43,7 @@
],
shared_libs: [
"libaudioclient_aidl_conversion",
- "libaudioutils", // for clock.h
+ "libaudioutils", // for clock.h, Statistics.h
"libbinder",
"libcutils",
"liblog",
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 2b765cc..5f269af 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -24,7 +24,7 @@
#include <utils/Log.h>
#include "debuggerd/handler.h"
-namespace android {
+namespace android::mediautils {
namespace {
@@ -48,7 +48,7 @@
void TimeCheck::accessAudioHalPids(std::vector<pid_t>* pids, bool update) {
static constexpr int kNumAudioHalPidsVectors = 3;
static std::vector<pid_t> audioHalPids[kNumAudioHalPidsVectors];
- static std::atomic<int> curAudioHalPids = 0;
+ static std::atomic<unsigned> curAudioHalPids = 0;
if (update) {
audioHalPids[(curAudioHalPids++ + 1) % kNumAudioHalPidsVectors] = *pids;
@@ -70,27 +70,54 @@
}
/* static */
-TimerThread* TimeCheck::getTimeCheckThread() {
- static TimerThread* sTimeCheckThread = new TimerThread();
+TimerThread& TimeCheck::getTimeCheckThread() {
+ static TimerThread sTimeCheckThread{};
return sTimeCheckThread;
}
-TimeCheck::TimeCheck(const char* tag, uint32_t timeoutMs)
- : mTimerHandle(getTimeCheckThread()->scheduleTask(
- [tag, startTime = std::chrono::system_clock::now()] { crash(tag, startTime); },
+TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
+ bool crashOnTimeout)
+ : mTimeCheckHandler(new TimeCheckHandler{
+ std::move(tag), std::move(onTimer), crashOnTimeout,
+ std::chrono::system_clock::now(), gettid()})
+ , mTimerHandle(getTimeCheckThread().scheduleTask(
+ // Pass in all the arguments by value to this task for safety.
+ // The thread could call the callback before the constructor is finished.
+ // The destructor will be blocked on the callback, but that is implementation
+ // dependent.
+ [ timeCheckHandler = mTimeCheckHandler ] {
+ timeCheckHandler->onTimeout();
+ },
std::chrono::milliseconds(timeoutMs))) {}
TimeCheck::~TimeCheck() {
- getTimeCheckThread()->cancelTask(mTimerHandle);
+ mTimeCheckHandler->onCancel(mTimerHandle);
}
-/* static */
-void TimeCheck::crash(const char* tag, std::chrono::system_clock::time_point startTime) {
- std::chrono::system_clock::time_point endTime = std::chrono::system_clock::now();
+void TimeCheck::TimeCheckHandler::onCancel(TimerThread::Handle timerHandle) const
+{
+ if (TimeCheck::getTimeCheckThread().cancelTask(timerHandle) && onTimer) {
+ const std::chrono::system_clock::time_point endTime = std::chrono::system_clock::now();
+ onTimer(false /* timeout */,
+ std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
+ endTime - startTime).count());
+ }
+}
+
+void TimeCheck::TimeCheckHandler::onTimeout() const
+{
+ const std::chrono::system_clock::time_point endTime = std::chrono::system_clock::now();
+ if (onTimer) {
+ onTimer(true /* timeout */,
+ std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
+ endTime - startTime).count());
+ }
+
+ if (!crashOnTimeout) return;
// Generate audio HAL processes tombstones and allow time to complete
// before forcing restart
- std::vector<pid_t> pids = getAudioHalPids();
+ std::vector<pid_t> pids = TimeCheck::getAudioHalPids();
if (pids.size() != 0) {
for (const auto& pid : pids) {
ALOGI("requesting tombstone for pid: %d", pid);
@@ -100,9 +127,9 @@
} else {
ALOGI("No HAL process pid available, skipping tombstones");
}
- LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag);
- LOG_ALWAYS_FATAL("TimeCheck timeout for %s (start=%s, end=%s)", tag,
- formatTime(startTime).c_str(), formatTime(endTime).c_str());
+ LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag.c_str());
+ LOG_ALWAYS_FATAL("TimeCheck timeout for %s on thread %d (start=%s, end=%s)",
+ tag.c_str(), tid, formatTime(startTime).c_str(), formatTime(endTime).c_str());
}
-}; // namespace android
+} // namespace android::mediautils
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index 3c95798..dfc84e1 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -50,9 +50,12 @@
return deadline;
}
-void TimerThread::cancelTask(Handle handle) {
+// Returns true if cancelled, false if handle doesn't exist.
+// Beware of lock inversion with cancelTask() as the callback
+// is called while holding mMutex.
+bool TimerThread::cancelTask(Handle handle) {
std::lock_guard _l(mMutex);
- mMonitorRequests.erase(handle);
+ return mMonitorRequests.erase(handle) != 0;
}
void TimerThread::threadFunc() {
diff --git a/media/utils/fuzzers/TimeCheckFuzz.cpp b/media/utils/fuzzers/TimeCheckFuzz.cpp
index eeb6ba6..7966469 100644
--- a/media/utils/fuzzers/TimeCheckFuzz.cpp
+++ b/media/utils/fuzzers/TimeCheckFuzz.cpp
@@ -44,11 +44,11 @@
// 2. We also have setAudioHalPids, which is populated with the pids set
// above.
- android::TimeCheck::setAudioHalPids(pids);
+ android::mediautils::TimeCheck::setAudioHalPids(pids);
std::string name = data_provider.ConsumeRandomLengthString(kMaxStringLen);
// 3. The constructor, which is fuzzed here:
- android::TimeCheck timeCheck(name.c_str(), timeoutMs);
+ android::mediautils::TimeCheck timeCheck(name.c_str(), {} /* onTimer */, timeoutMs);
// We will leave some buffer to avoid sleeping too long
uint8_t sleep_amount_ms = data_provider.ConsumeIntegralInRange<uint8_t>(0, timeoutMs / 2);
diff --git a/media/utils/include/mediautils/MethodStatistics.h b/media/utils/include/mediautils/MethodStatistics.h
new file mode 100644
index 0000000..7d8061d
--- /dev/null
+++ b/media/utils/include/mediautils/MethodStatistics.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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 <map>
+#include <mutex>
+#include <string>
+
+#include <android-base/thread_annotations.h>
+#include <audio_utils/Statistics.h>
+
+namespace android::mediautils {
+
+/**
+ * MethodStatistics is used to associate Binder codes
+ * with a method name and execution time statistics.
+ *
+ * This is used to track binder transaction times for
+ * AudioFlinger and AudioPolicy services.
+ *
+ * Here, Code is the enumeration type for the method
+ * lookup.
+ */
+template <typename Code>
+class MethodStatistics {
+public:
+ using FloatType = float;
+ using StatsType = audio_utils::Statistics<FloatType>;
+
+ /**
+ * Method statistics.
+ *
+ * Initialized with the Binder transaction list for tracking AudioFlinger
+ * and AudioPolicyManager execution statistics.
+ */
+ explicit MethodStatistics(
+ const std::initializer_list<std::pair<const Code, std::string>>& methodMap = {})
+ : mMethodMap{methodMap} {}
+
+ /**
+ * Adds a method event, typically execution time in ms.
+ */
+ void event(Code code, FloatType executeMs) {
+ std::lock_guard lg(mLock);
+ mStatisticsMap[code].add(executeMs);
+ }
+
+ /**
+ * Returns the name for the method code.
+ */
+ std::string getMethodForCode(Code code) const {
+ auto it = mMethodMap.find(code);
+ return it == mMethodMap.end() ? std::to_string((int)code) : it->second;
+ }
+
+ /**
+ * Returns the number of times the method was invoked by event().
+ */
+ size_t getMethodCount(Code code) const {
+ std::lock_guard lg(mLock);
+ auto it = mStatisticsMap.find(code);
+ return it == mStatisticsMap.end() ? 0 : it->second.getN();
+ }
+
+ /**
+ * Returns the statistics object for the method.
+ */
+ StatsType getStatistics(Code code) const {
+ std::lock_guard lg(mLock);
+ auto it = mStatisticsMap.find(code);
+ return it == mStatisticsMap.end() ? StatsType{} : it->second;
+ }
+
+ /**
+ * Dumps the current method statistics.
+ */
+ std::string dump() const {
+ std::stringstream ss;
+ std::lock_guard lg(mLock);
+ for (const auto &[code, stats] : mStatisticsMap) {
+ ss << int(code) << " " << getMethodForCode(code) <<
+ " n=" << stats.getN() << " " << stats.toString() << "\n";
+ }
+ return ss.str();
+ }
+
+private:
+ const std::map<Code, std::string> mMethodMap;
+ mutable std::mutex mLock;
+ std::map<Code, StatsType> mStatisticsMap GUARDED_BY(mLock);
+};
+
+// Only if used, requires IBinder.h to be included at the location of invocation.
+#define METHOD_STATISTICS_BINDER_CODE_NAMES(CODE_TYPE) \
+ {(CODE_TYPE)IBinder::PING_TRANSACTION , "ping"}, \
+ {(CODE_TYPE)IBinder::DUMP_TRANSACTION , "dump"}, \
+ {(CODE_TYPE)IBinder::SHELL_COMMAND_TRANSACTION , "shellCommand"}, \
+ {(CODE_TYPE)IBinder::INTERFACE_TRANSACTION , "getInterfaceDescriptor"}, \
+ {(CODE_TYPE)IBinder::SYSPROPS_TRANSACTION , "SYSPROPS_TRANSACTION"}, \
+ {(CODE_TYPE)IBinder::EXTENSION_TRANSACTION , "EXTENSION_TRANSACTION"}, \
+ {(CODE_TYPE)IBinder::DEBUG_PID_TRANSACTION , "DEBUG_PID_TRANSACTION"}, \
+
+} // android::mediautils
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index 0d6e80d..991a921 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -20,27 +20,76 @@
#include <mediautils/TimerThread.h>
-namespace android {
+namespace android::mediautils {
// A class monitoring execution time for a code block (scoped variable) and causing an assert
// if it exceeds a certain time
class TimeCheck {
public:
+ using OnTimerFunc = std::function<void(bool /* timeout */, float /* elapsedMs */ )>;
+
// The default timeout is chosen to be less than system server watchdog timeout
static constexpr uint32_t kDefaultTimeOutMs = 5000;
- TimeCheck(const char* tag, uint32_t timeoutMs = kDefaultTimeOutMs);
+ /**
+ * TimeCheck is a RAII object which will notify a callback
+ * on timer expiration or when the object is deallocated.
+ *
+ * TimeCheck is used as a watchdog and aborts by default on timer expiration.
+ * When it aborts, it will also send a debugger signal to pids passed in through
+ * setAudioHalPids().
+ *
+ * If the callback function returns for timeout it will not be called again for
+ * the deallocation.
+ *
+ * \param tag string associated with the TimeCheck object.
+ * \param onTimer callback function with 2 parameters
+ * bool timeout (which is true when the TimeCheck object
+ * times out, false when the TimeCheck object is
+ * destroyed or leaves scope before the timer expires.)
+ * float elapsedMs (the elapsed time to this event).
+ * The callback when timeout is true will be called on a different thread.
+ * Currently this is guaranteed to block the destructor
+ * (potential lock inversion warning here) nevertheless
+ * it would be safer not to depend on stack contents.
+ * \param timeoutMs timeout in milliseconds.
+ * \param crashOnTimeout true if the object issues an abort on timeout.
+ */
+ explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {},
+ uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true);
+ // Remove copy constructors as there should only be one call to the destructor.
+ // Move is kept implicitly disabled, but would be logically consistent if enabled.
+ TimeCheck(const TimeCheck& other) = delete;
+ TimeCheck& operator=(const TimeCheck&) = delete;
+
~TimeCheck();
static void setAudioHalPids(const std::vector<pid_t>& pids);
static std::vector<pid_t> getAudioHalPids();
private:
- static TimerThread* getTimeCheckThread();
+ static TimerThread& getTimeCheckThread();
static void accessAudioHalPids(std::vector<pid_t>* pids, bool update);
- static void crash(const char* tag, std::chrono::system_clock::time_point startTime);
+ // Helper class for handling events.
+ // The usage here is const safe.
+ class TimeCheckHandler {
+ public:
+ const std::string tag;
+ const OnTimerFunc onTimer;
+ const bool crashOnTimeout;
+ const std::chrono::system_clock::time_point startTime;
+ const pid_t tid;
+
+ void onCancel(TimerThread::Handle handle) const;
+ void onTimeout() const;
+ };
+
+ // mTimeCheckHandler is immutable, prefer to be first initialized, last destroyed.
+ // Technically speaking, we do not need a shared_ptr here because TimerThread::cancelTask()
+ // is mutually exclusive of the callback, but the price paid for lifetime safety is minimal.
+ const std::shared_ptr<const TimeCheckHandler> mTimeCheckHandler;
const TimerThread::Handle mTimerHandle;
};
-}; // namespace android
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
index cf457b8..acf0b16 100644
--- a/media/utils/include/mediautils/TimerThread.h
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -48,9 +48,9 @@
/**
* Cancel a task, previously scheduled with scheduleTask().
- * If the task has already executed, this is a no-op.
+ * If the task has already executed, this is a no-op and returns false.
*/
- void cancelTask(Handle handle);
+ bool cancelTask(Handle handle);
private:
using TimePoint = std::chrono::steady_clock::time_point;
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 6593d56..5498ac5 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -26,3 +26,51 @@
"media_synchronization_tests.cpp",
],
}
+
+cc_test {
+ name: "methodstatistics_tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ srcs: [
+ "methodstatistics_tests.cpp",
+ ],
+}
+
+cc_test {
+ name: "timecheck_tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ sanitize:{
+ address: true,
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
+ },
+
+ shared_libs: [
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ srcs: [
+ "timecheck_tests.cpp",
+ ],
+}
diff --git a/media/utils/tests/methodstatistics_tests.cpp b/media/utils/tests/methodstatistics_tests.cpp
new file mode 100644
index 0000000..85c4ad5
--- /dev/null
+++ b/media/utils/tests/methodstatistics_tests.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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_TAG "methodstatistics_tests"
+
+#include <mediautils/MethodStatistics.h>
+
+#include <atomic>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android::mediautils;
+using CodeType = size_t;
+
+constexpr CodeType HELLO_CODE = 10;
+constexpr const char * HELLO_NAME = "hello";
+constexpr float HELLO_EVENTS[] = { 1.f, 3.f }; // needs lossless average
+
+constexpr CodeType WORLD_CODE = 21;
+constexpr const char * WORLD_NAME = "world";
+
+constexpr CodeType UNKNOWN_CODE = 12345;
+
+TEST(methodstatistics_tests, method_names) {
+ const MethodStatistics<CodeType> methodStatistics{
+ {HELLO_CODE, HELLO_NAME},
+ {WORLD_CODE, WORLD_NAME},
+ };
+
+ ASSERT_EQ(std::string(HELLO_NAME), methodStatistics.getMethodForCode(HELLO_CODE));
+ ASSERT_EQ(std::string(WORLD_NAME), methodStatistics.getMethodForCode(WORLD_CODE));
+ // an unknown code returns itself as a number.
+ ASSERT_EQ(std::to_string(UNKNOWN_CODE), methodStatistics.getMethodForCode(UNKNOWN_CODE));
+}
+
+TEST(methodstatistics_tests, events) {
+ MethodStatistics<CodeType> methodStatistics{
+ {HELLO_CODE, HELLO_NAME},
+ {WORLD_CODE, WORLD_NAME},
+ };
+
+ size_t n = 0;
+ float sum = 0.f;
+ for (const auto event : HELLO_EVENTS) {
+ methodStatistics.event(HELLO_CODE, event);
+ sum += event;
+ ++n;
+ }
+
+ const auto helloStats = methodStatistics.getStatistics(HELLO_CODE);
+ ASSERT_EQ((signed)n, helloStats.getN());
+ ASSERT_EQ(sum / n, helloStats.getMean());
+ ASSERT_EQ(n, methodStatistics.getMethodCount(HELLO_CODE));
+
+ const auto unsetStats = methodStatistics.getStatistics(UNKNOWN_CODE);
+ ASSERT_EQ(0, unsetStats.getN());
+ ASSERT_EQ(0.f, unsetStats.getMean());
+ ASSERT_EQ(0U, methodStatistics.getMethodCount(UNKNOWN_CODE));
+}
diff --git a/media/utils/tests/timecheck_tests.cpp b/media/utils/tests/timecheck_tests.cpp
new file mode 100644
index 0000000..9833dc9
--- /dev/null
+++ b/media/utils/tests/timecheck_tests.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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_TAG "timecheck_tests"
+
+#include <mediautils/TimeCheck.h>
+
+#include <atomic>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android::mediautils;
+
+TEST(timecheck_tests, success) {
+ bool timeoutRegistered = false;
+ float elapsedMsRegistered = 0.f;
+ bool event = false;
+
+ {
+ TimeCheck timeCheck("success",
+ [&event, &timeoutRegistered, &elapsedMsRegistered]
+ (bool timeout, float elapsedMs) {
+ timeoutRegistered = timeout;
+ elapsedMsRegistered = elapsedMs;
+ event = true;
+ }, 1000 /* msec */, false /* crash */);
+ }
+ ASSERT_TRUE(event);
+ ASSERT_FALSE(timeoutRegistered);
+ ASSERT_GT(elapsedMsRegistered, 0.f);
+}
+
+TEST(timecheck_tests, timeout) {
+ bool timeoutRegistered = false;
+ float elapsedMsRegistered = 0.f;
+ std::atomic_bool event = false; // seq-cst implies acquire-release
+
+ {
+ TimeCheck timeCheck("timeout",
+ [&event, &timeoutRegistered, &elapsedMsRegistered]
+ (bool timeout, float elapsedMs) {
+ timeoutRegistered = timeout;
+ elapsedMsRegistered = elapsedMs;
+ event = true; // store-release, must be last.
+ }, 1 /* msec */, false /* crash */);
+ usleep(100 * 1000 /* usec */); // extra time as callback called by different thread.
+ }
+ ASSERT_TRUE(event); // load-acquire, must be first.
+ ASSERT_TRUE(timeoutRegistered); // only called once on failure, not on dealloc.
+ ASSERT_GT(elapsedMsRegistered, 0.f);
+}
+
+// Note: We do not test TimeCheck crash because TimeCheck is multithreaded and the
+// EXPECT_EXIT() signal catching is imperfect due to the gtest fork.
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index eb3c164..8cafdfd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -79,6 +79,7 @@
#include <media/nbaio/PipeReader.h>
#include <mediautils/BatteryNotifier.h>
#include <mediautils/MemoryLeakTrackUtil.h>
+#include <mediautils/MethodStatistics.h>
#include <mediautils/ServiceUtilities.h>
#include <mediautils/TimeCheck.h>
#include <private/android_filesystem_config.h>
@@ -158,6 +159,92 @@
return sExternalVibratorService;
}
+// Creates association between Binder code to name for IAudioFlinger.
+#define IAUDIOFLINGER_BINDER_METHOD_MACRO_LIST \
+BINDER_METHOD_ENTRY(createTrack) \
+BINDER_METHOD_ENTRY(createRecord) \
+BINDER_METHOD_ENTRY(sampleRate) \
+BINDER_METHOD_ENTRY(format) \
+BINDER_METHOD_ENTRY(frameCount) \
+BINDER_METHOD_ENTRY(latency) \
+BINDER_METHOD_ENTRY(setMasterVolume) \
+BINDER_METHOD_ENTRY(setMasterMute) \
+BINDER_METHOD_ENTRY(masterVolume) \
+BINDER_METHOD_ENTRY(masterMute) \
+BINDER_METHOD_ENTRY(setStreamVolume) \
+BINDER_METHOD_ENTRY(setStreamMute) \
+BINDER_METHOD_ENTRY(streamVolume) \
+BINDER_METHOD_ENTRY(streamMute) \
+BINDER_METHOD_ENTRY(setMode) \
+BINDER_METHOD_ENTRY(setMicMute) \
+BINDER_METHOD_ENTRY(getMicMute) \
+BINDER_METHOD_ENTRY(setRecordSilenced) \
+BINDER_METHOD_ENTRY(setParameters) \
+BINDER_METHOD_ENTRY(getParameters) \
+BINDER_METHOD_ENTRY(registerClient) \
+BINDER_METHOD_ENTRY(getInputBufferSize) \
+BINDER_METHOD_ENTRY(openOutput) \
+BINDER_METHOD_ENTRY(openDuplicateOutput) \
+BINDER_METHOD_ENTRY(closeOutput) \
+BINDER_METHOD_ENTRY(suspendOutput) \
+BINDER_METHOD_ENTRY(restoreOutput) \
+BINDER_METHOD_ENTRY(openInput) \
+BINDER_METHOD_ENTRY(closeInput) \
+BINDER_METHOD_ENTRY(invalidateStream) \
+BINDER_METHOD_ENTRY(setVoiceVolume) \
+BINDER_METHOD_ENTRY(getRenderPosition) \
+BINDER_METHOD_ENTRY(getInputFramesLost) \
+BINDER_METHOD_ENTRY(newAudioUniqueId) \
+BINDER_METHOD_ENTRY(acquireAudioSessionId) \
+BINDER_METHOD_ENTRY(releaseAudioSessionId) \
+BINDER_METHOD_ENTRY(queryNumberEffects) \
+BINDER_METHOD_ENTRY(queryEffect) \
+BINDER_METHOD_ENTRY(getEffectDescriptor) \
+BINDER_METHOD_ENTRY(createEffect) \
+BINDER_METHOD_ENTRY(moveEffects) \
+BINDER_METHOD_ENTRY(loadHwModule) \
+BINDER_METHOD_ENTRY(getPrimaryOutputSamplingRate) \
+BINDER_METHOD_ENTRY(getPrimaryOutputFrameCount) \
+BINDER_METHOD_ENTRY(setLowRamDevice) \
+BINDER_METHOD_ENTRY(getAudioPort) \
+BINDER_METHOD_ENTRY(createAudioPatch) \
+BINDER_METHOD_ENTRY(releaseAudioPatch) \
+BINDER_METHOD_ENTRY(listAudioPatches) \
+BINDER_METHOD_ENTRY(setAudioPortConfig) \
+BINDER_METHOD_ENTRY(getAudioHwSyncForSession) \
+BINDER_METHOD_ENTRY(systemReady) \
+BINDER_METHOD_ENTRY(audioPolicyReady) \
+BINDER_METHOD_ENTRY(frameCountHAL) \
+BINDER_METHOD_ENTRY(getMicrophones) \
+BINDER_METHOD_ENTRY(setMasterBalance) \
+BINDER_METHOD_ENTRY(getMasterBalance) \
+BINDER_METHOD_ENTRY(setEffectSuspended) \
+BINDER_METHOD_ENTRY(setAudioHalPids) \
+BINDER_METHOD_ENTRY(setVibratorInfos) \
+BINDER_METHOD_ENTRY(updateSecondaryOutputs) \
+BINDER_METHOD_ENTRY(getMmapPolicyInfos) \
+BINDER_METHOD_ENTRY(getAAudioMixerBurstCount) \
+BINDER_METHOD_ENTRY(getAAudioHardwareBurstMinUsec) \
+BINDER_METHOD_ENTRY(setDeviceConnectedState) \
+
+// singleton for Binder Method Statistics for IAudioFlinger
+static auto& getIAudioFlingerStatistics() {
+ using Code = android::AudioFlingerServerAdapter::Delegate::TransactionCode;
+
+#pragma push_macro("BINDER_METHOD_ENTRY")
+#undef BINDER_METHOD_ENTRY
+#define BINDER_METHOD_ENTRY(ENTRY) \
+ {(Code)media::BnAudioFlingerService::TRANSACTION_##ENTRY, #ENTRY},
+
+ static mediautils::MethodStatistics<Code> methodStatistics{
+ IAUDIOFLINGER_BINDER_METHOD_MACRO_LIST
+ METHOD_STATISTICS_BINDER_CODE_NAMES(Code)
+ };
+#pragma pop_macro("BINDER_METHOD_ENTRY")
+
+ return methodStatistics;
+}
+
class DevicesFactoryHalCallbackImpl : public DevicesFactoryHalCallback {
public:
void onNewDevicesAvailable() override {
@@ -276,7 +363,7 @@
mMediaLogNotifier->run("MediaLogNotifier");
std::vector<pid_t> halPids;
mDevicesFactoryHal->getHalPids(&halPids);
- TimeCheck::setAudioHalPids(halPids);
+ mediautils::TimeCheck::setAudioHalPids(halPids);
// Notify that we have started (also called when audioserver service restarts)
mediametrics::LogItem(mMetricsId)
@@ -316,7 +403,7 @@
}
status_t AudioFlinger::setAudioHalPids(const std::vector<pid_t>& pids) {
- TimeCheck::setAudioHalPids(pids);
+ mediautils::TimeCheck::setAudioHalPids(pids);
return NO_ERROR;
}
@@ -828,6 +915,16 @@
std::string s = GetUnreachableMemoryString(true /* contents */, 100 /* limit */);
write(fd, s.c_str(), s.size());
}
+ {
+ std::string timeCheckStats = getIAudioFlingerStatistics().dump();
+ dprintf(fd, "\nIAudioFlinger binder call profile\n");
+ write(fd, timeCheckStats.c_str(), timeCheckStats.size());
+
+ extern mediautils::MethodStatistics<int>& getIEffectStatistics();
+ timeCheckStats = getIEffectStatistics().dump();
+ dprintf(fd, "\nIEffect binder call profile\n");
+ write(fd, timeCheckStats.c_str(), timeCheckStats.size());
+ }
}
return NO_ERROR;
}
@@ -4417,9 +4514,20 @@
break;
}
- std::string tag("IAudioFlinger command " +
- std::to_string(static_cast<std::underlying_type_t<TransactionCode>>(code)));
- TimeCheck check(tag.c_str());
+ const std::string methodName = getIAudioFlingerStatistics().getMethodForCode(code);
+ mediautils::TimeCheck check(
+ std::string("IAudioFlinger::").append(methodName),
+ [code, methodName](bool timeout, float elapsedMs) { // don't move methodName.
+ if (timeout) {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_TIMEOUT)
+ .set(AMEDIAMETRICS_PROP_METHODCODE, int64_t(code))
+ .set(AMEDIAMETRICS_PROP_METHODNAME, methodName.c_str())
+ .record();
+ } else {
+ getIAudioFlingerStatistics().event(code, elapsedMs);
+ }
+ });
// Make sure we connect to Audio Policy Service before calling into AudioFlinger:
// - AudioFlinger can call into Audio Policy Service with its global mutex held
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b748f9d..efd2dbd 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -39,6 +39,7 @@
#include <media/ShmemCompat.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
+#include <mediautils/MethodStatistics.h>
#include <mediautils/ServiceUtilities.h>
#include "AudioFlinger.h"
@@ -1751,6 +1752,44 @@
disconnect(false);
}
+// Creates an association between Binder code to name for IEffect.
+#define IEFFECT_BINDER_METHOD_MACRO_LIST \
+BINDER_METHOD_ENTRY(enable) \
+BINDER_METHOD_ENTRY(disable) \
+BINDER_METHOD_ENTRY(command) \
+BINDER_METHOD_ENTRY(disconnect) \
+BINDER_METHOD_ENTRY(getCblk) \
+
+// singleton for Binder Method Statistics for IEffect
+mediautils::MethodStatistics<int>& getIEffectStatistics() {
+ using Code = int;
+
+#pragma push_macro("BINDER_METHOD_ENTRY")
+#undef BINDER_METHOD_ENTRY
+#define BINDER_METHOD_ENTRY(ENTRY) \
+ {(Code)media::BnEffect::TRANSACTION_##ENTRY, #ENTRY},
+
+ static mediautils::MethodStatistics<Code> methodStatistics{
+ IEFFECT_BINDER_METHOD_MACRO_LIST
+ METHOD_STATISTICS_BINDER_CODE_NAMES(Code)
+ };
+#pragma pop_macro("BINDER_METHOD_ENTRY")
+
+ return methodStatistics;
+}
+
+status_t AudioFlinger::EffectHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now();
+ mediametrics::Defer defer([startTime, code] {
+ std::chrono::system_clock::time_point endTime = std::chrono::system_clock::now();
+ getIEffectStatistics().event(code,
+ std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
+ endTime - startTime).count());
+ });
+ return BnEffect::onTransact(code, data, reply, flags);
+}
+
status_t AudioFlinger::EffectHandle::initCheck()
{
return mClient == 0 || mCblkMemory != 0 ? OK : NO_MEMORY;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index e2bea67..42614cc 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -356,6 +356,8 @@
const sp<media::IEffectClient>& effectClient,
int32_t priority, bool notifyFramesProcessed);
virtual ~EffectHandle();
+ status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
virtual status_t initCheck();
// IEffect
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a2ee5f5..e45de32 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3962,8 +3962,16 @@
}
flags = (audio_output_flags_t)((flags & relevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
- DeviceVector outputDevices = mEngine->getOutputDevicesForAttributes(*attr);
+ DeviceVector engineOutputDevices = mEngine->getOutputDevicesForAttributes(*attr);
for (const auto& hwModule : mHwModules) {
+ DeviceVector outputDevices = engineOutputDevices;
+ // the MSD module checks for different conditions and output devices
+ if (strcmp(hwModule->getName(), AUDIO_HARDWARE_MODULE_ID_MSD) == 0) {
+ if (!msdHasPatchesToAllDevices(engineOutputDevices.toTypeAddrVector())) {
+ continue;
+ }
+ outputDevices = getMsdAudioOutDevices();
+ }
for (const auto& curProfile : hwModule->getOutputProfiles()) {
if (!curProfile->isCompatibleProfile(outputDevices,
config->sample_rate, nullptr /*updatedSamplingRate*/,
@@ -3990,11 +3998,10 @@
~AUDIO_DIRECT_OFFLOAD_SUPPORTED) |
AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED);
} else {
- directMode = (audio_direct_mode_t)(directMode |AUDIO_DIRECT_OFFLOAD_SUPPORTED);
+ directMode = (audio_direct_mode_t)(directMode | AUDIO_DIRECT_OFFLOAD_SUPPORTED);
}
} else {
- directMode = (audio_direct_mode_t) (directMode |
- AUDIO_DIRECT_BITSTREAM_SUPPORTED);
+ directMode = (audio_direct_mode_t) (directMode | AUDIO_DIRECT_BITSTREAM_SUPPORTED);
}
}
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 9955b6b..bdd86b1 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -39,6 +39,7 @@
#include <media/AidlConversion.h>
#include <media/AudioEffect.h>
#include <media/AudioParameter.h>
+#include <mediautils/MethodStatistics.h>
#include <mediautils/ServiceUtilities.h>
#include <mediautils/TimeCheck.h>
#include <sensorprivacy/SensorPrivacyManager.h>
@@ -60,6 +61,120 @@
static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY");
+// Creates an association between Binder code to name for IAudioPolicyService.
+#define IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST \
+BINDER_METHOD_ENTRY(onNewAudioModulesAvailable) \
+BINDER_METHOD_ENTRY(setDeviceConnectionState) \
+BINDER_METHOD_ENTRY(getDeviceConnectionState) \
+BINDER_METHOD_ENTRY(handleDeviceConfigChange) \
+BINDER_METHOD_ENTRY(setPhoneState) \
+BINDER_METHOD_ENTRY(setForceUse) \
+BINDER_METHOD_ENTRY(getForceUse) \
+BINDER_METHOD_ENTRY(getOutput) \
+BINDER_METHOD_ENTRY(getOutputForAttr) \
+BINDER_METHOD_ENTRY(startOutput) \
+BINDER_METHOD_ENTRY(stopOutput) \
+BINDER_METHOD_ENTRY(releaseOutput) \
+BINDER_METHOD_ENTRY(getInputForAttr) \
+BINDER_METHOD_ENTRY(startInput) \
+BINDER_METHOD_ENTRY(stopInput) \
+BINDER_METHOD_ENTRY(releaseInput) \
+BINDER_METHOD_ENTRY(initStreamVolume) \
+BINDER_METHOD_ENTRY(setStreamVolumeIndex) \
+BINDER_METHOD_ENTRY(getStreamVolumeIndex) \
+BINDER_METHOD_ENTRY(setVolumeIndexForAttributes) \
+BINDER_METHOD_ENTRY(getVolumeIndexForAttributes) \
+BINDER_METHOD_ENTRY(getMaxVolumeIndexForAttributes) \
+BINDER_METHOD_ENTRY(getMinVolumeIndexForAttributes) \
+BINDER_METHOD_ENTRY(getStrategyForStream) \
+BINDER_METHOD_ENTRY(getDevicesForAttributes) \
+BINDER_METHOD_ENTRY(getOutputForEffect) \
+BINDER_METHOD_ENTRY(registerEffect) \
+BINDER_METHOD_ENTRY(unregisterEffect) \
+BINDER_METHOD_ENTRY(setEffectEnabled) \
+BINDER_METHOD_ENTRY(moveEffectsToIo) \
+BINDER_METHOD_ENTRY(isStreamActive) \
+BINDER_METHOD_ENTRY(isStreamActiveRemotely) \
+BINDER_METHOD_ENTRY(isSourceActive) \
+BINDER_METHOD_ENTRY(queryDefaultPreProcessing) \
+BINDER_METHOD_ENTRY(addSourceDefaultEffect) \
+BINDER_METHOD_ENTRY(addStreamDefaultEffect) \
+BINDER_METHOD_ENTRY(removeSourceDefaultEffect) \
+BINDER_METHOD_ENTRY(removeStreamDefaultEffect) \
+BINDER_METHOD_ENTRY(setSupportedSystemUsages) \
+BINDER_METHOD_ENTRY(setAllowedCapturePolicy) \
+BINDER_METHOD_ENTRY(getOffloadSupport) \
+BINDER_METHOD_ENTRY(isDirectOutputSupported) \
+BINDER_METHOD_ENTRY(listAudioPorts) \
+BINDER_METHOD_ENTRY(getAudioPort) \
+BINDER_METHOD_ENTRY(createAudioPatch) \
+BINDER_METHOD_ENTRY(releaseAudioPatch) \
+BINDER_METHOD_ENTRY(listAudioPatches) \
+BINDER_METHOD_ENTRY(setAudioPortConfig) \
+BINDER_METHOD_ENTRY(registerClient) \
+BINDER_METHOD_ENTRY(setAudioPortCallbacksEnabled) \
+BINDER_METHOD_ENTRY(setAudioVolumeGroupCallbacksEnabled) \
+BINDER_METHOD_ENTRY(acquireSoundTriggerSession) \
+BINDER_METHOD_ENTRY(releaseSoundTriggerSession) \
+BINDER_METHOD_ENTRY(getPhoneState) \
+BINDER_METHOD_ENTRY(registerPolicyMixes) \
+BINDER_METHOD_ENTRY(setUidDeviceAffinities) \
+BINDER_METHOD_ENTRY(removeUidDeviceAffinities) \
+BINDER_METHOD_ENTRY(setUserIdDeviceAffinities) \
+BINDER_METHOD_ENTRY(removeUserIdDeviceAffinities) \
+BINDER_METHOD_ENTRY(startAudioSource) \
+BINDER_METHOD_ENTRY(stopAudioSource) \
+BINDER_METHOD_ENTRY(setMasterMono) \
+BINDER_METHOD_ENTRY(getMasterMono) \
+BINDER_METHOD_ENTRY(getStreamVolumeDB) \
+BINDER_METHOD_ENTRY(getSurroundFormats) \
+BINDER_METHOD_ENTRY(getReportedSurroundFormats) \
+BINDER_METHOD_ENTRY(getHwOffloadFormatsSupportedForBluetoothMedia) \
+BINDER_METHOD_ENTRY(setSurroundFormatEnabled) \
+BINDER_METHOD_ENTRY(setAssistantServicesUids) \
+BINDER_METHOD_ENTRY(setActiveAssistantServicesUids) \
+BINDER_METHOD_ENTRY(setA11yServicesUids) \
+BINDER_METHOD_ENTRY(setCurrentImeUid) \
+BINDER_METHOD_ENTRY(isHapticPlaybackSupported) \
+BINDER_METHOD_ENTRY(isUltrasoundSupported) \
+BINDER_METHOD_ENTRY(listAudioProductStrategies) \
+BINDER_METHOD_ENTRY(getProductStrategyFromAudioAttributes) \
+BINDER_METHOD_ENTRY(listAudioVolumeGroups) \
+BINDER_METHOD_ENTRY(getVolumeGroupFromAudioAttributes) \
+BINDER_METHOD_ENTRY(setRttEnabled) \
+BINDER_METHOD_ENTRY(isCallScreenModeSupported) \
+BINDER_METHOD_ENTRY(setDevicesRoleForStrategy) \
+BINDER_METHOD_ENTRY(removeDevicesRoleForStrategy) \
+BINDER_METHOD_ENTRY(getDevicesForRoleAndStrategy) \
+BINDER_METHOD_ENTRY(setDevicesRoleForCapturePreset) \
+BINDER_METHOD_ENTRY(addDevicesRoleForCapturePreset) \
+BINDER_METHOD_ENTRY(removeDevicesRoleForCapturePreset) \
+BINDER_METHOD_ENTRY(clearDevicesRoleForCapturePreset) \
+BINDER_METHOD_ENTRY(getDevicesForRoleAndCapturePreset) \
+BINDER_METHOD_ENTRY(registerSoundTriggerCaptureStateListener) \
+BINDER_METHOD_ENTRY(getSpatializer) \
+BINDER_METHOD_ENTRY(canBeSpatialized) \
+BINDER_METHOD_ENTRY(getDirectPlaybackSupport) \
+BINDER_METHOD_ENTRY(getDirectProfilesForAttributes) \
+
+// singleton for Binder Method Statistics for IAudioPolicyService
+static auto& getIAudioPolicyServiceStatistics() {
+ using Code = int;
+
+#pragma push_macro("BINDER_METHOD_ENTRY")
+#undef BINDER_METHOD_ENTRY
+#define BINDER_METHOD_ENTRY(ENTRY) \
+ {(Code)media::BnAudioPolicyService::TRANSACTION_##ENTRY, #ENTRY},
+
+ static mediautils::MethodStatistics<Code> methodStatistics{
+ IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST
+ METHOD_STATISTICS_BINDER_CODE_NAMES(Code)
+ };
+#pragma pop_macro("BINDER_METHOD_ENTRY")
+
+ return methodStatistics;
+}
+
// ----------------------------------------------------------------------------
static AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
@@ -1068,6 +1183,12 @@
mPackageManager.dump(fd);
dumpReleaseLock(mLock, locked);
+
+ {
+ std::string timeCheckStats = getIAudioPolicyServiceStatistics().dump();
+ dprintf(fd, "\nIAudioPolicyService binder call profile\n");
+ write(fd, timeCheckStats.c_str(), timeCheckStats.size());
+ }
}
return NO_ERROR;
}
@@ -1173,8 +1294,20 @@
break;
}
- std::string tag("IAudioPolicyService command " + std::to_string(code));
- TimeCheck check(tag.c_str());
+ const std::string methodName = getIAudioPolicyServiceStatistics().getMethodForCode(code);
+ mediautils::TimeCheck check(
+ std::string("IAudioPolicyService::").append(methodName),
+ [code, methodName](bool timeout, float elapsedMs) { // don't move methodName.
+ if (timeout) {
+ mediametrics::LogItem(AMEDIAMETRICS_KEY_AUDIO_POLICY)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_TIMEOUT)
+ .set(AMEDIAMETRICS_PROP_METHODCODE, int64_t(code))
+ .set(AMEDIAMETRICS_PROP_METHODNAME, methodName.c_str())
+ .record();
+ } else {
+ getIAudioPolicyServiceStatistics().event(code, elapsedMs);
+ }
+ });
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index d9e89aa..579b852 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -300,7 +300,7 @@
if (levelChanged && mEngine != nullptr) {
setEffectParameter_l(SPATIALIZER_PARAM_LEVEL, std::vector<SpatializationLevel>{level});
}
- checkHeadSensor_l();
+ checkSensorsState_l();
}
if (levelChanged) {
@@ -375,7 +375,7 @@
if (mPoseController != nullptr) {
mPoseController->setDesiredMode(mDesiredHeadTrackingMode);
- checkHeadSensor_l();
+ checkSensorsState_l();
}
return Status::ok();
@@ -449,9 +449,7 @@
}
std::lock_guard lock(mLock);
mHeadSensor = sensorHandle;
- if (mPoseController != nullptr) {
- checkHeadSensor_l();
- }
+ checkSensorsState_l();
return Status::ok();
}
@@ -462,9 +460,7 @@
}
std::lock_guard lock(mLock);
mScreenSensor = sensorHandle;
- if (mPoseController != nullptr) {
- mPoseController->setScreenSensor(mScreenSensor);
- }
+ checkSensorsState_l();
return Status::ok();
}
@@ -669,8 +665,7 @@
mPoseController->setDesiredMode(mDesiredHeadTrackingMode);
mNumActiveTracks = numActiveTracks;
- checkHeadSensor_l();
- mPoseController->setScreenSensor(mScreenSensor);
+ checkSensorsState_l();
mPoseController->setDisplayOrientation(mDisplayOrientation);
poseController = mPoseController;
}
@@ -715,17 +710,19 @@
void Spatializer::updateActiveTracks(size_t numActiveTracks) {
std::lock_guard lock(mLock);
mNumActiveTracks = numActiveTracks;
- checkHeadSensor_l();
+ checkSensorsState_l();
}
-void Spatializer::checkHeadSensor_l() {
+void Spatializer::checkSensorsState_l() {
if (mSupportsHeadTracking && mPoseController != nullptr) {
- if(mNumActiveTracks > 0 && mLevel != SpatializationLevel::NONE
+ if (mNumActiveTracks > 0 && mLevel != SpatializationLevel::NONE
&& mDesiredHeadTrackingMode != HeadTrackingMode::STATIC
&& mHeadSensor != SpatializerPoseController::INVALID_SENSOR) {
mPoseController->setHeadSensor(mHeadSensor);
+ mPoseController->setScreenSensor(mScreenSensor);
} else {
mPoseController->setHeadSensor(SpatializerPoseController::INVALID_SENSOR);
+ mPoseController->setScreenSensor(SpatializerPoseController::INVALID_SENSOR);
}
}
}
@@ -746,11 +743,11 @@
switch (event) {
case AudioEffect::EVENT_FRAMES_PROCESSED: {
int frames = info == nullptr ? 0 : *(int*)info;
- ALOGD("%s frames processed %d for me %p", __func__, frames, me);
+ ALOGV("%s frames processed %d for me %p", __func__, frames, me);
me->postFramesProcessedMsg(frames);
} break;
default:
- ALOGD("%s event %d", __func__, event);
+ ALOGV("%s event %d", __func__, event);
break;
}
}
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 4ce99d8..1382124 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -276,7 +276,12 @@
void postFramesProcessedMsg(int frames);
- void checkHeadSensor_l() REQUIRES(mLock);
+ /**
+ * Checks if head and screen sensors must be actively monitored based on
+ * spatializer state and playback activity and configures the pose controller
+ * accordingly.
+ */
+ void checkSensorsState_l() REQUIRES(mLock);
/** Effect engine descriptor */
const effect_descriptor_t mEngineDescriptor;
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index d6af886..da42ab4 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -766,6 +766,91 @@
ASSERT_FALSE(mManager->isDirectOutputSupported(msdNonDirectConfig, attr));
}
+TEST_P(AudioPolicyManagerTestMsd, GetDirectPlaybackSupportWithMsd) {
+ const audio_attributes_t attr = {
+ AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN,
+ AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""};
+
+ audio_config_t directConfig = AUDIO_CONFIG_INITIALIZER;
+ directConfig.format = AUDIO_FORMAT_DTS;
+ directConfig.sample_rate = 48000;
+ directConfig.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+
+ audio_config_t nonDirectConfig = AUDIO_CONFIG_INITIALIZER;
+ nonDirectConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ nonDirectConfig.sample_rate = 48000;
+ nonDirectConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+ audio_config_t nonExistentConfig = AUDIO_CONFIG_INITIALIZER;
+ nonExistentConfig.format = AUDIO_FORMAT_E_AC3;
+ nonExistentConfig.sample_rate = 48000;
+ nonExistentConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+ audio_config_t msdDirectConfig1 = AUDIO_CONFIG_INITIALIZER;
+ msdDirectConfig1.format = AUDIO_FORMAT_AC3;
+ msdDirectConfig1.sample_rate = 48000;
+ msdDirectConfig1.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+
+ audio_config_t msdDirectConfig2 = AUDIO_CONFIG_INITIALIZER;
+ msdDirectConfig2.format = AUDIO_FORMAT_IEC60958;
+ msdDirectConfig2.sample_rate = 48000;
+ msdDirectConfig2.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+ audio_config_t msdNonDirectConfig = AUDIO_CONFIG_INITIALIZER;
+ msdNonDirectConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ msdNonDirectConfig.sample_rate = 96000;
+ msdNonDirectConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+ ASSERT_EQ(AUDIO_DIRECT_BITSTREAM_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &directConfig));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &nonDirectConfig));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &nonExistentConfig));
+ // before setting MSD patches the direct MSD configs return AUDIO_DIRECT_NOT_SUPPORTED
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdDirectConfig1));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdDirectConfig2));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdNonDirectConfig));
+
+ DeviceVector outputDevices = mManager->getAvailableOutputDevices();
+ // Remove MSD output device to avoid patching to itself
+ outputDevices.remove(mMsdOutputDevice);
+ mManager->setMsdOutputPatches(&outputDevices);
+
+ ASSERT_EQ(AUDIO_DIRECT_BITSTREAM_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &directConfig));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &nonDirectConfig));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &nonExistentConfig));
+ // after setting MSD patches the direct MSD configs return values according to their flags
+ ASSERT_EQ(AUDIO_DIRECT_OFFLOAD_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdDirectConfig1));
+ ASSERT_EQ(AUDIO_DIRECT_BITSTREAM_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdDirectConfig2));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdNonDirectConfig));
+
+ mManager->releaseMsdOutputPatches(outputDevices);
+
+ ASSERT_EQ(AUDIO_DIRECT_BITSTREAM_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &directConfig));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &nonDirectConfig));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &nonExistentConfig));
+ // after releasing MSD patches the direct MSD configs return AUDIO_DIRECT_NOT_SUPPORTED
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdDirectConfig1));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdDirectConfig2));
+ ASSERT_EQ(AUDIO_DIRECT_NOT_SUPPORTED,
+ mManager->getDirectPlaybackSupport(&attr, &msdNonDirectConfig));
+}
+
class AudioPolicyManagerTestWithConfigurationFile : public AudioPolicyManagerTest {
protected:
void SetUpManagerConfig() override;
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index 600bd28..d32b71c 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -117,6 +117,41 @@
// Composite streams should behave accordingly.
void enableErrorState();
+ // Utility class to lock and unlock a GraphicBuffer
+ class GraphicBufferLocker {
+ public:
+ GraphicBufferLocker(sp<GraphicBuffer> buffer) : _buffer(buffer) {}
+
+ status_t lockAsync(void** dstBuffer, int fenceFd) {
+ if (_buffer == nullptr) return BAD_VALUE;
+
+ status_t res = OK;
+ if (!_locked) {
+ status_t res = _buffer->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN,
+ dstBuffer, fenceFd);
+ if (res == OK) {
+ _locked = true;
+ }
+ }
+ return res;
+ }
+
+ ~GraphicBufferLocker() {
+ if (_locked && _buffer != nullptr) {
+ auto res = _buffer->unlock();
+ if (res != OK) {
+ ALOGE("%s: Error trying to unlock buffer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ }
+ }
+ }
+
+ private:
+ sp<GraphicBuffer> _buffer;
+ bool _locked = false;
+ };
+
+
wp<CameraDeviceBase> mDevice;
wp<camera3::StatusTracker> mStatusTracker;
wp<hardware::camera2::ICameraDeviceCallbacks> mRemoteCallback;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index a66a592..aa057c7 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -297,7 +297,8 @@
}
sp<GraphicBuffer> gb = GraphicBuffer::from(anb);
- res = gb->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &dstBuffer, fenceFd);
+ GraphicBufferLocker gbLocker(gb);
+ res = gbLocker.lockAsync(&dstBuffer, fenceFd);
if (res != OK) {
ALOGE("%s: Error trying to lock output buffer fence: %s (%d)", __FUNCTION__,
strerror(-res), res);
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index a73ffb9..6058429 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -1130,7 +1130,8 @@
// Copy the content of the file to memory.
sp<GraphicBuffer> gb = GraphicBuffer::from(inputFrame.anb);
void* dstBuffer;
- auto res = gb->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &dstBuffer, inputFrame.fenceFd);
+ GraphicBufferLocker gbLocker(gb);
+ auto res = gbLocker.lockAsync(&dstBuffer, inputFrame.fenceFd);
if (res != OK) {
ALOGE("%s: Error trying to lock output buffer fence: %s (%d)", __FUNCTION__,
strerror(-res), res);