Merge "audio policy: choose bt-sco and speaker-safe path." into pi-dev
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 5d0f68e..ad1ccbc 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -262,6 +262,17 @@
void CryptoHal::clearHeapBase(int32_t seqNum) {
Mutex::Autolock autoLock(mLock);
+ /*
+ * Clear the remote shared memory mapping by setting the shared
+ * buffer base to a null hidl_memory.
+ *
+ * TODO: Add a releaseSharedBuffer method in a future DRM HAL
+ * API version to make this explicit.
+ */
+ uint32_t bufferId = mHeapBases.valueFor(seqNum).getBufferId();
+ Return<void> hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId);
+ ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
+
mHeapBases.removeItem(seqNum);
}
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index b6787af..ca9deab 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -506,7 +506,7 @@
ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
if (!derivedImage.dimgRefs.empty()) {
- ALOGW("dimgRefs if not clean!");
+ ALOGW("dimgRefs not clean!");
}
derivedImage.dimgRefs.appendVector(mRefs);
@@ -1490,6 +1490,17 @@
const ImageItem *image = &mItemIdToItemMap[itemIndex];
+ ssize_t tileItemIndex = -1;
+ if (image->isGrid()) {
+ if (image->dimgRefs.empty()) {
+ return NULL;
+ }
+ tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
+ if (tileItemIndex < 0) {
+ return NULL;
+ }
+ }
+
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
@@ -1530,10 +1541,6 @@
}
if (image->isGrid()) {
- ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
- if (tileItemIndex < 0) {
- return NULL;
- }
meta->setInt32(kKeyGridRows, image->rows);
meta->setInt32(kKeyGridCols, image->columns);
diff --git a/media/libaudioclient/include/media/AudioParameter.h b/media/libaudioclient/include/media/AudioParameter.h
index 59ac1db..967d895 100644
--- a/media/libaudioclient/include/media/AudioParameter.h
+++ b/media/libaudioclient/include/media/AudioParameter.h
@@ -81,6 +81,11 @@
static const char * const valueListSeparator;
+ // keyReconfigA2dp: Ask HwModule to reconfigure A2DP offloaded codec
+ // keyReconfigA2dpSupported: Query if HwModule supports A2DP offload codec config
+ static const char * const keyReconfigA2dp;
+ static const char * const keyReconfigA2dpSupported;
+
String8 toString() const { return toStringImpl(true); }
String8 keysToString() const { return toStringImpl(false); }
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index aa39443..f6f817a 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -192,7 +192,6 @@
// always recompute for both channel masks even if only one has changed.
const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
- const bool mixerChannelCountChanged = track->mMixerChannelCount != mixerChannelCount;
ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX)
&& trackChannelCount
@@ -213,7 +212,7 @@
// do it after downmix since track format may change!
track->prepareForReformat();
- if (track->mResampler.get() != nullptr && mixerChannelCountChanged) {
+ if (track->mResampler.get() != nullptr) {
// resampler channels may have changed.
const uint32_t resetToSampleRate = track->sampleRate;
track->mResampler.reset(nullptr);
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
index 3e88c7c..5fa9da9 100644
--- a/media/libeffects/config/Android.bp
+++ b/media/libeffects/config/Android.bp
@@ -1,5 +1,5 @@
// Effect configuration
-cc_library_shared {
+cc_library {
name: "libeffectsconfig",
vendor_available: true,
diff --git a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
index 55383eb..0b883f1 100644
--- a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
+++ b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
@@ -51,7 +51,7 @@
{0x7261676f, 0x6d75, 0x7369, 0x6364, {0x28, 0xe2, 0xfd, 0x3a, 0xc3, 0x9e}}, // type
{0xe0e6539b, 0x1781, 0x7261, 0x676f, {0x6d, 0x75, 0x73, 0x69, 0x63, 0x40}}, // uuid
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | EFFECT_FLAG_VOLUME_CTRL),
0, // TODO
1,
"Dynamics Processing",
@@ -367,6 +367,76 @@
return 0;
}
+//helper function
+bool DP_checkSizesInt(uint32_t paramSize, uint32_t valueSize, uint32_t expectedParams,
+ uint32_t expectedValues) {
+ if (paramSize < expectedParams * sizeof(int32_t)) {
+ ALOGE("Invalid paramSize: %u expected %u", paramSize,
+ (uint32_t)(expectedParams * sizeof(int32_t)));
+ return false;
+ }
+ if (valueSize < expectedValues * sizeof(int32_t)) {
+ ALOGE("Invalid valueSize %u expected %u", valueSize,
+ (uint32_t)(expectedValues * sizeof(int32_t)));
+ return false;
+ }
+ return true;
+}
+
+static dp_fx::DPChannel* DP_getChannel(DynamicsProcessingContext *pContext,
+ int32_t channel) {
+ if (pContext->mPDynamics == NULL) {
+ return NULL;
+ }
+ dp_fx::DPChannel *pChannel = pContext->mPDynamics->getChannel(channel);
+ ALOGE_IF(pChannel == NULL, "DPChannel NULL. invalid channel %d", channel);
+ return pChannel;
+}
+
+static dp_fx::DPEq* DP_getEq(DynamicsProcessingContext *pContext, int32_t channel,
+ int32_t eqType) {
+ dp_fx::DPChannel *pChannel = DP_getChannel(pContext, channel);
+ if (pChannel == NULL) {
+ return NULL;
+ }
+ dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
+ (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
+ ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
+ return pEq;
+}
+
+static dp_fx::DPEqBand* DP_getEqBand(DynamicsProcessingContext *pContext, int32_t channel,
+ int32_t eqType, int32_t band) {
+ dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqType);
+ if (pEq == NULL) {
+ return NULL;
+ }
+ dp_fx::DPEqBand *pEqBand = pEq->getBand(band);
+ ALOGE_IF(pEqBand == NULL, "DPEqBand NULL. invalid band %d", band);
+ return pEqBand;
+}
+
+static dp_fx::DPMbc* DP_getMbc(DynamicsProcessingContext *pContext, int32_t channel) {
+ dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
+ if (pChannel == NULL) {
+ return NULL;
+ }
+ dp_fx::DPMbc *pMbc = pChannel->getMbc();
+ ALOGE_IF(pMbc == NULL, "DPMbc NULL invalid MBC");
+ return pMbc;
+}
+
+static dp_fx::DPMbcBand* DP_getMbcBand(DynamicsProcessingContext *pContext, int32_t channel,
+ int32_t band) {
+ dp_fx::DPMbc *pMbc = DP_getMbc(pContext, channel);
+ if (pMbc == NULL) {
+ return NULL;
+ }
+ dp_fx::DPMbcBand *pMbcBand = pMbc->getBand(band);
+ ALOGE_IF(pMbcBand == NULL, "pMbcBand NULL. invalid band %d", band);
+ return pMbcBand;
+}
+
int DP_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
void *pCmdData, uint32_t *replySize, void *pReplyData) {
@@ -483,8 +553,49 @@
p->data + voffset);
break;
}
+ case EFFECT_CMD_SET_VOLUME: {
+ ALOGV("EFFECT_CMD_SET_VOLUME");
+ // if pReplyData is NULL, VOL_CTRL is delegated to another effect
+ if (pReplyData == NULL || replySize == NULL || *replySize < ((int)sizeof(int32_t) * 2)) {
+ ALOGV("no VOLUME data to return");
+ break;
+ }
+ if (pCmdData == NULL || cmdSize < ((int)sizeof(uint32_t) * 2)) {
+ ALOGE("\tLVM_ERROR : DynamicsProcessing EFFECT_CMD_SET_VOLUME ERROR");
+ return -EINVAL;
+ }
+
+ const int32_t unityGain = 1 << 24;
+ //channel count
+ int32_t channelCount = (int32_t)audio_channel_count_from_out_mask(
+ pContext->mConfig.inputCfg.channels);
+ for (int32_t ch = 0; ch < channelCount; ch++) {
+
+ dp_fx::DPChannel * pChannel = DP_getChannel(pContext, ch);
+ if (pChannel == NULL) {
+ ALOGE("%s EFFECT_CMD_SET_VOLUME invalid channel %d", __func__, ch);
+ return -EINVAL;
+ break;
+ }
+
+ int32_t offset = ch;
+ if (ch > 1) {
+ // FIXME: limited to 2 unique channels. If more channels present, use value for
+ // first channel
+ offset = 0;
+ }
+ const float gain = (float)*((uint32_t *)pCmdData + offset) / unityGain;
+ const float gainDb = linearToDb(gain);
+ ALOGVV("%s EFFECT_CMD_SET_VOLUME channel %d, engine outputlevel %f (%0.2f dB)",
+ __func__, ch, gain, gainDb);
+ pChannel->setOutputGain(gainDb);
+ }
+
+ const int32_t volRet[2] = {unityGain, unityGain}; // Apply no volume before effect.
+ memcpy(pReplyData, volRet, sizeof(volRet));
+ break;
+ }
case EFFECT_CMD_SET_DEVICE:
- case EFFECT_CMD_SET_VOLUME:
case EFFECT_CMD_SET_AUDIO_MODE:
break;
@@ -523,76 +634,6 @@
return 0;
}
-//helper function
-bool DP_checkSizesInt(uint32_t paramSize, uint32_t valueSize, uint32_t expectedParams,
- uint32_t expectedValues) {
- if (paramSize < expectedParams * sizeof(int32_t)) {
- ALOGE("Invalid paramSize: %u expected %u", paramSize,
- (uint32_t) (expectedParams * sizeof(int32_t)));
- return false;
- }
- if (valueSize < expectedValues * sizeof(int32_t)) {
- ALOGE("Invalid valueSize %u expected %u", valueSize,
- (uint32_t)(expectedValues * sizeof(int32_t)));
- return false;
- }
- return true;
-}
-
-static dp_fx::DPChannel* DP_getChannel(DynamicsProcessingContext *pContext,
- int32_t channel) {
- if (pContext->mPDynamics == NULL) {
- return NULL;
- }
- dp_fx::DPChannel *pChannel = pContext->mPDynamics->getChannel(channel);
- ALOGE_IF(pChannel == NULL, "DPChannel NULL. invalid channel %d", channel);
- return pChannel;
-}
-
-static dp_fx::DPEq* DP_getEq(DynamicsProcessingContext *pContext, int32_t channel,
- int32_t eqType) {
- dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
- if (pChannel == NULL) {
- return NULL;
- }
- dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
- (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
- ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
- return pEq;
-}
-
-static dp_fx::DPEqBand* DP_getEqBand(DynamicsProcessingContext *pContext, int32_t channel,
- int32_t eqType, int32_t band) {
- dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqType);
- if (pEq == NULL) {
- return NULL;
- }
- dp_fx::DPEqBand *pEqBand = pEq->getBand(band);
- ALOGE_IF(pEqBand == NULL, "DPEqBand NULL. invalid band %d", band);
- return pEqBand;
-}
-
-static dp_fx::DPMbc* DP_getMbc(DynamicsProcessingContext *pContext, int32_t channel) {
- dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
- if (pChannel == NULL) {
- return NULL;
- }
- dp_fx::DPMbc *pMbc = pChannel->getMbc();
- ALOGE_IF(pMbc == NULL, "DPMbc NULL invalid MBC");
- return pMbc;
-}
-
-static dp_fx::DPMbcBand* DP_getMbcBand(DynamicsProcessingContext *pContext, int32_t channel,
- int32_t band) {
- dp_fx::DPMbc *pMbc = DP_getMbc(pContext, channel);
- if (pMbc == NULL) {
- return NULL;
- }
- dp_fx::DPMbcBand *pMbcBand = pMbc->getBand(band);
- ALOGE_IF(pMbcBand == NULL, "pMbcBand NULL. invalid band %d", band);
- return pMbcBand;
-}
-
int DP_getParameter(DynamicsProcessingContext *pContext,
uint32_t paramSize,
void *pParam,
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.cpp b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
index 8b79991..ac758e0 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
@@ -174,8 +174,8 @@
}
//----
-DPChannel::DPChannel() : mInitialized(false), mInputGainDb(0), mPreEqInUse(false), mMbcInUse(false),
- mPostEqInUse(false), mLimiterInUse(false) {
+DPChannel::DPChannel() : mInitialized(false), mInputGainDb(0), mOutputGainDb(0),
+ mPreEqInUse(false), mMbcInUse(false), mPostEqInUse(false), mLimiterInUse(false) {
}
void DPChannel::init(float inputGain, bool preEqInUse, uint32_t preEqBandCount,
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.h b/media/libeffects/dynamicsproc/dsp/DPBase.h
index 355f64b..e74f91d 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.h
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.h
@@ -272,6 +272,16 @@
mInputGainDb = gain;
}
+ float getOutputGain() const {
+ if (!mInitialized) {
+ return 0;
+ }
+ return mOutputGainDb;
+ }
+ void setOutputGain(float gain) {
+ mOutputGainDb = gain;
+ }
+
DPEq* getPreEq();
DPMbc* getMbc();
DPEq* getPostEq();
@@ -281,6 +291,7 @@
private:
bool mInitialized;
float mInputGainDb;
+ float mOutputGainDb;
DPEq mPreEq;
DPMbc mMbc;
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
index 59195fc..e96a3ef 100644
--- a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
@@ -29,7 +29,7 @@
#define CIRCULAR_BUFFER_UPSAMPLE 4 //4 times buffer size
-static constexpr float MIN_ENVELOPE = 0.000001f;
+static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB
//helper functionS
static inline bool isPowerOf2(unsigned long n) {
return (n & (n - 1)) == 0;
@@ -53,14 +53,6 @@
#define IS_CHANGED(c, a, b) { c |= !compareEquality(a,b); \
(a) = (b); }
-float dBtoLinear(float valueDb) {
- return pow (10, valueDb / 20.0);
-}
-
-float linearToDb(float value) {
- return 20 * log10(value);
-}
-
//ChannelBuffers helper
void ChannelBuffer::initBuffers(unsigned int blockSize, unsigned int overlapSize,
unsigned int halfFftSize, unsigned int samplingRate, DPBase &dpBase) {
@@ -74,7 +66,7 @@
cBOutput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE);
//fill input with half block size...
- for (unsigned int k = 0; k < mBlockSize/2; k++) {
+ for (unsigned int k = 0; k < mBlockSize/2; k++) {
cBInput.write(0);
}
@@ -94,12 +86,14 @@
mMbcBands.size(), mPostEqBands.size());
DPChannel *pChannel = dpBase.getChannel(0);
- if (pChannel != NULL) {
+ if (pChannel != nullptr) {
mPreEqInUse = pChannel->getPreEq()->isInUse();
mMbcInUse = pChannel->getMbc()->isInUse();
mPostEqInUse = pChannel->getPostEq()->isInUse();
mLimiterInUse = pChannel->getLimiter()->isInUse();
}
+
+ mLimiterParams.linkGroup = -1; //no group.
}
void ChannelBuffer::computeBinStartStop(BandParams &bp, size_t binStart) {
@@ -108,8 +102,35 @@
bp.binStop = (int)(0.5 + bp.freqCutoffHz * mBlockSize / mSamplingRate);
}
-//== DPFrequency
+//== LinkedLimiters Helper
+void LinkedLimiters::reset() {
+ mGroupsMap.clear();
+}
+void LinkedLimiters::update(int32_t group, int index) {
+ mGroupsMap[group].push_back(index);
+}
+
+void LinkedLimiters::remove(int index) {
+ //check all groups and if index is found, remove it.
+ //if group is empty afterwards, remove it.
+ for (auto it = mGroupsMap.begin(); it != mGroupsMap.end(); ) {
+ for (auto itIndex = it->second.begin(); itIndex != it->second.end(); ) {
+ if (*itIndex == index) {
+ itIndex = it->second.erase(itIndex);
+ } else {
+ ++itIndex;
+ }
+ }
+ if (it->second.size() == 0) {
+ it = mGroupsMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+//== DPFrequency
void DPFrequency::reset() {
}
@@ -147,14 +168,25 @@
mSamplingRate, *this);
}
- //dsp
+ //effective number of frames processed per second
+ mBlocksPerSecond = (float)mSamplingRate / (mBlockSize - mOverlapSize);
+
fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize);
+
+ //compute window rms for energy compensation
+ mWindowRms = 0;
+ for (size_t i = 0; i < mVWindow.size(); i++) {
+ mWindowRms += mVWindow[i] * mVWindow[i];
+ }
+
+ //Making sure window rms is not zero.
+ mWindowRms = std::max(sqrt(mWindowRms / mVWindow.size()), MIN_ENVELOPE);
}
void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) {
DPChannel *pChannel = getChannel(channelIndex);
- if (pChannel == NULL) {
+ if (pChannel == nullptr) {
ALOGE("Error: updateParameters null DPChannel %d", channelIndex);
return;
}
@@ -166,7 +198,7 @@
//===EqPre
if (cb.mPreEqInUse) {
DPEq *pPreEq = pChannel->getPreEq();
- if (pPreEq == NULL) {
+ if (pPreEq == nullptr) {
ALOGE("Error: updateParameters null PreEq for channel: %d", channelIndex);
return;
}
@@ -174,7 +206,7 @@
if (cb.mPreEqEnabled) {
for (unsigned int b = 0; b < getPreEqBandCount(); b++) {
DPEqBand *pEqBand = pPreEq->getBand(b);
- if (pEqBand == NULL) {
+ if (pEqBand == nullptr) {
ALOGE("Error: updateParameters null PreEqBand for band %d", b);
return; //failed.
}
@@ -222,7 +254,7 @@
bool changed = false;
DPEq *pPostEq = pChannel->getPostEq();
- if (pPostEq == NULL) {
+ if (pPostEq == nullptr) {
ALOGE("Error: updateParameters null postEq for channel: %d", channelIndex);
return; //failed.
}
@@ -230,7 +262,7 @@
if (cb.mPostEqEnabled) {
for (unsigned int b = 0; b < getPostEqBandCount(); b++) {
DPEqBand *pEqBand = pPostEq->getBand(b);
- if (pEqBand == NULL) {
+ if (pEqBand == nullptr) {
ALOGE("Error: updateParameters PostEqBand NULL for band %d", b);
return; //failed.
}
@@ -265,7 +297,7 @@
//===MBC
if (cb.mMbcInUse) {
DPMbc *pMbc = pChannel->getMbc();
- if (pMbc == NULL) {
+ if (pMbc == nullptr) {
ALOGE("Error: updateParameters Mbc NULL for channel: %d", channelIndex);
return;
}
@@ -274,7 +306,7 @@
bool changed = false;
for (unsigned int b = 0; b < getMbcBandCount(); b++) {
DPMbcBand *pMbcBand = pMbc->getBand(b);
- if (pMbcBand == NULL) {
+ if (pMbcBand == nullptr) {
ALOGE("Error: updateParameters MbcBand NULL for band %d", b);
return; //failed.
}
@@ -307,11 +339,38 @@
cb.computeBinStartStop(*pMbcBandParams, binNext);
binNext = pMbcBandParams->binStop + 1;
}
-
}
-
}
}
+
+ //===Limiter
+ if (cb.mLimiterInUse) {
+ bool changed = false;
+ DPLimiter *pLimiter = pChannel->getLimiter();
+ if (pLimiter == nullptr) {
+ ALOGE("Error: updateParameters Limiter NULL for channel: %d", channelIndex);
+ return;
+ }
+ cb.mLimiterEnabled = pLimiter->isEnabled();
+ if (cb.mLimiterEnabled) {
+ IS_CHANGED(changed, cb.mLimiterParams.linkGroup ,
+ (int32_t)pLimiter->getLinkGroup());
+ cb.mLimiterParams.attackTimeMs = pLimiter->getAttackTime();
+ cb.mLimiterParams.releaseTimeMs = pLimiter->getReleaseTime();
+ cb.mLimiterParams.ratio = pLimiter->getRatio();
+ cb.mLimiterParams.thresholdDb = pLimiter->getThreshold();
+ cb.mLimiterParams.postGainDb = pLimiter->getPostGain();
+ }
+
+ if (changed) {
+ ALOGV("limiter changed, recomputing linkGroups for %d", channelIndex);
+ mLinkedLimiters.remove(channelIndex); //in case it was already there.
+ mLinkedLimiters.update(cb.mLimiterParams.linkGroup, channelIndex);
+ }
+ }
+
+ //=== Output Gain
+ cb.outputGainDb = pChannel->getOutputGain();
}
size_t DPFrequency::processSamples(const float *in, float *out, size_t samples) {
@@ -336,12 +395,8 @@
}
}
- //TODO: lookahead limiters
- //TODO: apply linked limiters to all channels.
- //**Process each Channel
- for (int ch = 0; ch < channelCount; ch++) {
- processMono(mChannelBuffers[ch]);
- }
+ //**process all channelBuffers
+ processChannelBuffers(mChannelBuffers);
//** estimate how much data is available in ALL channels
size_t available = mChannelBuffers[0].cBOutput.availableToRead();
@@ -370,62 +425,78 @@
return samples;
}
-size_t DPFrequency::processMono(ChannelBuffer &cb) {
-
+size_t DPFrequency::processChannelBuffers(CBufferVector &channelBuffers) {
+ const int channelCount = channelBuffers.size();
size_t processedSamples = 0;
+ size_t processFrames = mBlockSize - mOverlapSize;
- size_t available = cb.cBInput.availableToRead();
- while (available >= mBlockSize - mOverlapSize) {
-
- //move tail of previous
- for (unsigned int k = 0; k < mOverlapSize; ++k) {
- cb.input[k] = cb.input[mBlockSize - mOverlapSize + k];
- }
-
- //read new available data
- for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
- cb.input[mOverlapSize + k] = cb.cBInput.read();
- }
-
- //## Actual process
- processOneVector(cb.output, cb.input, cb);
- //##End of Process
-
- //mix tail (and capture new tail
- for (unsigned int k = 0; k < mOverlapSize; k++) {
- cb.output[k] += cb.outTail[k];
- cb.outTail[k] = cb.output[mBlockSize - mOverlapSize + k]; //new tail
- }
-
- //output data
- for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
- cb.cBOutput.write(cb.output[k]);
- }
-
- available = cb.cBInput.availableToRead();
+ size_t available = channelBuffers[0].cBInput.availableToRead();
+ for (int ch = 1; ch < channelCount; ch++) {
+ available = std::min(available, channelBuffers[ch].cBInput.availableToRead());
}
+ while (available >= processFrames) {
+ //First pass
+ for (int ch = 0; ch < channelCount; ch++) {
+ ChannelBuffer * pCb = &channelBuffers[ch];
+ //move tail of previous
+ std::copy(pCb->input.begin() + processFrames,
+ pCb->input.end(),
+ pCb->input.begin());
+
+ //read new available data
+ for (unsigned int k = 0; k < processFrames; k++) {
+ pCb->input[mOverlapSize + k] = pCb->cBInput.read();
+ }
+ //first stages: fft, preEq, mbc, postEq and start of Limiter
+ processedSamples += processFirstStages(*pCb);
+ }
+
+ //**compute linked limiters and update levels if needed
+ processLinkedLimiters(channelBuffers);
+
+ //final pass.
+ for (int ch = 0; ch < channelCount; ch++) {
+ ChannelBuffer * pCb = &channelBuffers[ch];
+
+ //linked limiter and ifft
+ processLastStages(*pCb);
+
+ //mix tail (and capture new tail
+ for (unsigned int k = 0; k < mOverlapSize; k++) {
+ pCb->output[k] += pCb->outTail[k];
+ pCb->outTail[k] = pCb->output[processFrames + k]; //new tail
+ }
+
+ //output data
+ for (unsigned int k = 0; k < processFrames; k++) {
+ pCb->cBOutput.write(pCb->output[k]);
+ }
+ }
+ available -= processFrames;
+ }
return processedSamples;
}
-
-size_t DPFrequency::processOneVector(FloatVec & output, FloatVec & input,
- ChannelBuffer &cb) {
+size_t DPFrequency::processFirstStages(ChannelBuffer &cb) {
//##apply window
Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size());
- Eigen::Map<Eigen::VectorXf> eInput(&input[0], input.size());
+ Eigen::Map<Eigen::VectorXf> eInput(&cb.input[0], cb.input.size());
Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window
- //##fft //TODO: refactor frequency transformations away from other stages.
- mFftServer.fwd(mComplexTemp, eWin);
+ //##fft
+ //Note: we are using eigen with the default scaling, which ensures that
+ // IFFT( FFT(x) ) = x.
+ // TODO: optimize by using the noscale option, and compensate with dB scale offsets
+ mFftServer.fwd(cb.complexTemp, eWin);
- size_t cSize = mComplexTemp.size();
+ size_t cSize = cb.complexTemp.size();
size_t maxBin = std::min(cSize/2, mHalfFFTSize);
//== EqPre (always runs)
for (size_t k = 0; k < maxBin; k++) {
- mComplexTemp[k] *= cb.mPreEqFactorVector[k];
+ cb.complexTemp[k] *= cb.mPreEqFactorVector[k];
}
//== MBC
@@ -439,29 +510,31 @@
float preGainSquared = preGainFactor * preGainFactor;
for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
- float fReal = mComplexTemp[k].real();
- float fImag = mComplexTemp[k].imag();
- float fSquare = (fReal * fReal + fImag * fImag) * preGainSquared;
-
- fEnergySum += fSquare;
+ fEnergySum += std::norm(cb.complexTemp[k]) * preGainSquared; //mag squared
}
- fEnergySum = sqrt(fEnergySum /2.0);
+ //Eigen FFT is full spectrum, even if the source was real data.
+ // Each half spectrum has half the energy. This is taken into account with the * 2
+ // factor in the energy computations.
+ // energy = sqrt(sum_components_squared) number_points
+ // in here, the fEnergySum is duplicated to account for the second half spectrum,
+ // and the windowRms is used to normalize by the expected energy reduction
+ // caused by the window used (expected for steady state signals)
+ fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms);
+
+ // updates computed per frame advance.
float fTheta = 0.0;
- float fFAtt = pMbcBandParams->attackTimeMs;
- float fFRel = pMbcBandParams->releaseTimeMs;
-
- float fUpdatesPerSecond = 10; //TODO: compute from framerate
-
+ float fFAttSec = pMbcBandParams->attackTimeMs / 1000; //in seconds
+ float fFRelSec = pMbcBandParams->releaseTimeMs / 1000; //in seconds
if (fEnergySum > pMbcBandParams->previousEnvelope) {
- fTheta = exp(-1.0 / (fFAtt * fUpdatesPerSecond));
+ fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
} else {
- fTheta = exp(-1.0 / (fFRel * fUpdatesPerSecond));
+ fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
}
- float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope;
+ float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope;
//preserve for next iteration
pMbcBandParams->previousEnvelope = fEnv;
@@ -494,7 +567,7 @@
//apply to this band
for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
- mComplexTemp[k] *= fNewFactor;
+ cb.complexTemp[k] *= fNewFactor;
}
} //end per band process
@@ -504,14 +577,96 @@
//== EqPost
if (cb.mPostEqInUse && cb.mPostEqEnabled) {
for (size_t k = 0; k < maxBin; k++) {
- mComplexTemp[k] *= cb.mPostEqFactorVector[k];
+ cb.complexTemp[k] *= cb.mPostEqFactorVector[k];
+ }
+ }
+
+ //== Limiter. First Pass
+ if (cb.mLimiterInUse && cb.mLimiterEnabled) {
+ float fEnergySum = 0;
+ for (size_t k = 0; k < maxBin; k++) {
+ fEnergySum += std::norm(cb.complexTemp[k]);
+ }
+
+ //see explanation above for energy computation logic
+ fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms);
+ float fTheta = 0.0;
+ float fFAttSec = cb.mLimiterParams.attackTimeMs / 1000; //in seconds
+ float fFRelSec = cb.mLimiterParams.releaseTimeMs / 1000; //in seconds
+
+ if (fEnergySum > cb.mLimiterParams.previousEnvelope) {
+ fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
+ } else {
+ fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
+ }
+
+ float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * cb.mLimiterParams.previousEnvelope;
+ //preserve for next iteration
+ cb.mLimiterParams.previousEnvelope = fEnv;
+
+ float fThreshold = dBtoLinear(cb.mLimiterParams.thresholdDb);
+
+ float fNewFactor = 1.0;
+
+ if (fEnv > fThreshold) {
+ float fDbAbove = linearToDb(fThreshold / fEnv);
+ float fDbTarget = fDbAbove / cb.mLimiterParams.ratio;
+ float fDbChange = fDbAbove - fDbTarget;
+ fNewFactor = dBtoLinear(fDbChange);
+ }
+
+ if (fNewFactor < 0) {
+ fNewFactor = 0;
+ }
+
+ cb.mLimiterParams.newFactor = fNewFactor;
+
+ } //end Limiter
+ return mBlockSize;
+}
+
+void DPFrequency::processLinkedLimiters(CBufferVector &channelBuffers) {
+
+ const int channelCount = channelBuffers.size();
+ for (auto &groupPair : mLinkedLimiters.mGroupsMap) {
+ float minFactor = 1.0;
+ //estimate minfactor for all linked
+ for(int index : groupPair.second) {
+ if (index >= 0 && index < channelCount) {
+ minFactor = std::min(channelBuffers[index].mLimiterParams.newFactor, minFactor);
+ }
+ }
+ //apply minFactor
+ for(int index : groupPair.second) {
+ if (index >= 0 && index < channelCount) {
+ channelBuffers[index].mLimiterParams.linkFactor = minFactor;
+ }
+ }
+ }
+}
+
+size_t DPFrequency::processLastStages(ChannelBuffer &cb) {
+
+ float outputGainFactor = dBtoLinear(cb.outputGainDb);
+ //== Limiter. last Pass
+ if (cb.mLimiterInUse && cb.mLimiterEnabled) {
+ //compute factor, with post-gain
+ float factor = cb.mLimiterParams.linkFactor * dBtoLinear(cb.mLimiterParams.postGainDb);
+ outputGainFactor *= factor;
+ }
+
+ //apply to all if != 1.0
+ if (!compareEquality(outputGainFactor, 1.0f)) {
+ size_t cSize = cb.complexTemp.size();
+ size_t maxBin = std::min(cSize/2, mHalfFFTSize);
+ for (size_t k = 0; k < maxBin; k++) {
+ cb.complexTemp[k] *= outputGainFactor;
}
}
//##ifft directly to output.
- Eigen::Map<Eigen::VectorXf> eOutput(&output[0], output.size());
- mFftServer.inv(eOutput, mComplexTemp);
-
+ Eigen::Map<Eigen::VectorXf> eOutput(&cb.output[0], cb.output.size());
+ mFftServer.inv(eOutput, cb.complexTemp);
return mBlockSize;
}
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.h b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
index 9919142..be8771d 100644
--- a/media/libeffects/dynamicsproc/dsp/DPFrequency.h
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
@@ -39,8 +39,11 @@
FloatVec output; // time domain temp vector for output
FloatVec outTail; // time domain temp vector for output tail (for overlap-add method)
+ Eigen::VectorXcf complexTemp; // complex temp vector for frequency domain operations
+
//Current parameters
float inputGainDb;
+ float outputGainDb;
struct BandParams {
bool enabled;
float freqCutoffHz;
@@ -64,6 +67,19 @@
//Historic values
float previousEnvelope;
};
+ struct LimiterParams {
+ int32_t linkGroup;
+ float attackTimeMs;
+ float releaseTimeMs;
+ float ratio;
+ float thresholdDb;
+ float postGainDb;
+
+ //Historic values
+ float previousEnvelope;
+ float newFactor;
+ float linkFactor;
+ };
bool mPreEqInUse;
bool mPreEqEnabled;
@@ -79,6 +95,7 @@
bool mLimiterInUse;
bool mLimiterEnabled;
+ LimiterParams mLimiterParams;
FloatVec mPreEqFactorVector; // temp pre-computed vector to shape spectrum at preEQ stage
FloatVec mPostEqFactorVector; // temp pre-computed vector to shape spectrum at postEQ stage
@@ -91,6 +108,18 @@
};
+using CBufferVector = std::vector<ChannelBuffer>;
+
+using GroupsMap = std::map<int32_t, IntVec>;
+
+class LinkedLimiters {
+public:
+ void reset();
+ void update(int32_t group, int index);
+ void remove(int index);
+ GroupsMap mGroupsMap;
+};
+
class DPFrequency : public DPBase {
public:
virtual size_t processSamples(const float *in, float *out, size_t samples);
@@ -104,16 +133,25 @@
size_t processMono(ChannelBuffer &cb);
size_t processOneVector(FloatVec &output, FloatVec &input, ChannelBuffer &cb);
+ size_t processChannelBuffers(CBufferVector &channelBuffers);
+ size_t processFirstStages(ChannelBuffer &cb);
+ size_t processLastStages(ChannelBuffer &cb);
+ void processLinkedLimiters(CBufferVector &channelBuffers);
+
size_t mBlockSize;
size_t mHalfFFTSize;
size_t mOverlapSize;
size_t mSamplingRate;
- std::vector<ChannelBuffer> mChannelBuffers;
+ float mBlocksPerSecond;
+
+ CBufferVector mChannelBuffers;
+
+ LinkedLimiters mLinkedLimiters;
//dsp
FloatVec mVWindow; //window class.
- Eigen::VectorXcf mComplexTemp;
+ float mWindowRms;
Eigen::FFT<float> mFftServer;
};
diff --git a/media/libeffects/dynamicsproc/dsp/RDsp.h b/media/libeffects/dynamicsproc/dsp/RDsp.h
index 1048442..cfa1305 100644
--- a/media/libeffects/dynamicsproc/dsp/RDsp.h
+++ b/media/libeffects/dynamicsproc/dsp/RDsp.h
@@ -20,10 +20,25 @@
#include <complex>
#include <log/log.h>
#include <vector>
+#include <map>
using FloatVec = std::vector<float>;
+using IntVec = std::vector<int>;
using ComplexVec = std::vector<std::complex<float>>;
// =======
+// Helper Functions
+// =======
+template <class T>
+static T dBtoLinear(T valueDb) {
+ return pow (10, valueDb / 20.0);
+}
+
+template <class T>
+static T linearToDb(T value) {
+ return 20 * log10(value);
+}
+
+// =======
// DSP window creation
// =======
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index cb0e927..034f7c2 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -47,6 +47,8 @@
const char * const AudioParameter::valueOn = AUDIO_PARAMETER_VALUE_ON;
const char * const AudioParameter::valueOff = AUDIO_PARAMETER_VALUE_OFF;
const char * const AudioParameter::valueListSeparator = AUDIO_PARAMETER_VALUE_LIST_SEPARATOR;
+const char * const AudioParameter::keyReconfigA2dp = AUDIO_PARAMETER_RECONFIG_A2DP;
+const char * const AudioParameter::keyReconfigA2dpSupported = AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED;
AudioParameter::AudioParameter(const String8& keyValuePairs)
{
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index f1b7806..6ad2441 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -70,6 +70,7 @@
kKeyWantsNALFragments = 'NALf',
kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool)
+ kKeyIsMuxerData = 'muxd', // int32_t (bool)
kKeyTime = 'time', // int64_t (usecs)
kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs)
kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp)
@@ -220,6 +221,7 @@
kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track
kKeyExifOffset = 'exof', // int64_t, Exif data offset
kKeyExifSize = 'exsz', // int64_t, Exif data size
+ kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block
};
enum {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index cbc3015..23d66bb 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -934,7 +934,11 @@
sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat();
if (meta == NULL) {
ALOGE("no metadata for track %zu", trackIndex);
- return NULL;
+ format->setInt32("type", MEDIA_TRACK_TYPE_UNKNOWN);
+ format->setString("mime", "application/octet-stream");
+ format->setString("language", "und");
+
+ return format;
}
const char *mime;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 0a1bdfe..a5f5fc6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1447,6 +1447,29 @@
break;
}
+ case kWhatGetStats:
+ {
+ ALOGV("kWhatGetStats");
+
+ Vector<sp<AMessage>> *trackStats;
+ CHECK(msg->findPointer("trackstats", (void**)&trackStats));
+
+ trackStats->clear();
+ if (mVideoDecoder != NULL) {
+ trackStats->push_back(mVideoDecoder->getStats());
+ }
+ if (mAudioDecoder != NULL) {
+ trackStats->push_back(mAudioDecoder->getStats());
+ }
+
+ // respond for synchronization
+ sp<AMessage> response = new AMessage;
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+ break;
+ }
+
default:
TRESPASS();
break;
@@ -2210,16 +2233,16 @@
return renderer->getCurrentPosition(mediaUs);
}
-void NuPlayer::getStats(Vector<sp<AMessage> > *mTrackStats) {
- CHECK(mTrackStats != NULL);
+void NuPlayer::getStats(Vector<sp<AMessage> > *trackStats) {
+ CHECK(trackStats != NULL);
- mTrackStats->clear();
- if (mVideoDecoder != NULL) {
- mTrackStats->push_back(mVideoDecoder->getStats());
- }
- if (mAudioDecoder != NULL) {
- mTrackStats->push_back(mAudioDecoder->getStats());
- }
+ ALOGV("NuPlayer::getStats()");
+ sp<AMessage> msg = new AMessage(kWhatGetStats, this);
+ msg->setPointer("trackstats", trackStats);
+
+ sp<AMessage> response;
+ (void) msg->postAndAwaitResponse(&response);
+ // response is for synchronization, ignore contents
}
sp<MetaData> NuPlayer::getFileMeta() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 3a7ef4e..e400d16 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -88,7 +88,7 @@
status_t getSelectedTrack(int32_t type, Parcel* reply) const;
status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
status_t getCurrentPosition(int64_t *mediaUs);
- void getStats(Vector<sp<AMessage> > *mTrackStats);
+ void getStats(Vector<sp<AMessage> > *trackStats);
sp<MetaData> getFileMeta();
float getFrameRate();
@@ -159,6 +159,7 @@
kWhatPrepareDrm = 'pDrm',
kWhatReleaseDrm = 'rDrm',
kWhatMediaClockNotify = 'mckN',
+ kWhatGetStats = 'gSts',
};
wp<NuPlayerDriver> mDriver;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 0402fca..fb12360 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -155,7 +155,9 @@
break;
default:
ALOGE("Unknown track type: %d", track.mTrackType);
- return NULL;
+ format->setInt32("type", MEDIA_TRACK_TYPE_UNKNOWN);
+ format->setString("mime", "application/octet-stream");
+ return format;
}
// For CEA-608 CC1, field 0 channel 0
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 2a08f62..69cd82e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -745,6 +745,7 @@
sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
reply->setSize("buffer-ix", index);
reply->setInt32("generation", mBufferGeneration);
+ reply->setSize("size", size);
if (eos) {
ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
@@ -1127,6 +1128,7 @@
int32_t render;
size_t bufferIx;
int32_t eos;
+ size_t size;
CHECK(msg->findSize("buffer-ix", &bufferIx));
if (!mIsAudio) {
@@ -1146,7 +1148,10 @@
CHECK(msg->findInt64("timestampNs", ×tampNs));
err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
} else {
- mNumOutputFramesDropped += !mIsAudio;
+ if (!msg->findInt32("eos", &eos) || !eos ||
+ !msg->findSize("size", &size) || size) {
+ mNumOutputFramesDropped += !mIsAudio;
+ }
err = mCodec->releaseOutputBuffer(bufferIx);
}
if (err != OK) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2abea9e..bf5ae2c 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -5322,13 +5322,13 @@
convertCodecColorAspectsToPlatformAspects(aspects, &range, &standard, &transfer);
// if some aspects are unspecified, use dataspace fields
- if (range != 0) {
+ if (range == 0) {
range = (dataSpace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
}
- if (standard != 0) {
+ if (standard == 0) {
standard = (dataSpace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
}
- if (transfer != 0) {
+ if (transfer == 0) {
transfer = (dataSpace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 550a99c..6ff3d78 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -83,6 +83,9 @@
static const char kMetaKey_TemporalLayerCount[] = "com.android.video.temporal_layers_count";
static const int kTimestampDebugCount = 10;
+static const int kItemIdBase = 10000;
+static const char kExifHeader[] = {'E', 'x', 'i', 'f', '\0', '\0'};
+static const int32_t kTiffHeaderOffset = htonl(sizeof(kExifHeader));
static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
kHevcNalUnitTypeVps,
@@ -112,7 +115,7 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
- int32_t getMetaSizeIncrease() const;
+ int32_t getMetaSizeIncrease(int32_t angle, int32_t trackCount) const;
void writeTrackHeader(bool use32BitOffset = true);
int64_t getMinCttsOffsetTimeUs();
void bufferChunk(int64_t timestampUs);
@@ -122,8 +125,10 @@
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; }
+ bool isExifData(const MediaBufferBase *buffer) const;
void addChunkOffset(off64_t offset);
- void addItemOffsetAndSize(off64_t offset, size_t size);
+ void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
+ void flushItemRefs();
int32_t getTrackId() const { return mTrackId; }
status_t dump(int fd, const Vector<String16>& args) const;
static const char *getFourCCForMime(const char *mime);
@@ -355,7 +360,9 @@
int32_t mRotation;
Vector<uint16_t> mProperties;
- Vector<uint16_t> mDimgRefs;
+ ItemRefs mDimgRefs;
+ ItemRefs mCdscRefs;
+ uint16_t mImageItemId;
int32_t mIsPrimary;
int32_t mWidth, mHeight;
int32_t mTileWidth, mTileHeight;
@@ -499,6 +506,7 @@
mPrimaryItemId = 0;
mAssociationEntryCount = 0;
mNumGrids = 0;
+ mHasRefs = false;
// Following variables only need to be set for the first recording session.
// And they will stay the same for all the recording sessions.
@@ -680,7 +688,12 @@
#endif
}
-int64_t MPEG4Writer::estimateFileLevelMetaSize() {
+int64_t MPEG4Writer::estimateFileLevelMetaSize(MetaData *params) {
+ int32_t rotation;
+ if (!params || !params->findInt32(kKeyRotation, &rotation)) {
+ rotation = 0;
+ }
+
// base meta size
int64_t metaSize = 12 // meta fullbox header
+ 33 // hdlr box
@@ -695,7 +708,7 @@
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
if ((*it)->isHeic()) {
- metaSize += (*it)->getMetaSizeIncrease();
+ metaSize += (*it)->getMetaSizeIncrease(rotation, mTracks.size());
}
}
@@ -900,7 +913,7 @@
if (mInMemoryCacheSize == 0) {
int32_t bitRate = -1;
if (mHasFileLevelMeta) {
- mInMemoryCacheSize += estimateFileLevelMetaSize();
+ mInMemoryCacheSize += estimateFileLevelMetaSize(param);
}
if (mHasMoovBox) {
if (param) {
@@ -1344,12 +1357,17 @@
}
off64_t MPEG4Writer::addSample_l(
- MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten) {
+ MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten) {
off64_t old_offset = mOffset;
if (usePrefix) {
addMultipleLengthPrefixedSamples_l(buffer);
} else {
+ if (isExif) {
+ ::write(mFd, &kTiffHeaderOffset, 4); // exif_tiff_header_offset field
+ mOffset += 4;
+ }
+
::write(mFd,
(const uint8_t *)buffer->data() + buffer->range_offset(),
buffer->range_length());
@@ -1767,6 +1785,9 @@
mReachedEOS(false),
mStartTimestampUs(-1),
mRotation(0),
+ mDimgRefs("dimg"),
+ mCdscRefs("cdsc"),
+ mImageItemId(0),
mIsPrimary(0),
mWidth(0),
mHeight(0),
@@ -1933,6 +1954,13 @@
return OK;
}
+bool MPEG4Writer::Track::isExifData(const MediaBufferBase *buffer) const {
+ return mIsHeic
+ && (buffer->range_length() > sizeof(kExifHeader))
+ && !memcmp((uint8_t *)buffer->data() + buffer->range_offset(),
+ kExifHeader, sizeof(kExifHeader));
+}
+
void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
CHECK(!mIsHeic);
if (mOwner->use32BitFileOffset()) {
@@ -1943,7 +1971,7 @@
}
}
-void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size) {
+void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size, bool isExif) {
CHECK(mIsHeic);
if (offset > UINT32_MAX || size > UINT32_MAX) {
@@ -1954,6 +1982,18 @@
if (mIsMalformed) {
return;
}
+
+ if (isExif) {
+ mCdscRefs.value.push_back(mOwner->addItem_l({
+ .itemType = "Exif",
+ .isPrimary = false,
+ .isHidden = false,
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ }));
+ return;
+ }
+
if (mTileIndex >= mNumTiles) {
ALOGW("Ignoring excess tiles!");
return;
@@ -1968,7 +2008,7 @@
default: break; // don't set if invalid
}
- bool hasGrid = (mNumTiles > 1);
+ bool hasGrid = (mTileWidth > 0);
if (mProperties.empty()) {
mProperties.push_back(mOwner->addProperty_l({
@@ -1990,18 +2030,16 @@
}
}
- uint16_t itemId = mOwner->addItem_l({
- .itemType = "hvc1",
- .isPrimary = hasGrid ? false : (mIsPrimary != 0),
- .isHidden = hasGrid,
- .offset = (uint32_t)offset,
- .size = (uint32_t)size,
- .properties = mProperties,
- });
-
mTileIndex++;
if (hasGrid) {
- mDimgRefs.push_back(itemId);
+ mDimgRefs.value.push_back(mOwner->addItem_l({
+ .itemType = "hvc1",
+ .isPrimary = false,
+ .isHidden = true,
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ .properties = mProperties,
+ }));
if (mTileIndex == mNumTiles) {
mProperties.clear();
@@ -2016,7 +2054,7 @@
.rotation = heifRotation,
}));
}
- mOwner->addItem_l({
+ mImageItemId = mOwner->addItem_l({
.itemType = "grid",
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
@@ -2025,9 +2063,31 @@
.width = (uint32_t)mWidth,
.height = (uint32_t)mHeight,
.properties = mProperties,
- .dimgRefs = mDimgRefs,
});
}
+ } else {
+ mImageItemId = mOwner->addItem_l({
+ .itemType = "hvc1",
+ .isPrimary = (mIsPrimary != 0),
+ .isHidden = false,
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ .properties = mProperties,
+ });
+ }
+}
+
+// Flush out the item refs for this track. Note that it must be called after the
+// writer thread has stopped, because there might be pending items in the last
+// few chunks written by the writer thread (as opposed to the track). In particular,
+// it affects the 'dimg' refs for tiled image, as we only have the refs after the
+// last tile sample is written.
+void MPEG4Writer::Track::flushItemRefs() {
+ CHECK(mIsHeic);
+
+ if (mImageItemId > 0) {
+ mOwner->addRefs_l(mImageItemId, mDimgRefs);
+ mOwner->addRefs_l(mImageItemId, mCdscRefs);
}
}
@@ -2174,15 +2234,20 @@
chunk->mTimeStampUs, chunk->mTrack->getTrackType());
int32_t isFirstSample = true;
- bool usePrefix = chunk->mTrack->usePrefix();
while (!chunk->mSamples.empty()) {
List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
+ int32_t isExif;
+ if (!(*it)->meta_data().findInt32(kKeyIsExif, &isExif)) {
+ isExif = 0;
+ }
+ bool usePrefix = chunk->mTrack->usePrefix() && !isExif;
+
size_t bytesWritten;
- off64_t offset = addSample_l(*it, usePrefix, &bytesWritten);
+ off64_t offset = addSample_l(*it, usePrefix, isExif, &bytesWritten);
if (chunk->mTrack->isHeic()) {
- chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten);
+ chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten, isExif);
} else if (isFirstSample) {
chunk->mTrack->addChunkOffset(offset);
isFirstSample = false;
@@ -2904,6 +2969,19 @@
break;
}
+ bool isExif = false;
+ int32_t isMuxerData;
+ if (buffer->meta_data().findInt32(kKeyIsMuxerData, &isMuxerData) && isMuxerData) {
+ // We only support one type of muxer data, which is Exif data block.
+ isExif = isExifData(buffer);
+ if (!isExif) {
+ ALOGW("Ignoring bad Exif data block");
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+ }
+
++nActualFrames;
// Make a deep copy of the MediaBuffer and Metadata and release
@@ -2916,10 +2994,15 @@
buffer->release();
buffer = NULL;
- if (usePrefix()) StripStartcode(copy);
+ if (isExif) {
+ copy->meta_data().setInt32(kKeyIsExif, 1);
+ }
+ bool usePrefix = this->usePrefix() && !isExif;
+
+ if (usePrefix) StripStartcode(copy);
size_t sampleSize = copy->range_length();
- if (usePrefix()) {
+ if (usePrefix) {
if (mOwner->useNalLengthFour()) {
sampleSize += 4;
} else {
@@ -3185,10 +3268,10 @@
}
if (!hasMultipleTracks) {
size_t bytesWritten;
- off64_t offset = mOwner->addSample_l(copy, usePrefix(), &bytesWritten);
+ off64_t offset = mOwner->addSample_l(copy, usePrefix, isExif, &bytesWritten);
if (mIsHeic) {
- addItemOffsetAndSize(offset, bytesWritten);
+ addItemOffsetAndSize(offset, bytesWritten, isExif);
} else {
uint32_t count = (mOwner->use32BitFileOffset()
? mStcoTableEntries->count()
@@ -3450,10 +3533,12 @@
return mEstimatedTrackSizeBytes;
}
-int32_t MPEG4Writer::Track::getMetaSizeIncrease() const {
+int32_t MPEG4Writer::Track::getMetaSizeIncrease(
+ int32_t angle, int32_t trackCount) const {
CHECK(mIsHeic);
- int32_t grid = (mNumTiles > 1);
+ int32_t grid = (mTileWidth > 0);
+ int32_t rotate = (angle > 0);
// Note that the rotation angle is in the file meta, and we don't have
// it until start, so here the calculation has to assume rotation.
@@ -3461,25 +3546,34 @@
// increase to ipco
int32_t increase = 20 * (grid + 1) // 'ispe' property
+ (8 + mCodecSpecificDataSize) // 'hvcC' property
- + 9; // 'irot' property (worst case)
+ ;
+
+ if (rotate) {
+ increase += 9; // 'irot' property (worst case)
+ }
// increase to iref and idat
if (grid) {
- increase += (8 + 2 + 2 + mNumTiles * 2) // 'dimg' in iref
- + 12; // ImageGrid in 'idat' (worst case)
+ increase += (12 + mNumTiles * 2) // 'dimg' in iref
+ + 12; // ImageGrid in 'idat' (worst case)
}
- // increase to iloc, iinf and ipma
- increase += (16 // increase to 'iloc'
- + 21 // increase to 'iinf'
- + (3 + 2 * 2)) // increase to 'ipma' (worst case, 2 props x 2 bytes)
- * (mNumTiles + grid);
+ increase += (12 + 2); // 'cdsc' in iref
- // adjust to ipma:
- // if rotation is present and only one tile, it could ref 3 properties
- if (!grid) {
- increase += 2;
- }
+ // increase to iloc, iinf
+ increase += (16 // increase to 'iloc'
+ + 21) // increase to 'iinf'
+ * (mNumTiles + grid + 1); // "+1" is for 'Exif'
+
+ // When total # of properties is > 127, the properties id becomes 2-byte.
+ // We write 4 properties at most for each image (2x'ispe', 1x'hvcC', 1x'irot').
+ // Set the threshold to be 30.
+ int32_t propBytes = trackCount > 30 ? 2 : 1;
+
+ // increase to ipma
+ increase += (3 + 2 * propBytes) * mNumTiles // 'ispe' + 'hvcC'
+ + grid * (3 + propBytes) // 'ispe' for grid
+ + rotate * propBytes; // 'irot' (either on grid or tile)
return increase;
}
@@ -4239,7 +4333,7 @@
writeInt16((uint16_t)itemCount);
for (size_t i = 0; i < itemCount; i++) {
writeInfeBox(mItems[i].itemId, mItems[i].itemType,
- mItems[i].isHidden ? 1 : 0);
+ (mItems[i].isImage() && mItems[i].isHidden) ? 1 : 0);
}
endBox();
@@ -4274,21 +4368,21 @@
writeInt32(0); // Version = 0, Flags = 0
{
for (size_t i = 0; i < mItems.size(); i++) {
- if (!mItems[i].isGrid()) {
- continue;
+ for (size_t r = 0; r < mItems[i].refsList.size(); r++) {
+ const ItemRefs &refs = mItems[i].refsList[r];
+ beginBox(refs.key);
+ writeInt16(mItems[i].itemId);
+ size_t refCount = refs.value.size();
+ if (refCount > 65535) {
+ ALOGW("too many entries in %s", refs.key);
+ refCount = 65535;
+ }
+ writeInt16((uint16_t)refCount);
+ for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
+ writeInt16(refs.value[refIndex]);
+ }
+ endBox();
}
- beginBox("dimg");
- writeInt16(mItems[i].itemId);
- size_t refCount = mItems[i].dimgRefs.size();
- if (refCount > 65535) {
- ALOGW("too many entries in dimg");
- refCount = 65535;
- }
- writeInt16((uint16_t)refCount);
- for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
- writeInt16(mItems[i].dimgRefs[refIndex]);
- }
- endBox();
}
}
endBox();
@@ -4384,32 +4478,45 @@
}
void MPEG4Writer::writeFileLevelMetaBox() {
- if (mItems.empty()) {
- ALOGE("no valid item was found");
- return;
- }
-
// patch up the mPrimaryItemId and count items with prop associations
uint16_t firstVisibleItemId = 0;
+ uint16_t firstImageItemId = 0;
for (size_t index = 0; index < mItems.size(); index++) {
+ if (!mItems[index].isImage()) continue;
+
if (mItems[index].isPrimary) {
mPrimaryItemId = mItems[index].itemId;
- } else if (!firstVisibleItemId && !mItems[index].isHidden) {
+ }
+ if (!firstImageItemId) {
+ firstImageItemId = mItems[index].itemId;
+ }
+ if (!firstVisibleItemId && !mItems[index].isHidden) {
firstVisibleItemId = mItems[index].itemId;
}
-
if (!mItems[index].properties.empty()) {
mAssociationEntryCount++;
}
}
+ if (!firstImageItemId) {
+ ALOGE("no valid image was found");
+ return;
+ }
+
if (mPrimaryItemId == 0) {
if (firstVisibleItemId > 0) {
- ALOGW("didn't find primary, using first visible item");
+ ALOGW("didn't find primary, using first visible image");
mPrimaryItemId = firstVisibleItemId;
} else {
- ALOGW("no primary and no visible item, using first item");
- mPrimaryItemId = mItems[0].itemId;
+ ALOGW("no primary and no visible item, using first image");
+ mPrimaryItemId = firstImageItemId;
+ }
+ }
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ if ((*it)->isHeic()) {
+ (*it)->flushItemRefs();
}
}
@@ -4422,6 +4529,8 @@
writeIprpBox();
if (mNumGrids > 0) {
writeIdatBox();
+ }
+ if (mHasRefs) {
writeIrefBox();
}
endBox();
@@ -4445,8 +4554,8 @@
size_t index = mItems.size();
mItems.push_back(info);
- // make the item id start at 10000
- mItems.editItemAt(index).itemId = index + 10000;
+ // make the item id start at kItemIdBase
+ mItems.editItemAt(index).itemId = index + kItemIdBase;
#if (LOG_NDEBUG==0)
if (!info.properties.empty()) {
@@ -4464,6 +4573,20 @@
return mItems[index].itemId;
}
+void MPEG4Writer::addRefs_l(uint16_t itemId, const ItemRefs &refs) {
+ if (refs.value.empty()) {
+ return;
+ }
+ if (itemId < kItemIdBase) {
+ ALOGW("itemId shouldn't be smaller than kItemIdBase");
+ return;
+ }
+
+ size_t index = itemId - kItemIdBase;
+ mItems.editItemAt(index).refsList.push_back(refs);
+ mHasRefs = true;
+}
+
/*
* Geodata is stored according to ISO-6709 standard.
*/
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 06a49d0..8436591 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -854,8 +854,7 @@
//static
sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
- static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
- if (ccodecEnabled && name.startsWithIgnoreCase("c2.")) {
+ if (name.startsWithIgnoreCase("c2.")) {
return CreateCCodec();
} else if (name.startsWithIgnoreCase("omx.")) {
// at this time only ACodec specifies a mime type.
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 9a33168..8598a49 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -93,9 +93,7 @@
std::vector<MediaCodecListBuilderBase *> GetBuilders() {
std::vector<MediaCodecListBuilderBase *> builders {&sOmxInfoBuilder};
- if (property_get_bool("debug.stagefright.ccodec", false)) {
- builders.push_back(GetCodec2InfoBuilder());
- }
+ builders.push_back(GetCodec2InfoBuilder());
return builders;
}
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 23e543d..98f59b5 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -190,6 +190,10 @@
sampleMetaData.setInt32(kKeyIsSyncFrame, true);
}
+ if (flags & MediaCodec::BUFFER_FLAG_MUXER_DATA) {
+ sampleMetaData.setInt32(kKeyIsMuxerData, 1);
+ }
+
sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
// This pushBuffer will wait until the mediaBuffer is consumed.
return currentTrack->pushBuffer(mediaBuffer);
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 7b41362..f18940d 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -132,7 +132,7 @@
status_t startTracks(MetaData *params);
size_t numTracks();
int64_t estimateMoovBoxSize(int32_t bitRate);
- int64_t estimateFileLevelMetaSize();
+ int64_t estimateFileLevelMetaSize(MetaData *params);
void writeCachedBoxToFile(const char *type);
struct Chunk {
@@ -167,8 +167,10 @@
Condition mChunkReadyCondition; // Signal that chunks are available
// HEIF writing
+ typedef key_value_pair_t< const char *, Vector<uint16_t> > ItemRefs;
typedef struct _ItemInfo {
bool isGrid() const { return !strcmp("grid", itemType); }
+ bool isImage() const { return !strcmp("hvc1", itemType) || isGrid(); }
const char *itemType;
uint16_t itemId;
bool isPrimary;
@@ -188,7 +190,7 @@
};
};
Vector<uint16_t> properties;
- Vector<uint16_t> dimgRefs;
+ Vector<ItemRefs> refsList;
} ItemInfo;
typedef struct _ItemProperty {
@@ -204,6 +206,7 @@
uint32_t mPrimaryItemId;
uint32_t mAssociationEntryCount;
uint32_t mNumGrids;
+ bool mHasRefs;
Vector<ItemInfo> mItems;
Vector<ItemProperty> mProperties;
@@ -252,11 +255,12 @@
void initInternal(int fd, bool isFirstSession);
// Acquire lock before calling these methods
- off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten);
+ off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten);
void addLengthPrefixedSample_l(MediaBuffer *buffer);
void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
uint16_t addProperty_l(const ItemProperty &);
uint16_t addItem_l(const ItemInfo &);
+ void addRefs_l(uint16_t itemId, const ItemRefs &);
bool exceedsFileSizeLimit();
bool use32BitFileOffset() const;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 67808f1..ad02004 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -61,9 +61,11 @@
};
enum BufferFlags {
- BUFFER_FLAG_SYNCFRAME = 1,
- BUFFER_FLAG_CODECCONFIG = 2,
- BUFFER_FLAG_EOS = 4,
+ BUFFER_FLAG_SYNCFRAME = 1,
+ BUFFER_FLAG_CODECCONFIG = 2,
+ BUFFER_FLAG_EOS = 4,
+ BUFFER_FLAG_PARTIAL_FRAME = 8,
+ BUFFER_FLAG_MUXER_DATA = 16,
};
enum {
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index dcf223c..2047dfd 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -595,7 +595,8 @@
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
|| mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
// Older effects may require exact STEREO position mask.
- if (mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) {
+ if (mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
+ && (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
ALOGV("Overriding effect input channels %#x as STEREO", mConfig.inputCfg.channels);
mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ee16e07..1e5f9ae 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -368,6 +368,9 @@
const char *device_name)
{
status_t status;
+ String8 reply;
+ AudioParameter param;
+ int isReconfigA2dpSupported = 0;
ALOGV("handleDeviceConfigChange(() device: 0x%X, address %s name %s",
device, device_address, device_name);
@@ -384,6 +387,26 @@
return NO_ERROR;
}
+ // For offloaded A2DP, Hw modules may have the capability to
+ // configure codecs. Check if any of the loaded hw modules
+ // supports this.
+ // If supported, send a set parameter to configure A2DP codecs
+ // and return. No need to toggle device state.
+ if (device & AUDIO_DEVICE_OUT_ALL_A2DP) {
+ reply = mpClientInterface->getParameters(
+ AUDIO_IO_HANDLE_NONE,
+ String8(AudioParameter::keyReconfigA2dpSupported));
+ AudioParameter repliedParameters(reply);
+ repliedParameters.getInt(
+ String8(AudioParameter::keyReconfigA2dpSupported), isReconfigA2dpSupported);
+ if (isReconfigA2dpSupported) {
+ const String8 key(AudioParameter::keyReconfigA2dp);
+ param.add(key, String8("true"));
+ mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString());
+ return NO_ERROR;
+ }
+ }
+
// Toggle the device state: UNAVAILABLE -> AVAILABLE
// This will force reading again the device configuration
status = setDeviceConnectionState(device,
@@ -2788,9 +2811,32 @@
return NO_ERROR;
}
-status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused)
+status_t AudioPolicyManager::getAudioPort(struct audio_port *port)
{
- return NO_ERROR;
+ if (port == nullptr || port->id == AUDIO_PORT_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ sp<DeviceDescriptor> dev = mAvailableOutputDevices.getDeviceFromId(port->id);
+ if (dev != 0) {
+ dev->toAudioPort(port);
+ return NO_ERROR;
+ }
+ dev = mAvailableInputDevices.getDeviceFromId(port->id);
+ if (dev != 0) {
+ dev->toAudioPort(port);
+ return NO_ERROR;
+ }
+ sp<SwAudioOutputDescriptor> out = mOutputs.getOutputFromId(port->id);
+ if (out != 0) {
+ out->toAudioPort(port);
+ return NO_ERROR;
+ }
+ sp<AudioInputDescriptor> in = mInputs.getInputFromId(port->id);
+ if (in != 0) {
+ in->toAudioPort(port);
+ return NO_ERROR;
+ }
+ return BAD_VALUE;
}
status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 9592b6a..66ac050 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -408,6 +408,7 @@
client->active = false;
client->isConcurrent = false;
client->isVirtualDevice = false; //TODO : update from APM->getInputForAttr()
+ client->deviceId = *selectedDeviceId;
mAudioRecordClients.add(*portId, client);
}
@@ -434,7 +435,8 @@
return rawbuffer;
}
-static std::string audioConcurrencyString(AudioPolicyInterface::concurrency_type__mask_t concurrency)
+static std::string audioConcurrencyString(
+ AudioPolicyInterface::concurrency_type__mask_t concurrency)
{
char buffer[64]; // oversized
if (concurrency & AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL) {
@@ -450,6 +452,17 @@
return &buffer[1];
}
+std::string AudioPolicyService::getDeviceTypeStrForPortId(audio_port_handle_t portId) {
+ std::string typeStr;
+ struct audio_port port = {};
+ port.id = portId;
+ status_t status = mAudioPolicyManager->getAudioPort(&port);
+ if (status == NO_ERROR && port.type == AUDIO_PORT_TYPE_DEVICE) {
+ deviceToString(port.ext.device.type, typeStr);
+ }
+ return typeStr;
+}
+
status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced)
{
if (mAudioPolicyManager == NULL) {
@@ -498,9 +511,14 @@
static constexpr char kAudioPolicyRqstSrc[] = "android.media.audiopolicy.rqst.src";
static constexpr char kAudioPolicyRqstPkg[] = "android.media.audiopolicy.rqst.pkg";
static constexpr char kAudioPolicyRqstSession[] = "android.media.audiopolicy.rqst.session";
+ static constexpr char kAudioPolicyRqstDevice[] =
+ "android.media.audiopolicy.rqst.device";
static constexpr char kAudioPolicyActiveSrc[] = "android.media.audiopolicy.active.src";
static constexpr char kAudioPolicyActivePkg[] = "android.media.audiopolicy.active.pkg";
- static constexpr char kAudioPolicyActiveSession[] = "android.media.audiopolicy.active.session";
+ static constexpr char kAudioPolicyActiveSession[] =
+ "android.media.audiopolicy.active.session";
+ static constexpr char kAudioPolicyActiveDevice[] =
+ "android.media.audiopolicy.active.device";
MediaAnalyticsItem *item = new MediaAnalyticsItem(kAudioPolicy);
if (item != NULL) {
@@ -508,10 +526,15 @@
item->setCString(kAudioPolicyReason, audioConcurrencyString(concurrency).c_str());
item->setInt32(kAudioPolicyStatus, status);
- item->setCString(kAudioPolicyRqstSrc, audioSourceString(client->attributes.source).c_str());
- item->setCString(kAudioPolicyRqstPkg, std::string(String8(client->opPackageName).string()).c_str());
+ item->setCString(kAudioPolicyRqstSrc,
+ audioSourceString(client->attributes.source).c_str());
+ item->setCString(kAudioPolicyRqstPkg,
+ std::string(String8(client->opPackageName).string()).c_str());
item->setInt32(kAudioPolicyRqstSession, client->session);
+ item->setCString(
+ kAudioPolicyRqstDevice, getDeviceTypeStrForPortId(client->deviceId).c_str());
+
// figure out who is active
// NB: might the other party have given up the microphone since then? how sure.
// perhaps could have given up on it.
@@ -527,8 +550,11 @@
// keeps the last of the clients marked active
item->setCString(kAudioPolicyActiveSrc,
audioSourceString(other->attributes.source).c_str());
- item->setCString(kAudioPolicyActivePkg, std::string(String8(other->opPackageName).string()).c_str());
+ item->setCString(kAudioPolicyActivePkg,
+ std::string(String8(other->opPackageName).string()).c_str());
item->setInt32(kAudioPolicyActiveSession, other->session);
+ item->setCString(kAudioPolicyActiveDevice,
+ getDeviceTypeStrForPortId(other->deviceId).c_str());
}
}
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 3e179c0..407d7a5 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -263,6 +263,8 @@
// Prints the shell command help
status_t printHelp(int out);
+ std::string getDeviceTypeStrForPortId(audio_port_handle_t portId);
+
// If recording we need to make sure the UID is allowed to do that. If the UID is idle
// then it cannot record and gets buffers with zeros - silence. As soon as the UID
// transitions to an active state we will start reporting buffers with data. This approach
@@ -643,7 +645,8 @@
const audio_session_t session; // audio session ID
bool active; // Capture is active or inactive
bool isConcurrent; // is allowed to concurrent capture
- bool isVirtualDevice; // uses vitual device: updated by APM::getInputForAttr()
+ bool isVirtualDevice; // uses virtual device: updated by APM::getInputForAttr()
+ audio_port_handle_t deviceId; // selected input device port ID
};
// A class automatically clearing and restoring binder caller identity inside
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 6b958a8..d9bcba3 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -77,7 +77,8 @@
mNextShutterFrameNumber(0),
mNextReprocessShutterFrameNumber(0),
mListener(NULL),
- mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
+ mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID),
+ mLastTemplateId(-1)
{
ATRACE_CALL();
camera3_callback_ops::notify = &sNotify;
@@ -1597,6 +1598,18 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ // In case the client doesn't include any session parameter, try a
+ // speculative configuration using the values from the last cached
+ // default request.
+ if (sessionParams.isEmpty() &&
+ ((mLastTemplateId > 0) && (mLastTemplateId < CAMERA3_TEMPLATE_COUNT)) &&
+ (!mRequestTemplateCache[mLastTemplateId].isEmpty())) {
+ ALOGV("%s: Speculative session param configuration with template id: %d", __func__,
+ mLastTemplateId);
+ return filterParamsAndConfigureLocked(mRequestTemplateCache[mLastTemplateId],
+ operatingMode);
+ }
+
return filterParamsAndConfigureLocked(sessionParams, operatingMode);
}
@@ -1673,6 +1686,7 @@
if (!mRequestTemplateCache[templateId].isEmpty()) {
*request = mRequestTemplateCache[templateId];
+ mLastTemplateId = templateId;
return OK;
}
}
@@ -1697,6 +1711,7 @@
mRequestTemplateCache[templateId].acquire(rawRequest);
*request = mRequestTemplateCache[templateId];
+ mLastTemplateId = templateId;
}
return OK;
}
@@ -4045,6 +4060,7 @@
mRepeatingLastFrameNumber(
hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
mPrepareVideoStream(false),
+ mConstrainedMode(false),
mRequestLatency(kRequestLatencyBinSize),
mSessionParamKeys(sessionParamKeys),
mLatestSessionParams(sessionParamKeys.size()) {
@@ -4068,6 +4084,7 @@
mLatestSessionParams = sessionParams;
// Prepare video stream for high speed recording.
mPrepareVideoStream = isConstrainedHighSpeed;
+ mConstrainedMode = isConstrainedHighSpeed;
}
status_t Camera3Device::RequestThread::queueRequestList(
@@ -4482,6 +4499,17 @@
return maxExpectedDuration;
}
+bool Camera3Device::RequestThread::skipHFRTargetFPSUpdate(int32_t tag,
+ const camera_metadata_ro_entry_t& newEntry, const camera_metadata_entry_t& currentEntry) {
+ if (mConstrainedMode && (ANDROID_CONTROL_AE_TARGET_FPS_RANGE == tag) &&
+ (newEntry.count == currentEntry.count) && (currentEntry.count == 2) &&
+ (currentEntry.data.i32[1] == newEntry.data.i32[1])) {
+ return true;
+ }
+
+ return false;
+}
+
bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
ATRACE_CALL();
bool updatesDetected = false;
@@ -4514,8 +4542,10 @@
if (isDifferent) {
ALOGV("%s: Session parameter tag id %d changed", __FUNCTION__, tag);
+ if (!skipHFRTargetFPSUpdate(tag, entry, lastEntry)) {
+ updatesDetected = true;
+ }
mLatestSessionParams.update(entry);
- updatesDetected = true;
}
} else if (lastEntry.count > 0) {
// Value has been removed
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 13b83ba..35f799d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -861,6 +861,11 @@
// Check and update latest session parameters based on the current request settings.
bool updateSessionParameters(const CameraMetadata& settings);
+ // Check whether FPS range session parameter re-configuration is needed in constrained
+ // high speed recording camera sessions.
+ bool skipHFRTargetFPSUpdate(int32_t tag, const camera_metadata_ro_entry_t& newEntry,
+ const camera_metadata_entry_t& currentEntry);
+
// Re-configure camera using the latest session parameters.
bool reconfigureCamera();
@@ -919,6 +924,8 @@
// Flag indicating if we should prepare video stream for video requests.
bool mPrepareVideoStream;
+ bool mConstrainedMode;
+
static const int32_t kRequestLatencyBinSize = 40; // in ms
CameraLatencyHistogram mRequestLatency;
@@ -1184,6 +1191,9 @@
metadata_vendor_id_t mVendorTagId;
+ // Cached last requested template id
+ int mLastTemplateId;
+
/**
* Static callback forwarding methods from HAL to instance
*/
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index f4d5a18..59ac636 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -212,7 +212,11 @@
SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
maxConsumerBuffers, mMaxHalBuffers);
- size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
+ // The output slot count requirement can change depending on the current amount
+ // of outputs and incoming buffer consumption rate. To avoid any issues with
+ // insufficient slots, set their count to the maximum supported. The output
+ // surface buffer allocation is disabled so no real buffers will get allocated.
+ size_t totalBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
res = native_window_set_buffer_count(outputQueue.get(),
totalBufferCount);
if (res != OK) {