Merge "NuPlayer: pull more video buffers in advance in GenericSource" into mnc-dev
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index a4b24d7..2ca3f1c 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -215,6 +215,7 @@
sp<MemoryDealer> mDealer[2];
sp<ANativeWindow> mNativeWindow;
+ int mNativeWindowUsageBits;
sp<AMessage> mInputFormat;
sp<AMessage> mOutputFormat;
sp<AMessage> mBaseOutputFormat;
@@ -266,7 +267,8 @@
status_t freeBuffer(OMX_U32 portIndex, size_t i);
status_t handleSetSurface(const sp<Surface> &surface);
- status_t setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */);
+ status_t setupNativeWindowSizeFormatAndUsage(
+ ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */);
status_t configureOutputBuffersFromNativeWindow(
OMX_U32 *nBufferCount, OMX_U32 *nBufferSize,
diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h
index 24df85a..65f415a 100644
--- a/include/media/stagefright/foundation/ADebug.h
+++ b/include/media/stagefright/foundation/ADebug.h
@@ -117,7 +117,7 @@
};
- // parse the property or string to get the debug level for a component name
+ // parse the property or string to get a long-type level for a component name
// string format is:
// <level>[:<glob>][,<level>[:<glob>]...]
// - <level> is 0-5 corresponding to ADebug::Level
@@ -125,10 +125,14 @@
// matches all components
// - string is read left-to-right, and the last matching level is returned, or
// the def if no terms matched
+ static long GetLevelFromSettingsString(
+ const char *name, const char *value, long def);
+ static long GetLevelFromProperty(
+ const char *name, const char *value, long def);
+
+ // same for ADebug::Level - performs clamping to valid debug ranges
static Level GetDebugLevelFromProperty(
const char *name, const char *propertyName, Level def = kDebugNone);
- static Level GetDebugLevelFromString(
- const char *name, const char *value, Level def = kDebugNone);
// remove redundant segments of a codec name, and return a newly allocated
// string suitable for debugging
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index f13bcf3..3bfb09a 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -476,11 +476,13 @@
switch (event) {
case AUDIO_OUTPUT_OPENED:
case AUDIO_INPUT_OPENED: {
- if (getIoDescriptor(ioDesc->mIoHandle) != 0) {
- ALOGV("ioConfigChanged() opening already existing output! %d", ioDesc->mIoHandle);
- break;
+ sp<AudioIoDescriptor> oldDesc = getIoDescriptor(ioDesc->mIoHandle);
+ if (oldDesc == 0) {
+ mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
+ } else {
+ deviceId = oldDesc->getDeviceId();
+ mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc);
}
- mIoDescriptors.add(ioDesc->mIoHandle, ioDesc);
if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
deviceId = ioDesc->getDeviceId();
@@ -1074,7 +1076,14 @@
if (afc == 0) {
return NO_INIT;
}
- return afc->addAudioDeviceCallback(callback, audioIo);
+ status_t status = afc->addAudioDeviceCallback(callback, audioIo);
+ if (status == NO_ERROR) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af != 0) {
+ af->registerClient(afc);
+ }
+ }
+ return status;
}
status_t AudioSystem::removeAudioDeviceCallback(
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index ae869d6..0ecfb1e 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1335,21 +1335,23 @@
mCallbackCookie(NULL),
mCallbackData(NULL),
mBytesWritten(0),
+ mStreamType(AUDIO_STREAM_MUSIC),
+ mAttributes(attr),
+ mLeftVolume(1.0),
+ mRightVolume(1.0),
+ mPlaybackRate(AUDIO_PLAYBACK_RATE_DEFAULT),
+ mSampleRateHz(0),
+ mMsecsPerFrame(0),
+ mFrameSize(0),
mSessionId(sessionId),
mUid(uid),
mPid(pid),
- mFlags(AUDIO_OUTPUT_FLAG_NONE) {
+ mSendLevel(0.0),
+ mAuxEffectId(0),
+ mFlags(AUDIO_OUTPUT_FLAG_NONE)
+{
ALOGV("AudioOutput(%d)", sessionId);
- mStreamType = AUDIO_STREAM_MUSIC;
- mLeftVolume = 1.0;
- mRightVolume = 1.0;
- mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
- mSampleRateHz = 0;
- mMsecsPerFrame = 0;
- mAuxEffectId = 0;
- mSendLevel = 0.0;
setMinBufferCount();
- mAttributes = attr;
}
MediaPlayerService::AudioOutput::~AudioOutput()
@@ -1358,6 +1360,7 @@
delete mCallbackData;
}
+//static
void MediaPlayerService::AudioOutput::setMinBufferCount()
{
char value[PROPERTY_VALUE_MAX];
@@ -1367,92 +1370,105 @@
}
}
+// static
bool MediaPlayerService::AudioOutput::isOnEmulator()
{
- setMinBufferCount();
+ setMinBufferCount(); // benign race wrt other threads
return mIsOnEmulator;
}
+// static
int MediaPlayerService::AudioOutput::getMinBufferCount()
{
- setMinBufferCount();
+ setMinBufferCount(); // benign race wrt other threads
return mMinBufferCount;
}
ssize_t MediaPlayerService::AudioOutput::bufferSize() const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
- return mTrack->frameCount() * frameSize();
+ return mTrack->frameCount() * mFrameSize;
}
ssize_t MediaPlayerService::AudioOutput::frameCount() const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
return mTrack->frameCount();
}
ssize_t MediaPlayerService::AudioOutput::channelCount() const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
return mTrack->channelCount();
}
ssize_t MediaPlayerService::AudioOutput::frameSize() const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
- return mTrack->frameSize();
+ return mFrameSize;
}
uint32_t MediaPlayerService::AudioOutput::latency () const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return 0;
return mTrack->latency();
}
float MediaPlayerService::AudioOutput::msecsPerFrame() const
{
+ Mutex::Autolock lock(mLock);
return mMsecsPerFrame;
}
status_t MediaPlayerService::AudioOutput::getPosition(uint32_t *position) const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
return mTrack->getPosition(position);
}
status_t MediaPlayerService::AudioOutput::getTimestamp(AudioTimestamp &ts) const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
return mTrack->getTimestamp(ts);
}
status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
- *frameswritten = mBytesWritten / frameSize();
+ *frameswritten = mBytesWritten / mFrameSize;
return OK;
}
status_t MediaPlayerService::AudioOutput::setParameters(const String8& keyValuePairs)
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return NO_INIT;
return mTrack->setParameters(keyValuePairs);
}
String8 MediaPlayerService::AudioOutput::getParameters(const String8& keys)
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return String8::empty();
return mTrack->getParameters(keys);
}
void MediaPlayerService::AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
+ Mutex::Autolock lock(mLock);
mAttributes = attributes;
}
-void MediaPlayerService::AudioOutput::deleteRecycledTrack()
+void MediaPlayerService::AudioOutput::deleteRecycledTrack_l()
{
- ALOGV("deleteRecycledTrack");
-
+ ALOGV("deleteRecycledTrack_l");
if (mRecycledTrack != 0) {
if (mCallbackData != NULL) {
@@ -1470,12 +1486,17 @@
// AudioFlinger to drain the track.
mRecycledTrack.clear();
+ close_l();
delete mCallbackData;
mCallbackData = NULL;
- close();
}
}
+void MediaPlayerService::AudioOutput::close_l()
+{
+ mTrack.clear();
+}
+
status_t MediaPlayerService::AudioOutput::open(
uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format, int bufferCount,
@@ -1535,6 +1556,7 @@
}
}
+ Mutex::Autolock lock(mLock);
mCallback = cb;
mCallbackCookie = cookie;
@@ -1577,7 +1599,7 @@
// we must close the previous output before opening a new one
if (bothOffloaded && !reuse) {
ALOGV("both offloaded and not recycling");
- deleteRecycledTrack();
+ deleteRecycledTrack_l();
}
sp<AudioTrack> t;
@@ -1655,7 +1677,7 @@
if (reuse) {
ALOGV("chaining to next output and recycling track");
- close();
+ close_l();
mTrack = mRecycledTrack;
mRecycledTrack.clear();
if (mCallbackData != NULL) {
@@ -1669,7 +1691,7 @@
// we're not going to reuse the track, unblock and flush it
// this was done earlier if both tracks are offloaded
if (!bothOffloaded) {
- deleteRecycledTrack();
+ deleteRecycledTrack_l();
}
CHECK((t != NULL) && ((mCallback == NULL) || (newcbd != NULL)));
@@ -1681,9 +1703,10 @@
mSampleRateHz = sampleRate;
mFlags = t->getFlags(); // we suggest the flags above, but new AudioTrack() may not grant it.
mMsecsPerFrame = 1E3f / (mPlaybackRate.mSpeed * sampleRate);
+ mFrameSize = t->frameSize();
uint32_t pos;
if (t->getPosition(&pos) == OK) {
- mBytesWritten = uint64_t(pos) * t->frameSize();
+ mBytesWritten = uint64_t(pos) * mFrameSize;
}
mTrack = t;
@@ -1704,6 +1727,7 @@
status_t MediaPlayerService::AudioOutput::start()
{
ALOGV("start");
+ Mutex::Autolock lock(mLock);
if (mCallbackData != NULL) {
mCallbackData->endTrackSwitch();
}
@@ -1716,30 +1740,88 @@
}
void MediaPlayerService::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) {
+ Mutex::Autolock lock(mLock);
mNextOutput = nextOutput;
}
-
void MediaPlayerService::AudioOutput::switchToNextOutput() {
ALOGV("switchToNextOutput");
- if (mNextOutput != NULL) {
- if (mCallbackData != NULL) {
- mCallbackData->beginTrackSwitch();
+
+ // Try to acquire the callback lock before moving track (without incurring deadlock).
+ const unsigned kMaxSwitchTries = 100;
+ Mutex::Autolock lock(mLock);
+ for (unsigned tries = 0;;) {
+ if (mTrack == 0) {
+ return;
}
- delete mNextOutput->mCallbackData;
- mNextOutput->mCallbackData = mCallbackData;
- mCallbackData = NULL;
- mNextOutput->mRecycledTrack = mTrack;
- mTrack.clear();
- mNextOutput->mSampleRateHz = mSampleRateHz;
- mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
- mNextOutput->mBytesWritten = mBytesWritten;
- mNextOutput->mFlags = mFlags;
+ if (mNextOutput != NULL && mNextOutput != this) {
+ if (mCallbackData != NULL) {
+ // two alternative approaches
+#if 1
+ CallbackData *callbackData = mCallbackData;
+ mLock.unlock();
+ // proper acquisition sequence
+ callbackData->lock();
+ mLock.lock();
+ // Caution: it is unlikely that someone deleted our callback or changed our target
+ if (callbackData != mCallbackData || mNextOutput == NULL || mNextOutput == this) {
+ // fatal if we are starved out.
+ LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
+ "switchToNextOutput() cannot obtain correct lock sequence");
+ callbackData->unlock();
+ continue;
+ }
+ callbackData->mSwitching = true; // begin track switch
+#else
+ // tryBeginTrackSwitch() returns false if the callback has the lock.
+ if (!mCallbackData->tryBeginTrackSwitch()) {
+ // fatal if we are starved out.
+ LOG_ALWAYS_FATAL_IF(++tries > kMaxSwitchTries,
+ "switchToNextOutput() cannot obtain callback lock");
+ mLock.unlock();
+ usleep(5 * 1000 /* usec */); // allow callback to use AudioOutput
+ mLock.lock();
+ continue;
+ }
+#endif
+ }
+
+ Mutex::Autolock nextLock(mNextOutput->mLock);
+
+ // If the next output track is not NULL, then it has been
+ // opened already for playback.
+ // This is possible even without the next player being started,
+ // for example, the next player could be prepared and seeked.
+ //
+ // Presuming it isn't advisable to force the track over.
+ if (mNextOutput->mTrack == NULL) {
+ ALOGD("Recycling track for gapless playback");
+ delete mNextOutput->mCallbackData;
+ mNextOutput->mCallbackData = mCallbackData;
+ mNextOutput->mRecycledTrack = mTrack;
+ mNextOutput->mSampleRateHz = mSampleRateHz;
+ mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
+ mNextOutput->mBytesWritten = mBytesWritten;
+ mNextOutput->mFlags = mFlags;
+ mNextOutput->mFrameSize = mFrameSize;
+ close_l();
+ mCallbackData = NULL; // destruction handled by mNextOutput
+ } else {
+ ALOGW("Ignoring gapless playback because next player has already started");
+ // remove track in case resource needed for future players.
+ if (mCallbackData != NULL) {
+ mCallbackData->endTrackSwitch(); // release lock for callbacks before close.
+ }
+ close_l();
+ }
+ }
+ break;
}
}
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size, bool blocking)
{
+ Mutex::Autolock lock(mLock);
LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
//ALOGV("write(%p, %u)", buffer, size);
@@ -1756,6 +1838,7 @@
void MediaPlayerService::AudioOutput::stop()
{
ALOGV("stop");
+ Mutex::Autolock lock(mLock);
mBytesWritten = 0;
if (mTrack != 0) mTrack->stop();
}
@@ -1763,6 +1846,7 @@
void MediaPlayerService::AudioOutput::flush()
{
ALOGV("flush");
+ Mutex::Autolock lock(mLock);
mBytesWritten = 0;
if (mTrack != 0) mTrack->flush();
}
@@ -1770,18 +1854,21 @@
void MediaPlayerService::AudioOutput::pause()
{
ALOGV("pause");
+ Mutex::Autolock lock(mLock);
if (mTrack != 0) mTrack->pause();
}
void MediaPlayerService::AudioOutput::close()
{
ALOGV("close");
- mTrack.clear();
+ Mutex::Autolock lock(mLock);
+ close_l();
}
void MediaPlayerService::AudioOutput::setVolume(float left, float right)
{
ALOGV("setVolume(%f, %f)", left, right);
+ Mutex::Autolock lock(mLock);
mLeftVolume = left;
mRightVolume = right;
if (mTrack != 0) {
@@ -1793,6 +1880,7 @@
{
ALOGV("setPlaybackRate(%f %f %d %d)",
rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) {
// remember rate so that we can set it when the track is opened
mPlaybackRate = rate;
@@ -1814,6 +1902,7 @@
status_t MediaPlayerService::AudioOutput::getPlaybackRate(AudioPlaybackRate *rate)
{
ALOGV("setPlaybackRate");
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) {
return NO_INIT;
}
@@ -1824,6 +1913,7 @@
status_t MediaPlayerService::AudioOutput::setAuxEffectSendLevel(float level)
{
ALOGV("setAuxEffectSendLevel(%f)", level);
+ Mutex::Autolock lock(mLock);
mSendLevel = level;
if (mTrack != 0) {
return mTrack->setAuxEffectSendLevel(level);
@@ -1834,6 +1924,7 @@
status_t MediaPlayerService::AudioOutput::attachAuxEffect(int effectId)
{
ALOGV("attachAuxEffect(%d)", effectId);
+ Mutex::Autolock lock(mLock);
mAuxEffectId = effectId;
if (mTrack != 0) {
return mTrack->attachAuxEffect(effectId);
@@ -1846,6 +1937,7 @@
int event, void *cookie, void *info) {
//ALOGV("callbackwrapper");
CallbackData *data = (CallbackData*)cookie;
+ // lock to ensure we aren't caught in the middle of a track switch.
data->lock();
AudioOutput *me = data->getOutput();
AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
@@ -1915,11 +2007,13 @@
int MediaPlayerService::AudioOutput::getSessionId() const
{
+ Mutex::Autolock lock(mLock);
return mSessionId;
}
uint32_t MediaPlayerService::AudioOutput::getSampleRate() const
{
+ Mutex::Autolock lock(mLock);
if (mTrack == 0) return 0;
return mTrack->getSampleRate();
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 7527506..9e6ca52 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -132,7 +132,8 @@
static void setMinBufferCount();
static void CallbackWrapper(
int event, void *me, void *info);
- void deleteRecycledTrack();
+ void deleteRecycledTrack_l();
+ void close_l();
sp<AudioTrack> mTrack;
sp<AudioTrack> mRecycledTrack;
@@ -148,32 +149,47 @@
AudioPlaybackRate mPlaybackRate;
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
+ size_t mFrameSize;
int mSessionId;
int mUid;
int mPid;
float mSendLevel;
int mAuxEffectId;
+ audio_output_flags_t mFlags;
+ mutable Mutex mLock;
+
+ // static variables below not protected by mutex
static bool mIsOnEmulator;
static int mMinBufferCount; // 12 for emulator; otherwise 4
- audio_output_flags_t mFlags;
// CallbackData is what is passed to the AudioTrack as the "user" data.
// We need to be able to target this to a different Output on the fly,
// so we can't use the Output itself for this.
class CallbackData {
+ friend AudioOutput;
public:
CallbackData(AudioOutput *cookie) {
mData = cookie;
mSwitching = false;
}
- AudioOutput * getOutput() { return mData;}
+ AudioOutput * getOutput() const { return mData; }
void setOutput(AudioOutput* newcookie) { mData = newcookie; }
// lock/unlock are used by the callback before accessing the payload of this object
- void lock() { mLock.lock(); }
- void unlock() { mLock.unlock(); }
- // beginTrackSwitch/endTrackSwitch are used when this object is being handed over
+ void lock() const { mLock.lock(); }
+ void unlock() const { mLock.unlock(); }
+
+ // tryBeginTrackSwitch/endTrackSwitch are used when the CallbackData is handed over
// to the next sink.
- void beginTrackSwitch() { mLock.lock(); mSwitching = true; }
+
+ // tryBeginTrackSwitch() returns true only if it obtains the lock.
+ bool tryBeginTrackSwitch() {
+ LOG_ALWAYS_FATAL_IF(mSwitching, "tryBeginTrackSwitch() already called");
+ if (mLock.tryLock() != OK) {
+ return false;
+ }
+ mSwitching = true;
+ return true;
+ }
void endTrackSwitch() {
if (mSwitching) {
mLock.unlock();
@@ -182,7 +198,7 @@
}
private:
AudioOutput * mData;
- mutable Mutex mLock;
+ mutable Mutex mLock; // a recursive mutex might make this unnecessary.
bool mSwitching;
DISALLOW_EVIL_CONSTRUCTORS(CallbackData);
};
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index 20193c3..cc6f743 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -25,7 +25,8 @@
$(TOP)/frameworks/av/media/libmediaplayerservice \
$(TOP)/frameworks/native/include/media/openmax
-LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CFLAGS += -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS
+
LOCAL_CLANG := true
LOCAL_MODULE:= libstagefright_nuplayer
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index a9d8904..3e0ee08 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -623,12 +623,19 @@
case kWhatSetVideoSurface:
{
- ALOGV("kWhatSetVideoSurface");
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
sp<Surface> surface = static_cast<Surface *>(obj.get());
- if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL) {
+
+ ALOGD("onSetVideoSurface(%p, %s video decoder)",
+ surface.get(),
+ (mSource != NULL && mSource->getFormat(false /* audio */) != NULL
+ && mVideoDecoder != NULL) ? "have" : "no");
+
+ if (mSource == NULL || mSource->getFormat(false /* audio */) == NULL
+ // NOTE: mVideoDecoder's mSurface is always non-null
+ || (mVideoDecoder != NULL && mVideoDecoder->setVideoSurface(surface) == OK)) {
performSetSurface(surface);
break;
}
@@ -639,7 +646,7 @@
mDeferredActions.push_back(new SetSurfaceAction(surface));
- if (obj != NULL) {
+ if (obj != NULL || mAudioDecoder != NULL) {
if (mStarted) {
// Issue a seek to refresh the video screen only if started otherwise
// the extractor may not yet be started and will assert.
@@ -830,20 +837,6 @@
// Don't try to re-open audio sink if there's an existing decoder.
if (mAudioSink != NULL && mAudioDecoder == NULL) {
- sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
- sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
- audio_stream_type_t streamType = mAudioSink->getAudioStreamType();
- const bool hasVideo = (videoFormat != NULL);
- const bool canOffload = canOffloadStream(
- audioMeta, hasVideo, mSource->isStreaming(), streamType);
- if (canOffload) {
- if (!mOffloadAudio) {
- mRenderer->signalEnableOffloadAudio();
- }
- // open audio sink early under offload mode.
- sp<AMessage> format = mSource->getFormat(true /*audio*/);
- tryOpenAudioSinkForOffload(format, hasVideo);
- }
instantiateDecoder(true, &mAudioDecoder);
}
@@ -1104,20 +1097,6 @@
performSeek(positionUs);
if (reason == Renderer::kDueToError) {
- sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
- sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
- audio_stream_type_t streamType = mAudioSink->getAudioStreamType();
- const bool hasVideo = (videoFormat != NULL);
- const bool canOffload = canOffloadStream(
- audioMeta, hasVideo, mSource->isStreaming(), streamType);
- if (canOffload) {
- mRenderer->signalEnableOffloadAudio();
- sp<AMessage> format = mSource->getFormat(true /*audio*/);
- tryOpenAudioSinkForOffload(format, hasVideo);
- } else {
- mRenderer->signalDisableOffloadAudio();
- mOffloadAudio = false;
- }
instantiateDecoder(true /* audio */, &mAudioDecoder);
}
}
@@ -1449,6 +1428,36 @@
mRenderer->closeAudioSink();
}
+void NuPlayer::determineAudioModeChange() {
+ if (mSource == NULL || mAudioSink == NULL) {
+ return;
+ }
+
+ if (mRenderer == NULL) {
+ ALOGW("No renderer can be used to determine audio mode. Use non-offload for safety.");
+ mOffloadAudio = false;
+ return;
+ }
+
+ sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */);
+ sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
+ audio_stream_type_t streamType = mAudioSink->getAudioStreamType();
+ const bool hasVideo = (videoFormat != NULL);
+ const bool canOffload = canOffloadStream(
+ audioMeta, hasVideo, mSource->isStreaming(), streamType);
+ if (canOffload) {
+ if (!mOffloadAudio) {
+ mRenderer->signalEnableOffloadAudio();
+ }
+ // open audio sink early under offload mode.
+ sp<AMessage> format = mSource->getFormat(true /*audio*/);
+ tryOpenAudioSinkForOffload(format, hasVideo);
+ } else {
+ mRenderer->signalDisableOffloadAudio();
+ mOffloadAudio = false;
+ }
+}
+
status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) {
if (*decoder != NULL) {
return OK;
@@ -1490,6 +1499,7 @@
++mAudioDecoderGeneration;
notify->setInt32("generation", mAudioDecoderGeneration);
+ determineAudioModeChange();
if (mOffloadAudio) {
const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL);
format->setInt32("has-video", hasVideo);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 38904bc..9f4c462 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -220,6 +220,7 @@
void tryOpenAudioSinkForOffload(const sp<AMessage> &format, bool hasVideo);
void closeAudioSink();
+ void determineAudioModeChange();
status_t instantiateDecoder(bool audio, sp<DecoderBase> *decoder);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 99a2a84..dcc28c4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -87,6 +87,22 @@
return mStats;
}
+status_t NuPlayer::Decoder::setVideoSurface(const sp<Surface> &surface) {
+ if (surface == NULL || ADebug::isExperimentEnabled("legacy-setsurface")) {
+ return BAD_VALUE;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
+
+ msg->setObject("surface", surface);
+ sp<AMessage> response;
+ status_t err = msg->postAndAwaitResponse(&response);
+ if (err == OK && response != NULL) {
+ CHECK(response->findInt32("err", &err));
+ }
+ return err;
+}
+
void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());
@@ -169,6 +185,46 @@
break;
}
+ case kWhatSetVideoSurface:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("surface", &obj));
+ sp<Surface> surface = static_cast<Surface *>(obj.get()); // non-null
+ int32_t err = INVALID_OPERATION;
+ // NOTE: in practice mSurface is always non-null, but checking here for completeness
+ if (mCodec != NULL && mSurface != NULL) {
+ // TODO: once AwesomePlayer is removed, remove this automatic connecting
+ // to the surface by MediaPlayerService.
+ //
+ // at this point MediaPlayerService::client has already connected to the
+ // surface, which MediaCodec does not expect
+ err = native_window_api_disconnect(surface.get(), NATIVE_WINDOW_API_MEDIA);
+ if (err == OK) {
+ err = mCodec->setSurface(surface);
+ ALOGI_IF(err, "codec setSurface returned: %d", err);
+ if (err == OK) {
+ // reconnect to the old surface as MPS::Client will expect to
+ // be able to disconnect from it.
+ (void)native_window_api_connect(mSurface.get(), NATIVE_WINDOW_API_MEDIA);
+ mSurface = surface;
+ }
+ }
+ if (err != OK) {
+ // reconnect to the new surface on error as MPS::Client will expect to
+ // be able to disconnect from it.
+ (void)native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA);
+ }
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
default:
DecoderBase::onMessageReceived(msg);
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index ceccb7a..ed0be62 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -32,6 +32,9 @@
virtual sp<AMessage> getStats() const;
+ // sets the output surface of video decoders.
+ virtual status_t setVideoSurface(const sp<Surface> &surface);
+
protected:
virtual ~Decoder();
@@ -50,6 +53,7 @@
enum {
kWhatCodecNotify = 'cdcN',
kWhatRenderBuffer = 'rndr',
+ kWhatSetVideoSurface = 'sSur'
};
sp<Surface> mSurface;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 8f030f0..b0dc01d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -27,6 +27,7 @@
struct ABuffer;
struct MediaCodec;
class MediaBuffer;
+class Surface;
struct NuPlayer::DecoderBase : public AHandler {
DecoderBase(const sp<AMessage> ¬ify);
@@ -36,6 +37,7 @@
void setParameters(const sp<AMessage> ¶ms);
void setRenderer(const sp<Renderer> &renderer);
+ virtual status_t setVideoSurface(const sp<Surface> &) { return INVALID_OPERATION; }
status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const;
void signalFlush();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 7e55aac..13a7d94 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -266,6 +266,7 @@
Mutex::Autolock autoLock(mLock);
if (audio) {
mNotifyCompleteAudio |= notifyComplete;
+ clearAudioFirstAnchorTime_l();
++mAudioQueueGeneration;
++mAudioDrainGeneration;
} else {
@@ -275,7 +276,6 @@
}
clearAnchorTime_l();
- clearAudioFirstAnchorTime_l();
mVideoLateByUs = 0;
mSyncQueues = false;
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7452e4b..cebd577 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -493,6 +493,7 @@
ACodec::ACodec()
: mQuirks(0),
mNode(0),
+ mNativeWindowUsageBits(0),
mSentFormat(false),
mIsVideo(false),
mIsEncoder(false),
@@ -642,7 +643,7 @@
return OK;
}
- // allow keeping unset surface
+ // cannot switch from bytebuffers to surface
if (mNativeWindow == NULL) {
ALOGW("component was not configured with a surface");
return INVALID_OPERATION;
@@ -661,11 +662,20 @@
return INVALID_OPERATION;
}
- status_t err = setupNativeWindowSizeFormatAndUsage(nativeWindow);
+ int usageBits = 0;
+ status_t err = setupNativeWindowSizeFormatAndUsage(nativeWindow, &usageBits);
if (err != OK) {
return err;
}
+ int ignoredFlags = (GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER
+ | GRALLOC_USAGE_EXTERNAL_DISP);
+ // New output surface is not allowed to add new usage flag except ignored ones.
+ if ((usageBits & ~(mNativeWindowUsageBits | ignoredFlags)) != 0) {
+ ALOGW("cannot change usage from %#x to %#x", mNativeWindowUsageBits, usageBits);
+ return BAD_VALUE;
+ }
+
// get min undequeued count. We cannot switch to a surface that has a higher
// undequeued count than we allocated.
int minUndequeuedBuffers = 0;
@@ -747,6 +757,7 @@
}
mNativeWindow = nativeWindow;
+ mNativeWindowUsageBits = usageBits;
return OK;
}
@@ -868,7 +879,8 @@
return OK;
}
-status_t ACodec::setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */) {
+status_t ACodec::setupNativeWindowSizeFormatAndUsage(
+ ANativeWindow *nativeWindow /* nonnull */, int *finalUsage /* nonnull */) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
@@ -894,6 +906,7 @@
}
usage |= GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP;
+ *finalUsage = usage;
ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage);
return setNativeWindowSizeFormatAndUsage(
@@ -916,9 +929,10 @@
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err == OK) {
- err = setupNativeWindowSizeFormatAndUsage(mNativeWindow.get());
+ err = setupNativeWindowSizeFormatAndUsage(mNativeWindow.get(), &mNativeWindowUsageBits);
}
if (err != OK) {
+ mNativeWindowUsageBits = 0;
return err;
}
@@ -1063,9 +1077,11 @@
for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
- status_t error = cancelBufferToNativeWindow(info);
- if (err == 0) {
- err = error;
+ if (info->mStatus == BufferInfo::OWNED_BY_US) {
+ status_t error = cancelBufferToNativeWindow(info);
+ if (err == 0) {
+ err = error;
+ }
}
}
@@ -1152,9 +1168,11 @@
for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) {
BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
- status_t error = cancelBufferToNativeWindow(info);
- if (err == OK) {
- err = error;
+ if (info->mStatus == BufferInfo::OWNED_BY_US) {
+ status_t error = cancelBufferToNativeWindow(info);
+ if (err == OK) {
+ err = error;
+ }
}
}
@@ -1933,6 +1951,7 @@
// to SW renderer
ALOGI("[%s] Falling back to software renderer", mComponentName.c_str());
mNativeWindow.clear();
+ mNativeWindowUsageBits = 0;
haveNativeWindow = false;
usingSwRenderer = true;
if (storingMetadataInDecodedBuffers()) {
@@ -4538,9 +4557,7 @@
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
- status_t err =
- ADebug::isExperimentEnabled("legacy-setsurface") ? BAD_VALUE :
- mCodec->handleSetSurface(static_cast<Surface *>(obj.get()));
+ status_t err = mCodec->handleSetSurface(static_cast<Surface *>(obj.get()));
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
@@ -4864,6 +4881,12 @@
case RESUBMIT_BUFFERS:
{
if (buffer != NULL && !mCodec->mPortEOS[kPortIndexInput]) {
+ // Do not send empty input buffer w/o EOS to the component.
+ if (buffer->size() == 0 && !eos) {
+ postFillThisBuffer(info);
+ break;
+ }
+
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
@@ -5331,6 +5354,7 @@
}
mCodec->mNativeWindow.clear();
+ mCodec->mNativeWindowUsageBits = 0;
mCodec->mNode = 0;
mCodec->mOMX.clear();
mCodec->mQuirks = 0;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index b696746..26b07d4 100755
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -4034,6 +4034,10 @@
CHECK(mBuffer == NULL);
return err;
}
+ if (size > mBuffer->size()) {
+ ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
+ return ERROR_BUFFER_TOO_SMALL;
+ }
}
if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) {
@@ -4294,6 +4298,10 @@
ALOGV("acquire_buffer returned %d", err);
return err;
}
+ if (size > mBuffer->size()) {
+ ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
+ return ERROR_BUFFER_TOO_SMALL;
+ }
}
const Sample *smpl = &mCurrentSamples[mCurrentSampleIndex];
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 69f44ed..fb32d3a 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2528,6 +2528,7 @@
err = native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA);
if (err == BAD_VALUE) {
ALOGI("native window already connected. Assuming no change of surface");
+ return err;
} else if (err == OK) {
// Require a fresh set of buffers after each connect by using a unique generation
// number. Rely on the fact that max supported process id by Linux is 2^22.
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 7ea5cbd..5edc04c 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -216,6 +216,12 @@
String8(kPolicySupportsMultipleSecureCodecs),
String8(value.c_str())));
}
+ if (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
+ policies.push_back(
+ MediaResourcePolicy(
+ String8(kPolicySupportsSecureWithNonSecureCodec),
+ String8(value.c_str())));
+ }
if (policies.size() > 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index a616b40..aab3af7 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -641,6 +641,11 @@
if (!inQueue.empty()) {
inInfo = *inQueue.begin();
inHeader = inInfo->mHeader;
+ if (inHeader == NULL) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ continue;
+ }
} else {
break;
}
@@ -652,14 +657,21 @@
outHeader->nTimeStamp = 0;
outHeader->nOffset = 0;
- if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
- mReceivedEOS = true;
+ if (inHeader != NULL) {
if (inHeader->nFilledLen == 0) {
inQueue.erase(inQueue.begin());
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
+
+ if (!(inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+ continue;
+ }
+
+ mReceivedEOS = true;
inHeader = NULL;
setFlushMode();
+ } else if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ mReceivedEOS = true;
}
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index ede645c..0c1a149 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -103,34 +103,41 @@
while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+ if (inHeader == NULL) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ continue;
+ }
PortInfo *port = editPortInfo(1);
OMX_BUFFERHEADERTYPE *outHeader =
port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader;
- if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) {
+ if (inHeader->nFilledLen == 0) {
inQueue.erase(inQueue.begin());
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
++mInputBufferCount;
- outHeader->nFilledLen = 0;
- outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
- List<BufferInfo *>::iterator it = outQueue.begin();
- while ((*it)->mHeader != outHeader) {
- ++it;
+ List<BufferInfo *>::iterator it = outQueue.begin();
+ while ((*it)->mHeader != outHeader) {
+ ++it;
+ }
+
+ BufferInfo *outInfo = *it;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(it);
+ outInfo = NULL;
+
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
}
-
- BufferInfo *outInfo = *it;
- outInfo->mOwnedByUs = false;
- outQueue.erase(it);
- outInfo = NULL;
-
- notifyFillBufferDone(outHeader);
- outHeader = NULL;
return;
}
diff --git a/media/libstagefright/foundation/ADebug.cpp b/media/libstagefright/foundation/ADebug.cpp
index 0d1cea4..24fa561 100644
--- a/media/libstagefright/foundation/ADebug.cpp
+++ b/media/libstagefright/foundation/ADebug.cpp
@@ -32,11 +32,10 @@
namespace android {
//static
-ADebug::Level ADebug::GetDebugLevelFromString(
- const char *name, const char *value, ADebug::Level def) {
+long ADebug::GetLevelFromSettingsString(
+ const char *name, const char *value, long def) {
// split on ,
const char *next = value, *current;
- const unsigned long maxLevel = (unsigned long)kDebugMax;
while (next != NULL) {
current = next;
next = strchr(current, ',');
@@ -52,8 +51,8 @@
// get level
char *end;
- errno = 0; // strtoul does not clear errno, but it can be set for any return value
- unsigned long level = strtoul(current, &end, 10);
+ errno = 0; // strtol does not clear errno, but it can be set for any return value
+ long level = strtol(current, &end, 10);
while (isspace(*end)) {
++end;
}
@@ -77,8 +76,18 @@
}
}
- // update debug level
- def = (Level)min(level, maxLevel);
+ // update value
+ def = level;
+ }
+ return def;
+}
+
+//static
+long ADebug::GetLevelFromProperty(
+ const char *name, const char *propertyName, long def) {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get(propertyName, value, NULL)) {
+ def = GetLevelFromSettingsString(name, value, def);
}
return def;
}
@@ -86,11 +95,8 @@
//static
ADebug::Level ADebug::GetDebugLevelFromProperty(
const char *name, const char *propertyName, ADebug::Level def) {
- char value[PROPERTY_VALUE_MAX];
- if (property_get(propertyName, value, NULL)) {
- return GetDebugLevelFromString(name, value, def);
- }
- return def;
+ long level = GetLevelFromProperty(name, propertyName, (long)def);
+ return (Level)min(max(level, (long)kDebugNone), (long)kDebugMax);
}
//static
@@ -118,6 +124,15 @@
bool ADebug::getExperimentFlag(
bool allow, const char *name, uint64_t modulo,
uint64_t limit, uint64_t plus, uint64_t timeDivisor) {
+ // see if this experiment should be disabled/enabled based on properties.
+ // default to 2 to allow 0/1 specification
+ const int undefined = 2;
+ long level = GetLevelFromProperty(name, "debug.stagefright.experiments", undefined);
+ if (level != undefined) {
+ ALOGI("experiment '%s': %s from property", name, level ? "ENABLED" : "disabled");
+ return level != 0;
+ }
+
static volatile int32_t haveSerial = 0;
static uint64_t serialNum;
if (!android_atomic_acquire_load(&haveSerial)) {
@@ -138,11 +153,10 @@
num = num * 256 + c;
}
}
- ALOGI("got serial");
serialNum = num;
android_atomic_release_store(1, &haveSerial);
}
- ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL));
+ ALOGD("serial: %llu, time: %lld", (long long unsigned)serialNum, (long long)time(NULL));
// MINOR: use modulo for counter and time, so that their sum does not
// roll over, and mess up the correlation between related experiments.
// e.g. keep (a mod 2N) = 0 impl (a mod N) = 0
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 27509cb..2fc5135 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -58,15 +58,21 @@
BandwidthEstimator();
void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
- bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL);
+ bool estimateBandwidth(
+ int32_t *bandwidth,
+ bool *isStable = NULL,
+ int32_t *shortTermBps = NULL);
private:
// Bandwidth estimation parameters
+ static const int32_t kShortTermBandwidthItems = 3;
static const int32_t kMinBandwidthHistoryItems = 20;
static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec
static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec
+ static const int64_t kMaxBandwidthHistoryAgeUs = 60000000ll; // 60 sec
struct BandwidthEntry {
+ int64_t mTimestampUs;
int64_t mDelayUs;
size_t mNumBytes;
};
@@ -74,6 +80,7 @@
Mutex mLock;
List<BandwidthEntry> mBandwidthHistory;
List<int32_t> mPrevEstimates;
+ int32_t mShortTermEstimate;
bool mHasNewSample;
bool mIsStable;
int64_t mTotalTransferTimeUs;
@@ -83,6 +90,7 @@
};
LiveSession::BandwidthEstimator::BandwidthEstimator() :
+ mShortTermEstimate(0),
mHasNewSample(false),
mIsStable(true),
mTotalTransferTimeUs(0),
@@ -93,7 +101,9 @@
size_t numBytes, int64_t delayUs) {
AutoMutex autoLock(mLock);
+ int64_t nowUs = ALooper::GetNowUs();
BandwidthEntry entry;
+ entry.mTimestampUs = nowUs;
entry.mDelayUs = delayUs;
entry.mNumBytes = numBytes;
mTotalTransferTimeUs += delayUs;
@@ -115,7 +125,10 @@
// and total transfer time at least kMaxBandwidthHistoryWindowUs.
while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) {
List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
- if (mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) {
+ // remove sample if either absolute age or total transfer time is
+ // over kMaxBandwidthHistoryWindowUs
+ if (nowUs - it->mTimestampUs < kMaxBandwidthHistoryAgeUs &&
+ mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) {
break;
}
mTotalTransferTimeUs -= it->mDelayUs;
@@ -125,7 +138,7 @@
}
bool LiveSession::BandwidthEstimator::estimateBandwidth(
- int32_t *bandwidthBps, bool *isStable) {
+ int32_t *bandwidthBps, bool *isStable, int32_t *shortTermBps) {
AutoMutex autoLock(mLock);
if (mBandwidthHistory.size() < 2) {
@@ -137,6 +150,9 @@
if (isStable) {
*isStable = mIsStable;
}
+ if (shortTermBps) {
+ *shortTermBps = mShortTermEstimate;
+ }
return true;
}
@@ -147,6 +163,21 @@
}
mHasNewSample = false;
+ int64_t totalTimeUs = 0;
+ size_t totalBytes = 0;
+ if (mBandwidthHistory.size() >= kShortTermBandwidthItems) {
+ List<BandwidthEntry>::iterator it = --mBandwidthHistory.end();
+ for (size_t i = 0; i < kShortTermBandwidthItems; i++, it--) {
+ totalTimeUs += it->mDelayUs;
+ totalBytes += it->mNumBytes;
+ }
+ }
+ mShortTermEstimate = totalTimeUs > 0 ?
+ (totalBytes * 8E6 / totalTimeUs) : *bandwidthBps;
+ if (shortTermBps) {
+ *shortTermBps = mShortTermEstimate;
+ }
+
int32_t minEstimate = -1, maxEstimate = -1;
List<int32_t>::iterator it;
for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) {
@@ -158,10 +189,14 @@
maxEstimate = estimate;
}
}
- mIsStable = (maxEstimate <= minEstimate * 4 / 3);
+ // consider it stable if long-term average is not jumping a lot
+ // and short-term average is not much lower than long-term average
+ mIsStable = (maxEstimate <= minEstimate * 4 / 3)
+ && mShortTermEstimate > minEstimate * 7 / 10;
if (isStable) {
- *isStable = mIsStable;
+ *isStable = mIsStable;
}
+
#if 0
{
char dumpStr[1024] = {0};
@@ -251,6 +286,7 @@
mCurBandwidthIndex(-1),
mOrigBandwidthIndex(-1),
mLastBandwidthBps(-1ll),
+ mLastBandwidthStable(false),
mBandwidthEstimator(new BandwidthEstimator()),
mMaxWidth(720),
mMaxHeight(480),
@@ -713,6 +749,20 @@
}
}
+ // remember the failure index (as mCurBandwidthIndex will be restored
+ // after cancelBandwidthSwitch()), and record last fail time
+ size_t failureIndex = mCurBandwidthIndex;
+ mBandwidthItems.editItemAt(
+ failureIndex).mLastFailureUs = ALooper::GetNowUs();
+
+ if (mSwitchInProgress) {
+ // if error happened when we switch to a variant, try fallback
+ // to other variant to save the session
+ if (tryBandwidthFallback()) {
+ break;
+ }
+ }
+
if (mInPreparationPhase) {
postPrepared(err);
}
@@ -887,6 +937,13 @@
}
// static
+bool LiveSession::isBandwidthValid(const BandwidthItem &item) {
+ static const int64_t kBlacklistWindowUs = 300 * 1000000ll;
+ return item.mLastFailureUs < 0
+ || ALooper::GetNowUs() - item.mLastFailureUs > kBlacklistWindowUs;
+}
+
+// static
int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
if (a->mBandwidth < b->mBandwidth) {
return -1;
@@ -986,6 +1043,7 @@
BandwidthItem item;
item.mPlaylistIndex = i;
+ item.mLastFailureUs = -1ll;
sp<AMessage> meta;
AString uri;
@@ -1223,6 +1281,13 @@
X/T < bw1 / (bw1 + bw0 - bw)
*/
+ // abort old bandwidth immediately if bandwidth is fluctuating a lot.
+ // our estimate could be far off, and fetching old bandwidth could
+ // take too long.
+ if (!mLastBandwidthStable) {
+ return 0.0f;
+ }
+
// Taking the measured current bandwidth at 50% face value only,
// as our bandwidth estimation is a lagging indicator. Being
// conservative on this, we prefer switching to lower bandwidth
@@ -1250,6 +1315,16 @@
mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs);
}
+ssize_t LiveSession::getLowestValidBandwidthIndex() const {
+ for (size_t index = 0; index < mBandwidthItems.size(); index++) {
+ if (isBandwidthValid(mBandwidthItems[index])) {
+ return index;
+ }
+ }
+ // if playlists are all blacklisted, return 0 and hope it's alive
+ return 0;
+}
+
size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) {
if (mBandwidthItems.size() < 2) {
// shouldn't be here if we only have 1 bandwidth, check
@@ -1284,14 +1359,18 @@
}
}
- // Pick the highest bandwidth stream below or equal to estimated bandwidth.
+ // Pick the highest bandwidth stream that's not currently blacklisted
+ // below or equal to estimated bandwidth.
index = mBandwidthItems.size() - 1;
- while (index > 0) {
+ ssize_t lowestBandwidth = getLowestValidBandwidthIndex();
+ while (index > lowestBandwidth) {
// be conservative (70%) to avoid overestimating and immediately
// switching down again.
size_t adjustedBandwidthBps = bandwidthBps * 7 / 10;
- if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) {
+ const BandwidthItem &item = mBandwidthItems[index];
+ if (item.mBandwidth <= adjustedBandwidthBps
+ && isBandwidthValid(item)) {
break;
}
--index;
@@ -2172,21 +2251,57 @@
notify->post();
}
+bool LiveSession::tryBandwidthFallback() {
+ if (mInPreparationPhase || mReconfigurationInProgress) {
+ // Don't try fallback during prepare or reconfig.
+ // If error happens there, it's likely unrecoverable.
+ return false;
+ }
+ if (mCurBandwidthIndex > mOrigBandwidthIndex) {
+ // if we're switching up, simply cancel and resume old variant
+ cancelBandwidthSwitch(true /* resume */);
+ return true;
+ } else {
+ // if we're switching down, we're likely about to underflow (if
+ // not already underflowing). try the lowest viable bandwidth if
+ // not on that variant already.
+ ssize_t lowestValid = getLowestValidBandwidthIndex();
+ if (mCurBandwidthIndex > lowestValid) {
+ cancelBandwidthSwitch();
+ changeConfiguration(-1ll, lowestValid);
+ return true;
+ }
+ }
+ // return false if we couldn't find any fallback
+ return false;
+}
+
/*
* returns true if a bandwidth switch is actually needed (and started),
* returns false otherwise
*/
bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// no need to check bandwidth if we only have 1 bandwidth settings
- if (mSwitchInProgress || mBandwidthItems.size() < 2) {
+ if (mBandwidthItems.size() < 2) {
return false;
}
- int32_t bandwidthBps;
+ if (mSwitchInProgress) {
+ if (mBuffering) {
+ tryBandwidthFallback();
+ }
+ return false;
+ }
+
+ int32_t bandwidthBps, shortTermBps;
bool isStable;
- if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) {
- ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
+ if (mBandwidthEstimator->estimateBandwidth(
+ &bandwidthBps, &isStable, &shortTermBps)) {
+ ALOGV("bandwidth estimated at %.2f kbps, "
+ "stable %d, shortTermBps %.2f kbps",
+ bandwidthBps / 1024.0f, isStable, shortTermBps / 1024.0f);
mLastBandwidthBps = bandwidthBps;
+ mLastBandwidthStable = isStable;
} else {
ALOGV("no bandwidth estimate.");
return false;
@@ -2203,9 +2318,13 @@
if (canSwitchDown || canSwitchUp) {
// bandwidth estimating has some delay, if we have to downswitch when
- // it hasn't stabilized, be very conservative on bandwidth.
+ // it hasn't stabilized, use the short term to guess real bandwidth,
+ // since it may be dropping too fast.
+ // (note this doesn't apply to upswitch, always use longer average there)
if (!isStable && canSwitchDown) {
- bandwidthBps /= 2;
+ if (shortTermBps < bandwidthBps) {
+ bandwidthBps = shortTermBps;
+ }
}
ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 21be413..90d56d0 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -146,6 +146,7 @@
struct BandwidthItem {
size_t mPlaylistIndex;
unsigned long mBandwidth;
+ int64_t mLastFailureUs;
};
struct FetcherInfo {
@@ -199,6 +200,7 @@
ssize_t mCurBandwidthIndex;
ssize_t mOrigBandwidthIndex;
int32_t mLastBandwidthBps;
+ bool mLastBandwidthStable;
sp<BandwidthEstimator> mBandwidthEstimator;
sp<M3UParser> mPlaylist;
@@ -268,8 +270,10 @@
ssize_t currentBWIndex, ssize_t targetBWIndex) const;
void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
size_t getBandwidthIndex(int32_t bandwidthBps);
+ ssize_t getLowestValidBandwidthIndex() const;
HLSTime latestMediaSegmentStartTime() const;
+ static bool isBandwidthValid(const BandwidthItem &item);
static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
static StreamType indexToType(int idx);
static ssize_t typeToIndex(int32_t type);
@@ -287,6 +291,7 @@
sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil);
bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
+ bool tryBandwidthFallback();
void schedulePollBuffering();
void cancelPollBuffering();
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 4851528..72d832e 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -511,6 +511,13 @@
msg->post();
}
+/*
+ * pauseAsync
+ *
+ * threshold: 0.0f - pause after current fetch block (default 47Kbytes)
+ * -1.0f - pause after finishing current segment
+ * 0.0~1.0f - pause if remaining of current segment exceeds threshold
+ */
void PlaylistFetcher::pauseAsync(
float thresholdRatio, bool disconnect) {
setStoppingThreshold(thresholdRatio, disconnect);
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index aae3e9f..cbe9673 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -264,8 +264,19 @@
if (event.isInit()) {
for (size_t i = 0; i < mSourceImpls.size(); ++i) {
if (mSourceImpls[i].get() == event.getMediaSource().get()) {
- mSyncPoints.editItemAt(i).add(
- event.getTimeUs(), event.getOffset());
+ KeyedVector<int64_t, off64_t> *syncPoints = &mSyncPoints.editItemAt(i);
+ syncPoints->add(event.getTimeUs(), event.getOffset());
+ // We're keeping the size of the sync points at most 5mb per a track.
+ size_t size = syncPoints->size();
+ if (size >= 327680) {
+ int64_t firstTimeUs = syncPoints->keyAt(0);
+ int64_t lastTimeUs = syncPoints->keyAt(size - 1);
+ if (event.getTimeUs() - firstTimeUs > lastTimeUs - event.getTimeUs()) {
+ syncPoints->removeItemsAt(0, 4096);
+ } else {
+ syncPoints->removeItemsAt(size - 4096, 4096);
+ }
+ }
break;
}
}
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 04303c4..e6a0c49 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -505,7 +505,15 @@
CHECK_LT(portIndex, mPorts.size());
PortInfo *port = &mPorts.editItemAt(portIndex);
- CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
+ // Ideally, the port should not in transitioning state when flushing.
+ // However, in error handling case, e.g., the client can't allocate buffers
+ // when it tries to re-enable the port, the port will be stuck in ENABLING.
+ // The client will then transition the component from Executing to Idle,
+ // which leads to flushing ports. At this time, it should be ok to notify
+ // the client of the error and still clear all buffers on the port.
+ if (port->mTransition != PortInfo::NONE) {
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+ }
for (size_t i = 0; i < port->mBuffers.size(); ++i) {
BufferInfo *buffer = &port->mBuffers.editItemAt(i);
diff --git a/media/libstagefright/tests/Utils_test.cpp b/media/libstagefright/tests/Utils_test.cpp
index c1e663c..d736501 100644
--- a/media/libstagefright/tests/Utils_test.cpp
+++ b/media/libstagefright/tests/Utils_test.cpp
@@ -109,21 +109,21 @@
TEST_F(UtilsTest, TestDebug) {
#define LVL(x) (ADebug::Level)(x)
- ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "", LVL(5)), LVL(5));
- ASSERT_EQ(ADebug::GetDebugLevelFromString("video", " \t \n ", LVL(2)), LVL(2));
- ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3", LVL(5)), LVL(3));
- ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo", LVL(5)), LVL(3));
- ASSERT_EQ(ADebug::GetDebugLevelFromString(
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "", LVL(5)), LVL(5));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", " \t \n ", LVL(2)), LVL(2));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "3", LVL(5)), LVL(3));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "3:*deo", LVL(5)), LVL(3));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString(
"video", "\t\n 3 \t\n:\t\n video \t\n", LVL(5)), LVL(3));
- ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "3:*deo,2:vid*", LVL(5)), LVL(2));
- ASSERT_EQ(ADebug::GetDebugLevelFromString(
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "3:*deo,2:vid*", LVL(5)), LVL(2));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString(
"avideo", "\t\n 3 \t\n:\t\n avideo \t\n,\t\n 2 \t\n:\t\n video \t\n", LVL(5)), LVL(3));
- ASSERT_EQ(ADebug::GetDebugLevelFromString(
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString(
"audio.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(2));
- ASSERT_EQ(ADebug::GetDebugLevelFromString(
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString(
"video.omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3));
- ASSERT_EQ(ADebug::GetDebugLevelFromString("video", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3));
- ASSERT_EQ(ADebug::GetDebugLevelFromString("omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(4));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("video", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(3));
+ ASSERT_EQ(ADebug::GetLevelFromSettingsString("omx", "4:*omx,3:*d*o*,2:audio*", LVL(5)), LVL(4));
#undef LVL
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 52fce34..8f1e050 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1252,11 +1252,9 @@
if (client == 0) {
return;
}
- bool clientAdded = false;
+ pid_t pid = IPCThreadState::self()->getCallingPid();
{
Mutex::Autolock _cl(mClientLock);
-
- pid_t pid = IPCThreadState::self()->getCallingPid();
if (mNotificationClients.indexOfKey(pid) < 0) {
sp<NotificationClient> notificationClient = new NotificationClient(this,
client,
@@ -1267,22 +1265,19 @@
sp<IBinder> binder = IInterface::asBinder(client);
binder->linkToDeath(notificationClient);
- clientAdded = true;
}
}
// mClientLock should not be held here because ThreadBase::sendIoConfigEvent() will lock the
// ThreadBase mutex and the locking order is ThreadBase::mLock then AudioFlinger::mClientLock.
- if (clientAdded) {
- // the config change is always sent from playback or record threads to avoid deadlock
- // with AudioSystem::gLock
- for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED);
- }
+ // the config change is always sent from playback or record threads to avoid deadlock
+ // with AudioSystem::gLock
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AUDIO_OUTPUT_OPENED, pid);
+ }
- for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED);
- }
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->sendIoConfigEvent(AUDIO_INPUT_OPENED, pid);
}
}
@@ -1316,12 +1311,15 @@
}
void AudioFlinger::ioConfigChanged(audio_io_config_event event,
- const sp<AudioIoDescriptor>& ioDesc)
+ const sp<AudioIoDescriptor>& ioDesc,
+ pid_t pid)
{
Mutex::Autolock _l(mClientLock);
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
+ if ((pid == 0) || (mNotificationClients.keyAt(i) == pid)) {
+ mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
+ }
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index d087ced..4f7e27d 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -559,7 +559,8 @@
float streamVolume_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].volume; }
void ioConfigChanged(audio_io_config_event event,
- const sp<AudioIoDescriptor>& ioDesc);
+ const sp<AudioIoDescriptor>& ioDesc,
+ pid_t pid = 0);
// Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t.
// They all share the same ID space, but the namespaces are actually independent
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d3ea9d8..d9f1a83 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -532,7 +532,8 @@
// RecordThread::readInputParameters_l()
//FIXME: mStandby should be true here. Is this some kind of hack?
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
- mPrevInDevice(AUDIO_DEVICE_NONE), mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
+ mPrevOutDevice(AUDIO_DEVICE_NONE), mPrevInDevice(AUDIO_DEVICE_NONE),
+ mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
// mName will be set by concrete (non-virtual) subclass
mDeathRecipient(new PMDeathRecipient(this)),
mSystemReady(systemReady)
@@ -627,16 +628,16 @@
return status;
}
-void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event)
+void AudioFlinger::ThreadBase::sendIoConfigEvent(audio_io_config_event event, pid_t pid)
{
Mutex::Autolock _l(mLock);
- sendIoConfigEvent_l(event);
+ sendIoConfigEvent_l(event, pid);
}
// sendIoConfigEvent_l() must be called with ThreadBase::mLock held
-void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event)
+void AudioFlinger::ThreadBase::sendIoConfigEvent_l(audio_io_config_event event, pid_t pid)
{
- sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event);
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, pid);
sendConfigEvent_l(configEvent);
}
@@ -706,7 +707,7 @@
} break;
case CFG_EVENT_IO: {
IoConfigEventData *data = (IoConfigEventData *)event->mData.get();
- ioConfigChanged(data->mEvent);
+ ioConfigChanged(data->mEvent, data->mPid);
} break;
case CFG_EVENT_SET_PARAMETER: {
SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get();
@@ -1999,7 +2000,7 @@
return out_s8;
}
-void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event) {
+void AudioFlinger::PlaybackThread::ioConfigChanged(audio_io_config_event event, pid_t pid) {
sp<AudioIoDescriptor> desc = new AudioIoDescriptor();
ALOGV("PlaybackThread::ioConfigChanged, thread %p, event %d", this, event);
@@ -2021,7 +2022,7 @@
default:
break;
}
- mAudioFlinger->ioConfigChanged(event, desc);
+ mAudioFlinger->ioConfigChanged(event, desc, pid);
}
void AudioFlinger::PlaybackThread::writeCallback()
@@ -3133,7 +3134,10 @@
for (size_t i = 0; i < mEffectChains.size(); i++) {
mEffectChains[i]->setDevice_l(type);
}
- bool configChanged = mOutDevice != type;
+
+ // mPrevOutDevice is the latest device set by createAudioPatch_l(). It is not set when
+ // the thread is created so that the first patch creation triggers an ioConfigChanged callback
+ bool configChanged = mPrevOutDevice != type;
mOutDevice = type;
mPatch = *patch;
@@ -3163,6 +3167,7 @@
*handle = AUDIO_PATCH_HANDLE_NONE;
}
if (configChanged) {
+ mPrevOutDevice = type;
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
}
return status;
@@ -4482,9 +4487,16 @@
sp<Track> previousTrack = mPreviousTrack.promote();
sp<Track> latestTrack = mLatestActiveTrack.promote();
- if (previousTrack != 0 && latestTrack != 0 &&
- (previousTrack->sessionId() != latestTrack->sessionId())) {
- mFlushPending = true;
+ if (previousTrack != 0 && latestTrack != 0) {
+ if (mType == DIRECT) {
+ if (previousTrack.get() != latestTrack.get()) {
+ mFlushPending = true;
+ }
+ } else /* mType == OFFLOAD */ {
+ if (previousTrack->sessionId() != latestTrack->sessionId()) {
+ mFlushPending = true;
+ }
+ }
}
PlaybackThread::onAddNewTrack_l();
}
@@ -4577,12 +4589,8 @@
if (track != previousTrack.get()) {
// Flush any data still being written from last track
mBytesRemaining = 0;
- // flush data already sent if changing audio session as audio
- // comes from a different source. Also invalidate previous track to force a
- // seek when resuming.
- if (previousTrack->sessionId() != track->sessionId()) {
- previousTrack->invalidate();
- }
+ // Invalidate previous track to force a seek when resuming.
+ previousTrack->invalidate();
}
}
mPreviousTrack = track;
@@ -6868,7 +6876,7 @@
return out_s8;
}
-void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event) {
+void AudioFlinger::RecordThread::ioConfigChanged(audio_io_config_event event, pid_t pid) {
sp<AudioIoDescriptor> desc = new AudioIoDescriptor();
desc->mIoHandle = mId;
@@ -6888,7 +6896,7 @@
default:
break;
}
- mAudioFlinger->ioConfigChanged(event, desc);
+ mAudioFlinger->ioConfigChanged(event, desc, pid);
}
void AudioFlinger::RecordThread::readInputParameters_l()
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 0783371..46ac300 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -104,21 +104,22 @@
class IoConfigEventData : public ConfigEventData {
public:
- IoConfigEventData(audio_io_config_event event) :
- mEvent(event) {}
+ IoConfigEventData(audio_io_config_event event, pid_t pid) :
+ mEvent(event), mPid(pid) {}
virtual void dump(char *buffer, size_t size) {
snprintf(buffer, size, "IO event: event %d\n", mEvent);
}
const audio_io_config_event mEvent;
+ const pid_t mPid;
};
class IoConfigEvent : public ConfigEvent {
public:
- IoConfigEvent(audio_io_config_event event) :
+ IoConfigEvent(audio_io_config_event event, pid_t pid) :
ConfigEvent(CFG_EVENT_IO) {
- mData = new IoConfigEventData(event);
+ mData = new IoConfigEventData(event, pid);
}
virtual ~IoConfigEvent() {}
};
@@ -255,13 +256,13 @@
status_t& status) = 0;
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys) = 0;
- virtual void ioConfigChanged(audio_io_config_event event) = 0;
+ virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0) = 0;
// sendConfigEvent_l() must be called with ThreadBase::mLock held
// Can temporarily release the lock if waiting for a reply from
// processConfigEvents_l().
status_t sendConfigEvent_l(sp<ConfigEvent>& event);
- void sendIoConfigEvent(audio_io_config_event event);
- void sendIoConfigEvent_l(audio_io_config_event event);
+ void sendIoConfigEvent(audio_io_config_event event, pid_t pid = 0);
+ void sendIoConfigEvent_l(audio_io_config_event event, pid_t pid = 0);
void sendPrioConfigEvent(pid_t pid, pid_t tid, int32_t prio);
void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
status_t sendSetParameterConfigEvent_l(const String8& keyValuePair);
@@ -436,6 +437,7 @@
bool mStandby; // Whether thread is currently in standby.
audio_devices_t mOutDevice; // output device
audio_devices_t mInDevice; // input device
+ audio_devices_t mPrevOutDevice; // previous output device
audio_devices_t mPrevInDevice; // previous input device
struct audio_patch mPatch;
audio_source_t mAudioSource;
@@ -572,7 +574,7 @@
{ return android_atomic_acquire_load(&mSuspended) > 0; }
virtual String8 getParameters(const String8& keys);
- virtual void ioConfigChanged(audio_io_config_event event);
+ virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0);
status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
// FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency.
// Consider also removing and passing an explicit mMainBuffer initialization
@@ -1254,7 +1256,7 @@
status_t& status);
virtual void cacheParameters_l() {}
virtual String8 getParameters(const String8& keys);
- virtual void ioConfigChanged(audio_io_config_event event);
+ virtual void ioConfigChanged(audio_io_config_event event, pid_t pid = 0);
virtual status_t createAudioPatch_l(const struct audio_patch *patch,
audio_patch_handle_t *handle);
virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h
index 4eef02f2..4b73e3c 100755
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -37,8 +37,9 @@
* A device mask for all audio input and output devices where matching inputs/outputs on device
* type alone is not enough: the address must match too
*/
-#define APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | \
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+#define APM_AUDIO_DEVICE_OUT_MATCH_ADDRESS_ALL (AUDIO_DEVICE_OUT_REMOTE_SUBMIX)
+
+#define APM_AUDIO_DEVICE_IN_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX)
/**
* Check if the state given correspond to an in call state.
@@ -80,5 +81,8 @@
*/
static inline bool device_distinguishes_on_address(audio_devices_t device)
{
- return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0);
+ return (((device & AUDIO_DEVICE_BIT_IN) != 0) &&
+ ((~AUDIO_DEVICE_BIT_IN & device & APM_AUDIO_DEVICE_IN_MATCH_ADDRESS_ALL) != 0)) ||
+ (((device & AUDIO_DEVICE_BIT_IN) == 0) &&
+ ((device & APM_AUDIO_DEVICE_OUT_MATCH_ADDRESS_ALL) != 0));
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index de204e5..6d99640 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1076,6 +1076,9 @@
beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT);
}
+ // check active before incrementing usage count
+ bool force = !outputDesc->isActive();
+
// increment usage count for this stream on the requested output:
// NOTE that the usage count is the same for duplicated output and hardware output which is
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
@@ -1091,7 +1094,6 @@
(strategy == STRATEGY_SONIFICATION_RESPECTFUL) ||
(beaconMuteLatency > 0);
uint32_t waitMs = beaconMuteLatency;
- bool force = false;
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != outputDesc) {
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index e2b6695..61147ff 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -204,6 +204,17 @@
}
}
+void ResourceManagerService::getClientForResource_l(
+ int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients) {
+ if (res == NULL) {
+ return;
+ }
+ sp<IResourceManagerClient> client;
+ if (getLowestPriorityBiggestClient_l(callingPid, res->mType, &client)) {
+ clients->push_back(client);
+ }
+}
+
bool ResourceManagerService::reclaimResource(
int callingPid, const Vector<MediaResource> &resources) {
String8 log = String8::format("reclaimResource(callingPid %d, resources %s)",
@@ -213,54 +224,61 @@
Vector<sp<IResourceManagerClient>> clients;
{
Mutex::Autolock lock(mLock);
- // first pass to handle secure/non-secure codec conflict
+ const MediaResource *secureCodec = NULL;
+ const MediaResource *nonSecureCodec = NULL;
+ const MediaResource *graphicMemory = NULL;
for (size_t i = 0; i < resources.size(); ++i) {
String8 type = resources[i].mType;
- if (type == kResourceSecureCodec) {
- if (!mSupportsMultipleSecureCodecs) {
- if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) {
- return false;
- }
- }
- if (!mSupportsSecureWithNonSecureCodec) {
- if (!getAllClients_l(callingPid, String8(kResourceNonSecureCodec), &clients)) {
- return false;
- }
- }
+ if (resources[i].mType == kResourceSecureCodec) {
+ secureCodec = &resources[i];
} else if (type == kResourceNonSecureCodec) {
- if (!mSupportsSecureWithNonSecureCodec) {
- if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) {
- return false;
- }
+ nonSecureCodec = &resources[i];
+ } else if (type == kResourceGraphicMemory) {
+ graphicMemory = &resources[i];
+ }
+ }
+
+ // first pass to handle secure/non-secure codec conflict
+ if (secureCodec != NULL) {
+ if (!mSupportsMultipleSecureCodecs) {
+ if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) {
+ return false;
+ }
+ }
+ if (!mSupportsSecureWithNonSecureCodec) {
+ if (!getAllClients_l(callingPid, String8(kResourceNonSecureCodec), &clients)) {
+ return false;
+ }
+ }
+ }
+ if (nonSecureCodec != NULL) {
+ if (!mSupportsSecureWithNonSecureCodec) {
+ if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) {
+ return false;
}
}
}
if (clients.size() == 0) {
// if no secure/non-secure codec conflict, run second pass to handle other resources.
- for (size_t i = 0; i < resources.size(); ++i) {
- String8 type = resources[i].mType;
- if (type == kResourceGraphicMemory) {
- sp<IResourceManagerClient> client;
- if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) {
- return false;
- }
- clients.push_back(client);
- }
- }
+ getClientForResource_l(callingPid, graphicMemory, &clients);
}
if (clients.size() == 0) {
// if we are here, run the third pass to free one codec with the same type.
- for (size_t i = 0; i < resources.size(); ++i) {
- String8 type = resources[i].mType;
- if (type == kResourceSecureCodec || type == kResourceNonSecureCodec) {
- sp<IResourceManagerClient> client;
- if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) {
- return false;
- }
- clients.push_back(client);
- }
+ getClientForResource_l(callingPid, secureCodec, &clients);
+ getClientForResource_l(callingPid, nonSecureCodec, &clients);
+ }
+
+ if (clients.size() == 0) {
+ // if we are here, run the fourth pass to free one codec with the different type.
+ if (secureCodec != NULL) {
+ MediaResource temp(String8(kResourceNonSecureCodec), 1);
+ getClientForResource_l(callingPid, &temp, &clients);
+ }
+ if (nonSecureCodec != NULL) {
+ MediaResource temp(String8(kResourceSecureCodec), 1);
+ getClientForResource_l(callingPid, &temp, &clients);
}
}
}
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 0d9d878..ca218fc 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -65,6 +65,9 @@
virtual void removeResource(int64_t clientId);
+ // Tries to reclaim resource from processes with lower priority than the calling process
+ // according to the requested resources.
+ // Returns true if any resource has been reclaimed, otherwise returns false.
virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources);
protected:
@@ -95,6 +98,11 @@
bool isCallingPriorityHigher_l(int callingPid, int pid);
+ // A helper function basically calls getLowestPriorityBiggestClient_l and add the result client
+ // to the given Vector.
+ void getClientForResource_l(
+ int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients);
+
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
sp<ServiceLog> mServiceLog;
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 3d53f1f..8ae6a55 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -79,6 +79,10 @@
static const int kTestPid1 = 30;
static const int kTestPid2 = 20;
+static const int kLowPriorityPid = 40;
+static const int kMidPriorityPid = 25;
+static const int kHighPriorityPid = 10;
+
class ResourceManagerServiceTest : public ::testing::Test {
public:
ResourceManagerServiceTest()
@@ -227,15 +231,12 @@
String8 type = String8(kResourceSecureCodec);
String8 unknowType = String8("unknowType");
Vector<sp<IResourceManagerClient> > clients;
- int lowPriorityPid = 100;
- EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients));
- int midPriorityPid = 25;
+ EXPECT_FALSE(mService->getAllClients_l(kLowPriorityPid, type, &clients));
// some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l
// will fail.
- EXPECT_FALSE(mService->getAllClients_l(midPriorityPid, type, &clients));
- int highPriorityPid = 10;
- EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, unknowType, &clients));
- EXPECT_TRUE(mService->getAllClients_l(highPriorityPid, type, &clients));
+ EXPECT_FALSE(mService->getAllClients_l(kMidPriorityPid, type, &clients));
+ EXPECT_TRUE(mService->getAllClients_l(kHighPriorityPid, unknowType, &clients));
+ EXPECT_TRUE(mService->getAllClients_l(kHighPriorityPid, type, &clients));
EXPECT_EQ(2u, clients.size());
EXPECT_EQ(mTestClient3, clients[0]);
@@ -254,19 +255,19 @@
mService->mSupportsSecureWithNonSecureCodec = true;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(40, resources));
- EXPECT_FALSE(mService->reclaimResource(25, resources));
+ EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
+ EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
// reclaim all secure codecs
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(true, false, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(true /* c1 */, false /* c2 */, true /* c3 */);
// call again should reclaim one largest graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, true, false);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
}
// ### secure codecs can't coexist and secure codec can't coexist with non-secure codec ###
@@ -276,15 +277,15 @@
mService->mSupportsSecureWithNonSecureCodec = false;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(40, resources));
- EXPECT_FALSE(mService->reclaimResource(25, resources));
+ EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
+ EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
// reclaim all secure and non-secure codecs
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(true, true, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(true /* c1 */, true /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
}
@@ -295,23 +296,23 @@
mService->mSupportsSecureWithNonSecureCodec = false;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(40, resources));
- EXPECT_FALSE(mService->reclaimResource(25, resources));
+ EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
+ EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
// reclaim all non-secure codecs
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, true, false);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// call again should reclaim one largest graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(true, false, false);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another largest graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, false, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
}
// ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
@@ -321,22 +322,22 @@
mService->mSupportsSecureWithNonSecureCodec = true;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(40, resources));
+ EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
- EXPECT_TRUE(mService->reclaimResource(10, resources));
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
// one largest graphic memory from lowest process got reclaimed
- verifyClients(true, false, false);
+ verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, true, false);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, false, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
}
// ### secure codecs can coexist and secure codec can coexist with non-secure codec ###
@@ -348,19 +349,17 @@
Vector<MediaResource> resources;
resources.push_back(MediaResource(String8(kResourceSecureCodec), 1));
- EXPECT_TRUE(mService->reclaimResource(10, resources));
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
// secure codec from lowest process got reclaimed
- verifyClients(true, false, false);
+ verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another secure codec from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, false, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
- // nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
-
- // clean up client 2 which still has non secure codec left
- mService->removeResource((int64_t) mTestClient2.get());
+ // no more secure codec, non-secure codec will be reclaimed.
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
}
}
@@ -375,19 +374,19 @@
mService->mSupportsSecureWithNonSecureCodec = false;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(40, resources));
- EXPECT_FALSE(mService->reclaimResource(25, resources));
+ EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
+ EXPECT_FALSE(mService->reclaimResource(kMidPriorityPid, resources));
// reclaim all secure codecs
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(true, false, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(true /* c1 */, false /* c2 */, true /* c3 */);
// call again should reclaim one graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, true, false);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
}
@@ -397,22 +396,22 @@
mService->mSupportsSecureWithNonSecureCodec = true;
// priority too low
- EXPECT_FALSE(mService->reclaimResource(40, resources));
+ EXPECT_FALSE(mService->reclaimResource(kLowPriorityPid, resources));
- EXPECT_TRUE(mService->reclaimResource(10, resources));
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
// one largest graphic memory from lowest process got reclaimed
- verifyClients(true, false, false);
+ verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, true, false);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
// call again should reclaim another graphic memory from lowest process
- EXPECT_TRUE(mService->reclaimResource(10, resources));
- verifyClients(false, false, true);
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
// nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ EXPECT_FALSE(mService->reclaimResource(kHighPriorityPid, resources));
}
// ### secure codec can coexist with non-secure codec ###
@@ -423,15 +422,15 @@
Vector<MediaResource> resources;
resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1));
- EXPECT_TRUE(mService->reclaimResource(10, resources));
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
// one non secure codec from lowest process got reclaimed
- verifyClients(false, true, false);
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
- // nothing left
- EXPECT_FALSE(mService->reclaimResource(10, resources));
+ // no more non-secure codec, secure codec from lowest priority process will be reclaimed
+ EXPECT_TRUE(mService->reclaimResource(kHighPriorityPid, resources));
+ verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
- // clean up client 1 and 3 which still have secure codec left
- mService->removeResource((int64_t) mTestClient1.get());
+ // clean up client 3 which still left
mService->removeResource((int64_t) mTestClient3.get());
}
}
@@ -439,12 +438,12 @@
void testGetLowestPriorityBiggestClient() {
String8 type = String8(kResourceGraphicMemory);
sp<IResourceManagerClient> client;
- EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(10, type, &client));
+ EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(kHighPriorityPid, type, &client));
addResource();
- EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(100, type, &client));
- EXPECT_TRUE(mService->getLowestPriorityBiggestClient_l(10, type, &client));
+ EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(kLowPriorityPid, type, &client));
+ EXPECT_TRUE(mService->getLowestPriorityBiggestClient_l(kHighPriorityPid, type, &client));
// kTestPid1 is the lowest priority process with kResourceGraphicMemory.
// mTestClient1 has the largest kResourceGraphicMemory within kTestPid1.