Merge "AudioTrack: Enforce worker thread join in dtor" into main
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp
index 591d56d..7b63e75 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.cpp
+++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp
@@ -21,6 +21,7 @@
#include <audio_utils/primitives.h>
#include <media/stagefright/foundation/MediaDefs.h>
+#include <C2Debug.h>
#include <C2PlatformSupport.h>
#include <SimpleC2Interface.h>
@@ -81,10 +82,6 @@
FLAC_COMPRESSION_LEVEL_MIN, FLAC_COMPRESSION_LEVEL_MAX)})
.withSetter(Setter<decltype(*mComplexity)>::NonStrictValueWithNoDeps)
.build());
- addParameter(
- DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
- .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 4608))
- .build());
addParameter(
DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING)
@@ -96,6 +93,26 @@
})
.withSetter((Setter<decltype(*mPcmEncodingInfo)>::StrictValueWithNoDeps))
.build());
+
+ addParameter(
+ DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMaxBlockSize))
+ .withFields({
+ C2F(mInputMaxBufSize, value).any(),
+ })
+ .withSetter(MaxInputSizeSetter, mChannelCount, mPcmEncodingInfo)
+ .build());
+ }
+
+ static C2R MaxInputSizeSetter(bool mayBlock,
+ C2P<C2StreamMaxBufferSizeInfo::input> &me,
+ const C2P<C2StreamChannelCountInfo::input> &channelCount,
+ const C2P<C2StreamPcmEncodingInfo::input> &pcmEncoding) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ int bytesPerSample = pcmEncoding.v.value == C2Config::PCM_FLOAT ? 4 : 2;
+ me.set().value = kMaxBlockSize * bytesPerSample * channelCount.v.value;
+ return res;
}
uint32_t getSampleRate() const { return mSampleRate->value; }
@@ -446,6 +463,9 @@
mBlockSize = FLAC__stream_encoder_get_blocksize(mFlacStreamEncoder);
+ // Update kMaxBlockSize to match maximum size used by the encoder
+ CHECK(mBlockSize <= kMaxBlockSize);
+
ALOGV("encoder successfully configured");
return OK;
}
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.h b/media/codec2/components/flac/C2SoftFlacEnc.h
index a971ab5..1f3be3c 100644
--- a/media/codec2/components/flac/C2SoftFlacEnc.h
+++ b/media/codec2/components/flac/C2SoftFlacEnc.h
@@ -63,7 +63,8 @@
std::shared_ptr<IntfImpl> mIntf;
const unsigned int kInBlockSize = 1152;
- const unsigned int kMaxNumChannels = 2;
+ static constexpr unsigned int kMaxNumChannels = 2;
+ static constexpr unsigned int kMaxBlockSize = 4608;
FLAC__StreamEncoder* mFlacStreamEncoder;
FLAC__int32* mInputBufferPcm32;
std::shared_ptr<C2LinearBlock> mOutputBlock;
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index fed2919..750edd5 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -506,27 +506,31 @@
"%s %s: must be invoked from the worker thread (%d)",
__func__, command.toString().c_str(), workerTid);
}
- if (!mContext.getCommandMQ()->writeBlocking(&command, 1)) {
- ALOGE("%s: failed to write command %s to MQ", __func__, command.toString().c_str());
- return NOT_ENOUGH_DATA;
- }
- StreamDescriptor::Reply localReply{};
- if (reply == nullptr) {
- reply = &localReply;
- }
- if (!mContext.getReplyMQ()->readBlocking(reply, 1)) {
- ALOGE("%s: failed to read from reply MQ, command %s", __func__, command.toString().c_str());
- return NOT_ENOUGH_DATA;
- }
{
- std::lock_guard l(mLock);
- // Not every command replies with 'latencyMs' field filled out, substitute the last
- // returned value in that case.
- if (reply->latencyMs <= 0) {
- reply->latencyMs = mLastReply.latencyMs;
+ std::lock_guard l(mCommandReplyLock);
+ if (!mContext.getCommandMQ()->writeBlocking(&command, 1)) {
+ ALOGE("%s: failed to write command %s to MQ", __func__, command.toString().c_str());
+ return NOT_ENOUGH_DATA;
}
- mLastReply = *reply;
- mLastReplyExpirationNs = uptimeNanos() + mLastReplyLifeTimeNs;
+ StreamDescriptor::Reply localReply{};
+ if (reply == nullptr) {
+ reply = &localReply;
+ }
+ if (!mContext.getReplyMQ()->readBlocking(reply, 1)) {
+ ALOGE("%s: failed to read from reply MQ, command %s",
+ __func__, command.toString().c_str());
+ return NOT_ENOUGH_DATA;
+ }
+ {
+ std::lock_guard l(mLock);
+ // Not every command replies with 'latencyMs' field filled out, substitute the last
+ // returned value in that case.
+ if (reply->latencyMs <= 0) {
+ reply->latencyMs = mLastReply.latencyMs;
+ }
+ mLastReply = *reply;
+ mLastReplyExpirationNs = uptimeNanos() + mLastReplyLifeTimeNs;
+ }
}
switch (reply->status) {
case STATUS_OK: return OK;
diff --git a/media/libaudiohal/impl/StreamHalAidl.h b/media/libaudiohal/impl/StreamHalAidl.h
index 53d46e5..b20eb00 100644
--- a/media/libaudiohal/impl/StreamHalAidl.h
+++ b/media/libaudiohal/impl/StreamHalAidl.h
@@ -243,6 +243,15 @@
const bool mIsInput;
const audio_config_base_t mConfig;
const StreamContextAidl mContext;
+ // This lock is used to make sending of a command and receiving a reply an atomic
+ // operation. Otherwise, when two threads are trying to send a command, they may both advance to
+ // reading of the reply once the HAL has consumed the command from the MQ, and that creates a
+ // race condition between them.
+ //
+ // Note that only access to command and reply MQs needs to be protected because the data MQ is
+ // only accessed by the I/O thread. Also, there is no need to protect lookup operations on the
+ // queues as they are thread-safe, only send/receive operation must be protected.
+ std::mutex mCommandReplyLock;
private:
static audio_config_base_t configToBase(const audio_config& config) {
@@ -256,6 +265,8 @@
std::lock_guard l(mLock);
return mLastReply.state;
}
+ // Note: Since `sendCommand` takes mLock while holding mCommandReplyLock, never call
+ // it with `mLock` being held.
status_t sendCommand(
const ::aidl::android::hardware::audio::core::StreamDescriptor::Command &command,
::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply = nullptr,