Merge "audioflinger: fix pre processing transfer between record threads." into lmp-dev
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index df0dc58..28e5c56 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -77,8 +77,14 @@
};
static bool isFlexibleColorFormat(
- const sp<IOMX> &omx, IOMX::node_id node,
- uint32_t colorFormat, OMX_U32 *flexibleEquivalent);
+ const sp<IOMX> &omx, IOMX::node_id node,
+ uint32_t colorFormat, OMX_U32 *flexibleEquivalent);
+
+ // Returns 0 if configuration is not supported. NOTE: this is treated by
+ // some OMX components as auto level, and by others as invalid level.
+ static int /* OMX_VIDEO_AVCLEVELTYPE */ getAVCLevelFor(
+ int width, int height, int rate, int bitrate,
+ OMX_VIDEO_AVCPROFILETYPE profile = OMX_VIDEO_AVCProfileBaseline);
protected:
virtual ~ACodec();
diff --git a/include/media/stagefright/foundation/AUtils.h b/include/media/stagefright/foundation/AUtils.h
new file mode 100644
index 0000000..3a73a39
--- /dev/null
+++ b/include/media/stagefright/foundation/AUtils.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef A_UTILS_H_
+
+#define A_UTILS_H_
+
+/* ============================ math templates ============================ */
+
+/* T must be integer type, den must not be 0 */
+template<class T>
+inline static const T divRound(const T &nom, const T &den) {
+ if ((nom >= 0) ^ (den >= 0)) {
+ return (nom - den / 2) / den;
+ } else {
+ return (nom + den / 2) / den;
+ }
+}
+
+/* == ceil(nom / den). T must be integer type, den must not be 0 */
+template<class T>
+inline static const T divUp(const T &nom, const T &den) {
+ if (den < 0) {
+ return (nom < 0 ? nom + den + 1 : nom) / den;
+ } else {
+ return (nom < 0 ? nom : nom + den - 1) / den;
+ }
+}
+
+template<class T>
+inline static T abs(const T &a) {
+ return a < 0 ? -a : a;
+}
+
+template<class T>
+inline static const T &min(const T &a, const T &b) {
+ return a < b ? a : b;
+}
+
+template<class T>
+inline static const T &max(const T &a, const T &b) {
+ return a > b ? a : b;
+}
+
+/* T must be integer type, period must be positive */
+template<class T>
+inline static T periodicError(const T &val, const T &period) {
+ T err = abs(val) % period;
+ return (err < (period / 2)) ? err : (period - err);
+}
+
+#endif // A_UTILS_H_
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index b904aa8..17190fb 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/ACodec.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/AACWriter.h>
@@ -1243,6 +1244,10 @@
if (videoCodec == VIDEO_ENCODER_H264) {
ALOGI("Force to use AVC baseline profile");
setParamVideoEncoderProfile(OMX_VIDEO_AVCProfileBaseline);
+ // set 0 for invalid levels - this will be rejected by the
+ // codec if it cannot handle it during configure
+ setParamVideoEncoderLevel(ACodec::getAVCLevelFor(
+ videoFrameWidth, videoFrameHeight, videoFrameRate, videoBitRate));
}
}
}
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.cpp b/media/libmediaplayerservice/VideoFrameScheduler.cpp
index 1a5f3e0..ce5f5fe 100644
--- a/media/libmediaplayerservice/VideoFrameScheduler.cpp
+++ b/media/libmediaplayerservice/VideoFrameScheduler.cpp
@@ -27,6 +27,7 @@
#include <ui/DisplayStatInfo.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
#include "VideoFrameScheduler.h"
@@ -35,36 +36,6 @@
static const nsecs_t kNanosIn1s = 1000000000;
template<class T>
-inline static const T divRound(const T &nom, const T &den) {
- if ((nom >= 0) ^ (den >= 0)) {
- return (nom - den / 2) / den;
- } else {
- return (nom + den / 2) / den;
- }
-}
-
-template<class T>
-inline static T abs(const T &a) {
- return a < 0 ? -a : a;
-}
-
-template<class T>
-inline static const T &min(const T &a, const T &b) {
- return a < b ? a : b;
-}
-
-template<class T>
-inline static const T &max(const T &a, const T &b) {
- return a > b ? a : b;
-}
-
-template<class T>
-inline static T periodicError(const T &val, const T &period) {
- T err = abs(val) % period;
- return (err < (period / 2)) ? err : (period - err);
-}
-
-template<class T>
static int compare(const T *lhs, const T *rhs) {
if (*lhs < *rhs) {
return -1;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 7814bf1..27f6131 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -189,6 +189,8 @@
for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) {
mInputBufferIsDequeued.editItemAt(i) = false;
}
+
+ mPendingInputMessages.clear();
}
void NuPlayer::Decoder::requestCodecNotification() {
@@ -274,7 +276,19 @@
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
reply->setBuffer("buffer", buffer);
mCSDsToSubmit.removeAt(0);
- reply->post();
+ CHECK(onInputBufferFilled(reply));
+ return true;
+ }
+
+ while (!mPendingInputMessages.empty()) {
+ sp<AMessage> msg = *mPendingInputMessages.begin();
+ if (!onInputBufferFilled(msg)) {
+ break;
+ }
+ mPendingInputMessages.erase(mPendingInputMessages.begin());
+ }
+
+ if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
return true;
}
@@ -286,7 +300,7 @@
return true;
}
-void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
+bool android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
size_t bufferIx;
CHECK(msg->findSize("buffer-ix", &bufferIx));
CHECK_LT(bufferIx, mInputBuffers.size());
@@ -306,9 +320,12 @@
const sp<ABuffer> &buf = mInputBuffers[ix];
if (buf->data() == mediaBuffer->data()) {
// all input buffers are dequeued on start, hence the check
- CHECK(mInputBufferIsDequeued[ix]);
- ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
- mComponentName.c_str(), ix, bufferIx);
+ if (!mInputBufferIsDequeued[ix]) {
+ ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
+ mComponentName.c_str(), ix, bufferIx);
+ mediaBuffer->release();
+ return false;
+ }
// TRICKY: need buffer for the metadata, so instead, set
// codecBuffer to the same (though incorrect) buffer to
@@ -333,7 +350,7 @@
if (streamErr == OK) {
/* buffers are returned to hold on to */
- return;
+ return true;
}
// attempt to queue EOS
@@ -398,6 +415,7 @@
}
}
}
+ return true;
}
bool NuPlayer::Decoder::handleAnOutputBuffer() {
@@ -604,13 +622,21 @@
case kWhatCodecNotify:
{
if (!isStaleReply(msg)) {
- if (!mPaused) {
- while (handleAnInputBuffer()) {
- }
+ int32_t numInput, numOutput;
+
+ if (!msg->findInt32("input-buffers", &numInput)) {
+ numInput = INT32_MAX;
}
- while (handleAnOutputBuffer()) {
+ if (!msg->findInt32("output-buffers", &numOutput)) {
+ numOutput = INT32_MAX;
}
+
+ if (!mPaused) {
+ while (numInput-- > 0 && handleAnInputBuffer()) {}
+ }
+
+ while (numOutput-- > 0 && handleAnOutputBuffer()) {}
}
requestCodecNotification();
@@ -620,7 +646,10 @@
case kWhatInputBufferFilled:
{
if (!isStaleReply(msg)) {
- onInputBufferFilled(msg);
+ if (!mPendingInputMessages.empty()
+ || !onInputBufferFilled(msg)) {
+ mPendingInputMessages.push_back(msg);
+ }
}
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index cc1bdff..dba3eee 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -80,6 +80,8 @@
sp<ALooper> mCodecLooper;
sp<ALooper> mDecoderLooper;
+ List<sp<AMessage> > mPendingInputMessages;
+
Vector<sp<ABuffer> > mInputBuffers;
Vector<sp<ABuffer> > mOutputBuffers;
Vector<sp<ABuffer> > mCSDsForCurrentFormat;
@@ -98,7 +100,7 @@
void onConfigure(const sp<AMessage> &format);
void onFlush();
void onResume();
- void onInputBufferFilled(const sp<AMessage> &msg);
+ bool onInputBufferFilled(const sp<AMessage> &msg);
void onRenderBuffer(const sp<AMessage> &msg);
void onShutdown();
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index d6fba98..b6ac2a0 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -32,6 +32,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodecList.h>
@@ -2490,6 +2491,58 @@
return setupErrorCorrectionParameters();
}
+// static
+int /* OMX_VIDEO_AVCLEVELTYPE */ ACodec::getAVCLevelFor(
+ int width, int height, int rate, int bitrate,
+ OMX_VIDEO_AVCPROFILETYPE profile) {
+ // convert bitrate to main/baseline profile kbps equivalent
+ switch (profile) {
+ case OMX_VIDEO_AVCProfileHigh10:
+ bitrate = divUp(bitrate, 3000); break;
+ case OMX_VIDEO_AVCProfileHigh:
+ bitrate = divUp(bitrate, 1250); break;
+ default:
+ bitrate = divUp(bitrate, 1000); break;
+ }
+
+ // convert size and rate to MBs
+ width = divUp(width, 16);
+ height = divUp(height, 16);
+ int mbs = width * height;
+ rate *= mbs;
+ int maxDimension = max(width, height);
+
+ static const int limits[][5] = {
+ /* MBps MB dim bitrate level */
+ { 1485, 99, 28, 64, OMX_VIDEO_AVCLevel1 },
+ { 1485, 99, 28, 128, OMX_VIDEO_AVCLevel1b },
+ { 3000, 396, 56, 192, OMX_VIDEO_AVCLevel11 },
+ { 6000, 396, 56, 384, OMX_VIDEO_AVCLevel12 },
+ { 11880, 396, 56, 768, OMX_VIDEO_AVCLevel13 },
+ { 11880, 396, 56, 2000, OMX_VIDEO_AVCLevel2 },
+ { 19800, 792, 79, 4000, OMX_VIDEO_AVCLevel21 },
+ { 20250, 1620, 113, 4000, OMX_VIDEO_AVCLevel22 },
+ { 40500, 1620, 113, 10000, OMX_VIDEO_AVCLevel3 },
+ { 108000, 3600, 169, 14000, OMX_VIDEO_AVCLevel31 },
+ { 216000, 5120, 202, 20000, OMX_VIDEO_AVCLevel32 },
+ { 245760, 8192, 256, 20000, OMX_VIDEO_AVCLevel4 },
+ { 245760, 8192, 256, 50000, OMX_VIDEO_AVCLevel41 },
+ { 522240, 8704, 263, 50000, OMX_VIDEO_AVCLevel42 },
+ { 589824, 22080, 420, 135000, OMX_VIDEO_AVCLevel5 },
+ { 983040, 36864, 543, 240000, OMX_VIDEO_AVCLevel51 },
+ { 2073600, 36864, 543, 240000, OMX_VIDEO_AVCLevel52 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(limits); i++) {
+ const int (&limit)[5] = limits[i];
+ if (rate <= limit[0] && mbs <= limit[1] && maxDimension <= limit[2]
+ && bitrate <= limit[3]) {
+ return limit[4];
+ }
+ }
+ return 0;
+}
+
status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
int32_t bitrate, iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b56819c..b568063 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2141,11 +2141,24 @@
return;
}
- if ((mFlags & (kFlagStickyError
+ bool isErrorOrOutputChanged =
+ (mFlags & (kFlagStickyError
| kFlagOutputBuffersChanged
- | kFlagOutputFormatChanged))
+ | kFlagOutputFormatChanged));
+
+ if (isErrorOrOutputChanged
|| !mAvailPortBuffers[kPortIndexInput].empty()
|| !mAvailPortBuffers[kPortIndexOutput].empty()) {
+ mActivityNotify->setInt32("input-buffers",
+ mAvailPortBuffers[kPortIndexInput].size());
+
+ if (isErrorOrOutputChanged) {
+ // we want consumer to dequeue as many times as it can
+ mActivityNotify->setInt32("output-buffers", INT32_MAX);
+ } else {
+ mActivityNotify->setInt32("output-buffers",
+ mAvailPortBuffers[kPortIndexOutput].size());
+ }
mActivityNotify->post();
mActivityNotify.clear();
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index d98fa80..1f4b6fd 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -156,7 +156,8 @@
(mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
Bool success = PVInitVideoDecoder(
- mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
+ mHandle, vol_data, &vol_size, 1,
+ outputBufferWidth(), outputBufferHeight(), mode);
if (!success) {
ALOGW("PVInitVideoDecoder failed. Unsupported content?");
@@ -321,7 +322,7 @@
vol_data[0] = NULL;
if (!PVInitVideoDecoder(
- mHandle, vol_data, &vol_size, 1, mWidth, mHeight,
+ mHandle, vol_data, &vol_size, 1, outputBufferWidth(), outputBufferHeight(),
H263_MODE)) {
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
mSignalledError = true;
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 3d20a79..2f83610 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -166,8 +166,15 @@
bool sizeChanged = (width != mWidth || height != mHeight);
bool updateCrop = (cropSettingsMode == kCropUnSet);
bool cropChanged = (cropSettingsMode == kCropChanged);
+ bool strideChanged = false;
+ if (fakeStride) {
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
+ if (def->format.video.nStride != width || def->format.video.nSliceHeight != height) {
+ strideChanged = true;
+ }
+ }
- if (sizeChanged || cropChanged) {
+ if (sizeChanged || cropChanged || strideChanged) {
mWidth = width;
mHeight = height;
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 903af49..99b480ad 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -9,7 +9,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
- SurfaceMediaSource_test.cpp \
+ SurfaceMediaSource_test.cpp \
DummyRecorder.cpp \
LOCAL_SHARED_LIBRARIES := \
@@ -33,10 +33,10 @@
libgtest_main \
LOCAL_C_INCLUDES := \
- bionic \
- bionic/libstdc++/include \
- external/gtest/include \
- external/stlport/stlport \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport \
frameworks/av/media/libstagefright \
frameworks/av/media/libstagefright/include \
$(TOP)/frameworks/native/include/media/openmax \
@@ -47,6 +47,41 @@
endif
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := Utils_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ Utils_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog \
+ libmedia \
+ libstagefright \
+ libstagefright_foundation \
+ libstagefright_omx \
+ libstlport \
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest \
+ libgtest_main \
+
+LOCAL_C_INCLUDES := \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport \
+ frameworks/av/include \
+ frameworks/av/media/libstagefright \
+ frameworks/av/media/libstagefright/include \
+ $(TOP)/frameworks/native/include/media/openmax \
+
+include $(BUILD_EXECUTABLE)
+
# Include subdirectory makefiles
# ============================================================
diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp
new file mode 100644
index 0000000..f2825dd
--- /dev/null
+++ b/media/libstagefright/tests/Utils_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Utils_test"
+
+#include <gtest/gtest.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+class UtilsTest : public ::testing::Test {
+};
+
+TEST_F(UtilsTest, TestFourCC) {
+ ASSERT_EQ(FOURCC('s', 't', 'm' , 'u'), 'stmu');
+}
+
+TEST_F(UtilsTest, TestMathTemplates) {
+ ASSERT_EQ(divRound(-10, -4), 3);
+ ASSERT_EQ(divRound(-11, -4), 3);
+ ASSERT_EQ(divRound(-12, -4), 3);
+ ASSERT_EQ(divRound(-13, -4), 3);
+ ASSERT_EQ(divRound(-14, -4), 4);
+
+ ASSERT_EQ(divRound(10, -4), -3);
+ ASSERT_EQ(divRound(11, -4), -3);
+ ASSERT_EQ(divRound(12, -4), -3);
+ ASSERT_EQ(divRound(13, -4), -3);
+ ASSERT_EQ(divRound(14, -4), -4);
+
+ ASSERT_EQ(divRound(-10, 4), -3);
+ ASSERT_EQ(divRound(-11, 4), -3);
+ ASSERT_EQ(divRound(-12, 4), -3);
+ ASSERT_EQ(divRound(-13, 4), -3);
+ ASSERT_EQ(divRound(-14, 4), -4);
+
+ ASSERT_EQ(divRound(10, 4), 3);
+ ASSERT_EQ(divRound(11, 4), 3);
+ ASSERT_EQ(divRound(12, 4), 3);
+ ASSERT_EQ(divRound(13, 4), 3);
+ ASSERT_EQ(divRound(14, 4), 4);
+
+ ASSERT_EQ(divUp(-11, -4), 3);
+ ASSERT_EQ(divUp(-12, -4), 3);
+ ASSERT_EQ(divUp(-13, -4), 4);
+
+ ASSERT_EQ(divUp(11, -4), -2);
+ ASSERT_EQ(divUp(12, -4), -3);
+ ASSERT_EQ(divUp(13, -4), -3);
+
+ ASSERT_EQ(divUp(-11, 4), -2);
+ ASSERT_EQ(divUp(-12, 4), -3);
+ ASSERT_EQ(divUp(-13, 4), -3);
+
+ ASSERT_EQ(divUp(11, 4), 3);
+ ASSERT_EQ(divUp(12, 4), 3);
+ ASSERT_EQ(divUp(13, 4), 4);
+
+ ASSERT_EQ(abs(5L), 5L);
+ ASSERT_EQ(abs(-25), 25);
+
+ ASSERT_EQ(min(5.6f, 6.0f), 5.6f);
+ ASSERT_EQ(min(6.0f, 5.6f), 5.6f);
+ ASSERT_EQ(min(-4.3, 8.6), -4.3);
+ ASSERT_EQ(min(8.6, -4.3), -4.3);
+
+ ASSERT_EQ(max(5.6f, 6.0f), 6.0f);
+ ASSERT_EQ(max(6.0f, 5.6f), 6.0f);
+ ASSERT_EQ(max(-4.3, 8.6), 8.6);
+ ASSERT_EQ(max(8.6, -4.3), 8.6);
+
+ ASSERT_EQ(periodicError(124, 100), 24);
+ ASSERT_EQ(periodicError(288, 100), 12);
+ ASSERT_EQ(periodicError(-345, 100), 45);
+ ASSERT_EQ(periodicError(-493, 100), 7);
+ ASSERT_EQ(periodicError(-550, 100), 50);
+ ASSERT_EQ(periodicError(-600, 100), 0);
+}
+
+} // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index bdf74d5..44e34b7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2101,6 +2101,7 @@
// If an NBAIO sink is present, use it to write the normal mixer's submix
if (mNormalSink != 0) {
+
const size_t count = mBytesRemaining / mFrameSize;
ATRACE_BEGIN("write");
@@ -2126,6 +2127,7 @@
size_t totalFramesWritten = mNormalSink->framesWritten();
if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
+ // mLatchD.mFramesReleased is set immediately before D is clocked into Q
mLatchDValid = true;
}
}
@@ -2418,6 +2420,18 @@
logString = NULL;
}
+ // Gather the framesReleased counters for all active tracks,
+ // and latch them atomically with the timestamp.
+ // FIXME We're using raw pointers as indices. A unique track ID would be a better index.
+ mLatchD.mFramesReleased.clear();
+ size_t size = mActiveTracks.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<Track> t = mActiveTracks[i].promote();
+ if (t != 0) {
+ mLatchD.mFramesReleased.add(t.get(),
+ t->mAudioTrackServerProxy->framesReleased());
+ }
+ }
if (mLatchDValid) {
mLatchQ = mLatchD;
mLatchDValid = false;
@@ -3093,6 +3107,7 @@
sleepTime = 0;
standbyTime = systemTime() + standbyDelay;
//TODO: delay standby when effects have a tail
+
}
void AudioFlinger::MixerThread::threadLoop_sleepTime()
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7af5264..bb9aa18 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -819,8 +819,11 @@
struct {
AudioTimestamp mTimestamp;
uint32_t mUnpresentedFrames;
+ KeyedVector<Track *, uint32_t> mFramesReleased;
} mLatchD, mLatchQ;
- bool mLatchDValid; // true means mLatchD is valid, and clock it into latch at next opportunity
+ bool mLatchDValid; // true means mLatchD is valid
+ // (except for mFramesReleased which is filled in later),
+ // and clock it into latch at next opportunity
bool mLatchQValid; // true means mLatchQ is valid
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 75190f3..b9308fa 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -898,7 +898,16 @@
uint32_t unpresentedFrames =
((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
playbackThread->mSampleRate;
- uint32_t framesWritten = mAudioTrackServerProxy->framesReleased();
+ // FIXME Since we're using a raw pointer as the key, it is theoretically possible
+ // for a brand new track to share the same address as a recently destroyed
+ // track, and thus for us to get the frames released of the wrong track.
+ // It is unlikely that we would be able to call getTimestamp() so quickly
+ // right after creating a new track. Nevertheless, the index here should
+ // be changed to something that is unique. Or use a completely different strategy.
+ ssize_t i = playbackThread->mLatchQ.mFramesReleased.indexOfKey(this);
+ uint32_t framesWritten = i >= 0 ?
+ playbackThread->mLatchQ.mFramesReleased[i] :
+ mAudioTrackServerProxy->framesReleased();
bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten;
if (framesWritten < unpresentedFrames) {
mPreviousValid = false;