Merge "stagefright: don't update meta for the same buffer"
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3479f76..a8dcbe0 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -44,6 +44,7 @@
kStreamedFromLocalHost = 2,
kIsCachingDataSource = 4,
kIsHTTPBasedSource = 8,
+ kIsLocalFileSource = 16,
};
static sp<DataSource> CreateFromURI(
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index b6349e0..9f3bb5e 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -39,6 +39,10 @@
virtual status_t getSize(off64_t *size);
+ virtual uint32_t flags() {
+ return kIsLocalFileSource;
+ }
+
virtual sp<DecryptHandle> DrmInitialization(const char *mime);
virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 93e9a4b..ef55620 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -89,6 +89,8 @@
// Add a SynPoint derived from |event|.
void addSyncPoint_l(const ATSParser::SyncEvent &event);
+ status_t estimateDurationsFromTimesUsAtEnd();
+
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
};
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 844479e..4975d9a 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -90,6 +90,10 @@
return mParser->mFlags;
}
+ uint64_t firstPTS() const {
+ return mFirstPTS;
+ }
+
private:
struct StreamInfo {
unsigned mType;
@@ -135,6 +139,7 @@
void signalEOS(status_t finalResult);
+ SourceType getSourceType();
sp<MediaSource> getSource(SourceType type);
bool isAudio() const;
@@ -208,11 +213,12 @@
: mHasReturnedData(false), mOffset(offset), mTimeUs(0) {}
void ATSParser::SyncEvent::init(off64_t offset, const sp<MediaSource> &source,
- int64_t timeUs) {
+ int64_t timeUs, SourceType type) {
mHasReturnedData = true;
mOffset = offset;
mMediaSource = source;
mTimeUs = timeUs;
+ mType = type;
}
void ATSParser::SyncEvent::reset() {
@@ -1121,13 +1127,24 @@
int64_t timeUs;
if (accessUnit->meta()->findInt64("timeUs", &timeUs)) {
found = true;
- event->init(pesStartOffset, mSource, timeUs);
+ event->init(pesStartOffset, mSource, timeUs, getSourceType());
}
}
}
}
}
+ATSParser::SourceType ATSParser::Stream::getSourceType() {
+ if (isVideo()) {
+ return VIDEO;
+ } else if (isAudio()) {
+ return AUDIO;
+ } else if (isMeta()) {
+ return META;
+ }
+ return NUM_SOURCE_TYPES;
+}
+
sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
switch (type) {
case VIDEO:
@@ -1565,6 +1582,16 @@
return mPrograms.editItemAt(0)->PTSTimeDeltaEstablished();
}
+int64_t ATSParser::getFirstPTSTimeUs() {
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ sp<ATSParser::Program> program = mPrograms.itemAt(i);
+ if (program->PTSTimeDeltaEstablished()) {
+ return (program->firstPTS() * 100) / 9;
+ }
+ }
+ return -1;
+}
+
__attribute__((no_sanitize("integer")))
void ATSParser::updatePCR(
unsigned /* PID */, uint64_t PCR, uint64_t byteOffsetFromStart) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 2b166f0..faae6c9 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -62,18 +62,26 @@
ALIGNED_VIDEO_DATA = 2,
};
+ enum SourceType {
+ VIDEO = 0,
+ AUDIO = 1,
+ META = 2,
+ NUM_SOURCE_TYPES = 3
+ };
+
// Event is used to signal sync point event at feedTSPacket().
struct SyncEvent {
explicit SyncEvent(off64_t offset);
void init(off64_t offset, const sp<MediaSource> &source,
- int64_t timeUs);
+ int64_t timeUs, SourceType type);
bool hasReturnedData() const { return mHasReturnedData; }
void reset();
off64_t getOffset() const { return mOffset; }
const sp<MediaSource> &getMediaSource() const { return mMediaSource; }
int64_t getTimeUs() const { return mTimeUs; }
+ SourceType getType() const { return mType; }
private:
bool mHasReturnedData;
@@ -87,6 +95,7 @@
sp<MediaSource> mMediaSource;
/* The timestamp of the sync frame. */
int64_t mTimeUs;
+ SourceType mType;
};
explicit ATSParser(uint32_t flags = 0);
@@ -107,17 +116,13 @@
void signalEOS(status_t finalResult);
- enum SourceType {
- VIDEO = 0,
- AUDIO = 1,
- META = 2,
- NUM_SOURCE_TYPES = 3
- };
sp<MediaSource> getSource(SourceType type);
bool hasSource(SourceType type) const;
bool PTSTimeDeltaEstablished();
+ int64_t getFirstPTSTimeUs();
+
enum {
// From ISO/IEC 13818-1: 2000 (E), Table 2-29
STREAMTYPE_RESERVED = 0x00,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 4fcf7b5..548f44e 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -44,6 +44,7 @@
mEnabled(true),
mFormat(NULL),
mLastQueuedTimeUs(0),
+ mEstimatedBufferDurationUs(-1),
mEOSResult(OK),
mLatestEnqueuedMeta(NULL),
mLatestDequeuedMeta(NULL) {
@@ -309,6 +310,8 @@
mFormat = NULL;
mLatestEnqueuedMeta = NULL;
+
+ mEstimatedBufferDurationUs = -1;
}
void AnotherPacketSource::queueDiscontinuity(
@@ -431,6 +434,31 @@
return durationUs;
}
+int64_t AnotherPacketSource::getEstimatedBufferDurationUs() {
+ Mutex::Autolock autoLock(mLock);
+ if (mEstimatedBufferDurationUs >= 0) {
+ return mEstimatedBufferDurationUs;
+ }
+
+ SortedVector<int64_t> maxTimesUs;
+ List<sp<ABuffer> >::iterator it;
+ int64_t t1 = 0, t2 = 0;
+ for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+ int64_t timeUs = 0;
+ const sp<ABuffer> &buffer = *it;
+ if (!buffer->meta()->findInt64("timeUs", &timeUs)) {
+ continue;
+ }
+ maxTimesUs.add(timeUs);
+ while (maxTimesUs.size() > 2) {
+ maxTimesUs.removeAt(0);
+ t1 = maxTimesUs.itemAt(0);
+ t2 = maxTimesUs.itemAt(1);
+ }
+ }
+ return mEstimatedBufferDurationUs = t2 - t1;
+}
+
status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
*timeUs = 0;
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index dd6849e..b0890d7 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -57,6 +57,9 @@
// presentation timestamps since the last discontinuity (if any).
int64_t getBufferedDurationUs(status_t *finalResult);
+ // Returns the difference between the two largest timestamps queued
+ int64_t getEstimatedBufferDurationUs();
+
status_t nextBufferTime(int64_t *timeUs);
void queueAccessUnit(const sp<ABuffer> &buffer);
@@ -113,6 +116,7 @@
bool mEnabled;
sp<MetaData> mFormat;
int64_t mLastQueuedTimeUs;
+ int64_t mEstimatedBufferDurationUs;
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
sp<AMessage> mLatestEnqueuedMeta;
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 116a5bc..bde33dc 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -26,6 +26,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -40,6 +41,8 @@
namespace android {
static const size_t kTSPacketSize = 188;
+static const int kMaxDurationReadSize = 250000LL;
+static const int kMaxDurationRetry = 6;
struct MPEG2TSSource : public MediaSource {
MPEG2TSSource(
@@ -243,6 +246,8 @@
const sp<MetaData> meta = impl->getFormat();
meta->setInt64(kKeyDuration, durationUs);
impl->setFormat(meta);
+ } else {
+ estimateDurationsFromTimesUsAtEnd();
}
}
@@ -301,6 +306,106 @@
}
}
+status_t MPEG2TSExtractor::estimateDurationsFromTimesUsAtEnd() {
+ if (!(mDataSource->flags() & DataSource::kIsLocalFileSource)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ off64_t size = 0;
+ status_t err = mDataSource->getSize(&size);
+ if (err != OK) {
+ return err;
+ }
+
+ uint8_t packet[kTSPacketSize];
+ const off64_t zero = 0;
+ off64_t offset = max(zero, size - kMaxDurationReadSize);
+ if (mDataSource->readAt(offset, &packet, 0) < 0) {
+ return ERROR_IO;
+ }
+
+ int retry = 0;
+ bool allDurationsFound = false;
+ int64_t timeAnchorUs = mParser->getFirstPTSTimeUs();
+ do {
+ int bytesRead = 0;
+ sp<ATSParser> parser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
+ ATSParser::SyncEvent ev(0);
+ offset = max(zero, size - (kMaxDurationReadSize << retry));
+ offset = (offset / kTSPacketSize) * kTSPacketSize;
+ for (;;) {
+ if (bytesRead >= kMaxDurationReadSize << max(0, retry - 1)) {
+ break;
+ }
+
+ ssize_t n = mDataSource->readAt(offset, packet, kTSPacketSize);
+ if (n < 0) {
+ return n;
+ } else if (n < (ssize_t)kTSPacketSize) {
+ break;
+ }
+
+ offset += kTSPacketSize;
+ bytesRead += kTSPacketSize;
+ err = parser->feedTSPacket(packet, kTSPacketSize, &ev);
+ if (err != OK) {
+ return err;
+ }
+
+ if (ev.hasReturnedData()) {
+ int64_t durationUs = ev.getTimeUs();
+ ATSParser::SourceType type = ev.getType();
+ ev.reset();
+
+ int64_t firstTimeUs;
+ sp<AnotherPacketSource> src =
+ (AnotherPacketSource *)mParser->getSource(type).get();
+ if (src == NULL || src->nextBufferTime(&firstTimeUs) != OK) {
+ continue;
+ }
+ durationUs += src->getEstimatedBufferDurationUs();
+ durationUs -= timeAnchorUs;
+ durationUs -= firstTimeUs;
+ if (durationUs > 0) {
+ int64_t origDurationUs, lastDurationUs;
+ const sp<MetaData> meta = src->getFormat();
+ const uint32_t kKeyLastDuration = 'ldur';
+ // Require two consecutive duration calculations to be within 1 sec before
+ // updating; use MetaData to store previous duration estimate in per-stream
+ // context.
+ if (!meta->findInt64(kKeyDuration, &origDurationUs)
+ || !meta->findInt64(kKeyLastDuration, &lastDurationUs)
+ || (origDurationUs < durationUs
+ && abs(durationUs - lastDurationUs) < 60000000)) {
+ meta->setInt64(kKeyDuration, durationUs);
+ }
+ meta->setInt64(kKeyLastDuration, durationUs);
+ }
+ }
+ }
+
+ if (!allDurationsFound) {
+ allDurationsFound = true;
+ for (auto t: {ATSParser::VIDEO, ATSParser::AUDIO}) {
+ sp<AnotherPacketSource> src = (AnotherPacketSource *)mParser->getSource(t).get();
+ if (src == NULL) {
+ continue;
+ }
+ int64_t durationUs;
+ const sp<MetaData> meta = src->getFormat();
+ if (!meta->findInt64(kKeyDuration, &durationUs)) {
+ allDurationsFound = false;
+ break;
+ }
+ }
+ }
+
+ ++retry;
+ } while(!allDurationsFound && offset > 0 && retry <= kMaxDurationRetry);
+
+ return allDurationsFound? OK : ERROR_UNSUPPORTED;
+}
+
uint32_t MPEG2TSExtractor::flags() const {
return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD;
}
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 3ba7f62..54f9b95 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -73,8 +73,7 @@
ALOGI("loaded default module %s, handle %d", descriptor.properties.description,
descriptor.handle);
- sp<ISoundTriggerClient> client;
- sp<Module> module = new Module(this, halInterface, descriptor, client);
+ sp<Module> module = new Module(this, halInterface, descriptor);
mModules.add(descriptor.handle, module);
mCallbackThread = new CallbackThread(this);
}
@@ -126,11 +125,13 @@
}
sp<Module> module = mModules.valueAt(index);
- module->setClient(client);
- IInterface::asBinder(client)->linkToDeath(module);
- moduleInterface = module;
+ sp<ModuleClient> moduleClient = module->addClient(client);
+ if (moduleClient == 0) {
+ return NO_INIT;
+ }
- module->setCaptureState_l(mCaptureState);
+ moduleClient->setCaptureState_l(mCaptureState);
+ moduleInterface = moduleClient;
return NO_ERROR;
}
@@ -147,14 +148,6 @@
}
-void SoundTriggerHwService::detachModule(const sp<Module>& module)
-{
- ALOGV("detachModule");
- AutoMutex lock(mServiceLock);
- module->clearClient();
-}
-
-
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 60000;
@@ -276,8 +269,10 @@
return;
}
- sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
- eventMemory, strongModule));
+ sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
+ eventMemory);
+ callbackEvent->setModule(strongModule);
+ sendCallbackEvent_l(callbackEvent);
}
// static
@@ -329,8 +324,10 @@
if (strongModule == 0) {
return;
}
- sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
- eventMemory, strongModule));
+ sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
+ eventMemory);
+ callbackEvent->setModule(strongModule);
+ sendCallbackEvent_l(callbackEvent);
}
@@ -366,8 +363,23 @@
if (strongModule == 0) {
return;
}
- sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
- eventMemory, strongModule));
+ sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
+ eventMemory);
+ callbackEvent->setModule(strongModule);
+ sendCallbackEvent_l(callbackEvent);
+}
+
+void SoundTriggerHwService::sendServiceStateEvent_l(sound_trigger_service_state_t state,
+ ModuleClient *moduleClient)
+{
+ sp<IMemory> eventMemory = prepareServiceStateEvent_l(state);
+ if (eventMemory == 0) {
+ return;
+ }
+ sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
+ eventMemory);
+ callbackEvent->setModuleClient(moduleClient);
+ sendCallbackEvent_l(callbackEvent);
}
// call with mServiceLock held
@@ -380,14 +392,25 @@
{
ALOGV("onCallbackEvent");
sp<Module> module;
+ sp<ModuleClient> moduleClient;
{
AutoMutex lock(mServiceLock);
+ //CallbackEvent is either for Module or ModuleClient
module = event->mModule.promote();
if (module == 0) {
- return;
+ moduleClient = event->mModuleClient.promote();
+ if (moduleClient == 0) {
+ return;
+ }
}
}
- module->onCallbackEvent(event);
+ if (module != 0) {
+ ALOGV("onCallbackEvent for module");
+ module->onCallbackEvent(event);
+ } else if (moduleClient != 0) {
+ ALOGV("onCallbackEvent for moduleClient");
+ moduleClient->onCallbackEvent(event);
+ }
{
AutoMutex lock(mServiceLock);
// clear now to execute with mServiceLock locked
@@ -457,9 +480,8 @@
mCallbackCond.signal();
}
-SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory,
- wp<Module> module)
- : mType(type), mMemory(memory), mModule(module)
+SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory)
+ : mType(type), mMemory(memory)
{
}
@@ -473,25 +495,59 @@
SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
const sp<SoundTriggerHalInterface>& halInterface,
- sound_trigger_module_descriptor descriptor,
- const sp<ISoundTriggerClient>& client)
+ sound_trigger_module_descriptor descriptor)
: mService(service), mHalInterface(halInterface), mDescriptor(descriptor),
- mClient(client), mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
+ mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
{
}
SoundTriggerHwService::Module::~Module() {
+ mModuleClients.clear();
}
-void SoundTriggerHwService::Module::detach() {
- ALOGV("detach()");
- if (!captureHotwordAllowed()) {
+sp<SoundTriggerHwService::ModuleClient>
+SoundTriggerHwService::Module::addClient(const sp<ISoundTriggerClient>& client)
+{
+ AutoMutex lock(mLock);
+ sp<ModuleClient> moduleClient;
+
+ for (size_t i = 0; i < mModuleClients.size(); i++) {
+ if (mModuleClients[i]->client() == client) {
+ // Client already present, reuse client
+ return moduleClient;
+ }
+ }
+ moduleClient = new ModuleClient(this, client);
+
+ ALOGV("addClient() client %p", moduleClient.get());
+ mModuleClients.add(moduleClient);
+
+ return moduleClient;
+}
+
+void SoundTriggerHwService::Module::detach(const sp<ModuleClient>& moduleClient)
+{
+ ALOGV("Module::detach()");
+ AutoMutex lock(mLock);
+ ssize_t index = -1;
+
+ for (size_t i = 0; i < mModuleClients.size(); i++) {
+ if (mModuleClients[i] == moduleClient) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
return;
}
- {
- AutoMutex lock(mLock);
- for (size_t i = 0; i < mModels.size(); i++) {
- sp<Model> model = mModels.valueAt(i);
+
+ ALOGV("remove client %p", moduleClient.get());
+ mModuleClients.removeAt(index);
+
+ for (size_t i = 0; i < mModels.size(); i++) {
+ sp<Model> model = mModels.valueAt(i);
+ if (moduleClient == model->mModuleClient) {
+ mModels.removeItemsAt(i);
ALOGV("detach() unloading model %d", model->mHandle);
if (mHalInterface != 0) {
if (model->mState == Model::STATE_ACTIVE) {
@@ -499,29 +555,20 @@
}
mHalInterface->unloadSoundModel(model->mHandle);
}
+ AudioSystem::releaseSoundTriggerSession(model->mCaptureSession);
+ mHalInterface->unloadSoundModel(model->mHandle);
}
- mModels.clear();
}
- if (mClient != 0) {
- IInterface::asBinder(mClient)->unlinkToDeath(this);
- }
- sp<SoundTriggerHwService> service = mService.promote();
- if (service == 0) {
- return;
- }
- service->detachModule(this);
}
status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory,
- sound_model_handle_t *handle)
+ sp<ModuleClient> moduleClient,
+ sound_model_handle_t *handle)
{
ALOGV("loadSoundModel() handle");
if (mHalInterface == 0) {
return NO_INIT;
}
- if (!captureHotwordAllowed()) {
- return PERMISSION_DENIED;
- }
if (modelMemory == 0 || modelMemory->pointer() == NULL) {
ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()");
return BAD_VALUE;
@@ -569,7 +616,8 @@
return status;
}
- sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type);
+ sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type,
+ moduleClient);
mModels.replaceValueFor(*handle, model);
return status;
@@ -578,10 +626,6 @@
status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle)
{
ALOGV("unloadSoundModel() model handle %d", handle);
- if (!captureHotwordAllowed()) {
- return PERMISSION_DENIED;
- }
-
AutoMutex lock(mLock);
return unloadSoundModel_l(handle);
}
@@ -612,10 +656,6 @@
if (mHalInterface == 0) {
return NO_INIT;
}
- if (!captureHotwordAllowed()) {
- return PERMISSION_DENIED;
- }
-
if (dataMemory == 0 || dataMemory->pointer() == NULL) {
ALOGE("startRecognition() dataMemory is 0 or has NULL pointer()");
return BAD_VALUE;
@@ -668,10 +708,6 @@
if (mHalInterface == 0) {
return NO_INIT;
}
- if (!captureHotwordAllowed()) {
- return PERMISSION_DENIED;
- }
-
AutoMutex lock(mLock);
sp<Model> model = getModel(handle);
if (model == 0) {
@@ -686,7 +722,6 @@
return NO_ERROR;
}
-
void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
{
ALOGV("onCallbackEvent type %d", event->mType);
@@ -696,8 +731,8 @@
if (eventMemory == 0 || eventMemory->pointer() == NULL) {
return;
}
- if (mClient == 0) {
- ALOGI("%s mClient == 0", __func__);
+ if (mModuleClients.isEmpty()) {
+ ALOGI("%s no clients", __func__);
return;
}
@@ -720,7 +755,7 @@
recognitionEvent->capture_session = model->mCaptureSession;
model->mState = Model::STATE_IDLE;
- client = mClient;
+ client = model->mModuleClient->client();
}
if (client != 0) {
client->onRecognitionEvent(eventMemory);
@@ -737,20 +772,24 @@
ALOGW("%s model == 0", __func__);
return;
}
- client = mClient;
+ client = model->mModuleClient->client();
}
if (client != 0) {
client->onSoundModelEvent(eventMemory);
}
} break;
case CallbackEvent::TYPE_SERVICE_STATE: {
- sp<ISoundTriggerClient> client;
+ Vector< sp<ISoundTriggerClient> > clients;
{
AutoMutex lock(mLock);
- client = mClient;
+ for (size_t i = 0; i < mModuleClients.size(); i++) {
+ if (mModuleClients[i] != 0) {
+ clients.add(mModuleClients[i]->client());
+ }
+ }
}
- if (client != 0) {
- client->onServiceStateChange(eventMemory);
+ for (size_t i = 0; i < clients.size(); i++) {
+ clients[i]->onServiceStateChange(eventMemory);
}
} break;
default:
@@ -769,12 +808,6 @@
return model;
}
-void SoundTriggerHwService::Module::binderDied(
- const wp<IBinder> &who __unused) {
- ALOGW("client binder died for module %d", mDescriptor.handle);
- detach();
-}
-
// Called with mServiceLock held
void SoundTriggerHwService::Module::setCaptureState_l(bool active)
{
@@ -858,8 +891,10 @@
}
for (size_t i = 0; i < events.size(); i++) {
- service->sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, events[i],
- this));
+ sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
+ events[i]);
+ callbackEvent->setModule(this);
+ service->sendCallbackEvent_l(callbackEvent);
}
exit:
@@ -869,17 +904,170 @@
SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t session,
audio_io_handle_t ioHandle, audio_devices_t device,
- sound_trigger_sound_model_type_t type) :
+ sound_trigger_sound_model_type_t type,
+ sp<ModuleClient>& moduleClient) :
mHandle(handle), mState(STATE_IDLE), mCaptureSession(session),
- mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type)
+ mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type),
+ mModuleClient(moduleClient)
{
-
}
-status_t SoundTriggerHwService::Module::dump(int fd __unused,
- const Vector<String16>& args __unused) {
+#undef LOG_TAG
+#define LOG_TAG "SoundTriggerHwService::ModuleClient"
+
+SoundTriggerHwService::ModuleClient::ModuleClient(const sp<Module>& module,
+ const sp<ISoundTriggerClient>& client)
+ : mModule(module), mClient(client)
+{
+}
+
+void SoundTriggerHwService::ModuleClient::onFirstRef()
+{
+ IInterface::asBinder(mClient)->linkToDeath(this);
+}
+
+SoundTriggerHwService::ModuleClient::~ModuleClient()
+{
+}
+
+status_t SoundTriggerHwService::ModuleClient::dump(int fd __unused,
+ const Vector<String16>& args __unused) {
String8 result;
return NO_ERROR;
}
+void SoundTriggerHwService::ModuleClient::detach() {
+ ALOGV("detach()");
+ if (!captureHotwordAllowed()) {
+ return;
+ }
+
+ {
+ AutoMutex lock(mLock);
+ if (mClient != 0) {
+ IInterface::asBinder(mClient)->unlinkToDeath(this);
+ mClient.clear();
+ }
+ }
+
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return;
+ }
+ module->detach(this);
+}
+
+status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle)
+{
+ ALOGV("loadSoundModel() handle");
+ if (!captureHotwordAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return NO_INIT;
+ }
+ return module->loadSoundModel(modelMemory, this, handle);
+}
+
+status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle)
+{
+ ALOGV("unloadSoundModel() model handle %d", handle);
+ if (!captureHotwordAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return NO_INIT;
+ }
+ return module->unloadSoundModel(handle);
+}
+
+status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory)
+{
+ ALOGV("startRecognition() model handle %d", handle);
+ if (!captureHotwordAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return NO_INIT;
+ }
+ return module->startRecognition(handle, dataMemory);
+}
+
+status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle)
+{
+ ALOGV("stopRecognition() model handle %d", handle);
+ if (!captureHotwordAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return NO_INIT;
+ }
+ return module->stopRecognition(handle);
+}
+
+void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active)
+{
+ ALOGV("ModuleClient::setCaptureState_l %d", active);
+ sp<SoundTriggerHwService> service;
+ sound_trigger_service_state_t state;
+
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return;
+ }
+ {
+ AutoMutex lock(mLock);
+ state = (active && !module->isConcurrentCaptureAllowed()) ?
+ SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED;
+
+ service = module->service().promote();
+ if (service == 0) {
+ return;
+ }
+ }
+ service->sendServiceStateEvent_l(state, this);
+}
+
+void SoundTriggerHwService::ModuleClient::onCallbackEvent(const sp<CallbackEvent>& event)
+{
+ ALOGV("ModuleClient onCallbackEvent type %d", event->mType);
+
+ sp<IMemory> eventMemory = event->mMemory;
+
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ return;
+ }
+
+ switch (event->mType) {
+ case CallbackEvent::TYPE_SERVICE_STATE: {
+ sp<ISoundTriggerClient> client;
+ {
+ AutoMutex lock(mLock);
+ client = mClient;
+ }
+ if (client !=0 ) {
+ client->onServiceStateChange(eventMemory);
+ }
+ } break;
+ default:
+ LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType);
+ }
+}
+
+void SoundTriggerHwService::ModuleClient::binderDied(
+ const wp<IBinder> &who __unused) {
+ ALOGW("client binder died for client %p", this);
+ detach();
+}
+
}; // namespace android
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index 7f7d0cc..60ebb35 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -39,6 +39,7 @@
friend class BinderService<SoundTriggerHwService>;
public:
class Module;
+ class ModuleClient;
static char const* getServiceName() { return "media.sound_trigger_hw"; }
@@ -69,7 +70,8 @@
};
Model(sound_model_handle_t handle, audio_session_t session, audio_io_handle_t ioHandle,
- audio_devices_t device, sound_trigger_sound_model_type_t type);
+ audio_devices_t device, sound_trigger_sound_model_type_t type,
+ sp<ModuleClient>& moduleClient);
~Model() {}
sound_model_handle_t mHandle;
@@ -79,6 +81,7 @@
audio_devices_t mCaptureDevice;
sound_trigger_sound_model_type_t mType;
struct sound_trigger_recognition_config mConfig;
+ sp<ModuleClient> mModuleClient;
};
class CallbackEvent : public RefBase {
@@ -88,27 +91,76 @@
TYPE_SOUNDMODEL,
TYPE_SERVICE_STATE,
} event_type;
- CallbackEvent(event_type type, sp<IMemory> memory, wp<Module> module);
+ CallbackEvent(event_type type, sp<IMemory> memory);
virtual ~CallbackEvent();
+ void setModule(wp<Module> module) { mModule = module; }
+ void setModuleClient(wp<ModuleClient> moduleClient) { mModuleClient = moduleClient; }
+
event_type mType;
sp<IMemory> mMemory;
wp<Module> mModule;
+ wp<ModuleClient> mModuleClient;
};
- class Module : public virtual RefBase,
- public BnSoundTrigger,
- public IBinder::DeathRecipient {
+ class Module : public RefBase {
public:
Module(const sp<SoundTriggerHwService>& service,
const sp<SoundTriggerHalInterface>& halInterface,
- sound_trigger_module_descriptor descriptor,
- const sp<ISoundTriggerClient>& client);
+ sound_trigger_module_descriptor descriptor);
virtual ~Module();
+ virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
+ sp<ModuleClient> moduleClient,
+ sound_model_handle_t *handle);
+
+ virtual status_t unloadSoundModel(sound_model_handle_t handle);
+
+ virtual status_t startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory);
+ virtual status_t stopRecognition(sound_model_handle_t handle);
+
+ sp<SoundTriggerHalInterface> halInterface() const { return mHalInterface; }
+ struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
+ wp<SoundTriggerHwService> service() const { return mService; }
+ bool isConcurrentCaptureAllowed() const { return mDescriptor.properties.concurrent_capture; }
+
+ sp<Model> getModel(sound_model_handle_t handle);
+
+ void setCaptureState_l(bool active);
+
+ sp<ModuleClient> addClient(const sp<ISoundTriggerClient>& client);
+
+ void detach(const sp<ModuleClient>& moduleClient);
+
+ void onCallbackEvent(const sp<CallbackEvent>& event);
+
+ private:
+
+ status_t unloadSoundModel_l(sound_model_handle_t handle);
+
+ Mutex mLock;
+ wp<SoundTriggerHwService> mService;
+ sp<SoundTriggerHalInterface> mHalInterface;
+ struct sound_trigger_module_descriptor mDescriptor;
+ Vector< sp<ModuleClient> > mModuleClients;
+ DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels;
+ sound_trigger_service_state_t mServiceState;
+ }; // class Module
+
+ class ModuleClient : public virtual RefBase,
+ public BnSoundTrigger,
+ public IBinder::DeathRecipient {
+ public:
+
+ ModuleClient(const sp<Module>& module,
+ const sp<ISoundTriggerClient>& client);
+
+ virtual ~ModuleClient();
+
virtual void detach();
virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
@@ -122,35 +174,23 @@
virtual status_t dump(int fd, const Vector<String16>& args);
-
- struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
- void setClient(const sp<ISoundTriggerClient>& client) { mClient = client; }
- void clearClient() { mClient.clear(); }
- sp<ISoundTriggerClient> client() const { return mClient; }
- wp<SoundTriggerHwService> service() const { return mService; }
-
- void onCallbackEvent(const sp<CallbackEvent>& event);
-
- sp<Model> getModel(sound_model_handle_t handle);
-
- void setCaptureState_l(bool active);
+ virtual void onFirstRef();
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
+ void onCallbackEvent(const sp<CallbackEvent>& event);
+
+ void setCaptureState_l(bool active);
+
+ sp<ISoundTriggerClient> client() const { return mClient; }
+
private:
- status_t unloadSoundModel_l(sound_model_handle_t handle);
-
-
- Mutex mLock;
- wp<SoundTriggerHwService> mService;
- sp<SoundTriggerHalInterface> mHalInterface;
- struct sound_trigger_module_descriptor mDescriptor;
- sp<ISoundTriggerClient> mClient;
- DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels;
- sound_trigger_service_state_t mServiceState;
- }; // class Module
+ mutable Mutex mLock;
+ wp<Module> mModule;
+ sp<ISoundTriggerClient> mClient;
+ }; // class ModuleClient
class CallbackThread : public Thread {
public:
@@ -175,8 +215,6 @@
Vector< sp<CallbackEvent> > mEventQueue;
};
- void detachModule(const sp<Module>& module);
-
static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie);
sp<IMemory> prepareRecognitionEvent_l(struct sound_trigger_recognition_event *event);
void sendRecognitionEvent(struct sound_trigger_recognition_event *event, Module *module);
@@ -187,6 +225,8 @@
sp<IMemory> prepareServiceStateEvent_l(sound_trigger_service_state_t state);
void sendServiceStateEvent_l(sound_trigger_service_state_t state, Module *module);
+ void sendServiceStateEvent_l(sound_trigger_service_state_t state,
+ ModuleClient *moduleClient);
void sendCallbackEvent_l(const sp<CallbackEvent>& event);
void onCallbackEvent(const sp<CallbackEvent>& event);