Merge "CameraMetadata: fix metadata alignment issue"
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index c817443..439b6e4 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "sf2"
#include <utils/Log.h>
+#include <signal.h>
+
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/hexdump.h>
@@ -42,6 +44,18 @@
using namespace android;
+volatile static bool ctrlc = false;
+
+static sighandler_t oldhandler = NULL;
+
+static void mysighandler(int signum) {
+ if (signum == SIGINT) {
+ ctrlc = true;
+ return;
+ }
+ oldhandler(signum);
+}
+
struct Controller : public AHandler {
Controller(const char *uri, bool decodeAudio,
const sp<Surface> &surface, bool renderToSurface)
@@ -62,7 +76,29 @@
virtual ~Controller() {
}
+ virtual void printStatistics() {
+ int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
+ if (mDecodeAudio) {
+ printf("%lld bytes received. %.2f KB/sec\n",
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ } else {
+ printf("%d frames decoded, %.2f fps. %lld bytes "
+ "received. %.2f KB/sec\n",
+ mNumOutputBuffersReceived,
+ mNumOutputBuffersReceived * 1E6 / delayUs,
+ mTotalBytesReceived,
+ mTotalBytesReceived * 1E6 / 1024 / delayUs);
+ }
+ }
+
virtual void onMessageReceived(const sp<AMessage> &msg) {
+ if (ctrlc) {
+ printf("\n");
+ printStatistics();
+ (new AMessage(kWhatStop, id()))->post();
+ ctrlc = false;
+ }
switch (msg->what()) {
case kWhatStart:
{
@@ -98,7 +134,10 @@
break;
}
}
- CHECK(mSource != NULL);
+ if (mSource == NULL) {
+ printf("no %s track found\n", mDecodeAudio ? "audio" : "video");
+ exit (1);
+ }
CHECK_EQ(mSource->start(), (status_t)OK);
@@ -180,21 +219,7 @@
|| what == ACodec::kWhatError) {
printf((what == ACodec::kWhatEOS) ? "$\n" : "E\n");
- int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
-
- if (mDecodeAudio) {
- printf("%lld bytes received. %.2f KB/sec\n",
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- } else {
- printf("%d frames decoded, %.2f fps. %lld bytes "
- "received. %.2f KB/sec\n",
- mNumOutputBuffersReceived,
- mNumOutputBuffersReceived * 1E6 / delayUs,
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- }
-
+ printStatistics();
(new AMessage(kWhatStop, id()))->post();
} else if (what == ACodec::kWhatFlushCompleted) {
mSeekState = SEEK_FLUSH_COMPLETED;
@@ -638,6 +663,8 @@
looper->registerHandler(controller);
+ signal(SIGINT, mysighandler);
+
controller->startAsync();
CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 045bf64..ba8d0b4 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -441,7 +441,7 @@
bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
{
- return iMemory.get() == mIMemory.get();
+ return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
}
} // namespace android
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 1a9a26b..da23fea 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -127,7 +127,6 @@
QueueItem item;
item.event = event;
item.realtime_us = realtime_us;
- item.has_wakelock = false;
if (it == mQueue.begin()) {
mQueueHeadChangedCondition.signal();
@@ -135,7 +134,7 @@
if (realtime_us > ALooper::GetNowUs() + kWakelockMinDelay) {
acquireWakeLock_l();
- item.has_wakelock = true;
+ event->setWakeLock();
}
mQueue.insert(it, item);
@@ -191,7 +190,7 @@
ALOGV("cancelling event %d", (*it).event->eventID());
(*it).event->setEventID(0);
- if ((*it).has_wakelock) {
+ if ((*it).event->hasWakeLock()) {
releaseWakeLock_l();
}
it = mQueue.erase(it);
@@ -289,6 +288,9 @@
if (event != NULL) {
// Fire event with the lock NOT held.
event->fire(this, now_us);
+ if (event->hasWakeLock()) {
+ releaseWakeLock_l();
+ }
}
}
}
@@ -300,9 +302,6 @@
if ((*it).event->eventID() == id) {
sp<Event> event = (*it).event;
event->setEventID(0);
- if ((*it).has_wakelock) {
- releaseWakeLock_l();
- }
mQueue.erase(it);
return event;
}
diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
index 38a08b1..4e8912d 100644
--- a/media/libstagefright/include/TimedEventQueue.h
+++ b/media/libstagefright/include/TimedEventQueue.h
@@ -33,7 +33,7 @@
struct Event : public RefBase {
Event()
- : mEventID(0) {
+ : mEventID(0), mHasWakeLock(false) {
}
virtual ~Event() {}
@@ -42,6 +42,14 @@
return mEventID;
}
+ void setWakeLock() {
+ mHasWakeLock = true;
+ }
+
+ bool hasWakeLock() {
+ return mHasWakeLock;
+ }
+
protected:
virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
@@ -49,6 +57,7 @@
friend class TimedEventQueue;
event_id mEventID;
+ bool mHasWakeLock;
void setEventID(event_id id) {
mEventID = id;
@@ -118,7 +127,6 @@
struct QueueItem {
sp<Event> event;
int64_t realtime_us;
- bool has_wakelock;
};
struct StopEvent : public TimedEventQueue::Event {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 5cf6ef3..443051c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -213,6 +213,18 @@
audio_hw_device_close(mAudioHwDevs.valueAt(i)->hwDevice());
delete mAudioHwDevs.valueAt(i);
}
+
+ // Tell media.log service about any old writers that still need to be unregistered
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ sp<IMediaLogService> mediaLogService(interface_cast<IMediaLogService>(binder));
+ for (size_t count = mUnregisteredWriters.size(); count > 0; count--) {
+ sp<IMemory> iMemory(mUnregisteredWriters.top()->getIMemory());
+ mUnregisteredWriters.pop();
+ mediaLogService->unregisterWriter(iMemory);
+ }
+ }
+
}
static const char * const audio_interfaces[] = {
@@ -406,16 +418,44 @@
sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name)
{
+ // If there is no memory allocated for logs, return a dummy writer that does nothing
if (mLogMemoryDealer == 0) {
return new NBLog::Writer();
}
- sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
- sp<NBLog::Writer> writer = new NBLog::Writer(size, shared);
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
- if (binder != 0) {
- interface_cast<IMediaLogService>(binder)->registerWriter(shared, size, name);
+ // Similarly if we can't contact the media.log service, also return a dummy writer
+ if (binder == 0) {
+ return new NBLog::Writer();
}
- return writer;
+ sp<IMediaLogService> mediaLogService(interface_cast<IMediaLogService>(binder));
+ sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
+ // If allocation fails, consult the vector of previously unregistered writers
+ // and garbage-collect one or more them until an allocation succeeds
+ if (shared == 0) {
+ Mutex::Autolock _l(mUnregisteredWritersLock);
+ for (size_t count = mUnregisteredWriters.size(); count > 0; count--) {
+ {
+ // Pick the oldest stale writer to garbage-collect
+ sp<IMemory> iMemory(mUnregisteredWriters[0]->getIMemory());
+ mUnregisteredWriters.removeAt(0);
+ mediaLogService->unregisterWriter(iMemory);
+ // Now the media.log remote reference to IMemory is gone. When our last local
+ // reference to IMemory also drops to zero at end of this block,
+ // the IMemory destructor will deallocate the region from mLogMemoryDealer.
+ }
+ // Re-attempt the allocation
+ shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
+ if (shared != 0) {
+ goto success;
+ }
+ }
+ // Even after garbage-collecting all old writers, there is still not enough memory,
+ // so return a dummy writer
+ return new NBLog::Writer();
+ }
+success:
+ mediaLogService->registerWriter(shared, size, name);
+ return new NBLog::Writer(size, shared);
}
void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer)
@@ -427,13 +467,10 @@
if (iMemory == 0) {
return;
}
- sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
- if (binder != 0) {
- interface_cast<IMediaLogService>(binder)->unregisterWriter(iMemory);
- // Now the media.log remote reference to IMemory is gone.
- // When our last local reference to IMemory also drops to zero,
- // the IMemory destructor will deallocate the region from mMemoryDealer.
- }
+ // Rather than removing the writer immediately, append it to a queue of old writers to
+ // be garbage-collected later. This allows us to continue to view old logs for a while.
+ Mutex::Autolock _l(mUnregisteredWritersLock);
+ mUnregisteredWriters.push(writer);
}
// IAudioFlinger interface
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 9137040..066d5d5 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -235,8 +235,12 @@
sp<NBLog::Writer> newWriter_l(size_t size, const char *name);
void unregisterWriter(const sp<NBLog::Writer>& writer);
private:
- static const size_t kLogMemorySize = 10 * 1024;
+ static const size_t kLogMemorySize = 40 * 1024;
sp<MemoryDealer> mLogMemoryDealer; // == 0 when NBLog is disabled
+ // When a log writer is unregistered, it is done lazily so that media.log can continue to see it
+ // for as long as possible. The memory is only freed when it is needed for another log writer.
+ Vector< sp<NBLog::Writer> > mUnregisteredWriters;
+ Mutex mUnregisteredWritersLock;
public:
class SyncEvent;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7d39822..8887b38 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3874,7 +3874,12 @@
{
Mutex::Autolock _l(mLock);
- mWaitWorkCV.wait(mLock);
+ while (!((mWriteAckSequence & 1) ||
+ (mDrainSequence & 1) ||
+ exitPending())) {
+ mWaitWorkCV.wait(mLock);
+ }
+
if (exitPending()) {
break;
}
@@ -4391,7 +4396,7 @@
#endif
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
- mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ mInput(input), mActiveTracksGen(0), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
// mRsmpInFrames, mRsmpInFramesP2, mRsmpInUnrel, mRsmpInFront, and mRsmpInRear
// are set by readInputParameters()
// mRsmpInIndex LEGACY
@@ -4404,6 +4409,7 @@
#endif
{
snprintf(mName, kNameLength, "AudioIn_%X", id);
+ mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
readInputParameters();
}
@@ -4411,6 +4417,7 @@
AudioFlinger::RecordThread::~RecordThread()
{
+ mAudioFlinger->unregisterWriter(mNBLogWriter);
delete[] mRsmpInBuffer;
delete mResampler;
delete[] mRsmpOutBuffer;
@@ -4435,10 +4442,25 @@
reacquire_wakelock:
sp<RecordTrack> activeTrack;
+ int activeTracksGen;
{
Mutex::Autolock _l(mLock);
- activeTrack = mActiveTrack;
- acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1);
+ size_t size = mActiveTracks.size();
+ activeTracksGen = mActiveTracksGen;
+ if (size > 0) {
+ // FIXME an arbitrary choice
+ activeTrack = mActiveTracks[0];
+ acquireWakeLock_l(activeTrack->uid());
+ if (size > 1) {
+ SortedVector<int> tmp;
+ for (size_t i = 0; i < size; i++) {
+ tmp.add(mActiveTracks[i]->uid());
+ }
+ updateWakeLockUids_l(tmp);
+ }
+ } else {
+ acquireWakeLock_l(-1);
+ }
}
// start recording
@@ -4461,8 +4483,9 @@
// return value 'reconfig' is currently unused
bool reconfig = checkForNewParameters_l();
- // if no active track, then standby and release wakelock
- if (mActiveTrack == 0) {
+ // if no active track(s), then standby and release wakelock
+ size_t size = mActiveTracks.size();
+ if (size == 0) {
standbyIfNotAlreadyInStandby();
// exitPending() can't become true here
releaseWakeLock_l();
@@ -4473,16 +4496,21 @@
goto reacquire_wakelock;
}
- if (activeTrack != mActiveTrack) {
+ if (mActiveTracksGen != activeTracksGen) {
+ activeTracksGen = mActiveTracksGen;
SortedVector<int> tmp;
- tmp.add(mActiveTrack->uid());
+ for (size_t i = 0; i < size; i++) {
+ tmp.add(mActiveTracks[i]->uid());
+ }
updateWakeLockUids_l(tmp);
- activeTrack = mActiveTrack;
+ // FIXME an arbitrary choice
+ activeTrack = mActiveTracks[0];
}
if (activeTrack->isTerminated()) {
removeTrack_l(activeTrack);
- mActiveTrack.clear();
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
continue;
}
@@ -4490,7 +4518,8 @@
switch (activeTrackState) {
case TrackBase::PAUSING:
standbyIfNotAlreadyInStandby();
- mActiveTrack.clear();
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
mStartStopCond.broadcast();
doSleep = true;
continue;
@@ -4498,7 +4527,8 @@
case TrackBase::RESUMING:
mStandby = false;
if (mReqChannelCount != activeTrack->channelCount()) {
- mActiveTrack.clear();
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
mStartStopCond.broadcast();
continue;
}
@@ -4506,7 +4536,8 @@
mStartStopCond.broadcast();
// record start succeeds only if first read from audio input succeeds
if (mBytesRead < 0) {
- mActiveTrack.clear();
+ mActiveTracks.remove(activeTrack);
+ mActiveTracksGen++;
continue;
}
activeTrack->mState = TrackBase::ACTIVE;
@@ -4527,7 +4558,7 @@
lockEffectChains_l(effectChains);
}
- // thread mutex is now unlocked, mActiveTrack unknown, activeTrack != 0, kept, immutable
+ // thread mutex is now unlocked, mActiveTracks unknown, activeTrack != 0, kept, immutable
// activeTrack->mState unknown, activeTrackState immutable and is ACTIVE or RESUMING
for (size_t i = 0; i < effectChains.size(); i ++) {
@@ -4718,7 +4749,8 @@
sp<RecordTrack> track = mTracks[i];
track->invalidate();
}
- mActiveTrack.clear();
+ mActiveTracks.clear();
+ mActiveTracksGen++;
mStartStopCond.broadcast();
}
@@ -4877,8 +4909,9 @@
{
// This section is a rendezvous between binder thread executing start() and RecordThread
AutoMutex lock(mLock);
- if (mActiveTrack != 0) {
- if (recordTrack != mActiveTrack.get()) {
+ if (mActiveTracks.size() > 0) {
+ // FIXME does not work for multiple active tracks
+ if (mActiveTracks.indexOf(recordTrack) != 0) {
status = -EBUSY;
} else if (recordTrack->mState == TrackBase::PAUSING) {
recordTrack->mState = TrackBase::ACTIVE;
@@ -4888,13 +4921,15 @@
// FIXME why? already set in constructor, 'STARTING_1' would be more accurate
recordTrack->mState = TrackBase::IDLE;
- mActiveTrack = recordTrack;
+ mActiveTracks.add(recordTrack);
+ mActiveTracksGen++;
mLock.unlock();
status_t status = AudioSystem::startInput(mId);
mLock.lock();
// FIXME should verify that mActiveTrack is still == recordTrack
if (status != NO_ERROR) {
- mActiveTrack.clear();
+ mActiveTracks.remove(recordTrack);
+ mActiveTracksGen++;
clearSyncStartEvent();
return status;
}
@@ -4915,13 +4950,14 @@
mWaitWorkCV.broadcast();
// do not wait for mStartStopCond if exiting
if (exitPending()) {
- mActiveTrack.clear();
+ mActiveTracks.remove(recordTrack);
+ mActiveTracksGen++;
status = INVALID_OPERATION;
goto startError;
}
// FIXME incorrect usage of wait: no explicit predicate or loop
mStartStopCond.wait(mLock);
- if (mActiveTrack == 0) {
+ if (mActiveTracks.indexOf(recordTrack) < 0) {
ALOGV("Record failed to start");
status = BAD_VALUE;
goto startError;
@@ -4967,7 +5003,7 @@
bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
AutoMutex _l(mLock);
- if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
+ if (mActiveTracks.indexOf(recordTrack) != 0 || recordTrack->mState == TrackBase::PAUSING) {
return false;
}
// note that threadLoop may still be processing the track at this point [without lock]
@@ -4978,8 +5014,8 @@
}
// FIXME incorrect usage of wait: no explicit predicate or loop
mStartStopCond.wait(mLock);
- // if we have been restarted, recordTrack == mActiveTrack.get() here
- if (exitPending() || recordTrack != mActiveTrack.get()) {
+ // if we have been restarted, recordTrack is in mActiveTracks here
+ if (exitPending() || mActiveTracks.indexOf(recordTrack) != 0) {
ALOGV("Record stopped OK");
return true;
}
@@ -5022,7 +5058,7 @@
track->terminate();
track->mState = TrackBase::STOPPED;
// active tracks are removed by threadLoop()
- if (mActiveTrack != track) {
+ if (mActiveTracks.indexOf(track) < 0) {
removeTrack_l(track);
}
}
@@ -5049,7 +5085,7 @@
snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
result.append(buffer);
- if (mActiveTrack != 0) {
+ if (mActiveTracks.size() > 0) {
snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
result.append(buffer);
snprintf(buffer, SIZE, "Buffer size: %u bytes\n", mBufferSize);
@@ -5086,12 +5122,16 @@
}
}
- if (mActiveTrack != 0) {
+ size_t size = mActiveTracks.size();
+ if (size > 0) {
snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
result.append(buffer);
RecordTrack::appendDumpHeader(result);
- mActiveTrack->dump(buffer, SIZE);
- result.append(buffer);
+ for (size_t i = 0; i < size; ++i) {
+ sp<RecordTrack> track = mActiveTracks[i];
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
}
write(fd, result.string(), result.size());
@@ -5182,7 +5222,7 @@
// 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 (mActiveTrack != 0) {
+ if (mActiveTracks.size() > 0) {
status = INVALID_OPERATION;
} else {
reconfig = true;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index d31009e..6b81c38 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -938,9 +938,11 @@
AudioStreamIn *mInput;
SortedVector < sp<RecordTrack> > mTracks;
- // mActiveTrack has dual roles: it indicates the current active track, and
+ // mActiveTracks has dual roles: it indicates the current active track(s), and
// is used together with mStartStopCond to indicate start()/stop() progress
- sp<RecordTrack> mActiveTrack;
+ SortedVector< sp<RecordTrack> > mActiveTracks;
+ // generation counter for mActiveTracks
+ int mActiveTracksGen;
Condition mStartStopCond;
// updated by RecordThread::readInputParameters()