Merge "Camera2: Fix exposure compensation step value." into jb-mr1-dev
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 534afce..0df77c1 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -55,6 +55,7 @@
METADATA_KEY_TIMED_TEXT_LANGUAGES = 21,
METADATA_KEY_IS_DRM = 22,
METADATA_KEY_LOCATION = 23,
+ METADATA_KEY_VIDEO_ROTATION = 24,
// Add more here...
};
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 3c25a14..e91904c 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -111,6 +111,7 @@
kKeyTrackTimeStatus = 'tktm', // int64_t
kKeyNotRealTime = 'ntrt', // bool (int32_t)
+ kKeyNumBuffers = 'nbbf', // int32_t
// Ogg files can be tagged to be automatically looping...
kKeyAutoLoop = 'autL', // bool (int32_t)
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index cde551b..f23c337 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -64,6 +64,7 @@
enum State {
UNINITIALIZED,
+ PREPARED,
PLAYING,
PAUSED,
};
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 2f5e9a4..dabb0e7 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3623,11 +3623,6 @@
}
params->setInt64(kKeyTime, startTimeUs);
}
- status_t err = mSource->start(params.get());
-
- if (err != OK) {
- return err;
- }
mCodecSpecificDataIndex = 0;
mInitialBufferSubmit = true;
@@ -3640,6 +3635,23 @@
mFilledBuffers.clear();
mPaused = false;
+ status_t err;
+ if (mIsEncoder) {
+ // Calling init() before starting its source so that we can configure,
+ // if supported, the source to use exactly the same number of input
+ // buffers as requested by the encoder.
+ if ((err = init()) != OK) {
+ return err;
+ }
+
+ params->setInt32(kKeyNumBuffers, mPortBuffers[kPortIndexInput].size());
+ return mSource->start(params.get());
+ }
+
+ // Decoder case
+ if ((err = mSource->start(params.get())) != OK) {
+ return err;
+ }
return init();
}
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 7951496..c9ef4d9 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -462,6 +462,7 @@
int32_t videoWidth = -1;
int32_t videoHeight = -1;
int32_t audioBitrate = -1;
+ int32_t rotationAngle = -1;
// The overall duration is the duration of the longest track.
int64_t maxDurationUs = 0;
@@ -489,6 +490,9 @@
CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
+ if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
+ rotationAngle = 0;
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
const char *lang;
trackMeta->findCString(kKeyMediaLanguage, &lang);
@@ -521,6 +525,9 @@
sprintf(tmp, "%d", videoHeight);
mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
+
+ sprintf(tmp, "%d", rotationAngle);
+ mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp));
}
if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
index 42ca1f5..54ce7ac 100644
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -61,7 +61,7 @@
source = mTextSourceVector.valueFor(index);
mPlayer->setDataSource(source);
if (mState == UNINITIALIZED) {
- mState = PAUSED;
+ mState = PREPARED;
}
mCurrentTrackIndex = index;
return OK;
@@ -74,42 +74,47 @@
return INVALID_OPERATION;
case PLAYING:
return OK;
- case PAUSED:
+ case PREPARED:
mPlayer->start();
- break;
+ mState = PLAYING;
+ return OK;
+ case PAUSED:
+ mPlayer->resume();
+ mState = PLAYING;
+ return OK;
default:
TRESPASS();
}
- mState = PLAYING;
- return OK;
+ return UNKNOWN_ERROR;
}
-// TODO: Test if pause() works properly.
-// Scenario 1: start - pause - resume
-// Scenario 2: start - seek
-// Scenario 3: start - pause - seek - resume
status_t TimedTextDriver::pause() {
Mutex::Autolock autoLock(mLock);
+ ALOGV("%s() is called", __FUNCTION__);
switch (mState) {
case UNINITIALIZED:
return INVALID_OPERATION;
case PLAYING:
mPlayer->pause();
- break;
+ mState = PAUSED;
+ return OK;
+ case PREPARED:
+ return INVALID_OPERATION;
case PAUSED:
return OK;
default:
TRESPASS();
}
- mState = PAUSED;
- return OK;
+ return UNKNOWN_ERROR;
}
status_t TimedTextDriver::selectTrack(size_t index) {
status_t ret = OK;
Mutex::Autolock autoLock(mLock);
+ ALOGV("%s() is called", __FUNCTION__);
switch (mState) {
case UNINITIALIZED:
+ case PREPARED:
case PAUSED:
ret = selectTrack_l(index);
break;
@@ -128,21 +133,50 @@
}
status_t TimedTextDriver::unselectTrack(size_t index) {
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("%s() is called", __FUNCTION__);
if (mCurrentTrackIndex != index) {
return INVALID_OPERATION;
}
- status_t err = pause();
- if (err != OK) {
- return err;
+ switch (mState) {
+ case UNINITIALIZED:
+ return INVALID_OPERATION;
+ case PLAYING:
+ mPlayer->pause();
+ mState = UNINITIALIZED;
+ return OK;
+ case PREPARED:
+ case PAUSED:
+ mState = UNINITIALIZED;
+ return OK;
+ default:
+ TRESPASS();
}
- Mutex::Autolock autoLock(mLock);
- mState = UNINITIALIZED;
- return OK;
+ return UNKNOWN_ERROR;
}
status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
- mPlayer->seekToAsync(timeUs);
- return OK;
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("%s() is called", __FUNCTION__);
+ switch (mState) {
+ case UNINITIALIZED:
+ return INVALID_OPERATION;
+ case PREPARED:
+ mPlayer->seekToAsync(timeUs);
+ mPlayer->pause();
+ mState = PAUSED;
+ return OK;
+ case PAUSED:
+ mPlayer->seekToAsync(timeUs);
+ mPlayer->pause();
+ return OK;
+ case PLAYING:
+ mPlayer->seekToAsync(timeUs);
+ return OK;
+ defaut:
+ TRESPASS();
+ }
+ return UNKNOWN_ERROR;
}
status_t TimedTextDriver::addInBandTextSource(
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index 9b001cc..df7eb39 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "TimedTextPlayer"
#include <utils/Log.h>
+#include <limits.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/timedtext/TimedTextDriver.h>
@@ -30,12 +31,18 @@
namespace android {
+// Event should be fired a bit earlier considering the processing time till
+// application actually gets the notification message.
static const int64_t kAdjustmentProcessingTimeUs = 100000ll;
+static const int64_t kMaxDelayUs = 5000000ll;
static const int64_t kWaitTimeUsToRetryRead = 100000ll;
+static const int64_t kInvalidTimeUs = INT_MIN;
TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener)
: mListener(listener),
mSource(NULL),
+ mPendingSeekTimeUs(kInvalidTimeUs),
+ mPaused(false),
mSendSubtitleGeneration(0) {
}
@@ -48,15 +55,17 @@
}
void TimedTextPlayer::start() {
- sp<AMessage> msg = new AMessage(kWhatSeek, id());
- msg->setInt64("seekTimeUs", -1);
- msg->post();
+ (new AMessage(kWhatStart, id()))->post();
}
void TimedTextPlayer::pause() {
(new AMessage(kWhatPause, id()))->post();
}
+void TimedTextPlayer::resume() {
+ (new AMessage(kWhatResume, id()))->post();
+}
+
void TimedTextPlayer::seekToAsync(int64_t timeUs) {
sp<AMessage> msg = new AMessage(kWhatSeek, id());
msg->setInt64("seekTimeUs", timeUs);
@@ -72,10 +81,43 @@
void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatPause: {
+ mPaused = true;
+ break;
+ }
+ case kWhatResume: {
+ mPaused = false;
+ if (mPendingSeekTimeUs != kInvalidTimeUs) {
+ seekToAsync(mPendingSeekTimeUs);
+ mPendingSeekTimeUs = kInvalidTimeUs;
+ } else {
+ doRead();
+ }
+ break;
+ }
+ case kWhatStart: {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener == NULL) {
+ ALOGE("Listener is NULL when kWhatStart is received.");
+ break;
+ }
+ mPaused = false;
+ mPendingSeekTimeUs = kInvalidTimeUs;
+ int32_t positionMs = 0;
+ listener->getCurrentPosition(&positionMs);
+ int64_t seekTimeUs = positionMs * 1000ll;
+
+ notifyListener();
mSendSubtitleGeneration++;
+ doSeekAndRead(seekTimeUs);
break;
}
case kWhatRetryRead: {
+ int32_t generation = -1;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mSendSubtitleGeneration) {
+ // Drop obsolete msg.
+ break;
+ }
int64_t seekTimeUs;
int seekMode;
if (msg->findInt64("seekTimeUs", &seekTimeUs) &&
@@ -91,9 +133,11 @@
break;
}
case kWhatSeek: {
- int64_t seekTimeUs = 0;
+ int64_t seekTimeUs = kInvalidTimeUs;
+ // Clear a displayed timed text before seeking.
+ notifyListener();
msg->findInt64("seekTimeUs", &seekTimeUs);
- if (seekTimeUs < 0) {
+ if (seekTimeUs == kInvalidTimeUs) {
sp<MediaPlayerBase> listener = mListener.promote();
if (listener != NULL) {
int32_t positionMs = 0;
@@ -101,6 +145,11 @@
seekTimeUs = positionMs * 1000ll;
}
}
+ if (mPaused) {
+ mPendingSeekTimeUs = seekTimeUs;
+ break;
+ }
+ mSendSubtitleGeneration++;
doSeekAndRead(seekTimeUs);
break;
}
@@ -108,8 +157,19 @@
int32_t generation;
CHECK(msg->findInt32("generation", &generation));
if (generation != mSendSubtitleGeneration) {
- // Drop obsolete msg.
- break;
+ // Drop obsolete msg.
+ break;
+ }
+ // If current time doesn't reach to the fire time,
+ // re-post the message with the adjusted delay time.
+ int64_t fireTimeUs = kInvalidTimeUs;
+ if (msg->findInt64("fireTimeUs", &fireTimeUs)) {
+ // TODO: check if fireTimeUs is not kInvalidTimeUs.
+ int64_t delayUs = delayUsFromCurrentTime(fireTimeUs);
+ if (delayUs > 0) {
+ msg->post(delayUs);
+ break;
+ }
}
sp<RefBase> obj;
if (msg->findObject("subtitle", &obj)) {
@@ -162,12 +222,14 @@
if (err == WOULD_BLOCK) {
sp<AMessage> msg = new AMessage(kWhatRetryRead, id());
if (options != NULL) {
- int64_t seekTimeUs;
- MediaSource::ReadOptions::SeekMode seekMode;
+ int64_t seekTimeUs = kInvalidTimeUs;
+ MediaSource::ReadOptions::SeekMode seekMode =
+ MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
CHECK(options->getSeekTo(&seekTimeUs, &seekMode));
msg->setInt64("seekTimeUs", seekTimeUs);
msg->setInt32("seekMode", seekMode);
}
+ msg->setInt32("generation", mSendSubtitleGeneration);
msg->post(kWaitTimeUsToRetryRead);
return;
} else if (err != OK) {
@@ -185,49 +247,57 @@
}
void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
- sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- int64_t positionUs, delayUs;
- int32_t positionMs = 0;
- listener->getCurrentPosition(&positionMs);
- positionUs = positionMs * 1000ll;
-
- if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) {
- delayUs = 0;
- } else {
- delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs;
- }
- postTextEventDelayUs(parcel, delayUs);
+ int64_t delayUs = delayUsFromCurrentTime(timeUs);
+ sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id());
+ msg->setInt32("generation", mSendSubtitleGeneration);
+ if (parcel != NULL) {
+ msg->setObject("subtitle", parcel);
}
+ msg->setInt64("fireTimeUs", timeUs);
+ msg->post(delayUs);
}
-void TimedTextPlayer::postTextEventDelayUs(const sp<ParcelEvent>& parcel, int64_t delayUs) {
+int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) {
sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id());
- msg->setInt32("generation", mSendSubtitleGeneration);
- if (parcel != NULL) {
- msg->setObject("subtitle", parcel);
+ if (listener == NULL) {
+ // TODO: it may be better to return kInvalidTimeUs
+ ALOGE("%s: Listener is NULL.", __FUNCTION__, fireTimeUs);
+ return 0;
+ }
+ int32_t positionMs = 0;
+ listener->getCurrentPosition(&positionMs);
+ int64_t positionUs = positionMs * 1000ll;
+
+ if (fireTimeUs <= positionUs + kAdjustmentProcessingTimeUs) {
+ return 0;
+ } else {
+ int64_t delayUs = fireTimeUs - positionUs - kAdjustmentProcessingTimeUs;
+ if (delayUs > kMaxDelayUs) {
+ return kMaxDelayUs;
}
- msg->post(delayUs);
+ return delayUs;
}
}
void TimedTextPlayer::notifyError(int error) {
sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
+ if (listener == NULL) {
+ ALOGE("%s(error=%d): Listener is NULL.", __FUNCTION__, error);
+ return;
}
+ listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
}
void TimedTextPlayer::notifyListener(const Parcel *parcel) {
sp<MediaPlayerBase> listener = mListener.promote();
- if (listener != NULL) {
- if (parcel != NULL && (parcel->dataSize() > 0)) {
- listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
- } else { // send an empty timed text to clear the screen
- listener->sendEvent(MEDIA_TIMED_TEXT);
- }
+ if (listener == NULL) {
+ ALOGE("%s: Listener is NULL.", __FUNCTION__);
+ return;
+ }
+ if (parcel != NULL && (parcel->dataSize() > 0)) {
+ listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
+ } else { // send an empty timed text to clear the screen
+ listener->sendEvent(MEDIA_TIMED_TEXT);
}
}
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
index b7e15f8..ec8ed25 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -40,6 +40,7 @@
void start();
void pause();
+ void resume();
void seekToAsync(int64_t timeUs);
void setDataSource(sp<TimedTextSource> source);
@@ -49,6 +50,8 @@
private:
enum {
kWhatPause = 'paus',
+ kWhatResume = 'resm',
+ kWhatStart = 'strt',
kWhatSeek = 'seek',
kWhatRetryRead = 'read',
kWhatSendSubtitle = 'send',
@@ -62,13 +65,15 @@
wp<MediaPlayerBase> mListener;
sp<TimedTextSource> mSource;
+ int64_t mPendingSeekTimeUs;
+ bool mPaused;
int32_t mSendSubtitleGeneration;
void doSeekAndRead(int64_t seekTimeUs);
void doRead(MediaSource::ReadOptions* options = NULL);
void onTextEvent();
void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
- void postTextEventDelayUs(const sp<ParcelEvent>& parcel = NULL, int64_t delayUs = -1);
+ int64_t delayUsFromCurrentTime(int64_t fireTimeUs);
void notifyError(int error = 0);
void notifyListener(const Parcel *parcel = NULL);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index fe9d09d..d65a2b6 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -868,16 +868,19 @@
if (mBtNrecIsOff != btNrecIsOff) {
for (size_t i = 0; i < mRecordThreads.size(); i++) {
sp<RecordThread> thread = mRecordThreads.valueAt(i);
- RecordThread::RecordTrack *track = thread->track();
- if (track != NULL) {
- audio_devices_t device = thread->device() & AUDIO_DEVICE_IN_ALL;
- bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
+ audio_devices_t device = thread->device() & AUDIO_DEVICE_IN_ALL;
+ bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
+ // collect all of the thread's session IDs
+ KeyedVector<int, bool> ids = thread->sessionIds();
+ // suspend effects associated with those session IDs
+ for (size_t j = 0; j < ids.size(); ++j) {
+ int sessionId = ids.keyAt(j);
thread->setEffectSuspended(FX_IID_AEC,
suspend,
- track->sessionId());
+ sessionId);
thread->setEffectSuspended(FX_IID_NS,
suspend,
- track->sessionId());
+ sessionId);
}
}
mBtNrecIsOff = btNrecIsOff;
@@ -3178,7 +3181,7 @@
}
//ALOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user, cblk->server, this);
- if ((track->sharedBuffer() != 0) || track->isTerminated() ||
+ if ((track->sharedBuffer() != 0) ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
@@ -3198,8 +3201,8 @@
track->mUnderrunCount++;
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
- if (--(track->mRetryCount) <= 0) {
- ALOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
+ if (--(track->mRetryCount) <= 0 || track->isTerminated()) {
+ ALOGV_IF(track->mRetryCount <= 0, "BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
@@ -3698,7 +3701,7 @@
}
//ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
- if ((track->sharedBuffer() != 0) || track->isTerminated() ||
+ if ((track->sharedBuffer() != 0) ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
@@ -3716,8 +3719,8 @@
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
- if (--(track->mRetryCount) <= 0) {
- ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ if (--(track->mRetryCount) <= 0 || track->isTerminated()) {
+ ALOGV_IF(track->mRetryCount <= 0, "BUFFER TIMEOUT: remove(%d) from active list", track->name());
trackToRemove = track;
} else {
mixerStatus = MIXER_TRACKS_ENABLED;
@@ -4277,11 +4280,6 @@
AudioFlinger::PlaybackThread::Track::~Track()
{
ALOGV("PlaybackThread::Track destructor");
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- mState = TERMINATED;
- }
}
void AudioFlinger::PlaybackThread::Track::destroy()
@@ -5301,10 +5299,7 @@
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- AudioSystem::releaseInput(thread->id());
- }
+ ALOGV("%s", __func__);
}
// AudioBufferProvider interface
@@ -5377,6 +5372,11 @@
}
}
+/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
+{
+ result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User\n");
+}
+
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x\n",
@@ -5861,6 +5861,7 @@
AudioFlinger::RecordHandle::~RecordHandle() {
stop_nonvirtual();
+ mRecordTrack->destroy();
}
sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
@@ -5896,7 +5897,7 @@
audio_io_handle_t id,
audio_devices_t device) :
ThreadBase(audioFlinger, id, device, RECORD),
- mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
// mRsmpInIndex and mInputBytes set by readInputParameters()
mReqChannelCount(popcount(channelMask)),
mReqSampleRate(sampleRate)
@@ -5980,6 +5981,9 @@
mStartStopCond.broadcast();
}
mStandby = false;
+ } else if (mActiveTrack->mState == TrackBase::TERMINATED) {
+ removeTrack_l(mActiveTrack);
+ mActiveTrack.clear();
}
}
lockEffectChains_l(effectChains);
@@ -6168,8 +6172,8 @@
lStatus = NO_MEMORY;
goto Exit;
}
+ mTracks.add(track);
- mTrack = track.get();
// disable AEC and NS if the device is a BT SCO headset supporting those pre processings
bool suspend = audio_is_bluetooth_sco_device(mDevice & AUDIO_DEVICE_IN_ALL) &&
mAudioFlinger->btNrecIsOff();
@@ -6320,17 +6324,64 @@
return BAD_VALUE;
}
+ int eventSession = event->triggerSession();
+ status_t ret = NAME_NOT_FOUND;
+
Mutex::Autolock _l(mLock);
- if (mTrack != NULL && event->triggerSession() == mTrack->sessionId()) {
- mTrack->setSyncEvent(event);
- return NO_ERROR;
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ if (eventSession == track->sessionId()) {
+ track->setSyncEvent(event);
+ ret = NO_ERROR;
+ }
}
- return NAME_NOT_FOUND;
+ return ret;
+}
+
+void AudioFlinger::RecordThread::RecordTrack::destroy()
+{
+ // see comments at AudioFlinger::PlaybackThread::Track::destroy()
+ sp<RecordTrack> keep(this);
+ {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopInput(thread->id());
+ }
+ AudioSystem::releaseInput(thread->id());
+ Mutex::Autolock _l(thread->mLock);
+ RecordThread *recordThread = (RecordThread *) thread.get();
+ recordThread->destroyTrack_l(this);
+ }
+ }
+}
+
+// destroyTrack_l() must be called with ThreadBase::mLock held
+void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
+{
+ track->mState = TrackBase::TERMINATED;
+ // active tracks are removed by threadLoop()
+ if (mActiveTrack != track) {
+ removeTrack_l(track);
+ }
+}
+
+void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track)
+{
+ mTracks.remove(track);
+ // need anything related to effects here?
}
void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
{
+ dumpInternals(fd, args);
+ dumpTracks(fd, args);
+ dumpEffectChains(fd, args);
+}
+
+void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
+{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
@@ -6339,11 +6390,6 @@
result.append(buffer);
if (mActiveTrack != 0) {
- result.append("Active Track:\n");
- result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User\n");
- mActiveTrack->dump(buffer, SIZE);
- result.append(buffer);
-
snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
result.append(buffer);
snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
@@ -6354,15 +6400,41 @@
result.append(buffer);
snprintf(buffer, SIZE, "Out sample rate: %d\n", mReqSampleRate);
result.append(buffer);
-
-
} else {
- result.append("No record client\n");
+ result.append("No active record client\n");
}
+
write(fd, result.string(), result.size());
dumpBase(fd, args);
- dumpEffectChains(fd, args);
+}
+
+void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "Input thread %p tracks\n", this);
+ result.append(buffer);
+ RecordTrack::appendDumpHeader(result);
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<RecordTrack> track = mTracks[i];
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ if (mActiveTrack != 0) {
+ snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
+ result.append(buffer);
+ RecordTrack::appendDumpHeader(result);
+ mActiveTrack->dump(buffer, SIZE);
+ result.append(buffer);
+
+ }
+ write(fd, result.string(), result.size());
}
// AudioBufferProvider interface
@@ -6462,11 +6534,14 @@
} else {
newDevice &= ~(value & AUDIO_DEVICE_IN_ALL);
// disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- if (mTrack != NULL) {
+ if (mTracks.size() > 0) {
bool suspend = audio_is_bluetooth_sco_device(
(audio_devices_t)value) && mAudioFlinger->btNrecIsOff();
- setEffectSuspended_l(FX_IID_AEC, suspend, mTrack->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, mTrack->sessionId());
+ 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());
+ }
}
}
newDevice |= value;
@@ -6605,17 +6680,28 @@
result = EFFECT_SESSION;
}
- if (mTrack != NULL && sessionId == mTrack->sessionId()) {
- result |= TRACK_SESSION;
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ if (sessionId == mTracks[i]->sessionId()) {
+ result |= TRACK_SESSION;
+ break;
+ }
}
return result;
}
-AudioFlinger::RecordThread::RecordTrack* AudioFlinger::RecordThread::track()
+KeyedVector<int, bool> AudioFlinger::RecordThread::sessionIds()
{
+ KeyedVector<int, bool> ids;
Mutex::Autolock _l(mLock);
- return mTrack;
+ for (size_t j = 0; j < mTracks.size(); ++j) {
+ sp<RecordThread::RecordTrack> track = mTracks[j];
+ int sessionId = track->sessionId();
+ if (ids.indexOfKey(sessionId) < 0) {
+ ids.add(sessionId, true);
+ }
+ }
+ return ids;
}
AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::clearInput()
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e629533..2b6d00f 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -1381,11 +1381,14 @@
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
virtual void stop();
+ void destroy();
+
// clear the buffer overflow flag
void clearOverflow() { mOverflow = false; }
// set the buffer overflow flag and return previous value
bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
+ static void appendDumpHeader(String8& result);
void dump(char* buffer, size_t size);
private:
@@ -1409,6 +1412,13 @@
audio_devices_t device);
virtual ~RecordThread();
+ // no addTrack_l ?
+ void destroyTrack_l(const sp<RecordTrack>& track);
+ void removeTrack_l(const sp<RecordTrack>& track);
+
+ void dumpInternals(int fd, const Vector<String16>& args);
+ void dumpTracks(int fd, const Vector<String16>& args);
+
// Thread
virtual bool threadLoop();
virtual status_t readyToRun();
@@ -1453,7 +1463,11 @@
virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
virtual uint32_t hasAudioSession(int sessionId);
- RecordTrack* track();
+
+ // Return the set of unique session IDs across all tracks.
+ // The keys are the session IDs, and the associated values are meaningless.
+ // FIXME replace by Set [and implement Bag/Multiset for other uses].
+ KeyedVector<int, bool> sessionIds();
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
virtual bool isValidSyncEvent(const sp<SyncEvent>& event);
@@ -1471,7 +1485,9 @@
void inputStandBy();
AudioStreamIn *mInput;
- RecordTrack* mTrack;
+ SortedVector < sp<RecordTrack> > mTracks;
+ // mActiveTrack has dual roles: it indicates the current active track, and
+ // is used together with mStartStopCond to indicate start()/stop() progress
sp<RecordTrack> mActiveTrack;
Condition mStartStopCond;
AudioResampler *mResampler;
diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp
index 80ccb43..54829ef 100644
--- a/services/camera/libcameraservice/CameraClient.cpp
+++ b/services/camera/libcameraservice/CameraClient.cpp
@@ -445,9 +445,9 @@
Mutex::Autolock lock(mLock);
if (checkPidAndHardware() != NO_ERROR) return;
- mCameraService->playSound(CameraService::SOUND_RECORDING);
disableMsgType(CAMERA_MSG_VIDEO_FRAME);
mHardware->stopRecording();
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
mPreviewBuffer.clear();
}