Merge "NuPlayer: don't restart AudioSink when torn down due to pause timeout." into lmp-dev
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index da4c20c..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();
@@ -267,7 +273,8 @@
bool encoder,
int32_t numChannels, int32_t sampleRate, int32_t bitRate,
int32_t aacProfile, bool isADTS, int32_t sbrMode,
- int32_t maxOutputChannelCount, const drcParams_t& drc);
+ int32_t maxOutputChannelCount, const drcParams_t& drc,
+ int32_t pcmLimiterEnable);
status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate);
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 8000e84..3630263 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -34,6 +34,7 @@
struct AString;
struct IMediaHTTPService;
class String8;
+struct HTTPBase;
class DataSource : public RefBase {
public:
@@ -48,7 +49,10 @@
const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers = NULL,
- String8 *contentType = NULL);
+ String8 *contentType = NULL,
+ HTTPBase *httpSource = NULL);
+
+ static sp<DataSource> CreateMediaHTTP(const sp<IMediaHTTPService> &httpService);
DataSource() {}
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/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
index 14b31ec..d3b4a35 100644
--- a/media/img_utils/src/DngUtils.cpp
+++ b/media/img_utils/src/DngUtils.cpp
@@ -229,7 +229,9 @@
err = mEndianOut.write(version, 0, NELEMS(version));
if (err != OK) return err;
- uint32_t flags = FLAG_OPTIONAL | FLAG_OPTIONAL_FOR_PREVIEW;
+ // Do not include optional flag for preview, as this can have a large effect on the output.
+ uint32_t flags = FLAG_OPTIONAL;
+
err = mEndianOut.write(&flags, 0, 1);
if (err != OK) return err;
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/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index f84decd..6859a1a 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -36,6 +36,7 @@
#include "../../libstagefright/include/DRMExtractor.h"
#include "../../libstagefright/include/NuCachedSource2.h"
#include "../../libstagefright/include/WVMExtractor.h"
+#include "../../libstagefright/include/HTTPBase.h"
namespace android {
@@ -64,6 +65,7 @@
mAudioTimeUs = 0;
mVideoTimeUs = 0;
mHTTPService.clear();
+ mHttpSource.clear();
mUri.clear();
mUriHeaders.clear();
mFd = -1;
@@ -73,6 +75,7 @@
mDecryptHandle = NULL;
mDrmManagerClient = NULL;
mStarted = false;
+ mStopRead = true;
}
status_t NuPlayer::GenericSource::setDataSource(
@@ -284,10 +287,23 @@
// delayed data source creation
if (mDataSource == NULL) {
if (!mUri.empty()) {
- mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11);
+ const char* uri = mUri.c_str();
+ mIsWidevine = !strncasecmp(uri, "widevine://", 11);
+
+ if (!strncasecmp("http://", uri, 7)
+ || !strncasecmp("https://", uri, 8)
+ || mIsWidevine) {
+ mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
+ if (mHttpSource == NULL) {
+ ALOGE("Failed to create http source!");
+ notifyPreparedAndCleanup(UNKNOWN_ERROR);
+ return;
+ }
+ }
mDataSource = DataSource::CreateFromURI(
- mHTTPService, mUri.c_str(), &mUriHeaders, &mContentType);
+ mHTTPService, uri, &mUriHeaders, &mContentType,
+ static_cast<HTTPBase *>(mHttpSource.get()));
} else {
// set to false first, if the extractor
// comes back as secure, set it to true then.
@@ -360,6 +376,7 @@
mSniffedMIME = "";
mDataSource.clear();
mCachedSource.clear();
+ mHttpSource.clear();
cancelPollBuffering();
}
@@ -439,6 +456,7 @@
void NuPlayer::GenericSource::start() {
ALOGI("start");
+ mStopRead = false;
if (mAudioTrack.mSource != NULL) {
CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK);
@@ -459,6 +477,12 @@
// nothing to do, just account for DRM playback status
setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
mStarted = false;
+ if (mIsWidevine) {
+ // For a widevine source we need to prevent any further reads.
+ sp<AMessage> msg = new AMessage(kWhatStopWidevine, id());
+ sp<AMessage> response;
+ (void) msg->postAndAwaitResponse(&response);
+ }
}
void NuPlayer::GenericSource::pause() {
@@ -479,6 +503,8 @@
if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect();
}
+ } else if (mHttpSource != NULL) {
+ static_cast<HTTPBase *>(mHttpSource.get())->disconnect();
}
}
@@ -675,6 +701,20 @@
break;
}
+ case kWhatStopWidevine:
+ {
+ // mStopRead is only used for Widevine to prevent the video source
+ // from being read while the associated video decoder is shutting down.
+ mStopRead = true;
+ if (mVideoTrack.mSource != NULL) {
+ mVideoTrack.mPackets->clear();
+ }
+ sp<AMessage> response = new AMessage;
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+ break;
+ }
default:
Source::onMessageReceived(msg);
break;
@@ -1082,6 +1122,11 @@
}
status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
+ // If the Widevine source is stopped, do not attempt to read any
+ // more buffers.
+ if (mStopRead) {
+ return INVALID_OPERATION;
+ }
if (mVideoTrack.mSource != NULL) {
int64_t actualTimeUs;
readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs);
@@ -1193,6 +1238,10 @@
void NuPlayer::GenericSource::readBuffer(
media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
+ // Do not read data if Widevine source is stopped
+ if (mStopRead) {
+ return;
+ }
Track *track;
size_t maxBuffers = 1;
switch (trackType) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 24bb6af..f8601ea 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -93,6 +93,7 @@
kWhatSelectTrack,
kWhatSeek,
kWhatReadBuffer,
+ kWhatStopWidevine,
};
Vector<sp<MediaSource> > mSources;
@@ -126,11 +127,13 @@
sp<DataSource> mDataSource;
sp<NuCachedSource2> mCachedSource;
+ sp<DataSource> mHttpSource;
sp<WVMExtractor> mWVMExtractor;
sp<MetaData> mFileMeta;
DrmManagerClient *mDrmManagerClient;
sp<DecryptHandle> mDecryptHandle;
bool mStarted;
+ bool mStopRead;
String8 mContentType;
AString mSniffedMIME;
off64_t mMetaDataSize;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index ea60c06..5270efc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -791,6 +791,11 @@
ALOGV("initiating %s decoder shutdown",
audio ? "audio" : "video");
+ // Widevine source reads must stop before releasing the video decoder.
+ if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+ mSource->stop();
+ }
+
getDecoder(audio)->initiateShutdown();
if (audio) {
@@ -833,30 +838,50 @@
finishFlushIfPossible();
} else if (what == Decoder::kWhatError) {
status_t err;
- if (!msg->findInt32("err", &err)) {
+ if (!msg->findInt32("err", &err) || err == OK) {
err = UNKNOWN_ERROR;
}
- ALOGE("received error from %s decoder %#x", audio ? "audio" : "video", err);
- ALOGI("shutting down %s", audio ? "audio" : "video");
- if (audio && mFlushingAudio != NONE) {
- mRenderer->queueEOS(audio, err);
- mAudioDecoder.clear();
- ++mAudioDecoderGeneration;
- mFlushingAudio = SHUT_DOWN;
- finishFlushIfPossible();
- } else if (!audio && mFlushingVideo != NONE) {
- mRenderer->queueEOS(audio, err);
- mVideoDecoder.clear();
- ++mVideoDecoderGeneration;
- mFlushingVideo = SHUT_DOWN;
- finishFlushIfPossible();
- } else {
- mDeferredActions.push_back(
- new ShutdownDecoderAction(audio, !audio /* video */));
- processDeferredActions();
- notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ // Decoder errors can be due to Source (e.g. from streaming),
+ // or from decoding corrupted bitstreams, or from other decoder
+ // MediaCodec operations (e.g. from an ongoing reset or seek).
+ //
+ // We try to gracefully shut down the affected decoder if possible,
+ // rather than trying to force the shutdown with something
+ // similar to performReset(). This method can lead to a hang
+ // if MediaCodec functions block after an error, but they should
+ // typically return INVALID_OPERATION instead of blocking.
+
+ FlushStatus *flushing = audio ? &mFlushingAudio : &mFlushingVideo;
+ ALOGE("received error(%#x) from %s decoder, flushing(%d), now shutting down",
+ err, audio ? "audio" : "video", *flushing);
+
+ switch (*flushing) {
+ case NONE:
+ mDeferredActions.push_back(
+ new ShutdownDecoderAction(audio, !audio /* video */));
+ processDeferredActions();
+ break;
+ case FLUSHING_DECODER:
+ *flushing = FLUSHING_DECODER_SHUTDOWN; // initiate shutdown after flush.
+ break; // Wait for flush to complete.
+ case FLUSHING_DECODER_SHUTDOWN:
+ break; // Wait for flush to complete.
+ case SHUTTING_DOWN_DECODER:
+ break; // Wait for shutdown to complete.
+ case FLUSHED:
+ // Widevine source reads must stop before releasing the video decoder.
+ if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+ mSource->stop();
+ }
+ getDecoder(audio)->initiateShutdown(); // In the middle of a seek.
+ *flushing = SHUTTING_DOWN_DECODER; // Shut down.
+ break;
+ case SHUT_DOWN:
+ finishFlushIfPossible(); // Should not occur.
+ break; // Finish anyways.
}
+ notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
} else if (what == Decoder::kWhatDrainThisBuffer) {
renderBuffer(audio, msg);
} else {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 1a066b7..27f6131 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -140,6 +140,8 @@
format, surface, NULL /* crypto */, 0 /* flags */);
if (err != OK) {
ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
+ mCodec->release();
+ mCodec.clear();
handleError(err);
return;
}
@@ -152,6 +154,8 @@
err = mCodec->start();
if (err != OK) {
ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
+ mCodec->release();
+ mCodec.clear();
handleError(err);
return;
}
@@ -185,6 +189,8 @@
for (size_t i = 0; i < mInputBufferIsDequeued.size(); i++) {
mInputBufferIsDequeued.editItemAt(i) = false;
}
+
+ mPendingInputMessages.clear();
}
void NuPlayer::Decoder::requestCodecNotification() {
@@ -270,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;
}
@@ -282,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());
@@ -302,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
@@ -329,7 +350,7 @@
if (streamErr == OK) {
/* buffers are returned to hold on to */
- return;
+ return true;
}
// attempt to queue EOS
@@ -394,6 +415,7 @@
}
}
}
+ return true;
}
bool NuPlayer::Decoder::handleAnOutputBuffer() {
@@ -511,9 +533,9 @@
if (err != OK) {
ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
handleError(err);
- return;
+ // finish with posting kWhatFlushCompleted.
+ // we attempt to release the buffers even if flush fails.
}
-
releaseAndResetMediaBuffers();
sp<AMessage> notify = mNotify->dup();
@@ -551,7 +573,7 @@
if (err != OK) {
ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
handleError(err);
- return;
+ // finish with posting kWhatShutdownCompleted.
}
sp<AMessage> notify = mNotify->dup();
@@ -600,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();
@@ -616,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/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index a9bca49..1a01d52 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -27,6 +27,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
namespace android {
@@ -47,6 +48,7 @@
mLooping(false),
mAutoLoop(false),
mStartupSeekTimeUs(-1) {
+ ALOGV("NuPlayerDriver(%p)", this);
mLooper->setName("NuPlayerDriver Looper");
mLooper->start(
@@ -61,6 +63,7 @@
}
NuPlayerDriver::~NuPlayerDriver() {
+ ALOGV("~NuPlayerDriver(%p)", this);
mLooper->stop();
}
@@ -78,9 +81,9 @@
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers) {
+ ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
Mutex::Autolock autoLock(mLock);
- ALOGV("setDataSource: url=%s", url);
if (mState != STATE_IDLE) {
return INVALID_OPERATION;
}
@@ -97,9 +100,9 @@
}
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
+ ALOGV("setDataSource(%p) file(%d)", this, fd);
Mutex::Autolock autoLock(mLock);
- ALOGV("setDataSource: fd=%d", fd);
if (mState != STATE_IDLE) {
return INVALID_OPERATION;
}
@@ -116,9 +119,9 @@
}
status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
+ ALOGV("setDataSource(%p) stream source", this);
Mutex::Autolock autoLock(mLock);
- ALOGV("setDataSource: stream source");
if (mState != STATE_IDLE) {
return INVALID_OPERATION;
}
@@ -136,6 +139,7 @@
status_t NuPlayerDriver::setVideoSurfaceTexture(
const sp<IGraphicBufferProducer> &bufferProducer) {
+ ALOGV("setVideoSurfaceTexture(%p)", this);
Mutex::Autolock autoLock(mLock);
if (mSetSurfaceInProgress) {
@@ -163,6 +167,7 @@
}
status_t NuPlayerDriver::prepare() {
+ ALOGV("prepare(%p)", this);
Mutex::Autolock autoLock(mLock);
return prepare_l();
}
@@ -197,6 +202,7 @@
}
status_t NuPlayerDriver::prepareAsync() {
+ ALOGV("prepareAsync(%p)", this);
Mutex::Autolock autoLock(mLock);
switch (mState) {
@@ -218,6 +224,7 @@
}
status_t NuPlayerDriver::start() {
+ ALOGD("start(%p)", this);
Mutex::Autolock autoLock(mLock);
switch (mState) {
@@ -292,6 +299,7 @@
}
status_t NuPlayerDriver::stop() {
+ ALOGD("stop(%p)", this);
Mutex::Autolock autoLock(mLock);
switch (mState) {
@@ -346,6 +354,7 @@
}
status_t NuPlayerDriver::seekTo(int msec) {
+ ALOGD("seekTo(%p) %d ms", this, msec);
Mutex::Autolock autoLock(mLock);
int64_t seekTimeUs = msec * 1000ll;
@@ -430,6 +439,7 @@
}
status_t NuPlayerDriver::reset() {
+ ALOGD("reset(%p)", this);
Mutex::Autolock autoLock(mLock);
switch (mState) {
@@ -572,6 +582,7 @@
}
void NuPlayerDriver::notifyResetComplete() {
+ ALOGI("notifyResetComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
@@ -580,6 +591,7 @@
}
void NuPlayerDriver::notifySetSurfaceComplete() {
+ ALOGV("notifySetSurfaceComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
CHECK(mSetSurfaceInProgress);
@@ -602,6 +614,7 @@
}
void NuPlayerDriver::notifySeekComplete() {
+ ALOGV("notifySeekComplete(%p)", this);
Mutex::Autolock autoLock(mLock);
notifySeekComplete_l();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 958ab5b..d6bf1de 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1052,7 +1052,7 @@
// become stale. Assuming that the MixerThread runs 20ms, with FastMixer at 5ms,
// the max latency should be about 25ms with an average around 12ms (to be verified).
// For safety we use 100ms.
- ALOGW("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
+ ALOGV("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
(long long)nowUs, (long long)numFramesPlayedAt);
numFramesPlayedAt = nowUs - kStaleTimestamp100ms;
}
@@ -1082,7 +1082,7 @@
// numFramesPlayedAt, by a time amount greater than numFramesPlayed.
//
// Both of these are transitory conditions.
- ALOGW("getPlayedOutAudioDurationUs: negative timestamp %lld set to zero", (long long)durationUs);
+ ALOGV("getPlayedOutAudioDurationUs: negative duration %lld set to zero", (long long)durationUs);
durationUs = 0;
}
ALOGV("getPlayedOutAudioDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 4589ed1..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>
@@ -1359,6 +1360,7 @@
int32_t isADTS, aacProfile;
int32_t sbrMode;
int32_t maxOutputChannelCount;
+ int32_t pcmLimiterEnable;
drcParams_t drc;
if (!msg->findInt32("is-adts", &isADTS)) {
isADTS = 0;
@@ -1373,6 +1375,10 @@
if (!msg->findInt32("aac-max-output-channel_count", &maxOutputChannelCount)) {
maxOutputChannelCount = -1;
}
+ if (!msg->findInt32("aac-pcm-limiter-enable", &pcmLimiterEnable)) {
+ // value is unknown
+ pcmLimiterEnable = -1;
+ }
if (!msg->findInt32("aac-encoded-target-level", &drc.encodedTargetLevel)) {
// value is unknown
drc.encodedTargetLevel = -1;
@@ -1396,7 +1402,8 @@
err = setupAACCodec(
encoder, numChannels, sampleRate, bitRate, aacProfile,
- isADTS != 0, sbrMode, maxOutputChannelCount, drc);
+ isADTS != 0, sbrMode, maxOutputChannelCount, drc,
+ pcmLimiterEnable);
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
@@ -1561,7 +1568,8 @@
status_t ACodec::setupAACCodec(
bool encoder, int32_t numChannels, int32_t sampleRate,
int32_t bitRate, int32_t aacProfile, bool isADTS, int32_t sbrMode,
- int32_t maxOutputChannelCount, const drcParams_t& drc) {
+ int32_t maxOutputChannelCount, const drcParams_t& drc,
+ int32_t pcmLimiterEnable) {
if (encoder && isADTS) {
return -EINVAL;
}
@@ -1691,6 +1699,7 @@
presentation.nHeavyCompression = drc.heavyCompression;
presentation.nTargetReferenceLevel = drc.targetRefLevel;
presentation.nEncodedTargetLevel = drc.encodedTargetLevel;
+ presentation.nPCMLimiterEnable = pcmLimiterEnable;
status_t res = mOMX->setParameter(mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
if (res == OK) {
@@ -2482,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/DataSource.cpp b/media/libstagefright/DataSource.cpp
index a72cbd5..c99db84 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -186,7 +186,8 @@
const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers,
- String8 *contentType) {
+ String8 *contentType,
+ HTTPBase *httpSource) {
if (contentType != NULL) {
*contentType = "";
}
@@ -204,14 +205,15 @@
return NULL;
}
- sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
- if (conn == NULL) {
- ALOGE("Failed to make http connection from http service!");
- return NULL;
+ if (httpSource == NULL) {
+ sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
+ if (conn == NULL) {
+ ALOGE("Failed to make http connection from http service!");
+ return NULL;
+ }
+ httpSource = new MediaHTTP(conn);
}
- sp<HTTPBase> httpSource = new MediaHTTP(conn);
-
String8 tmp;
if (isWidevine) {
tmp = String8("http://");
@@ -264,6 +266,19 @@
return source;
}
+sp<DataSource> DataSource::CreateMediaHTTP(const sp<IMediaHTTPService> &httpService) {
+ if (httpService == NULL) {
+ return NULL;
+ }
+
+ sp<IMediaHTTPConnection> conn = httpService->makeHTTPConnection();
+ if (conn == NULL) {
+ return NULL;
+ } else {
+ return new MediaHTTP(conn);
+ }
+}
+
String8 DataSource::getMIMEType() const {
return String8("application/octet-stream");
}
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/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index f469d4d..bd0a41d 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -456,6 +456,10 @@
}
Mutex::Autolock autoLock(mLock);
+ if (mDisconnecting) {
+ mCondition.signal();
+ return;
+ }
CHECK(mAsyncResult == NULL);
@@ -502,6 +506,9 @@
ALOGV("readAt offset %lld, size %zu", offset, size);
Mutex::Autolock autoLock(mLock);
+ if (mDisconnecting) {
+ return ERROR_END_OF_STREAM;
+ }
// If the request can be completely satisfied from the cache, do so.
@@ -528,6 +535,7 @@
}
if (mDisconnecting) {
+ mAsyncResult.clear();
return ERROR_END_OF_STREAM;
}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index e701e9e..1b6eac4 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -368,6 +368,10 @@
aacPresParams->nEncodedTargetLevel);
updateDrcWrapper = true;
}
+ if (aacPresParams->nPCMLimiterEnable >= 0) {
+ aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE,
+ (aacPresParams->nPCMLimiterEnable != 0));
+ }
if (updateDrcWrapper) {
mDrcWrap.update();
}
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/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e200857..e48af20 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1436,6 +1436,16 @@
IPCThreadState::self()->getCallingUid(),
flags, tid, &lStatus);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
+
+ if (lStatus == NO_ERROR) {
+ // Check if one effect chain was awaiting for an AudioRecord to be created on this
+ // session and move it to this thread.
+ sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)lSessionId);
+ if (chain != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ thread->addEffectChain_l(chain);
+ }
+ }
}
if (lStatus != NO_ERROR) {
@@ -2034,14 +2044,41 @@
}
ALOGV("closeInput() %d", input);
+
+ // If we still have effect chains, it means that a client still holds a handle
+ // on at least one effect. We must either move the chain to an existing thread with the
+ // same session ID or put it aside in case a new record thread is opened for a
+ // new capture on the same session
+ sp<EffectChain> chain;
{
- // If we still have effect chains, it means that a client still holds a handle
- // on at least one effect. We must keep the chain alive in case a new record
- // thread is opened for a new capture on the same session
Mutex::Autolock _sl(thread->mLock);
Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
- for (size_t i = 0; i < effectChains.size(); i++) {
- putOrphanEffectChain_l(effectChains[i]);
+ // Note: maximum one chain per record thread
+ if (effectChains.size() != 0) {
+ chain = effectChains[0];
+ }
+ }
+ if (chain != 0) {
+ // first check if a record thread is already opened with a client on the same session.
+ // This should only happen in case of overlap between one thread tear down and the
+ // creation of its replacement
+ size_t i;
+ for (i = 0; i < mRecordThreads.size(); i++) {
+ sp<RecordThread> t = mRecordThreads.valueAt(i);
+ if (t == thread) {
+ continue;
+ }
+ if (t->hasAudioSession(chain->sessionId()) != 0) {
+ Mutex::Autolock _l(t->mLock);
+ ALOGV("closeInput() found thread %d for effect session %d",
+ t->id(), chain->sessionId());
+ t->addEffectChain_l(chain);
+ break;
+ }
+ }
+ // put the chain aside if we could not find a record thread with the same session id.
+ if (i == mRecordThreads.size()) {
+ putOrphanEffectChain_l(chain);
}
}
audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
@@ -2478,6 +2515,7 @@
// session and used it instead of creating a new one.
sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)sessionId);
if (chain != 0) {
+ Mutex::Autolock _l(thread->mLock);
thread->addEffectChain_l(chain);
}
}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 15f1f23..bcaf8ae 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -440,6 +440,20 @@
return status;
}
+void AudioFlinger::EffectModule::addEffectToHal_l()
+{
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
+ (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ audio_stream_t *stream = thread->stream();
+ if (stream != NULL) {
+ stream->add_audio_effect(stream, mEffectInterface);
+ }
+ }
+ }
+}
+
status_t AudioFlinger::EffectModule::start()
{
Mutex::Autolock _l(mLock);
@@ -465,15 +479,11 @@
if (status == 0) {
status = cmdStatus;
}
- if (status == 0 &&
- ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- audio_stream_t *stream = thread->stream();
- if (stream != NULL) {
- stream->add_audio_effect(stream, mEffectInterface);
- }
+ if (status == 0) {
+ addEffectToHal_l();
+ sp<EffectChain> chain = mChain.promote();
+ if (chain != 0) {
+ chain->forceVolume();
}
}
return status;
@@ -1326,7 +1336,7 @@
int sessionId)
: mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX), mForceVolume(false)
{
mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
if (thread == NULL) {
@@ -1649,7 +1659,8 @@
}
}
- if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) {
+ if (!isVolumeForced() && ctrlIdx == mVolumeCtrlIdx &&
+ *left == mLeftVolume && *right == mRightVolume) {
if (hasControl) {
*left = mNewLeftVolume;
*right = mNewRightVolume;
@@ -1690,6 +1701,17 @@
return hasControl;
}
+void AudioFlinger::EffectChain::syncHalEffectsState()
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ if (mEffects[i]->state() == EffectModule::ACTIVE ||
+ mEffects[i]->state() == EffectModule::STOPPING) {
+ mEffects[i]->addEffectToHal_l();
+ }
+ }
+}
+
void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index eaf90e7..6f93f81 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -119,6 +119,7 @@
{ return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
status_t setOffloaded(bool offloaded, audio_io_handle_t io);
bool isOffloaded() const;
+ void addEffectToHal_l();
void dump(int fd, const Vector<String16>& args);
@@ -318,6 +319,14 @@
// At least one non offloadable effect in the chain is enabled
bool isNonOffloadableEnabled();
+ // use release_cas because we don't care about the observed value, just want to make sure the
+ // new value is observable.
+ void forceVolume() { android_atomic_release_cas(false, true, &mForceVolume); }
+ // use acquire_cas because we are interested in the observed value and
+ // we are the only observers.
+ bool isVolumeForced() { return (android_atomic_acquire_cas(true, false, &mForceVolume) == 0); }
+
+ void syncHalEffectsState();
void dump(int fd, const Vector<String16>& args);
@@ -375,4 +384,5 @@
// timeLow fields among effect type UUIDs.
// Updated by updateSuspendedSessions_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+ volatile int32_t mForceVolume; // force next volume command because a new effect was enabled
};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 66edf45..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,7 +2127,7 @@
size_t totalFramesWritten = mNormalSink->framesWritten();
if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
- // mLatchD.mFramesReleased is set in threadloop_mix()
+ // mLatchD.mFramesReleased is set immediately before D is clocked into Q
mLatchDValid = true;
}
}
@@ -2419,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;
@@ -3095,17 +3108,6 @@
standbyTime = systemTime() + standbyDelay;
//TODO: delay standby when effects have a tail
- mLatchD.mFramesReleased.clear();
- {
- Mutex::Autolock _l(mLock);
- 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());
- }
- }
- }
}
void AudioFlinger::MixerThread::threadLoop_sleepTime()
@@ -6205,6 +6207,10 @@
checkSuspendOnAddEffectChain_l(chain);
+ // make sure enabled pre processing effects state is communicated to the HAL as we
+ // just moved them to a new input stream.
+ chain->syncHalEffectsState();
+
mEffectChains.add(chain);
return NO_ERROR;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7d3b854..bb9aa18 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -821,7 +821,9 @@
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 b2d53cf..b9308fa 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -906,7 +906,8 @@
// 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();
+ playbackThread->mLatchQ.mFramesReleased[i] :
+ mAudioTrackServerProxy->framesReleased();
bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten;
if (framesWritten < unpresentedFrames) {
mPreviousValid = false;