Merge "Add AMediaCodec callback"
diff --git a/camera/Android.mk b/camera/Android.mk
index 18800b4..5774b6f 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -52,11 +52,6 @@
system/media/camera/include \
system/media/private/camera/include
-## Enable asserts for 'eng' builds
-ifeq ($(TARGET_BUILD_VARIANT),eng)
-LOCAL_CFLAGS += -UNDEBUG
-endif
-
LOCAL_MODULE:= libcamera_client
include $(BUILD_SHARED_LIBRARY)
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index ba24fcb..59dce91 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -120,7 +120,7 @@
// Set up tag to section index map
ssize_t index = sections.indexOf(sectionString);
- assert(index >= 0);
+ LOG_ALWAYS_FATAL_IF(index < 0, "index %zd must be non-negative", index);
desc->mTagToSectionMap.add(tag, static_cast<uint32_t>(index));
// Set up reverse mapping
@@ -217,7 +217,8 @@
__FUNCTION__, sectionCount, (maxSectionIndex + 1));
return BAD_VALUE;
}
- assert(desc->mSections.setCapacity(sectionCount) > 0);
+ LOG_ALWAYS_FATAL_IF(desc->mSections.setCapacity(sectionCount) <= 0,
+ "Vector capacity must be positive");
for (size_t i = 0; i < sectionCount; ++i) {
String8 sectionName = parcel->readString8();
if (sectionName.isEmpty()) {
@@ -228,7 +229,7 @@
}
}
- assert(tagCount == allTags.size());
+ LOG_ALWAYS_FATAL_IF(tagCount != allTags.size(), "tagCount must be the same as allTags size");
// Set up reverse mapping
for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) {
uint32_t tag = allTags[i];
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index b3c44a8..6a68c94 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -490,10 +490,12 @@
int mSessionId;
transfer_type mTransfer;
- // Next 4 fields may be changed if IAudioRecord is re-created, but always != 0
+ // Next 5 fields may be changed if IAudioRecord is re-created, but always != 0
+ // provided the initial set() was successful
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk; // re-load after mLock.unlock()
+ sp<IMemory> mBufferMemory;
audio_io_handle_t mInput; // returned by AudioSystem::getInput()
int mPreviousPriority; // before start()
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 2c48bbf..79db323 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -154,8 +154,9 @@
* streamType: Select the type of audio stream this track is attached to
* (e.g. AUDIO_STREAM_MUSIC).
* sampleRate: Data source sampling rate in Hz.
- * format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
- * 16 bits per sample).
+ * format: Audio format. For mixed tracks, any PCM format supported by server is OK
+ * or AUDIO_FORMAT_PCM_8_BIT which is handled on client side. For direct
+ * and offloaded tracks, the possible format(s) depends on the output sink.
* channelMask: Channel mask, such that audio_is_output_channel(channelMask) is true.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 9101f06..7db6a48 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -88,6 +88,8 @@
track_flags_t *flags,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int *sessionId,
+ sp<IMemory>& cblk,
+ sp<IMemory>& buffers, // return value 0 means it follows cblk
status_t *status) = 0;
/* query the audio hardware state. This state never changes,
diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h
index eccc2ca..2003985 100644
--- a/include/media/IAudioRecord.h
+++ b/include/media/IAudioRecord.h
@@ -34,9 +34,6 @@
public:
DECLARE_META_INTERFACE(AudioRecord);
- /* get this tracks control block */
- virtual sp<IMemory> getCblk() const = 0;
-
/* After it's created the track is not active. Call start() to
* make it active.
*/
diff --git a/include/media/nbaio/AudioStreamInSource.h b/include/media/nbaio/AudioStreamInSource.h
index eaea63c..5169f1e 100644
--- a/include/media/nbaio/AudioStreamInSource.h
+++ b/include/media/nbaio/AudioStreamInSource.h
@@ -45,7 +45,7 @@
// FIXME Use an audio HAL API to query the buffer filling status when it's available.
virtual ssize_t availableToRead() { return mStreamBufferSizeBytes / mFrameSize; }
- virtual ssize_t read(void *buffer, size_t count);
+ virtual ssize_t read(void *buffer, size_t count, int64_t readPTS);
// NBAIO_Sink end
diff --git a/include/media/nbaio/Pipe.h b/include/media/nbaio/Pipe.h
index c784129..eba37bc 100644
--- a/include/media/nbaio/Pipe.h
+++ b/include/media/nbaio/Pipe.h
@@ -30,7 +30,11 @@
public:
// maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
- Pipe(size_t maxFrames, const NBAIO_Format& format);
+ // buffer is an optional parameter specifying the virtual address of the pipe buffer,
+ // which must be of size roundup(maxFrames) * Format_frameSize(format) bytes.
+ Pipe(size_t maxFrames, const NBAIO_Format& format, void *buffer = NULL);
+
+ // If a buffer was specified in the constructor, it is not automatically freed by destructor.
virtual ~Pipe();
// NBAIO_Port interface
@@ -57,6 +61,7 @@
void * const mBuffer;
volatile int32_t mRear; // written by android_atomic_release_store
volatile int32_t mReaders; // number of PipeReader clients currently attached to this Pipe
+ const bool mFreeBufferInDestructor;
};
} // namespace android
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 276543b..39e57de 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -100,6 +100,7 @@
uint32_t *flags,
int64_t timeoutUs = 0ll);
+ status_t renderOutputBufferAndRelease(size_t index, int64_t timestampNs);
status_t renderOutputBufferAndRelease(size_t index);
status_t releaseOutputBuffer(size_t index);
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
index 2bb6dbe..e0ca8af 100644
--- a/media/libeffects/downmix/Android.mk
+++ b/media/libeffects/downmix/Android.mk
@@ -15,16 +15,10 @@
LOCAL_MODULE_RELATIVE_PATH := soundfx
-ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
-LOCAL_LDLIBS += -ldl
-endif
-
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
-LOCAL_PRELINK_MODULE := false
-
LOCAL_CFLAGS += -fvisibility=hidden
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
index 9e8cb83..ea3c59d 100644
--- a/media/libeffects/preprocessing/Android.mk
+++ b/media/libeffects/preprocessing/Android.mk
@@ -24,12 +24,7 @@
libutils \
liblog
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_LDLIBS += -ldl
-else
LOCAL_SHARED_LIBRARIES += libdl
-endif
-
LOCAL_CFLAGS += -fvisibility=hidden
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 2c8605c..97ab8f8 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -484,6 +484,8 @@
size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
// but we will still need the original value also
int originalSessionId = mSessionId;
+ sp<IMemory> iMem; // for cblk
+ sp<IMemory> bufferMem;
sp<IAudioRecord> record = audioFlinger->openRecord(input,
mSampleRate, mFormat,
mChannelMask,
@@ -491,6 +493,8 @@
&trackFlags,
tid,
&mSessionId,
+ iMem,
+ bufferMem,
&status);
ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
"session ID changed from %d to %d", originalSessionId, mSessionId);
@@ -504,7 +508,6 @@
// AudioFlinger now owns the reference to the I/O handle,
// so we are no longer responsible for releasing it.
- sp<IMemory> iMem = record->getCblk();
if (iMem == 0) {
ALOGE("Could not get control block");
return NO_INIT;
@@ -514,6 +517,22 @@
ALOGE("Could not get control block pointer");
return NO_INIT;
}
+ audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
+
+ // Starting address of buffers in shared memory.
+ // The buffers are either immediately after the control block,
+ // or in a separate area at discretion of server.
+ void *buffers;
+ if (bufferMem == 0) {
+ buffers = cblk + 1;
+ } else {
+ buffers = bufferMem->pointer();
+ if (buffers == NULL) {
+ ALOGE("Could not get buffer pointer");
+ return NO_INIT;
+ }
+ }
+
// invariant that mAudioRecord != 0 is true only after set() returns successfully
if (mAudioRecord != 0) {
mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
@@ -522,7 +541,7 @@
mAudioRecord = record;
mCblkMemory = iMem;
- audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
+ mBufferMemory = bufferMem;
mCblk = cblk;
// note that temp is the (possibly revised) value of frameCount
if (temp < frameCount || (frameCount == 0 && temp == 0)) {
@@ -552,11 +571,6 @@
mInput = input;
mRefreshRemaining = true;
- // Starting address of buffers in shared memory, immediately after the control block. This
- // address is for the mapping within client address space. AudioFlinger::TrackBase::mBuffer
- // is for the server address space.
- void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
-
mFrameCount = frameCount;
// If IAudioRecord is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
@@ -631,6 +645,7 @@
// keep them from going away if another thread re-creates the track during obtainBuffer()
sp<AudioRecordClientProxy> proxy;
sp<IMemory> iMem;
+ sp<IMemory> bufferMem;
{
// start of lock scope
AutoMutex lock(mLock);
@@ -654,6 +669,7 @@
// Keep the extra references
proxy = mProxy;
iMem = mCblkMemory;
+ bufferMem = mBufferMemory;
// Non-blocking if track is stopped
if (!mActive) {
@@ -986,7 +1002,7 @@
status_t result;
// if the new IAudioRecord is created, openRecord_l() will modify the
- // following member variables: mAudioRecord, mCblkMemory and mCblk.
+ // following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory.
// It will also delete the strong references on previous IAudioRecord and IMemory
size_t position = mProxy->getPosition();
mNewPosition = position + mUpdatePeriod;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index dc4f90e..aaaa3f1 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -315,12 +315,20 @@
flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
}
- if (audio_is_linear_pcm(format)) {
- mFrameSize = channelCount * audio_bytes_per_sample(format);
- mFrameSizeAF = channelCount * sizeof(int16_t);
+ if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
+ if (audio_is_linear_pcm(format)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ }
+ mFrameSizeAF = mFrameSize;
} else {
- mFrameSize = sizeof(uint8_t);
- mFrameSizeAF = sizeof(uint8_t);
+ ALOG_ASSERT(audio_is_linear_pcm(format));
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ mFrameSizeAF = channelCount * audio_bytes_per_sample(
+ format == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : format);
+ // createTrack will return an error if PCM format is not supported by server,
+ // so no need to check for specific PCM formats here
}
// Make copy of input parameter offloadInfo so that in the future:
@@ -931,7 +939,11 @@
// Ensure that buffer alignment matches channel count
// 8-bit data in shared memory is not currently supported by AudioFlinger
- size_t alignment = /* mFormat == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2;
+ size_t alignment = audio_bytes_per_sample(
+ mFormat == AUDIO_FORMAT_PCM_8_BIT ? AUDIO_FORMAT_PCM_16_BIT : mFormat);
+ if (alignment & 1) {
+ alignment = 1;
+ }
if (mChannelCount > 1) {
// More than 2 channels does not require stronger alignment than stereo
alignment <<= 1;
@@ -947,7 +959,7 @@
// there's no frameCount parameter.
// But when initializing a shared buffer AudioTrack via set(),
// there _is_ a frameCount parameter. We silently ignore it.
- frameCount = mSharedBuffer->size()/mChannelCount/sizeof(int16_t);
+ frameCount = mSharedBuffer->size() / mFrameSizeAF;
} else if (!(mFlags & AUDIO_OUTPUT_FLAG_FAST)) {
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 58c9fc1..323b675 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -621,7 +621,7 @@
android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
}
- mCblk->mServer += stepCount;
+ cblk->mServer += stepCount;
size_t half = mFrameCount / 2;
if (half == 0) {
@@ -679,10 +679,11 @@
}
bool AudioTrackServerProxy::setStreamEndDone() {
+ audio_track_cblk_t* cblk = mCblk;
bool old =
- (android_atomic_or(CBLK_STREAM_END_DONE, &mCblk->mFlags) & CBLK_STREAM_END_DONE) != 0;
+ (android_atomic_or(CBLK_STREAM_END_DONE, &cblk->mFlags) & CBLK_STREAM_END_DONE) != 0;
if (!old) {
- (void) __futex_syscall3(&mCblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
1);
}
return old;
@@ -690,10 +691,11 @@
void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
{
- mCblk->u.mStreaming.mUnderrunFrames += frameCount;
+ audio_track_cblk_t* cblk = mCblk;
+ cblk->u.mStreaming.mUnderrunFrames += frameCount;
// FIXME also wake futex so that underrun is noticed more quickly
- (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
+ (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
}
// ---------------------------------------------------------------------------
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 1940fe7..0e2463e 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -169,6 +169,8 @@
track_flags_t *flags,
pid_t tid,
int *sessionId,
+ sp<IMemory>& cblk,
+ sp<IMemory>& buffers,
status_t *status)
{
Parcel data, reply;
@@ -188,6 +190,8 @@
lSessionId = *sessionId;
}
data.writeInt32(lSessionId);
+ cblk.clear();
+ buffers.clear();
status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply);
if (lStatus != NO_ERROR) {
ALOGE("openRecord error: %s", strerror(-lStatus));
@@ -206,17 +210,34 @@
}
lStatus = reply.readInt32();
record = interface_cast<IAudioRecord>(reply.readStrongBinder());
+ cblk = interface_cast<IMemory>(reply.readStrongBinder());
+ if (cblk != 0 && cblk->pointer() == NULL) {
+ cblk.clear();
+ }
+ buffers = interface_cast<IMemory>(reply.readStrongBinder());
+ if (buffers != 0 && buffers->pointer() == NULL) {
+ buffers.clear();
+ }
if (lStatus == NO_ERROR) {
if (record == 0) {
ALOGE("openRecord should have returned an IAudioRecord");
lStatus = UNKNOWN_ERROR;
+ } else if (cblk == 0) {
+ ALOGE("openRecord should have returned a cblk");
+ lStatus = NO_MEMORY;
}
+ // buffers is permitted to be 0
} else {
- if (record != 0) {
- ALOGE("openRecord returned an IAudioRecord but with status %d", lStatus);
- record.clear();
+ if (record != 0 || cblk != 0 || buffers != 0) {
+ ALOGE("openRecord returned an IAudioRecord, cblk, "
+ "or buffers but with status %d", lStatus);
}
}
+ if (lStatus != NO_ERROR) {
+ record.clear();
+ cblk.clear();
+ buffers.clear();
+ }
}
if (status != NULL) {
*status = lStatus;
@@ -838,15 +859,20 @@
track_flags_t flags = (track_flags_t) data.readInt32();
pid_t tid = (pid_t) data.readInt32();
int sessionId = data.readInt32();
+ sp<IMemory> cblk;
+ sp<IMemory> buffers;
status_t status;
sp<IAudioRecord> record = openRecord(input,
- sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId, &status);
+ sampleRate, format, channelMask, &frameCount, &flags, tid, &sessionId,
+ cblk, buffers, &status);
LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
reply->writeInt64(frameCount);
reply->writeInt32(flags);
reply->writeInt32(sessionId);
reply->writeInt32(status);
reply->writeStrongBinder(record->asBinder());
+ reply->writeStrongBinder(cblk->asBinder());
+ reply->writeStrongBinder(buffers->asBinder());
return NO_ERROR;
} break;
case SAMPLE_RATE: {
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 9866d70..8a4a383 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -29,7 +29,7 @@
namespace android {
enum {
- GET_CBLK = IBinder::FIRST_CALL_TRANSACTION,
+ UNUSED_WAS_GET_CBLK = IBinder::FIRST_CALL_TRANSACTION,
START,
STOP
};
@@ -42,21 +42,6 @@
{
}
- virtual sp<IMemory> getCblk() const
- {
- Parcel data, reply;
- sp<IMemory> cblk;
- data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor());
- status_t status = remote()->transact(GET_CBLK, data, &reply);
- if (status == NO_ERROR) {
- cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->pointer() == NULL) {
- cblk.clear();
- }
- }
- return cblk;
- }
-
virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession)
{
Parcel data, reply;
@@ -89,11 +74,6 @@
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
- case GET_CBLK: {
- CHECK_INTERFACE(IAudioRecord, data, reply);
- reply->writeStrongBinder(getCblk()->asBinder());
- return NO_ERROR;
- } break;
case START: {
CHECK_INTERFACE(IAudioRecord, data, reply);
int /*AudioSystem::sync_event_t*/ event = data.readInt32();
diff --git a/media/libnbaio/AudioStreamInSource.cpp b/media/libnbaio/AudioStreamInSource.cpp
index 80bf61a..af8f365 100644
--- a/media/libnbaio/AudioStreamInSource.cpp
+++ b/media/libnbaio/AudioStreamInSource.cpp
@@ -63,7 +63,7 @@
return mFramesOverrun;
}
-ssize_t AudioStreamInSource::read(void *buffer, size_t count)
+ssize_t AudioStreamInSource::read(void *buffer, size_t count, int64_t readPTS __unused)
{
if (CC_UNLIKELY(!Format_isValid(mFormat))) {
return NEGOTIATE;
diff --git a/media/libnbaio/Pipe.cpp b/media/libnbaio/Pipe.cpp
index 28a034c..6e0ec8c 100644
--- a/media/libnbaio/Pipe.cpp
+++ b/media/libnbaio/Pipe.cpp
@@ -25,19 +25,22 @@
namespace android {
-Pipe::Pipe(size_t maxFrames, const NBAIO_Format& format) :
+Pipe::Pipe(size_t maxFrames, const NBAIO_Format& format, void *buffer) :
NBAIO_Sink(format),
mMaxFrames(roundup(maxFrames)),
- mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
+ mBuffer(buffer == NULL ? malloc(mMaxFrames * Format_frameSize(format)) : buffer),
mRear(0),
- mReaders(0)
+ mReaders(0),
+ mFreeBufferInDestructor(buffer == NULL)
{
}
Pipe::~Pipe()
{
ALOG_ASSERT(android_atomic_acquire_load(&mReaders) == 0);
- free(mBuffer);
+ if (mFreeBufferInDestructor) {
+ free(mBuffer);
+ }
}
ssize_t Pipe::write(const void *buffer, size_t count)
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 537d9de..0a3a3b6 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3670,7 +3670,28 @@
ATRACE_NAME("render");
// The client wants this buffer to be rendered.
+ int64_t timestampNs = 0;
+ if (!msg->findInt64("timestampNs", ×tampNs)) {
+ // TODO: it seems like we should use the timestamp
+ // in the (media)buffer as it potentially came from
+ // an input surface, but we did not propagate it prior to
+ // API 20. Perhaps check for target SDK version.
+#if 0
+ if (info->mData->meta()->findInt64("timeUs", ×tampNs)) {
+ ALOGI("using buffer PTS of %" PRId64, timestampNs);
+ timestampNs *= 1000;
+ }
+#endif
+ }
+
status_t err;
+ err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs);
+ if (err != OK) {
+ ALOGW("failed to set buffer timestamp: %d", err);
+ } else {
+ ALOGI("set PTS to %" PRId64, timestampNs);
+ }
+
if ((err = mCodec->mNativeWindow->queueBuffer(
mCodec->mNativeWindow.get(),
info->mGraphicBuffer.get(), -1)) == OK) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8d3032b..d679be1 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -106,12 +106,15 @@
}
virtual void render(MediaBuffer *buffer) {
+ int64_t timeUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
render((const uint8_t *)buffer->data() + buffer->range_offset(),
- buffer->range_length());
+ buffer->range_length(), timeUs * 1000);
}
- void render(const void *data, size_t size) {
- mTarget->render(data, size, NULL);
+ void render(const void *data, size_t size, int64_t timestampNs) {
+ mTarget->render(data, size, timestampNs, NULL);
}
protected:
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 2a3fa04..e07b6aa 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -3540,7 +3540,7 @@
off64_t offset;
size_t size;
- uint32_t cts;
+ uint32_t cts, stts;
bool isSyncSample;
bool newBuffer = false;
if (mBuffer == NULL) {
@@ -3548,7 +3548,7 @@
status_t err =
mSampleTable->getMetaDataForSample(
- mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);
+ mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
if (err != OK) {
return err;
@@ -3579,6 +3579,8 @@
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -3701,6 +3703,8 @@
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -3850,6 +3854,8 @@
mBuffer->set_range(0, size);
mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -3973,6 +3979,8 @@
mBuffer->meta_data()->setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+ mBuffer->meta_data()->setInt64(
+ kKeyDuration, ((int64_t)smpl->duration * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 601dccf..5b525f2 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodec"
#include <utils/Log.h>
+#include <inttypes.h>
#include <media/stagefright/MediaCodec.h>
@@ -323,6 +324,16 @@
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::renderOutputBufferAndRelease(size_t index, int64_t timestampNs) {
+ sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+ msg->setSize("index", index);
+ msg->setInt32("render", true);
+ msg->setInt64("timestampNs", timestampNs);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::releaseOutputBuffer(size_t index) {
sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
msg->setSize("index", index);
@@ -1707,9 +1718,25 @@
if (render && info->mData != NULL && info->mData->size() != 0) {
info->mNotify->setInt32("render", true);
+ int64_t timestampNs = 0;
+ if (msg->findInt64("timestampNs", ×tampNs)) {
+ info->mNotify->setInt64("timestampNs", timestampNs);
+ } else {
+ // TODO: it seems like we should use the timestamp
+ // in the (media)buffer as it potentially came from
+ // an input surface, but we did not propagate it prior to
+ // API 20. Perhaps check for target SDK version.
+#if 0
+ if (info->mData->meta()->findInt64("timeUs", ×tampNs)) {
+ ALOGI("using buffer PTS of %" PRId64, timestampNs);
+ timestampNs *= 1000;
+ }
+#endif
+ }
+
if (mSoftRenderer != NULL) {
mSoftRenderer->render(
- info->mData->data(), info->mData->size(), NULL);
+ info->mData->data(), info->mData->size(), timestampNs, NULL);
}
}
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
index eae721b..2748349 100644
--- a/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/SampleIterator.cpp
@@ -133,7 +133,8 @@
}
status_t err;
- if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
+ if ((err = findSampleTimeAndDuration(
+ sampleIndex, &mCurrentSampleTime, &mCurrentSampleDuration)) != OK) {
ALOGE("findSampleTime return error");
return err;
}
@@ -285,8 +286,8 @@
return OK;
}
-status_t SampleIterator::findSampleTime(
- uint32_t sampleIndex, uint32_t *time) {
+status_t SampleIterator::findSampleTimeAndDuration(
+ uint32_t sampleIndex, uint32_t *time, uint32_t *duration) {
if (sampleIndex >= mTable->mNumSampleSizes) {
return ERROR_OUT_OF_RANGE;
}
@@ -309,6 +310,8 @@
*time += mTable->getCompositionTimeOffset(sampleIndex);
+ *duration = mTTSDuration;
+
return OK;
}
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index d9858d7..9a92805 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -778,7 +778,8 @@
off64_t *offset,
size_t *size,
uint32_t *compositionTime,
- bool *isSyncSample) {
+ bool *isSyncSample,
+ uint32_t *sampleDuration) {
Mutex::Autolock autoLock(mLock);
status_t err;
@@ -820,6 +821,10 @@
}
}
+ if (sampleDuration) {
+ *sampleDuration = mSampleIterator->getSampleDuration();
+ }
+
return OK;
}
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 77f21b7..67dfcd2 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -138,7 +138,7 @@
}
void SoftwareRenderer::render(
- const void *data, size_t size, void *platformPrivate) {
+ const void *data, size_t size, int64_t timestampNs, void *platformPrivate) {
ANativeWindowBuffer *buf;
int err;
if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(),
@@ -230,6 +230,11 @@
CHECK_EQ(0, mapper.unlock(buf->handle));
+ if ((err = native_window_set_buffers_timestamp(mNativeWindow.get(),
+ timestampNs)) != 0) {
+ ALOGW("Surface::set_buffers_timestamp returned error %d", err);
+ }
+
if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf,
-1)) != 0) {
ALOGW("Surface::queueBuffer returned error %d", err);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index c34f3cb..326d85b 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -69,6 +69,7 @@
mNumRetries(0),
mStartup(true),
mPrepared(false),
+ mSkipToFirstIDRAfterConnect(false),
mNextPTSTimeUs(-1ll),
mMonitorQueueGeneration(0),
mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
@@ -1097,12 +1098,30 @@
continue;
}
+ if (stream == LiveSession::STREAMTYPE_VIDEO && mVideoMime.empty()) {
+ const char *mime;
+ if (source->getFormat()->findCString(kKeyMIMEType, &mime)) {
+ mVideoMime.setTo(mime);
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ mSkipToFirstIDRAfterConnect = true;
+ }
+ }
+ }
+
int64_t timeUs;
sp<ABuffer> accessUnit;
status_t finalResult;
while (source->hasBufferAvailable(&finalResult)
&& source->dequeueAccessUnit(&accessUnit) == OK) {
+ if (stream == LiveSession::STREAMTYPE_VIDEO && mSkipToFirstIDRAfterConnect) {
+ if (!IsIDR(accessUnit)) {
+ continue;
+ } else {
+ mSkipToFirstIDRAfterConnect = false;
+ }
+ }
+
CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
if (mMinStartTimeUs > 0) {
if (timeUs < mMinStartTimeUs) {
@@ -1183,9 +1202,35 @@
return OK;
}
+/* static */
+bool PlaylistFetcher::bufferStartsWithWebVTTMagicSequence(
+ const sp<ABuffer> &buffer) {
+ size_t pos = 0;
+
+ // skip possible BOM
+ if (buffer->size() >= pos + 3 &&
+ !memcmp("\xef\xbb\xbf", buffer->data() + pos, 3)) {
+ pos += 3;
+ }
+
+ // accept WEBVTT followed by SPACE, TAB or (CR) LF
+ if (buffer->size() < pos + 6 ||
+ memcmp("WEBVTT", buffer->data() + pos, 6)) {
+ return false;
+ }
+ pos += 6;
+
+ if (buffer->size() == pos) {
+ return true;
+ }
+
+ uint8_t sep = buffer->data()[pos];
+ return sep == ' ' || sep == '\t' || sep == '\n' || sep == '\r';
+}
+
status_t PlaylistFetcher::extractAndQueueAccessUnits(
const sp<ABuffer> &buffer, const sp<AMessage> &itemMeta) {
- if (buffer->size() >= 7 && !memcmp("WEBVTT\n", buffer->data(), 7)) {
+ if (bufferStartsWithWebVTTMagicSequence(buffer)) {
if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES) {
ALOGE("This stream only contains subtitles.");
return ERROR_MALFORMED;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 7e21523..e4fdbff 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -91,6 +91,7 @@
static const int32_t kNumSkipFrames;
static bool bufferStartsWithTsSyncByte(const sp<ABuffer>& buffer);
+ static bool bufferStartsWithWebVTTMagicSequence(const sp<ABuffer>& buffer);
// notifications to mSession
sp<AMessage> mNotify;
@@ -98,6 +99,7 @@
sp<LiveSession> mSession;
AString mURI;
+ AString mVideoMime;
uint32_t mStreamTypeMask;
int64_t mStartTimeUs;
@@ -115,6 +117,7 @@
int32_t mNumRetries;
bool mStartup;
bool mPrepared;
+ bool mSkipToFirstIDRAfterConnect;
int64_t mNextPTSTimeUs;
int32_t mMonitorQueueGeneration;
diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
index b5a043c..60c9e7e 100644
--- a/media/libstagefright/include/SampleIterator.h
+++ b/media/libstagefright/include/SampleIterator.h
@@ -30,6 +30,7 @@
off64_t getSampleOffset() const { return mCurrentSampleOffset; }
size_t getSampleSize() const { return mCurrentSampleSize; }
uint32_t getSampleTime() const { return mCurrentSampleTime; }
+ uint32_t getSampleDuration() const { return mCurrentSampleDuration; }
status_t getSampleSizeDirect(
uint32_t sampleIndex, size_t *size);
@@ -61,11 +62,12 @@
off64_t mCurrentSampleOffset;
size_t mCurrentSampleSize;
uint32_t mCurrentSampleTime;
+ uint32_t mCurrentSampleDuration;
void reset();
status_t findChunkRange(uint32_t sampleIndex);
status_t getChunkOffset(uint32_t chunk, off64_t *offset);
- status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
+ status_t findSampleTimeAndDuration(uint32_t sampleIndex, uint32_t *time, uint32_t *duration);
SampleIterator(const SampleIterator &);
SampleIterator &operator=(const SampleIterator &);
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index 847dff7..fe146f2 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -66,7 +66,8 @@
off64_t *offset,
size_t *size,
uint32_t *compositionTime,
- bool *isSyncSample = NULL);
+ bool *isSyncSample = NULL,
+ uint32_t *sampleDuration = NULL);
enum {
kFlagBefore,
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 7ab0042..0ba670c 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -34,7 +34,7 @@
~SoftwareRenderer();
void render(
- const void *data, size_t size, void *platformPrivate);
+ const void *data, size_t size, int64_t timestampNs, void *platformPrivate);
private:
enum YUVMode {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index eb00c82..e256f32 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1195,12 +1195,17 @@
}
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2)
+void AudioFlinger::audioConfigChanged_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients,
+ int event,
+ audio_io_handle_t ioHandle,
+ const void *param2)
{
- size_t size = mNotificationClients.size();
+ size_t size = notificationClients.size();
for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
- param2);
+ notificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event,
+ ioHandle,
+ param2);
}
}
@@ -1313,6 +1318,8 @@
IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
+ sp<IMemory>& cblk,
+ sp<IMemory>& buffers,
status_t *status)
{
sp<RecordThread::RecordTrack> recordTrack;
@@ -1321,6 +1328,9 @@
status_t lStatus;
int lSessionId;
+ cblk.clear();
+ buffers.clear();
+
// check calling permissions
if (!recordingAllowed()) {
ALOGE("openRecord() permission denied: recording not allowed");
@@ -1396,6 +1406,9 @@
goto Exit;
}
+ cblk = recordTrack->getCblk();
+ buffers = recordTrack->getBuffers();
+
// return handle to client
recordHandle = new RecordHandle(recordTrack);
@@ -1625,7 +1638,7 @@
}
// notify client processes of the new output creation
- thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
+ thread->audioConfigChanged_l(mNotificationClients, AudioSystem::OUTPUT_OPENED);
// the first primary output opened designates the primary hw device
if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
@@ -1661,7 +1674,7 @@
thread->addOutputTrack(thread2);
mPlaybackThreads.add(id, thread);
// notify client processes of the new output creation
- thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
+ thread->audioConfigChanged_l(mNotificationClients, AudioSystem::OUTPUT_OPENED);
return id;
}
@@ -1711,7 +1724,7 @@
}
}
}
- audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
+ audioConfigChanged_l(mNotificationClients, AudioSystem::OUTPUT_CLOSED, output, NULL);
}
thread->exit();
// The thread entity (active unit of execution) is no longer running here,
@@ -1891,7 +1904,7 @@
}
// notify client processes of the new input creation
- thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
+ thread->audioConfigChanged_l(mNotificationClients, AudioSystem::INPUT_OPENED);
return id;
}
@@ -1916,7 +1929,7 @@
}
ALOGV("closeInput() %d", input);
- audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, NULL);
+ audioConfigChanged_l(mNotificationClients, AudioSystem::INPUT_CLOSED, input, NULL);
mRecordThreads.removeItem(input);
}
thread->exit();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ec32edd..894bd35 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -120,6 +120,8 @@
IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
+ sp<IMemory>& cblk,
+ sp<IMemory>& buffers,
status_t *status /*non-NULL*/);
virtual uint32_t sampleRate(audio_io_handle_t output) const;
@@ -430,7 +432,6 @@
public:
RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
virtual ~RecordHandle();
- virtual sp<IMemory> getCblk() const;
virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession);
virtual void stop();
virtual status_t onTransact(
@@ -452,7 +453,11 @@
// no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
float streamVolume_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].volume; }
- void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2);
+ void audioConfigChanged_l(const DefaultKeyedVector< pid_t,sp<NotificationClient> >&
+ notificationClients,
+ int event,
+ audio_io_handle_t ioHandle,
+ const void *param2);
// 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
@@ -477,7 +482,8 @@
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
-
+ DefaultKeyedVector< pid_t,sp<NotificationClient> > notificationClients() {
+ Mutex::Autolock _l(mLock); return mNotificationClients; }
bool isNonOffloadableGlobalEffectEnabled_l();
void onNonOffloadableGlobalEffectEnable();
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 6fc06d8..4ca2ad4 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -29,7 +29,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
- int uid);
+ int uid,
+ bool isFast);
virtual ~RecordTrack();
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index a916b32..470b018 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -97,8 +97,8 @@
// RecordThread loop sleep time upon application overrun or audio HAL read error
static const int kRecordThreadSleepUs = 5000;
-// maximum time to wait for setParameters to complete
-static const nsecs_t kSetParametersTimeoutNs = seconds(2);
+// maximum time to wait in sendConfigEvent_l() for a status to be received
+static const nsecs_t kConfigEventTimeoutNs = seconds(2);
// minimum sleep time for the mixer thread loop when tracks are active but in underrun
static const uint32_t kMinThreadSleepTimeUs = 5000;
@@ -143,6 +143,12 @@
// See the client's minBufCount and mNotificationFramesAct calculations for details.
static const int kFastTrackMultiplier = 2;
+// See Thread::readOnlyHeap().
+// Initially this heap is used to allocate client buffers for "fast" AudioRecord.
+// Eventually it will be the single buffer that FastCapture writes into via HAL read(),
+// and that all "fast" AudioRecord clients read from. In either case, the size can be small.
+static const size_t kRecordThreadReadOnlyHeapSize = 0x1000;
+
// ----------------------------------------------------------------------------
#ifdef ADD_BATTERY_DATA
@@ -277,7 +283,6 @@
// mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize
// are set by PlaybackThread::readOutputParameters_l() or
// RecordThread::readInputParameters_l()
- mParamStatus(NO_ERROR),
//FIXME: mStandby should be true here. Is this some kind of hack?
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
@@ -289,12 +294,8 @@
AudioFlinger::ThreadBase::~ThreadBase()
{
// mConfigEvents should be empty, but just in case it isn't, free the memory it owns
- for (size_t i = 0; i < mConfigEvents.size(); i++) {
- delete mConfigEvents[i];
- }
mConfigEvents.clear();
- mParamCond.broadcast();
// do not lock the mutex in destructor
releaseWakeLock_l();
if (mPowerManager != 0) {
@@ -345,16 +346,30 @@
ALOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
Mutex::Autolock _l(mLock);
- mNewParameters.add(keyValuePairs);
+ return sendSetParameterConfigEvent_l(keyValuePairs);
+}
+
+// sendConfigEvent_l() must be called with ThreadBase::mLock held
+// Can temporarily release the lock if waiting for a reply from processConfigEvents_l().
+status_t AudioFlinger::ThreadBase::sendConfigEvent_l(sp<ConfigEvent>& event)
+{
+ status_t status = NO_ERROR;
+
+ mConfigEvents.add(event);
+ ALOGV("sendConfigEvent_l() num events %d event %d", mConfigEvents.size(), event->mType);
mWaitWorkCV.signal();
- // wait condition with timeout in case the thread loop has exited
- // before the request could be processed
- if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) {
- status = mParamStatus;
- mWaitWorkCV.signal();
- } else {
- status = TIMED_OUT;
+ mLock.unlock();
+ {
+ Mutex::Autolock _l(event->mLock);
+ while (event->mWaitStatus) {
+ if (event->mCond.waitRelative(event->mLock, kConfigEventTimeoutNs) != NO_ERROR) {
+ event->mStatus = TIMED_OUT;
+ event->mWaitStatus = false;
+ }
+ }
+ status = event->mStatus;
}
+ mLock.lock();
return status;
}
@@ -367,63 +382,71 @@
// sendIoConfigEvent_l() must be called with ThreadBase::mLock held
void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param)
{
- IoConfigEvent *ioEvent = new IoConfigEvent(event, param);
- mConfigEvents.add(static_cast<ConfigEvent *>(ioEvent));
- ALOGV("sendIoConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event,
- param);
- mWaitWorkCV.signal();
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, param);
+ sendConfigEvent_l(configEvent);
}
// sendPrioConfigEvent_l() must be called with ThreadBase::mLock held
void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio)
{
- PrioConfigEvent *prioEvent = new PrioConfigEvent(pid, tid, prio);
- mConfigEvents.add(static_cast<ConfigEvent *>(prioEvent));
- ALOGV("sendPrioConfigEvent_l() num events %d pid %d, tid %d prio %d",
- mConfigEvents.size(), pid, tid, prio);
- mWaitWorkCV.signal();
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new PrioConfigEvent(pid, tid, prio);
+ sendConfigEvent_l(configEvent);
}
-void AudioFlinger::ThreadBase::processConfigEvents()
+// sendSetParameterConfigEvent_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::ThreadBase::sendSetParameterConfigEvent_l(const String8& keyValuePair)
{
- Mutex::Autolock _l(mLock);
- processConfigEvents_l();
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new SetParameterConfigEvent(keyValuePair);
+ return sendConfigEvent_l(configEvent);
}
// post condition: mConfigEvents.isEmpty()
-void AudioFlinger::ThreadBase::processConfigEvents_l()
+void AudioFlinger::ThreadBase::processConfigEvents_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients)
{
+ bool configChanged = false;
+
while (!mConfigEvents.isEmpty()) {
- ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
- ConfigEvent *event = mConfigEvents[0];
+ ALOGV("processConfigEvents_l() remaining events %d", mConfigEvents.size());
+ sp<ConfigEvent> event = mConfigEvents[0];
mConfigEvents.removeAt(0);
- // release mLock before locking AudioFlinger mLock: lock order is always
- // AudioFlinger then ThreadBase to avoid cross deadlock
- mLock.unlock();
- switch (event->type()) {
+ switch (event->mType) {
case CFG_EVENT_PRIO: {
- PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event);
- // FIXME Need to understand why this has be done asynchronously
- int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(),
+ PrioConfigEventData *data = (PrioConfigEventData *)event->mData.get();
+ // FIXME Need to understand why this has to be done asynchronously
+ int err = requestPriority(data->mPid, data->mTid, data->mPrio,
true /*asynchronous*/);
if (err != 0) {
ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err);
+ data->mPrio, data->mPid, data->mTid, err);
}
} break;
case CFG_EVENT_IO: {
- IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event);
- {
- Mutex::Autolock _l(mAudioFlinger->mLock);
- audioConfigChanged_l(ioEvent->event(), ioEvent->param());
+ IoConfigEventData *data = (IoConfigEventData *)event->mData.get();
+ audioConfigChanged_l(notificationClients, data->mEvent, data->mParam);
+ } break;
+ case CFG_EVENT_SET_PARAMETER: {
+ SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get();
+ if (checkForNewParameter_l(data->mKeyValuePairs, event->mStatus)) {
+ configChanged = true;
}
} break;
default:
- ALOGE("processConfigEvents() unknown event type %d", event->type());
+ ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType);
break;
}
- delete event;
- mLock.lock();
+ {
+ Mutex::Autolock _l(event->mLock);
+ if (event->mWaitStatus) {
+ event->mWaitStatus = false;
+ event->mCond.signal();
+ }
+ }
+ ALOGV_IF(mConfigEvents.isEmpty(), "processConfigEvents_l() DONE thread %p", this);
+ }
+
+ if (configChanged) {
+ cacheParameters_l();
}
}
@@ -496,18 +519,6 @@
channelMaskToString(mChannelMask, mType != RECORD).string());
fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
fdprintf(fd, " Frame size: %zu\n", mFrameSize);
- fdprintf(fd, " Pending setParameters commands:");
- size_t numParams = mNewParameters.size();
- if (numParams) {
- fdprintf(fd, "\n Index Command");
- for (size_t i = 0; i < numParams; ++i) {
- fdprintf(fd, "\n %02zu ", i);
- fdprintf(fd, mNewParameters[i]);
- }
- fdprintf(fd, "\n");
- } else {
- fdprintf(fd, " none\n");
- }
fdprintf(fd, " Pending config events:");
size_t numConfig = mConfigEvents.size();
if (numConfig) {
@@ -1628,7 +1639,10 @@
}
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
+void AudioFlinger::PlaybackThread::audioConfigChanged_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients,
+ int event,
+ int param) {
AudioSystem::OutputDescriptor desc;
void *param2 = NULL;
@@ -1643,7 +1657,7 @@
desc.format = mFormat;
desc.frameCount = mNormalFrameCount; // FIXME see
// AudioFlinger::frameCount(audio_io_handle_t)
- desc.latency = latency();
+ desc.latency = latency_l();
param2 = &desc;
break;
@@ -1653,7 +1667,7 @@
default:
break;
}
- mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+ mAudioFlinger->audioConfigChanged_l(notificationClients, event, mId, param2);
}
void AudioFlinger::PlaybackThread::writeCallback()
@@ -2303,12 +2317,16 @@
Vector< sp<EffectChain> > effectChains;
- processConfigEvents();
+ DefaultKeyedVector< pid_t,sp<NotificationClient> > notificationClients =
+ mAudioFlinger->notificationClients();
{ // scope for mLock
Mutex::Autolock _l(mLock);
+ processConfigEvents_l(notificationClients);
+ notificationClients.clear();
+
if (logString != NULL) {
mNBLogWriter->logTimestamp();
mNBLogWriter->log(logString);
@@ -2321,10 +2339,6 @@
mLatchQValid = true;
}
- if (checkForNewParameters_l()) {
- cacheParameters_l();
- }
-
saveOutputTracks();
if (mSignalPending) {
// A signal was raised while we were unlocked
@@ -2746,7 +2760,7 @@
if (state->mCommand == FastMixerState::COLD_IDLE) {
int32_t old = android_atomic_inc(&mFastMixerFutex);
if (old == -1) {
- __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+ (void) __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
}
state->mCommand = FastMixerState::EXIT;
@@ -2803,7 +2817,7 @@
if (state->mCommand == FastMixerState::COLD_IDLE) {
int32_t old = android_atomic_inc(&mFastMixerFutex);
if (old == -1) {
- __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+ (void) __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
@@ -3529,128 +3543,117 @@
mAudioMixer->deleteTrackName(name);
}
-// checkForNewParameters_l() must be called with ThreadBase::mLock held
-bool AudioFlinger::MixerThread::checkForNewParameters_l()
+// checkForNewParameter_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status)
{
- // if !&IDLE, holds the FastMixer state to restore after new parameters processed
- FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
bool reconfig = false;
- while (!mNewParameters.isEmpty()) {
+ status = NO_ERROR;
- if (mFastMixer != NULL) {
- FastMixerStateQueue *sq = mFastMixer->sq();
- FastMixerState *state = sq->begin();
- if (!(state->mCommand & FastMixerState::IDLE)) {
- previousCommand = state->mCommand;
- state->mCommand = FastMixerState::HOT_IDLE;
- sq->end();
- sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
- } else {
- sq->end(false /*didModify*/);
- }
+ // if !&IDLE, holds the FastMixer state to restore after new parameters processed
+ FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
+ if (mFastMixer != NULL) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (!(state->mCommand & FastMixerState::IDLE)) {
+ previousCommand = state->mCommand;
+ state->mCommand = FastMixerState::HOT_IDLE;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+ } else {
+ sq->end(false /*didModify*/);
}
+ }
- status_t status = NO_ERROR;
- String8 keyValuePair = mNewParameters[0];
- AudioParameter param = AudioParameter(keyValuePair);
- int value;
-
- if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ // no need to save value, since it's constant
reconfig = true;
}
- if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
- status = BAD_VALUE;
- } else {
- // no need to save value, since it's constant
- reconfig = true;
- }
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) {
+ status = BAD_VALUE;
+ } else {
+ // no need to save value, since it's constant
+ reconfig = true;
}
- if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) {
- status = BAD_VALUE;
- } else {
- // no need to save value, since it's constant
- reconfig = true;
- }
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be guaranteed
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
}
- if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
- // do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be guaranteed
- // if frame count is changed after track creation
- if (!mTracks.isEmpty()) {
- status = INVALID_OPERATION;
- } else {
- reconfig = true;
- }
- }
- if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ }
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
#ifdef ADD_BATTERY_DATA
- // when changing the audio output device, call addBatteryData to notify
- // the change
- if (mOutDevice != value) {
- uint32_t params = 0;
- // check whether speaker is on
- if (value & AUDIO_DEVICE_OUT_SPEAKER) {
- params |= IMediaPlayerService::kBatteryDataSpeakerOn;
- }
-
- audio_devices_t deviceWithoutSpeaker
- = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
- // check if any other device (except speaker) is on
- if (value & deviceWithoutSpeaker ) {
- params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
- }
-
- if (params != 0) {
- addBatteryData(params);
- }
+ // when changing the audio output device, call addBatteryData to notify
+ // the change
+ if (mOutDevice != value) {
+ uint32_t params = 0;
+ // check whether speaker is on
+ if (value & AUDIO_DEVICE_OUT_SPEAKER) {
+ params |= IMediaPlayerService::kBatteryDataSpeakerOn;
}
+
+ audio_devices_t deviceWithoutSpeaker
+ = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
+ // check if any other device (except speaker) is on
+ if (value & deviceWithoutSpeaker ) {
+ params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
+ }
+
+ if (params != 0) {
+ addBatteryData(params);
+ }
+ }
#endif
- // forward device change to effects that have requested to be
- // aware of attached audio device.
- if (value != AUDIO_DEVICE_NONE) {
- mOutDevice = value;
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mOutDevice);
- }
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ if (value != AUDIO_DEVICE_NONE) {
+ mOutDevice = value;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mOutDevice);
}
}
+ }
- if (status == NO_ERROR) {
+ if (status == NO_ERROR) {
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ mStandby = true;
+ mBytesWritten = 0;
status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- if (!mStandby && status == INVALID_OPERATION) {
- mOutput->stream->common.standby(&mOutput->stream->common);
- mStandby = true;
- mBytesWritten = 0;
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- }
- if (status == NO_ERROR && reconfig) {
- readOutputParameters_l();
- delete mAudioMixer;
- mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
- for (size_t i = 0; i < mTracks.size() ; i++) {
- int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
- if (name < 0) {
- break;
- }
- mTracks[i]->mName = name;
- }
- sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
- }
+ keyValuePair.string());
}
-
- mNewParameters.removeAt(0);
-
- mParamStatus = status;
- mParamCond.signal();
- // wait for condition with time out in case the thread calling ThreadBase::setParameters()
- // already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
+ if (status == NO_ERROR && reconfig) {
+ readOutputParameters_l();
+ delete mAudioMixer;
+ mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
+ for (size_t i = 0; i < mTracks.size() ; i++) {
+ int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
+ if (name < 0) {
+ break;
+ }
+ mTracks[i]->mName = name;
+ }
+ sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
}
if (!(previousCommand & FastMixerState::IDLE)) {
@@ -3940,61 +3943,52 @@
{
}
-// checkForNewParameters_l() must be called with ThreadBase::mLock held
-bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
+// checkForNewParameter_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status)
{
bool reconfig = false;
- while (!mNewParameters.isEmpty()) {
- status_t status = NO_ERROR;
- String8 keyValuePair = mNewParameters[0];
- AudioParameter param = AudioParameter(keyValuePair);
- int value;
+ status = NO_ERROR;
- if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
- // forward device change to effects that have requested to be
- // aware of attached audio device.
- if (value != AUDIO_DEVICE_NONE) {
- mOutDevice = value;
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mOutDevice);
- }
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ if (value != AUDIO_DEVICE_NONE) {
+ mOutDevice = value;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mOutDevice);
}
}
- if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
- // do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be garantied
- // if frame count is changed after track creation
- if (!mTracks.isEmpty()) {
- status = INVALID_OPERATION;
- } else {
- reconfig = true;
- }
- }
- if (status == NO_ERROR) {
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- if (!mStandby && status == INVALID_OPERATION) {
- mOutput->stream->common.standby(&mOutput->stream->common);
- mStandby = true;
- mBytesWritten = 0;
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- }
- if (status == NO_ERROR && reconfig) {
- readOutputParameters_l();
- sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
- }
- }
-
- mNewParameters.removeAt(0);
-
- mParamStatus = status;
- mParamCond.signal();
- // wait for condition with time out in case the thread calling ThreadBase::setParameters()
- // already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
}
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ }
+ if (status == NO_ERROR && reconfig) {
+ readOutputParameters_l();
+ sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+
return reconfig;
}
@@ -4635,6 +4629,8 @@
#ifdef TEE_SINK
, mTeeSink(teeSink)
#endif
+ , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize,
+ "RecordThreadRO", MemoryHeapBase::READ_ONLY))
{
snprintf(mName, kNameLength, "AudioIn_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
@@ -4699,12 +4695,14 @@
// activeTracks accumulates a copy of a subset of mActiveTracks
Vector< sp<RecordTrack> > activeTracks;
+ DefaultKeyedVector< pid_t,sp<NotificationClient> > notificationClients =
+ mAudioFlinger->notificationClients();
+
{ // scope for mLock
Mutex::Autolock _l(mLock);
- processConfigEvents_l();
- // return value 'reconfig' is currently unused
- bool reconfig = checkForNewParameters_l();
+ processConfigEvents_l(notificationClients);
+ notificationClients.clear();
// check exitPending here because checkForNewParameters_l() and
// checkForNewParameters_l() can temporarily release mLock
@@ -5155,7 +5153,8 @@
Mutex::Autolock _l(mLock);
track = new RecordTrack(this, client, sampleRate,
- format, channelMask, frameCount, sessionId, uid);
+ format, channelMask, frameCount, sessionId, uid,
+ (*flags & IAudioFlinger::TRACK_FAST) != 0);
lStatus = track->initCheck();
if (lStatus != NO_ERROR) {
@@ -5478,126 +5477,118 @@
buffer->frameCount = 0;
}
-bool AudioFlinger::RecordThread::checkForNewParameters_l()
+bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status)
{
bool reconfig = false;
- while (!mNewParameters.isEmpty()) {
- status_t status = NO_ERROR;
- String8 keyValuePair = mNewParameters[0];
- AudioParameter param = AudioParameter(keyValuePair);
- int value;
- audio_format_t reqFormat = mFormat;
- uint32_t samplingRate = mSampleRate;
- audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+ status = NO_ERROR;
- // TODO Investigate when this code runs. Check with audio policy when a sample rate and
- // channel count change can be requested. Do we mandate the first client defines the
- // HAL sampling rate and channel count or do we allow changes on the fly?
- if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
- samplingRate = value;
+ audio_format_t reqFormat = mFormat;
+ uint32_t samplingRate = mSampleRate;
+ audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+ // TODO Investigate when this code runs. Check with audio policy when a sample rate and
+ // channel count change can be requested. Do we mandate the first client defines the
+ // HAL sampling rate and channel count or do we allow changes on the fly?
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ samplingRate = value;
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reqFormat = (audio_format_t) value;
reconfig = true;
}
- if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
- status = BAD_VALUE;
- } else {
- reqFormat = (audio_format_t) value;
- reconfig = true;
- }
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ audio_channel_mask_t mask = (audio_channel_mask_t) value;
+ if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
+ status = BAD_VALUE;
+ } else {
+ channelMask = mask;
+ reconfig = true;
}
- if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- audio_channel_mask_t mask = (audio_channel_mask_t) value;
- if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
- status = BAD_VALUE;
- } else {
- channelMask = mask;
- reconfig = true;
- }
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be guaranteed
+ // if frame count is changed after track creation
+ if (mActiveTracks.size() > 0) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
}
- if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
- // do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be guaranteed
- // if frame count is changed after track creation
- if (mActiveTracks.size() > 0) {
- status = INVALID_OPERATION;
- } else {
- reconfig = true;
- }
+ }
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(value);
}
- if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
- // forward device change to effects that have requested to be
- // aware of attached audio device.
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(value);
- }
- // store input device and output device but do not forward output device to audio HAL.
- // Note that status is ignored by the caller for output device
- // (see AudioFlinger::setParameters()
- if (audio_is_output_devices(value)) {
- mOutDevice = value;
- status = BAD_VALUE;
- } else {
- mInDevice = value;
- // disable AEC and NS if the device is a BT SCO headset supporting those
- // pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
+ // store input device and output device but do not forward output device to audio HAL.
+ // Note that status is ignored by the caller for output device
+ // (see AudioFlinger::setParameters()
+ if (audio_is_output_devices(value)) {
+ mOutDevice = value;
+ status = BAD_VALUE;
+ } else {
+ mInDevice = value;
+ // disable AEC and NS if the device is a BT SCO headset supporting those
+ // pre processings
+ if (mTracks.size() > 0) {
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
}
}
}
- if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
- mAudioSource != (audio_source_t)value) {
- // forward device change to effects that have requested to be
- // aware of attached audio device.
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setAudioSource_l((audio_source_t)value);
- }
- mAudioSource = (audio_source_t)value;
+ }
+ if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
+ mAudioSource != (audio_source_t)value) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setAudioSource_l((audio_source_t)value);
}
+ mAudioSource = (audio_source_t)value;
+ }
- if (status == NO_ERROR) {
+ if (status == NO_ERROR) {
+ status = mInput->stream->common.set_parameters(&mInput->stream->common,
+ keyValuePair.string());
+ if (status == INVALID_OPERATION) {
+ inputStandBy();
status = mInput->stream->common.set_parameters(&mInput->stream->common,
keyValuePair.string());
- if (status == INVALID_OPERATION) {
- inputStandBy();
- status = mInput->stream->common.set_parameters(&mInput->stream->common,
- keyValuePair.string());
+ }
+ if (reconfig) {
+ if (status == BAD_VALUE &&
+ reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
+ reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+ (mInput->stream->common.get_sample_rate(&mInput->stream->common)
+ <= (2 * samplingRate)) &&
+ popcount(mInput->stream->common.get_channels(&mInput->stream->common))
+ <= FCC_2 &&
+ (channelMask == AUDIO_CHANNEL_IN_MONO ||
+ channelMask == AUDIO_CHANNEL_IN_STEREO)) {
+ status = NO_ERROR;
}
- if (reconfig) {
- if (status == BAD_VALUE &&
- reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
- reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
- (mInput->stream->common.get_sample_rate(&mInput->stream->common)
- <= (2 * samplingRate)) &&
- popcount(mInput->stream->common.get_channels(&mInput->stream->common))
- <= FCC_2 &&
- (channelMask == AUDIO_CHANNEL_IN_MONO ||
- channelMask == AUDIO_CHANNEL_IN_STEREO)) {
- status = NO_ERROR;
- }
- if (status == NO_ERROR) {
- readInputParameters_l();
- sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
- }
+ if (status == NO_ERROR) {
+ readInputParameters_l();
+ sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
}
}
-
- mNewParameters.removeAt(0);
-
- mParamStatus = status;
- mParamCond.signal();
- // wait for condition with time out in case the thread calling ThreadBase::setParameters()
- // already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
}
+
return reconfig;
}
@@ -5614,7 +5605,10 @@
return out_s8;
}
-void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param __unused) {
+void AudioFlinger::RecordThread::audioConfigChanged_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients,
+ int event,
+ int param __unused) {
AudioSystem::OutputDescriptor desc;
const void *param2 = NULL;
@@ -5633,7 +5627,7 @@
default:
break;
}
- mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+ mAudioFlinger->audioConfigChanged_l(notificationClients, event, mId, param2);
}
void AudioFlinger::RecordThread::readInputParameters_l()
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 5617c0c..9578993 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -46,60 +46,121 @@
// base for record and playback
enum {
CFG_EVENT_IO,
- CFG_EVENT_PRIO
+ CFG_EVENT_PRIO,
+ CFG_EVENT_SET_PARAMETER,
};
- class ConfigEvent {
+ class ConfigEventData: public RefBase {
public:
- ConfigEvent(int type) : mType(type) {}
- virtual ~ConfigEvent() {}
-
- int type() const { return mType; }
+ virtual ~ConfigEventData() {}
virtual void dump(char *buffer, size_t size) = 0;
-
- private:
- const int mType;
+ protected:
+ ConfigEventData() {}
};
- class IoConfigEvent : public ConfigEvent {
- public:
- IoConfigEvent(int event, int param) :
- ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(param) {}
- virtual ~IoConfigEvent() {}
+ // Config event sequence by client if status needed (e.g binder thread calling setParameters()):
+ // 1. create SetParameterConfigEvent. This sets mWaitStatus in config event
+ // 2. Lock mLock
+ // 3. Call sendConfigEvent_l(): Append to mConfigEvents and mWaitWorkCV.signal
+ // 4. sendConfigEvent_l() reads status from event->mStatus;
+ // 5. sendConfigEvent_l() returns status
+ // 6. Unlock
+ //
+ // Parameter sequence by server: threadLoop calling processConfigEvents_l():
+ // 1. Lock mLock
+ // 2. If there is an entry in mConfigEvents proceed ...
+ // 3. Read first entry in mConfigEvents
+ // 4. Remove first entry from mConfigEvents
+ // 5. Process
+ // 6. Set event->mStatus
+ // 7. event->mCond.signal
+ // 8. Unlock
- int event() const { return mEvent; }
- int param() const { return mParam; }
+ class ConfigEvent: public RefBase {
+ public:
+ virtual ~ConfigEvent() {}
+
+ void dump(char *buffer, size_t size) { mData->dump(buffer, size); }
+
+ const int mType; // event type e.g. CFG_EVENT_IO
+ Mutex mLock; // mutex associated with mCond
+ Condition mCond; // condition for status return
+ status_t mStatus; // status communicated to sender
+ bool mWaitStatus; // true if sender is waiting for status
+ sp<ConfigEventData> mData; // event specific parameter data
+
+ protected:
+ ConfigEvent(int type) : mType(type), mStatus(NO_ERROR), mWaitStatus(false), mData(NULL) {}
+ };
+
+ class IoConfigEventData : public ConfigEventData {
+ public:
+ IoConfigEventData(int event, int param) :
+ mEvent(event), mParam(param) {}
virtual void dump(char *buffer, size_t size) {
snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam);
}
- private:
const int mEvent;
const int mParam;
};
- class PrioConfigEvent : public ConfigEvent {
+ class IoConfigEvent : public ConfigEvent {
public:
- PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) :
- ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {}
- virtual ~PrioConfigEvent() {}
+ IoConfigEvent(int event, int param) :
+ ConfigEvent(CFG_EVENT_IO) {
+ mData = new IoConfigEventData(event, param);
+ }
+ virtual ~IoConfigEvent() {}
+ };
- pid_t pid() const { return mPid; }
- pid_t tid() const { return mTid; }
- int32_t prio() const { return mPrio; }
+ class PrioConfigEventData : public ConfigEventData {
+ public:
+ PrioConfigEventData(pid_t pid, pid_t tid, int32_t prio) :
+ mPid(pid), mTid(tid), mPrio(prio) {}
virtual void dump(char *buffer, size_t size) {
snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio);
}
- private:
const pid_t mPid;
const pid_t mTid;
const int32_t mPrio;
};
+ class PrioConfigEvent : public ConfigEvent {
+ public:
+ PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) :
+ ConfigEvent(CFG_EVENT_PRIO) {
+ mData = new PrioConfigEventData(pid, tid, prio);
+ }
+ virtual ~PrioConfigEvent() {}
+ };
+
+ class SetParameterConfigEventData : public ConfigEventData {
+ public:
+ SetParameterConfigEventData(String8 keyValuePairs) :
+ mKeyValuePairs(keyValuePairs) {}
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "KeyValue: %s\n", mKeyValuePairs.string());
+ }
+
+ const String8 mKeyValuePairs;
+ };
+
+ class SetParameterConfigEvent : public ConfigEvent {
+ public:
+ SetParameterConfigEvent(String8 keyValuePairs) :
+ ConfigEvent(CFG_EVENT_SET_PARAMETER) {
+ mData = new SetParameterConfigEventData(keyValuePairs);
+ mWaitStatus = true;
+ }
+ virtual ~SetParameterConfigEvent() {}
+ };
+
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
@@ -135,15 +196,25 @@
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
void exit();
- virtual bool checkForNewParameters_l() = 0;
+ virtual bool checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status) = 0;
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys) = 0;
- virtual void audioConfigChanged_l(int event, int param = 0) = 0;
+ virtual void audioConfigChanged_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients,
+ int event,
+ int param = 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(int event, int param = 0);
void sendIoConfigEvent_l(int event, int param = 0);
void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
- void processConfigEvents();
- void processConfigEvents_l();
+ status_t sendSetParameterConfigEvent_l(const String8& keyValuePair);
+ void processConfigEvents_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients);
+ virtual void cacheParameters_l() = 0;
// see note at declaration of mStandby, mOutDevice and mInDevice
bool standby() const { return mStandby; }
@@ -226,6 +297,13 @@
virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
+ // Return a reference to a per-thread heap which can be used to allocate IMemory
+ // objects that will be read-only to client processes, read/write to mediaserver,
+ // and shared by all client processes of the thread.
+ // The heap is per-thread rather than common across all threads, because
+ // clients can't be trusted not to modify the offset of the IMemory they receive.
+ // If a thread does not have such a heap, this method returns 0.
+ virtual sp<MemoryDealer> readOnlyHeap() const { return 0; }
mutable Mutex mLock;
@@ -280,31 +358,7 @@
audio_format_t mFormat;
size_t mBufferSize; // HAL buffer size for read() or write()
- // Parameter sequence by client: binder thread calling setParameters():
- // 1. Lock mLock
- // 2. Append to mNewParameters
- // 3. mWaitWorkCV.signal
- // 4. mParamCond.waitRelative with timeout
- // 5. read mParamStatus
- // 6. mWaitWorkCV.signal
- // 7. Unlock
- //
- // Parameter sequence by server: threadLoop calling checkForNewParameters_l():
- // 1. Lock mLock
- // 2. If there is an entry in mNewParameters proceed ...
- // 2. Read first entry in mNewParameters
- // 3. Process
- // 4. Remove first entry from mNewParameters
- // 5. Set mParamStatus
- // 6. mParamCond.signal
- // 7. mWaitWorkCV.wait with timeout (this is to avoid overwriting mParamStatus)
- // 8. Unlock
- Condition mParamCond;
- Vector<String8> mNewParameters;
- status_t mParamStatus;
-
- // vector owns each ConfigEvent *, so must delete after removing
- Vector<ConfigEvent *> mConfigEvents;
+ Vector< sp<ConfigEvent> > mConfigEvents;
// These fields are written and read by thread itself without lock or barrier,
// and read by other threads without lock or barrier via standby(), outDevice()
@@ -448,7 +502,10 @@
{ return android_atomic_acquire_load(&mSuspended) > 0; }
virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged_l(int event, int param = 0);
+ virtual void audioConfigChanged_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients,
+ int event,
+ int param = 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
@@ -717,7 +774,8 @@
// Thread virtuals
- virtual bool checkForNewParameters_l();
+ virtual bool checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status);
virtual void dumpInternals(int fd, const Vector<String16>& args);
protected:
@@ -771,7 +829,8 @@
// Thread virtuals
- virtual bool checkForNewParameters_l();
+ virtual bool checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status);
protected:
virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
@@ -947,6 +1006,8 @@
virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; }
+ virtual sp<MemoryDealer> readOnlyHeap() const { return mReadOnlyHeap; }
+
sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
@@ -972,9 +1033,14 @@
virtual audio_stream_t* stream() const;
- virtual bool checkForNewParameters_l();
+ virtual bool checkForNewParameter_l(const String8& keyValuePair,
+ status_t& status);
+ virtual void cacheParameters_l() {}
virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged_l(int event, int param = 0);
+ virtual void audioConfigChanged_l(
+ const DefaultKeyedVector< pid_t,sp<NotificationClient> >& notificationClients,
+ int event,
+ int param = 0);
void readInputParameters_l();
virtual uint32_t getInputFramesLost();
@@ -1021,4 +1087,6 @@
// For dumpsys
const sp<NBAIO_Sink> mTeeSink;
+
+ const sp<MemoryDealer> mReadOnlyHeap;
};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 58705c4..06023fd 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -48,7 +48,8 @@
const sp<IMemory>& sharedBuffer,
int sessionId,
int uid,
- bool isOut);
+ bool isOut,
+ bool useReadOnlyHeap = false);
virtual ~TrackBase();
virtual status_t initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; }
@@ -61,6 +62,8 @@
int uid() const { return mUid; }
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
+ sp<IMemory> getBuffers() const { return mBufferMemory; }
+
protected:
TrackBase(const TrackBase&);
TrackBase& operator = (const TrackBase&);
@@ -112,6 +115,7 @@
/*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
+ sp<IMemory> mBufferMemory; // currently non-0 for fast RecordTrack only
void* mBuffer; // start of track buffer, typically in shared memory
// except for OutputTrack when it is in local memory
// we don't really need a lock for these
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 1064fd1..d8f3423 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -69,7 +69,8 @@
const sp<IMemory>& sharedBuffer,
int sessionId,
int clientUid,
- bool isOut)
+ bool isOut,
+ bool useReadOnlyHeap)
: RefBase(),
mThread(thread),
mClient(client),
@@ -110,7 +111,7 @@
// ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
size_t size = sizeof(audio_track_cblk_t);
size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
- if (sharedBuffer == 0) {
+ if (sharedBuffer == 0 && !useReadOnlyHeap) {
size += bufferSize;
}
@@ -132,15 +133,31 @@
// construct the shared structure in-place.
if (mCblk != NULL) {
new(mCblk) audio_track_cblk_t();
- // clear all buffers
- if (sharedBuffer == 0) {
- mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+ if (useReadOnlyHeap) {
+ const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
+ if (roHeap == 0 ||
+ (mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
+ (mBuffer = mBufferMemory->pointer()) == NULL) {
+ ALOGE("not enough memory for read-only buffer size=%zu", bufferSize);
+ if (roHeap != 0) {
+ roHeap->dump("buffer");
+ }
+ mCblkMemory.clear();
+ mBufferMemory.clear();
+ return;
+ }
memset(mBuffer, 0, bufferSize);
} else {
- mBuffer = sharedBuffer->pointer();
+ // clear all buffers
+ if (sharedBuffer == 0) {
+ mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+ memset(mBuffer, 0, bufferSize);
+ } else {
+ mBuffer = sharedBuffer->pointer();
#if 0
- mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
+ mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
#endif
+ }
}
#ifdef TEE_SINK
@@ -1783,10 +1800,6 @@
mRecordTrack->destroy();
}
-sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
- return mRecordTrack->getCblk();
-}
-
status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event,
int triggerSession) {
ALOGV("RecordHandle::start()");
@@ -1819,9 +1832,11 @@
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
- int uid)
+ int uid,
+ bool isFast)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/,
+ isFast /*useReadOnlyHeap*/),
mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
// See real initialization of mRsmpInFront at RecordThread::start()
mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index fc9b81a..62a44ee 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -2920,9 +2920,6 @@
uint32_t muteWaitMs = 0;
audio_devices_t device = outputDesc->device();
bool shouldMute = outputDesc->isActive() && (popcount(device) >= 2);
- // temporary mute output if device selection changes to avoid volume bursts due to
- // different per device volumes
- bool tempMute = outputDesc->isActive() && (device != prevDevice);
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
@@ -2936,7 +2933,7 @@
doMute = true;
outputDesc->mStrategyMutedByDevice[i] = false;
}
- if (doMute || tempMute) {
+ if (doMute) {
for (size_t j = 0; j < mOutputs.size(); j++) {
AudioOutputDescriptor *desc = mOutputs.valueAt(j);
// skip output if it does not share any device with current output
@@ -2949,15 +2946,14 @@
mute ? "muting" : "unmuting", i, curDevice, curOutput);
setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs);
if (desc->isStrategyActive((routing_strategy)i)) {
- // do tempMute only for current output
- if (tempMute && (desc == outputDesc)) {
- setStrategyMute((routing_strategy)i, true, curOutput);
- setStrategyMute((routing_strategy)i, false, curOutput,
- desc->latency() * 2, device);
- }
- if ((tempMute && (desc == outputDesc)) || mute) {
- if (muteWaitMs < desc->latency()) {
- muteWaitMs = desc->latency();
+ if (mute) {
+ // FIXME: should not need to double latency if volume could be applied
+ // immediately by the audioflinger mixer. We must account for the delay
+ // between now and the next time the audioflinger thread for this output
+ // will process a buffer (which corresponds to one buffer size,
+ // usually 1/2 or 1/4 of the latency).
+ if (muteWaitMs < desc->latency() * 2) {
+ muteWaitMs = desc->latency() * 2;
}
}
}
@@ -2965,11 +2961,22 @@
}
}
- // FIXME: should not need to double latency if volume could be applied immediately by the
- // audioflinger mixer. We must account for the delay between now and the next time
- // the audioflinger thread for this output will process a buffer (which corresponds to
- // one buffer size, usually 1/2 or 1/4 of the latency).
- muteWaitMs *= 2;
+ // temporary mute output if device selection changes to avoid volume bursts due to
+ // different per device volumes
+ if (outputDesc->isActive() && (device != prevDevice)) {
+ if (muteWaitMs < outputDesc->latency() * 2) {
+ muteWaitMs = outputDesc->latency() * 2;
+ }
+ for (size_t i = 0; i < NUM_STRATEGIES; i++) {
+ if (outputDesc->isStrategyActive((routing_strategy)i)) {
+ setStrategyMute((routing_strategy)i, true, outputDesc->mId);
+ // do tempMute unmute after twice the mute wait time
+ setStrategyMute((routing_strategy)i, false, outputDesc->mId,
+ muteWaitMs *2, device);
+ }
+ }
+ }
+
// wait for the PCM output buffers to empty before proceeding with the rest of the command
if (muteWaitMs > delayMs) {
muteWaitMs -= delayMs;
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index 2811475..4e9a2f0 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -51,7 +51,7 @@
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
-static const nsecs_t kAudioCommandTimeout = 3000000000LL; // 3 seconds
+static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
namespace {
extern struct audio_policy_service_ops aps_ops;
@@ -268,10 +268,6 @@
if (!mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
- for (size_t k=0; k < mAudioCommands.size(); k++) {
- delete mAudioCommands[k]->mParam;
- delete mAudioCommands[k];
- }
mAudioCommands.clear();
delete mpToneGenerator;
}
@@ -292,20 +288,19 @@
nsecs_t curTime = systemTime();
// commands are sorted by increasing time stamp: execute them from index 0 and up
if (mAudioCommands[0]->mTime <= curTime) {
- AudioCommand *command = mAudioCommands[0];
+ sp<AudioCommand> command = mAudioCommands[0];
mAudioCommands.removeAt(0);
- mLastCommand = *command;
+ mLastCommand = command;
switch (command->mCommand) {
case START_TONE: {
mLock.unlock();
- ToneData *data = (ToneData *)command->mParam;
+ ToneData *data = (ToneData *)command->mParam.get();
ALOGV("AudioCommandThread() processing start tone %d on stream %d",
data->mType, data->mStream);
delete mpToneGenerator;
mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
mpToneGenerator->startTone(data->mType);
- delete data;
mLock.lock();
}break;
case STOP_TONE: {
@@ -319,42 +314,27 @@
mLock.lock();
}break;
case SET_VOLUME: {
- VolumeData *data = (VolumeData *)command->mParam;
+ VolumeData *data = (VolumeData *)command->mParam.get();
ALOGV("AudioCommandThread() processing set volume stream %d, \
volume %f, output %d", data->mStream, data->mVolume, data->mIO);
command->mStatus = AudioSystem::setStreamVolume(data->mStream,
data->mVolume,
data->mIO);
- if (command->mWaitStatus) {
- command->mCond.signal();
- command->mCond.waitRelative(mLock, kAudioCommandTimeout);
- }
- delete data;
}break;
case SET_PARAMETERS: {
- ParametersData *data = (ParametersData *)command->mParam;
+ ParametersData *data = (ParametersData *)command->mParam.get();
ALOGV("AudioCommandThread() processing set parameters string %s, io %d",
data->mKeyValuePairs.string(), data->mIO);
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
- if (command->mWaitStatus) {
- command->mCond.signal();
- command->mCond.waitRelative(mLock, kAudioCommandTimeout);
- }
- delete data;
}break;
case SET_VOICE_VOLUME: {
- VoiceVolumeData *data = (VoiceVolumeData *)command->mParam;
+ VoiceVolumeData *data = (VoiceVolumeData *)command->mParam.get();
ALOGV("AudioCommandThread() processing set voice volume volume %f",
data->mVolume);
command->mStatus = AudioSystem::setVoiceVolume(data->mVolume);
- if (command->mWaitStatus) {
- command->mCond.signal();
- command->mCond.waitRelative(mLock, kAudioCommandTimeout);
- }
- delete data;
}break;
case STOP_OUTPUT: {
- StopOutputData *data = (StopOutputData *)command->mParam;
+ StopOutputData *data = (StopOutputData *)command->mParam.get();
ALOGV("AudioCommandThread() processing stop output %d",
data->mIO);
sp<AudioPolicyService> svc = mService.promote();
@@ -364,10 +344,9 @@
mLock.unlock();
svc->doStopOutput(data->mIO, data->mStream, data->mSession);
mLock.lock();
- delete data;
}break;
case RELEASE_OUTPUT: {
- ReleaseOutputData *data = (ReleaseOutputData *)command->mParam;
+ ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get();
ALOGV("AudioCommandThread() processing release output %d",
data->mIO);
sp<AudioPolicyService> svc = mService.promote();
@@ -377,12 +356,17 @@
mLock.unlock();
svc->doReleaseOutput(data->mIO);
mLock.lock();
- delete data;
}break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
- delete command;
+ {
+ Mutex::Autolock _l(command->mLock);
+ if (command->mWaitStatus) {
+ command->mWaitStatus = false;
+ command->mCond.signal();
+ }
+ }
waitTime = INT64_MAX;
} else {
waitTime = mAudioCommands[0]->mTime - curTime;
@@ -425,8 +409,12 @@
result.append(buffer);
}
result.append(" Last Command\n");
- mLastCommand.dump(buffer, SIZE);
- result.append(buffer);
+ if (mLastCommand != 0) {
+ mLastCommand->dump(buffer, SIZE);
+ result.append(buffer);
+ } else {
+ result.append(" none\n");
+ }
write(fd, result.string(), result.size());
@@ -438,27 +426,22 @@
void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type,
audio_stream_type_t stream)
{
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = START_TONE;
- ToneData *data = new ToneData();
+ sp<ToneData> data = new ToneData();
data->mType = type;
data->mStream = stream;
command->mParam = data;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command);
ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream);
- mWaitWorkCV.signal();
+ sendCommand(command);
}
void AudioPolicyService::AudioCommandThread::stopToneCommand()
{
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = STOP_TONE;
- command->mParam = NULL;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command);
ALOGV("AudioCommandThread() adding tone stop");
- mWaitWorkCV.signal();
+ sendCommand(command);
}
status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
@@ -466,109 +449,96 @@
audio_io_handle_t output,
int delayMs)
{
- status_t status = NO_ERROR;
-
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = SET_VOLUME;
- VolumeData *data = new VolumeData();
+ sp<VolumeData> data = new VolumeData();
data->mStream = stream;
data->mVolume = volume;
data->mIO = output;
command->mParam = data;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command, delayMs);
+ command->mWaitStatus = true;
ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",
stream, volume, output);
- mWaitWorkCV.signal();
- if (command->mWaitStatus) {
- command->mCond.wait(mLock);
- status = command->mStatus;
- command->mCond.signal();
- }
- return status;
+ return sendCommand(command, delayMs);
}
status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs,
int delayMs)
{
- status_t status = NO_ERROR;
-
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = SET_PARAMETERS;
- ParametersData *data = new ParametersData();
+ sp<ParametersData> data = new ParametersData();
data->mIO = ioHandle;
data->mKeyValuePairs = String8(keyValuePairs);
command->mParam = data;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command, delayMs);
+ command->mWaitStatus = true;
ALOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d",
keyValuePairs, ioHandle, delayMs);
- mWaitWorkCV.signal();
- if (command->mWaitStatus) {
- command->mCond.wait(mLock);
- status = command->mStatus;
- command->mCond.signal();
- }
- return status;
+ return sendCommand(command, delayMs);
}
status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs)
{
- status_t status = NO_ERROR;
-
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = SET_VOICE_VOLUME;
- VoiceVolumeData *data = new VoiceVolumeData();
+ sp<VoiceVolumeData> data = new VoiceVolumeData();
data->mVolume = volume;
command->mParam = data;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command, delayMs);
+ command->mWaitStatus = true;
ALOGV("AudioCommandThread() adding set voice volume volume %f", volume);
- mWaitWorkCV.signal();
- if (command->mWaitStatus) {
- command->mCond.wait(mLock);
- status = command->mStatus;
- command->mCond.signal();
- }
- return status;
+ return sendCommand(command, delayMs);
}
void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output,
audio_stream_type_t stream,
int session)
{
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = STOP_OUTPUT;
- StopOutputData *data = new StopOutputData();
+ sp<StopOutputData> data = new StopOutputData();
data->mIO = output;
data->mStream = stream;
data->mSession = session;
command->mParam = data;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command);
ALOGV("AudioCommandThread() adding stop output %d", output);
- mWaitWorkCV.signal();
+ sendCommand(command);
}
void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output)
{
- AudioCommand *command = new AudioCommand();
+ sp<AudioCommand> command = new AudioCommand();
command->mCommand = RELEASE_OUTPUT;
- ReleaseOutputData *data = new ReleaseOutputData();
+ sp<ReleaseOutputData> data = new ReleaseOutputData();
data->mIO = output;
command->mParam = data;
- Mutex::Autolock _l(mLock);
- insertCommand_l(command);
ALOGV("AudioCommandThread() adding release output %d", output);
- mWaitWorkCV.signal();
+ sendCommand(command);
+}
+
+status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
+{
+ {
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command, delayMs);
+ mWaitWorkCV.signal();
+ }
+ Mutex::Autolock _l(command->mLock);
+ while (command->mWaitStatus) {
+ nsecs_t timeOutNs = kAudioCommandTimeoutNs + milliseconds(delayMs);
+ if (command->mCond.waitRelative(command->mLock, timeOutNs) != NO_ERROR) {
+ command->mStatus = TIMED_OUT;
+ command->mWaitStatus = false;
+ }
+ }
+ return command->mStatus;
}
// insertCommand_l() must be called with mLock held
-void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
+void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& command, int delayMs)
{
ssize_t i; // not size_t because i will count down to -1
- Vector <AudioCommand *> removedCommands;
+ Vector < sp<AudioCommand> > removedCommands;
command->mTime = systemTime() + milliseconds(delayMs);
// acquire wake lock to make sure delayed commands are processed
@@ -578,15 +548,15 @@
// check same pending commands with later time stamps and eliminate them
for (i = mAudioCommands.size()-1; i >= 0; i--) {
- AudioCommand *command2 = mAudioCommands[i];
+ sp<AudioCommand> command2 = mAudioCommands[i];
// commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands
if (command2->mTime <= command->mTime) break;
if (command2->mCommand != command->mCommand) continue;
switch (command->mCommand) {
case SET_PARAMETERS: {
- ParametersData *data = (ParametersData *)command->mParam;
- ParametersData *data2 = (ParametersData *)command2->mParam;
+ ParametersData *data = (ParametersData *)command->mParam.get();
+ ParametersData *data2 = (ParametersData *)command2->mParam.get();
if (data->mIO != data2->mIO) break;
ALOGV("Comparing parameter command %s to new command %s",
data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
@@ -621,8 +591,8 @@
} break;
case SET_VOLUME: {
- VolumeData *data = (VolumeData *)command->mParam;
- VolumeData *data2 = (VolumeData *)command2->mParam;
+ VolumeData *data = (VolumeData *)command->mParam.get();
+ VolumeData *data2 = (VolumeData *)command2->mParam.get();
if (data->mIO != data2->mIO) break;
if (data->mStream != data2->mStream) break;
ALOGV("Filtering out volume command on output %d for stream %d",
@@ -644,12 +614,8 @@
for (size_t j = 0; j < removedCommands.size(); j++) {
// removed commands always have time stamps greater than current command
for (size_t k = i + 1; k < mAudioCommands.size(); k++) {
- if (mAudioCommands[k] == removedCommands[j]) {
+ if (mAudioCommands[k].get() == removedCommands[j].get()) {
ALOGV("suppressing command: %d", mAudioCommands[k]->mCommand);
- // for commands that are not filtered,
- // command->mParam is deleted in threadLoop
- delete mAudioCommands[k]->mParam;
- delete mAudioCommands[k];
mAudioCommands.removeAt(k);
break;
}
@@ -657,10 +623,8 @@
}
removedCommands.clear();
- // wait for status only if delay is 0
- if (delayMs == 0) {
- command->mWaitStatus = true;
- } else {
+ // Disable wait for status if delay is not 0
+ if (delayMs != 0) {
command->mWaitStatus = false;
}
@@ -688,7 +652,7 @@
(int)ns2s(mTime),
(int)ns2ms(mTime)%1000,
mWaitStatus,
- mParam);
+ mParam.get());
}
/******* helpers for the service_ops callbacks defined below *********/
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index 28e3a4b..26037e4 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -194,30 +194,31 @@
audio_stream_type_t stream,
int session);
void releaseOutputCommand(audio_io_handle_t output);
-
- void insertCommand_l(AudioCommand *command, int delayMs = 0);
+ status_t sendCommand(sp<AudioCommand>& command, int delayMs = 0);
+ void insertCommand_l(sp<AudioCommand>& command, int delayMs = 0);
private:
class AudioCommandData;
// descriptor for requested tone playback event
- class AudioCommand {
+ class AudioCommand: public RefBase {
public:
AudioCommand()
- : mCommand(-1) {}
+ : mCommand(-1), mStatus(NO_ERROR), mWaitStatus(false) {}
void dump(char* buffer, size_t size);
int mCommand; // START_TONE, STOP_TONE ...
nsecs_t mTime; // time stamp
+ Mutex mLock; // mutex associated to mCond
Condition mCond; // condition for status return
status_t mStatus; // command status
bool mWaitStatus; // true if caller is waiting for status
- AudioCommandData *mParam; // command specific parameter data
+ sp<AudioCommandData> mParam; // command specific parameter data
};
- class AudioCommandData {
+ class AudioCommandData: public RefBase {
public:
virtual ~AudioCommandData() {}
protected:
@@ -262,9 +263,9 @@
Mutex mLock;
Condition mWaitWorkCV;
- Vector <AudioCommand *> mAudioCommands; // list of pending commands
+ Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
ToneGenerator *mpToneGenerator; // the tone generator
- AudioCommand mLastCommand; // last processed command (used by dump)
+ sp<AudioCommand> mLastCommand; // last processed command (used by dump)
String8 mName; // string used by wake lock fo delayed commands
wp<AudioPolicyService> mService;
};
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 5bfb969..65592d3 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -1754,6 +1754,9 @@
camera_metadata_entry_t intent =
request->find(ANDROID_CONTROL_CAPTURE_INTENT);
+
+ if (intent.count == 0) return BAD_VALUE;
+
if (intent.data.u8[0] == ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE) {
res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
fastInfo.bestStillCaptureFpsRange, 2);