Merge "NuPlayer: handle audio output format change in a clean way."
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
index 888862a..36e3927 100644
--- a/camera/cameraserver/Android.mk
+++ b/camera/cameraserver/Android.mk
@@ -22,7 +22,6 @@
LOCAL_SHARED_LIBRARIES := \
libcameraservice \
liblog \
- libcutils \
libutils \
libbinder \
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index b28d509..9a236fc 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -124,29 +124,6 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- sf2.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libstagefright liblog libutils libbinder libstagefright_foundation \
- libmedia libgui libcutils
-
-LOCAL_C_INCLUDES:= \
- frameworks/av/media/libstagefright \
- $(TOP)/frameworks/native/include/media/openmax
-
-LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE:= sf2
-
-include $(BUILD_EXECUTABLE)
-
-################################################################################
-
-include $(CLEAR_VARS)
-
LOCAL_SRC_FILES:= \
codec.cpp \
SimplePlayer.cpp \
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 26135d7..3108a67 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -32,7 +32,6 @@
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
@@ -401,8 +400,6 @@
ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
-
sp<ALooper> looper = new ALooper;
looper->start();
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index 410dd69..f219e69 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -30,7 +30,6 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/NuMediaExtractor.h>
#include <media/stagefright/RenderScriptWrapper.h>
@@ -738,8 +737,6 @@
ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
-
android::sp<ALooper> looper = new ALooper;
looper->start();
diff --git a/cmds/stagefright/muxer.cpp b/cmds/stagefright/muxer.cpp
index 0a3bdf3..4a83a4a 100644
--- a/cmds/stagefright/muxer.cpp
+++ b/cmds/stagefright/muxer.cpp
@@ -29,7 +29,6 @@
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaMuxer.h>
@@ -319,9 +318,6 @@
}
ProcessState::self()->startThreadPool();
- // Make sure setDataSource() works.
- DataSource::RegisterDefaultSniffers();
-
sp<ALooper> looper = new ALooper;
looper->start();
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 9aa0156..94c2e96 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -170,8 +170,6 @@
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
-
#if 1
if (argc != 3) {
fprintf(stderr, "usage: %s <filename> <input_color_format>\n", argv[0]);
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
deleted file mode 100644
index 76dbb78..0000000
--- a/cmds/stagefright/sf2.cpp
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "sf2"
-#include <inttypes.h>
-#include <utils/Log.h>
-
-#include <signal.h>
-
-#include <binder/ProcessState.h>
-
-#include <media/IMediaHTTPService.h>
-
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/AMessage.h>
-
-#include <media/stagefright/ACodec.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MediaExtractor.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
-
-#include <gui/SurfaceComposerClient.h>
-#include <gui/Surface.h>
-
-#include "include/ESDS.h"
-
-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)
- : mURI(uri),
- mDecodeAudio(decodeAudio),
- mSurface(surface),
- mRenderToSurface(renderToSurface),
- mCodec(new ACodec),
- mIsVorbis(false) {
- CHECK(!mDecodeAudio || mSurface == NULL);
- }
-
- void startAsync() {
- (new AMessage(kWhatStart, this))->post();
- }
-
-protected:
- virtual ~Controller() {
- }
-
- virtual void printStatistics() {
- int64_t delayUs = ALooper::GetNowUs() - mStartTimeUs;
-
- if (mDecodeAudio) {
- printf("%" PRId64 " bytes received. %.2f KB/sec\n",
- mTotalBytesReceived,
- mTotalBytesReceived * 1E6 / 1024 / delayUs);
- } else {
- printf("%d frames decoded, %.2f fps. %" PRId64 " 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, this))->post();
- ctrlc = false;
- }
- switch (msg->what()) {
- case kWhatStart:
- {
-#if 1
- mDecodeLooper = looper();
-#else
- mDecodeLooper = new ALooper;
- mDecodeLooper->setName("sf2 decode looper");
- mDecodeLooper->start();
-#endif
-
- sp<DataSource> dataSource =
- DataSource::CreateFromURI(
- NULL /* httpService */, mURI.c_str());
-
- sp<IMediaExtractor> extractor =
- MediaExtractor::Create(dataSource);
-
- for (size_t i = 0; i < extractor->countTracks(); ++i) {
- sp<MetaData> meta = extractor->getTrackMetaData(i);
-
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- if (!strncasecmp(mDecodeAudio ? "audio/" : "video/",
- mime, 6)) {
- mSource = extractor->getTrack(i);
-
- if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
- mIsVorbis = true;
- } else {
- mIsVorbis = false;
- }
- break;
- }
- }
- if (mSource == NULL) {
- printf("no %s track found\n", mDecodeAudio ? "audio" : "video");
- exit (1);
- }
-
- CHECK_EQ(mSource->start(), (status_t)OK);
-
- mDecodeLooper->registerHandler(mCodec);
-
- mCodec->setNotificationMessage(
- new AMessage(kWhatCodecNotify, this));
-
- sp<AMessage> format = makeFormat(mSource->getFormat());
-
- if (mSurface != NULL) {
- format->setObject("surface", mSurface);
- }
-
- mCodec->initiateSetup(format);
-
- mCSDIndex = 0;
- mStartTimeUs = ALooper::GetNowUs();
- mNumOutputBuffersReceived = 0;
- mTotalBytesReceived = 0;
- mLeftOverBuffer = NULL;
- mFinalResult = OK;
- mSeekState = SEEK_NONE;
-
- // (new AMessage(kWhatSeek, this))->post(5000000ll);
- break;
- }
-
- case kWhatSeek:
- {
- printf("+");
- fflush(stdout);
-
- CHECK(mSeekState == SEEK_NONE
- || mSeekState == SEEK_FLUSH_COMPLETED);
-
- if (mLeftOverBuffer != NULL) {
- mLeftOverBuffer->release();
- mLeftOverBuffer = NULL;
- }
-
- mSeekState = SEEK_FLUSHING;
- mSeekTimeUs = 30000000ll;
-
- mCodec->signalFlush();
- break;
- }
-
- case kWhatStop:
- {
- if (mLeftOverBuffer != NULL) {
- mLeftOverBuffer->release();
- mLeftOverBuffer = NULL;
- }
-
- CHECK_EQ(mSource->stop(), (status_t)OK);
- mSource.clear();
-
- mCodec->initiateShutdown();
- break;
- }
-
- case kWhatCodecNotify:
- {
- int32_t what;
- CHECK(msg->findInt32("what", &what));
-
- if (what == CodecBase::kWhatFillThisBuffer) {
- onFillThisBuffer(msg);
- } else if (what == CodecBase::kWhatDrainThisBuffer) {
- if ((mNumOutputBuffersReceived++ % 16) == 0) {
- printf(".");
- fflush(stdout);
- }
-
- onDrainThisBuffer(msg);
- } else if (what == CodecBase::kWhatEOS
- || what == CodecBase::kWhatError) {
- printf((what == CodecBase::kWhatEOS) ? "$\n" : "E\n");
-
- printStatistics();
- (new AMessage(kWhatStop, this))->post();
- } else if (what == CodecBase::kWhatFlushCompleted) {
- mSeekState = SEEK_FLUSH_COMPLETED;
- mCodec->signalResume();
-
- (new AMessage(kWhatSeek, this))->post(5000000ll);
- } else if (what == CodecBase::kWhatStopCompleted ||
- what == CodecBase::kWhatReleaseCompleted) {
- mDecodeLooper->unregisterHandler(mCodec->id());
-
- if (mDecodeLooper != looper()) {
- mDecodeLooper->stop();
- }
-
- looper()->stop();
- }
- break;
- }
-
- default:
- TRESPASS();
- break;
- }
- }
-
-private:
- enum {
- kWhatStart = 'strt',
- kWhatStop = 'stop',
- kWhatCodecNotify = 'noti',
- kWhatSeek = 'seek',
- };
-
- sp<ALooper> mDecodeLooper;
-
- AString mURI;
- bool mDecodeAudio;
- sp<Surface> mSurface;
- bool mRenderToSurface;
- sp<ACodec> mCodec;
- sp<IMediaSource> mSource;
- bool mIsVorbis;
-
- Vector<sp<ABuffer> > mCSD;
- size_t mCSDIndex;
-
- MediaBuffer *mLeftOverBuffer;
- status_t mFinalResult;
-
- int64_t mStartTimeUs;
- int32_t mNumOutputBuffersReceived;
- int64_t mTotalBytesReceived;
-
- enum SeekState {
- SEEK_NONE,
- SEEK_FLUSHING,
- SEEK_FLUSH_COMPLETED,
- };
- SeekState mSeekState;
- int64_t mSeekTimeUs;
-
- sp<AMessage> makeFormat(const sp<MetaData> &meta) {
- CHECK(mCSD.isEmpty());
-
- const char *mime;
- CHECK(meta->findCString(kKeyMIMEType, &mime));
-
- sp<AMessage> msg = new AMessage;
- msg->setString("mime", mime);
-
- if (!strncasecmp("video/", mime, 6)) {
- int32_t width, height;
- CHECK(meta->findInt32(kKeyWidth, &width));
- CHECK(meta->findInt32(kKeyHeight, &height));
-
- msg->setInt32("width", width);
- msg->setInt32("height", height);
- } else {
- CHECK(!strncasecmp("audio/", mime, 6));
-
- int32_t numChannels, sampleRate;
- CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
- CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-
- msg->setInt32("channel-count", numChannels);
- msg->setInt32("sample-rate", sampleRate);
-
- int32_t isADTS;
- if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
- msg->setInt32("is-adts", true);
- }
- }
-
- uint32_t type;
- const void *data;
- size_t size;
- if (meta->findData(kKeyAVCC, &type, &data, &size)) {
- // Parse the AVCDecoderConfigurationRecord
-
- const uint8_t *ptr = (const uint8_t *)data;
-
- CHECK(size >= 7);
- CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
- uint8_t profile __unused = ptr[1];
- uint8_t level __unused = ptr[3];
-
- // There is decodable content out there that fails the following
- // assertion, let's be lenient for now...
- // CHECK((ptr[4] >> 2) == 0x3f); // reserved
-
- size_t lengthSize __unused = 1 + (ptr[4] & 3);
-
- // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
- // violates it...
- // CHECK((ptr[5] >> 5) == 7); // reserved
-
- size_t numSeqParameterSets = ptr[5] & 31;
-
- ptr += 6;
- size -= 6;
-
- sp<ABuffer> buffer = new ABuffer(1024);
- buffer->setRange(0, 0);
-
- for (size_t i = 0; i < numSeqParameterSets; ++i) {
- CHECK(size >= 2);
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- CHECK(size >= length);
-
- memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
- memcpy(buffer->data() + buffer->size() + 4, ptr, length);
- buffer->setRange(0, buffer->size() + 4 + length);
-
- ptr += length;
- size -= length;
- }
-
- buffer->meta()->setInt32("csd", true);
- mCSD.push(buffer);
-
- buffer = new ABuffer(1024);
- buffer->setRange(0, 0);
-
- CHECK(size >= 1);
- size_t numPictureParameterSets = *ptr;
- ++ptr;
- --size;
-
- for (size_t i = 0; i < numPictureParameterSets; ++i) {
- CHECK(size >= 2);
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- CHECK(size >= length);
-
- memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
- memcpy(buffer->data() + buffer->size() + 4, ptr, length);
- buffer->setRange(0, buffer->size() + 4 + length);
-
- ptr += length;
- size -= length;
- }
-
- buffer->meta()->setInt32("csd", true);
- mCSD.push(buffer);
-
- msg->setBuffer("csd", buffer);
- } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
- ESDS esds((const char *)data, size);
- CHECK_EQ(esds.InitCheck(), (status_t)OK);
-
- const void *codec_specific_data;
- size_t codec_specific_data_size;
- esds.getCodecSpecificInfo(
- &codec_specific_data, &codec_specific_data_size);
-
- sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
-
- memcpy(buffer->data(), codec_specific_data,
- codec_specific_data_size);
-
- buffer->meta()->setInt32("csd", true);
- mCSD.push(buffer);
- } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
- sp<ABuffer> buffer = new ABuffer(size);
- memcpy(buffer->data(), data, size);
-
- buffer->meta()->setInt32("csd", true);
- mCSD.push(buffer);
-
- CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
-
- buffer = new ABuffer(size);
- memcpy(buffer->data(), data, size);
-
- buffer->meta()->setInt32("csd", true);
- mCSD.push(buffer);
- }
-
- int32_t maxInputSize;
- if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
- msg->setInt32("max-input-size", maxInputSize);
- }
-
- return msg;
- }
-
- void onFillThisBuffer(const sp<AMessage> &msg) {
- sp<AMessage> reply;
- CHECK(msg->findMessage("reply", &reply));
-
- if (mSource == NULL || mSeekState == SEEK_FLUSHING) {
- reply->setInt32("err", ERROR_END_OF_STREAM);
- reply->post();
- return;
- }
-
- sp<ABuffer> outBuffer;
- CHECK(msg->findBuffer("buffer", &outBuffer));
-
- if (mCSDIndex < mCSD.size()) {
- outBuffer = mCSD.editItemAt(mCSDIndex++);
- outBuffer->meta()->setInt64("timeUs", 0);
- } else {
- size_t sizeLeft = outBuffer->capacity();
- outBuffer->setRange(0, 0);
-
- int32_t n = 0;
-
- for (;;) {
- MediaBuffer *inBuffer;
-
- if (mLeftOverBuffer != NULL) {
- inBuffer = mLeftOverBuffer;
- mLeftOverBuffer = NULL;
- } else if (mFinalResult != OK) {
- break;
- } else {
- MediaSource::ReadOptions options;
- if (mSeekState == SEEK_FLUSH_COMPLETED) {
- options.setSeekTo(mSeekTimeUs);
- mSeekState = SEEK_NONE;
- }
- status_t err = mSource->read(&inBuffer, &options);
-
- if (err != OK) {
- mFinalResult = err;
- break;
- }
- }
-
- size_t sizeNeeded = inBuffer->range_length();
- if (mIsVorbis) {
- // Vorbis data is suffixed with the number of
- // valid samples on the page.
- sizeNeeded += sizeof(int32_t);
- }
-
- if (sizeNeeded > sizeLeft) {
- if (outBuffer->size() == 0) {
- ALOGE("Unable to fit even a single input buffer of size %zu.",
- sizeNeeded);
- }
- CHECK_GT(outBuffer->size(), 0u);
-
- mLeftOverBuffer = inBuffer;
- break;
- }
-
- ++n;
-
- if (outBuffer->size() == 0) {
- int64_t timeUs;
- CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
-
- outBuffer->meta()->setInt64("timeUs", timeUs);
- }
-
- memcpy(outBuffer->data() + outBuffer->size(),
- (const uint8_t *)inBuffer->data()
- + inBuffer->range_offset(),
- inBuffer->range_length());
-
- if (mIsVorbis) {
- int32_t numPageSamples;
- if (!inBuffer->meta_data()->findInt32(
- kKeyValidSamples, &numPageSamples)) {
- numPageSamples = -1;
- }
-
- memcpy(outBuffer->data()
- + outBuffer->size() + inBuffer->range_length(),
- &numPageSamples, sizeof(numPageSamples));
- }
-
- outBuffer->setRange(
- 0, outBuffer->size() + sizeNeeded);
-
- sizeLeft -= sizeNeeded;
-
- inBuffer->release();
- inBuffer = NULL;
-
- break; // Don't coalesce
- }
-
- ALOGV("coalesced %d input buffers", n);
-
- if (outBuffer->size() == 0) {
- CHECK_NE(mFinalResult, (status_t)OK);
-
- reply->setInt32("err", mFinalResult);
- reply->post();
- return;
- }
- }
-
- reply->setBuffer("buffer", outBuffer);
- reply->post();
- }
-
- void onDrainThisBuffer(const sp<AMessage> &msg) {
- sp<ABuffer> buffer;
- CHECK(msg->findBuffer("buffer", &buffer));
-
- mTotalBytesReceived += buffer->size();
-
- sp<AMessage> reply;
- CHECK(msg->findMessage("reply", &reply));
-
- if (mRenderToSurface) {
- reply->setInt32("render", 1);
- }
-
- reply->post();
- }
-
- DISALLOW_EVIL_CONSTRUCTORS(Controller);
-};
-
-static void usage(const char *me) {
- fprintf(stderr, "usage: %s\n", me);
- fprintf(stderr, " -h(elp)\n");
- fprintf(stderr, " -a(udio)\n");
-
- fprintf(stderr,
- " -S(urface) Allocate output buffers on a surface.\n"
- " -R(ender) Render surface-allocated buffers.\n");
-}
-
-int main(int argc, char **argv) {
- android::ProcessState::self()->startThreadPool();
-
- bool decodeAudio = false;
- bool useSurface = false;
- bool renderToSurface = false;
-
- int res;
- while ((res = getopt(argc, argv, "haSR")) >= 0) {
- switch (res) {
- case 'a':
- decodeAudio = true;
- break;
-
- case 'S':
- useSurface = true;
- break;
-
- case 'R':
- renderToSurface = true;
- break;
-
- case '?':
- case 'h':
- default:
- {
- usage(argv[0]);
- return 1;
- }
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc != 1) {
- usage(argv[-optind]);
- return 1;
- }
-
- DataSource::RegisterDefaultSniffers();
-
- sp<ALooper> looper = new ALooper;
- looper->setName("sf2");
-
- sp<SurfaceComposerClient> composerClient;
- sp<SurfaceControl> control;
- sp<Surface> surface;
-
- if (!decodeAudio && useSurface) {
- composerClient = new SurfaceComposerClient;
- CHECK_EQ(composerClient->initCheck(), (status_t)OK);
-
- control = composerClient->createSurface(
- String8("A Surface"),
- 1280,
- 800,
- PIXEL_FORMAT_RGB_565,
- 0);
-
- CHECK(control != NULL);
- CHECK(control->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
- CHECK_EQ(control->show(), (status_t)OK);
- SurfaceComposerClient::closeGlobalTransaction();
-
- surface = control->getSurface();
- CHECK(surface != NULL);
-
- CHECK_EQ((status_t)OK,
- native_window_api_connect(
- surface.get(), NATIVE_WINDOW_API_MEDIA));
- }
-
- sp<Controller> controller =
- new Controller(argv[0], decodeAudio, surface, renderToSurface);
-
- looper->registerHandler(controller);
-
- signal(SIGINT, mysighandler);
-
- controller->startAsync();
-
- CHECK_EQ(looper->start(true /* runOnCallingThread */), (status_t)OK);
-
- looper->unregisterHandler(controller->id());
-
- if (!decodeAudio && useSurface) {
- CHECK_EQ((status_t)OK,
- native_window_api_disconnect(
- surface.get(), NATIVE_WINDOW_API_MEDIA));
-
- composerClient->dispose();
- }
-
- return 0;
-}
-
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 2bb35cb..5e3a859 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -965,8 +965,6 @@
}
}
- DataSource::RegisterDefaultSniffers();
-
status_t err = OK;
for (int k = 0; k < argc && err == OK; ++k) {
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 16ff39d..8f9333a 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -301,8 +301,6 @@
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
-
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
return 1;
@@ -349,9 +347,7 @@
sp<IStreamSource> source;
- char prop[PROPERTY_VALUE_MAX];
- bool usemp4 = property_get("media.stagefright.use-mp4source", prop, NULL) &&
- (!strcmp(prop, "1") || !strcasecmp(prop, "true"));
+ bool usemp4 = property_get_bool("media.stagefright.use-mp4source", false);
size_t len = strlen(argv[1]);
if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) ||
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
index 0750406..d75f71c 100644
--- a/include/drm/drm_framework_common.h
+++ b/include/drm/drm_framework_common.h
@@ -234,10 +234,6 @@
* POSIX based Decrypt API set for container based DRM
*/
static const int CONTAINER_BASED = 0x02;
- /**
- * Decrypt API for Widevine streams
- */
- static const int WV_BASED = 0x3;
};
/**
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h
index 9719efa..1ace607 100644
--- a/include/media/AudioParameter.h
+++ b/include/media/AudioParameter.h
@@ -88,6 +88,7 @@
status_t get(const String8& key, String8& value) const;
status_t getInt(const String8& key, int& value) const;
status_t getFloat(const String8& key, float& value) const;
+ status_t getAt(size_t index, String8& key) const;
status_t getAt(size_t index, String8& key, String8& value) const;
size_t size() const { return mParameters.size(); }
diff --git a/include/media/BufferingSettings.h b/include/media/BufferingSettings.h
new file mode 100644
index 0000000..7dd9d40
--- /dev/null
+++ b/include/media/BufferingSettings.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFERING_SETTINGS_H
+#define ANDROID_BUFFERING_SETTINGS_H
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+enum BufferingMode : int {
+ // Do not support buffering.
+ BUFFERING_MODE_NONE = 0,
+ // Support only time based buffering.
+ BUFFERING_MODE_TIME_ONLY = 1,
+ // Support only size based buffering.
+ BUFFERING_MODE_SIZE_ONLY = 2,
+ // Support both time and size based buffering, time based calculation precedes size based.
+ // Size based calculation will be used only when time information is not available for
+ // the stream.
+ BUFFERING_MODE_TIME_THEN_SIZE = 3,
+ // Number of modes.
+ BUFFERING_MODE_COUNT = 4,
+};
+
+struct BufferingSettings : public Parcelable {
+ static const int kNoWatermark = -1;
+
+ static bool IsValidBufferingMode(int mode);
+
+ BufferingMode mInitialBufferingMode; // for prepare
+ BufferingMode mRebufferingMode; // for playback
+
+ int mInitialWatermarkMs; // time based
+ int mInitialWatermarkKB; // size based
+
+ // When cached data is below this mark, playback will be paused for buffering
+ // till data reach |mRebufferingWatermarkHighMs| or end of stream.
+ int mRebufferingWatermarkLowMs;
+ // When cached data is above this mark, buffering will be paused.
+ int mRebufferingWatermarkHighMs;
+
+ // When cached data is below this mark, playback will be paused for buffering
+ // till data reach |mRebufferingWatermarkHighKB| or end of stream.
+ int mRebufferingWatermarkLowKB;
+ // When cached data is above this mark, buffering will be paused.
+ int mRebufferingWatermarkHighKB;
+
+ BufferingSettings();
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+};
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_BUFFERING_SETTINGS_H
diff --git a/include/media/IMediaAnalyticsService.h b/include/media/IMediaAnalyticsService.h
new file mode 100644
index 0000000..21da6ad
--- /dev/null
+++ b/include/media/IMediaAnalyticsService.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMEDIAANALYTICSSERVICE_H
+#define ANDROID_IMEDIAANALYTICSSERVICE_H
+
+#include <utils/String8.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/List.h>
+
+#include <binder/IServiceManager.h>
+
+#include <media/MediaAnalyticsItem.h>
+// nope...#include <media/MediaAnalytics.h>
+
+namespace android {
+
+class IMediaAnalyticsService: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MediaAnalyticsService);
+
+ // generate a unique sessionID to use across multiple requests
+ // 'unique' is within this device, since last reboot
+ virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;
+
+ // submit the indicated record to the mediaanalytics service, where
+ // it will be merged (if appropriate) with incomplete records that
+ // share the same key and sessionid.
+ // 'forcenew' marks any matching incomplete record as complete before
+ // inserting this new record.
+ // returns the sessionID associated with that item.
+ virtual MediaAnalyticsItem::SessionID_t submit(sp<MediaAnalyticsItem> item, bool forcenew) = 0;
+
+
+ // return lists of records that match the supplied parameters.
+ // finished [or not] records since time 'ts' with key 'key'
+ // timestamp 'ts' is nanoseconds, unix time.
+ virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, int64_t ts) = 0;
+ virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMediaAnalyticsService: public BnInterface<IMediaAnalyticsService>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIASTATISTICSSERVICE_H
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 99ef59f..f642373 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -39,6 +39,7 @@
struct IMediaHTTPService;
struct AudioPlaybackRate;
struct AVSyncSettings;
+struct BufferingSettings;
typedef IMediaSource::ReadOptions::SeekMode MediaPlayerSeekMode;
@@ -59,6 +60,9 @@
virtual status_t setDataSource(const sp<IDataSource>& source) = 0;
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
+ virtual status_t getDefaultBufferingSettings(
+ BufferingSettings* buffering /* nonnull */) = 0;
+ virtual status_t setBufferingSettings(const BufferingSettings& buffering) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
diff --git a/include/media/MediaAnalyticsItem.h b/include/media/MediaAnalyticsItem.h
new file mode 100644
index 0000000..73c9dd4
--- /dev/null
+++ b/include/media/MediaAnalyticsItem.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
+#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H
+
+#include <cutils/properties.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+
+
+class IMediaAnalyticsService;
+
+// the class interface
+//
+
+class MediaAnalyticsItem : public RefBase {
+
+ friend class MediaAnalyticsService;
+ friend class IMediaAnalyticsService;
+
+ public:
+
+ // sessionid
+ // unique within device, within boot,
+ typedef int64_t SessionID_t;
+ static constexpr SessionID_t SessionIDInvalid = -1;
+ static constexpr SessionID_t SessionIDNone = 0;
+
+ // Key: the record descriminator
+ // values for the record discriminator
+ // values can be "component/component"
+ // basic values: "video", "audio", "drm"
+ // XXX: need to better define the format
+ typedef AString Key;
+ static const Key kKeyNone; // ""
+ static const Key kKeyAny; // "*"
+
+ // Attr: names for attributes within a record
+ // format "prop1" or "prop/subprop"
+ // XXX: need to better define the format
+ typedef AString Attr;
+
+
+ public:
+
+ // access functions for the class
+ MediaAnalyticsItem();
+ MediaAnalyticsItem(Key);
+ ~MediaAnalyticsItem();
+
+ // so clients can send intermediate values to be overlaid later
+ MediaAnalyticsItem &setFinalized(bool);
+ bool getFinalized() const;
+
+ // SessionID ties multiple submissions for same key together
+ // so that if video "height" and "width" are known at one point
+ // and "framerate" is only known later, they can be be brought
+ // together.
+ MediaAnalyticsItem &setSessionID(SessionID_t);
+ MediaAnalyticsItem &clearSessionID();
+ SessionID_t getSessionID() const;
+ // generates and stores a new ID iff mSessionID == SessionIDNone
+ SessionID_t generateSessionID();
+
+ // reset all contents, discarding any extra data
+ void clear();
+
+ // set the key discriminator for the record.
+ // most often initialized as part of the constructor
+ MediaAnalyticsItem &setKey(MediaAnalyticsItem::Key);
+ MediaAnalyticsItem::Key getKey();
+
+ // # of attributes in the record
+ int32_t count() const;
+
+ // set values appropriately
+ // return values tell us whether we overwrote an existing value
+ bool setInt32(Attr, int32_t value);
+ bool setInt64(Attr, int64_t value);
+ bool setDouble(Attr, double value);
+ bool setCString(Attr, const char *value);
+
+ // fused get/add/set; if attr wasn't there, it's a simple set.
+ // type-mismatch counts as "wasn't there".
+ // return value tells us whether we overwrote an existing value
+ bool addInt32(Attr, int32_t value);
+ bool addInt64(Attr, int64_t value);
+ bool addDouble(Attr, double value);
+
+ // find & extract values
+ // return indicates whether attr exists (and thus value filled in)
+ bool getInt32(Attr, int32_t *value);
+ bool getInt64(Attr, int64_t *value);
+ bool getDouble(Attr, double *value);
+ bool getCString(Attr, char **value);
+
+ // parameter indicates whether to close any existing open
+ // record with same key before establishing a new record
+ bool selfrecord(bool);
+ bool selfrecord();
+
+ // remove indicated attributes and their values
+ // filterNot() could also be called keepOnly()
+ // return value is # attributes removed
+ // XXX: perhaps 'remove' instead of 'filter'
+ // XXX: filterNot would become 'keep'
+ int32_t filter(int count, Attr attrs[]);
+ int32_t filterNot(int count, Attr attrs[]);
+ int32_t filter(Attr attr);
+
+ // below here are used on server side or to talk to server
+ // clients need not worry about these.
+
+ // timestamp, pid, and uid only used on server side
+ // timestamp is in 'nanoseconds, unix time'
+ MediaAnalyticsItem &setTimestamp(nsecs_t);
+ nsecs_t getTimestamp() const;
+
+ MediaAnalyticsItem &setPid(pid_t);
+ pid_t getPid() const;
+
+ MediaAnalyticsItem &setUid(uid_t);
+ uid_t getUid() const;
+
+ // our serialization code for binder calls
+ int32_t writeToParcel(Parcel *);
+ int32_t readFromParcel(const Parcel&);
+
+ AString toString();
+
+ // are we collecting analytics data
+ static bool isEnabled();
+
+ protected:
+
+ // merge fields from arg into this
+ // with rules for first/last/add, etc
+ // XXX: document semantics and how they are indicated
+ bool merge(sp<MediaAnalyticsItem> );
+
+ // enabled 1, disabled 0
+ static const char * const EnabledProperty;
+ static const char * const EnabledPropertyPersist;
+ static const int EnabledProperty_default;
+
+ private:
+
+ // to help validate that A doesn't mess with B's records
+ pid_t mPid;
+ uid_t mUid;
+
+ // let's reuse a binder connection
+ static sp<IMediaAnalyticsService> sAnalyticsService;
+ static sp<IMediaAnalyticsService> getInstance();
+
+ // tracking information
+ SessionID_t mSessionID; // grouping similar records
+ nsecs_t mTimestamp; // ns, system_time_monotonic
+
+ // will this record accept further updates
+ bool mFinalized;
+
+ Key mKey;
+
+ class Item : public RefBase {
+
+ public:
+
+ enum Type {
+ kTypeNone = 0,
+ kTypeInt32 = 1,
+ kTypeInt64 = 2,
+ kTypeDouble = 3,
+ kTypeCString = 4,
+ };
+
+ Item();
+ ~Item();
+ void clear();
+
+ Type mType;
+ union {
+ int32_t int32Value;
+ int64_t int64Value;
+ double doubleValue;
+ char *CStringValue;
+ } u;
+ };
+ KeyedVector<Attr, sp<Item>> mItems;
+
+};
+
+} // namespace android
+
+#endif
diff --git a/include/media/MediaDefs.h b/include/media/MediaDefs.h
index 5f2a32d..0682413 100644
--- a/include/media/MediaDefs.h
+++ b/include/media/MediaDefs.h
@@ -59,8 +59,6 @@
extern const char *MEDIA_MIMETYPE_CONTAINER_AVI;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS;
-extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
-
extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
extern const char *MEDIA_MIMETYPE_TEXT_VTT;
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 4e4878a..0e815cb 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -30,6 +30,7 @@
#include <media/AudioSystem.h>
#include <media/AudioTimestamp.h>
#include <media/AVSyncSettings.h>
+#include <media/BufferingSettings.h>
#include <media/Metadata.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -174,6 +175,15 @@
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
+ virtual status_t getDefaultBufferingSettings(
+ BufferingSettings* buffering /* nonnull */) {
+ *buffering = BufferingSettings();
+ return OK;
+ }
+ virtual status_t setBufferingSettings(const BufferingSettings& /* buffering */) {
+ return OK;
+ }
+
virtual status_t prepare() = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
diff --git a/include/media/audiohal/DevicesFactoryHalInterface.h b/include/media/audiohal/DevicesFactoryHalInterface.h
index 823a0da..14af384 100644
--- a/include/media/audiohal/DevicesFactoryHalInterface.h
+++ b/include/media/audiohal/DevicesFactoryHalInterface.h
@@ -26,8 +26,6 @@
class DevicesFactoryHalInterface : public RefBase
{
public:
- virtual ~DevicesFactoryHalInterface() {}
-
// Opens a device with the specified name. To close the device, it is
// necessary to release references to the returned object.
virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device) = 0;
@@ -37,6 +35,8 @@
protected:
// Subclasses can not be constructed directly by clients.
DevicesFactoryHalInterface() {}
+
+ virtual ~DevicesFactoryHalInterface() {}
};
} // namespace android
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 8fc2809..cde188c 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -35,6 +35,7 @@
namespace android {
struct ABuffer;
+class ACodecBufferChannel;
class MediaCodecBuffer;
struct MemoryDealer;
struct DescribeColorFormat2Params;
@@ -43,10 +44,9 @@
struct ACodec : public AHierarchicalStateMachine, public CodecBase {
ACodec();
- virtual void setNotificationMessage(const sp<AMessage> &msg);
-
void initiateSetup(const sp<AMessage> &msg);
+ virtual std::shared_ptr<BufferChannelBase> getBufferChannel() override;
virtual void initiateAllocateComponent(const sp<AMessage> &msg);
virtual void initiateConfigureComponent(const sp<AMessage> &msg);
virtual void initiateCreateInputSurface();
@@ -72,23 +72,6 @@
handleMessage(msg);
}
- struct PortDescription : public CodecBase::PortDescription {
- size_t countBuffers();
- IOMX::buffer_id bufferIDAt(size_t index) const;
- sp<MediaCodecBuffer> bufferAt(size_t index) const;
-
- private:
- friend struct ACodec;
-
- Vector<IOMX::buffer_id> mBufferIDs;
- Vector<sp<MediaCodecBuffer> > mBuffers;
-
- PortDescription();
- void addBuffer(IOMX::buffer_id id, const sp<MediaCodecBuffer> &buffer);
-
- DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
- };
-
// Returns 0 if configuration is not supported. NOTE: this is treated by
// some OMX components as auto level, and by others as invalid level.
static int /* OMX_VIDEO_AVCLEVELTYPE */ getAVCLevelFor(
@@ -188,6 +171,7 @@
sp<RefBase> mCodecRef; // and a reference to the IMemory
sp<GraphicBuffer> mGraphicBuffer;
+ bool mNewGraphicBuffer;
int mFenceFd;
FrameRenderTracker::Info *mRenderInfo;
@@ -217,8 +201,6 @@
KeyedVector<int64_t, BufferStats> mBufferStats;
#endif
- sp<AMessage> mNotify;
-
sp<UninitializedState> mUninitializedState;
sp<LoadedState> mLoadedState;
sp<LoadedToIdleState> mLoadedToIdleState;
@@ -295,6 +277,8 @@
OMX_INDEXTYPE mDescribeColorAspectsIndex;
OMX_INDEXTYPE mDescribeHDRStaticInfoIndex;
+ std::shared_ptr<ACodecBufferChannel> mBufferChannel;
+
status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
status_t allocateBuffersOnPort(OMX_U32 portIndex);
status_t freeBuffersOnPort(OMX_U32 portIndex);
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 39a9089..52daa9e 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -18,12 +18,16 @@
#define CODEC_BASE_H_
+#include <memory>
+
#include <stdint.h>
#define STRINGIFY_ENUMS
+#include <media/ICrypto.h>
#include <media/IOMX.h>
#include <media/MediaCodecInfo.h>
+#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/hardware/HardwareAPI.h>
@@ -34,33 +38,164 @@
namespace android {
+class BufferChannelBase;
+class BufferProducerWrapper;
class MediaCodecBuffer;
struct PersistentSurface;
+struct RenderedFrameInfo;
class Surface;
struct CodecBase : public AHandler, /* static */ ColorUtils {
- enum {
- kWhatFillThisBuffer = 'fill',
- kWhatDrainThisBuffer = 'drai',
- kWhatEOS = 'eos ',
- kWhatStopCompleted = 'scom',
- kWhatReleaseCompleted = 'rcom',
- kWhatFlushCompleted = 'fcom',
- kWhatError = 'erro',
- kWhatComponentAllocated = 'cAll',
- kWhatComponentConfigured = 'cCon',
- kWhatInputSurfaceCreated = 'isfc',
- kWhatInputSurfaceAccepted = 'isfa',
- kWhatSignaledInputEOS = 'seos',
- kWhatBuffersAllocated = 'allc',
- kWhatOutputFramesRendered = 'outR',
+ /**
+ * This interface defines events firing from CodecBase back to MediaCodec.
+ * All methods must not block.
+ */
+ class CodecCallback {
+ public:
+ virtual ~CodecCallback() = default;
+
+ /**
+ * Notify MediaCodec for seeing an output EOS.
+ *
+ * @param err the underlying cause of the EOS. If the value is neither
+ * OK nor ERROR_END_OF_STREAM, the EOS is declared
+ * prematurely for that error.
+ */
+ virtual void onEos(status_t err) = 0;
+ /**
+ * Notify MediaCodec that start operation is complete.
+ */
+ virtual void onStartCompleted() = 0;
+ /**
+ * Notify MediaCodec that stop operation is complete.
+ */
+ virtual void onStopCompleted() = 0;
+ /**
+ * Notify MediaCodec that release operation is complete.
+ */
+ virtual void onReleaseCompleted() = 0;
+ /**
+ * Notify MediaCodec that flush operation is complete.
+ */
+ virtual void onFlushCompleted() = 0;
+ /**
+ * Notify MediaCodec that an error is occurred.
+ *
+ * @param err an error code for the occurred error.
+ * @param actionCode an action code for severity of the error.
+ */
+ virtual void onError(status_t err, enum ActionCode actionCode) = 0;
+ /**
+ * Notify MediaCodec that the underlying component is allocated.
+ *
+ * @param componentName the unique name of the component specified in
+ * MediaCodecList.
+ */
+ virtual void onComponentAllocated(const char *componentName) = 0;
+ /**
+ * Notify MediaCodec that the underlying component is configured.
+ *
+ * @param inputFormat an input format at configure time.
+ * @param outputFormat an output format at configure time.
+ */
+ virtual void onComponentConfigured(
+ const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) = 0;
+ /**
+ * Notify MediaCodec that the input surface is created.
+ *
+ * @param inputFormat an input format at surface creation. Formats
+ * could change from the previous state as a result
+ * of creating a surface.
+ * @param outputFormat an output format at surface creation.
+ * @param inputSurface the created surface.
+ */
+ virtual void onInputSurfaceCreated(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat,
+ const sp<BufferProducerWrapper> &inputSurface) = 0;
+ /**
+ * Notify MediaCodec that the input surface creation is failed.
+ *
+ * @param err an error code of the cause.
+ */
+ virtual void onInputSurfaceCreationFailed(status_t err) = 0;
+ /**
+ * Notify MediaCodec that the component accepted the provided input
+ * surface.
+ *
+ * @param inputFormat an input format at surface assignment. Formats
+ * could change from the previous state as a result
+ * of assigning a surface.
+ * @param outputFormat an output format at surface assignment.
+ */
+ virtual void onInputSurfaceAccepted(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat) = 0;
+ /**
+ * Notify MediaCodec that the component declined the provided input
+ * surface.
+ *
+ * @param err an error code of the cause.
+ */
+ virtual void onInputSurfaceDeclined(status_t err) = 0;
+ /**
+ * Noitfy MediaCodec that the requested input EOS is sent to the input
+ * surface.
+ *
+ * @param err an error code returned from the surface. If there is no
+ * input surface, the value is INVALID_OPERATION.
+ */
+ virtual void onSignaledInputEOS(status_t err) = 0;
+ /**
+ * Notify MediaCodec that output frames are rendered with information on
+ * those frames.
+ *
+ * @param done a list of rendered frames.
+ */
+ virtual void onOutputFramesRendered(const std::list<RenderedFrameInfo> &done) = 0;
+ /**
+ * Notify MediaCodec that output buffers are changed.
+ */
+ virtual void onOutputBuffersChanged() = 0;
};
+ /**
+ * This interface defines events firing from BufferChannelBase back to MediaCodec.
+ * All methods must not block.
+ */
+ class BufferCallback {
+ public:
+ virtual ~BufferCallback() = default;
+
+ /**
+ * Notify MediaCodec that an input buffer is available with given index.
+ * When BufferChannelBase::getInputBufferArray() is not called,
+ * BufferChannelBase may report different buffers with the same index if
+ * MediaCodec already queued/discarded the buffer. After calling
+ * BufferChannelBase::getInputBufferArray(), the buffer and index match the
+ * returned array.
+ */
+ virtual void onInputBufferAvailable(
+ size_t index, const sp<MediaCodecBuffer> &buffer) = 0;
+ /**
+ * Notify MediaCodec that an output buffer is available with given index.
+ * When BufferChannelBase::getOutputBufferArray() is not called,
+ * BufferChannelBase may report different buffers with the same index if
+ * MediaCodec already queued/discarded the buffer. After calling
+ * BufferChannelBase::getOutputBufferArray(), the buffer and index match the
+ * returned array.
+ */
+ virtual void onOutputBufferAvailable(
+ size_t index, const sp<MediaCodecBuffer> &buffer) = 0;
+ };
enum {
kMaxCodecBufferSize = 8192 * 4096 * 4, // 8K RGBA
};
- virtual void setNotificationMessage(const sp<AMessage> &msg) = 0;
+ inline void setCallback(std::unique_ptr<CodecCallback> &&callback) {
+ mCallback = std::move(callback);
+ }
+ virtual std::shared_ptr<BufferChannelBase> getBufferChannel() = 0;
virtual void initiateAllocateComponent(const sp<AMessage> &msg) = 0;
virtual void initiateConfigureComponent(const sp<AMessage> &msg) = 0;
@@ -86,31 +221,104 @@
virtual void signalSetParameters(const sp<AMessage> &msg) = 0;
virtual void signalEndOfInputStream() = 0;
- struct PortDescription : public RefBase {
- virtual size_t countBuffers() = 0;
- virtual IOMX::buffer_id bufferIDAt(size_t index) const = 0;
- virtual sp<MediaCodecBuffer> bufferAt(size_t index) const = 0;
-
- protected:
- PortDescription();
- virtual ~PortDescription();
-
- private:
- DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
- };
-
/*
* Codec-related defines
*/
protected:
- CodecBase();
- virtual ~CodecBase();
+ CodecBase() = default;
+ virtual ~CodecBase() = default;
+
+ std::unique_ptr<CodecCallback> mCallback;
private:
DISALLOW_EVIL_CONSTRUCTORS(CodecBase);
};
+/**
+ * A channel between MediaCodec and CodecBase object which manages buffer
+ * passing. Only MediaCodec is expected to call these methods, and
+ * underlying CodecBase implementation should define its own interface
+ * separately for itself.
+ *
+ * Concurrency assumptions:
+ *
+ * 1) Clients may access the object at multiple threads concurrently.
+ * 2) All methods do not call underlying CodecBase object while holding a lock.
+ * 3) Code inside critical section executes within 1ms.
+ */
+class BufferChannelBase {
+public:
+ virtual ~BufferChannelBase() = default;
+
+ inline void setCallback(std::unique_ptr<CodecBase::BufferCallback> &&callback) {
+ mCallback = std::move(callback);
+ }
+
+ inline void setCrypto(const sp<ICrypto> &crypto) {
+ mCrypto = crypto;
+ }
+
+ /**
+ * Queue an input buffer into the buffer channel.
+ *
+ * @return OK if successful;
+ * -ENOENT if the buffer is not known (TODO: this should be
+ * handled gracefully in the future, here and below).
+ */
+ virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
+ /**
+ * Queue a secure input buffer into the buffer channel.
+ *
+ * @return OK if successful;
+ * -ENOENT if the buffer is not known;
+ * -ENOSYS if mCrypto is not set so that decryption is not
+ * possible;
+ * other errors if decryption failed.
+ */
+ virtual status_t queueSecureInputBuffer(
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ AString *errorDetailMsg) = 0;
+ /**
+ * Request buffer rendering at specified time.
+ *
+ * @param timestampNs nanosecond timestamp for rendering time.
+ * @return OK if successful;
+ * -ENOENT if the buffer is not known.
+ */
+ virtual status_t renderOutputBuffer(
+ const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) = 0;
+ /**
+ * Discard a buffer to the underlying CodecBase object.
+ *
+ * TODO: remove once this operation can be handled by just clearing the
+ * reference.
+ *
+ * @return OK if successful;
+ * -ENOENT if the buffer is not known.
+ */
+ virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
+ /**
+ * Clear and fill array with input buffers.
+ */
+ virtual void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) = 0;
+ /**
+ * Clear and fill array with output buffers.
+ */
+ virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) = 0;
+
+protected:
+ std::unique_ptr<CodecBase::BufferCallback> mCallback;
+ sp<ICrypto> mCrypto;
+};
+
} // namespace android
#endif // CODEC_BASE_H_
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 0254545..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(
@@ -102,17 +103,6 @@
////////////////////////////////////////////////////////////////////////////
- bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta);
-
- // The sniffer can optionally fill in "meta" with an AMessage containing
- // a dictionary of values that helps the corresponding extractor initialize
- // its state without duplicating effort already exerted by the sniffer.
- typedef bool (*SnifferFunc)(
- const sp<DataSource> &source, String8 *mimeType,
- float *confidence, sp<AMessage> *meta);
-
- static void RegisterDefaultSniffers();
-
// for DRM
virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL) {
return NULL;
@@ -131,12 +121,6 @@
virtual ~DataSource() {}
private:
- static Mutex gSnifferMutex;
- static List<SnifferFunc> gSniffers;
- static bool gSniffersRegistered;
-
- static void RegisterSniffer_l(SnifferFunc func);
-
DataSource(const DataSource &);
DataSource &operator=(const DataSource &);
};
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/include/media/stagefright/FrameRenderTracker.h b/include/media/stagefright/FrameRenderTracker.h
index 8396657..6c572b8 100644
--- a/include/media/stagefright/FrameRenderTracker.h
+++ b/include/media/stagefright/FrameRenderTracker.h
@@ -32,58 +32,61 @@
class Fence;
class GraphicBuffer;
+// Tracks the render information about a frame. Frames go through several states while
+// the render information is tracked:
+//
+// 1. queued frame: mMediaTime and mGraphicBuffer are set for the frame. mFence is the
+// queue fence (read fence). mIndex is negative, and mRenderTimeNs is invalid.
+// Key characteristics: mFence is not NULL and mIndex is negative.
+//
+// 2. dequeued frame: mFence is updated with the dequeue fence (write fence). mIndex is set.
+// Key characteristics: mFence is not NULL and mIndex is non-negative. mRenderTime is still
+// invalid.
+//
+// 3. rendered frame or frame: mFence is cleared, mRenderTimeNs is set.
+// Key characteristics: mFence is NULL.
+//
+struct RenderedFrameInfo {
+ // set by client during onFrameQueued or onFrameRendered
+ int64_t getMediaTimeUs() const { return mMediaTimeUs; }
+
+ // -1 if frame is not yet rendered
+ nsecs_t getRenderTimeNs() const { return mRenderTimeNs; }
+
+ // set by client during updateRenderInfoForDequeuedBuffer; -1 otherwise
+ ssize_t getIndex() const { return mIndex; }
+
+ // creates information for a queued frame
+ RenderedFrameInfo(int64_t mediaTimeUs, const sp<GraphicBuffer> &graphicBuffer,
+ const sp<Fence> &fence)
+ : mMediaTimeUs(mediaTimeUs),
+ mRenderTimeNs(-1),
+ mIndex(-1),
+ mGraphicBuffer(graphicBuffer),
+ mFence(fence) {
+ }
+
+ // creates information for a frame rendered on a tunneled surface
+ RenderedFrameInfo(int64_t mediaTimeUs, nsecs_t renderTimeNs)
+ : mMediaTimeUs(mediaTimeUs),
+ mRenderTimeNs(renderTimeNs),
+ mIndex(-1),
+ mGraphicBuffer(NULL),
+ mFence(NULL) {
+ }
+
+private:
+ int64_t mMediaTimeUs;
+ nsecs_t mRenderTimeNs;
+ ssize_t mIndex; // to be used by client
+ sp<GraphicBuffer> mGraphicBuffer;
+ sp<Fence> mFence;
+
+ friend class FrameRenderTracker;
+};
+
struct FrameRenderTracker {
- // Tracks the render information about a frame. Frames go through several states while
- // the render information is tracked:
- //
- // 1. queued frame: mMediaTime and mGraphicBuffer are set for the frame. mFence is the
- // queue fence (read fence). mIndex is negative, and mRenderTimeNs is invalid.
- // Key characteristics: mFence is not NULL and mIndex is negative.
- //
- // 2. dequeued frame: mFence is updated with the dequeue fence (write fence). mIndex is set.
- // Key characteristics: mFence is not NULL and mIndex is non-negative. mRenderTime is still
- // invalid.
- //
- // 3. rendered frame or frame: mFence is cleared, mRenderTimeNs is set.
- // Key characteristics: mFence is NULL.
- //
- struct Info {
- // set by client during onFrameQueued or onFrameRendered
- int64_t getMediaTimeUs() const { return mMediaTimeUs; }
-
- // -1 if frame is not yet rendered
- nsecs_t getRenderTimeNs() const { return mRenderTimeNs; }
-
- // set by client during updateRenderInfoForDequeuedBuffer; -1 otherwise
- ssize_t getIndex() const { return mIndex; }
-
- // creates information for a queued frame
- Info(int64_t mediaTimeUs, const sp<GraphicBuffer> &graphicBuffer, const sp<Fence> &fence)
- : mMediaTimeUs(mediaTimeUs),
- mRenderTimeNs(-1),
- mIndex(-1),
- mGraphicBuffer(graphicBuffer),
- mFence(fence) {
- }
-
- // creates information for a frame rendered on a tunneled surface
- Info(int64_t mediaTimeUs, nsecs_t renderTimeNs)
- : mMediaTimeUs(mediaTimeUs),
- mRenderTimeNs(renderTimeNs),
- mIndex(-1),
- mGraphicBuffer(NULL),
- mFence(NULL) {
- }
-
- private:
- int64_t mMediaTimeUs;
- nsecs_t mRenderTimeNs;
- ssize_t mIndex; // to be used by client
- sp<GraphicBuffer> mGraphicBuffer;
- sp<Fence> mFence;
-
- friend class FrameRenderTracker;
- };
+ typedef RenderedFrameInfo Info;
FrameRenderTracker();
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 89def5d..50fb312 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -18,6 +18,9 @@
#define MEDIA_CODEC_H_
+#include <memory>
+#include <vector>
+
#include <gui/IGraphicBufferProducer.h>
#include <media/hardware/CryptoAPI.h>
#include <media/MediaCodecInfo.h>
@@ -32,12 +35,12 @@
struct AMessage;
struct AReplyToken;
struct AString;
+class BufferChannelBase;
struct CodecBase;
class IBatteryStats;
struct ICrypto;
class MediaCodecBuffer;
class IMemory;
-struct MemoryDealer;
class IResourceManagerClient;
class IResourceManagerService;
struct PersistentSurface;
@@ -151,8 +154,6 @@
status_t getOutputFormat(sp<AMessage> *format) const;
status_t getInputFormat(sp<AMessage> *format) const;
- status_t getWidevineLegacyBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const;
-
status_t getInputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const;
status_t getOutputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const;
@@ -254,11 +255,9 @@
};
struct BufferInfo {
- uint32_t mBufferID;
+ BufferInfo();
+
sp<MediaCodecBuffer> mData;
- sp<MediaCodecBuffer> mSecureData;
- sp<IMemory> mSharedEncryptedBuffer;
- sp<AMessage> mNotify;
bool mOwnedByClient;
};
@@ -303,7 +302,6 @@
sp<AMessage> mInputFormat;
sp<AMessage> mCallback;
sp<AMessage> mOnFrameRenderedNotification;
- sp<MemoryDealer> mDealer;
sp<IResourceManagerClient> mResourceManagerClient;
sp<ResourceManagerServiceProxy> mResourceManagerService;
@@ -329,8 +327,7 @@
Mutex mBufferLock;
List<size_t> mAvailPortBuffers[2];
- Vector<BufferInfo> mPortBuffers[2];
- Vector<sp<MediaCodecBuffer>> mPortBufferArrays[2];
+ std::vector<BufferInfo> mPortBuffers[2];
int32_t mDequeueInputTimeoutGeneration;
sp<AReplyToken> mDequeueInputReplyID;
@@ -347,6 +344,8 @@
bool mHaveInputSurface;
bool mHavePendingInputBuffers;
+ std::shared_ptr<BufferChannelBase> mBufferChannel;
+
MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 6bf8c9e..e5ee72e 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -80,6 +80,24 @@
private:
bool mIsDrm;
+ typedef bool (*SnifferFunc)(
+ const sp<DataSource> &source, String8 *mimeType,
+ float *confidence, sp<AMessage> *meta);
+
+ static Mutex gSnifferMutex;
+ static List<SnifferFunc> gSniffers;
+ static bool gSniffersRegistered;
+
+ // The sniffer can optionally fill in "meta" with an AMessage containing
+ // a dictionary of values that helps the corresponding extractor initialize
+ // its state without duplicating effort already exerted by the sniffer.
+ static void RegisterSniffer_l(SnifferFunc func);
+
+ static bool sniff(const sp<DataSource> &source,
+ String8 *mimeType, float *confidence, sp<AMessage> *meta);
+
+ static void RegisterDefaultSniffers();
+
MediaExtractor(const MediaExtractor &);
MediaExtractor &operator=(const MediaExtractor &);
};
diff --git a/include/media/stagefright/MediaFilter.h b/include/media/stagefright/MediaFilter.h
index 0e39431..0e4539b 100644
--- a/include/media/stagefright/MediaFilter.h
+++ b/include/media/stagefright/MediaFilter.h
@@ -21,6 +21,7 @@
namespace android {
+class ACodecBufferChannel;
struct GraphicBufferListener;
struct MemoryDealer;
struct SimpleFilter;
@@ -28,8 +29,7 @@
struct MediaFilter : public CodecBase {
MediaFilter();
- virtual void setNotificationMessage(const sp<AMessage> &msg);
-
+ virtual std::shared_ptr<BufferChannelBase> getBufferChannel() override;
virtual void initiateAllocateComponent(const sp<AMessage> &msg);
virtual void initiateConfigureComponent(const sp<AMessage> &msg);
virtual void initiateCreateInputSurface();
@@ -47,25 +47,6 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
- struct PortDescription : public CodecBase::PortDescription {
- virtual size_t countBuffers();
- virtual IOMX::buffer_id bufferIDAt(size_t index) const;
- virtual sp<MediaCodecBuffer> bufferAt(size_t index) const;
-
- protected:
- PortDescription();
-
- private:
- friend struct MediaFilter;
-
- Vector<IOMX::buffer_id> mBufferIDs;
- Vector<sp<MediaCodecBuffer> > mBuffers;
-
- void addBuffer(IOMX::buffer_id id, const sp<MediaCodecBuffer> &buffer);
-
- DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
- };
-
protected:
virtual ~MediaFilter();
@@ -120,7 +101,6 @@
int32_t mColorFormatIn, mColorFormatOut;
size_t mMaxInputSize, mMaxOutputSize;
int32_t mGeneration;
- sp<AMessage> mNotify;
sp<AMessage> mInputFormat;
sp<AMessage> mOutputFormat;
@@ -133,6 +113,8 @@
sp<SimpleFilter> mFilter;
sp<GraphicBufferListener> mGraphicBufferListener;
+ std::shared_ptr<ACodecBufferChannel> mBufferChannel;
+
// helper functions
void signalProcessBuffers();
void signalError(status_t error);
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 03e2185..a8aca5a 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -108,7 +108,6 @@
sp<DataSource> mDataSource;
sp<IMediaExtractor> mImpl;
- bool mIsWidevineExtractor;
Vector<TrackInfo> mSelectedTracks;
int64_t mTotalBitrate; // in bits/sec
diff --git a/include/media/stagefright/foundation/AData.h b/include/media/stagefright/foundation/AData.h
new file mode 100644
index 0000000..49aa0dc
--- /dev/null
+++ b/include/media/stagefright/foundation/AData.h
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STAGEFRIGHT_FOUNDATION_A_DATA_H_
+#define STAGEFRIGHT_FOUNDATION_A_DATA_H_
+
+#include <memory> // for std::shared_ptr, weak_ptr and unique_ptr
+#include <type_traits> // for std::aligned_union
+
+#include <utils/StrongPointer.h> // for android::sp and wp
+
+#include <media/stagefright/foundation/TypeTraits.h>
+#include <media/stagefright/foundation/Flagged.h>
+
+namespace android {
+
+/**
+ * AData is a flexible union type that supports non-POD members. It supports arbitrary types as long
+ * as they are either moveable or copyable.
+ *
+ * Internally, AData is using AUnion - a structure providing the union support. AUnion should not
+ * be used by generic code as it is very unsafe - it opens type aliasing errors where an object of
+ * one type can be easily accessed as an object of another type. AData prevents this.
+ *
+ * AData allows a custom type flagger to be used for future extensions (e.g. allowing automatic
+ * type conversion). A strict and a relaxed flagger are provided as internal types.
+ *
+ * Use as follows:
+ *
+ * AData<int, float>::Basic data; // strict type support
+ * int i = 1;
+ * float f = 7.0f;
+ *
+ * data.set(5);
+ * EXPECT_TRUE(data.find(&i));
+ * EXPECT_FALSE(data.find(&f));
+ * EXPECT_EQ(i, 5);
+ *
+ * data.set(6.0f);
+ * EXPECT_FALSE(data.find(&i));
+ * EXPECT_TRUE(data.find(&f));
+ * EXPECT_EQ(f, 6.0f);
+ *
+ * AData<int, sp<RefBase>>::RelaxedBasic objdata; // relaxed type support
+ * sp<ABuffer> buf = new ABuffer(16), buf2;
+ * sp<RefBase> obj;
+ *
+ * objdata.set(buf);
+ * EXPECT_TRUE(objdata.find(&buf2));
+ * EXPECT_EQ(buf, buf2);
+ * EXPECT_FALSE(objdata.find(&i));
+ * EXPECT_TRUE(objdata.find(&obj));
+ * EXPECT_TRUE(obj == buf);
+ *
+ * obj = buf;
+ * objdata.set(obj); // storing as sp<RefBase>
+ * EXPECT_FALSE(objdata.find(&buf2)); // not stored as ABuffer(!)
+ * EXPECT_TRUE(objdata.find(&obj));
+ */
+
+/// \cond Internal
+
+/**
+ * Helper class to call constructor and destructor for a specific type in AUnion.
+ * This class is needed as member function specialization is not allowed for a
+ * templated class.
+ */
+struct _AUnion_impl {
+ /**
+ * Calls placement constuctor for type T with arbitrary arguments for a storage at an address.
+ * Storage MUST be large enough to contain T.
+ * Also clears the slack space after type T. \todo This is not technically needed, so we may
+ * choose to do this just for debugging.
+ *
+ * \param totalSize size of the storage
+ * \param addr pointer to where object T should be constructed
+ * \param args arbitrary arguments for constructor
+ */
+ template<typename T, typename ...Args>
+ inline static void emplace(size_t totalSize, T *addr, Args&&... args) {
+ new(addr)T(std::forward<Args>(args)...);
+ // clear slack space - this is not technically required
+ constexpr size_t size = sizeof(T);
+ memset(reinterpret_cast<uint8_t*>(addr) + size, 0, totalSize - size);
+ }
+
+ /**
+ * Calls destuctor for an object of type T located at a specific address.
+ *
+ * \note we do not clear the storage in this case as the storage should not be used
+ * until another object is placed there, at which case the storage will be cleared.
+ *
+ * \param addr pointer to where object T is stored
+ */
+ template<typename T>
+ inline static void del(T *addr) {
+ addr->~T();
+ }
+};
+
+/** Constructor specialization for void type */
+template<>
+inline void _AUnion_impl::emplace<void>(size_t totalSize, void *addr) {
+ memset(addr, 0, totalSize);
+}
+
+/** Destructor specialization for void type */
+template<>
+inline void _AUnion_impl::del<void>(void *) {
+}
+
+/// \endcond
+
+/**
+ * A templated union class that can contain specific types of data, and provides
+ * constructors, destructor and access methods strictly for those types.
+ *
+ * \note This class is VERY UNSAFE compared to a union, but it allows non-POD unions.
+ * In particular care must be taken that methods are called in a careful order to
+ * prevent accessing objects of one type as another type. This class provides no
+ * facilities to help with this ordering. This is meant to be wrapped by safer
+ * utility classes that do that.
+ *
+ * \param Ts types stored in this union.
+ */
+template<typename ...Ts>
+struct AUnion {
+private:
+ using _type = typename std::aligned_union<0, Ts...>::type; ///< storage type
+ _type mValue; ///< storage
+
+public:
+ /**
+ * Constructs an object of type T with arbitrary arguments in this union. After this call,
+ * this union will contain this object.
+ *
+ * This method MUST be called only when either 1) no object or 2) a void object (equivalent to
+ * no object) is contained in this union.
+ *
+ * \param T type of object to be constructed. This must be one of the template parameters of
+ * the union class with the same cv-qualification, or void.
+ * \param args arbitrary arguments for the constructor
+ */
+ template<
+ typename T, typename ...Args,
+ typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
+ inline void emplace(Args&&... args) {
+ _AUnion_impl::emplace(
+ sizeof(_type), reinterpret_cast<T*>(&mValue), std::forward<Args>(args)...);
+ }
+
+ /**
+ * Destructs an object of type T in this union. After this call, this union will contain no
+ * object.
+ *
+ * This method MUST be called only when this union contains an object of type T.
+ *
+ * \param T type of object to be destructed. This must be one of the template parameters of
+ * the union class with the same cv-qualification, or void.
+ */
+ template<
+ typename T,
+ typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
+ inline void del() {
+ _AUnion_impl::del(reinterpret_cast<T*>(&mValue));
+ }
+
+ /**
+ * Returns a const reference to the object of type T in this union.
+ *
+ * This method MUST be called only when this union contains an object of type T.
+ *
+ * \param T type of object to be returned. This must be one of the template parameters of
+ * the union class with the same cv-qualification.
+ */
+ template<
+ typename T,
+ typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
+ inline const T &get() const {
+ return *reinterpret_cast<const T*>(&mValue);
+ }
+
+ /**
+ * Returns a reference to the object of type T in this union.
+ *
+ * This method MUST be called only when this union contains an object of type T.
+ *
+ * \param T type of object to be returned. This must be one of the template parameters of
+ * the union class with the same cv-qualification.
+ */
+ template<typename T>
+ inline T &get() {
+ return *reinterpret_cast<T*>(&mValue);
+ }
+};
+
+/**
+ * Helper utility class that copies an object of type T to a destination.
+ *
+ * T must be copy assignable or copy constructible.
+ *
+ * It provides:
+ *
+ * void assign(T*, const U&) // for copiable types - this leaves the source unchanged, hence const.
+ *
+ * \param T type of object to assign to
+ */
+template<
+ typename T,
+ bool=std::is_copy_assignable<T>::value>
+struct _AData_copier {
+ static_assert(std::is_copy_assignable<T>::value, "T must be copy assignable here");
+
+ /**
+ * Copies src to data without modifying data.
+ *
+ * \param data pointer to destination
+ * \param src source object
+ */
+ inline static void assign(T *data, const T &src) {
+ *data = src;
+ }
+
+ template<typename U>
+ using enable_if_T_is_same_as = typename std::enable_if<std::is_same<U, T>::value>::type;
+
+ /**
+ * Downcast specializations for sp<>, shared_ptr<> and weak_ptr<>
+ */
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
+ inline static void assign(sp<Tp> *data, const sp<U> &src) {
+ *data = static_cast<Tp*>(src.get());
+ }
+
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<wp<Tp>>>
+ inline static void assign(wp<Tp> *data, const wp<U> &src) {
+ sp<U> __tmp = src.promote();
+ *data = static_cast<Tp*>(__tmp.get());
+ }
+
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
+ inline static void assign(sp<Tp> *data, sp<U> &&src) {
+ sp<U> __tmp = std::move(src); // move src out as get cannot
+ *data = static_cast<Tp*>(__tmp.get());
+ }
+
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
+ inline static void assign(std::shared_ptr<Tp> *data, const std::shared_ptr<U> &src) {
+ *data = std::static_pointer_cast<Tp>(src);
+ }
+
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
+ inline static void assign(std::shared_ptr<Tp> *data, std::shared_ptr<U> &&src) {
+ std::shared_ptr<U> __tmp = std::move(src); // move src out as static_pointer_cast cannot
+ *data = std::static_pointer_cast<Tp>(__tmp);
+ }
+
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::weak_ptr<Tp>>>
+ inline static void assign(std::weak_ptr<Tp> *data, const std::weak_ptr<U> &src) {
+ *data = std::static_pointer_cast<Tp>(src.lock());
+ }
+
+ // shared_ptrs are implicitly convertible to weak_ptrs but not vice versa, but picking the
+ // first compatible type in Ts requires having shared_ptr types before weak_ptr types, so that
+ // they are stored as shared_ptrs.
+ /**
+ * Provide sensible error message if encountering shared_ptr/weak_ptr ambiguity. This method
+ * is not enough to detect this, only if someone is trying to find the shared_ptr.
+ */
+ template<typename Tp, typename U>
+ inline static void assign(std::shared_ptr<Tp> *, const std::weak_ptr<U> &) {
+ static_assert(std::is_same<Tp, void>::value,
+ "shared/weak pointer ambiguity. move shared ptr types before weak_ptrs");
+ }
+};
+
+/**
+ * Template specialization for non copy assignable, but copy constructible types.
+ *
+ * \todo Test this. No basic classes are copy constructible but not assignable.
+ *
+ */
+template<typename T>
+struct _AData_copier<T, false> {
+ static_assert(!std::is_copy_assignable<T>::value, "T must not be copy assignable here");
+ static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible here");
+
+ inline static void copy(T *data, const T &src) {
+ data->~T();
+ new(data)T(src);
+ }
+};
+
+/**
+ * Helper utility class that moves an object of type T to a destination.
+ *
+ * T must be move assignable or move constructible.
+ *
+ * It provides multiple methods:
+ *
+ * void assign(T*, T&&)
+ *
+ * \param T type of object to assign
+ */
+template<
+ typename T,
+ bool=std::is_move_assignable<T>::value>
+struct _AData_mover {
+ static_assert(std::is_move_assignable<T>::value, "T must be move assignable here");
+
+ /**
+ * Moves src to data while likely modifying it.
+ *
+ * \param data pointer to destination
+ * \param src source object
+ */
+ inline static void assign(T *data, T &&src) {
+ *data = std::move(src);
+ }
+
+ template<typename U>
+ using enable_if_T_is_same_as = typename std::enable_if<std::is_same<U, T>::value>::type;
+
+ /**
+ * Downcast specializations for sp<>, shared_ptr<> and weak_ptr<>
+ */
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
+ inline static void assign(sp<Tp> *data, sp<U> &&src) {
+ sp<U> __tmp = std::move(src); // move src out as get cannot
+ *data = static_cast<Tp*>(__tmp.get());
+ }
+
+ template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
+ inline static void assign(std::shared_ptr<Tp> *data, std::shared_ptr<U> &&src) {
+ std::shared_ptr<U> __tmp = std::move(src); // move src out as static_pointer_cast cannot
+ *data = std::static_pointer_cast<Tp>(__tmp);
+ }
+
+ template<typename Tp, typename Td, typename U, typename Ud,
+ typename=enable_if_T_is_same_as<std::unique_ptr<Tp, Td>>>
+ inline static void assign(std::unique_ptr<Tp, Td> *data, std::unique_ptr<U, Ud> &&src) {
+ *data = std::unique_ptr<Tp, Td>(static_cast<Tp*>(src.release()));
+ }
+
+ // shared_ptrs are implicitly convertible to weak_ptrs but not vice versa, but picking the
+ // first compatible type in Ts requires having shared_ptr types before weak_ptr types, so that
+ // they are stored as shared_ptrs.
+ /**
+ * Provide sensible error message if encountering shared_ptr/weak_ptr ambiguity. This method
+ * is not enough to detect this, only if someone is trying to remove the shared_ptr.
+ */
+ template<typename Tp, typename U>
+ inline static void assign(std::shared_ptr<Tp> *, std::weak_ptr<U> &&) {
+ static_assert(std::is_same<Tp, void>::value,
+ "shared/weak pointer ambiguity. move shared ptr types before weak_ptrs");
+ }
+
+ // unique_ptrs are implicitly convertible to shared_ptrs but not vice versa, but picking the
+ // first compatible type in Ts requires having unique_ptrs types before shared_ptrs types, so
+ // that they are stored as unique_ptrs.
+ /**
+ * Provide sensible error message if encountering shared_ptr/unique_ptr ambiguity. This method
+ * is not enough to detect this, only if someone is trying to remove the unique_ptr.
+ */
+ template<typename Tp, typename U>
+ inline static void assign(std::unique_ptr<Tp> *, std::shared_ptr<U> &&) {
+ static_assert(std::is_same<Tp, void>::value,
+ "unique/shared pointer ambiguity. move unique ptr types before shared_ptrs");
+ }
+};
+
+/**
+ * Template specialization for non move assignable, but move constructible types.
+ *
+ * \todo Test this. No basic classes are move constructible but not assignable.
+ *
+ */
+template<typename T>
+struct _AData_mover<T, false> {
+ static_assert(!std::is_move_assignable<T>::value, "T must not be move assignable here");
+ static_assert(std::is_move_constructible<T>::value, "T must be move constructible here");
+
+ inline static void assign(T *data, T &&src) {
+ data->~T();
+ new(data)T(std::move(src));
+ }
+};
+
+/**
+ * Helper template that deletes an object of a specific type (member) in an AUnion.
+ *
+ * \param Flagger type flagger class (see AData)
+ * \param U AUnion object in which the member should be deleted
+ * \param Ts types to consider for the member
+ */
+template<typename Flagger, typename U, typename ...Ts>
+struct _AData_deleter;
+
+/**
+ * Template specialization when there are still types to consider (T and rest)
+ */
+template<typename Flagger, typename U, typename T, typename ...Ts>
+struct _AData_deleter<Flagger, U, T, Ts...> {
+ static bool del(typename Flagger::type flags, U &data) {
+ if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
+ data.template del<T>();
+ return true;
+ }
+ return _AData_deleter<Flagger, U, Ts...>::del(flags, data);
+ }
+};
+
+/**
+ * Template specialization when there are no more types to consider.
+ */
+template<typename Flagger, typename U>
+struct _AData_deleter<Flagger, U> {
+ inline static bool del(typename Flagger::type, U &) {
+ return false;
+ }
+};
+
+/**
+ * Container that can store an arbitrary object of a set of specified types.
+ *
+ * This struct is an outer class that contains various inner classes based on desired type
+ * strictness. The following inner classes are supported:
+ *
+ * AData<types...>::Basic - strict type support using uint32_t flag.
+ *
+ * AData<types...>::Strict<Flag> - strict type support using custom flag.
+ * AData<types...>::Relaxed<Flag, MaxSize, Align>
+ * - relaxed type support with compatible (usually derived) class support
+ * for pointer types with added size checking for minimal additional
+ * safety.
+ *
+ * AData<types...>::RelaxedBasic - strict type support using uint32_t flag.
+ *
+ * AData<types...>::Custom<flagger> - custom type support (flaggers determine the supported types
+ * and the base type to use for each)
+ *
+ */
+template<typename ...Ts>
+struct AData {
+private:
+ static_assert(are_unique<Ts...>::value, "types must be unique");
+
+ static constexpr size_t num_types = sizeof...(Ts); ///< number of types to support
+
+public:
+ /**
+ * Default (strict) type flagger provided.
+ *
+ * The default flagger simply returns the index of the type within Ts, or 0 for void.
+ *
+ * Type flaggers return a flag for a supported type.
+ *
+ * They must provide:
+ *
+ * - a flagFor(T*) method for supported types _and_ for T=void. T=void is used to mark that no
+ * object is stored in the container. For this, an arbitrary unique value may be returned.
+ * - a mask field that contains the flag mask.
+ * - a canDeleteAs(Flag, Flag) flag comparison method that checks if a type of a flag can be
+ * deleted as another type.
+ *
+ * \param Flag the underlying unsigned integral to use for the flags.
+ */
+ template<typename Flag>
+ struct flagger {
+ private:
+ static_assert(std::is_unsigned<Flag>::value, "Flag must be unsigned");
+ static_assert(std::is_integral<Flag>::value, "Flag must be an integral type");
+
+ static constexpr Flag count = num_types + 1;
+
+ public:
+ typedef Flag type; ///< flag type
+
+ static constexpr Flag mask = _Flagged_helper::minMask<Flag>(count); ///< flag mask
+
+ /**
+ * Return the stored type for T. This is itself.
+ */
+ template<typename T>
+ struct store {
+ typedef T as_type; ///< the base type that T is stored as
+ };
+
+ /**
+ * Constexpr method that returns if two flags are compatible for deletion.
+ *
+ * \param objectFlag flag for object to be deleted
+ * \param deleteFlag flag for type that object is to be deleted as
+ */
+ static constexpr bool canDeleteAs(Flag objectFlag, Flag deleteFlag) {
+ // default flagger requires strict type equality
+ return objectFlag == deleteFlag;
+ }
+
+ /**
+ * Constexpr method that returns the flag to use for a given type.
+ *
+ * Function overload for void*.
+ */
+ static constexpr Flag flagFor(void*) {
+ return 0u;
+ }
+
+ /**
+ * Constexpr method that returns the flag to use for a given supported type (T).
+ */
+ template<typename T, typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
+ static constexpr Flag flagFor(T*) {
+ return find_first<T, Ts...>::index;
+ }
+ };
+
+ /**
+ * Relaxed flagger returns the index of the type within Ts. However, for pointers T* it returns
+ * the first type in Ts that T* can be converted into (this is normally a base type, but also
+ * works for sp<>, shared_ptr<> or unique_ptr<>). For a bit more strictness, the flag also
+ * contains the size of the class to avoid finding objects that were stored as a different
+ * derived class of the same base class.
+ *
+ * Flag is basically the index of the (base) type in Ts multiplied by the max size stored plus
+ * the size of the type (divided by alignment) for derived pointer types.
+ *
+ * \param MaxSize max supported size for derived class pointers
+ * \param Align alignment to assume for derived class pointers
+ */
+ template<typename Flag, size_t MaxSize=1024, size_t Align=4>
+ struct relaxed_flagger {
+ private:
+ static_assert(std::is_unsigned<Flag>::value, "Flag must be unsigned");
+ static_assert(std::is_integral<Flag>::value, "Flag must be an integral type");
+
+ static constexpr Flag count = num_types + 1;
+ static_assert(std::numeric_limits<Flag>::max() / count > (MaxSize / Align),
+ "not enough bits to fit into flag");
+
+ static constexpr Flag max_size_stored = MaxSize / Align + 1;
+
+ // T can be converted if it's size is <= MaxSize and it can be converted to one of the Ts
+ template<typename T, size_t size>
+ using enable_if_can_be_converted = typename std::enable_if<
+ (size / Align < max_size_stored
+ && find_first_convertible_to<T, Ts...>::index)>::type;
+
+
+ template<typename W, typename T, typename=enable_if_can_be_converted<W, sizeof(T)>>
+ static constexpr Flag relaxedFlagFor(W*, T*) {
+ return find_first_convertible_to<W, Ts...>::index * max_size_stored
+ + (is_one_of<W, Ts...>::value ? 0 : (sizeof(T) / Align));
+ }
+
+ public:
+ typedef Flag type; ///< flag type
+
+ static constexpr Flag mask =
+ _Flagged_helper::minMask<Flag>(count * max_size_stored); ///< flag mask
+
+ /**
+ * Constexpr method that returns if two flags are compatible for deletion.
+ *
+ * \param objectFlag flag for object to be deleted
+ * \param deleteFlag flag for type that object is to be deleted as
+ */
+ static constexpr bool canDeleteAs(Flag objectFlag, Flag deleteFlag) {
+ // can delete if objects have the same base type
+ return
+ objectFlag / max_size_stored == deleteFlag / max_size_stored &&
+ (deleteFlag % max_size_stored) == 0;
+ }
+
+ /**
+ * Constexpr method that returns the flag to use for a given type.
+ *
+ * Function overload for void*.
+ */
+ static constexpr Flag flagFor(void*) {
+ return 0u;
+ }
+
+ /**
+ * Constexpr method that returns the flag to use for a given supported type (T).
+ *
+ * This is a member method to enable both overloading as well as template specialization.
+ */
+ template<typename T, typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
+ static constexpr Flag flagFor(T*) {
+ return find_first<T, Ts...>::index * max_size_stored;
+ }
+
+ /**
+ * For precaution, we only consider converting pointers to their base classes.
+ */
+
+ /**
+ * Template specialization for derived class pointers and managed pointers.
+ */
+ template<typename T>
+ static constexpr Flag flagFor(T**p) { return relaxedFlagFor(p, (T*)0); }
+ template<typename T>
+ static constexpr Flag flagFor(std::shared_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
+ template<typename T>
+ static constexpr Flag flagFor(std::unique_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
+ template<typename T>
+ static constexpr Flag flagFor(std::weak_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
+ template<typename T>
+ static constexpr Flag flagFor(sp<T>*p) { return relaxedFlagFor(p, (T*)0); }
+ template<typename T>
+ static constexpr Flag flagFor(wp<T>*p) { return relaxedFlagFor(p, (T*)0); }
+
+ /**
+ * Type support template that provodes the stored type for T.
+ * This is itself if it is one of Ts, or the first type in Ts that T is convertible to.
+ *
+ * NOTE: This template may provide a base class for an unsupported type. Support is
+ * determined by flagFor().
+ */
+ template<typename T>
+ struct store {
+ typedef typename std::conditional<
+ is_one_of<T, Ts...>::value,
+ T,
+ typename find_first_convertible_to<T, Ts...>::type>::type as_type;
+ };
+ };
+
+ /**
+ * Implementation of AData.
+ */
+ template<typename Flagger>
+ struct Custom : protected Flagged<AUnion<Ts...>, typename Flagger::type, Flagger::mask> {
+ using data_t = AUnion<Ts...>;
+ using base_t = Flagged<AUnion<Ts...>, typename Flagger::type, Flagger::mask>;
+
+ /**
+ * Constructor. Initializes this to a container that does not contain any object.
+ */
+ Custom() : base_t(Flagger::flagFor((void*)0)) { }
+
+ /**
+ * Removes the contained object, if any.
+ */
+ ~Custom() {
+ if (!this->clear()) {
+ __builtin_trap();
+ // std::cerr << "could not delete data of type " << this->flags() << std::endl;
+ }
+ }
+
+ /**
+ * Returns whether there is any object contained.
+ */
+ inline bool used() const {
+ return this->flags() != Flagger::flagFor((void*)0);
+ }
+
+ /**
+ * Removes the contained object, if any. Returns true if there are no objects contained,
+ * or false on any error (this is highly unexpected).
+ */
+ bool clear() {
+ if (this->used()) {
+ if (_AData_deleter<Flagger, data_t, Ts...>::del(this->flags(), this->get())) {
+ this->setFlags(Flagger::flagFor((void*)0));
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ template<typename T>
+ using is_supported_by_flagger =
+ typename std::enable_if<Flagger::flagFor((T*)0) != Flagger::flagFor((void*)0)>::type;
+
+ /**
+ * Checks if there is a copiable object of type T in this container. If there is, it copies
+ * that object into the provided address and returns true. Otherwise, it does nothing and
+ * returns false.
+ *
+ * This method normally requires a flag equality between the stored and retrieved types.
+ * However, it also allows retrieving the stored object as the stored type
+ * (usually base type).
+ *
+ * \param T type of object to sought
+ * \param data address at which the object should be retrieved
+ *
+ * \return true if the object was retrieved. false if it was not.
+ */
+ template<
+ typename T,
+ typename=is_supported_by_flagger<T>>
+ bool find(T *data) const {
+ using B = typename Flagger::template store<T>::as_type;
+ if (this->flags() == Flagger::flagFor((T*)0) ||
+ Flagger::canDeleteAs(this->flags(), Flagger::flagFor((T*)0))) {
+ _AData_copier<T>::assign(data, this->get().template get<B>());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if there is an object of type T in this container. If there is, it moves that
+ * object into the provided address and returns true. Otherwise, it does nothing and returns
+ * false.
+ *
+ * This method normally requires a flag equality between the stored and retrieved types.
+ * However, it also allows retrieving the stored object as the stored type
+ * (usually base type).
+ *
+ * \param T type of object to sought
+ * \param data address at which the object should be retrieved.
+ *
+ * \return true if the object was retrieved. false if it was not.
+ */
+ template<
+ typename T,
+ typename=is_supported_by_flagger<T>>
+ bool remove(T *data) {
+ using B = typename Flagger::template store<T>::as_type;
+ if (this->flags() == Flagger::flagFor((T*)0) ||
+ Flagger::canDeleteAs(this->flags(), Flagger::flagFor((T*)0))) {
+ _AData_mover<T>::assign(data, std::move(this->get().template get<B>()));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stores an object into this container by copying. If it was successful, returns true.
+ * Otherwise, (e.g. it could not destroy the already stored object) it returns false. This
+ * latter would be highly unexpected.
+ *
+ * \param T type of object to store
+ * \param data object to store
+ *
+ * \return true if the object was stored. false if it was not.
+ */
+ template<
+ typename T,
+ typename=is_supported_by_flagger<T>,
+ typename=typename std::enable_if<
+ std::is_copy_constructible<T>::value ||
+ (std::is_default_constructible<T>::value &&
+ std::is_copy_assignable<T>::value)>::type>
+ bool set(const T &data) {
+ using B = typename Flagger::template store<T>::as_type;
+
+ // if already contains an object of this type, simply assign
+ if (this->flags() == Flagger::flagFor((T*)0) && std::is_same<T, B>::value) {
+ _AData_copier<B>::assign(&this->get().template get<B>(), data);
+ return true;
+ } else if (this->used()) {
+ // destroy previous object
+ if (!this->clear()) {
+ return false;
+ }
+ }
+ this->get().template emplace<B>(data);
+ this->setFlags(Flagger::flagFor((T *)0));
+ return true;
+ }
+
+ /**
+ * Moves an object into this container. If it was successful, returns true. Otherwise,
+ * (e.g. it could not destroy the already stored object) it returns false. This latter
+ * would be highly unexpected.
+ *
+ * \param T type of object to store
+ * \param data object to store
+ *
+ * \return true if the object was stored. false if it was not.
+ */
+ template<
+ typename T,
+ typename=is_supported_by_flagger<T>>
+ bool set(T &&data) {
+ using B = typename Flagger::template store<T>::as_type;
+
+ // if already contains an object of this type, simply assign
+ if (this->flags() == Flagger::flagFor((T*)0) && std::is_same<T, B>::value) {
+ _AData_mover<B>::assign(&this->get().template get<B>(), std::forward<T&&>(data));
+ return true;
+ } else if (this->used()) {
+ // destroy previous object
+ if (!this->clear()) {
+ return false;
+ }
+ }
+ this->get().template emplace<B>(std::forward<T&&>(data));
+ this->setFlags(Flagger::flagFor((T *)0));
+ return true;
+ }
+ };
+
+ /**
+ * Basic AData using the default type flagger and requested flag type.
+ *
+ * \param Flag desired flag type to use. Must be an unsigned and std::integral type.
+ */
+ template<typename Flag>
+ using Strict = Custom<flagger<Flag>>;
+
+ /**
+ * Basic AData using the default type flagger and uint32_t flag.
+ */
+ using Basic = Strict<uint32_t>;
+
+ /**
+ * AData using the relaxed type flagger for max size and requested flag type.
+ *
+ * \param Flag desired flag type to use. Must be an unsigned and std::integral type.
+ */
+ template<typename Flag, size_t MaxSize = 1024, size_t Align = 4>
+ using Relaxed = Custom<relaxed_flagger<Flag, MaxSize, Align>>;
+
+ /**
+ * Basic AData using the relaxed type flagger and uint32_t flag.
+ */
+ using RelaxedBasic = Relaxed<uint32_t>;
+};
+
+} // namespace android
+
+#endif // STAGEFRIGHT_FOUNDATION_A_DATA_H_
+
diff --git a/include/media/stagefright/foundation/TypeTraits.h b/include/media/stagefright/foundation/TypeTraits.h
index 2eaec35..1250e9b 100644
--- a/include/media/stagefright/foundation/TypeTraits.h
+++ b/include/media/stagefright/foundation/TypeTraits.h
@@ -85,6 +85,139 @@
typename underlying_integral_type<T, signed>::type>::value> {
};
+/**
+ * Type support relationship query template.
+ *
+ * If T occurs as one of the types in Us with the same const-volatile qualifications, provides the
+ * member constant |value| equal to true. Otherwise value is false.
+ */
+template<typename T, typename ...Us>
+struct is_one_of;
+
+/// \if 0
+/**
+ * Template specialization when first type matches the searched type.
+ */
+template<typename T, typename ...Us>
+struct is_one_of<T, T, Us...> : std::true_type {};
+
+/**
+ * Template specialization when first type does not match the searched type.
+ */
+template<typename T, typename U, typename ...Us>
+struct is_one_of<T, U, Us...> : is_one_of<T, Us...> {};
+
+/**
+ * Template specialization when there are no types to search.
+ */
+template<typename T>
+struct is_one_of<T> : std::false_type {};
+/// \endif
+
+/**
+ * Type support relationship query template.
+ *
+ * If all types in Us are unique, provides the member constant |value| equal to true.
+ * Otherwise value is false.
+ */
+template<typename ...Us>
+struct are_unique;
+
+/// \if 0
+/**
+ * Template specialization when there are no types.
+ */
+template<>
+struct are_unique<> : std::true_type {};
+
+/**
+ * Template specialization when there is at least one type to check.
+ */
+template<typename T, typename ...Us>
+struct are_unique<T, Us...>
+ : std::integral_constant<bool, are_unique<Us...>::value && !is_one_of<T, Us...>::value> {};
+/// \endif
+
+/// \if 0
+template<size_t Base, typename T, typename ...Us>
+struct _find_first_impl;
+
+/**
+ * Template specialization when there are no types to search.
+ */
+template<size_t Base, typename T>
+struct _find_first_impl<Base, T> : std::integral_constant<size_t, 0> {};
+
+/**
+ * Template specialization when T is the first type in Us.
+ */
+template<size_t Base, typename T, typename ...Us>
+struct _find_first_impl<Base, T, T, Us...> : std::integral_constant<size_t, Base> {};
+
+/**
+ * Template specialization when T is not the first type in Us.
+ */
+template<size_t Base, typename T, typename U, typename ...Us>
+struct _find_first_impl<Base, T, U, Us...>
+ : std::integral_constant<size_t, _find_first_impl<Base + 1, T, Us...>::value> {};
+
+/// \endif
+
+/**
+ * Type support relationship query template.
+ *
+ * If T occurs in Us, index is the 1-based left-most index of T in Us. Otherwise, index is 0.
+ */
+template<typename T, typename ...Us>
+struct find_first {
+ static constexpr size_t index = _find_first_impl<1, T, Us...>::value;
+};
+
+/// \if 0
+/**
+ * Helper class for find_first_convertible_to template.
+ *
+ * Adds a base index.
+ */
+template<size_t Base, typename T, typename ...Us>
+struct _find_first_convertible_to_helper;
+
+/**
+ * Template specialization for when there are more types to consider
+ */
+template<size_t Base, typename T, typename U, typename ...Us>
+struct _find_first_convertible_to_helper<Base, T, U, Us...> {
+ static constexpr size_t index =
+ std::is_convertible<T, U>::value ? Base :
+ _find_first_convertible_to_helper<Base + 1, T, Us...>::index;
+ typedef typename std::conditional<
+ std::is_convertible<T, U>::value, U,
+ typename _find_first_convertible_to_helper<Base + 1, T, Us...>::type>::type type;
+};
+
+/**
+ * Template specialization for when there are no more types to consider
+ */
+template<size_t Base, typename T>
+struct _find_first_convertible_to_helper<Base, T> {
+ static constexpr size_t index = 0;
+ typedef void type;
+};
+
+/// \endif
+
+/**
+ * Type support template that returns the type that T can be implicitly converted into, and its
+ * index, from a list of other types (Us).
+ *
+ * Returns index of 0 and type of void if there are no convertible types.
+ *
+ * \param T type that is converted
+ * \param Us types into which the conversion is considered
+ */
+template<typename T, typename ...Us>
+struct find_first_convertible_to : public _find_first_convertible_to_helper<1, T, Us...> { };
+
} // namespace android
#endif // STAGEFRIGHT_FOUNDATION_TYPE_TRAITS_H_
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 3c7e8b7..aef7dfb 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -671,6 +671,8 @@
mState = STATE_STOPPING;
} else {
mState = STATE_STOPPED;
+ ALOGD_IF(mSharedBuffer == nullptr,
+ "stop() called with %u frames delivered", mReleased.value());
mReleased = 0;
}
diff --git a/media/libaudioclient/IEffect.cpp b/media/libaudioclient/IEffect.cpp
index 115ca75..ce72dae 100644
--- a/media/libaudioclient/IEffect.cpp
+++ b/media/libaudioclient/IEffect.cpp
@@ -25,6 +25,9 @@
namespace android {
+// Maximum command/reply size expected
+#define EFFECT_PARAM_SIZE_MAX 65536
+
enum {
ENABLE = IBinder::FIRST_CALL_TRANSACTION,
DISABLE,
@@ -156,6 +159,10 @@
uint32_t cmdSize = data.readInt32();
char *cmd = NULL;
if (cmdSize) {
+ if (cmdSize > EFFECT_PARAM_SIZE_MAX) {
+ reply->writeInt32(NO_MEMORY);
+ return NO_ERROR;
+ }
cmd = (char *)calloc(cmdSize, 1);
if (cmd == NULL) {
reply->writeInt32(NO_MEMORY);
@@ -167,6 +174,11 @@
uint32_t replySz = replySize;
char *resp = NULL;
if (replySize) {
+ if (replySize > EFFECT_PARAM_SIZE_MAX) {
+ free(cmd);
+ reply->writeInt32(NO_MEMORY);
+ return NO_ERROR;
+ }
resp = (char *)calloc(replySize, 1);
if (resp == NULL) {
free(cmd);
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
deleted file mode 100644
index e943eed..0000000
--- a/media/libaudiohal/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-cc_library_shared {
- name: "libaudiohal",
-
- srcs: [
- "DeviceHalLocal.cpp",
- "DevicesFactoryHalLocal.cpp",
- "EffectHalLocal.cpp",
- "EffectsFactoryHalLocal.cpp",
- "StreamHalLocal.cpp",
- ],
-
- shared_libs: [
- "libcutils",
- "libhardware",
- "liblog",
- "libeffects",
- "libutils",
- ],
-
- cflags: [
- "-Werror",
- "-Wall",
- ],
-}
diff --git a/media/libaudiohal/Android.mk b/media/libaudiohal/Android.mk
new file mode 100644
index 0000000..63aa9c6
--- /dev/null
+++ b/media/libaudiohal/Android.mk
@@ -0,0 +1,50 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libhardware \
+ liblog \
+ libutils \
+ libeffects
+
+ifeq ($(ENABLE_TREBLE), true)
+
+LOCAL_CFLAGS += -DENABLE_TREBLE
+
+LOCAL_SRC_FILES := \
+ ConversionHelperHidl.cpp \
+ DeviceHalHidl.cpp \
+ DevicesFactoryHalHidl.cpp \
+ EffectHalHidl.cpp \
+ EffectsFactoryHalHidl.cpp \
+ StreamHalHidl.cpp
+
+LOCAL_SHARED_LIBRARIES += \
+ libhwbinder \
+ libhidlbase \
+ libhidltransport \
+ libbase \
+ android.hardware.audio@2.0 \
+ android.hardware.audio.common@2.0 \
+ android.hardware.audio.common@2.0-util \
+ android.hardware.audio.effect@2.0
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
+
+else # if !ENABLE_TREBLE
+
+LOCAL_SRC_FILES := \
+ DeviceHalLocal.cpp \
+ DevicesFactoryHalLocal.cpp \
+ EffectHalLocal.cpp \
+ EffectsFactoryHalLocal.cpp \
+ StreamHalLocal.cpp
+endif # ENABLE_TREBLE
+
+LOCAL_MODULE := libaudiohal
+
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libaudiohal/ConversionHelperHidl.cpp b/media/libaudiohal/ConversionHelperHidl.cpp
new file mode 100644
index 0000000..ebbc02c
--- /dev/null
+++ b/media/libaudiohal/ConversionHelperHidl.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#define LOG_TAG "HalHidl"
+#include <media/AudioParameter.h>
+#include <utils/Log.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V2_0::Result;
+
+namespace android {
+
+// static
+status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys) {
+ AudioParameter halKeys(keys);
+ if (halKeys.size() == 0) return BAD_VALUE;
+ hidlKeys->resize(halKeys.size());
+ for (size_t i = 0; i < halKeys.size(); ++i) {
+ String8 key;
+ status_t status = halKeys.getAt(i, key);
+ if (status != OK) return status;
+ (*hidlKeys)[i] = key.string();
+ }
+ return OK;
+}
+
+// static
+status_t ConversionHelperHidl::parametersFromHal(
+ const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams) {
+ AudioParameter params(kvPairs);
+ if (params.size() == 0) return BAD_VALUE;
+ hidlParams->resize(params.size());
+ for (size_t i = 0; i < params.size(); ++i) {
+ String8 key, value;
+ status_t status = params.getAt(i, key, value);
+ if (status != OK) return status;
+ (*hidlParams)[i].key = key.string();
+ (*hidlParams)[i].value = value.string();
+ }
+ return OK;
+}
+
+// static
+void ConversionHelperHidl::parametersToHal(
+ const hidl_vec<ParameterValue>& parameters, String8 *values) {
+ AudioParameter params;
+ for (size_t i = 0; i < parameters.size(); ++i) {
+ params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str()));
+ }
+ values->setTo(params.toString());
+}
+
+ConversionHelperHidl::ConversionHelperHidl(const char* className)
+ : mClassName(className) {
+}
+
+// static
+status_t ConversionHelperHidl::analyzeResult(const Result& result) {
+ switch (result) {
+ case Result::OK: return OK;
+ case Result::INVALID_ARGUMENTS: return BAD_VALUE;
+ case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
+ case Result::NOT_INITIALIZED: return NO_INIT;
+ case Result::NOT_SUPPORTED: return INVALID_OPERATION;
+ default: return NO_INIT;
+ }
+}
+
+status_t ConversionHelperHidl::processReturn(const char* funcName, const Status& status) {
+ const status_t st = status.transactionError();
+ ALOGE_IF(st, "%s %p %s: %s (from rpc)", mClassName, this, funcName, strerror(-st));
+ return st;
+}
+
+status_t ConversionHelperHidl::processReturn(
+ const char* funcName, const Status& status, hardware::audio::V2_0::Result retval) {
+ const status_t st = status.isOk() ? analyzeResult(retval) : status.transactionError();
+ ALOGE_IF(st, "%s %p %s: %s (from %s)",
+ mClassName, this, funcName, strerror(-st), status.isOk() ? "hal" : "rpc");
+ return st;
+}
+
+} // namespace android
diff --git a/media/libaudiohal/ConversionHelperHidl.h b/media/libaudiohal/ConversionHelperHidl.h
new file mode 100644
index 0000000..628913a
--- /dev/null
+++ b/media/libaudiohal/ConversionHelperHidl.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
+#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
+
+#include <android/hardware/audio/2.0/types.h>
+#include <hidl/HidlSupport.h>
+#include <utils/String8.h>
+
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+
+class ConversionHelperHidl {
+ protected:
+ static status_t keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys);
+ static status_t parametersFromHal(const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams);
+ static void parametersToHal(const hidl_vec<ParameterValue>& parameters, String8 *values);
+
+ ConversionHelperHidl(const char* className);
+
+ status_t processReturn(const char* funcName, const Return<void>& ret) {
+ return processReturn(funcName, ret.getStatus());
+ }
+
+ template<typename R, typename T>
+ status_t processReturn(const char* funcName, const Return<R>& ret, T *retval) {
+ if (ret.getStatus().isOk()) {
+ // This way it also works for enum class to unscoped enum conversion.
+ *retval = static_cast<T>(static_cast<R>(ret));
+ return OK;
+ }
+ return processReturn(funcName, ret.getStatus());
+ }
+
+ status_t processReturn(const char* funcName, const Return<hardware::audio::V2_0::Result>& ret) {
+ return processReturn(funcName, ret, ret);
+ }
+
+ template<typename T>
+ status_t processReturn(
+ const char* funcName, const Return<T>& ret, hardware::audio::V2_0::Result retval) {
+ return processReturn(funcName, ret.getStatus(), retval);
+ }
+
+ private:
+ const char* mClassName;
+
+ static status_t analyzeResult(const hardware::audio::V2_0::Result& result);
+ status_t processReturn(const char* funcName, const Status& status);
+ status_t processReturn(
+ const char* funcName, const Status& status, hardware::audio::V2_0::Result retval);
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
diff --git a/media/libaudiohal/DeviceHalHidl.cpp b/media/libaudiohal/DeviceHalHidl.cpp
new file mode 100644
index 0000000..a6ced12
--- /dev/null
+++ b/media/libaudiohal/DeviceHalHidl.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#define LOG_TAG "DeviceHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/2.0/IPrimaryDevice.h>
+#include <cutils/native_handle.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "HidlUtils.h"
+#include "StreamHalHidl.h"
+
+using ::android::hardware::audio::common::V2_0::AudioConfig;
+using ::android::hardware::audio::common::V2_0::AudioDevice;
+using ::android::hardware::audio::common::V2_0::AudioInputFlag;
+using ::android::hardware::audio::common::V2_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V2_0::AudioPatchHandle;
+using ::android::hardware::audio::common::V2_0::AudioPort;
+using ::android::hardware::audio::common::V2_0::AudioPortConfig;
+using ::android::hardware::audio::common::V2_0::AudioMode;
+using ::android::hardware::audio::common::V2_0::AudioSource;
+using ::android::hardware::audio::V2_0::DeviceAddress;
+using ::android::hardware::audio::V2_0::IPrimaryDevice;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+
+namespace {
+
+status_t deviceAddressFromHal(
+ audio_devices_t device, const char* halAddress, DeviceAddress* address) {
+ address->device = AudioDevice(device);
+ const bool isInput = (device & AUDIO_DEVICE_BIT_IN) != 0;
+ if (isInput) device &= ~AUDIO_DEVICE_BIT_IN;
+ if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_A2DP) != 0)
+ || (isInput && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) != 0)) {
+ int status = sscanf(halAddress,
+ "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX",
+ &address->address.mac[0], &address->address.mac[1], &address->address.mac[2],
+ &address->address.mac[3], &address->address.mac[4], &address->address.mac[5]);
+ return status == 6 ? OK : BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_IP) != 0)
+ || (isInput && (device & AUDIO_DEVICE_IN_IP) != 0)) {
+ int status = sscanf(halAddress,
+ "%hhu.%hhu.%hhu.%hhu",
+ &address->address.ipv4[0], &address->address.ipv4[1],
+ &address->address.ipv4[2], &address->address.ipv4[3]);
+ return status == 4 ? OK : BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_USB)) != 0
+ || (isInput && (device & AUDIO_DEVICE_IN_ALL_USB)) != 0) {
+ int status = sscanf(halAddress,
+ "card=%d;device=%d",
+ &address->address.alsa.card, &address->address.alsa.device);
+ return status == 2 ? OK : BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_BUS) != 0)
+ || (isInput && (device & AUDIO_DEVICE_IN_BUS) != 0)) {
+ if (halAddress != NULL) {
+ address->busAddress = halAddress;
+ return OK;
+ }
+ return BAD_VALUE;
+ } else if ((!isInput && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) != 0
+ || (isInput && (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) != 0)) {
+ if (halAddress != NULL) {
+ address->rSubmixAddress = halAddress;
+ return OK;
+ }
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+} // namespace
+
+DeviceHalHidl::DeviceHalHidl(const sp<IDevice>& device)
+ : ConversionHelperHidl("Device"), mDevice(device) {
+}
+
+DeviceHalHidl::~DeviceHalHidl() {
+}
+
+status_t DeviceHalHidl::getSupportedDevices(uint32_t*) {
+ // Obsolete.
+ return INVALID_OPERATION;
+}
+
+status_t DeviceHalHidl::initCheck() {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("initCheck", mDevice->initCheck());
+}
+
+status_t DeviceHalHidl::setVoiceVolume(float volume) {
+ if (mDevice == 0) return NO_INIT;
+ sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+ if (primaryDev == 0) return INVALID_OPERATION;
+ return processReturn("setVoiceVolume", primaryDev->setVoiceVolume(volume));
+}
+
+status_t DeviceHalHidl::setMasterVolume(float volume) {
+ if (mDevice == 0) return NO_INIT;
+ sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+ if (primaryDev == 0) return INVALID_OPERATION;
+ return processReturn("setMasterVolume", primaryDev->setMasterVolume(volume));
+}
+
+status_t DeviceHalHidl::getMasterVolume(float *volume) {
+ if (mDevice == 0) return NO_INIT;
+ sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+ if (primaryDev == 0) return INVALID_OPERATION;
+ Result retval;
+ Return<void> ret = primaryDev->getMasterVolume(
+ [&](Result r, float v) {
+ retval = r;
+ if (retval == Result::OK) {
+ *volume = v;
+ }
+ });
+ return processReturn("getMasterVolume", ret, retval);
+}
+
+status_t DeviceHalHidl::setMode(audio_mode_t mode) {
+ if (mDevice == 0) return NO_INIT;
+ sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+ if (primaryDev == 0) return INVALID_OPERATION;
+ return processReturn("setMode", primaryDev->setMode(AudioMode(mode)));
+}
+
+status_t DeviceHalHidl::setMicMute(bool state) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("setMicMute", mDevice->setMicMute(state));
+}
+
+status_t DeviceHalHidl::getMicMute(bool *state) {
+ if (mDevice == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mDevice->getMicMute(
+ [&](Result r, bool mute) {
+ retval = r;
+ if (retval == Result::OK) {
+ *state = mute;
+ }
+ });
+ return processReturn("getMicMute", ret, retval);
+}
+
+status_t DeviceHalHidl::setMasterMute(bool state) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("setMasterMute", mDevice->setMasterMute(state));
+}
+
+status_t DeviceHalHidl::getMasterMute(bool *state) {
+ if (mDevice == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mDevice->getMasterMute(
+ [&](Result r, bool mute) {
+ retval = r;
+ if (retval == Result::OK) {
+ *state = mute;
+ }
+ });
+ return processReturn("getMasterMute", ret, retval);
+}
+
+status_t DeviceHalHidl::setParameters(const String8& kvPairs) {
+ if (mDevice == 0) return NO_INIT;
+ hidl_vec<ParameterValue> hidlParams;
+ status_t status = parametersFromHal(kvPairs, &hidlParams);
+ if (status != OK) return status;
+ return processReturn("setParameters", mDevice->setParameters(hidlParams));
+}
+
+status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) {
+ values->clear();
+ if (mDevice == 0) return NO_INIT;
+ hidl_vec<hidl_string> hidlKeys;
+ status_t status = keysFromHal(keys, &hidlKeys);
+ if (status != OK) return status;
+ Result retval;
+ Return<void> ret = mDevice->getParameters(
+ hidlKeys,
+ [&](Result r, const hidl_vec<ParameterValue>& parameters) {
+ retval = r;
+ if (retval == Result::OK) {
+ parametersToHal(parameters, values);
+ }
+ });
+ return processReturn("getParameters", ret, retval);
+}
+
+status_t DeviceHalHidl::getInputBufferSize(
+ const struct audio_config *config, size_t *size) {
+ if (mDevice == 0) return NO_INIT;
+ AudioConfig hidlConfig;
+ HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+ Result retval;
+ Return<void> ret = mDevice->getInputBufferSize(
+ hidlConfig,
+ [&](Result r, uint64_t bufferSize) {
+ retval = r;
+ if (retval == Result::OK) {
+ *size = static_cast<size_t>(bufferSize);
+ }
+ });
+ return processReturn("getInputBufferSize", ret, retval);
+}
+
+status_t DeviceHalHidl::openOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ sp<StreamOutHalInterface> *outStream) {
+ if (mDevice == 0) return NO_INIT;
+ DeviceAddress hidlDevice;
+ status_t status = deviceAddressFromHal(devices, address, &hidlDevice);
+ if (status != OK) return status;
+ AudioConfig hidlConfig;
+ HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mDevice->openOutputStream(
+ handle,
+ hidlDevice,
+ hidlConfig,
+ AudioOutputFlag(flags),
+ [&](Result r, const sp<IStreamOut>& result, const AudioConfig& suggestedConfig) {
+ retval = r;
+ if (retval == Result::OK) {
+ *outStream = new StreamOutHalHidl(result);
+ }
+ HidlUtils::audioConfigToHal(suggestedConfig, config);
+ });
+ return processReturn("openOutputStream", ret, retval);
+}
+
+status_t DeviceHalHidl::openInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source,
+ sp<StreamInHalInterface> *inStream) {
+ if (mDevice == 0) return NO_INIT;
+ DeviceAddress hidlDevice;
+ status_t status = deviceAddressFromHal(devices, address, &hidlDevice);
+ if (status != OK) return status;
+ AudioConfig hidlConfig;
+ HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mDevice->openInputStream(
+ handle,
+ hidlDevice,
+ hidlConfig,
+ AudioInputFlag(flags),
+ AudioSource(source),
+ [&](Result r, const sp<IStreamIn>& result, const AudioConfig& suggestedConfig) {
+ retval = r;
+ if (retval == Result::OK) {
+ *inStream = new StreamInHalHidl(result);
+ }
+ HidlUtils::audioConfigToHal(suggestedConfig, config);
+ });
+ return processReturn("openInputStream", ret, retval);
+}
+
+status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches);
+}
+
+status_t DeviceHalHidl::createAudioPatch(
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *patch) {
+ if (mDevice == 0) return NO_INIT;
+ hidl_vec<AudioPortConfig> hidlSources, hidlSinks;
+ HidlUtils::audioPortConfigsFromHal(num_sources, sources, &hidlSources);
+ HidlUtils::audioPortConfigsFromHal(num_sinks, sinks, &hidlSinks);
+ Result retval;
+ Return<void> ret = mDevice->createAudioPatch(
+ hidlSources, hidlSinks,
+ [&](Result r, AudioPatchHandle hidlPatch) {
+ retval = r;
+ if (retval == Result::OK) {
+ *patch = static_cast<audio_patch_handle_t>(hidlPatch);
+ }
+ });
+ return processReturn("createAudioPatch", ret, retval);
+}
+
+status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch));
+}
+
+status_t DeviceHalHidl::getAudioPort(struct audio_port *port) {
+ if (mDevice == 0) return NO_INIT;
+ AudioPort hidlPort;
+ HidlUtils::audioPortFromHal(*port, &hidlPort);
+ Result retval;
+ Return<void> ret = mDevice->getAudioPort(
+ hidlPort,
+ [&](Result r, const AudioPort& p) {
+ retval = r;
+ if (retval == Result::OK) {
+ HidlUtils::audioPortToHal(p, port);
+ }
+ });
+ return processReturn("getAudioPort", ret, retval);
+}
+
+status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) {
+ if (mDevice == 0) return NO_INIT;
+ AudioPortConfig hidlConfig;
+ HidlUtils::audioPortConfigFromHal(*config, &hidlConfig);
+ return processReturn("setAudioPortConfig", mDevice->setAudioPortConfig(hidlConfig));
+}
+
+status_t DeviceHalHidl::dump(int fd) {
+ if (mDevice == 0) return NO_INIT;
+ native_handle_t* hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = fd;
+ Return<void> ret = mDevice->debugDump(hidlHandle);
+ native_handle_delete(hidlHandle);
+ return processReturn("dump", ret);
+}
+
+} // namespace android
diff --git a/media/libaudiohal/DeviceHalHidl.h b/media/libaudiohal/DeviceHalHidl.h
new file mode 100644
index 0000000..9da02a4
--- /dev/null
+++ b/media/libaudiohal/DeviceHalHidl.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICE_HAL_HIDL_H
+#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <media/audiohal/DeviceHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::Return;
+
+namespace android {
+
+class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl
+{
+ public:
+ // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
+ virtual status_t getSupportedDevices(uint32_t *devices);
+
+ // Check to see if the audio hardware interface has been initialized.
+ virtual status_t initCheck();
+
+ // Set the audio volume of a voice call. Range is between 0.0 and 1.0.
+ virtual status_t setVoiceVolume(float volume);
+
+ // Set the audio volume for all audio activities other than voice call.
+ virtual status_t setMasterVolume(float volume);
+
+ // Get the current master volume value for the HAL.
+ virtual status_t getMasterVolume(float *volume);
+
+ // Called when the audio mode changes.
+ virtual status_t setMode(audio_mode_t mode);
+
+ // Muting control.
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool *state);
+ virtual status_t setMasterMute(bool state);
+ virtual status_t getMasterMute(bool *state);
+
+ // Set global audio parameters.
+ virtual status_t setParameters(const String8& kvPairs);
+
+ // Get global audio parameters.
+ virtual status_t getParameters(const String8& keys, String8 *values);
+
+ // Returns audio input buffer size according to parameters passed.
+ virtual status_t getInputBufferSize(const struct audio_config *config,
+ size_t *size);
+
+ // Creates and opens the audio hardware output stream. The stream is closed
+ // by releasing all references to the returned object.
+ virtual status_t openOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ const char *address,
+ sp<StreamOutHalInterface> *outStream);
+
+ // Creates and opens the audio hardware input stream. The stream is closed
+ // by releasing all references to the returned object.
+ virtual status_t openInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source,
+ sp<StreamInHalInterface> *inStream);
+
+ // Returns whether createAudioPatch and releaseAudioPatch operations are supported.
+ virtual status_t supportsAudioPatches(bool *supportsPatches);
+
+ // Creates an audio patch between several source and sink ports.
+ virtual status_t createAudioPatch(
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *patch);
+
+ // Releases an audio patch.
+ virtual status_t releaseAudioPatch(audio_patch_handle_t patch);
+
+ // Fills the list of supported attributes for a given audio port.
+ virtual status_t getAudioPort(struct audio_port *port);
+
+ // Set audio port configuration.
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
+ virtual status_t dump(int fd);
+
+ private:
+ friend class DevicesFactoryHalHidl;
+ sp<IDevice> mDevice;
+
+ // Can not be constructed directly by clients.
+ explicit DeviceHalHidl(const sp<IDevice>& device);
+
+ // The destructor automatically closes the device.
+ virtual ~DeviceHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H
diff --git a/media/libaudiohal/DeviceHalLocal.cpp b/media/libaudiohal/DeviceHalLocal.cpp
index 78adfef..fc098f5 100644
--- a/media/libaudiohal/DeviceHalLocal.cpp
+++ b/media/libaudiohal/DeviceHalLocal.cpp
@@ -109,11 +109,17 @@
const char *address,
sp<StreamOutHalInterface> *outStream) {
audio_stream_out_t *halStream;
+ ALOGV("open_output_stream handle: %d devices: %x flags: %#x"
+ "srate: %d format %#x channels %x address %s",
+ handle, devices, flags,
+ config->sample_rate, config->format, config->channel_mask,
+ address);
int openResut = mDev->open_output_stream(
mDev, handle, devices, flags, config, &halStream, address);
if (openResut == OK) {
*outStream = new StreamOutHalLocal(halStream, this);
}
+ ALOGV("open_output_stream status %d stream %p", openResut, halStream);
return openResut;
}
@@ -126,11 +132,17 @@
audio_source_t source,
sp<StreamInHalInterface> *inStream) {
audio_stream_in_t *halStream;
+ ALOGV("open_input_stream handle: %d devices: %x flags: %#x "
+ "srate: %d format %#x channels %x address %s source %d",
+ handle, devices, flags,
+ config->sample_rate, config->format, config->channel_mask,
+ address, source);
int openResult = mDev->open_input_stream(
mDev, handle, devices, config, &halStream, flags, address, source);
if (openResult == OK) {
*inStream = new StreamInHalLocal(halStream, this);
}
+ ALOGV("open_input_stream status %d stream %p", openResult, inStream);
return openResult;
}
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/DevicesFactoryHalHidl.cpp
new file mode 100644
index 0000000..155b1a8
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHidl.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#define LOG_TAG "DevicesFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "DevicesFactoryHalHidl.h"
+
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+
+namespace android {
+
+// static
+sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
+ return new DevicesFactoryHalHidl();
+}
+
+DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
+ mDevicesFactory = IDevicesFactory::getService("audio_devices_factory");
+}
+
+DevicesFactoryHalHidl::~DevicesFactoryHalHidl() {
+}
+
+// static
+status_t DevicesFactoryHalHidl::nameFromHal(const char *name, IDevicesFactory::Device *device) {
+ if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) {
+ *device = IDevicesFactory::Device::PRIMARY;
+ return OK;
+ } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) {
+ *device = IDevicesFactory::Device::A2DP;
+ return OK;
+ } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) {
+ *device = IDevicesFactory::Device::USB;
+ return OK;
+ } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) {
+ *device = IDevicesFactory::Device::R_SUBMIX;
+ return OK;
+ }
+ ALOGE("Invalid device name %s", name);
+ return BAD_VALUE;
+}
+
+status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+ if (mDevicesFactory == 0) return NO_INIT;
+ IDevicesFactory::Device hidlDevice;
+ status_t status = nameFromHal(name, &hidlDevice);
+ if (status != OK) return status;
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mDevicesFactory->openDevice(
+ hidlDevice,
+ [&](Result r, const sp<IDevice>& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ *device = new DeviceHalHidl(result);
+ }
+ });
+ if (ret.getStatus().isOk()) {
+ if (retval == Result::OK) return OK;
+ else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE;
+ else return NO_INIT;
+ }
+ return ret.getStatus().transactionError();
+}
+
+} // namespace android
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.h b/media/libaudiohal/DevicesFactoryHalHidl.h
new file mode 100644
index 0000000..a26dec1
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHidl.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
+
+#include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include "DeviceHalHidl.h"
+
+using ::android::hardware::audio::V2_0::IDevicesFactory;
+
+namespace android {
+
+class DevicesFactoryHalHidl : public DevicesFactoryHalInterface
+{
+ public:
+ // Opens a device with the specified name. To close the device, it is
+ // necessary to release references to the returned object.
+ virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+ private:
+ friend class DevicesFactoryHalInterface;
+
+ sp<IDevicesFactory> mDevicesFactory;
+
+ static status_t nameFromHal(const char *name, IDevicesFactory::Device *device);
+
+ // Can not be constructed directly by clients.
+ DevicesFactoryHalHidl();
+
+ virtual ~DevicesFactoryHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.h b/media/libaudiohal/DevicesFactoryHalLocal.h
index 690cd34..58ce4ff 100644
--- a/media/libaudiohal/DevicesFactoryHalLocal.h
+++ b/media/libaudiohal/DevicesFactoryHalLocal.h
@@ -28,8 +28,6 @@
class DevicesFactoryHalLocal : public DevicesFactoryHalInterface
{
public:
- virtual ~DevicesFactoryHalLocal() {}
-
// Opens a device with the specified name. To close the device, it is
// necessary to release references to the returned object.
virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
@@ -39,6 +37,8 @@
// Can not be constructed directly by clients.
DevicesFactoryHalLocal() {}
+
+ virtual ~DevicesFactoryHalLocal() {}
};
} // namespace android
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
new file mode 100644
index 0000000..b508cb5
--- /dev/null
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <media/EffectsFactoryApi.h>
+#include <utils/Log.h>
+
+#include "EffectHalHidl.h"
+#include "HidlUtils.h"
+
+using ::android::hardware::audio::effect::V2_0::Result;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+
+namespace android {
+
+EffectHalHidl::EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId)
+ : mEffect(effect), mEffectId(effectId) {
+}
+
+EffectHalHidl::~EffectHalHidl() {
+}
+
+// static
+void EffectHalHidl::effectDescriptorToHal(
+ const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor) {
+ HidlUtils::uuidToHal(descriptor.type, &halDescriptor->type);
+ HidlUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid);
+ halDescriptor->flags = static_cast<uint32_t>(descriptor.flags);
+ halDescriptor->cpuLoad = descriptor.cpuLoad;
+ halDescriptor->memoryUsage = descriptor.memoryUsage;
+ memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size());
+ memcpy(halDescriptor->implementor,
+ descriptor.implementor.data(), descriptor.implementor.size());
+}
+
+// static
+status_t EffectHalHidl::analyzeResult(const Result& result) {
+ switch (result) {
+ case Result::OK: return OK;
+ case Result::INVALID_ARGUMENTS: return BAD_VALUE;
+ case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
+ case Result::NOT_INITIALIZED: return NO_INIT;
+ case Result::NOT_SUPPORTED: return INVALID_OPERATION;
+ case Result::RESULT_TOO_BIG: return NO_MEMORY;
+ default: return NO_INIT;
+ }
+}
+
+status_t EffectHalHidl::process(audio_buffer_t */*inBuffer*/, audio_buffer_t */*outBuffer*/) {
+ // Idea -- intercept set buffer config command, capture audio format, use it
+ // for determining frame size in bytes on input and output.
+ return OK;
+}
+
+status_t EffectHalHidl::processReverse(audio_buffer_t */*inBuffer*/, audio_buffer_t */*outBuffer*/) {
+ return OK;
+}
+
+status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+ uint32_t *replySize, void *pReplyData) {
+ if (mEffect == 0) return NO_INIT;
+ hidl_vec<uint8_t> hidlData;
+ hidlData.setToExternal(reinterpret_cast<uint8_t*>(pCmdData), cmdSize);
+ status_t status;
+ Return<void> ret = mEffect->command(cmdCode, hidlData, *replySize,
+ [&](int32_t s, const hidl_vec<uint8_t>& result) {
+ status = s;
+ if (status == 0) {
+ if (*replySize > result.size()) *replySize = result.size();
+ if (pReplyData && *replySize > 0) {
+ memcpy(pReplyData, &result[0], *replySize);
+ }
+ }
+ });
+ return status;
+}
+
+status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) {
+ if (mEffect == 0) return NO_INIT;
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffect->getDescriptor(
+ [&](Result r, const EffectDescriptor& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ effectDescriptorToHal(result, pDescriptor);
+ }
+ });
+ return ret.getStatus().isOk() ? analyzeResult(retval) : ret.getStatus().transactionError();
+}
+
+} // namespace android
diff --git a/media/libaudiohal/EffectHalHidl.h b/media/libaudiohal/EffectHalHidl.h
new file mode 100644
index 0000000..b79bee0
--- /dev/null
+++ b/media/libaudiohal/EffectHalHidl.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_EFFECT_HAL_HIDL_H
+#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H
+
+#include <android/hardware/audio/effect/2.0/IEffect.h>
+#include <media/audiohal/EffectHalInterface.h>
+#include <system/audio_effect.h>
+
+using ::android::hardware::audio::effect::V2_0::EffectDescriptor;
+using ::android::hardware::audio::effect::V2_0::IEffect;
+
+namespace android {
+
+class EffectHalHidl : public EffectHalInterface
+{
+ public:
+ // Effect process function. Takes input samples as specified
+ // in input buffer descriptor and output processed samples as specified
+ // in output buffer descriptor.
+ virtual status_t process(audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
+
+ // Process reverse stream function. This function is used to pass
+ // a reference stream to the effect engine.
+ virtual status_t processReverse(audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
+
+ // Send a command and receive a response to/from effect engine.
+ virtual status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+ uint32_t *replySize, void *pReplyData);
+
+ // Returns the effect descriptor.
+ virtual status_t getDescriptor(effect_descriptor_t *pDescriptor);
+
+ uint64_t effectId() const { return mEffectId; }
+
+ static void effectDescriptorToHal(
+ const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor);
+
+ private:
+ friend class EffectsFactoryHalHidl;
+ sp<IEffect> mEffect;
+ const uint64_t mEffectId;
+
+ static status_t analyzeResult(const hardware::audio::effect::V2_0::Result& result);
+
+ // Can not be constructed directly by clients.
+ EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId);
+
+ // The destructor automatically releases the effect.
+ virtual ~EffectHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.cpp b/media/libaudiohal/EffectsFactoryHalHidl.cpp
new file mode 100644
index 0000000..4f2eef0
--- /dev/null
+++ b/media/libaudiohal/EffectsFactoryHalHidl.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectsFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <cutils/native_handle.h>
+#include <media/EffectsFactoryApi.h>
+
+#include "EffectHalHidl.h"
+#include "EffectsFactoryHalHidl.h"
+#include "HidlUtils.h"
+
+using ::android::hardware::audio::common::V2_0::Uuid;
+using ::android::hardware::audio::effect::V2_0::IEffect;
+using ::android::hardware::audio::effect::V2_0::Result;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+
+namespace android {
+
+// static
+sp<EffectsFactoryHalInterface> EffectsFactoryHalInterface::create() {
+ return new EffectsFactoryHalHidl();
+}
+
+// static
+bool EffectsFactoryHalInterface::isNullUuid(const effect_uuid_t *pEffectUuid) {
+ return EffectIsNullUuid(pEffectUuid);
+}
+
+EffectsFactoryHalHidl::EffectsFactoryHalHidl() {
+ mEffectsFactory = IEffectsFactory::getService("audio_effects_factory");
+}
+
+EffectsFactoryHalHidl::~EffectsFactoryHalHidl() {
+}
+
+status_t EffectsFactoryHalHidl::queryAllDescriptors() {
+ if (mEffectsFactory == 0) return NO_INIT;
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffectsFactory->getAllDescriptors(
+ [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ mLastDescriptors = result;
+ }
+ });
+ if (ret.getStatus().isOk()) {
+ return retval == Result::OK ? OK : NO_INIT;
+ }
+ mLastDescriptors.resize(0);
+ return ret.getStatus().transactionError();
+}
+
+status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) {
+ status_t queryResult = queryAllDescriptors();
+ if (queryResult == OK) {
+ *pNumEffects = mLastDescriptors.size();
+ }
+ return queryResult;
+}
+
+status_t EffectsFactoryHalHidl::getDescriptor(
+ uint32_t index, effect_descriptor_t *pDescriptor) {
+ // TODO: We need somehow to track the changes on the server side
+ // or figure out how to convert everybody to query all the descriptors at once.
+ // TODO: check for nullptr
+ if (mLastDescriptors.size() == 0) {
+ status_t queryResult = queryAllDescriptors();
+ if (queryResult != OK) return queryResult;
+ }
+ if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND;
+ EffectHalHidl::effectDescriptorToHal(mLastDescriptors[index], pDescriptor);
+ return OK;
+}
+
+status_t EffectsFactoryHalHidl::getDescriptor(
+ const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor) {
+ // TODO: check for nullptr
+ if (mEffectsFactory == 0) return NO_INIT;
+ Uuid hidlUuid;
+ HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffectsFactory->getDescriptor(hidlUuid,
+ [&](Result r, const EffectDescriptor& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ EffectHalHidl::effectDescriptorToHal(result, pDescriptor);
+ }
+ });
+ if (ret.getStatus().isOk()) {
+ if (retval == Result::OK) return OK;
+ else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
+ else return NO_INIT;
+ }
+ return ret.getStatus().transactionError();
+}
+
+status_t EffectsFactoryHalHidl::createEffect(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
+ sp<EffectHalInterface> *effect) {
+ if (mEffectsFactory == 0) return NO_INIT;
+ Uuid hidlUuid;
+ HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
+ Result retval = Result::NOT_INITIALIZED;
+ Return<void> ret = mEffectsFactory->createEffect(
+ hidlUuid, sessionId, ioId,
+ [&](Result r, const sp<IEffect>& result, uint64_t effectId) {
+ retval = r;
+ if (retval == Result::OK) {
+ *effect = new EffectHalHidl(result, effectId);
+ }
+ });
+ if (ret.getStatus().isOk()) {
+ if (retval == Result::OK) return OK;
+ else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
+ else return NO_INIT;
+ }
+ return ret.getStatus().transactionError();
+}
+
+status_t EffectsFactoryHalHidl::dumpEffects(int fd) {
+ if (mEffectsFactory == 0) return NO_INIT;
+ native_handle_t* hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = fd;
+ Return<void> ret = mEffectsFactory->debugDump(hidlHandle);
+ native_handle_delete(hidlHandle);
+ return ret.getStatus().transactionError();
+}
+
+} // namespace android
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.h b/media/libaudiohal/EffectsFactoryHalHidl.h
new file mode 100644
index 0000000..f16db17
--- /dev/null
+++ b/media/libaudiohal/EffectsFactoryHalHidl.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
+#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
+
+#include <android/hardware/audio/effect/2.0/IEffectsFactory.h>
+#include <android/hardware/audio/effect/2.0/types.h>
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+
+namespace android {
+
+using ::android::hardware::audio::effect::V2_0::EffectDescriptor;
+using ::android::hardware::audio::effect::V2_0::IEffectsFactory;
+using ::android::hardware::hidl_vec;
+
+class EffectsFactoryHalHidl : public EffectsFactoryHalInterface
+{
+ public:
+ // Returns the number of different effects in all loaded libraries.
+ virtual status_t queryNumberEffects(uint32_t *pNumEffects);
+
+ // Returns a descriptor of the next available effect.
+ virtual status_t getDescriptor(uint32_t index,
+ effect_descriptor_t *pDescriptor);
+
+ virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid,
+ effect_descriptor_t *pDescriptor);
+
+ // Creates an effect engine of the specified type.
+ // To release the effect engine, it is necessary to release references
+ // to the returned effect object.
+ virtual status_t createEffect(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t ioId,
+ sp<EffectHalInterface> *effect);
+
+ virtual status_t dumpEffects(int fd);
+
+ private:
+ friend class EffectsFactoryHalInterface;
+
+ sp<IEffectsFactory> mEffectsFactory;
+ hidl_vec<EffectDescriptor> mLastDescriptors;
+
+ // Can not be constructed directly by clients.
+ EffectsFactoryHalHidl();
+ virtual ~EffectsFactoryHalHidl();
+
+ status_t queryAllDescriptors();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
diff --git a/media/libaudiohal/StreamHalHidl.cpp b/media/libaudiohal/StreamHalHidl.cpp
new file mode 100644
index 0000000..9383a36
--- /dev/null
+++ b/media/libaudiohal/StreamHalHidl.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "StreamHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/2.0/IStreamOutCallback.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "EffectHalHidl.h"
+#include "StreamHalHidl.h"
+
+using ::android::hardware::audio::common::V2_0::AudioChannelMask;
+using ::android::hardware::audio::common::V2_0::AudioFormat;
+using ::android::hardware::audio::V2_0::AudioDrain;
+using ::android::hardware::audio::V2_0::IStreamOutCallback;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::audio::V2_0::TimeSpec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+namespace android {
+
+StreamHalHidl::StreamHalHidl(IStream *stream)
+ : ConversionHelperHidl("Stream"), mStream(stream) {
+}
+
+StreamHalHidl::~StreamHalHidl() {
+ mStream = nullptr;
+}
+
+status_t StreamHalHidl::getSampleRate(uint32_t *rate) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getSampleRate", mStream->getSampleRate(), rate);
+}
+
+status_t StreamHalHidl::getBufferSize(size_t *size) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getBufferSize", mStream->getBufferSize(), size);
+}
+
+status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getChannelMask", mStream->getChannelMask(), mask);
+}
+
+status_t StreamHalHidl::getFormat(audio_format_t *format) {
+ if (!mStream) return NO_INIT;
+ return processReturn("getFormat", mStream->getFormat(), format);
+}
+
+status_t StreamHalHidl::getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
+ if (!mStream) return NO_INIT;
+ Return<void> ret = mStream->getAudioProperties(
+ [&](uint32_t sr, AudioChannelMask m, AudioFormat f) {
+ *sampleRate = sr;
+ *mask = static_cast<audio_channel_mask_t>(m);
+ *format = static_cast<audio_format_t>(f);
+ });
+ return processReturn("getAudioProperties", ret);
+}
+
+status_t StreamHalHidl::setParameters(const String8& kvPairs) {
+ if (!mStream) return NO_INIT;
+ hidl_vec<ParameterValue> hidlParams;
+ status_t status = parametersFromHal(kvPairs, &hidlParams);
+ if (status != OK) return status;
+ return processReturn("setParameters", mStream->setParameters(hidlParams));
+}
+
+status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) {
+ values->clear();
+ if (!mStream) return NO_INIT;
+ hidl_vec<hidl_string> hidlKeys;
+ status_t status = keysFromHal(keys, &hidlKeys);
+ if (status != OK) return status;
+ Result retval;
+ Return<void> ret = mStream->getParameters(
+ hidlKeys,
+ [&](Result r, const hidl_vec<ParameterValue>& parameters) {
+ retval = r;
+ if (retval == Result::OK) {
+ parametersToHal(parameters, values);
+ }
+ });
+ return processReturn("getParameters", ret, retval);
+}
+
+status_t StreamHalHidl::addEffect(sp<EffectHalInterface> effect) {
+ if (!mStream) return NO_INIT;
+ return processReturn("addEffect", mStream->addEffect(
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+
+status_t StreamHalHidl::removeEffect(sp<EffectHalInterface> effect) {
+ if (!mStream) return NO_INIT;
+ return processReturn("removeEffect", mStream->removeEffect(
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+
+status_t StreamHalHidl::standby() {
+ if (!mStream) return NO_INIT;
+ return processReturn("standby", mStream->standby());
+}
+
+status_t StreamHalHidl::dump(int fd) {
+ if (!mStream) return NO_INIT;
+ native_handle_t* hidlHandle = native_handle_create(1, 0);
+ hidlHandle->data[0] = fd;
+ Return<void> ret = mStream->debugDump(hidlHandle);
+ native_handle_delete(hidlHandle);
+ return processReturn("dump", ret);
+}
+
+
+namespace {
+
+/* Notes on callback ownership.
+
+This is how (Hw)Binder ownership model looks like. The server implementation
+is owned by Binder framework (via sp<>). Proxies are owned by clients.
+When the last proxy disappears, Binder framework releases the server impl.
+
+Thus, it is not needed to keep any references to StreamOutCallback (this is
+the server impl) -- it will live as long as HAL server holds a strong ref to
+IStreamOutCallback proxy. We clear that reference by calling 'clearCallback'
+from the destructor of StreamOutHalHidl.
+
+The callback only keeps a weak reference to the stream. The stream is owned
+by AudioFlinger.
+
+*/
+
+struct StreamOutCallback : public IStreamOutCallback {
+ StreamOutCallback(const wp<StreamOutHalHidl>& stream) : mStream(stream) {}
+
+ // IStreamOutCallback implementation
+ Return<void> onWriteReady() override {
+ sp<StreamOutHalHidl> stream = mStream.promote();
+ if (stream != 0) {
+ stream->onWriteReady();
+ }
+ return Void();
+ }
+
+ Return<void> onDrainReady() override {
+ sp<StreamOutHalHidl> stream = mStream.promote();
+ if (stream != 0) {
+ stream->onDrainReady();
+ }
+ return Void();
+ }
+
+ Return<void> onError() override {
+ sp<StreamOutHalHidl> stream = mStream.promote();
+ if (stream != 0) {
+ stream->onError();
+ }
+ return Void();
+ }
+
+ private:
+ wp<StreamOutHalHidl> mStream;
+};
+
+} // namespace
+
+StreamOutHalHidl::StreamOutHalHidl(const sp<IStreamOut>& stream)
+ : StreamHalHidl(stream.get()), mStream(stream) {
+}
+
+StreamOutHalHidl::~StreamOutHalHidl() {
+ if (mCallback.unsafe_get() && mStream != 0) {
+ processReturn("clearCallback", mStream->clearCallback());
+ }
+ mCallback.clear();
+}
+
+status_t StreamOutHalHidl::getFrameSize(size_t *size) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getFrameSize", mStream->getFrameSize(), size);
+}
+
+status_t StreamOutHalHidl::getLatency(uint32_t *latency) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getLatency", mStream->getLatency(), latency);
+}
+
+status_t StreamOutHalHidl::setVolume(float left, float right) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("setVolume", mStream->setVolume(left, right));
+}
+
+status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
+ if (mStream == 0) return NO_INIT;
+ hidl_vec<uint8_t> hidlData;
+ hidlData.setToExternal(static_cast<uint8_t*>(const_cast<void*>(buffer)), bytes);
+ Result retval;
+ Return<void> ret = mStream->write(
+ hidlData,
+ [&](Result r, uint64_t w) {
+ retval = r;
+ *written = w;
+ });
+ return processReturn("write", ret, retval);
+}
+
+status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->getRenderPosition(
+ [&](Result r, uint32_t d) {
+ retval = r;
+ if (retval == Result::OK) {
+ *dspFrames = d;
+ }
+ });
+ return processReturn("getRenderPosition", ret, retval);
+}
+
+status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->getNextWriteTimestamp(
+ [&](Result r, int64_t t) {
+ retval = r;
+ if (retval == Result::OK) {
+ *timestamp = t;
+ }
+ });
+ return processReturn("getRenderPosition", ret, retval);
+}
+
+status_t StreamOutHalHidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
+ if (mStream == 0) return NO_INIT;
+ status_t status = processReturn(
+ "setCallback", mStream->setCallback(new StreamOutCallback(this)));
+ if (status == OK) {
+ mCallback = callback;
+ }
+ return status;
+}
+
+status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
+ if (mStream == 0) return NO_INIT;
+ Return<void> ret = mStream->supportsPauseAndResume(
+ [&](bool p, bool r) {
+ *supportsPause = p;
+ *supportsResume = r;
+ });
+ return processReturn("supportsPauseAndResume", ret);
+}
+
+status_t StreamOutHalHidl::pause() {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("pause", mStream->pause());
+}
+
+status_t StreamOutHalHidl::resume() {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("pause", mStream->resume());
+}
+
+status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain);
+}
+
+status_t StreamOutHalHidl::drain(bool earlyNotify) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn(
+ "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL));
+}
+
+status_t StreamOutHalHidl::flush() {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("pause", mStream->flush());
+}
+
+status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->getPresentationPosition(
+ [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) {
+ retval = r;
+ if (retval == Result::OK) {
+ *frames = hidlFrames;
+ timestamp->tv_sec = hidlTimeStamp.tvSec;
+ timestamp->tv_nsec = hidlTimeStamp.tvNSec;
+ }
+ });
+ return processReturn("getPresentationPosition", ret, retval);
+}
+
+void StreamOutHalHidl::onWriteReady() {
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ if (callback == 0) return;
+ ALOGV("asyncCallback onWriteReady");
+ callback->onWriteReady();
+}
+
+void StreamOutHalHidl::onDrainReady() {
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ if (callback == 0) return;
+ ALOGV("asyncCallback onDrainReady");
+ callback->onDrainReady();
+}
+
+void StreamOutHalHidl::onError() {
+ sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+ if (callback == 0) return;
+ ALOGV("asyncCallback onError");
+ callback->onError();
+}
+
+
+StreamInHalHidl::StreamInHalHidl(const sp<IStreamIn>& stream)
+ : StreamHalHidl(stream.get()), mStream(stream) {
+}
+
+StreamInHalHidl::~StreamInHalHidl() {
+}
+
+status_t StreamInHalHidl::getFrameSize(size_t *size) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getFrameSize", mStream->getFrameSize(), size);
+}
+
+status_t StreamInHalHidl::setGain(float gain) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("setGain", mStream->setGain(gain));
+}
+
+status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->read(
+ bytes,
+ [&](Result r, const hidl_vec<uint8_t>& hidlData) {
+ retval = r;
+ *read = std::min(hidlData.size(), bytes);
+ if (retval == Result::OK) {
+ memcpy(buffer, &hidlData[0], *read);
+ }
+ });
+ return processReturn("read", ret, retval);
+}
+
+status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) {
+ if (mStream == 0) return NO_INIT;
+ return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost);
+}
+
+status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
+ if (mStream == 0) return NO_INIT;
+ Result retval;
+ Return<void> ret = mStream->getCapturePosition(
+ [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) {
+ retval = r;
+ if (retval == Result::OK) {
+ *frames = hidlFrames;
+ *time = hidlTime;
+ }
+ });
+ return processReturn("getCapturePosition", ret, retval);
+}
+
+} // namespace android
diff --git a/media/libaudiohal/StreamHalHidl.h b/media/libaudiohal/StreamHalHidl.h
new file mode 100644
index 0000000..e0a067e
--- /dev/null
+++ b/media/libaudiohal/StreamHalHidl.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_STREAM_HAL_HIDL_H
+#define ANDROID_HARDWARE_STREAM_HAL_HIDL_H
+
+#include <android/hardware/audio/2.0/IStream.h>
+#include <android/hardware/audio/2.0/IStreamIn.h>
+#include <android/hardware/audio/2.0/IStreamOut.h>
+#include <media/audiohal/StreamHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V2_0::IStream;
+using ::android::hardware::audio::V2_0::IStreamIn;
+using ::android::hardware::audio::V2_0::IStreamOut;
+using ::android::hardware::Return;
+
+namespace android {
+
+class DeviceHalHidl;
+
+class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl
+{
+ public:
+ // Return the sampling rate in Hz - eg. 44100.
+ virtual status_t getSampleRate(uint32_t *rate);
+
+ // Return size of input/output buffer in bytes for this stream - eg. 4800.
+ virtual status_t getBufferSize(size_t *size);
+
+ // Return the channel mask.
+ virtual status_t getChannelMask(audio_channel_mask_t *mask);
+
+ // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
+ virtual status_t getFormat(audio_format_t *format);
+
+ // Convenience method.
+ virtual status_t getAudioProperties(
+ uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format);
+
+ // Set audio stream parameters.
+ virtual status_t setParameters(const String8& kvPairs);
+
+ // Get audio stream parameters.
+ virtual status_t getParameters(const String8& keys, String8 *values);
+
+ // Add or remove the effect on the stream.
+ virtual status_t addEffect(sp<EffectHalInterface> effect);
+ virtual status_t removeEffect(sp<EffectHalInterface> effect);
+
+ // Put the audio hardware input/output into standby mode.
+ virtual status_t standby();
+
+ virtual status_t dump(int fd);
+
+ protected:
+ // Subclasses can not be constructed directly by clients.
+ explicit StreamHalHidl(IStream *stream);
+
+ // The destructor automatically closes the stream.
+ virtual ~StreamHalHidl();
+
+ private:
+ IStream *mStream;
+};
+
+class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl {
+ public:
+ // Return the frame size (number of bytes per sample) of a stream.
+ virtual status_t getFrameSize(size_t *size);
+
+ // Return the audio hardware driver estimated latency in milliseconds.
+ virtual status_t getLatency(uint32_t *latency);
+
+ // Use this method in situations where audio mixing is done in the hardware.
+ virtual status_t setVolume(float left, float right);
+
+ // Write audio buffer to driver.
+ virtual status_t write(const void *buffer, size_t bytes, size_t *written);
+
+ // Return the number of audio frames written by the audio dsp to DAC since
+ // the output has exited standby.
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+ // Get the local time at which the next write to the audio driver will be presented.
+ virtual status_t getNextWriteTimestamp(int64_t *timestamp);
+
+ // Set the callback for notifying completion of non-blocking write and drain.
+ virtual status_t setCallback(wp<StreamOutHalInterfaceCallback> callback);
+
+ // Returns whether pause and resume operations are supported.
+ virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume);
+
+ // Notifies to the audio driver to resume playback following a pause.
+ virtual status_t pause();
+
+ // Notifies to the audio driver to resume playback following a pause.
+ virtual status_t resume();
+
+ // Returns whether drain operation is supported.
+ virtual status_t supportsDrain(bool *supportsDrain);
+
+ // Requests notification when data buffered by the driver/hardware has been played.
+ virtual status_t drain(bool earlyNotify);
+
+ // Notifies to the audio driver to flush the queued data.
+ virtual status_t flush();
+
+ // Return a recent count of the number of audio frames presented to an external observer.
+ virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+ // Methods used by StreamOutCallback (HIDL).
+ void onWriteReady();
+ void onDrainReady();
+ void onError();
+
+ private:
+ friend class DeviceHalHidl;
+
+ wp<StreamOutHalInterfaceCallback> mCallback;
+ sp<IStreamOut> mStream;
+
+ // Can not be constructed directly by clients.
+ StreamOutHalHidl(const sp<IStreamOut>& stream);
+
+ virtual ~StreamOutHalHidl();
+};
+
+class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl {
+ public:
+ // Return the frame size (number of bytes per sample) of a stream.
+ virtual status_t getFrameSize(size_t *size);
+
+ // Set the input gain for the audio driver.
+ virtual status_t setGain(float gain);
+
+ // Read audio buffer in from driver.
+ virtual status_t read(void *buffer, size_t bytes, size_t *read);
+
+ // Return the amount of input frames lost in the audio driver.
+ virtual status_t getInputFramesLost(uint32_t *framesLost);
+
+ // Return a recent count of the number of audio frames received and
+ // the clock time associated with that frame count.
+ virtual status_t getCapturePosition(int64_t *frames, int64_t *time);
+
+ private:
+ friend class DeviceHalHidl;
+
+ sp<IStreamIn> mStream;
+
+ // Can not be constructed directly by clients.
+ StreamInHalHidl(const sp<IStreamIn>& stream);
+
+ virtual ~StreamInHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index a3d6761..af8cb50 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -3105,10 +3105,6 @@
//ALOGV("\tEffect_command cmdCode Case: EFFECT_CMD_GET_PARAM start");
effect_param_t *p = (effect_param_t *)pCmdData;
- if (SIZE_MAX - sizeof(effect_param_t) < (size_t)p->psize) {
- android_errorWriteLog(0x534e4554, "26347509");
- return -EINVAL;
- }
if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) ||
cmdSize < (sizeof(effect_param_t) + p->psize) ||
pReplyData == NULL || replySize == NULL ||
@@ -3116,13 +3112,32 @@
ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: ERROR");
return -EINVAL;
}
+ if (EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) < (size_t)p->psize) {
+ android_errorWriteLog(0x534e4554, "26347509");
+ ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: psize too big");
+ return -EINVAL;
+ }
+ uint32_t paddedParamSize = ((p->psize + sizeof(int32_t) - 1) / sizeof(int32_t)) *
+ sizeof(int32_t);
+ if ((EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) < paddedParamSize) ||
+ (EFFECT_PARAM_SIZE_MAX - sizeof(effect_param_t) - paddedParamSize <
+ p->vsize)) {
+ ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: padded_psize or vsize too big");
+ return -EINVAL;
+ }
+ uint32_t expectedReplySize = sizeof(effect_param_t) + paddedParamSize + p->vsize;
+ if (*replySize < expectedReplySize) {
+ ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: min. replySize %u, got %u bytes",
+ expectedReplySize, *replySize);
+ android_errorWriteLog(0x534e4554, "32705438");
+ return -EINVAL;
+ }
memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize);
p = (effect_param_t *)pReplyData;
- int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
-
+ uint32_t voffset = paddedParamSize;
if(pContext->EffectType == LVM_BASS_BOOST){
p->status = android::BassBoost_getParameter(pContext,
p->data,
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 2e4cf7d..02947b0 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -12,6 +12,7 @@
LOCAL_SRC_FILES += \
IDataSource.cpp \
IHDCP.cpp \
+ BufferingSettings.cpp \
mediaplayer.cpp \
IMediaCodecList.cpp \
IMediaCodecService.cpp \
@@ -25,6 +26,7 @@
IMediaPlayer.cpp \
IMediaRecorder.cpp \
IMediaSource.cpp \
+ IMediaAnalyticsService.cpp \
IRemoteDisplay.cpp \
IRemoteDisplayClient.cpp \
IResourceManagerClient.cpp \
@@ -33,6 +35,7 @@
MediaCodecBuffer.cpp \
MediaCodecInfo.cpp \
MediaDefs.cpp \
+ MediaAnalyticsItem.cpp \
MediaUtils.cpp \
Metadata.cpp \
mediarecorder.cpp \
@@ -58,7 +61,7 @@
libcamera_client libstagefright_foundation \
libgui libdl libaudioutils libaudioclient
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libsonivox
LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index d244a0a..65fc70b 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -189,6 +189,16 @@
return result;
}
+status_t AudioParameter::getAt(size_t index, String8& key) const
+{
+ if (mParameters.size() > index) {
+ key = mParameters.keyAt(index);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
status_t AudioParameter::getAt(size_t index, String8& key, String8& value) const
{
if (mParameters.size() > index) {
diff --git a/media/libmedia/BufferingSettings.cpp b/media/libmedia/BufferingSettings.cpp
new file mode 100644
index 0000000..6dc4a53
--- /dev/null
+++ b/media/libmedia/BufferingSettings.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferingSettings"
+//#define LOG_NDEBUG 0
+
+#include <binder/Parcel.h>
+
+#include <media/BufferingSettings.h>
+
+namespace android {
+
+// static
+bool BufferingSettings::IsValidBufferingMode(int mode) {
+ return (mode >= BUFFERING_MODE_NONE && mode < BUFFERING_MODE_COUNT);
+}
+
+BufferingSettings::BufferingSettings()
+ : mInitialBufferingMode(BUFFERING_MODE_NONE),
+ mRebufferingMode(BUFFERING_MODE_NONE),
+ mInitialWatermarkMs(kNoWatermark),
+ mInitialWatermarkKB(kNoWatermark),
+ mRebufferingWatermarkLowMs(kNoWatermark),
+ mRebufferingWatermarkHighMs(kNoWatermark),
+ mRebufferingWatermarkLowKB(kNoWatermark),
+ mRebufferingWatermarkHighKB(kNoWatermark) { }
+
+status_t BufferingSettings::readFromParcel(const Parcel* parcel) {
+ if (parcel == nullptr) {
+ return BAD_VALUE;
+ }
+ mInitialBufferingMode = (BufferingMode)parcel->readInt32();
+ mRebufferingMode = (BufferingMode)parcel->readInt32();
+ mInitialWatermarkMs = parcel->readInt32();
+ mInitialWatermarkKB = parcel->readInt32();
+ mRebufferingWatermarkLowMs = parcel->readInt32();
+ mRebufferingWatermarkHighMs = parcel->readInt32();
+ mRebufferingWatermarkLowKB = parcel->readInt32();
+ mRebufferingWatermarkHighKB = parcel->readInt32();
+
+ return OK;
+}
+
+status_t BufferingSettings::writeToParcel(Parcel* parcel) const {
+ if (parcel == nullptr) {
+ return BAD_VALUE;
+ }
+ parcel->writeInt32(mInitialBufferingMode);
+ parcel->writeInt32(mRebufferingMode);
+ parcel->writeInt32(mInitialWatermarkMs);
+ parcel->writeInt32(mInitialWatermarkKB);
+ parcel->writeInt32(mRebufferingWatermarkLowMs);
+ parcel->writeInt32(mRebufferingWatermarkHighMs);
+ parcel->writeInt32(mRebufferingWatermarkLowKB);
+ parcel->writeInt32(mRebufferingWatermarkHighKB);
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libmedia/IMediaAnalyticsService.cpp b/media/libmedia/IMediaAnalyticsService.cpp
new file mode 100644
index 0000000..afe9c36
--- /dev/null
+++ b/media/libmedia/IMediaAnalyticsService.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaAnalytics"
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <media/IHDCP.h>
+#include <media/IMediaCodecList.h>
+#include <media/IMediaHTTPService.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaRecorder.h>
+#include <media/IOMX.h>
+#include <media/IRemoteDisplay.h>
+#include <media/IRemoteDisplayClient.h>
+#include <media/IStreamSource.h>
+
+#include <utils/Errors.h> // for status_t
+#include <utils/List.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <media/MediaAnalyticsItem.h>
+#include <media/IMediaAnalyticsService.h>
+
+#define DEBUGGING 0
+#define DEBUGGING_FLOW 0
+#define DEBUGGING_RETURNS 0
+
+namespace android {
+
+enum {
+ GENERATE_UNIQUE_SESSIONID = IBinder::FIRST_CALL_TRANSACTION,
+ SUBMIT_ITEM,
+ GET_ITEM_LIST,
+};
+
+class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
+{
+public:
+ explicit BpMediaAnalyticsService(const sp<IBinder>& impl)
+ : BpInterface<IMediaAnalyticsService>(impl)
+ {
+ }
+
+ virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() {
+ Parcel data, reply;
+ status_t err;
+ MediaAnalyticsItem::SessionID_t sessionid =
+ MediaAnalyticsItem::SessionIDInvalid;
+
+ data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+ err = remote()->transact(GENERATE_UNIQUE_SESSIONID, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGW("bad response from service");
+ return MediaAnalyticsItem::SessionIDInvalid;
+ }
+ sessionid = reply.readInt64();
+ if (DEBUGGING_RETURNS) {
+ ALOGD("the caller gets a sessionid of %" PRId64 " back", sessionid);
+ }
+ return sessionid;
+ }
+
+ virtual MediaAnalyticsItem::SessionID_t submit(sp<MediaAnalyticsItem> item, bool forcenew)
+ {
+ // have this record submit itself
+ // this will be a binder call with appropriate timing
+ // return value is the uuid that the system generated for it.
+ // the return value 0 and -1 are reserved.
+ // -1 to indicate that there was a problem recording...
+
+ Parcel data, reply;
+ status_t err;
+
+ if (item == NULL) {
+ return MediaAnalyticsItem::SessionIDInvalid;
+ }
+
+ data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+ if(DEBUGGING_FLOW) {
+ ALOGD("client offers record: %s", item->toString().c_str());
+ }
+ data.writeBool(forcenew);
+ item->writeToParcel(&data);
+
+ err = remote()->transact(SUBMIT_ITEM, data, &reply);
+ if (err != NO_ERROR) {
+ return MediaAnalyticsItem::SessionIDInvalid;
+ }
+
+ // get an answer out of 'reply'
+ int64_t sessionid = reply.readInt64();
+ if (DEBUGGING_RETURNS) {
+ ALOGD("the caller gets sessionid=%" PRId64 "", sessionid);
+ }
+ return sessionid;
+ }
+
+ virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, nsecs_t ts)
+ {
+ return getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
+ }
+
+ virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, nsecs_t ts, MediaAnalyticsItem::Key key)
+ {
+ Parcel data, reply;
+ status_t err;
+
+ data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+ data.writeInt32(finished);
+ data.writeInt64(ts);
+ const char *str = key.c_str();
+ if (key.empty()) {
+ str = MediaAnalyticsItem::kKeyNone.c_str();
+ }
+ data.writeCString(str);
+ err = remote()->transact(GET_ITEM_LIST, data, &reply);
+ if (err != NO_ERROR) {
+ return NULL;
+ }
+
+ // read a count
+ int32_t count = reply.readInt32();
+ List<sp<MediaAnalyticsItem>> *list = NULL;
+
+ if (count > 0) {
+ list = new List<sp<MediaAnalyticsItem>>();
+ for (int i=0;i<count;i++) {
+ sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
+ // XXX: watch for failures here
+ item->readFromParcel(reply);
+ list->push_back(item);
+ }
+ }
+
+ return list;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaAnalyticsService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+
+
+ // get calling pid/tid
+ IPCThreadState *ipc = IPCThreadState::self();
+ int clientPid = ipc->getCallingPid();
+ // permission checking
+
+ if(DEBUGGING_FLOW) {
+ ALOGD("running in service, code %d, pid %d; called from pid %d",
+ code, getpid(), clientPid);
+ }
+
+ switch (code) {
+
+ case GENERATE_UNIQUE_SESSIONID: {
+ CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+
+ MediaAnalyticsItem::SessionID_t sessionid = generateUniqueSessionID();
+ reply->writeInt64(sessionid);
+
+ return NO_ERROR;
+ } break;
+
+ case SUBMIT_ITEM: {
+ CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+
+ bool forcenew;
+ sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
+
+ data.readBool(&forcenew);
+ item->readFromParcel(data);
+
+ item->setPid(clientPid);
+
+ // submit() takes ownership of / responsibility for the item
+ MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
+ reply->writeInt64(sessionid);
+
+ return NO_ERROR;
+ } break;
+
+ case GET_ITEM_LIST: {
+ CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ // get the parameters
+ bool finished = data.readInt32();
+ nsecs_t ts = data.readInt64();
+ MediaAnalyticsItem::Key key = data.readCString();
+
+ // find the (0 or more) items
+ List<sp<MediaAnalyticsItem>> *list = getMediaAnalyticsItemList(finished, ts, key);
+ // encapsulate/serialize them
+ reply->writeInt32(list->size());
+ if (list->size() > 0) {
+ for (List<sp<MediaAnalyticsItem>>::iterator it = list->begin();
+ it != list->end(); it++) {
+ (*it)->writeToParcel(reply);
+ }
+
+
+ }
+
+ // avoid leakiness; organized discarding of list and its contents
+ list->clear();
+ delete list;
+
+ return NO_ERROR;
+ } break;
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace android
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index dda2570..9ffde4e 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -23,6 +23,7 @@
#include <media/AudioResamplerPublic.h>
#include <media/AVSyncSettings.h>
+#include <media/BufferingSettings.h>
#include <media/IDataSource.h>
#include <media/IMediaHTTPService.h>
@@ -40,6 +41,8 @@
SET_DATA_SOURCE_FD,
SET_DATA_SOURCE_STREAM,
SET_DATA_SOURCE_CALLBACK,
+ SET_BUFFERING_SETTINGS,
+ GET_DEFAULT_BUFFERING_SETTINGS,
PREPARE_ASYNC,
START,
STOP,
@@ -148,6 +151,30 @@
return reply.readInt32();
}
+ status_t setBufferingSettings(const BufferingSettings& buffering)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ buffering.writeToParcel(&data);
+ remote()->transact(SET_BUFFERING_SETTINGS, data, &reply);
+ return reply.readInt32();
+ }
+
+ status_t getDefaultBufferingSettings(BufferingSettings* buffering /* nonnull */)
+ {
+ if (buffering == nullptr) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ remote()->transact(GET_DEFAULT_BUFFERING_SETTINGS, data, &reply);
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ err = buffering->readFromParcel(&reply);
+ }
+ return err;
+ }
+
status_t prepareAsync()
{
Parcel data, reply;
@@ -497,6 +524,23 @@
reply->writeInt32(setVideoSurfaceTexture(bufferProducer));
return NO_ERROR;
} break;
+ case SET_BUFFERING_SETTINGS: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ BufferingSettings buffering;
+ buffering.readFromParcel(&data);
+ reply->writeInt32(setBufferingSettings(buffering));
+ return NO_ERROR;
+ } break;
+ case GET_DEFAULT_BUFFERING_SETTINGS: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ BufferingSettings buffering;
+ status_t err = getDefaultBufferingSettings(&buffering);
+ reply->writeInt32(err);
+ if (err == OK) {
+ buffering.writeToParcel(reply);
+ }
+ return NO_ERROR;
+ } break;
case PREPARE_ASYNC: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
reply->writeInt32(prepareAsync());
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index c1fe87f..3d466b1 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -590,7 +590,7 @@
params = mmap(NULL, allocSize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1 /* fd */, 0 /* offset */);
}
- if (params != MAP_FAILED) {
+ if (params != MAP_FAILED && params != NULL) {
err = data.read(params, size);
if (err != OK) {
android_errorWriteLog(0x534e4554, "26914474");
diff --git a/media/libmedia/MediaAnalyticsItem.cpp b/media/libmedia/MediaAnalyticsItem.cpp
new file mode 100644
index 0000000..5f05d5a
--- /dev/null
+++ b/media/libmedia/MediaAnalyticsItem.cpp
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "MediaAnalyticsItem"
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+#include <media/stagefright/foundation/AString.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaAnalyticsService.h>
+#include <media/MediaAnalyticsItem.h>
+
+namespace android {
+
+#define DEBUG_SERVICEACCESS 0
+
+// the few universal keys we have
+const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny = "any";
+const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone = "none";
+
+const char * const MediaAnalyticsItem::EnabledProperty = "media.analytics.enabled";
+const char * const MediaAnalyticsItem::EnabledPropertyPersist = "persist.media.analytics.enabled";
+const int MediaAnalyticsItem::EnabledProperty_default = 0;
+
+
+// access functions for the class
+MediaAnalyticsItem::MediaAnalyticsItem()
+ : RefBase(),
+ mPid(0),
+ mUid(0),
+ mSessionID(MediaAnalyticsItem::SessionIDNone),
+ mTimestamp(0),
+ mFinalized(0) {
+ mKey = MediaAnalyticsItem::kKeyNone;
+}
+
+MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
+ : RefBase(),
+ mPid(0),
+ mUid(0),
+ mSessionID(MediaAnalyticsItem::SessionIDNone),
+ mTimestamp(0),
+ mFinalized(0) {
+ mKey = key;
+}
+
+MediaAnalyticsItem::~MediaAnalyticsItem() {
+ clear();
+}
+
+// so clients can send intermediate values to be overlaid later
+MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
+ mFinalized = value;
+ return *this;
+}
+
+bool MediaAnalyticsItem::getFinalized() const {
+ return mFinalized;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
+ mSessionID = id;
+ return *this;
+}
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
+ return mSessionID;
+}
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
+ MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
+ ALOGD("generateSessionID()");
+
+ if (mSessionID == SessionIDNone) {
+ // get one from the server
+ sp<IMediaAnalyticsService> svc = getInstance();
+ if (svc != NULL) {
+ newid = svc->generateUniqueSessionID();
+ }
+ mSessionID = newid;
+ }
+
+ return mSessionID;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
+ mSessionID = MediaAnalyticsItem::SessionIDNone;
+ return *this;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
+ mTimestamp = ts;
+ return *this;
+}
+
+nsecs_t MediaAnalyticsItem::getTimestamp() const {
+ return mTimestamp;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
+ mPid = pid;
+ return *this;
+}
+
+pid_t MediaAnalyticsItem::getPid() const {
+ return mPid;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
+ mUid = uid;
+ return *this;
+}
+
+uid_t MediaAnalyticsItem::getUid() const {
+ return mUid;
+}
+
+void MediaAnalyticsItem::clear() {
+
+ mKey.clear();
+
+#if 0
+ // not sure that I need to (or should) be doing this...
+ // seeing some strangeness in some records
+ int count = mItems.size();
+ for (int i = 0 ; i < count; i++ ) {
+ MediaAnalyticsItem::Attr attr = mItems.keyAt(i);
+ const sp<Item> value = mItems.valueAt(i);
+ value->clear();
+ attr.clear();
+ }
+ mItems.clear();
+#endif
+
+ return;
+}
+
+// this key is for the overall record -- "vid" or "aud"
+// assuming for the moment we use int32_t like the
+// media frameworks MetaData.cpp
+MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
+ // XXX: possible validation of legal keys.
+ mKey = key;
+ return *this;
+}
+
+MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
+ return mKey;
+}
+
+// number of keys we have in our dictionary
+// we won't upload empty records
+int32_t MediaAnalyticsItem::count() const {
+ return mItems.size();
+}
+
+// set the values
+bool MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr attr, int32_t value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ bool overwrote = true;
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ item->mType = MediaAnalyticsItem::Item::kTypeInt32;
+ item->u.int32Value = value;
+ return overwrote;
+}
+
+bool MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr attr, int64_t value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ bool overwrote = true;
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ item->mType = MediaAnalyticsItem::Item::kTypeInt64;
+ item->u.int64Value = value;
+ return overwrote;
+}
+
+bool MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr attr, double value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ bool overwrote = true;
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ item->mType = MediaAnalyticsItem::Item::kTypeDouble;
+ item->u.doubleValue = value;
+ return overwrote;
+}
+
+bool MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr attr, const char *value) {
+ bool overwrote = true;
+ if (value == NULL) return false;
+ // we store our own copy of the supplied string
+ char *nvalue = strdup(value);
+ if (nvalue == NULL) {
+ return false;
+ }
+ ssize_t i = mItems.indexOfKey(attr);
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ if (item->mType == MediaAnalyticsItem::Item::kTypeCString
+ && item->u.CStringValue != NULL) {
+ free(item->u.CStringValue);
+ item->u.CStringValue = NULL;
+ }
+ item->mType = MediaAnalyticsItem::Item::kTypeCString;
+ item->u.CStringValue = nvalue;
+ return true;
+}
+
+// find/add/set fused into a single operation
+bool MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr attr, int32_t value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ bool overwrote = true;
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ if (overwrote
+ && item->mType == MediaAnalyticsItem::Item::kTypeInt32) {
+ item->u.int32Value += value;
+ } else {
+ // start clean if there was a type mismatch
+ item->u.int32Value = value;
+ }
+ item->mType = MediaAnalyticsItem::Item::kTypeInt32;
+ return overwrote;
+}
+
+bool MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr attr, int64_t value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ bool overwrote = true;
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ if (overwrote
+ && item->mType == MediaAnalyticsItem::Item::kTypeInt64) {
+ item->u.int64Value += value;
+ } else {
+ // start clean if there was a type mismatch
+ item->u.int64Value = value;
+ }
+ item->mType = MediaAnalyticsItem::Item::kTypeInt64;
+ return overwrote;
+}
+
+bool MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr attr, double value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ bool overwrote = true;
+ if (i<0) {
+ sp<Item> item = new Item();
+ i = mItems.add(attr, item);
+ overwrote = false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ if (overwrote
+ && item->mType == MediaAnalyticsItem::Item::kTypeDouble) {
+ item->u.doubleValue += value;
+ } else {
+ // start clean if there was a type mismatch
+ item->u.doubleValue = value;
+ }
+ item->mType = MediaAnalyticsItem::Item::kTypeDouble;
+ return overwrote;
+}
+
+// find & extract values
+bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr attr, int32_t *value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ if (i < 0) {
+ return false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ *value = item->u.int32Value;
+ return true;
+}
+bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr attr, int64_t *value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ if (i < 0) {
+ return false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ *value = item->u.int64Value;
+ return true;
+}
+bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr attr, double *value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ if (i < 0) {
+ return false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ *value = item->u.doubleValue;
+ return true;
+}
+
+// caller responsible for the returned string
+bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr attr, char **value) {
+ ssize_t i = mItems.indexOfKey(attr);
+ if (i < 0) {
+ return false;
+ }
+ sp<Item> &item = mItems.editValueAt(i);
+ char *p = strdup(item->u.CStringValue);
+ *value = p;
+ return true;
+}
+
+// remove indicated keys and their values
+// return value is # keys removed
+int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
+ int zapped = 0;
+ if (attrs == NULL) {
+ return -1;
+ }
+ if (n <= 0) {
+ return -1;
+ }
+ for (ssize_t i = 0 ; i < n ; i++) {
+ ssize_t j = mItems.indexOfKey(attrs[i]);
+ if (j >= 0) {
+ mItems.removeItemsAt(j);
+ zapped++;
+ }
+ }
+ return zapped;
+}
+
+// remove any keys NOT in the provided list
+// return value is # keys removed
+int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
+ int zapped = 0;
+ if (attrs == NULL) {
+ return -1;
+ }
+ if (n <= 0) {
+ return -1;
+ }
+ for (ssize_t i = mItems.size()-1 ; i >=0 ; i--) {
+ const MediaAnalyticsItem::Attr& lattr = mItems.keyAt(i);
+ ssize_t j;
+ for (j= 0; j < n ; j++) {
+ if (lattr == attrs[j]) {
+ mItems.removeItemsAt(i);
+ zapped++;
+ break;
+ }
+ }
+ }
+ return zapped;
+}
+
+// remove a single key
+// return value is 0 (not found) or 1 (found and removed)
+int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr attr) {
+ if (attr == 0) return -1;
+ ssize_t i = mItems.indexOfKey(attr);
+ if (i < 0) {
+ return 0;
+ }
+ mItems.removeItemsAt(i);
+ return 1;
+}
+
+
+// handle individual items/properties stored within the class
+//
+MediaAnalyticsItem::Item::Item()
+ : mType(kTypeNone)
+{
+}
+
+MediaAnalyticsItem::Item::~Item()
+{
+ clear();
+}
+
+void MediaAnalyticsItem::Item::clear()
+{
+ if (mType == kTypeCString && u.CStringValue != NULL) {
+ free(u.CStringValue);
+ u.CStringValue = NULL;
+ }
+ mType = kTypeNone;
+}
+
+// Parcel / serialize things for binder calls
+//
+
+int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
+ // into 'this' object
+ // .. we make a copy of the string to put away.
+ mKey = data.readCString();
+ mSessionID = data.readInt64();
+ mFinalized = data.readInt32();
+ mTimestamp = data.readInt64();
+
+ int count = data.readInt32();
+ for (int i = 0; i < count ; i++) {
+ MediaAnalyticsItem::Attr attr = data.readCString();
+ int32_t ztype = data.readInt32();
+ switch (ztype) {
+ case MediaAnalyticsItem::Item::kTypeInt32:
+ setInt32(attr, data.readInt32());
+ break;
+ case MediaAnalyticsItem::Item::kTypeInt64:
+ setInt64(attr, data.readInt64());
+ break;
+ case MediaAnalyticsItem::Item::kTypeDouble:
+ setDouble(attr, data.readDouble());
+ break;
+ case MediaAnalyticsItem::Item::kTypeCString:
+ setCString(attr, data.readCString());
+ break;
+ default:
+ ALOGE("reading bad item type: %d, idx %d",
+ ztype, i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
+ if (data == NULL) return -1;
+
+
+ data->writeCString(mKey.c_str());
+ data->writeInt64(mSessionID);
+ data->writeInt32(mFinalized);
+ data->writeInt64(mTimestamp);
+
+ // set of items
+ int count = mItems.size();
+ data->writeInt32(count);
+ for (int i = 0 ; i < count; i++ ) {
+ MediaAnalyticsItem::Attr attr = mItems.keyAt(i);
+ sp<Item> value = mItems.valueAt(i);
+ {
+ data->writeCString(attr.c_str());
+ data->writeInt32(value->mType);
+ switch (value->mType) {
+ case MediaAnalyticsItem::Item::kTypeInt32:
+ data->writeInt32(value->u.int32Value);
+ break;
+ case MediaAnalyticsItem::Item::kTypeInt64:
+ data->writeInt64(value->u.int64Value);
+ break;
+ case MediaAnalyticsItem::Item::kTypeDouble:
+ data->writeDouble(value->u.doubleValue);
+ break;
+ case MediaAnalyticsItem::Item::kTypeCString:
+ data->writeCString(value->u.CStringValue);
+ break;
+ default:
+ ALOGE("found bad item type: %d, idx %d",
+ value->mType, i);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+AString MediaAnalyticsItem::toString() {
+
+ AString result = "(";
+ char buffer[256];
+
+ // same order as we spill into the parcel, although not required
+ // key+session are our primary matching criteria
+ result.append(mKey.c_str());
+ result.append(":");
+ snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
+ result.append(buffer);
+
+ // we need these internally, but don't want to upload them
+ snprintf(buffer, sizeof(buffer), "%d:%d", mUid, mPid);
+ result.append(buffer);
+
+ snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
+ result.append(buffer);
+ snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
+ result.append(buffer);
+
+ // set of items
+ int count = mItems.size();
+ snprintf(buffer, sizeof(buffer), "%d:", count);
+ result.append(buffer);
+ for (int i = 0 ; i < count; i++ ) {
+ const MediaAnalyticsItem::Attr attr = mItems.keyAt(i);
+ const sp<Item> value = mItems.valueAt(i);
+ switch (value->mType) {
+ case MediaAnalyticsItem::Item::kTypeInt32:
+ snprintf(buffer,sizeof(buffer),
+ "%s=%d:", attr.c_str(), value->u.int32Value);
+ break;
+ case MediaAnalyticsItem::Item::kTypeInt64:
+ snprintf(buffer,sizeof(buffer),
+ "%s=%" PRId64 ":", attr.c_str(), value->u.int64Value);
+ break;
+ case MediaAnalyticsItem::Item::kTypeDouble:
+ snprintf(buffer,sizeof(buffer),
+ "%s=%e:", attr.c_str(), value->u.doubleValue);
+ break;
+ case MediaAnalyticsItem::Item::kTypeCString:
+ // XXX: worry about escape chars
+ // XXX: worry about overflowing buffer
+ snprintf(buffer,sizeof(buffer), "%s=", attr.c_str());
+ result.append(buffer);
+ result.append(value->u.CStringValue);
+ buffer[0] = ':';
+ buffer[1] = '\0';
+ break;
+ default:
+ ALOGE("to_String bad item type: %d",
+ value->mType);
+ break;
+ }
+ result.append(buffer);
+ }
+
+ result.append(")");
+
+ return result;
+}
+
+// for the lazy, we offer methods that finds the service and
+// calls the appropriate daemon
+bool MediaAnalyticsItem::selfrecord() {
+ return selfrecord(false);
+}
+
+bool MediaAnalyticsItem::selfrecord(bool forcenew) {
+
+ AString p = this->toString();
+ ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
+
+ sp<IMediaAnalyticsService> svc = getInstance();
+
+ if (svc != NULL) {
+ svc->submit(this, forcenew);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// get a connection we can reuse for most of our lifetime
+// static
+sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
+static Mutex sInitMutex;
+
+//static
+bool MediaAnalyticsItem::isEnabled() {
+ int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
+
+ if (enabled == -1) {
+ enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
+ }
+ if (enabled == -1) {
+ enabled = MediaAnalyticsItem::EnabledProperty_default;
+ }
+ if (enabled <= 0) {
+ return false;
+ }
+ return true;
+}
+
+//static
+sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
+ static const char *servicename = "media.analytics";
+ int enabled = isEnabled();
+
+ if (enabled == false) {
+ if (DEBUG_SERVICEACCESS) {
+ ALOGD("disabled");
+ }
+ return NULL;
+ }
+
+ {
+ Mutex::Autolock _l(sInitMutex);
+ const char *badness = "";
+
+
+ if (sAnalyticsService == NULL) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm != NULL) {
+ sp<IBinder> binder = sm->getService(String16(servicename));
+ if (binder != NULL) {
+ sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
+ } else {
+ badness = "did not find service";
+ }
+ } else {
+ badness = "No Service Manager access";
+ }
+ // always
+ if (1 || DEBUG_SERVICEACCESS) {
+ if (sAnalyticsService == NULL) {
+ ALOGD("Unable to bind to service %s: %s", servicename, badness);
+ }
+ }
+ }
+ return sAnalyticsService;
+ }
+}
+
+
+// merge the info from 'incoming' into this record.
+// we finish with a union of this+incoming and special handling for collisions
+bool MediaAnalyticsItem::merge(sp<MediaAnalyticsItem> incoming) {
+
+ // if I don't have key or session id, take them from incoming
+ // 'this' should never be missing both of them...
+ if (mKey.empty()) {
+ mKey = incoming->mKey;
+ } else if (mSessionID == 0) {
+ mSessionID = incoming->mSessionID;
+ }
+
+ // we always take the more recent 'finalized' value
+ setFinalized(incoming->getFinalized());
+
+ // for each attribute from 'incoming', resolve appropriately
+ int nattr = incoming->mItems.size();
+ for (int i = 0 ; i < nattr; i++ ) {
+ const MediaAnalyticsItem::Attr attr = incoming->mItems.keyAt(i);
+ const sp<Item> value = incoming->mItems.valueAt(i);
+
+ const char *p = attr.c_str();
+ char semantic = p[strlen(p)-1];
+
+ switch (semantic) {
+ default: // default operation is keep new
+ case '>': // last aka keep new
+ mItems.replaceValueFor(attr, value);
+ break;
+
+ case '<': /* first aka keep first*/
+ /* nop */
+ break;
+
+ case '+': /* sum */
+ // XXX validate numeric types, sum in place
+ break;
+
+ }
+ }
+
+ // not sure when we'd return false...
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libmedia/MediaDefs.cpp b/media/libmedia/MediaDefs.cpp
index a2110c9..2ae71f7 100644
--- a/media/libmedia/MediaDefs.cpp
+++ b/media/libmedia/MediaDefs.cpp
@@ -57,8 +57,6 @@
const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS = "video/mp2p";
-const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
-
const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
const char *MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
diff --git a/media/libmediaanalyticsservice/Android.mk b/media/libmediaanalyticsservice/Android.mk
new file mode 100644
index 0000000..dd59651
--- /dev/null
+++ b/media/libmediaanalyticsservice/Android.mk
@@ -0,0 +1,44 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# libmediaanalyticsservice
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ MediaAnalyticsService.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libcutils \
+ liblog \
+ libdl \
+ libgui \
+ libmedia \
+ libmediautils \
+ libstagefright_foundation \
+ libutils
+
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libmedia
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/av/media/libstagefright/include \
+ $(TOP)/frameworks/av/media/libstagefright/rtsp \
+ $(TOP)/frameworks/av/media/libstagefright/wifi-display \
+ $(TOP)/frameworks/av/media/libstagefright/webm \
+ $(TOP)/frameworks/av/include/media \
+ $(TOP)/frameworks/av/include/camera \
+ $(TOP)/frameworks/native/include/media/openmax \
+ $(TOP)/frameworks/native/include/media/hardware \
+ $(TOP)/external/tremolo/Tremolo \
+ libcore/include \
+
+LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall
+LOCAL_CLANG := true
+
+LOCAL_MODULE:= libmediaanalyticsservice
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libmediaanalyticsservice/MediaAnalyticsService.cpp b/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
new file mode 100644
index 0000000..a039c6c
--- /dev/null
+++ b/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Proxy for media player implementations
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaAnalyticsService"
+#include <utils/Log.h>
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include <string.h>
+
+#include <cutils/atomic.h>
+#include <cutils/properties.h> // for property_get
+
+#include <utils/misc.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <gui/Surface.h>
+#include <utils/Errors.h> // for status_t
+#include <utils/List.h>
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+#include <media/AudioPolicyHelper.h>
+#include <media/IMediaHTTPService.h>
+#include <media/IRemoteDisplay.h>
+#include <media/IRemoteDisplayClient.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/mediarecorder.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+#include <media/Metadata.h>
+#include <media/AudioTrack.h>
+#include <media/MemoryLeakTrackUtil.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <mediautils/BatteryNotifier.h>
+
+//#include <memunreachable/memunreachable.h>
+#include <system/audio.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "MediaAnalyticsService.h"
+
+
+namespace android {
+
+
+static int trackqueue = 0;
+
+//using android::status_t;
+//using android::OK;
+//using android::BAD_VALUE;
+//using android::NOT_ENOUGH_DATA;
+//using android::Parcel;
+
+
+void MediaAnalyticsService::instantiate() {
+ defaultServiceManager()->addService(
+ String16("media.analytics"), new MediaAnalyticsService());
+}
+
+// XXX: add dynamic controls for mMaxRecords
+MediaAnalyticsService::MediaAnalyticsService()
+ : mMaxRecords(100) {
+
+ ALOGD("MediaAnalyticsService created");
+ // clear our queues
+ mOpen = new List<sp<MediaAnalyticsItem>>();
+ mFinalized = new List<sp<MediaAnalyticsItem>>();
+
+ mItemsSubmitted = 0;
+ mItemsFinalized = 0;
+ mItemsDiscarded = 0;
+
+ mLastSessionID = 0;
+ // recover any persistency we set up
+ // etc
+}
+
+MediaAnalyticsService::~MediaAnalyticsService() {
+ ALOGD("MediaAnalyticsService destroyed");
+
+ // XXX: clean out mOpen and mFinalized
+}
+
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() {
+ // generate a new sessionid
+
+ Mutex::Autolock _l(mLock_ids);
+ return (++mLastSessionID);
+}
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(sp<MediaAnalyticsItem> item, bool forcenew) {
+
+ MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid;
+
+ // we control these, not using whatever the user might have sent
+ nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ item->setTimestamp(now);
+ int pid = IPCThreadState::self()->getCallingPid();
+ item->setPid(pid);
+ int uid = IPCThreadState::self()->getCallingUid();
+ item->setUid(uid);
+
+ mItemsSubmitted++;
+
+ // validate the record; we discard if we don't like it
+ if (contentValid(item) == false) {
+ return MediaAnalyticsItem::SessionIDInvalid;
+ }
+
+
+ // if we have a sesisonid in the new record, look to make
+ // sure it doesn't appear in the finalized list.
+ // XXX: this is for security / DOS prevention.
+ // may also require that we persist the unique sessionIDs
+ // across boots [instead of within a single boot]
+
+
+ // match this new record up against records in the open
+ // list...
+ // if there's a match, merge them together
+ // deal with moving the old / merged record into the finalized que
+
+ bool finalizing = item->getFinalized();
+
+ // if finalizing, we'll remove it
+ sp<MediaAnalyticsItem> oitem = findItem(mOpen, item, finalizing | forcenew);
+ if (oitem != NULL) {
+ if (forcenew) {
+ // old one gets finalized, then we insert the new one
+ // so we'll have 2 records at the end of this.
+ // but don't finalize an empty record
+ if (oitem->count() != 0) {
+ oitem->setFinalized(true);
+ saveItem(mFinalized, oitem, 0);
+ }
+ // new record could itself be marked finalized...
+ if (finalizing) {
+ saveItem(mFinalized, item, 0);
+ mItemsFinalized++;
+ } else {
+ saveItem(mOpen, item, 1);
+ }
+ id = item->getSessionID();
+ } else {
+ // combine the records, send it to finalized if appropriate
+ oitem->merge(item);
+ if (finalizing) {
+ saveItem(mFinalized, oitem, 0);
+ mItemsFinalized++;
+ }
+ id = oitem->getSessionID();
+ }
+ } else {
+ // nothing to merge, save the new record
+ if (finalizing) {
+ if (item->count() != 0) {
+ // drop empty records
+ saveItem(mFinalized, item, 0);
+ mItemsFinalized++;
+ }
+ } else {
+ saveItem(mOpen, item, 1);
+ }
+ id = item->getSessionID();
+ }
+
+ return id;
+}
+
+List<sp<MediaAnalyticsItem>> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
+ // this might never get called; the binder interface maps to the full parm list
+ // on the client side before making the binder call.
+ // but this lets us be sure...
+ List<sp<MediaAnalyticsItem>> *list;
+ list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
+ return list;
+}
+
+List<sp<MediaAnalyticsItem>> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
+
+ // XXX: implement the get-item-list semantics
+
+ List<sp<MediaAnalyticsItem>> *list = NULL;
+ // set up our query on the persistent data
+ // slurp in all of the pieces
+ // return that
+ return list;
+}
+
+// ignoring 2nd argument, name removed to keep compiler happy
+// XXX: arguments to parse:
+// -- a timestamp (either since X or last X seconds) to bound search
+status_t MediaAnalyticsService::dump(int fd, const Vector<String16>&)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump MediaAnalyticsService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ } else {
+
+ // crack parameters
+
+
+ Mutex::Autolock _l(mLock);
+
+ snprintf(buffer, SIZE, "Dump of the mediaanalytics process:\n");
+ result.append(buffer);
+
+ int enabled = MediaAnalyticsItem::isEnabled();
+ if (enabled) {
+ snprintf(buffer, SIZE, "Analytics gathering: enabled\n");
+ } else {
+ snprintf(buffer, SIZE, "Analytics gathering: DISABLED via property\n");
+ }
+ result.append(buffer);
+
+ snprintf(buffer, SIZE,
+ "Since Boot: Submissions: %" PRId64
+ " Finalizations: %" PRId64
+ " Discarded: %" PRId64 "\n",
+ mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
+ result.append(buffer);
+
+ // show the recently recorded records
+ snprintf(buffer, sizeof(buffer), "\nFinalized Analytics (oldest first):\n");
+ result.append(buffer);
+ result.append(this->dumpQueue(mFinalized));
+
+ snprintf(buffer, sizeof(buffer), "\nIn-Progress Analytics (newest first):\n");
+ result.append(buffer);
+ result.append(this->dumpQueue(mOpen));
+
+ // show who is connected and injecting records?
+ // talk about # records fed to the 'readers'
+ // talk about # records we discarded, perhaps "discarded w/o reading" too
+
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+// caller has locked mLock...
+String8 MediaAnalyticsService::dumpQueue(List<sp<MediaAnalyticsItem>> *theList) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ int slot = 0;
+
+ if (theList->empty()) {
+ result.append("empty\n");
+ } else {
+ List<sp<MediaAnalyticsItem>>::iterator it = theList->begin();
+ for (; it != theList->end(); it++, slot++) {
+ AString entry = (*it)->toString();
+ snprintf(buffer, sizeof(buffer), "%4d: %s\n",
+ slot, entry.c_str());
+ result.append(buffer);
+ }
+ }
+
+ return result;
+}
+
+//
+// Our Cheap in-core, non-persistent records management.
+// XXX: rewrite this to manage persistence, etc.
+
+// insert appropriately into queue
+void MediaAnalyticsService::saveItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAnalyticsItem> item, int front) {
+
+ Mutex::Autolock _l(mLock);
+
+ if (false)
+ ALOGD("Inject a record: session %" PRId64 " ts %" PRId64 "",
+ item->getSessionID(), item->getTimestamp());
+
+ if (trackqueue) {
+ String8 before = dumpQueue(l);
+ ALOGD("Q before insert: %s", before.string());
+ }
+
+ // adding at back of queue (fifo order)
+ if (front) {
+ l->push_front(item);
+ } else {
+ l->push_back(item);
+ }
+
+ if (trackqueue) {
+ String8 after = dumpQueue(l);
+ ALOGD("Q after insert: %s", after.string());
+ }
+
+ // keep removing old records the front until we're in-bounds
+ if (mMaxRecords > 0) {
+ while (l->size() > (size_t) mMaxRecords) {
+ sp<MediaAnalyticsItem> oitem = *(l->begin());
+ if (trackqueue) {
+ ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
+ oitem->getKey().c_str(), oitem->getSessionID(),
+ oitem->getTimestamp());
+ }
+ l->erase(l->begin());
+ mItemsDiscarded++;
+ }
+ }
+
+ if (trackqueue) {
+ String8 after = dumpQueue(l);
+ ALOGD("Q after cleanup: %s", after.string());
+ }
+}
+
+// are they alike enough that nitem can be folded into oitem?
+static bool compatibleItems(sp<MediaAnalyticsItem> oitem, sp<MediaAnalyticsItem> nitem) {
+
+ if (0) {
+ ALOGD("Compare: o %s n %s",
+ oitem->toString().c_str(), nitem->toString().c_str());
+ }
+
+ // general safety
+ if (nitem->getUid() != oitem->getUid()) {
+ return false;
+ }
+ if (nitem->getPid() != oitem->getPid()) {
+ return false;
+ }
+
+ // key -- needs to match
+ if (nitem->getKey() == oitem->getKey()) {
+ // still in the game.
+ } else {
+ return false;
+ }
+
+ // session id -- empty field in new is allowed
+ MediaAnalyticsItem::SessionID_t osession = oitem->getSessionID();
+ MediaAnalyticsItem::SessionID_t nsession = nitem->getSessionID();
+ if (nsession != osession) {
+ // incoming '0' matches value in osession
+ if (nsession != 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// find the incomplete record that this will overlay
+sp<MediaAnalyticsItem> MediaAnalyticsService::findItem(List<sp<MediaAnalyticsItem>> *theList, sp<MediaAnalyticsItem> nitem, bool removeit) {
+ sp<MediaAnalyticsItem> item;
+
+ if (nitem == NULL) {
+ return NULL;
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ for (List<sp<MediaAnalyticsItem>>::iterator it = theList->begin();
+ it != theList->end(); it++) {
+ sp<MediaAnalyticsItem> tmp = (*it);
+
+ if (!compatibleItems(tmp, nitem)) {
+ continue;
+ }
+
+ // we match! this is the one I want.
+ if (removeit) {
+ theList->erase(it);
+ }
+ item = tmp;
+ break;
+ }
+ return item;
+}
+
+
+// delete the indicated record
+void MediaAnalyticsService::deleteItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAnalyticsItem> item) {
+
+ Mutex::Autolock _l(mLock);
+
+ if(trackqueue) {
+ String8 before = dumpQueue(l);
+ ALOGD("Q before delete: %s", before.string());
+ }
+
+ for (List<sp<MediaAnalyticsItem>>::iterator it = l->begin();
+ it != l->end(); it++) {
+ if ((*it)->getSessionID() != item->getSessionID())
+ continue;
+
+ ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
+ l->erase(it);
+ break;
+ }
+
+ if (trackqueue) {
+ String8 after = dumpQueue(l);
+ ALOGD("Q after delete: %s", after.string());
+ }
+}
+
+// are the contents good
+bool MediaAnalyticsService::contentValid(sp<MediaAnalyticsItem>) {
+
+ // certain keys require certain uids
+ // internal consistency
+
+ return true;
+}
+
+// are we rate limited, normally false
+bool MediaAnalyticsService::rateLimited(sp<MediaAnalyticsItem>) {
+
+ return false;
+}
+
+
+} // namespace android
diff --git a/media/libmediaanalyticsservice/MediaAnalyticsService.h b/media/libmediaanalyticsservice/MediaAnalyticsService.h
new file mode 100644
index 0000000..f9afeb2
--- /dev/null
+++ b/media/libmediaanalyticsservice/MediaAnalyticsService.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_MEDIAANALYTICSSERVICE_H
+#define ANDROID_MEDIAANALYTICSSERVICE_H
+
+#include <arpa/inet.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+
+namespace android {
+
+class MediaAnalyticsService : public BnMediaAnalyticsService
+{
+
+ public:
+
+ virtual int64_t submit(sp<MediaAnalyticsItem> item, bool forcenew);
+
+ virtual List<sp<MediaAnalyticsItem>>
+ *getMediaAnalyticsItemList(bool finished, int64_t ts);
+ virtual List<sp<MediaAnalyticsItem>>
+ *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key);
+
+
+ static void instantiate();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ MediaAnalyticsService();
+ virtual ~MediaAnalyticsService();
+
+ private:
+ MediaAnalyticsItem::SessionID_t generateUniqueSessionID();
+
+ // statistics about our analytics
+ int64_t mItemsSubmitted;
+ int64_t mItemsFinalized;
+ int64_t mItemsDiscarded;
+ MediaAnalyticsItem::SessionID_t mLastSessionID;
+
+ // partitioned a bit so we don't over serialize
+ mutable Mutex mLock;
+ mutable Mutex mLock_ids;
+
+ // the most we hold in memory
+ // up to this many in each queue (open, finalized)
+ int32_t mMaxRecords;
+
+ // input validation after arrival from client
+ bool contentValid(sp<MediaAnalyticsItem>);
+ bool rateLimited(sp<MediaAnalyticsItem>);
+
+ // the ones that are still open
+ // (newest at front) since we keep looking for them
+ List<sp<MediaAnalyticsItem>> *mOpen;
+ // the ones we've finalized
+ // (oldest at front) so it prints nicely for dumpsys
+ List<sp<MediaAnalyticsItem>> *mFinalized;
+ // searching within these queues: queue, key
+ sp<MediaAnalyticsItem> findItem(List<sp<MediaAnalyticsItem>> *,
+ sp<MediaAnalyticsItem>, bool removeit);
+
+ void saveItem(sp<MediaAnalyticsItem>);
+ void saveItem(List<sp<MediaAnalyticsItem>>*, sp<MediaAnalyticsItem>, int);
+ void deleteItem(List<sp<MediaAnalyticsItem>>*, sp<MediaAnalyticsItem>);
+
+ String8 dumpQueue(List<sp<MediaAnalyticsItem>> *);
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAANALYTICSSERVICE_H
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 605c710..0a9f791 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -26,7 +26,6 @@
#include <media/stagefright/foundation/ADebug.h>
#include <utils/Errors.h>
#include <utils/misc.h>
-#include <../libstagefright/include/WVMExtractor.h>
#include "MediaPlayerFactory.h"
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 6e163d9..3ad461c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -972,6 +972,42 @@
return OK;
}
+status_t MediaPlayerService::Client::setBufferingSettings(
+ const BufferingSettings& buffering)
+{
+ ALOGV("[%d] setBufferingSettings(%d, %d, %d, %d, %d, %d, %d, %d)",
+ mConnId, buffering.mInitialBufferingMode, buffering.mRebufferingMode,
+ buffering.mInitialWatermarkMs, buffering.mInitialWatermarkKB,
+ buffering.mRebufferingWatermarkLowMs,
+ buffering.mRebufferingWatermarkHighMs,
+ buffering.mRebufferingWatermarkLowKB,
+ buffering.mRebufferingWatermarkHighKB);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+ return p->setBufferingSettings(buffering);
+}
+
+status_t MediaPlayerService::Client::getDefaultBufferingSettings(
+ BufferingSettings* buffering /* nonnull */)
+{
+ sp<MediaPlayerBase> p = getPlayer();
+ // TODO: create mPlayer on demand.
+ if (p == 0) return UNKNOWN_ERROR;
+ status_t ret = p->getDefaultBufferingSettings(buffering);
+ if (ret == NO_ERROR) {
+ ALOGV("[%d] getDefaultBufferingSettings(%d, %d, %d, %d, %d, %d, %d, %d)",
+ mConnId, buffering->mInitialBufferingMode, buffering->mRebufferingMode,
+ buffering->mInitialWatermarkMs, buffering->mInitialWatermarkKB,
+ buffering->mRebufferingWatermarkLowMs,
+ buffering->mRebufferingWatermarkHighMs,
+ buffering->mRebufferingWatermarkLowKB,
+ buffering->mRebufferingWatermarkHighKB);
+ } else {
+ ALOGV("[%d] getDefaultBufferingSettings returned %d", mConnId, ret);
+ }
+ return ret;
+}
+
status_t MediaPlayerService::Client::prepareAsync()
{
ALOGV("[%d] prepareAsync", mConnId);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index a4ea37f..8a6ada0 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -288,6 +288,9 @@
virtual void disconnect();
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer);
+ virtual status_t setBufferingSettings(const BufferingSettings& buffering) override;
+ virtual status_t getDefaultBufferingSettings(
+ BufferingSettings* buffering /* nonnull */) override;
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index fce9ab5..8761e9d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -33,9 +33,7 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
-#include "../../libstagefright/include/DRMExtractor.h"
#include "../../libstagefright/include/NuCachedSource2.h"
-#include "../../libstagefright/include/WVMExtractor.h"
#include "../../libstagefright/include/HTTPBase.h"
namespace android {
@@ -59,7 +57,6 @@
mFetchTimedTextDataGeneration(0),
mDurationUs(-1ll),
mAudioIsVorbis(false),
- mIsWidevine(false),
mIsSecure(false),
mIsStreaming(false),
mUIDValid(uidValid),
@@ -70,7 +67,6 @@
mPendingReadBufferTypes(0) {
mBufferingMonitor = new BufferingMonitor(notify);
resetDataSource();
- DataSource::RegisterDefaultSniffers();
}
void NuPlayer::GenericSource::resetDataSource() {
@@ -141,44 +137,9 @@
status_t NuPlayer::GenericSource::initFromDataSource() {
sp<IMediaExtractor> extractor;
- String8 mimeType;
- float confidence;
- sp<AMessage> dummy;
- bool isWidevineStreaming = false;
-
CHECK(mDataSource != NULL);
- if (mIsWidevine) {
- isWidevineStreaming = SniffWVM(
- mDataSource, &mimeType, &confidence, &dummy);
- if (!isWidevineStreaming ||
- strcasecmp(
- mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
- ALOGE("unsupported widevine mime: %s", mimeType.string());
- return UNKNOWN_ERROR;
- }
- } else if (mIsStreaming) {
- if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
- return UNKNOWN_ERROR;
- }
- isWidevineStreaming = !strcasecmp(
- mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM);
- }
-
- if (isWidevineStreaming) {
- // we don't want cached source for widevine streaming.
- mCachedSource.clear();
- mDataSource = mHttpSource;
- mWVMExtractor = new WVMExtractor(mDataSource);
- mWVMExtractor->setAdaptiveStreamingMode(true);
- if (mUIDValid) {
- mWVMExtractor->setUID(mUID);
- }
- extractor = mWVMExtractor;
- } else {
- extractor = MediaExtractor::Create(mDataSource,
- mimeType.isEmpty() ? NULL : mimeType.string());
- }
+ extractor = MediaExtractor::Create(mDataSource, NULL);
if (extractor == NULL) {
return UNKNOWN_ERROR;
@@ -194,17 +155,6 @@
if (mFileMeta->findInt64(kKeyDuration, &duration)) {
mDurationUs = duration;
}
-
- if (!mIsWidevine) {
- // Check mime to see if we actually have a widevine source.
- // If the data source is not URL-type (eg. file source), we
- // won't be able to tell until now.
- const char *fileMime;
- if (mFileMeta->findCString(kKeyMIMEType, &fileMime)
- && !strncasecmp(fileMime, "video/wvm", 9)) {
- mIsWidevine = true;
- }
- }
}
int32_t totalBitrate = 0;
@@ -296,6 +246,9 @@
// Widevine sources might re-initialize crypto when starting, if we delay
// this to start(), all data buffered during prepare would be wasted.
// (We don't actually start reading until start().)
+ //
+ // TODO: this logic may no longer be relevant after the removal of widevine
+ // support
if (mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK) {
ALOGE("failed to start audio track!");
return UNKNOWN_ERROR;
@@ -378,11 +331,8 @@
if (!mUri.empty()) {
const char* uri = mUri.c_str();
String8 contentType;
- mIsWidevine = !strncasecmp(uri, "widevine://", 11);
- if (!strncasecmp("http://", uri, 7)
- || !strncasecmp("https://", uri, 8)
- || mIsWidevine) {
+ if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);
if (mHttpSource == NULL) {
ALOGE("Failed to create http source!");
@@ -395,8 +345,6 @@
mHTTPService, uri, &mUriHeaders, &contentType,
static_cast<HTTPBase *>(mHttpSource.get()));
} else {
- mIsWidevine = false;
-
mDataSource = new FileSource(mFd, mOffset, mLength);
mFd = -1;
}
@@ -412,13 +360,9 @@
mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
}
- // For widevine or other cached streaming cases, we need to wait for
- // enough buffering before reporting prepared.
- // Note that even when URL doesn't start with widevine://, mIsWidevine
- // could still be set to true later, if the streaming or file source
- // is sniffed to be widevine. We don't want to buffer for file source
- // in that case, so must check the flag now.
- mIsStreaming = (mIsWidevine || mCachedSource != NULL);
+ // For cached streaming cases, we need to wait for enough
+ // buffering before reporting prepared.
+ mIsStreaming = (mCachedSource != NULL);
// init extractor from data source
status_t err = initFromDataSource();
@@ -450,6 +394,9 @@
if (mIsSecure) {
// secure decoders must be instantiated before starting widevine source
+ //
+ // TODO: mIsSecure and FLAG_SECURE may be obsolete, revisit after
+ // removing widevine
sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);
notifyInstantiateSecureDecoders(reply);
} else {
@@ -476,7 +423,7 @@
if (mIsStreaming) {
if (mBufferingMonitorLooper == NULL) {
- mBufferingMonitor->prepare(mCachedSource, mWVMExtractor, mDurationUs, mBitrate,
+ mBufferingMonitor->prepare(mCachedSource, mDurationUs, mBitrate,
mIsStreaming);
mBufferingMonitorLooper = new ALooper;
@@ -536,12 +483,6 @@
// nothing to do, just account for DRM playback status
setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
mStarted = false;
- if (mIsWidevine || mIsSecure) {
- // For widevine or secure sources we need to prevent any further reads.
- sp<AMessage> msg = new AMessage(kWhatStopWidevine, this);
- sp<AMessage> response;
- (void) msg->postAndAwaitResponse(&response);
- }
}
void NuPlayer::GenericSource::pause() {
@@ -719,20 +660,6 @@
break;
}
- case kWhatStopWidevine:
- {
- // mStopRead is only used for Widevine to prevent the video source
- // from being read while the associated video decoder is shutting down.
- mStopRead = true;
- if (mVideoTrack.mSource != NULL) {
- mVideoTrack.mPackets->clear();
- }
- sp<AMessage> response = new AMessage;
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
- break;
- }
default:
Source::onMessageReceived(msg);
break;
@@ -888,11 +815,6 @@
return -EWOULDBLOCK;
}
- if (mIsWidevine && !audio) {
- // try to read a buffer as we may not have been able to the last time
- postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
- }
-
status_t finalResult;
if (!track->mPackets->hasBufferAvailable(&finalResult)) {
if (finalResult == OK) {
@@ -1222,6 +1144,9 @@
// If the Widevine source is stopped, do not attempt to read any
// more buffers.
+ //
+ // TODO: revisit after widevine is removed. May be able to
+ // combine mStopRead with mStarted.
if (mStopRead) {
return INVALID_OPERATION;
}
@@ -1366,6 +1291,9 @@
media_track_type trackType, int64_t seekTimeUs, MediaPlayerSeekMode mode,
int64_t *actualTimeUs, bool formatChange) {
// Do not read data if Widevine source is stopped
+ //
+ // TODO: revisit after widevine is removed. May be able to
+ // combine mStopRead with mStarted.
if (mStopRead) {
return;
}
@@ -1374,19 +1302,11 @@
switch (trackType) {
case MEDIA_TRACK_TYPE_VIDEO:
track = &mVideoTrack;
- if (mIsWidevine) {
- maxBuffers = 2;
- } else {
- maxBuffers = 8; // too large of a number may influence seeks
- }
+ maxBuffers = 8; // too large of a number may influence seeks
break;
case MEDIA_TRACK_TYPE_AUDIO:
track = &mAudioTrack;
- if (mIsWidevine) {
- maxBuffers = 8;
- } else {
- maxBuffers = 64;
- }
+ maxBuffers = 64;
break;
case MEDIA_TRACK_TYPE_SUBTITLE:
track = &mSubtitleTrack;
@@ -1414,9 +1334,9 @@
seeking = true;
}
- const bool couldReadMultiple = (!mIsWidevine && track->mSource->supportReadMultiple());
+ const bool couldReadMultiple = (track->mSource->supportReadMultiple());
- if (mIsWidevine || couldReadMultiple) {
+ if (couldReadMultiple) {
options.setNonBlocking();
}
@@ -1538,17 +1458,16 @@
void NuPlayer::GenericSource::BufferingMonitor::prepare(
const sp<NuCachedSource2> &cachedSource,
- const sp<WVMExtractor> &wvmExtractor,
int64_t durationUs,
int64_t bitrate,
bool isStreaming) {
Mutex::Autolock _l(mLock);
- prepare_l(cachedSource, wvmExtractor, durationUs, bitrate, isStreaming);
+ prepare_l(cachedSource, durationUs, bitrate, isStreaming);
}
void NuPlayer::GenericSource::BufferingMonitor::stop() {
Mutex::Autolock _l(mLock);
- prepare_l(NULL /* cachedSource */, NULL /* wvmExtractor */, -1 /* durationUs */,
+ prepare_l(NULL /* cachedSource */, -1 /* durationUs */,
-1 /* bitrate */, false /* isStreaming */);
}
@@ -1603,22 +1522,17 @@
void NuPlayer::GenericSource::BufferingMonitor::prepare_l(
const sp<NuCachedSource2> &cachedSource,
- const sp<WVMExtractor> &wvmExtractor,
int64_t durationUs,
int64_t bitrate,
bool isStreaming) {
- ALOGW_IF(wvmExtractor != NULL && cachedSource != NULL,
- "WVMExtractor and NuCachedSource are both present when "
- "BufferingMonitor::prepare_l is called, ignore NuCachedSource");
mCachedSource = cachedSource;
- mWVMExtractor = wvmExtractor;
mDurationUs = durationUs;
mBitrate = bitrate;
mIsStreaming = isStreaming;
mAudioTimeUs = 0;
mVideoTimeUs = 0;
- mPrepareBuffering = (cachedSource != NULL || wvmExtractor != NULL);
+ mPrepareBuffering = (cachedSource != NULL);
cancelPollBuffering_l();
mOffloadAudio = false;
mFirstDequeuedBufferRealUs = -1ll;
@@ -1702,9 +1616,7 @@
int32_t kbps = 0;
status_t err = UNKNOWN_ERROR;
- if (mWVMExtractor != NULL) {
- err = mWVMExtractor->getEstimatedBandwidthKbps(&kbps);
- } else if (mCachedSource != NULL) {
+ if (mCachedSource != NULL) {
err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
}
@@ -1744,10 +1656,7 @@
int64_t cachedDurationUs = -1ll;
ssize_t cachedDataRemaining = -1;
- if (mWVMExtractor != NULL) {
- cachedDurationUs =
- mWVMExtractor->getCachedDurationUs(&finalStatus);
- } else if (mCachedSource != NULL) {
+ if (mCachedSource != NULL) {
cachedDataRemaining =
mCachedSource->approxDataRemaining(&finalStatus);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index d317c42..38d8616 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -37,7 +37,6 @@
struct MediaSource;
class MediaBuffer;
struct NuCachedSource2;
-class WVMExtractor;
struct NuPlayer::GenericSource : public NuPlayer::Source {
GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid);
@@ -103,7 +102,6 @@
kWhatSelectTrack,
kWhatSeek,
kWhatReadBuffer,
- kWhatStopWidevine,
kWhatStart,
kWhatResume,
kWhatSecureDecodersInstantiated,
@@ -123,7 +121,6 @@
// Set up state.
void prepare(const sp<NuCachedSource2> &cachedSource,
- const sp<WVMExtractor> &wvmExtractor,
int64_t durationUs,
int64_t bitrate,
bool isStreaming);
@@ -157,7 +154,6 @@
sp<AMessage> mNotify;
sp<NuCachedSource2> mCachedSource;
- sp<WVMExtractor> mWVMExtractor;
int64_t mDurationUs;
int64_t mBitrate;
bool mIsStreaming;
@@ -177,7 +173,6 @@
int64_t mlastDequeuedBufferMediaUs;
void prepare_l(const sp<NuCachedSource2> &cachedSource,
- const sp<WVMExtractor> &wvmExtractor,
int64_t durationUs,
int64_t bitrate,
bool isStreaming);
@@ -206,7 +201,6 @@
int32_t mFetchTimedTextDataGeneration;
int64_t mDurationUs;
bool mAudioIsVorbis;
- bool mIsWidevine;
bool mIsSecure;
bool mIsStreaming;
bool mUIDValid;
@@ -221,7 +215,6 @@
sp<DataSource> mDataSource;
sp<NuCachedSource2> mCachedSource;
sp<DataSource> mHttpSource;
- sp<WVMExtractor> mWVMExtractor;
sp<MetaData> mFileMeta;
DrmManagerClient *mDrmManagerClient;
sp<DecryptHandle> mDecryptHandle;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d240521..f8a6a4e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -263,9 +263,6 @@
} else {
sp<GenericSource> genericSource =
new GenericSource(notify, mUIDValid, mUID);
- // Don't set FLAG_SECURE on mSourceFlags here for widevine.
- // The correct flags will be updated in Source::kWhatFlagsChanged
- // handler when GenericSource is prepared.
status_t err = genericSource->setDataSource(httpService, url, headers);
@@ -1661,9 +1658,7 @@
// directly queuing to display, as this will even improve textureview
// playback.
{
- char value[PROPERTY_VALUE_MAX];
- if (property_get("persist.sys.media.avsync", value, NULL) &&
- (!strcmp("1", value) || !strcasecmp("true", value))) {
+ if (property_get_bool("persist.sys.media.avsync", false)) {
format->setInt32("auto-frc", 1);
}
}
@@ -1671,29 +1666,6 @@
(*decoder)->init();
(*decoder)->configure(format);
- // allocate buffers to decrypt widevine source buffers
- if (!audio && (mSourceFlags & Source::FLAG_SECURE)) {
- Vector<sp<MediaCodecBuffer> > inputBufs;
- CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK);
-
- Vector<MediaBuffer *> mediaBufs;
- for (size_t i = 0; i < inputBufs.size(); i++) {
- const sp<MediaCodecBuffer> &buffer = inputBufs[i];
- MediaBuffer *mbuf = new MediaBuffer(buffer->data(), buffer->size());
- mediaBufs.push(mbuf);
- }
-
- status_t err = mSource->setBuffers(audio, mediaBufs);
- if (err != OK) {
- for (size_t i = 0; i < mediaBufs.size(); ++i) {
- mediaBufs[i]->release();
- }
- mediaBufs.clear();
- ALOGE("Secure source didn't support secure mediaBufs.");
- return err;
- }
- }
-
if (!audio) {
sp<AMessage> params = new AMessage();
float rate = getFrameRate();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 3da5030..1d62498 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -423,17 +423,7 @@
}
void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
- bool hadNoRenderer = (mRenderer == NULL);
mRenderer = renderer;
- if (hadNoRenderer && mRenderer != NULL) {
- // this means that the widevine legacy source is ready
- onRequestInputBuffers();
- }
-}
-
-void NuPlayer::Decoder::onGetInputBuffers(
- Vector<sp<MediaCodecBuffer> > *dstBuffers) {
- CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers));
}
void NuPlayer::Decoder::onResume(bool notifyComplete) {
@@ -530,9 +520,7 @@
* returns true if we should request more data
*/
bool NuPlayer::Decoder::doRequestBuffers() {
- // mRenderer is only NULL if we have a legacy widevine source that
- // is not yet ready. In this case we must not fetch input.
- if (isDiscontinuityPending() || mRenderer == NULL) {
+ if (isDiscontinuityPending()) {
return false;
}
status_t err = OK;
@@ -885,40 +873,6 @@
bool hasBuffer = msg->findBuffer("buffer", &buffer);
bool needsCopy = true;
- // handle widevine classic source - that fills an arbitrary input buffer
- MediaBuffer *mediaBuffer = NULL;
- if (hasBuffer) {
- mediaBuffer = (MediaBuffer *)(buffer->getMediaBufferBase());
- if (mediaBuffer != NULL) {
- // likely filled another buffer than we requested: adjust buffer index
- size_t ix;
- for (ix = 0; ix < mInputBuffers.size(); ix++) {
- const sp<MediaCodecBuffer> &buf = mInputBuffers[ix];
- if (buf->data() == mediaBuffer->data()) {
- // all input buffers are dequeued on start, hence the check
- if (!mInputBufferIsDequeued[ix]) {
- ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
- mComponentName.c_str(), ix, bufferIx);
- mediaBuffer->release();
- return false;
- }
-
- // TRICKY: need buffer for the metadata, so instead, set
- // codecBuffer to the same (though incorrect) buffer to
- // avoid a memcpy into the codecBuffer
- codecBuffer = new MediaCodecBuffer(codecBuffer->format(), buffer);
- codecBuffer->setRange(
- mediaBuffer->range_offset(),
- mediaBuffer->range_length());
- bufferIx = ix;
- needsCopy = false;
- break;
- }
- }
- CHECK(ix < mInputBuffers.size());
- }
- }
-
if (buffer == NULL /* includes !hasBuffer */) {
int32_t streamErr = ERROR_END_OF_STREAM;
CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
@@ -988,18 +942,11 @@
timeUs,
flags);
if (err != OK) {
- if (mediaBuffer != NULL) {
- mediaBuffer->release();
- }
ALOGE("Failed to queue input buffer for %s (err=%d)",
mComponentName.c_str(), err);
handleError(err);
} else {
mInputBufferIsDequeued.editItemAt(bufferIx) = false;
- if (mediaBuffer != NULL) {
- CHECK(mMediaBuffers[bufferIx] == NULL);
- mMediaBuffers.editItemAt(bufferIx) = mediaBuffer;
- }
}
}
return true;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 7217ed1..82db59c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -47,7 +47,6 @@
virtual void onConfigure(const sp<AMessage> &format);
virtual void onSetParameters(const sp<AMessage> ¶ms);
virtual void onSetRenderer(const sp<Renderer> &renderer);
- virtual void onGetInputBuffers(Vector<sp<MediaCodecBuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 9c007ae..1210dc9 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -92,14 +92,6 @@
PostAndAwaitResponse(msg, &response);
}
-status_t NuPlayer::DecoderBase::getInputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const {
- sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, this);
- msg->setPointer("buffers", buffers);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
-}
-
void NuPlayer::DecoderBase::signalFlush() {
(new AMessage(kWhatFlush, this))->post();
}
@@ -166,20 +158,6 @@
break;
}
- case kWhatGetInputBuffers:
- {
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- Vector<sp<MediaCodecBuffer> > *dstBuffers;
- CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
-
- onGetInputBuffers(dstBuffers);
-
- (new AMessage)->postReply(replyID);
- break;
- }
-
case kWhatRequestInputBuffers:
{
mRequestInputBuffersPending = false;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 6f4ead6..6811903 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -43,7 +43,6 @@
void setRenderer(const sp<Renderer> &renderer);
virtual status_t setVideoSurface(const sp<Surface> &) { return INVALID_OPERATION; }
- status_t getInputBuffers(Vector<sp<MediaCodecBuffer> > *dstBuffers) const;
void signalFlush();
void signalResume(bool notifyComplete);
void initiateShutdown();
@@ -71,7 +70,6 @@
virtual void onConfigure(const sp<AMessage> &format) = 0;
virtual void onSetParameters(const sp<AMessage> ¶ms) = 0;
virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
- virtual void onGetInputBuffers(Vector<sp<MediaCodecBuffer> > *dstBuffers) = 0;
virtual void onResume(bool notifyComplete) = 0;
virtual void onFlush() = 0;
virtual void onShutdown(bool notifyComplete) = 0;
@@ -91,7 +89,6 @@
kWhatSetParameters = 'setP',
kWhatSetRenderer = 'setR',
kWhatPause = 'paus',
- kWhatGetInputBuffers = 'gInB',
kWhatRequestInputBuffers = 'reqB',
kWhatFlush = 'flus',
kWhatShutdown = 'shuD',
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index e4767ff..cb668e4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -93,11 +93,6 @@
"ignoring request to change renderer");
}
-void NuPlayer::DecoderPassThrough::onGetInputBuffers(
- Vector<sp<MediaCodecBuffer> > * /* dstBuffers */) {
- ALOGE("onGetInputBuffers() called unexpectedly");
-}
-
bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) {
int32_t generation;
CHECK(msg->findInt32("generation", &generation));
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 9af25ff..173387a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -38,7 +38,6 @@
virtual void onConfigure(const sp<AMessage> &format);
virtual void onSetParameters(const sp<AMessage> ¶ms);
virtual void onSetRenderer(const sp<Renderer> &renderer);
- virtual void onGetInputBuffers(Vector<sp<MediaCodecBuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 3efa54c..6ec79e6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -478,9 +478,7 @@
notifyListener_l(MEDIA_STOPPED);
}
- char value[PROPERTY_VALUE_MAX];
- if (property_get("persist.debug.sf.stats", value, NULL) &&
- (!strcmp("1", value) || !strcasecmp("true", value))) {
+ if (property_get_bool("persist.debug.sf.stats", false)) {
Vector<String16> args;
dump(-1, args);
}
diff --git a/media/liboboe/README.md b/media/liboboe/README.md
new file mode 100644
index 0000000..80894c6
--- /dev/null
+++ b/media/liboboe/README.md
@@ -0,0 +1 @@
+Oboe Audio input/output API
diff --git a/media/liboboe/include/oboe/OboeAudio.h b/media/liboboe/include/oboe/OboeAudio.h
new file mode 100644
index 0000000..32fef1b
--- /dev/null
+++ b/media/liboboe/include/oboe/OboeAudio.h
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This is the 'C' ABI for Oboe.
+ */
+#ifndef OBOE_OBOEAUDIO_H
+#define OBOE_OBOEAUDIO_H
+
+#include "OboeDefinitions.h"
+
+typedef int32_t OboeDeviceId;
+typedef oboe_handle_t OboeStream;
+typedef oboe_handle_t OboeStreamBuilder;
+typedef oboe_handle_t OboeThread;
+
+#define OBOE_STREAM_NONE ((OboeStream)OBOE_HANDLE_INVALID)
+#define OBOE_STREAM_BUILDER_NONE ((OboeStreamBuilder)OBOE_HANDLE_INVALID)
+
+
+// ============================================================
+// Audio System
+// ============================================================
+
+/**
+ * @return time in the same clock domain as the timestamps
+ */
+oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
+
+/**
+ * The text is the ASCII symbol corresponding to the returnCode,
+ * or an English message saying the returnCode is unrecognized.
+ * This is intended for developers to use when debugging.
+ * It is not for display to users.
+ *
+ * @return pointer to a text representation of an Oboe result code.
+ */
+const char * Oboe_convertResultToText(oboe_result_t returnCode);
+
+/**
+ * The text is the ASCII symbol corresponding to the stream state,
+ * or an English message saying the state is unrecognized.
+ * This is intended for developers to use when debugging.
+ * It is not for display to users.
+ *
+ * @return pointer to a text representation of an Oboe state.
+ */
+const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
+
+// ============================================================
+// StreamBuilder
+// ============================================================
+
+/**
+ * Create a StreamBuilder that can be used to open a Stream.
+ *
+ * The deviceId is initially unspecified, meaning that the current default device will be used.
+ *
+ * The default direction is OBOE_DIRECTION_OUTPUT.
+ * The default sharing mode is OBOE_SHARING_MODE_LEGACY.
+ * The data format, samplesPerFrames and sampleRate are unspecified and will be
+ * chosen by the device when it is opened.
+ *
+ * OboeStreamBuilder_delete() must be called when you are done using the builder.
+ */
+oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
+
+/**
+ * Request an audio device identified device using an ID.
+ * The ID is platform specific.
+ * On Android, for example, the ID could be obtained from the Java AudioManager.
+ *
+ * By default, the primary device will be used.
+ *
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder, OboeDeviceId deviceId);
+
+/**
+ * Request a sample rate in Hz.
+ * The stream may be opened with a different sample rate.
+ * So the application should query for the actual rate after the stream is opened.
+ *
+ * Technically, this should be called the "frame rate" or "frames per second",
+ * because it refers to the number of complete frames transferred per second.
+ * But it is traditionally called "sample rate". Se we use that term.
+ *
+ * Default is OBOE_UNSPECIFIED.
+ *
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+ oboe_sample_rate_t sampleRate);
+
+/**
+ * Returns sample rate in Hertz (samples per second).
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+ oboe_sample_rate_t *sampleRate);
+
+
+/**
+ * Request a number of samples per frame.
+ * The stream may be opened with a different value.
+ * So the application should query for the actual value after the stream is opened.
+ *
+ * Default is OBOE_UNSPECIFIED.
+ *
+ * Note, this quantity is sometimes referred to as "channel count".
+ *
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+ int32_t samplesPerFrame);
+
+/**
+ * Note, this quantity is sometimes referred to as "channel count".
+ *
+ * @param builder handle provided by Oboe_createStreamBuilder()
+ * @param samplesPerFrame pointer to a variable to be set to samplesPerFrame.
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+ int32_t *samplesPerFrame);
+
+
+/**
+ * Request a sample data format, for example OBOE_AUDIO_FORMAT_PCM16.
+ * The application should query for the actual format after the stream is opened.
+ *
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder, oboe_audio_format_t format);
+
+/**
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder, oboe_audio_format_t *format);
+
+/**
+ * Request a mode for sharing the device.
+ * The requested sharing mode may not be available.
+ * So the application should query for the actual mode after the stream is opened.
+ *
+ * @param builder handle provided by Oboe_createStreamBuilder()
+ * @param sharingMode OBOE_SHARING_MODE_LEGACY or OBOE_SHARING_MODE_EXCLUSIVE
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t sharingMode);
+
+/**
+ * Return requested sharing mode.
+ * @return OBOE_OK or a negative error
+ */
+oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t *sharingMode);
+
+/**
+ * Request the direction for a stream. The default is OBOE_DIRECTION_OUTPUT.
+ *
+ * @param builder handle provided by Oboe_createStreamBuilder()
+ * @param direction OBOE_DIRECTION_OUTPUT or OBOE_DIRECTION_INPUT
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+ oboe_direction_t direction);
+
+/**
+ * @param builder handle provided by Oboe_createStreamBuilder()
+ * @param direction pointer to a variable to be set to the currently requested direction.
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+ oboe_direction_t *direction);
+
+/**
+ * Open a stream based on the options in the StreamBuilder.
+ *
+ * OboeStream_close must be called when finished with the stream to recover
+ * the memory and to free the associated resources.
+ *
+ * @param builder handle provided by Oboe_createStreamBuilder()
+ * @param stream pointer to a variable to receive the new stream handle
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder, OboeStream *stream);
+
+/**
+ * Delete the resources associated with the StreamBuilder.
+ *
+ * @param builder handle provided by Oboe_createStreamBuilder()
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder);
+
+// ============================================================
+// Stream Control
+// ============================================================
+
+/**
+ * Free the resources associated with a stream created by OboeStreamBuilder_openStream()
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_close(OboeStream stream);
+
+/**
+ * Asynchronously request to start playing the stream. For output streams, one should
+ * write to the stream to fill the buffer before starting.
+ * Otherwise it will underflow.
+ * After this call the state will be in OBOE_STREAM_STATE_STARTING or OBOE_STREAM_STATE_STARTED.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_requestStart(OboeStream stream);
+
+/**
+ * Asynchronous request for the stream to pause.
+ * Pausing a stream will freeze the data flow but not flush any buffers.
+ * Use OboeStream_Start() to resume playback after a pause.
+ * After this call the state will be in OBOE_STREAM_STATE_PAUSING or OBOE_STREAM_STATE_PAUSED.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_requestPause(OboeStream stream);
+
+/**
+ * Asynchronous request for the stream to flush.
+ * Flushing will discard any pending data.
+ * This call only works if the stream is pausing or paused. TODO review
+ * Frame counters are not reset by a flush. They may be advanced.
+ * After this call the state will be in OBOE_STREAM_STATE_FLUSHING or OBOE_STREAM_STATE_FLUSHED.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_requestFlush(OboeStream stream);
+
+/**
+ * Asynchronous request for the stream to stop.
+ * The stream will stop after all of the data currently buffered has been played.
+ * After this call the state will be in OBOE_STREAM_STATE_STOPPING or OBOE_STREAM_STATE_STOPPED.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_requestStop(OboeStream stream);
+
+/**
+ * Query the current state, eg. OBOE_STREAM_STATE_PAUSING
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param state pointer to a variable that will be set to the current state
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
+
+/**
+ * Wait until the current state no longer matches the input state.
+ *
+ * <pre><code>
+ * oboe_stream_state_t currentState;
+ * oboe_result_t result = OboeStream_getState(stream, ¤tState);
+ * while (result == OBOE_OK && currentState != OBOE_STREAM_STATE_PAUSING) {
+ * result = OboeStream_waitForStateChange(
+ * stream, currentState, ¤tState, MY_TIMEOUT_NANOS);
+ * }
+ * </code></pre>
+ *
+ * @param stream A handle provided by OboeStreamBuilder_openStream()
+ * @param inputState The state we want to avoid.
+ * @param nextState Pointer to a variable that will be set to the new state.
+ * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+ oboe_stream_state_t inputState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+// ============================================================
+// Stream I/O
+// ============================================================
+
+/**
+ * Read data from the stream.
+ *
+ * The call will wait until the read is complete or until it runs out of time.
+ * If timeoutNanos is zero then this call will not wait.
+ *
+ * Note that timeoutNanoseconds is a relative duration in wall clock time.
+ * Time will not stop if the thread is asleep.
+ * So it will be implemented using CLOCK_BOOTTIME.
+ *
+ * This call is "strong non-blocking" unless it has to wait for data.
+ *
+ * @param stream A stream created using OboeStreamBuilder_openStream().
+ * @param buffer The address of the first sample.
+ * @param numFrames Number of frames to read. Only complete frames will be written.
+ * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
+ * @return The number of frames actually written or a negative error.
+ */
+oboe_result_t OboeStream_read(OboeStream stream,
+ void *buffer,
+ int32_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+/**
+ * Write data to the stream.
+ *
+ * The call will wait until the write is complete or until it runs out of time.
+ * If timeoutNanos is zero then this call will not wait.
+ *
+ * Note that timeoutNanoseconds is a relative duration in wall clock time.
+ * Time will not stop if the thread is asleep.
+ * So it will be implemented using CLOCK_BOOTTIME.
+ *
+ * This call is "strong non-blocking" unless it has to wait for room in the buffer.
+ *
+ * @param stream A stream created using OboeStreamBuilder_openStream().
+ * @param buffer The address of the first sample.
+ * @param numFrames Number of frames to write. Only complete frames will be written.
+ * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
+ * @return The number of frames actually written or a negative error.
+ */
+oboe_result_t OboeStream_write(OboeStream stream,
+ const void *buffer,
+ int32_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+
+// ============================================================
+// High priority audio threads
+// ============================================================
+
+/**
+ * Create a thread with special properties for low latency audio performance.
+ * This thread can be used to implement a callback API.
+ *
+ * Note that this API is in flux.
+ *
+ * @param threadHandlePtr a pointer to receive a thread handle
+ * @param periodNanoseconds the estimated period at which the audio thread will need to wake up
+ * @param start_routine your thread entry point
+ * @param arg an argument that will be passed to your thread entry point
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t Oboe_createAudioThread(OboeThread *threadHandlePtr,
+ oboe_nanoseconds_t periodNanoseconds,
+ void *(*start_routine)(void *), void *arg);
+
+/**
+ * Wait until the thread exits or an error occurs.
+ * The thread handle will be deleted.
+ *
+ * @param thread the thread handle passed back from Oboe_createAudioThread()
+ * @param returnArg a pointer to a variable to receive the return value
+ * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t Oboe_joinAudioThread(OboeThread thread,
+ void **returnArg,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+// ============================================================
+// Stream - queries
+// ============================================================
+
+
+/**
+ * This can be used to adjust the latency of the buffer by changing
+ * the threshold where blocking will occur.
+ * By combining this with OboeStream_getUnderrunCount(), the latency can be tuned
+ * at run-time for each device.
+ *
+ * This cannot be set higher than OboeStream_getBufferCapacity().
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param frames requested number of frames that can be filled without blocking
+ * @return actual number of frames or a negative error
+ */
+oboe_result_t OboeStream_setBufferSize(OboeStream stream, oboe_size_frames_t frames);
+
+/**
+ * Query the maximum number of frames that can be filled without blocking.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param frames pointer to variable to receive the buffer size
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
+
+/**
+ * Query the number of frames that are read or written by the endpoint at one time.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param frames pointer to variable to receive the burst size
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
+
+/**
+ * Query maximum buffer capacity in frames.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param frames pointer to variable to receive the buffer capacity
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
+
+/**
+ * An XRun is an Underrun or an Overrun.
+ * During playing, an underrun will occur if the stream is not written in time
+ * and the system runs out of valid data.
+ * During recording, an overrun will occur if the stream is not read in time
+ * and there is no place to put the incoming data so it is discarded.
+ *
+ * An underrun or overrun can cause an audible "pop" or "glitch".
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param xRunCount pointer to variable to receive the underrun or overrun count
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
+
+/**
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param sampleRate pointer to variable to receive the actual sample rate
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getSampleRate(OboeStream stream, int32_t *sampleRate);
+
+/**
+ * The samplesPerFrame is also known as channelCount.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param samplesPerFrame pointer to variable to receive the actual samples per frame
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
+
+/**
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param format pointer to variable to receive the actual data format
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
+
+/**
+ * Provide actual sharing mode.
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param sharingMode pointer to variable to receive the actual sharing mode
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+ oboe_sharing_mode_t *sharingMode);
+
+/**
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param direction pointer to a variable to be set to the current direction.
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getDirection(OboeStream stream, uint32_t *direction);
+
+/**
+ * Passes back the number of frames that have been written since the stream was created.
+ * For an output stream, this will be advanced by the application calling write().
+ * For an input stream, this will be advanced by the device or service.
+ *
+ * The frame position is monotonically increasing.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param frames pointer to variable to receive the frames written
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getFramesWritten(OboeStream stream, oboe_position_frames_t *frames);
+
+/**
+ * Passes back the number of frames that have been read since the stream was created.
+ * For an output stream, this will be advanced by the device or service.
+ * For an input stream, this will be advanced by the application calling read().
+ *
+ * The frame position is monotonically increasing.
+ *
+ * @param stream handle provided by OboeStreamBuilder_openStream()
+ * @param frames pointer to variable to receive the frames written
+ * @return OBOE_OK or a negative error.
+ */
+oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
+
+/**
+ * Passes back the time at which a particular frame was presented.
+ * This can be used to synchronize audio with video or MIDI.
+ * It can also be used to align a recorded stream with a playback stream.
+ *
+ * Timestamps are only valid when the stream is in OBOE_STREAM_STATE_STARTED.
+ * OBOE_ERROR_INVALID_STATE will be returned if the stream is not started.
+ * Note that because requestStart() is asynchronous, timestamps will not be valid until
+ * a short time after calling requestStart().
+ * So OBOE_ERROR_INVALID_STATE should not be considered a fatal error.
+ * Just try calling again later.
+ *
+ * If an error occurs, then the position and time will not be modified.
+ *
+ * The position and time passed back are monotonically increasing.
+ *
+ * @param stream A handle provided by OboeStreamBuilder_openStream()
+ * @param clockId OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
+ * @param framePosition pointer to a variable to receive the position
+ * @param timeNanoseconds pointer to a variable to receive the time
+ * @return OBOE_OK or a negative error
+ */
+oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+ oboe_clockid_t clockid,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds);
+
+#endif //NATIVEOBOE_OBOEAUDIO_H
diff --git a/media/liboboe/include/oboe/OboeDefinitions.h b/media/liboboe/include/oboe/OboeDefinitions.h
new file mode 100644
index 0000000..b3e2deb
--- /dev/null
+++ b/media/liboboe/include/oboe/OboeDefinitions.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OBOE_OBOEDEFINITIONS_H
+#define OBOE_OBOEDEFINITIONS_H
+
+#include <stdint.h>
+
+typedef int32_t oboe_handle_t;
+typedef int32_t oboe_result_t;
+typedef int32_t oboe_sample_rate_t;
+/** This is used for small quantities such as the number of frames in a buffer. */
+typedef int32_t oboe_size_frames_t;
+/** This is used for large quantities, such as the number of frames that have
+ * been played since a stream was started.
+ * At 48000 Hz, a 32-bit integer would wrap around in just over 12 hours.
+ */
+typedef int64_t oboe_position_frames_t;
+
+typedef int64_t oboe_nanoseconds_t;
+typedef uint32_t oboe_audio_format_t;
+
+/**
+ * This is used to represent a value that has not been specified.
+ * For example, an application could use OBOE_UNSPECIFIED to indicate
+ * that is did not not care what the specific value of a parameter was
+ * and would accept whatever it was given.
+ */
+#define OBOE_UNSPECIFIED 0
+#define OBOE_NANOS_PER_MICROSECOND ((int64_t)1000)
+#define OBOE_NANOS_PER_MILLISECOND (OBOE_NANOS_PER_MICROSECOND * 1000)
+#define OBOE_MILLIS_PER_SECOND 1000
+#define OBOE_NANOS_PER_SECOND (OBOE_NANOS_PER_MILLISECOND * OBOE_MILLIS_PER_SECOND)
+
+#define OBOE_HANDLE_INVALID ((oboe_handle_t)-1)
+
+enum oboe_direction_t {
+ OBOE_DIRECTION_OUTPUT,
+ OBOE_DIRECTION_INPUT,
+ OBOE_DIRECTION_COUNT // This should always be last.
+};
+
+enum oboe_datatype_t {
+ OBOE_AUDIO_DATATYPE_INT16,
+ OBOE_AUDIO_DATATYPE_INT32,
+ OBOE_AUDIO_DATATYPE_INT824,
+ OBOE_AUDIO_DATATYPE_UINT8,
+ OBOE_AUDIO_DATATYPE_FLOAT32, // Add new values below.
+ OBOE_AUDIO_DATATYPE_COUNT // This should always be last.
+};
+
+enum oboe_content_t {
+ OBOE_AUDIO_CONTENT_PCM,
+ OBOE_AUDIO_CONTENT_MP3,
+ OBOE_AUDIO_CONTENT_AAC,
+ OBOE_AUDIO_CONTENT_AC3,
+ OBOE_AUDIO_CONTENT_EAC3,
+ OBOE_AUDIO_CONTENT_DTS,
+ OBOE_AUDIO_CONTENT_DTSHD, // Add new values below.
+ OBOE_AUDIO_CONTENT_COUNT // This should always be last.
+};
+
+enum oboe_wrapper_t {
+ OBOE_AUDIO_WRAPPER_NONE,
+ OBOE_AUDIO_WRAPPER_IEC61937, // Add new values below.
+ OBOE_AUDIO_WRAPPER_COUNT // This should always be last.
+};
+
+/**
+ * Fields packed into oboe_audio_format_t, from most to least significant bits.
+ * Reserved:8
+ * Wrapper:8
+ * Content:8
+ * Data Type:8
+ */
+#define OBOE_AUDIO_FORMAT(dataType, content, wrapper) \
+ ((oboe_audio_format_t)((wrapper << 16) | (content << 8) | dataType))
+
+#define OBOE_AUDIO_FORMAT_RAW(dataType, content) \
+ OBOE_AUDIO_FORMAT(dataType, content, OBOE_AUDIO_WRAPPER_NONE)
+
+#define OBOE_AUDIO_FORMAT_DATA_TYPE(format) \
+ (format & 0x0FF)
+
+// Define some common formats.
+#define OBOE_AUDIO_FORMAT_PCM16 \
+ OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT16, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_PCM_FLOAT \
+ OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_FLOAT32, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_PCM824 \
+ OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT824, OBOE_AUDIO_CONTENT_PCM)
+
+enum {
+ OBOE_OK,
+ OBOE_ERROR_BASE = -900, // TODO review
+ OBOE_ERROR_DISCONNECTED,
+ OBOE_ERROR_ILLEGAL_ARGUMENT,
+ OBOE_ERROR_INCOMPATIBLE,
+ OBOE_ERROR_INTERNAL, // an underlying API returned an error code
+ OBOE_ERROR_INVALID_STATE,
+ OBOE_ERROR_UNEXPECTED_STATE,
+ OBOE_ERROR_UNEXPECTED_VALUE,
+ OBOE_ERROR_INVALID_HANDLE,
+ OBOE_ERROR_INVALID_QUERY,
+ OBOE_ERROR_UNIMPLEMENTED,
+ OBOE_ERROR_UNAVAILABLE,
+ OBOE_ERROR_NO_FREE_HANDLES,
+ OBOE_ERROR_NO_MEMORY,
+ OBOE_ERROR_NULL,
+ OBOE_ERROR_TIMEOUT,
+ OBOE_ERROR_WOULD_BLOCK,
+ OBOE_ERROR_INVALID_ORDER
+};
+
+typedef enum {
+ OBOE_CLOCK_MONOTONIC, // Clock since booted, pauses when CPU is sleeping.
+ OBOE_CLOCK_BOOTTIME, // Clock since booted, runs all the time.
+ OBOE_CLOCK_COUNT // This should always be last.
+} oboe_clockid_t;
+
+typedef enum
+{
+ OBOE_STREAM_STATE_UNINITIALIZED = 0,
+ OBOE_STREAM_STATE_OPEN,
+ OBOE_STREAM_STATE_STARTING,
+ OBOE_STREAM_STATE_STARTED,
+ OBOE_STREAM_STATE_PAUSING,
+ OBOE_STREAM_STATE_PAUSED,
+ OBOE_STREAM_STATE_FLUSHING,
+ OBOE_STREAM_STATE_FLUSHED,
+ OBOE_STREAM_STATE_STOPPING,
+ OBOE_STREAM_STATE_STOPPED,
+ OBOE_STREAM_STATE_CLOSING,
+ OBOE_STREAM_STATE_CLOSED,
+} oboe_stream_state_t;
+
+// TODO review API
+typedef enum {
+ /**
+ * This will use an AudioTrack object for playing audio
+ * and an AudioRecord for recording data.
+ */
+ OBOE_SHARING_MODE_LEGACY,
+ /**
+ * This will be the only stream using a particular source or sink.
+ * This mode will provide the lowest possible latency.
+ * You should close EXCLUSIVE streams immediately when you are not using them.
+ */
+ OBOE_SHARING_MODE_EXCLUSIVE,
+ /**
+ * Multiple applications will be mixed by the Oboe Server.
+ * This will have higher latency than the EXCLUSIVE mode.
+ */
+ OBOE_SHARING_MODE_SHARED,
+ /**
+ * Multiple applications will do their own mixing into a memory mapped buffer.
+ * It may be possible for malicious applications to read the data produced by
+ * other apps. So do not use this for private data such as telephony or messaging.
+ */
+ OBOE_SHARING_MODE_PUBLIC_MIX,
+ OBOE_SHARING_MODE_COUNT // This should always be last.
+} oboe_sharing_mode_t;
+
+#endif // OBOE_OBOEDEFINITIONS_H
diff --git a/media/liboboe/include/oboe/README.md b/media/liboboe/include/oboe/README.md
new file mode 100644
index 0000000..de60d03
--- /dev/null
+++ b/media/liboboe/include/oboe/README.md
@@ -0,0 +1,4 @@
+Oboe Audio headers
+
+This folder contains the public header files.
+
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 21c90f0..f247475 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -53,6 +53,7 @@
#include <OMX_AsString.h>
#include "include/avc_utils.h"
+#include "include/ACodecBufferChannel.h"
#include "include/DataConverter.h"
#include "include/SecureBuffer.h"
#include "include/SharedMemoryBuffer.h"
@@ -564,16 +565,21 @@
ACodec::~ACodec() {
}
-void ACodec::setNotificationMessage(const sp<AMessage> &msg) {
- mNotify = msg;
-}
-
void ACodec::initiateSetup(const sp<AMessage> &msg) {
msg->setWhat(kWhatSetup);
msg->setTarget(this);
msg->post();
}
+std::shared_ptr<BufferChannelBase> ACodec::getBufferChannel() {
+ if (!mBufferChannel) {
+ mBufferChannel = std::make_shared<ACodecBufferChannel>(
+ new AMessage(kWhatInputBufferFilled, this),
+ new AMessage(kWhatOutputBufferDrained, this));
+ }
+ return mBufferChannel;
+}
+
void ACodec::signalSetParameters(const sp<AMessage> ¶ms) {
sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
msg->setMessage("params", params);
@@ -882,6 +888,8 @@
info.mStatus = BufferInfo::OWNED_BY_US;
info.mFenceFd = -1;
info.mRenderInfo = NULL;
+ info.mGraphicBuffer = NULL;
+ info.mNewGraphicBuffer = false;
if (mode == IOMX::kPortModePresetSecureBuffer) {
void *ptr = NULL;
@@ -934,20 +942,17 @@
return err;
}
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatBuffersAllocated);
-
- notify->setInt32("portIndex", portIndex);
-
- sp<PortDescription> desc = new PortDescription;
-
+ std::vector<ACodecBufferChannel::BufferAndId> array(mBuffers[portIndex].size());
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
- const BufferInfo &info = mBuffers[portIndex][i];
- desc->addBuffer(info.mBufferID, info.mData);
+ array[i] = {mBuffers[portIndex][i].mData, mBuffers[portIndex][i].mBufferID};
}
-
- notify->setObject("portDesc", desc);
- notify->post();
+ if (portIndex == kPortIndexInput) {
+ mBufferChannel->setInputBufferArray(array);
+ } else if (portIndex == kPortIndexOutput) {
+ mBufferChannel->setOutputBufferArray(array);
+ } else {
+ TRESPASS();
+ }
return OK;
}
@@ -1124,6 +1129,7 @@
info.mIsReadFence = false;
info.mRenderInfo = NULL;
info.mGraphicBuffer = graphicBuffer;
+ info.mNewGraphicBuffer = false;
// TODO: We shouln't need to create MediaCodecBuffer. In metadata mode
// OMX doesn't use the shared memory buffer, but some code still
@@ -1198,6 +1204,7 @@
info.mFenceFd = -1;
info.mRenderInfo = NULL;
info.mGraphicBuffer = NULL;
+ info.mNewGraphicBuffer = false;
info.mDequeuedAt = mDequeueCounter;
info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
@@ -1229,7 +1236,7 @@
}
ALOGV("[%s] submitting output meta buffer ID %u for graphic buffer %p",
- mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get());
+ mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer->handle);
--mMetadataBuffersToSubmit;
info->checkWriteFence("submitOutputMetadataBuffer");
@@ -1309,8 +1316,6 @@
}
void ACodec::notifyOfRenderedFrames(bool dropIncomplete, FrameRenderTracker::Info *until) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", CodecBase::kWhatOutputFramesRendered);
std::list<FrameRenderTracker::Info> done =
mRenderTracker.checkFencesAndGetRenderedFrames(until, dropIncomplete);
@@ -1326,9 +1331,7 @@
}
}
- if (MediaCodec::CreateFramesRenderedMessage(done, msg)) {
- msg->post();
- }
+ mCallback->onOutputFramesRendered(done);
}
ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
@@ -1374,7 +1377,11 @@
break;
}
- ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer());
+ ALOGV("dequeued buffer #%u with age %u, graphicBuffer %p",
+ (unsigned)(info - &mBuffers[kPortIndexOutput][0]),
+ mDequeueCounter - info->mDequeuedAt,
+ info->mGraphicBuffer->handle);
+
info->mStatus = BufferInfo::OWNED_BY_US;
info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow");
updateRenderInfoForDequeuedBuffer(buf, fenceFd, info);
@@ -1420,6 +1427,7 @@
// discard buffer in LRU info and replace with new buffer
oldest->mGraphicBuffer = new GraphicBuffer(buf, false);
+ oldest->mNewGraphicBuffer = true;
oldest->mStatus = BufferInfo::OWNED_BY_US;
oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest");
mRenderTracker.untrackFrame(oldest->mRenderInfo);
@@ -1428,13 +1436,19 @@
ALOGV("replaced oldest buffer #%u with age %u, graphicBuffer %p",
(unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
mDequeueCounter - oldest->mDequeuedAt,
- oldest->mGraphicBuffer->getNativeBuffer());
+ oldest->mGraphicBuffer->handle);
updateRenderInfoForDequeuedBuffer(buf, fenceFd, oldest);
return oldest;
}
status_t ACodec::freeBuffersOnPort(OMX_U32 portIndex) {
+ if (portIndex == kPortIndexInput) {
+ mBufferChannel->setInputBufferArray({});
+ } else {
+ mBufferChannel->setOutputBufferArray({});
+ }
+
status_t err = OK;
for (size_t i = mBuffers[portIndex].size(); i > 0;) {
i--;
@@ -1534,7 +1548,13 @@
status_t ACodec::fillBuffer(BufferInfo *info) {
status_t err;
- if (!storingMetadataInDecodedBuffers()) {
+ // Even in dynamic ANW buffer mode, if the graphic buffer is not changing,
+ // send sPreset instead of the same graphic buffer, so that OMX server
+ // side doesn't update the meta. In theory it should make no difference,
+ // however when the same buffer is parcelled again, a new handle could be
+ // created on server side, and some decoder doesn't recognize the handle
+ // even if it's the same buffer.
+ if (!storingMetadataInDecodedBuffers() || !info->mNewGraphicBuffer) {
err = mOMXNode->fillBuffer(
info->mBufferID, OMXBuffer::sPreset, info->mFenceFd);
} else {
@@ -1542,6 +1562,7 @@
info->mBufferID, info->mGraphicBuffer, info->mFenceFd);
}
+ info->mNewGraphicBuffer = false;
info->mFenceFd = -1;
if (err == OK) {
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
@@ -5072,25 +5093,6 @@
}
}
-void ACodec::addKeyFormatChangesToRenderBufferNotification(sp<AMessage> ¬ify) {
- AString mime;
- CHECK(mOutputFormat->findString("mime", &mime));
-
- if (mime == MEDIA_MIMETYPE_VIDEO_RAW && mNativeWindow != NULL) {
- // notify renderer of the crop change and dataspace change
- // NOTE: native window uses extended right-bottom coordinate
- int32_t left, top, right, bottom;
- if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) {
- notify->setRect("crop", left, top, right + 1, bottom + 1);
- }
-
- int32_t dataSpace;
- if (mOutputFormat->findInt32("android._dataspace", &dataSpace)) {
- notify->setInt32("dataspace", dataSpace);
- }
- }
-}
-
void ACodec::sendFormatChange() {
AString mime;
CHECK(mOutputFormat->findString("mime", &mime));
@@ -5118,8 +5120,6 @@
}
void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatError);
ALOGE("signalError(omxError %#x, internalError %d)", error, internalError);
if (internalError == UNKNOWN_ERROR) { // find better error code
@@ -5132,10 +5132,7 @@
}
mFatalError = true;
-
- notify->setInt32("err", internalError);
- notify->setInt32("actionCode", ACTION_CODE_FATAL); // could translate from OMX error.
- notify->post();
+ mCallback->onError(internalError, ACTION_CODE_FATAL);
}
status_t ACodec::requestIDRFrame() {
@@ -5157,29 +5154,6 @@
////////////////////////////////////////////////////////////////////////////////
-ACodec::PortDescription::PortDescription() {
-}
-
-void ACodec::PortDescription::addBuffer(
- IOMX::buffer_id id, const sp<MediaCodecBuffer> &buffer) {
- mBufferIDs.push_back(id);
- mBuffers.push_back(buffer);
-}
-
-size_t ACodec::PortDescription::countBuffers() {
- return mBufferIDs.size();
-}
-
-IOMX::buffer_id ACodec::PortDescription::bufferIDAt(size_t index) const {
- return mBufferIDs.itemAt(index);
-}
-
-sp<MediaCodecBuffer> ACodec::PortDescription::bufferAt(size_t index) const {
- return mBuffers.itemAt(index);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
: AState(parentState),
mCodec(codec) {
@@ -5262,9 +5236,7 @@
status_t err = mCodec->mOMXNode->freeNode();
ALOGE_IF("[%s] failed to release codec instance: err=%d",
mCodec->mComponentName.c_str(), err);
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatReleaseCompleted);
- notify->post();
+ mCodec->mCallback->onReleaseCompleted();
break;
}
@@ -5488,21 +5460,9 @@
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFillThisBuffer);
- notify->setInt32("buffer-id", info->mBufferID);
-
info->mData->setFormat(mCodec->mInputFormat);
- notify->setObject("buffer", info->mData);
+ mCodec->mBufferChannel->fillThisBuffer(info->mBufferID);
info->mData.clear();
-
- sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec);
- reply->setInt32("buffer-id", info->mBufferID);
-
- notify->setMessage("reply", reply);
-
- notify->post();
-
info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}
@@ -5513,23 +5473,15 @@
int32_t err = OK;
bool eos = false;
PortMode mode = getPortMode(kPortIndexInput);
-
- sp<RefBase> obj;
- if (!msg->findObject("buffer", &obj)) {
- /* these are unfilled buffers returned by client */
- CHECK(msg->findInt32("err", &err));
-
- if (err == OK) {
- /* buffers with no errors are returned on MediaCodec.flush */
- mode = KEEP_BUFFERS;
- } else {
- ALOGV("[%s] saw error %d instead of an input buffer",
- mCodec->mComponentName.c_str(), err);
- eos = true;
- }
- } else {
- buffer = static_cast<MediaCodecBuffer *>(obj.get());
+ int32_t discarded = 0;
+ if (msg->findInt32("discarded", &discarded) && discarded) {
+ // these are unfilled buffers returned by client
+ // buffers are returned on MediaCodec.flush
+ mode = KEEP_BUFFERS;
}
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ buffer = static_cast<MediaCodecBuffer *>(obj.get());
int32_t tmp;
if (buffer != NULL && buffer->meta()->findInt32("eos", &tmp) && tmp) {
@@ -5852,8 +5804,6 @@
break;
}
- sp<AMessage> reply =
- new AMessage(kWhatOutputBufferDrained, mCodec);
sp<MediaCodecBuffer> buffer = info->mData;
if (mCodec->mOutputFormat != mCodec->mLastOutputFormat && rangeLength > 0) {
@@ -5861,12 +5811,7 @@
if (mCodec->mBaseOutputFormat == mCodec->mOutputFormat) {
mCodec->onOutputFormatChanged(mCodec->mOutputFormat);
}
- mCodec->addKeyFormatChangesToRenderBufferNotification(reply);
mCodec->sendFormatChange();
- } else if (rangeLength > 0 && mCodec->mNativeWindow != NULL) {
- // If potentially rendering onto a surface, always save key format data (crop &
- // data space) so that we can set it if and once the buffer is rendered.
- mCodec->addKeyFormatChangesToRenderBufferNotification(reply);
}
buffer->setFormat(mCodec->mOutputFormat);
@@ -5909,29 +5854,16 @@
}
buffer->meta()->setInt64("timeUs", timeUs);
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
- notify->setInt32("buffer-id", info->mBufferID);
- notify->setObject("buffer", buffer);
info->mData.clear();
- notify->setInt32("flags", flags);
- reply->setInt32("buffer-id", info->mBufferID);
-
- notify->setMessage("reply", reply);
-
- notify->post();
+ mCodec->mBufferChannel->drainThisBuffer(info->mBufferID, flags);
info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
if (flags & OMX_BUFFERFLAG_EOS) {
ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str());
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatEOS);
- notify->setInt32("err", mCodec->mInputEOSResult);
- notify->post();
-
+ mCodec->mCallback->onEos(mCodec->mInputEOSResult);
mCodec->mPortEOS[kPortIndexOutput] = true;
}
break;
@@ -5957,10 +5889,11 @@
IOMX::buffer_id bufferID;
CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
sp<RefBase> obj;
- sp<MediaCodecBuffer> buffer = nullptr;
- if (msg->findObject("buffer", &obj)) {
- buffer = static_cast<MediaCodecBuffer *>(obj.get());
- }
+ CHECK(msg->findObject("buffer", &obj));
+ sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
+ int32_t discarded = 0;
+ msg->findInt32("discarded", &discarded);
+
ssize_t index;
BufferInfo *info = mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
BufferInfo::Status status = BufferInfo::getSafeStatus(info);
@@ -5971,31 +5904,34 @@
return;
}
info->mData = buffer;
-
- android_native_rect_t crop;
- if (msg->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)
- && memcmp(&crop, &mCodec->mLastNativeWindowCrop, sizeof(crop)) != 0) {
- mCodec->mLastNativeWindowCrop = crop;
- status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
- ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
- }
-
- int32_t dataSpace;
- if (msg->findInt32("dataspace", &dataSpace)
- && dataSpace != mCodec->mLastNativeWindowDataSpace) {
- status_t err = native_window_set_buffers_data_space(
- mCodec->mNativeWindow.get(), (android_dataspace)dataSpace);
- mCodec->mLastNativeWindowDataSpace = dataSpace;
- ALOGW_IF(err != NO_ERROR, "failed to set dataspace: %d", err);
- }
-
int32_t render;
if (mCodec->mNativeWindow != NULL
&& msg->findInt32("render", &render) && render != 0
- && buffer != NULL && buffer->size() != 0) {
+ && !discarded && buffer->size() != 0) {
ATRACE_NAME("render");
// The client wants this buffer to be rendered.
+ android_native_rect_t crop;
+ if (buffer->format()->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)) {
+ // NOTE: native window uses extended right-bottom coordinate
+ ++crop.right;
+ ++crop.bottom;
+ if (memcmp(&crop, &mCodec->mLastNativeWindowCrop, sizeof(crop)) != 0) {
+ mCodec->mLastNativeWindowCrop = crop;
+ status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
+ ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
+ }
+ }
+
+ int32_t dataSpace;
+ if (buffer->format()->findInt32("android._dataspace", &dataSpace)
+ && dataSpace != mCodec->mLastNativeWindowDataSpace) {
+ status_t err = native_window_set_buffers_data_space(
+ mCodec->mNativeWindow.get(), (android_dataspace)dataSpace);
+ mCodec->mLastNativeWindowDataSpace = dataSpace;
+ ALOGW_IF(err != NO_ERROR, "failed to set dataspace: %d", err);
+ }
+
// save buffers sent to the surface so we can get render time when they return
int64_t mediaTimeUs = -1;
buffer->meta()->findInt64("timeUs", &mediaTimeUs);
@@ -6031,8 +5967,7 @@
info->mIsReadFence = false;
}
} else {
- if (mCodec->mNativeWindow != NULL &&
- (buffer == NULL || buffer->size() != 0)) {
+ if (mCodec->mNativeWindow != NULL && (discarded || buffer->size() != 0)) {
// move read fence into write fence to avoid clobbering
info->mIsReadFence = false;
ATRACE_NAME("frame-drop");
@@ -6150,22 +6085,18 @@
"keepComponentAllocated", &keepComponentAllocated));
ALOGW_IF(keepComponentAllocated,
"cannot keep component allocated on shutdown in Uninitialized state");
-
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", keepComponentAllocated ?
- CodecBase::kWhatStopCompleted : CodecBase::kWhatReleaseCompleted);
- notify->post();
-
+ if (keepComponentAllocated) {
+ mCodec->mCallback->onStopCompleted();
+ } else {
+ mCodec->mCallback->onReleaseCompleted();
+ }
handled = true;
break;
}
case ACodec::kWhatFlush:
{
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFlushCompleted);
- notify->post();
-
+ mCodec->mCallback->onFlushCompleted();
handled = true;
break;
}
@@ -6293,14 +6224,7 @@
omxNode->setQuirks(quirks);
mCodec->mOMX = omx;
mCodec->mOMXNode = omxNode;
-
- {
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatComponentAllocated);
- notify->setString("componentName", mCodec->mComponentName.c_str());
- notify->post();
- }
-
+ mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
mCodec->changeState(mCodec->mLoadedState);
return true;
@@ -6349,10 +6273,11 @@
}
if (mCodec->mExplicitShutdown) {
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", keepComponentAllocated ?
- CodecBase::kWhatStopCompleted : CodecBase::kWhatReleaseCompleted);
- notify->post();
+ if (keepComponentAllocated) {
+ mCodec->mCallback->onStopCompleted();
+ } else {
+ mCodec->mCallback->onReleaseCompleted();
+ }
mCodec->mExplicitShutdown = false;
}
}
@@ -6404,10 +6329,7 @@
case ACodec::kWhatFlush:
{
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFlushCompleted);
- notify->post();
-
+ mCodec->mCallback->onFlushCompleted();
handled = true;
break;
}
@@ -6440,13 +6362,7 @@
return false;
}
- {
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatComponentConfigured);
- notify->setMessage("input-format", mCodec->mInputFormat);
- notify->setMessage("output-format", mCodec->mOutputFormat);
- notify->post();
- }
+ mCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat);
return true;
}
@@ -6568,9 +6484,6 @@
const sp<AMessage> & /* msg */) {
ALOGV("onCreateInputSurface");
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
-
sp<IGraphicBufferProducer> bufferProducer;
status_t err = mCodec->mOMX->createInputSurface(
&bufferProducer, &mCodec->mGraphicBufferSource);
@@ -6580,10 +6493,9 @@
}
if (err == OK) {
- notify->setMessage("input-format", mCodec->mInputFormat);
- notify->setMessage("output-format", mCodec->mOutputFormat);
-
- notify->setObject("input-surface",
+ mCodec->mCallback->onInputSurfaceCreated(
+ mCodec->mInputFormat,
+ mCodec->mOutputFormat,
new BufferProducerWrapper(bufferProducer));
} else {
// Can't use mCodec->signalError() here -- MediaCodec won't forward
@@ -6591,18 +6503,13 @@
// send a kWhatInputSurfaceCreated with an error value instead.
ALOGE("[%s] onCreateInputSurface returning error %d",
mCodec->mComponentName.c_str(), err);
- notify->setInt32("err", err);
+ mCodec->mCallback->onInputSurfaceCreationFailed(err);
}
- notify->post();
}
-void ACodec::LoadedState::onSetInputSurface(
- const sp<AMessage> &msg) {
+void ACodec::LoadedState::onSetInputSurface(const sp<AMessage> &msg) {
ALOGV("onSetInputSurface");
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatInputSurfaceAccepted);
-
sp<RefBase> obj;
CHECK(msg->findObject("input-surface", &obj));
sp<PersistentSurface> surface = static_cast<PersistentSurface *>(obj.get());
@@ -6611,17 +6518,16 @@
status_t err = setupInputSurface();
if (err == OK) {
- notify->setMessage("input-format", mCodec->mInputFormat);
- notify->setMessage("output-format", mCodec->mOutputFormat);
+ mCodec->mCallback->onInputSurfaceAccepted(
+ mCodec->mInputFormat, mCodec->mOutputFormat);
} else {
// Can't use mCodec->signalError() here -- MediaCodec won't forward
// the error through because it's in the "configured" state. We
// send a kWhatInputSurfaceAccepted with an error value instead.
ALOGE("[%s] onSetInputSurface returning error %d",
mCodec->mComponentName.c_str(), err);
- notify->setInt32("err", err);
+ mCodec->mCallback->onInputSurfaceDeclined(err);
}
- notify->post();
}
void ACodec::LoadedState::onStart() {
@@ -6667,12 +6573,18 @@
status_t ACodec::LoadedToIdleState::allocateBuffers() {
status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
-
if (err != OK) {
return err;
}
- return mCodec->allocateBuffersOnPort(kPortIndexOutput);
+ err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
+ if (err != OK) {
+ return err;
+ }
+
+ mCodec->mCallback->onStartCompleted();
+
+ return OK;
}
bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) {
@@ -6699,9 +6611,7 @@
case kWhatFlush:
{
// We haven't even started yet, so we're flushed alright...
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFlushCompleted);
- notify->post();
+ mCodec->mCallback->onFlushCompleted();
return true;
}
@@ -6771,10 +6681,7 @@
case kWhatFlush:
{
// We haven't even started yet, so we're flushed alright...
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFlushCompleted);
- notify->post();
-
+ mCodec->mCallback->onFlushCompleted();
return true;
}
@@ -7149,17 +7056,11 @@
}
void ACodec::onSignalEndOfInputStream() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatSignaledInputEOS);
-
status_t err = INVALID_OPERATION;
if (mGraphicBufferSource != NULL) {
err = statusFromBinderStatus(mGraphicBufferSource->signalEndOfInputStream());
}
- if (err != OK) {
- notify->setInt32("err", err);
- }
- notify->post();
+ mCallback->onSignaledInputEOS(err);
}
bool ACodec::ExecutingState::onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) {
@@ -7291,6 +7192,7 @@
err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
ALOGE_IF(err != OK, "Failed to allocate output port buffers after port "
"reconfiguration: (%d)", err);
+ mCodec->mCallback->onOutputBuffersChanged();
}
if (err != OK) {
@@ -7636,9 +7538,7 @@
mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFlushCompleted);
- notify->post();
+ mCodec->mCallback->onFlushCompleted();
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
new file mode 100644
index 0000000..1db7ab0
--- /dev/null
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ACodecBufferChannel"
+#include <utils/Log.h>
+
+#include <numeric>
+
+#include <binder/MemoryDealer.h>
+#include <media/openmax/OMX_Core.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/MediaCodecBuffer.h>
+#include <system/window.h>
+
+#include "include/ACodecBufferChannel.h"
+#include "include/SecureBuffer.h"
+#include "include/SharedMemoryBuffer.h"
+
+namespace android {
+
+using BufferInfo = ACodecBufferChannel::BufferInfo;
+using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;
+
+static BufferInfoIterator findClientBuffer(
+ const std::shared_ptr<const std::vector<const BufferInfo>> &array,
+ const sp<MediaCodecBuffer> &buffer) {
+ return std::find_if(
+ array->begin(), array->end(),
+ [buffer](const BufferInfo &info) { return info.mClientBuffer == buffer; });
+}
+
+static BufferInfoIterator findBufferId(
+ const std::shared_ptr<const std::vector<const BufferInfo>> &array,
+ IOMX::buffer_id bufferId) {
+ return std::find_if(
+ array->begin(), array->end(),
+ [bufferId](const BufferInfo &info) { return bufferId == info.mBufferId; });
+}
+
+ACodecBufferChannel::BufferInfo::BufferInfo(
+ const sp<MediaCodecBuffer> &buffer,
+ IOMX::buffer_id bufferId,
+ const sp<IMemory> &sharedEncryptedBuffer)
+ : mClientBuffer(
+ (sharedEncryptedBuffer == nullptr)
+ ? buffer
+ : new SharedMemoryBuffer(buffer->format(), sharedEncryptedBuffer)),
+ mCodecBuffer(buffer),
+ mBufferId(bufferId),
+ mSharedEncryptedBuffer(sharedEncryptedBuffer) {
+}
+
+ACodecBufferChannel::ACodecBufferChannel(
+ const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained)
+ : mInputBufferFilled(inputBufferFilled),
+ mOutputBufferDrained(outputBufferDrained) {
+}
+
+status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mInputBuffers));
+ BufferInfoIterator it = findClientBuffer(array, buffer);
+ if (it == array->end()) {
+ return -ENOENT;
+ }
+ ALOGV("queueInputBuffer #%d", it->mBufferId);
+ sp<AMessage> msg = mInputBufferFilled->dup();
+ msg->setObject("buffer", it->mCodecBuffer);
+ msg->setInt32("buffer-id", it->mBufferId);
+ msg->post();
+ return OK;
+}
+
+status_t ACodecBufferChannel::queueSecureInputBuffer(
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ AString *errorDetailMsg) {
+ if (mCrypto == nullptr) {
+ return -ENOSYS;
+ }
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mInputBuffers));
+ BufferInfoIterator it = findClientBuffer(array, buffer);
+ if (it == array->end()) {
+ return -ENOENT;
+ }
+
+ void *dst_pointer = nullptr;
+ ICrypto::DestinationType dst_type = ICrypto::kDestinationTypeOpaqueHandle;
+
+ if (secure) {
+ sp<SecureBuffer> secureData = static_cast<SecureBuffer *>(it->mCodecBuffer.get());
+ dst_pointer = secureData->getDestinationPointer();
+ dst_type = secureData->getDestinationType();
+ } else {
+ dst_pointer = it->mCodecBuffer->base();
+ dst_type = ICrypto::kDestinationTypeVmPointer;
+ }
+
+ ssize_t result = mCrypto->decrypt(
+ dst_type,
+ key,
+ iv,
+ mode,
+ pattern,
+ it->mSharedEncryptedBuffer,
+ it->mClientBuffer->offset(),
+ subSamples,
+ numSubSamples,
+ dst_pointer,
+ errorDetailMsg);
+
+ if (result < 0) {
+ return result;
+ }
+
+ it->mCodecBuffer->setRange(0, result);
+
+ // Copy metadata from client to codec buffer.
+ it->mCodecBuffer->meta()->clear();
+ int64_t timeUs;
+ CHECK(it->mClientBuffer->meta()->findInt64("timeUs", &timeUs));
+ it->mCodecBuffer->meta()->setInt64("timeUs", timeUs);
+ int32_t eos;
+ if (it->mClientBuffer->meta()->findInt32("eos", &eos)) {
+ it->mCodecBuffer->meta()->setInt32("eos", eos);
+ }
+ int32_t csd;
+ if (it->mClientBuffer->meta()->findInt32("csd", &csd)) {
+ it->mCodecBuffer->meta()->setInt32("csd", csd);
+ }
+
+ ALOGV("queueSecureInputBuffer #%d", it->mBufferId);
+ sp<AMessage> msg = mInputBufferFilled->dup();
+ msg->setObject("buffer", it->mCodecBuffer);
+ msg->setInt32("buffer-id", it->mBufferId);
+ msg->post();
+ return OK;
+}
+
+status_t ACodecBufferChannel::renderOutputBuffer(
+ const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mOutputBuffers));
+ BufferInfoIterator it = findClientBuffer(array, buffer);
+ if (it == array->end()) {
+ return -ENOENT;
+ }
+
+ ALOGV("renderOutputBuffer #%d", it->mBufferId);
+ sp<AMessage> msg = mOutputBufferDrained->dup();
+ msg->setObject("buffer", buffer);
+ msg->setInt32("buffer-id", it->mBufferId);
+ msg->setInt32("render", true);
+ msg->setInt64("timestampNs", timestampNs);
+ msg->post();
+ return OK;
+}
+
+status_t ACodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mInputBuffers));
+ bool input = true;
+ BufferInfoIterator it = findClientBuffer(array, buffer);
+ if (it == array->end()) {
+ array = std::atomic_load(&mOutputBuffers);
+ input = false;
+ it = findClientBuffer(array, buffer);
+ if (it == array->end()) {
+ return -ENOENT;
+ }
+ }
+ ALOGV("discardBuffer #%d", it->mBufferId);
+ sp<AMessage> msg = input ? mInputBufferFilled->dup() : mOutputBufferDrained->dup();
+ msg->setObject("buffer", it->mCodecBuffer);
+ msg->setInt32("buffer-id", it->mBufferId);
+ msg->setInt32("discarded", true);
+ msg->post();
+ return OK;
+}
+
+void ACodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
+ std::shared_ptr<const std::vector<const BufferInfo>> inputBuffers(
+ std::atomic_load(&mInputBuffers));
+ array->clear();
+ for (const BufferInfo &elem : *inputBuffers) {
+ array->push_back(elem.mClientBuffer);
+ }
+}
+
+void ACodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
+ std::shared_ptr<const std::vector<const BufferInfo>> outputBuffers(
+ std::atomic_load(&mOutputBuffers));
+ array->clear();
+ for (const BufferInfo &elem : *outputBuffers) {
+ array->push_back(elem.mClientBuffer);
+ }
+}
+
+void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
+ bool secure = (mCrypto != nullptr);
+ if (secure) {
+ size_t totalSize = std::accumulate(
+ array.begin(), array.end(), 0u,
+ [alignment = MemoryDealer::getAllocationAlignment()]
+ (size_t sum, const BufferAndId& elem) {
+ return sum + align(elem.mBuffer->capacity(), alignment);
+ });
+ mDealer = new MemoryDealer(totalSize, "ACodecBufferChannel");
+ }
+ std::vector<const BufferInfo> inputBuffers;
+ for (const BufferAndId &elem : array) {
+ sp<IMemory> sharedEncryptedBuffer;
+ if (secure) {
+ sharedEncryptedBuffer = mDealer->allocate(elem.mBuffer->capacity());
+ }
+ inputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, sharedEncryptedBuffer);
+ }
+ std::atomic_store(
+ &mInputBuffers,
+ std::make_shared<const std::vector<const BufferInfo>>(inputBuffers));
+}
+
+void ACodecBufferChannel::setOutputBufferArray(const std::vector<BufferAndId> &array) {
+ std::vector<const BufferInfo> outputBuffers;
+ for (const BufferAndId &elem : array) {
+ outputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, nullptr);
+ }
+ std::atomic_store(
+ &mOutputBuffers,
+ std::make_shared<const std::vector<const BufferInfo>>(outputBuffers));
+}
+
+void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {
+ ALOGV("fillThisBuffer #%d", bufferId);
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mInputBuffers));
+ BufferInfoIterator it = findBufferId(array, bufferId);
+
+ if (it == array->end()) {
+ ALOGE("fillThisBuffer: unrecognized buffer #%d", bufferId);
+ return;
+ }
+ if (it->mClientBuffer != it->mCodecBuffer) {
+ it->mClientBuffer->setFormat(it->mCodecBuffer->format());
+ }
+
+ mCallback->onInputBufferAvailable(
+ std::distance(array->begin(), it),
+ it->mClientBuffer);
+}
+
+void ACodecBufferChannel::drainThisBuffer(
+ IOMX::buffer_id bufferId,
+ OMX_U32 omxFlags) {
+ ALOGV("drainThisBuffer #%d", bufferId);
+ std::shared_ptr<const std::vector<const BufferInfo>> array(
+ std::atomic_load(&mOutputBuffers));
+ BufferInfoIterator it = findBufferId(array, bufferId);
+
+ if (it == array->end()) {
+ ALOGE("drainThisBuffer: unrecognized buffer #%d", bufferId);
+ return;
+ }
+ if (it->mClientBuffer != it->mCodecBuffer) {
+ it->mClientBuffer->setFormat(it->mCodecBuffer->format());
+ }
+
+ uint32_t flags = 0;
+ if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+ flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_EOS) {
+ flags |= MediaCodec::BUFFER_FLAG_EOS;
+ }
+ it->mClientBuffer->meta()->setInt32("flags", flags);
+
+ mCallback->onOutputBufferAvailable(
+ std::distance(array->begin(), it),
+ it->mClientBuffer);
+}
+
+} // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 5e921e3..bd837ae 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -4,6 +4,7 @@
LOCAL_SRC_FILES:= \
ACodec.cpp \
+ ACodecBufferChannel.cpp \
AACExtractor.cpp \
AACWriter.cpp \
AMRExtractor.cpp \
@@ -14,7 +15,6 @@
CallbackDataSource.cpp \
CameraSource.cpp \
CameraSourceTimeLapse.cpp \
- CodecBase.cpp \
DataConverter.cpp \
DataSource.cpp \
DataURISource.cpp \
@@ -59,7 +59,6 @@
VBRISeeker.cpp \
VideoFrameScheduler.cpp \
WAVExtractor.cpp \
- WVMExtractor.cpp \
XINGSeeker.cpp \
avc_utils.cpp \
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 990d4b7..0fe44eb 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -770,9 +770,7 @@
return mInitCheck;
}
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.record-stats", value, NULL)
- && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ if (property_get_bool("media.stagefright.record-stats", false)) {
mCollectStats = true;
}
diff --git a/media/libstagefright/CodecBase.cpp b/media/libstagefright/CodecBase.cpp
deleted file mode 100644
index f729d4d..0000000
--- a/media/libstagefright/CodecBase.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "CodecBase"
-
-#include <inttypes.h>
-
-#include <media/stagefright/CodecBase.h>
-
-namespace android {
-
-CodecBase::CodecBase() {
-}
-
-CodecBase::~CodecBase() {
-}
-
-CodecBase::PortDescription::PortDescription() {
-}
-
-CodecBase::PortDescription::~PortDescription() {
-}
-
-} // namespace android
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 163a527..4a965ba 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -16,24 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DataSource"
-#include "include/AMRExtractor.h"
-
-#include "include/AACExtractor.h"
#include "include/CallbackDataSource.h"
-#include "include/DRMExtractor.h"
-#include "include/FLACExtractor.h"
#include "include/HTTPBase.h"
-#include "include/MidiExtractor.h"
-#include "include/MP3Extractor.h"
-#include "include/MPEG2PSExtractor.h"
-#include "include/MPEG2TSExtractor.h"
-#include "include/MPEG4Extractor.h"
#include "include/NuCachedSource2.h"
-#include "include/OggExtractor.h"
-#include "include/WAVExtractor.h"
-#include "include/WVMExtractor.h"
-
-#include "matroska/MatroskaExtractor.h"
#include <media/IMediaHTTPConnection.h>
#include <media/IMediaHTTPService.h>
@@ -44,12 +29,15 @@
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/Utils.h>
#include <utils/String8.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h>
+#include <arpa/inet.h>
+
namespace android {
bool DataSource::getUInt16(off64_t offset, uint16_t *x) {
@@ -112,83 +100,6 @@
////////////////////////////////////////////////////////////////////////////////
-Mutex DataSource::gSnifferMutex;
-List<DataSource::SnifferFunc> DataSource::gSniffers;
-bool DataSource::gSniffersRegistered = false;
-
-bool DataSource::sniff(
- String8 *mimeType, float *confidence, sp<AMessage> *meta) {
- *mimeType = "";
- *confidence = 0.0f;
- meta->clear();
-
- {
- Mutex::Autolock autoLock(gSnifferMutex);
- if (!gSniffersRegistered) {
- return false;
- }
- }
-
- for (List<SnifferFunc>::iterator it = gSniffers.begin();
- it != gSniffers.end(); ++it) {
- String8 newMimeType;
- float newConfidence;
- sp<AMessage> newMeta;
- if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
- if (newConfidence > *confidence) {
- *mimeType = newMimeType;
- *confidence = newConfidence;
- *meta = newMeta;
- }
- }
- }
-
- return *confidence > 0.0;
-}
-
-// static
-void DataSource::RegisterSniffer_l(SnifferFunc func) {
- for (List<SnifferFunc>::iterator it = gSniffers.begin();
- it != gSniffers.end(); ++it) {
- if (*it == func) {
- return;
- }
- }
-
- gSniffers.push_back(func);
-}
-
-// static
-void DataSource::RegisterDefaultSniffers() {
- Mutex::Autolock autoLock(gSnifferMutex);
- if (gSniffersRegistered) {
- return;
- }
-
- RegisterSniffer_l(SniffMPEG4);
- RegisterSniffer_l(SniffMatroska);
- RegisterSniffer_l(SniffOgg);
- RegisterSniffer_l(SniffWAV);
- RegisterSniffer_l(SniffFLAC);
- RegisterSniffer_l(SniffAMR);
- RegisterSniffer_l(SniffMPEG2TS);
- RegisterSniffer_l(SniffMP3);
- RegisterSniffer_l(SniffAAC);
- RegisterSniffer_l(SniffMPEG2PS);
- if (getuid() == AID_MEDIA) {
- // WVM only in the media server process
- RegisterSniffer_l(SniffWVM);
- }
- RegisterSniffer_l(SniffMidi);
-
- char value[PROPERTY_VALUE_MAX];
- if (property_get("drm.service.enabled", value, NULL)
- && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
- RegisterSniffer_l(SniffDRM);
- }
- gSniffersRegistered = true;
-}
-
// static
sp<DataSource> DataSource::CreateFromURI(
const sp<IMediaHTTPService> &httpService,
@@ -200,14 +111,10 @@
*contentType = "";
}
- bool isWidevine = !strncasecmp("widevine://", uri, 11);
-
sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = new FileSource(uri + 7);
- } else if (!strncasecmp("http://", uri, 7)
- || !strncasecmp("https://", uri, 8)
- || isWidevine) {
+ } else if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {
if (httpService == NULL) {
ALOGE("Invalid http service!");
return NULL;
@@ -222,14 +129,6 @@
httpSource = new MediaHTTP(conn);
}
- String8 tmp;
- if (isWidevine) {
- tmp = String8("http://");
- tmp.append(uri + 11);
-
- uri = tmp.string();
- }
-
String8 cacheConfig;
bool disconnectAtHighwatermark;
KeyedVector<String8, String8> nonCacheSpecificHeaders;
@@ -246,20 +145,14 @@
return NULL;
}
- if (!isWidevine) {
- if (contentType != NULL) {
- *contentType = httpSource->getMIMEType();
- }
-
- source = NuCachedSource2::Create(
- httpSource,
- cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
- disconnectAtHighwatermark);
- } else {
- // We do not want that prefetching, caching, datasource wrapper
- // in the widevine:// case.
- source = httpSource;
+ if (contentType != NULL) {
+ *contentType = httpSource->getMIMEType();
}
+
+ source = NuCachedSource2::Create(
+ httpSource,
+ cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
+ disconnectAtHighwatermark);
} else if (!strncasecmp("data:", uri, 5)) {
source = DataURISource::Create(uri);
} else {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index ee603a4..f9af5e1 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1022,7 +1022,9 @@
while (cur && cur->next != mLastTrack) {
cur = cur->next;
}
- cur->next = NULL;
+ if (cur) {
+ cur->next = NULL;
+ }
delete mLastTrack;
mLastTrack = cur;
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 9978b76..e57057c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1128,9 +1128,7 @@
// Test mode is enabled only if rw.media.record.test system
// property is enabled.
- char value[PROPERTY_VALUE_MAX];
- if (property_get("rw.media.record.test", value, NULL) &&
- (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
+ if (property_get_bool("rw.media.record.test", false)) {
return true;
}
return false;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7761fb1..24f0d20 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -37,6 +37,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BufferProducerWrapper.h>
@@ -66,6 +67,9 @@
static const int kMaxRetry = 2;
static const int kMaxReclaimWaitTimeInUs = 500000; // 0.5s
+static const int kNumBuffersAlign = 16;
+
+////////////////////////////////////////////////////////////////////////////////
struct ResourceManagerClient : public BnResourceManagerClient {
explicit ResourceManagerClient(MediaCodec* codec) : mMediaCodec(codec) {}
@@ -171,6 +175,216 @@
return mService->reclaimResource(mPid, resources);
}
+////////////////////////////////////////////////////////////////////////////////
+
+MediaCodec::BufferInfo::BufferInfo() : mOwnedByClient(false) {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+enum {
+ kWhatFillThisBuffer = 'fill',
+ kWhatDrainThisBuffer = 'drai',
+ kWhatEOS = 'eos ',
+ kWhatStartCompleted = 'Scom',
+ kWhatStopCompleted = 'scom',
+ kWhatReleaseCompleted = 'rcom',
+ kWhatFlushCompleted = 'fcom',
+ kWhatError = 'erro',
+ kWhatComponentAllocated = 'cAll',
+ kWhatComponentConfigured = 'cCon',
+ kWhatInputSurfaceCreated = 'isfc',
+ kWhatInputSurfaceAccepted = 'isfa',
+ kWhatSignaledInputEOS = 'seos',
+ kWhatOutputFramesRendered = 'outR',
+ kWhatOutputBuffersChanged = 'outC',
+};
+
+class BufferCallback : public CodecBase::BufferCallback {
+public:
+ explicit BufferCallback(const sp<AMessage> ¬ify);
+ virtual ~BufferCallback() = default;
+
+ virtual void onInputBufferAvailable(
+ size_t index, const sp<MediaCodecBuffer> &buffer) override;
+ virtual void onOutputBufferAvailable(
+ size_t index, const sp<MediaCodecBuffer> &buffer) override;
+private:
+ const sp<AMessage> mNotify;
+};
+
+BufferCallback::BufferCallback(const sp<AMessage> ¬ify)
+ : mNotify(notify) {}
+
+void BufferCallback::onInputBufferAvailable(
+ size_t index, const sp<MediaCodecBuffer> &buffer) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatFillThisBuffer);
+ notify->setSize("index", index);
+ notify->setObject("buffer", buffer);
+ notify->post();
+}
+
+void BufferCallback::onOutputBufferAvailable(
+ size_t index, const sp<MediaCodecBuffer> &buffer) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatDrainThisBuffer);
+ notify->setSize("index", index);
+ notify->setObject("buffer", buffer);
+ notify->post();
+}
+
+class CodecCallback : public CodecBase::CodecCallback {
+public:
+ explicit CodecCallback(const sp<AMessage> ¬ify);
+ virtual ~CodecCallback() = default;
+
+ virtual void onEos(status_t err) override;
+ virtual void onStartCompleted() override;
+ virtual void onStopCompleted() override;
+ virtual void onReleaseCompleted() override;
+ virtual void onFlushCompleted() override;
+ virtual void onError(status_t err, enum ActionCode actionCode) override;
+ virtual void onComponentAllocated(const char *componentName) override;
+ virtual void onComponentConfigured(
+ const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) override;
+ virtual void onInputSurfaceCreated(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat,
+ const sp<BufferProducerWrapper> &inputSurface) override;
+ virtual void onInputSurfaceCreationFailed(status_t err) override;
+ virtual void onInputSurfaceAccepted(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat) override;
+ virtual void onInputSurfaceDeclined(status_t err) override;
+ virtual void onSignaledInputEOS(status_t err) override;
+ virtual void onOutputFramesRendered(const std::list<FrameRenderTracker::Info> &done) override;
+ virtual void onOutputBuffersChanged() override;
+private:
+ const sp<AMessage> mNotify;
+};
+
+CodecCallback::CodecCallback(const sp<AMessage> ¬ify) : mNotify(notify) {}
+
+void CodecCallback::onEos(status_t err) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatEOS);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void CodecCallback::onStartCompleted() {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatStartCompleted);
+ notify->post();
+}
+
+void CodecCallback::onStopCompleted() {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatStopCompleted);
+ notify->post();
+}
+
+void CodecCallback::onReleaseCompleted() {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatReleaseCompleted);
+ notify->post();
+}
+
+void CodecCallback::onFlushCompleted() {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
+}
+
+void CodecCallback::onError(status_t err, enum ActionCode actionCode) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->setInt32("actionCode", actionCode);
+ notify->post();
+}
+
+void CodecCallback::onComponentAllocated(const char *componentName) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatComponentAllocated);
+ notify->setString("componentName", componentName);
+ notify->post();
+}
+
+void CodecCallback::onComponentConfigured(
+ const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatComponentConfigured);
+ notify->setMessage("input-format", inputFormat);
+ notify->setMessage("output-format", outputFormat);
+ notify->post();
+}
+
+void CodecCallback::onInputSurfaceCreated(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat,
+ const sp<BufferProducerWrapper> &inputSurface) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatInputSurfaceCreated);
+ notify->setMessage("input-format", inputFormat);
+ notify->setMessage("output-format", outputFormat);
+ notify->setObject("input-surface", inputSurface);
+ notify->post();
+}
+
+void CodecCallback::onInputSurfaceCreationFailed(status_t err) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatInputSurfaceCreated);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void CodecCallback::onInputSurfaceAccepted(
+ const sp<AMessage> &inputFormat,
+ const sp<AMessage> &outputFormat) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatInputSurfaceAccepted);
+ notify->setMessage("input-format", inputFormat);
+ notify->setMessage("output-format", outputFormat);
+ notify->post();
+}
+
+void CodecCallback::onInputSurfaceDeclined(status_t err) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatInputSurfaceAccepted);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void CodecCallback::onSignaledInputEOS(status_t err) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatSignaledInputEOS);
+ if (err != OK) {
+ notify->setInt32("err", err);
+ }
+ notify->post();
+}
+
+void CodecCallback::onOutputFramesRendered(const std::list<FrameRenderTracker::Info> &done) {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatOutputFramesRendered);
+ if (MediaCodec::CreateFramesRenderedMessage(done, notify)) {
+ notify->post();
+ }
+}
+
+void CodecCallback::onOutputBuffersChanged() {
+ sp<AMessage> notify(mNotify->dup());
+ notify->setInt32("what", kWhatOutputBuffersChanged);
+ notify->post();
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
// static
sp<MediaCodec> MediaCodec::CreateByType(
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
@@ -370,7 +584,13 @@
mLooper->registerHandler(this);
- mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, this));
+ mCodec->setCallback(
+ std::unique_ptr<CodecBase::CodecCallback>(
+ new CodecCallback(new AMessage(kWhatCodecNotify, this))));
+ mBufferChannel = mCodec->getBufferChannel();
+ mBufferChannel->setCallback(
+ std::unique_ptr<CodecBase::BufferCallback>(
+ new BufferCallback(new AMessage(kWhatCodecNotify, this))));
sp<AMessage> msg = new AMessage(kWhatInit, this);
msg->setString("name", name);
@@ -597,14 +817,9 @@
}
bool MediaCodec::hasPendingBuffer(int portIndex) {
- const Vector<BufferInfo> &buffers = mPortBuffers[portIndex];
- for (size_t i = 0; i < buffers.size(); ++i) {
- const BufferInfo &info = buffers.itemAt(i);
- if (info.mOwnedByClient) {
- return true;
- }
- }
- return false;
+ return std::any_of(
+ mPortBuffers[portIndex].begin(), mPortBuffers[portIndex].end(),
+ [](const BufferInfo &info) { return info.mOwnedByClient; });
}
bool MediaCodec::hasPendingBuffer() {
@@ -843,16 +1058,6 @@
return OK;
}
-status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const {
- sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
- msg->setInt32("portIndex", kPortIndexInput);
- msg->setPointer("buffers", buffers);
- msg->setInt32("widevine", true);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
-}
-
status_t MediaCodec::getInputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const {
sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
msg->setInt32("portIndex", kPortIndexInput);
@@ -921,14 +1126,14 @@
// we also don't want mOwnedByClient to change during this
Mutex::Autolock al(mBufferLock);
- Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
- if (index >= buffers->size()) {
+ std::vector<BufferInfo> &buffers = mPortBuffers[portIndex];
+ if (index >= buffers.size()) {
ALOGE("getBufferAndFormat - trying to get buffer with "
- "bad index (index=%zu buffer_size=%zu)", index, buffers->size());
+ "bad index (index=%zu buffer_size=%zu)", index, buffers.size());
return INVALID_OPERATION;
}
- const BufferInfo &info = buffers->itemAt(index);
+ const BufferInfo &info = buffers[index];
if (!info.mOwnedByClient) {
ALOGE("getBufferAndFormat - invalid operation "
"(the index %zu is not owned by client)", index);
@@ -1026,7 +1231,7 @@
}
const sp<MediaCodecBuffer> &buffer =
- mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+ mPortBuffers[kPortIndexOutput][index].mData;
response->setSize("index", index);
response->setSize("offset", buffer->offset());
@@ -1037,19 +1242,8 @@
response->setInt64("timeUs", timeUs);
- int32_t omxFlags;
- CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
-
- uint32_t flags = 0;
- if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
- flags |= BUFFER_FLAG_SYNCFRAME;
- }
- if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
- flags |= BUFFER_FLAG_CODECCONFIG;
- }
- if (omxFlags & OMX_BUFFERFLAG_EOS) {
- flags |= BUFFER_FLAG_EOS;
- }
+ int32_t flags;
+ CHECK(buffer->meta()->findInt32("flags", &flags));
response->setInt32("flags", flags);
response->postReply(replyID);
@@ -1066,7 +1260,7 @@
CHECK(msg->findInt32("what", &what));
switch (what) {
- case CodecBase::kWhatError:
+ case kWhatError:
{
int32_t err, actionCode;
CHECK(msg->findInt32("err", &err));
@@ -1201,7 +1395,7 @@
break;
}
- case CodecBase::kWhatComponentAllocated:
+ case kWhatComponentAllocated:
{
CHECK_EQ(mState, INITIALIZING);
setState(INITIALIZED);
@@ -1233,7 +1427,7 @@
break;
}
- case CodecBase::kWhatComponentConfigured:
+ case kWhatComponentConfigured:
{
if (mState == UNINITIALIZED || mState == INITIALIZED) {
// In case a kWhatError message came in and replied with error,
@@ -1262,7 +1456,7 @@
break;
}
- case CodecBase::kWhatInputSurfaceCreated:
+ case kWhatInputSurfaceCreated:
{
// response to initiateCreateInputSurface()
status_t err = NO_ERROR;
@@ -1286,7 +1480,7 @@
break;
}
- case CodecBase::kWhatInputSurfaceAccepted:
+ case kWhatInputSurfaceAccepted:
{
// response to initiateSetInputSurface()
status_t err = NO_ERROR;
@@ -1302,7 +1496,7 @@
break;
}
- case CodecBase::kWhatSignaledInputEOS:
+ case kWhatSignaledInputEOS:
{
// response to signalEndOfInputStream()
sp<AMessage> response = new AMessage;
@@ -1314,79 +1508,28 @@
break;
}
-
- case CodecBase::kWhatBuffersAllocated:
+ case kWhatStartCompleted:
{
- Mutex::Autolock al(mBufferLock);
- int32_t portIndex;
- CHECK(msg->findInt32("portIndex", &portIndex));
-
- ALOGV("%s buffers allocated",
- portIndex == kPortIndexInput ? "input" : "output");
-
- CHECK(portIndex == kPortIndexInput
- || portIndex == kPortIndexOutput);
-
- mPortBuffers[portIndex].clear();
- mPortBufferArrays[portIndex].clear();
-
- Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
-
- sp<RefBase> obj;
- CHECK(msg->findObject("portDesc", &obj));
-
- sp<CodecBase::PortDescription> portDesc =
- static_cast<CodecBase::PortDescription *>(obj.get());
-
- size_t numBuffers = portDesc->countBuffers();
-
- size_t totalSize = 0;
- for (size_t i = 0; i < numBuffers; ++i) {
- if (portIndex == kPortIndexInput && mCrypto != NULL) {
- totalSize += portDesc->bufferAt(i)->capacity();
- }
+ CHECK_EQ(mState, STARTING);
+ if (mIsVideo) {
+ addResource(
+ MediaResource::kGraphicMemory,
+ MediaResource::kUnspecifiedSubType,
+ getGraphicBufferSize());
}
-
- if (totalSize) {
- mDealer = new MemoryDealer(totalSize, "MediaCodec");
- }
-
- for (size_t i = 0; i < numBuffers; ++i) {
- BufferInfo info;
- info.mBufferID = portDesc->bufferIDAt(i);
- info.mOwnedByClient = false;
- sp<MediaCodecBuffer> buffer = portDesc->bufferAt(i);
- if (portIndex == kPortIndexInput && mCrypto != NULL) {
- info.mSharedEncryptedBuffer = mDealer->allocate(buffer->capacity());
- buffer = new SharedMemoryBuffer(
- mInputFormat, info.mSharedEncryptedBuffer);
- }
- buffers->push_back(info);
- mPortBufferArrays[portIndex].push_back(buffer);
- }
-
- if (portIndex == kPortIndexOutput) {
- if (mState == STARTING) {
- // We're always allocating output buffers after
- // allocating input buffers, so this is a good
- // indication that now all buffers are allocated.
- if (mIsVideo) {
- addResource(
- MediaResource::kGraphicMemory,
- MediaResource::kUnspecifiedSubType,
- getGraphicBufferSize());
- }
- setState(STARTED);
- (new AMessage)->postReply(mReplyID);
- } else {
- mFlags |= kFlagOutputBuffersChanged;
- postActivityNotificationIfPossible();
- }
- }
+ setState(STARTED);
+ (new AMessage)->postReply(mReplyID);
break;
}
- case CodecBase::kWhatOutputFramesRendered:
+ case kWhatOutputBuffersChanged:
+ {
+ mFlags |= kFlagOutputBuffersChanged;
+ postActivityNotificationIfPossible();
+ break;
+ }
+
+ case kWhatOutputFramesRendered:
{
// ignore these in all states except running, and check that we have a
// notification set
@@ -1398,7 +1541,7 @@
break;
}
- case CodecBase::kWhatFillThisBuffer:
+ case kWhatFillThisBuffer:
{
/* size_t index = */updateBuffers(kPortIndexInput, msg);
@@ -1409,9 +1552,6 @@
break;
}
- // TODO: hold reference of buffer from downstream when
- // mPortBuffers is removed.
-
if (!mCSD.empty()) {
ssize_t index = dequeuePortBuffer(kPortIndexInput);
CHECK_GE(index, 0);
@@ -1456,7 +1596,7 @@
break;
}
- case CodecBase::kWhatDrainThisBuffer:
+ case kWhatDrainThisBuffer:
{
/* size_t index = */updateBuffers(kPortIndexOutput, msg);
@@ -1470,12 +1610,7 @@
sp<RefBase> obj;
CHECK(msg->findObject("buffer", &obj));
sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
- // TODO: hold buffer's reference when we remove mPortBuffers
- int32_t omxFlags;
- CHECK(msg->findInt32("flags", &omxFlags));
-
- buffer->meta()->setInt32("omxFlags", omxFlags);
if (mOutputFormat != buffer->format()) {
mOutputFormat = buffer->format();
ALOGV("[%s] output format changed to: %s",
@@ -1508,7 +1643,9 @@
// Before we announce the format change we should
// collect codec specific data and amend the output
// format as necessary.
- if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ int32_t flags = 0;
+ (void) buffer->meta()->findInt32("flags", &flags);
+ if (flags & BUFFER_FLAG_CODECCONFIG) {
status_t err =
amendOutputFormatWithCodecSpecificData(buffer);
@@ -1553,14 +1690,14 @@
break;
}
- case CodecBase::kWhatEOS:
+ case kWhatEOS:
{
// We already notify the client of this by using the
// corresponding flag in "onOutputBufferReady".
break;
}
- case CodecBase::kWhatStopCompleted:
+ case kWhatStopCompleted:
{
if (mState != STOPPING) {
ALOGW("Received kWhatStopCompleted in state %d", mState);
@@ -1571,7 +1708,7 @@
break;
}
- case CodecBase::kWhatReleaseCompleted:
+ case kWhatReleaseCompleted:
{
if (mState != RELEASING) {
ALOGW("Received kWhatReleaseCompleted in state %d", mState);
@@ -1588,7 +1725,7 @@
break;
}
- case CodecBase::kWhatFlushCompleted:
+ case kWhatFlushCompleted:
{
if (mState != FLUSHING) {
ALOGW("received FlushCompleted message in state %d",
@@ -1731,6 +1868,7 @@
}
mCrypto = static_cast<ICrypto *>(crypto);
+ mBufferChannel->setCrypto(mCrypto);
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -2139,12 +2277,7 @@
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- // Unfortunately widevine legacy source requires knowing all of the
- // codec input buffers, so we have to provide them even in async mode.
- int32_t widevine = 0;
- msg->findInt32("widevine", &widevine);
-
- if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) {
+ if (!isExecuting() || (mFlags & kFlagIsAsync)) {
PostReplyWithError(replyID, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
@@ -2163,10 +2296,10 @@
// createInputSurface(), or persistent set by setInputSurface()),
// give the client an empty input buffers array.
if (portIndex != kPortIndexInput || !mHaveInputSurface) {
- const Vector<sp<MediaCodecBuffer>> &srcBuffers = mPortBufferArrays[portIndex];
-
- for (size_t i = 0; i < srcBuffers.size(); ++i) {
- dstBuffers->push_back(srcBuffers[i]);
+ if (portIndex == kPortIndexInput) {
+ mBufferChannel->getInputBufferArray(dstBuffers);
+ } else {
+ mBufferChannel->getOutputBufferArray(dstBuffers);
}
}
@@ -2295,13 +2428,12 @@
status_t MediaCodec::queueCSDInputBuffer(size_t bufferIndex) {
CHECK(!mCSD.empty());
- const BufferInfo *info =
- &mPortBuffers[kPortIndexInput].itemAt(bufferIndex);
+ const BufferInfo &info = mPortBuffers[kPortIndexInput][bufferIndex];
sp<ABuffer> csd = *mCSD.begin();
mCSD.erase(mCSD.begin());
- const sp<MediaCodecBuffer> &codecInputData = info->mData;
+ const sp<MediaCodecBuffer> &codecInputData = info.mData;
if (csd->size() > codecInputData->capacity()) {
return -EINVAL;
@@ -2369,28 +2501,19 @@
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
Mutex::Autolock al(mBufferLock);
- Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+ for (size_t i = 0; i < mPortBuffers[portIndex].size(); ++i) {
+ BufferInfo *info = &mPortBuffers[portIndex][i];
- for (size_t i = 0; i < buffers->size(); ++i) {
- BufferInfo *info = &buffers->editItemAt(i);
-
- if (info->mNotify != NULL) {
- sp<AMessage> msg = info->mNotify;
- info->mNotify = NULL;
+ if (info->mData != nullptr) {
+ sp<MediaCodecBuffer> buffer = info->mData;
if (isReclaim && info->mOwnedByClient) {
ALOGD("port %d buffer %zu still owned by client when codec is reclaimed",
portIndex, i);
} else {
info->mOwnedByClient = false;
info->mData.clear();
- info->mSecureData.clear();
}
-
- if (portIndex == kPortIndexInput) {
- /* no error, just returning buffers */
- msg->setInt32("err", OK);
- }
- msg->post();
+ mBufferChannel->discardBuffer(buffer);
}
}
@@ -2400,37 +2523,22 @@
size_t MediaCodec::updateBuffers(
int32_t portIndex, const sp<AMessage> &msg) {
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
-
- uint32_t bufferID;
- CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
+ size_t index;
+ CHECK(msg->findSize("index", &index));
sp<RefBase> obj;
CHECK(msg->findObject("buffer", &obj));
sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
- Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
-
- for (size_t i = 0; i < buffers->size(); ++i) {
- BufferInfo *info = &buffers->editItemAt(i);
-
- if (info->mBufferID == bufferID) {
- CHECK(info->mNotify == NULL);
- CHECK(msg->findMessage("reply", &info->mNotify));
-
- if (portIndex == kPortIndexInput && mCrypto != NULL) {
- info->mSecureData = buffer;
- info->mData = mPortBufferArrays[portIndex][i];
- } else {
- info->mData = buffer;
- }
- mAvailPortBuffers[portIndex].push_back(i);
-
- return i;
+ {
+ Mutex::Autolock al(mBufferLock);
+ if (mPortBuffers[portIndex].size() <= index) {
+ mPortBuffers[portIndex].resize(align(index + 1, kNumBuffersAlign));
}
+ mPortBuffers[portIndex][index].mData = buffer;
}
+ mAvailPortBuffers[portIndex].push_back(index);
- TRESPASS();
-
- return 0;
+ return index;
}
status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
@@ -2495,9 +2603,9 @@
return -ERANGE;
}
- BufferInfo *info = &mPortBuffers[kPortIndexInput].editItemAt(index);
+ BufferInfo *info = &mPortBuffers[kPortIndexInput][index];
- if (info->mNotify == NULL || !info->mOwnedByClient) {
+ if (info->mData == nullptr || !info->mOwnedByClient) {
return -EACCES;
}
@@ -2505,69 +2613,44 @@
return -EINVAL;
}
- sp<AMessage> reply = info->mNotify;
info->mData->setRange(offset, size);
+ info->mData->meta()->setInt64("timeUs", timeUs);
+ if (flags & BUFFER_FLAG_EOS) {
+ info->mData->meta()->setInt32("eos", true);
+ }
- sp<MediaCodecBuffer> buffer = info->mData;
+ if (flags & BUFFER_FLAG_CODECCONFIG) {
+ info->mData->meta()->setInt32("csd", true);
+ }
+
+ // synchronization boundary for getBufferAndFormat
+ sp<MediaCodecBuffer> buffer;
+ {
+ Mutex::Autolock al(mBufferLock);
+ info->mOwnedByClient = false;
+ buffer = info->mData;
+ info->mData.clear();
+ }
+ status_t err = OK;
if (mCrypto != NULL) {
AString *errorDetailMsg;
CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
- void *dst_pointer = nullptr;
- ICrypto::DestinationType dst_type = ICrypto::kDestinationTypeOpaqueHandle;
-
- if ((mFlags & kFlagIsSecure) == 0) {
- dst_pointer = info->mSecureData->base();
- dst_type = ICrypto::kDestinationTypeVmPointer;
- } else {
- sp<SecureBuffer> secureData = static_cast<SecureBuffer *>(info->mSecureData.get());
- dst_pointer = secureData->getDestinationPointer();
- dst_type = secureData->getDestinationType();
- }
-
- ssize_t result = mCrypto->decrypt(
- dst_type,
+ err = mBufferChannel->queueSecureInputBuffer(
+ buffer,
+ (mFlags & kFlagIsSecure),
key,
iv,
mode,
pattern,
- info->mSharedEncryptedBuffer,
- offset,
subSamples,
numSubSamples,
- dst_pointer,
errorDetailMsg);
-
- if (result < 0) {
- return result;
- }
-
- info->mSecureData->setRange(0, result);
- buffer = info->mSecureData;
- }
- buffer->meta()->setInt64("timeUs", timeUs);
-
- if (flags & BUFFER_FLAG_EOS) {
- buffer->meta()->setInt32("eos", true);
+ } else {
+ err = mBufferChannel->queueInputBuffer(buffer);
}
- if (flags & BUFFER_FLAG_CODECCONFIG) {
- buffer->meta()->setInt32("csd", true);
- }
-
- // synchronization boundary for getBufferAndFormat
- {
- Mutex::Autolock al(mBufferLock);
- info->mOwnedByClient = false;
- }
- info->mData.clear();
- info->mSecureData.clear();
- reply->setObject("buffer", buffer);
- reply->post();
-
- info->mNotify = NULL;
-
- return OK;
+ return err;
}
//static
@@ -2604,23 +2687,24 @@
return -ERANGE;
}
- BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
+ BufferInfo *info = &mPortBuffers[kPortIndexOutput][index];
- if (info->mNotify == NULL || !info->mOwnedByClient) {
+ if (info->mData == nullptr || !info->mOwnedByClient) {
return -EACCES;
}
// synchronization boundary for getBufferAndFormat
+ sp<MediaCodecBuffer> buffer;
{
Mutex::Autolock al(mBufferLock);
info->mOwnedByClient = false;
+ buffer = info->mData;
+ info->mData.clear();
}
- if (render && info->mData != NULL && info->mData->size() != 0) {
- info->mNotify->setInt32("render", true);
-
+ if (render && buffer->size() != 0) {
int64_t mediaTimeUs = -1;
- info->mData->meta()->findInt64("timeUs", &mediaTimeUs);
+ buffer->meta()->findInt64("timeUs", &mediaTimeUs);
int64_t renderTimeNs = 0;
if (!msg->findInt64("timestampNs", &renderTimeNs)) {
@@ -2628,12 +2712,11 @@
ALOGV("using buffer PTS of %lld", (long long)mediaTimeUs);
renderTimeNs = mediaTimeUs * 1000;
}
- info->mNotify->setInt64("timestampNs", renderTimeNs);
if (mSoftRenderer != NULL) {
std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render(
- info->mData->data(), info->mData->size(),
- mediaTimeUs, renderTimeNs, NULL, info->mData->format());
+ buffer->data(), buffer->size(),
+ mediaTimeUs, renderTimeNs, NULL, buffer->format());
// if we are running, notify rendered frames
if (!doneFrames.empty() && mState == STARTED && mOnFrameRenderedNotification != NULL) {
@@ -2645,13 +2728,11 @@
}
}
}
+ mBufferChannel->renderOutputBuffer(buffer, renderTimeNs);
+ } else {
+ mBufferChannel->discardBuffer(buffer);
}
- info->mNotify->setObject("buffer", info->mData);
- info->mData.clear();
- info->mNotify->post();
- info->mNotify.clear();
-
return OK;
}
@@ -2667,7 +2748,7 @@
size_t index = *availBuffers->begin();
availBuffers->erase(availBuffers->begin());
- BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index);
+ BufferInfo *info = &mPortBuffers[portIndex][index];
CHECK(!info->mOwnedByClient);
{
Mutex::Autolock al(mBufferLock);
@@ -2771,7 +2852,7 @@
int32_t index;
while ((index = dequeuePortBuffer(kPortIndexOutput)) >= 0) {
const sp<MediaCodecBuffer> &buffer =
- mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+ mPortBuffers[kPortIndexOutput][index].mData;
sp<AMessage> msg = mCallback->dup();
msg->setInt32("callbackID", CB_OUTPUT_AVAILABLE);
msg->setInt32("index", index);
@@ -2783,19 +2864,8 @@
msg->setInt64("timeUs", timeUs);
- int32_t omxFlags;
- CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
-
- uint32_t flags = 0;
- if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
- flags |= BUFFER_FLAG_SYNCFRAME;
- }
- if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
- flags |= BUFFER_FLAG_CODECCONFIG;
- }
- if (omxFlags & OMX_BUFFERFLAG_EOS) {
- flags |= BUFFER_FLAG_EOS;
- }
+ int32_t flags;
+ CHECK(buffer->meta()->findInt32("flags", &flags));
msg->setInt32("flags", flags);
@@ -2827,7 +2897,6 @@
}
}
-
void MediaCodec::postActivityNotificationIfPossible() {
if (mActivityNotify == NULL) {
return;
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index f2fdbc9..aeaead5 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -28,7 +28,6 @@
#include "include/MPEG2PSExtractor.h"
#include "include/MPEG2TSExtractor.h"
#include "include/DRMExtractor.h"
-#include "include/WVMExtractor.h"
#include "include/FLACExtractor.h"
#include "include/AACExtractor.h"
#include "include/MidiExtractor.h"
@@ -144,22 +143,14 @@
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractor::Create %s", mime);
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.extractremote", value, NULL)
- && (!strcmp("0", value) || !strcasecmp("false", value))) {
+ if (!property_get_bool("media.stagefright.extractremote", true)) {
// local extractor
ALOGW("creating media extractor in calling process");
return CreateFromService(source, mime);
} else {
- // Check if it's WVM, since WVMExtractor needs to be created in the media server process,
- // not the extractor process.
String8 mime8;
float confidence;
sp<AMessage> meta;
- if (SniffWVM(source, &mime8, &confidence, &meta) &&
- !strcasecmp(mime8, MEDIA_MIMETYPE_CONTAINER_WVM)) {
- return new WVMExtractor(source);
- }
// Check if it's es-based DRM, since DRMExtractor needs to be created in the media server
// process, not the extractor process.
@@ -192,14 +183,14 @@
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractor::CreateFromService %s", mime);
- DataSource::RegisterDefaultSniffers();
+ RegisterDefaultSniffers();
sp<AMessage> meta;
String8 tmp;
if (mime == NULL) {
float confidence;
- if (!source->sniff(&tmp, &confidence, &meta)) {
+ if (!sniff(source, &tmp, &confidence, &meta)) {
ALOGV("FAILED to autodetect media content.");
return NULL;
@@ -251,9 +242,6 @@
ret = new MatroskaExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
ret = new MPEG2TSExtractor(source);
- } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM) && getuid() == AID_MEDIA) {
- // Return now. WVExtractor should not have the DrmFlag set in the block below.
- return new WVMExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
ret = new AACExtractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
@@ -273,4 +261,77 @@
return ret;
}
+Mutex MediaExtractor::gSnifferMutex;
+List<MediaExtractor::SnifferFunc> MediaExtractor::gSniffers;
+bool MediaExtractor::gSniffersRegistered = false;
+
+// static
+bool MediaExtractor::sniff(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *meta) {
+ *mimeType = "";
+ *confidence = 0.0f;
+ meta->clear();
+
+ {
+ Mutex::Autolock autoLock(gSnifferMutex);
+ if (!gSniffersRegistered) {
+ return false;
+ }
+ }
+
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ String8 newMimeType;
+ float newConfidence;
+ sp<AMessage> newMeta;
+ if ((*it)(source, &newMimeType, &newConfidence, &newMeta)) {
+ if (newConfidence > *confidence) {
+ *mimeType = newMimeType;
+ *confidence = newConfidence;
+ *meta = newMeta;
+ }
+ }
+ }
+
+ return *confidence > 0.0;
+}
+
+// static
+void MediaExtractor::RegisterSniffer_l(SnifferFunc func) {
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ if (*it == func) {
+ return;
+ }
+ }
+
+ gSniffers.push_back(func);
+}
+
+// static
+void MediaExtractor::RegisterDefaultSniffers() {
+ Mutex::Autolock autoLock(gSnifferMutex);
+ if (gSniffersRegistered) {
+ return;
+ }
+
+ RegisterSniffer_l(SniffMPEG4);
+ RegisterSniffer_l(SniffMatroska);
+ RegisterSniffer_l(SniffOgg);
+ RegisterSniffer_l(SniffWAV);
+ RegisterSniffer_l(SniffFLAC);
+ RegisterSniffer_l(SniffAMR);
+ RegisterSniffer_l(SniffMPEG2TS);
+ RegisterSniffer_l(SniffMP3);
+ RegisterSniffer_l(SniffAAC);
+ RegisterSniffer_l(SniffMPEG2PS);
+ RegisterSniffer_l(SniffMidi);
+
+ if (property_get_bool("drm.service.enabled", false)) {
+ RegisterSniffer_l(SniffDRM);
+ }
+ gSniffersRegistered = true;
+}
+
+
} // namespace android
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index e3270ed..6f8220f 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -22,7 +22,6 @@
#include "include/ESDS.h"
#include "include/NuCachedSource2.h"
-#include "include/WVMExtractor.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -40,8 +39,7 @@
namespace android {
NuMediaExtractor::NuMediaExtractor()
- : mIsWidevineExtractor(false),
- mTotalBitrate(-1ll),
+ : mTotalBitrate(-1ll),
mDurationUs(-1ll) {
}
@@ -78,48 +76,15 @@
return -ENOENT;
}
- mIsWidevineExtractor = false;
- if (!strncasecmp("widevine://", path, 11)) {
- String8 mimeType;
- float confidence;
- sp<AMessage> dummy;
- bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
-
- if (!success
- || strcasecmp(
- mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
- return ERROR_UNSUPPORTED;
- }
-
- sp<WVMExtractor> extractor = new WVMExtractor(dataSource);
- extractor->setAdaptiveStreamingMode(true);
-
- mImpl = extractor;
- mIsWidevineExtractor = true;
- } else {
- mImpl = MediaExtractor::Create(dataSource);
- }
+ mImpl = MediaExtractor::Create(dataSource);
if (mImpl == NULL) {
return ERROR_UNSUPPORTED;
}
sp<MetaData> fileMeta = mImpl->getMetaData();
- const char *containerMime;
- if (fileMeta != NULL
- && fileMeta->findCString(kKeyMIMEType, &containerMime)
- && !strcasecmp(containerMime, "video/wvm")) {
- // We always want to use "cryptoPluginMode" when using the wvm
- // extractor. We can tell that it is this extractor by looking
- // at the container mime type.
- // The cryptoPluginMode ensures that the extractor will actually
- // give us data in a call to MediaSource::read(), unlike its
- // default mode that we used in AwesomePlayer.
- // TODO: change default mode
- static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true);
- } else if (mImpl->getDrmFlag()) {
- // For all other drm content, we don't want to expose decrypted
- // content to Java application.
+ if (mImpl->getDrmFlag()) {
+ // Don't expose decrypted content to Java application
mImpl.clear();
mImpl = NULL;
return ERROR_UNSUPPORTED;
@@ -633,15 +598,7 @@
Mutex::Autolock autoLock(mLock);
int64_t bitrate;
- if (mIsWidevineExtractor) {
- sp<WVMExtractor> wvmExtractor =
- static_cast<WVMExtractor *>(mImpl.get());
-
- status_t finalStatus;
- *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
- *eos = (finalStatus != OK);
- return true;
- } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
+ if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
&& getTotalBitrate(&bitrate)) {
sp<NuCachedSource2> cachedSource =
static_cast<NuCachedSource2 *>(mDataSource.get());
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index d8fec5c..5e00c44 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -55,8 +55,6 @@
: mParsedMetaData(false),
mAlbumArt(NULL) {
ALOGV("StagefrightMetadataRetriever()");
-
- DataSource::RegisterDefaultSniffers();
}
StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index c593eb5..f2638ed 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1659,9 +1659,7 @@
return AString("<URI suppressed>");
}
- char prop[PROPERTY_VALUE_MAX];
- if (property_get("media.stagefright.log-uri", prop, "false") &&
- (!strcmp(prop, "1") || !strcmp(prop, "true"))) {
+ if (property_get_bool("media.stagefright.log-uri", false)) {
return uri;
}
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
deleted file mode 100644
index d1b2f54..0000000
--- a/media/libstagefright/WVMExtractor.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "WVMExtractor"
-#include <utils/Log.h>
-
-#include "include/WVMExtractor.h"
-
-#include <arpa/inet.h>
-#include <utils/String8.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <dlfcn.h>
-
-#include <utils/Errors.h>
-
-/* The extractor lifetime is short - just long enough to get
- * the media sources constructed - so the shared lib needs to remain open
- * beyond the lifetime of the extractor. So keep the handle as a global
- * rather than a member of the extractor
- */
-void *gVendorLibHandle = NULL;
-
-namespace android {
-
-static Mutex gWVMutex;
-
-WVMExtractor::WVMExtractor(const sp<DataSource> &source)
- : mDataSource(source)
-{
- Mutex::Autolock autoLock(gWVMutex);
-
- if (!getVendorLibHandle()) {
- return;
- }
-
- typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>);
- GetInstanceFunc getInstanceFunc =
- (GetInstanceFunc) dlsym(gVendorLibHandle,
- "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
-
- if (getInstanceFunc) {
- if (source->DrmInitialization(
- MEDIA_MIMETYPE_CONTAINER_WVM) != NULL) {
- mImpl = (*getInstanceFunc)(source);
- CHECK(mImpl != NULL);
- setDrmFlag(true);
- } else {
- ALOGE("Drm manager failed to initialize.");
- }
- } else {
- ALOGE("Failed to locate GetInstance in libwvm.so");
- }
-}
-
-static void init_routine()
-{
- gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
- if (gVendorLibHandle == NULL) {
- ALOGE("Failed to open libwvm.so: %s", dlerror());
- }
-}
-
-bool WVMExtractor::getVendorLibHandle()
-{
- static pthread_once_t sOnceControl = PTHREAD_ONCE_INIT;
- pthread_once(&sOnceControl, init_routine);
-
- return gVendorLibHandle != NULL;
-}
-
-WVMExtractor::~WVMExtractor() {
-}
-
-size_t WVMExtractor::countTracks() {
- return (mImpl != NULL) ? mImpl->countTracks() : 0;
-}
-
-sp<IMediaSource> WVMExtractor::getTrack(size_t index) {
- if (mImpl == NULL) {
- return NULL;
- }
- return mImpl->getTrack(index);
-}
-
-sp<MetaData> WVMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
- if (mImpl == NULL) {
- return NULL;
- }
- return mImpl->getTrackMetaData(index, flags);
-}
-
-sp<MetaData> WVMExtractor::getMetaData() {
- if (mImpl == NULL) {
- return NULL;
- }
- return mImpl->getMetaData();
-}
-
-int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) {
- if (mImpl == NULL) {
- return 0;
- }
-
- return mImpl->getCachedDurationUs(finalStatus);
-}
-
-status_t WVMExtractor::getEstimatedBandwidthKbps(int32_t *kbps) {
- if (mImpl == NULL) {
- return UNKNOWN_ERROR;
- }
-
- return mImpl->getEstimatedBandwidthKbps(kbps);
-}
-
-
-void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) {
- if (mImpl != NULL) {
- mImpl->setAdaptiveStreamingMode(adaptive);
- }
-}
-
-void WVMExtractor::setCryptoPluginMode(bool cryptoPluginMode) {
- if (mImpl != NULL) {
- mImpl->setCryptoPluginMode(cryptoPluginMode);
- }
-}
-
-void WVMExtractor::setUID(uid_t uid) {
- if (mImpl != NULL) {
- mImpl->setUID(uid);
- }
-}
-
-status_t WVMExtractor::getError() {
- if (mImpl == NULL) {
- return UNKNOWN_ERROR;
- }
-
- return mImpl->getError();
-}
-
-void WVMExtractor::setError(status_t err) {
- if (mImpl != NULL) {
- mImpl->setError(err);
- }
-}
-
-bool SniffWVM(
- const sp<DataSource> &source, String8 *mimeType, float *confidence,
- sp<AMessage> *) {
-
- Mutex::Autolock autoLock(gWVMutex);
-
- if (!WVMExtractor::getVendorLibHandle()) {
- return false;
- }
-
- typedef WVMLoadableExtractor *(*SnifferFunc)(const sp<DataSource>&);
- SnifferFunc snifferFunc =
- (SnifferFunc) dlsym(gVendorLibHandle,
- "_ZN7android15IsWidevineMediaERKNS_2spINS_10DataSourceEEE");
-
- if (snifferFunc) {
- if ((*snifferFunc)(source)) {
- *mimeType = MEDIA_MIMETYPE_CONTAINER_WVM;
- *confidence = 10.0f;
- return true;
- }
- } else {
- ALOGE("IsWidevineMedia not found in libwvm.so");
- }
-
- return false;
-}
-
-} //namespace android
-
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index 877aead..777ab5b 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -42,6 +42,7 @@
#include "SaturationFilter.h"
#include "ZeroFilter.h"
+#include "../include/ACodecBufferChannel.h"
#include "../include/SharedMemoryBuffer.h"
namespace android {
@@ -53,6 +54,9 @@
: mState(UNINITIALIZED),
mGeneration(0),
mGraphicBufferListener(NULL) {
+ mBufferChannel = std::make_shared<ACodecBufferChannel>(
+ new AMessage(kWhatInputBufferFilled, this),
+ new AMessage(kWhatOutputBufferDrained, this));
}
MediaFilter::~MediaFilter() {
@@ -60,8 +64,8 @@
//////////////////// PUBLIC FUNCTIONS //////////////////////////////////////////
-void MediaFilter::setNotificationMessage(const sp<AMessage> &msg) {
- mNotify = msg;
+std::shared_ptr<BufferChannelBase> MediaFilter::getBufferChannel() {
+ return mBufferChannel;
}
void MediaFilter::initiateAllocateComponent(const sp<AMessage> &msg) {
@@ -193,29 +197,6 @@
}
}
-//////////////////// PORT DESCRIPTION //////////////////////////////////////////
-
-MediaFilter::PortDescription::PortDescription() {
-}
-
-void MediaFilter::PortDescription::addBuffer(
- IOMX::buffer_id id, const sp<MediaCodecBuffer> &buffer) {
- mBufferIDs.push_back(id);
- mBuffers.push_back(buffer);
-}
-
-size_t MediaFilter::PortDescription::countBuffers() {
- return mBufferIDs.size();
-}
-
-IOMX::buffer_id MediaFilter::PortDescription::bufferIDAt(size_t index) const {
- return mBufferIDs.itemAt(index);
-}
-
-sp<MediaCodecBuffer> MediaFilter::PortDescription::bufferAt(size_t index) const {
- return mBuffers.itemAt(index);
-}
-
//////////////////// HELPER FUNCTIONS //////////////////////////////////////////
void MediaFilter::signalProcessBuffers() {
@@ -223,10 +204,7 @@
}
void MediaFilter::signalError(status_t error) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatError);
- notify->setInt32("err", error);
- notify->post();
+ mCallback->onError(error, ACTION_CODE_FATAL);
}
status_t MediaFilter::allocateBuffersOnPort(OMX_U32 portIndex) {
@@ -266,21 +244,15 @@
}
}
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatBuffersAllocated);
-
- notify->setInt32("portIndex", portIndex);
-
- sp<PortDescription> desc = new PortDescription;
-
+ std::vector<ACodecBufferChannel::BufferAndId> array(mBuffers[portIndex].size());
for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
- const BufferInfo &info = mBuffers[portIndex][i];
-
- desc->addBuffer(info.mBufferID, info.mData);
+ array[i] = {mBuffers[portIndex][i].mData, mBuffers[portIndex][i].mBufferID};
}
-
- notify->setObject("portDesc", desc);
- notify->post();
+ if (portIndex == kPortIndexInput) {
+ mBufferChannel->setInputBufferArray(array);
+ } else {
+ mBufferChannel->setOutputBufferArray(array);
+ }
return OK;
}
@@ -314,20 +286,14 @@
info->mGeneration = mGeneration;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFillThisBuffer);
- notify->setInt32("buffer-id", info->mBufferID);
-
info->mData->meta()->clear();
- notify->setObject("buffer", info->mData);
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, this);
reply->setInt32("buffer-id", info->mBufferID);
- notify->setMessage("reply", reply);
-
info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
- notify->post();
+
+ mBufferChannel->fillThisBuffer(info->mBufferID);
}
void MediaFilter::postDrainThisBuffer(BufferInfo *info) {
@@ -335,27 +301,16 @@
info->mGeneration = mGeneration;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
- notify->setInt32("buffer-id", info->mBufferID);
- notify->setInt32("flags", info->mOutputFlags);
- notify->setObject("buffer", info->mData);
-
sp<AMessage> reply = new AMessage(kWhatOutputBufferDrained, this);
reply->setInt32("buffer-id", info->mBufferID);
- notify->setMessage("reply", reply);
-
- notify->post();
+ mBufferChannel->drainThisBuffer(info->mBufferID, info->mOutputFlags);
info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}
void MediaFilter::postEOS() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatEOS);
- notify->setInt32("err", ERROR_END_OF_STREAM);
- notify->post();
+ mCallback->onEos(ERROR_END_OF_STREAM);
ALOGV("Sent kWhatEOS.");
}
@@ -445,11 +400,8 @@
return;
}
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatComponentAllocated);
// HACK - need "OMX.google" to use MediaCodec's software renderer
- notify->setString("componentName", "OMX.google.MediaFilter");
- notify->post();
+ mCallback->onComponentAllocated("OMX.google.MediaFilter");
mState = INITIALIZED;
ALOGV("Handled kWhatAllocateComponent.");
}
@@ -526,12 +478,7 @@
mOutputFormat->setInt32("width", mWidth);
mOutputFormat->setInt32("height", mHeight);
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatComponentConfigured);
- notify->setString("componentName", "MediaFilter");
- notify->setMessage("input-format", mInputFormat);
- notify->setMessage("output-format", mOutputFormat);
- notify->post();
+ mCallback->onComponentConfigured(mInputFormat, mOutputFormat);
mState = CONFIGURED;
ALOGV("Handled kWhatConfigureComponent.");
}
@@ -543,6 +490,8 @@
allocateBuffersOnPort(kPortIndexOutput);
+ mCallback->onStartCompleted();
+
status_t err = mFilter->start();
if (err != (status_t)OK) {
ALOGE("Failed to start filter component, err %d", err);
@@ -675,10 +624,11 @@
mState = INITIALIZED;
}
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", keepComponentAllocated ?
- CodecBase::kWhatStopCompleted : CodecBase::kWhatReleaseCompleted);
- notify->post();
+ if (keepComponentAllocated) {
+ mCallback->onStopCompleted();
+ } else {
+ mCallback->onReleaseCompleted();
+ }
}
void MediaFilter::onFlush() {
@@ -700,9 +650,7 @@
mPortEOS[kPortIndexOutput] = false;
mInputEOSResult = OK;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatFlushCompleted);
- notify->post();
+ mCallback->onFlushCompleted();
ALOGV("Posted kWhatFlushCompleted");
// MediaCodec returns all input buffers after flush, so in
@@ -734,13 +682,10 @@
return;
}
- sp<AMessage> reply = mNotify->dup();
- reply->setInt32("what", CodecBase::kWhatInputSurfaceCreated);
- reply->setObject(
- "input-surface",
+ mCallback->onInputSurfaceCreated(
+ nullptr, nullptr,
new BufferProducerWrapper(
mGraphicBufferListener->getIGraphicBufferProducer()));
- reply->post();
}
void MediaFilter::onInputFrameAvailable() {
@@ -802,9 +747,7 @@
}
mPortEOS[kPortIndexOutput] = true;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", CodecBase::kWhatSignaledInputEOS);
- notify->post();
+ mCallback->onSignaledInputEOS(OK);
ALOGV("Output stream saw EOS.");
}
diff --git a/media/libstagefright/foundation/tests/AData_test.cpp b/media/libstagefright/foundation/tests/AData_test.cpp
new file mode 100644
index 0000000..f014c25
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AData_test.cpp
@@ -0,0 +1,981 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AData_test"
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+//#include <utils/StrongPointer.h>
+
+#include <media/stagefright/foundation/AData.h>
+#include <media/stagefright/foundation/ABuffer.h>
+
+namespace android {
+
+class ADataTest : public ::testing::Test {
+};
+
+// ============ AUnion
+
+struct Events {
+ int dtor;
+ int ctor_empty;
+ int ctor_copy;
+};
+
+struct EventCounter : public RefBase {
+ EventCounter(int *counter, int magic=1234) : mCounter(counter), mMagic(magic) { }
+ virtual ~EventCounter() { ++*mCounter; mMagic = 0; }
+ int magic() const { return mMagic; }
+private:
+ int *mCounter;
+ int mMagic;
+};
+
+struct DerivedCounter : public EventCounter {
+ DerivedCounter(int *counter, int magic=1234) : EventCounter(counter, magic) { }
+};
+
+TEST_F(ADataTest, AUnion_Test) {
+ AUnion<int, const char *, char> u;
+ u.emplace<int>(4);
+ u.del<int>();
+ EXPECT_EQ(4, u.get<int>()); // verify that del<> is a no-op for trivial types, such as int.
+ // specifically, verify that it does not clear the objet memory
+
+ u.emplace<const char *>("hello");
+ EXPECT_STREQ("hello", u.get<const char *>());
+ u.del<const char *>();
+
+ // u.del<char *>();
+ // u.emplace<const int>(4);
+ u.emplace<void>();
+ u.del<void>();
+
+ u.emplace<int>(~0);
+ u.del<int>();
+ EXPECT_EQ(~0, u.get<int>());
+ u.emplace<char>(0x15);
+ // verify that rest of memory after char is cleared upon construction
+ EXPECT_EQ(0, memcmp((char *)(&u) + sizeof(char), "\0\0\0", 3));
+ EXPECT_EQ(0x15, u.get<char>());
+ u.del<char>();
+
+ AUnion<EventCounter, EventCounter *> d;
+ int destructions = 0;
+
+ d.emplace<EventCounter>(&destructions);
+ d.del<EventCounter>();
+ EXPECT_EQ(1, destructions);
+
+ EventCounter *ctr = new EventCounter(&destructions);
+ d.emplace<EventCounter *>(ctr);
+ d.del<EventCounter *>();
+ EXPECT_EQ(1, destructions);
+
+ delete ctr;
+ EXPECT_EQ(2, destructions);
+
+ AUnion<std::shared_ptr<EventCounter>, std::unique_ptr<EventCounter>> md;
+ md.emplace<std::shared_ptr<EventCounter>>(new EventCounter(&destructions));
+ std::shared_ptr<EventCounter> copy(md.get<std::shared_ptr<EventCounter>>());
+ std::weak_ptr<EventCounter> weak(copy);
+ EXPECT_EQ(2, destructions);
+
+ copy.reset();
+ EXPECT_EQ(2, destructions);
+ md.del<std::shared_ptr<EventCounter>>();
+ EXPECT_EQ(3, destructions);
+ EXPECT_TRUE(weak.expired());
+
+ md.emplace<std::unique_ptr<EventCounter>>(new EventCounter(&destructions));
+ EXPECT_EQ(3, destructions);
+
+ std::unique_ptr<EventCounter> unique = std::move(md.get<std::unique_ptr<EventCounter>>());
+ EXPECT_EQ(3, destructions);
+ EXPECT_FALSE((bool)md.get<std::unique_ptr<EventCounter>>());
+
+ md.del<std::unique_ptr<EventCounter>>();
+ EXPECT_EQ(3, destructions);
+ md.emplace<std::unique_ptr<EventCounter>>(std::move(unique));
+ EXPECT_TRUE((bool)md.get<std::unique_ptr<EventCounter>>());
+ EXPECT_EQ(3, destructions);
+
+ md.del<std::unique_ptr<EventCounter>>();
+ EXPECT_EQ(4, destructions);
+}
+
+TEST_F(ADataTest, AData_StaticTest) {
+ using namespace std;
+
+ static_assert(is_copy_assignable<shared_ptr<EventCounter>>::value, "");
+ static_assert(is_copy_constructible<shared_ptr<EventCounter>>::value, "");
+ static_assert(is_default_constructible<shared_ptr<EventCounter>>::value, "");
+
+ static_assert(is_copy_assignable<weak_ptr<DerivedCounter>>::value, "");
+ static_assert(is_copy_constructible<weak_ptr<DerivedCounter>>::value, "");
+ static_assert(is_default_constructible<weak_ptr<DerivedCounter>>::value, "");
+
+ static_assert(!is_copy_assignable<unique_ptr<DerivedCounter>>::value, "");
+ static_assert(!is_copy_constructible<unique_ptr<DerivedCounter>>::value, "");
+ static_assert(is_default_constructible<unique_ptr<DerivedCounter>>::value, "");
+
+ static_assert(is_copy_assignable<sp<EventCounter>>::value, "");
+ static_assert(is_copy_constructible<sp<EventCounter>>::value, "");
+ static_assert(is_default_constructible<sp<EventCounter>>::value, "");
+
+ static_assert(is_copy_assignable<wp<EventCounter>>::value, "");
+ static_assert(is_copy_constructible<wp<EventCounter>>::value, "");
+ static_assert(is_default_constructible<wp<EventCounter>>::value, "");
+
+ static_assert(is_convertible<shared_ptr<DerivedCounter>, shared_ptr<EventCounter>>::value, "");
+ static_assert(!is_convertible<shared_ptr<EventCounter>, shared_ptr<DerivedCounter>>::value, "");
+
+ static_assert(is_convertible<unique_ptr<DerivedCounter>, unique_ptr<EventCounter>>::value, "");
+ static_assert(!is_convertible<unique_ptr<EventCounter>, unique_ptr<DerivedCounter>>::value, "");
+
+ static_assert(is_convertible<unique_ptr<DerivedCounter>, shared_ptr<EventCounter>>::value, "");
+ static_assert(!is_convertible<shared_ptr<DerivedCounter>, unique_ptr<EventCounter>>::value, "");
+
+ static_assert(is_convertible<weak_ptr<DerivedCounter>, weak_ptr<EventCounter>>::value, "");
+ static_assert(!is_convertible<weak_ptr<EventCounter>, weak_ptr<DerivedCounter>>::value, "");
+
+ static_assert(is_convertible<shared_ptr<DerivedCounter>, weak_ptr<EventCounter>>::value, "");
+ static_assert(!is_convertible<weak_ptr<DerivedCounter>, shared_ptr<EventCounter>>::value, "");
+
+ static_assert(is_convertible<sp<EventCounter>, sp<RefBase>>::value, "");
+ static_assert(is_convertible<sp<RefBase>, sp<EventCounter>>::value, "YES");
+
+ static_assert(is_convertible<wp<EventCounter>, wp<RefBase>>::value, "");
+ static_assert(is_convertible<wp<RefBase>, wp<EventCounter>>::value, "YES");
+
+ static_assert(is_convertible<sp<EventCounter>, wp<RefBase>>::value, "");
+ static_assert(!is_convertible<wp<EventCounter>, sp<RefBase>>::value, "");
+}
+
+TEST_F(ADataTest, AData_SampleTest) {
+ AData<int, float>::Basic data;
+ int i = 1;
+ float f = 7.0f;
+
+ data.set(5);
+ EXPECT_TRUE(data.find(&i));
+ EXPECT_FALSE(data.find(&f));
+ EXPECT_EQ(i, 5);
+
+ data.set(6.0f);
+ EXPECT_FALSE(data.find(&i));
+ EXPECT_TRUE(data.find(&f));
+ EXPECT_EQ(f, 6.0f);
+
+ AData<int, sp<RefBase>>::RelaxedBasic objdata; // relaxed type support
+ sp<ABuffer> buf = new ABuffer(16), buf2;
+ sp<RefBase> obj;
+
+ objdata.set(buf);
+ EXPECT_TRUE(objdata.find(&buf2));
+ EXPECT_EQ(buf, buf2);
+ EXPECT_FALSE(objdata.find(&i));
+ EXPECT_TRUE(objdata.find(&obj));
+ EXPECT_TRUE(obj == buf);
+
+ obj = buf;
+ objdata.set(obj); // storing as sp<RefBase>
+ EXPECT_FALSE(objdata.find(&buf2)); // not stored as ABuffer(!)
+ EXPECT_TRUE(objdata.find(&obj));
+}
+
+struct SampleTypeFlagger {
+ typedef unsigned type;
+ enum Flags : type {
+ kEmpty = 100,
+ kInt,
+ kConstCharPtr,
+ kEventCounter,
+ kEventCounterPointer,
+ kEventCounterSharedPointer,
+ kEventCounterUniquePointer,
+ kEventCounterWeakPointer,
+ kEventCounterSP,
+ kEventCounterWP,
+ };
+ constexpr static type mask = ~Flags(0);
+ constexpr static type flagFor(void*) { return kEmpty; }
+ constexpr static type flagFor(int*) { return kInt; }
+ constexpr static type flagFor(const char**) { return kConstCharPtr; }
+ constexpr static type flagFor(EventCounter*) { return kEventCounter; }
+ constexpr static type flagFor(EventCounter**) { return kEventCounterPointer; }
+ constexpr static
+ type flagFor(std::shared_ptr<EventCounter>*) { return kEventCounterSharedPointer; }
+ constexpr static
+ type flagFor(std::unique_ptr<EventCounter>*) { return kEventCounterUniquePointer; }
+ constexpr static type flagFor(std::weak_ptr<EventCounter>*) { return kEventCounterWeakPointer; }
+ constexpr static type flagFor(sp<EventCounter>*) { return kEventCounterSP; }
+ constexpr static type flagFor(wp<EventCounter>*) { return kEventCounterWP; }
+ constexpr static bool canDeleteAs(type object, type del) { return del == object; }
+ template <typename T> struct store { typedef T as_type; };
+};
+
+TEST_F(ADataTest, AData_SimpleTest) {
+ int _int = 0;
+ const char *_constCharPtr = NULL;
+ AData<int, const char *>::Custom<SampleTypeFlagger> u;
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find<int>(&_int));
+ EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+
+ EXPECT_TRUE(u.set<int>(4));
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.find<int>(&_int));
+ EXPECT_EQ(4, _int);
+ EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+ EXPECT_EQ(NULL, _constCharPtr);
+
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find<int>(&_int));
+ EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+
+ EXPECT_TRUE(u.set<int>(5));
+ EXPECT_TRUE(u.set<int>(6));
+ EXPECT_TRUE(u.find<int>(&_int));
+ EXPECT_EQ(6, _int);
+
+ EXPECT_TRUE(u.set<const char *>("hello"));
+ EXPECT_TRUE(u.used());
+ EXPECT_FALSE(u.find<int>(&_int));
+ EXPECT_TRUE(u.find<const char *>(&_constCharPtr));
+ EXPECT_STREQ("hello", _constCharPtr);
+
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find<int>(&_int));
+ EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+
+ EXPECT_TRUE(u.set<const char *>("world"));
+ EXPECT_TRUE(u.set<const char *>("!!"));
+ EXPECT_TRUE(u.used());
+ EXPECT_FALSE(u.find<int>(&_int));
+ EXPECT_TRUE(u.find<const char *>(&_constCharPtr));
+ EXPECT_STREQ("!!", _constCharPtr);
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_TRUE(u.find(&_constCharPtr));
+}
+
+void set(std::unique_ptr<int> &dst, std::unique_ptr<int> &&src) {
+ dst = std::move(src);
+}
+
+void set(std::unique_ptr<int> &dst, std::unique_ptr<int> &src) {
+ dst = std::move(src);
+}
+
+TEST_F(ADataTest, AData_CopyMoveTest) {
+ int destructions = 0;
+ int _int = 0;
+ std::shared_ptr<EventCounter> _shared;
+ std::unique_ptr<EventCounter> _unique;
+ std::weak_ptr<EventCounter> _weak;
+ const std::shared_ptr<EventCounter> _constShared(new EventCounter(&destructions));
+ const std::unique_ptr<EventCounter> _constUnique = nullptr;
+
+ AData<int, std::weak_ptr<EventCounter>, std::shared_ptr<EventCounter>,
+ std::unique_ptr<EventCounter>>::Basic u;
+
+ // test that data is empty
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+
+ // test that integer can be stored and read
+ EXPECT_TRUE(u.set<int>(1));
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.find(&_int));
+ EXPECT_EQ(1, _int);
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+
+ // test that movable type (unique_ptr) can be moved in and read out, and it moves
+ _unique = std::unique_ptr<EventCounter>(new EventCounter(&destructions, 123));
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_TRUE(u.used());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_TRUE((bool)_unique);
+ if (_unique) {
+ EXPECT_EQ(123, _unique->magic());
+ }
+
+ // the unique value should have been removed but still accessible as nullptr
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_EQ(1, destructions);
+
+ // test that movable-only type (unique_ptr) can be stored without moving (and is still
+ // moved)
+ _unique = std::unique_ptr<EventCounter>(new EventCounter(&destructions, 321));
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_TRUE(u.set(std::unique_ptr<EventCounter>(new EventCounter(&destructions, 1234))));
+ EXPECT_EQ(2, destructions);
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_TRUE((bool)_unique);
+ if (_unique) {
+ EXPECT_EQ(1234, _unique->magic());
+ }
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_EQ(2, destructions);
+ EXPECT_TRUE(u.clear());
+ EXPECT_EQ(3, destructions);
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+
+ // u.set(_constUnique);
+
+ // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+ _shared = std::make_shared<EventCounter>(&destructions, 234);
+ EXPECT_EQ(1L, _shared.use_count());
+ EXPECT_TRUE(u.set(_shared));
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ EXPECT_EQ(2L, _shared.use_count());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(2L, _shared.use_count());
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ // explicitly move in shared_ptr
+ EXPECT_TRUE(u.set(std::move(_shared)));
+ EXPECT_EQ(0, _shared.use_count()); // shared should be nullptr
+ EXPECT_FALSE((bool)_shared);
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(2L, _shared.use_count()); // now both u and _shared contains the object
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(2L, _shared.use_count()); // still both u and _shared contains the object
+
+ EXPECT_TRUE(u.clear());
+ EXPECT_TRUE(_shared.unique()); // now only _shared contains the object
+
+ EXPECT_TRUE(u.set(_constShared));
+ EXPECT_EQ(2L, _constShared.use_count()); // even though it is const, we can add a use count
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(3L, _shared.use_count()); // now u, _shared and _constShared contains the const object
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(1234, _shared->magic());
+ }
+
+ // test that weak pointer can be copied in (support for moving is from C++14 only)
+ _weak = _shared;
+ EXPECT_EQ(_weak.use_count(), _shared.use_count());
+ EXPECT_TRUE(u.set(_weak));
+
+ _weak.reset();
+ EXPECT_EQ(_weak.use_count(), 0);
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.use_count(), _shared.use_count());
+ EXPECT_EQ(_weak.lock(), _shared);
+
+ // we can remove a weak pointer multiple times
+ _weak.reset();
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.use_count(), _shared.use_count());
+ EXPECT_EQ(_weak.lock(), _shared);
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+};
+
+TEST_F(ADataTest, AData_RelaxedCopyMoveTest) {
+ int destructions = 0;
+ int _int = 0;
+ std::shared_ptr<DerivedCounter> _shared;
+ std::unique_ptr<DerivedCounter> _unique, _unique2;
+ std::weak_ptr<DerivedCounter> _weak;
+ std::shared_ptr<EventCounter> _shared_base;
+ std::unique_ptr<EventCounter> _unique_base;
+ std::weak_ptr<EventCounter> _weak_base;
+ const std::shared_ptr<DerivedCounter> _constShared(new DerivedCounter(&destructions));
+ const std::unique_ptr<DerivedCounter> _constUnique = nullptr;
+
+ AData<int, std::unique_ptr<EventCounter>, std::shared_ptr<EventCounter>,
+ std::weak_ptr<EventCounter>>::RelaxedBasic u;
+
+ // test that data is empty
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that integer can be stored and read
+ EXPECT_TRUE(u.set<int>(1));
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.find(&_int));
+ EXPECT_EQ(1, _int);
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that movable type (unique_ptr) can be moved in and read out, and it moves
+ _unique = std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 123));
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_TRUE(u.used());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_TRUE((bool)_unique);
+ if (_unique) {
+ EXPECT_EQ(123, _unique->magic());
+ }
+
+ // the unique value should have been removed but still accessible as nullptr
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_EQ(1, destructions);
+
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_TRUE(u.remove(&_unique_base));
+ EXPECT_FALSE((bool)_unique_base);
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that movable-only type (unique_ptr) can be stored without moving (and is still
+ // moved)
+ _unique = std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 321));
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_TRUE(u.set(std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 1234))));
+ EXPECT_EQ(2, destructions);
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_TRUE((bool)_unique);
+ if (_unique) {
+ EXPECT_EQ(1234, _unique->magic());
+ }
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_EQ(2, destructions);
+ EXPECT_TRUE(u.clear());
+ EXPECT_EQ(3, destructions);
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that unique pointer can be set and removed as base type (but removed as derived only
+ // if it was set as derived type)
+ _unique = std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 321));
+ EXPECT_TRUE(u.set(std::move(_unique)));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_TRUE(u.remove(&_unique_base));
+ EXPECT_TRUE((bool)_unique_base);
+ if (_unique_base) {
+ EXPECT_EQ(321, _unique_base->magic());
+ }
+ EXPECT_TRUE(u.remove(&_unique));
+ EXPECT_FALSE((bool)_unique);
+
+ EXPECT_TRUE(u.set(std::move(_unique_base)));
+ EXPECT_FALSE((bool)_unique_base);
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE((bool)_unique);
+ EXPECT_TRUE(u.remove(&_unique_base));
+ EXPECT_TRUE((bool)_unique_base);
+ if (_unique_base) {
+ EXPECT_EQ(321, _unique_base->magic());
+ }
+
+ EXPECT_EQ(3, destructions);
+ EXPECT_TRUE(u.remove(&_unique_base));
+ EXPECT_EQ(4, destructions);
+ EXPECT_FALSE((bool)_unique_base);
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // u.set(_constUnique);
+
+ // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+ _shared = std::make_shared<DerivedCounter>(&destructions, 234);
+ EXPECT_EQ(1L, _shared.use_count());
+ EXPECT_TRUE(u.set(_shared));
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ EXPECT_EQ(2L, _shared.use_count());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+ EXPECT_EQ(2L, _shared.use_count());
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ // explicitly move in shared_ptr
+ EXPECT_TRUE(u.set(std::move(_shared)));
+ EXPECT_EQ(0, _shared.use_count()); // shared should be nullptr
+ EXPECT_FALSE((bool)_shared);
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(2L, _shared.use_count()); // now both u and _shared contains the object
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(2L, _shared.use_count()); // still both u and _shared contains the object
+
+ EXPECT_TRUE(u.clear());
+ EXPECT_TRUE(_shared.unique()); // now only _shared contains the object
+
+ EXPECT_TRUE(u.set(_constShared));
+ EXPECT_EQ(2L, _constShared.use_count()); // even though it is const, we can add a use count
+ EXPECT_TRUE(u.find(&_shared));
+ EXPECT_EQ(3L, _shared.use_count()); // now u, _shared and _constShared contains the const object
+ EXPECT_TRUE((bool)_shared);
+ if (_shared) {
+ EXPECT_EQ(1234, _shared->magic());
+ }
+
+ // test that shared pointer can be set and removed as base type (but removed as derived only
+ // if it was set as derived type)
+ EXPECT_TRUE(u.find(&_shared_base));
+ EXPECT_TRUE((bool)_shared_base);
+ if (_shared_base) {
+ EXPECT_EQ(1234, _shared_base->magic());
+ }
+ EXPECT_EQ(4L, _shared.use_count()); // now u, _shared, _constShared and _shared_base contains
+ // the const object
+ _shared.reset();
+ EXPECT_EQ(3L, _shared_base.use_count()); // now u, _constShared and _shared_base contains it
+ EXPECT_TRUE(u.clear());
+ EXPECT_EQ(2L, _shared_base.use_count()); // now _constShared and _shared_base contains it
+
+ EXPECT_TRUE(u.set(_shared_base)); // now u_ also contains it as base class
+ EXPECT_EQ(3L, _shared_base.use_count());
+ EXPECT_FALSE(u.find(&_shared)); // cannot get it as derived type
+ EXPECT_FALSE((bool)_shared);
+ _shared_base.reset();
+ EXPECT_TRUE(u.find(&_shared_base)); // can still get it as base type
+ EXPECT_TRUE((bool)_shared_base);
+ if (_shared_base) {
+ EXPECT_EQ(1234, _shared_base->magic());
+ }
+ _shared = std::static_pointer_cast<DerivedCounter>(_shared_base);
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that weak pointer can be copied in (support for moving is from C++14 only)
+ _weak = _shared;
+ EXPECT_EQ(_weak.use_count(), _shared.use_count());
+ EXPECT_TRUE(u.set(_weak));
+
+ _weak.reset();
+ EXPECT_EQ(_weak.use_count(), 0);
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.use_count(), _shared.use_count());
+ EXPECT_EQ(_weak.lock(), _shared);
+
+ // we can remove a weak pointer multiple times
+ _weak.reset();
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.use_count(), _shared.use_count());
+ EXPECT_EQ(_weak.lock(), _shared);
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that weak pointer can be set and removed as base type (but removed as derived only
+ // if it was set as derived type)
+ _weak = _shared;
+ EXPECT_TRUE(u.set(_weak));
+ EXPECT_TRUE(u.find(&_weak_base));
+ EXPECT_FALSE(_weak_base.expired());
+ if (!_weak_base.expired()) {
+ EXPECT_EQ(1234, _weak_base.lock()->magic());
+ }
+ // now _shared, _constShared and _shared_base contains the const object
+ EXPECT_EQ(3L, _weak.use_count());
+ _weak.reset();
+ EXPECT_EQ(3L, _weak_base.use_count()); // _weak did not hold a reference
+ _shared.reset();
+ EXPECT_EQ(2L, _weak_base.use_count()); // now u, _constShared and _shared_base contains it
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.remove(&_unique));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.remove(&_unique_base));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ EXPECT_TRUE(u.set(_weak_base)); // now u_ also contains it as base class
+ EXPECT_FALSE(u.find(&_weak)); // cannot get it as derived type
+ EXPECT_TRUE(_weak.expired());
+ _weak_base.reset();
+ EXPECT_TRUE(u.find(&_weak_base)); // can still get it as base type
+ EXPECT_FALSE(_weak_base.expired());
+ if (!_weak_base.expired()) {
+ EXPECT_EQ(1234, _weak_base.lock()->magic());
+ }
+};
+
+TEST_F(ADataTest, AData_AndroidSpTest) {
+ int destructions = 0;
+ int _int = 0;
+ sp<EventCounter> _shared;
+ wp<EventCounter> _weak;
+ const sp<EventCounter> _constShared(new EventCounter(&destructions));
+
+ AData<int, sp<EventCounter>, wp<EventCounter>>::Strict<uint8_t> u;
+
+ // test that data is empty
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+
+ // test that integer can be stored and read
+ EXPECT_TRUE(u.set<int>(1));
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.find(&_int));
+ EXPECT_EQ(1, _int);
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+
+ // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+ _shared = new EventCounter(&destructions, 234);
+ _weak = _shared; // used for tracking #234
+
+ EXPECT_TRUE(u.set(_shared));
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ _shared.clear();
+ EXPECT_EQ(NULL, _shared.get());
+ EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+ // verify the find did not move out object
+ _shared.clear();
+ EXPECT_EQ(NULL, _shared.get());
+ EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+ EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ // verify that we can set object multiple times
+ EXPECT_TRUE(u.set(_shared));
+
+ // explicitly move in sp
+ EXPECT_TRUE(u.set(std::move(_shared)));
+ EXPECT_FALSE((bool)_shared.get()); // android also clears sp<> on move...
+ EXPECT_TRUE(u.find(&_shared)); // still can get it back
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_weak));
+
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.clear()); // now only _shared contains the object
+ EXPECT_FALSE(u.used());
+
+ // we still hold a copy
+ EXPECT_TRUE((bool)_shared.get());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared)); // _shared still contains the object
+
+ EXPECT_TRUE(u.set(_constShared));
+ EXPECT_TRUE(u.find(&_shared)); // now _shared contains _constShared
+ EXPECT_EQ(NULL, _weak.promote().get()); // original _shared is now lost
+
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(1234, _shared->magic());
+ }
+ EXPECT_TRUE(u.clear());
+
+ // test that wp can be copied in
+ _weak = _shared;
+ EXPECT_TRUE(u.set(_weak));
+
+ _weak.clear();
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.promote(), _shared);
+
+ // we can remove a weak pointer multiple times
+ _weak.clear();
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.promote(), _shared);
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+};
+
+TEST_F(ADataTest, AData_RelaxedAndroidSpTest) {
+ int destructions = 0;
+ int _int = 0;
+ sp<EventCounter> _shared;
+ wp<EventCounter> _weak;
+ sp<RefBase> _shared_base;
+ wp<RefBase> _weak_base;
+ const sp<EventCounter> _constShared(new EventCounter(&destructions));
+
+ AData<int, sp<RefBase>, wp<RefBase>>::Relaxed<uint16_t> u;
+
+ // test that data is empty
+ EXPECT_FALSE(u.used());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that integer can be stored and read
+ EXPECT_TRUE(u.set<int>(1));
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.find(&_int));
+ EXPECT_EQ(1, _int);
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+ _shared = new EventCounter(&destructions, 234);
+ _weak = _shared; // used for tracking #234
+
+ EXPECT_TRUE(u.set(_shared));
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ _shared.clear();
+ EXPECT_EQ(NULL, _shared.get());
+ EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+ // verify the find did not move out object
+ _shared.clear();
+ EXPECT_EQ(NULL, _shared.get());
+ EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+ EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+
+ // verify that we can set object multiple times
+ EXPECT_TRUE(u.set(_shared));
+
+ // explicitly move in sp
+ EXPECT_TRUE(u.set(std::move(_shared)));
+ EXPECT_FALSE((bool)_shared.get()); // android also clears sp<> on move...
+ EXPECT_TRUE(u.find(&_shared)); // still can get it back
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(234, _shared->magic());
+ }
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ EXPECT_TRUE(u.used());
+ EXPECT_TRUE(u.clear()); // now only _shared contains the object
+ EXPECT_FALSE(u.used());
+
+ // we still hold a copy
+ EXPECT_TRUE((bool)_shared.get());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared)); // _shared still contains the object
+
+ EXPECT_TRUE(u.set(_constShared));
+ EXPECT_TRUE(u.find(&_shared)); // now _shared contains _constShared
+ EXPECT_EQ(NULL, _weak.promote().get()); // original _shared is now lost
+
+ EXPECT_TRUE((bool)_shared.get());
+ if (_shared.get()) {
+ EXPECT_EQ(1234, _shared->magic());
+ }
+ EXPECT_TRUE(u.clear());
+
+ // test that shared pointer can be set and removed as base type (but removed as derived only
+ // if it was set as derived type)
+ EXPECT_TRUE(u.set(_constShared));
+ EXPECT_TRUE(u.find(&_shared_base));
+ EXPECT_TRUE((bool)_shared_base.get());
+ if (_shared_base.get()) {
+ EXPECT_EQ(1234, static_cast<EventCounter*>(_shared_base.get())->magic());
+ }
+ _shared.clear();
+ EXPECT_TRUE(u.clear());
+ EXPECT_TRUE((bool)_shared_base.get());
+ if (_shared_base.get()) {
+ EXPECT_EQ(1234, static_cast<EventCounter*>(_shared_base.get())->magic());
+ }
+
+ EXPECT_TRUE(u.set(_shared_base)); // now u contains it as base class
+ EXPECT_TRUE((bool)_shared_base.get());
+ EXPECT_FALSE(u.find(&_shared)); // cannot get it as derived type
+ EXPECT_FALSE((bool)_shared.get());
+ _shared_base.clear();
+ EXPECT_TRUE(u.find(&_shared_base)); // can still get it as base type
+ EXPECT_TRUE((bool)_shared_base.get());
+ if (_shared_base.get()) {
+ EXPECT_EQ(1234, static_cast<EventCounter*>(_shared_base.get())->magic());
+ }
+ _shared = static_cast<DerivedCounter*>(_shared_base.get());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that wp can be copied in
+ _weak = _shared;
+ EXPECT_TRUE(u.set(_weak));
+
+ _weak.clear();
+
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.promote(), _shared);
+
+ // we can remove a weak pointer multiple times
+ _weak.clear();
+ EXPECT_TRUE(u.find(&_weak));
+ EXPECT_EQ(_weak.promote(), _shared);
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ // test that weak pointer can be set and removed as base type (but removed as derived only
+ // if it was set as derived type)
+ _weak = _shared;
+ EXPECT_TRUE(u.set(_weak));
+ EXPECT_TRUE(u.find(&_weak_base));
+ EXPECT_TRUE(_weak_base.promote().get() == _shared.get());
+
+ _weak.clear();
+ _shared.clear();
+ EXPECT_TRUE(u.clear());
+ EXPECT_FALSE(u.find(&_int));
+ EXPECT_FALSE(u.find(&_shared));
+ EXPECT_FALSE(u.find(&_weak));
+ EXPECT_FALSE(u.find(&_shared_base));
+ EXPECT_FALSE(u.find(&_weak_base));
+
+ EXPECT_TRUE(u.set(_weak_base)); // now u_ also contains it as base class
+ EXPECT_FALSE(u.find(&_weak)); // cannot get it as derived type
+ EXPECT_FALSE(_weak.promote().get());
+ _weak_base.clear();
+ EXPECT_TRUE(u.find(&_weak_base)); // can still get it as base type
+ EXPECT_TRUE(_weak_base.promote().get());
+ if (_weak_base.promote().get()) {
+ EXPECT_EQ(1234, static_cast<EventCounter*>(_weak_base.promote().get())->magic());
+ }
+};
+
+} // namespace android
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
index e7598ca..d741c6f 100644
--- a/media/libstagefright/foundation/tests/Android.mk
+++ b/media/libstagefright/foundation/tests/Android.mk
@@ -8,12 +8,14 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
+ AData_test.cpp \
Flagged_test.cpp \
TypeTraits_test.cpp \
Utils_test.cpp \
LOCAL_SHARED_LIBRARIES := \
libstagefright_foundation \
+ libutils \
LOCAL_C_INCLUDES := \
frameworks/av/include \
diff --git a/media/libstagefright/foundation/tests/TypeTraits_test.cpp b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
index 9fba435..1e2049d 100644
--- a/media/libstagefright/foundation/tests/TypeTraits_test.cpp
+++ b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
@@ -32,6 +32,9 @@
// =========== basic sanity tests for type-support templates
TEST_F(TypeTraitsTest, StaticTests) {
+
+ // ============ is_integral_or_enum
+
static_assert(!std::is_integral<A>::value, "enums should not be integral");
static_assert(!std::is_integral<UA>::value, "enums should not be integral");
static_assert(!std::is_integral<IA>::value, "enums should not be integral");
@@ -42,6 +45,8 @@
static_assert(is_integral_or_enum<unsigned>::value, "unsigned ints should be integral_or_enum");
static_assert(!is_integral_or_enum<float>::value, "floats should not be integral_or_enum");
+ // ============ is_unsigned_integral
+
static_assert(!std::is_unsigned<UA>::value,
"unsigned enums should not be unsigned");
static_assert(!std::is_unsigned<IA>::value,
@@ -61,6 +66,8 @@
static_assert(!is_unsigned_integral<float>::value,
"floats should not be unsigned_integral");
+ // ============ is_signed_integral
+
static_assert(!std::is_signed<UA>::value,
"unsigned enums should not be signed");
static_assert(!std::is_signed<IA>::value,
@@ -80,6 +87,8 @@
static_assert(!is_signed_integral<float>::value,
"floats should not be signed_integral");
+ // ============ underlying_integral_type
+
static_assert(std::is_same<uint64_t, typename underlying_integral_type<uint64_t>::type>::value,
"underlying integral type of uint64_t should be uint64_t");
static_assert(std::is_same<uint32_t, typename underlying_integral_type<UA>::type>::value,
@@ -91,6 +100,68 @@
//typedef underlying_integral_type<float>::type no_type;
static_assert(std::is_same<void, typename underlying_integral_type<float, void>::type>::value,
"underlying integral type of float cannot be specified");
+
+ // ============ is_one_of
+
+ static_assert(!is_one_of<int>::value, "int shouldn't be one of {}");
+ static_assert(!is_one_of<int, unsigned>::value, "int shouldn't be one of {unsigned}");
+ static_assert(!is_one_of<int, unsigned, float>::value,
+ "int shouldn't be one of {unsigned, float}");
+ static_assert(is_one_of<int, int>::value, "int should be one of {int}");
+ static_assert(is_one_of<int, int, float>::value, "int should be one of {int, float}");
+ static_assert(is_one_of<int, float, int>::value, "int should be one of {float, int}");
+ static_assert(is_one_of<int, float, int, unsigned>::value,
+ "int should be one of {float, int, unsigned}");
+ static_assert(is_one_of<int, float, unsigned, int>::value,
+ "int should be one of {float, unsigned, int}");
+ static_assert(!is_one_of<int, int&>::value, "int shouldn't be one of {int&}");
+
+ // ============ are_unique
+
+ static_assert(are_unique<>::value, "{} should be unique");
+ static_assert(are_unique<int>::value, "{int} should be unique");
+ static_assert(are_unique<int, float>::value, "{int, float} should be unique");
+ static_assert(!are_unique<int, int>::value, "{int, int} shouldn't be unique");
+ static_assert(!are_unique<int, float, int>::value, "{int, float, int} shouldn't be unique");
+ static_assert(!are_unique<float, int, int>::value, "{float, int, int} shouldn't be unique");
+ static_assert(!are_unique<int, int, float>::value, "{int, int, float} shouldn't be unique");
+
+ // ============ find_first
+
+ static_assert(find_first<int>::index == 0, "int is not in {}");
+ static_assert(find_first<int, unsigned>::index == 0, "int is not in {unsigned}");
+ static_assert(find_first<int, unsigned, float>::index == 0, "int is not in {unsigned, float}");
+ static_assert(find_first<int, int>::index == 1, "int is 1st in {int}");
+ static_assert(find_first<int, int, float>::index == 1, "int is 1st in {int, float}");
+ static_assert(find_first<int, float, int>::index == 2, "int is 2nd in {float, int}");
+ static_assert(find_first<int, float, int, unsigned>::index == 2,
+ "int is 2nd in {float, int, unsigned}");
+ static_assert(find_first<int, float, int, unsigned>::index == 2,
+ "int is 2nd and 3rd in {float, int, int, unsigned}");
+ static_assert(find_first<int, float, unsigned, int>::index == 3,
+ "int is 3rd in {float, unsigned, int}");
+ static_assert(find_first<int, int&>::index == 0, "int is not in {int&}");
+
+ // ============ find_first_convertible_to
+
+ static_assert(find_first_convertible_to<int>::index == 0, "int is not convertible to {}");
+ static_assert(find_first_convertible_to<int, unsigned*>::index == 0,
+ "int is not convertible to {unsigned*}");
+ static_assert(find_first_convertible_to<int, unsigned*, float&>::index == 0,
+ "int is not convertible to {unsigned, float&}");
+ static_assert(find_first_convertible_to<int, int>::index == 1, "int is convertible to {int}");
+ static_assert(find_first_convertible_to<int, unsigned, int>::index == 1,
+ "int is convertible to 1st of {unsigned, int}");
+ static_assert(find_first_convertible_to<int, int&, float>::index == 2,
+ "int is convertible to 2nd of {int&, float}");
+ static_assert(find_first_convertible_to<float, float*, int, unsigned>::index == 2,
+ "float is convertible to 2nd of {float*, int, unsigned}");
+ static_assert(find_first_convertible_to<float, void, float[1], int>::index == 3,
+ "int is 3rd convertible to {void, float[], int}");
+ static_assert(find_first_convertible_to<int&, const int&>::index == 1,
+ "int& is convertible to {const int&}");
+ static_assert(find_first_convertible_to<const int&, int&>::index == 0,
+ "const int& is not convertible to {int&}");
}
} // namespace android
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index b2f4188..442a3ff 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -154,8 +154,6 @@
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
-
for (int i = 1; i < argc; ++i) {
scan(argv[i]);
}
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
new file mode 100644
index 0000000..d52ce53
--- /dev/null
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_BUFFER_CHANNEL_H_
+
+#define A_BUFFER_CHANNEL_H_
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <media/openmax/OMX_Types.h>
+#include <media/stagefright/CodecBase.h>
+#include <media/ICrypto.h>
+#include <media/IOMX.h>
+
+namespace android {
+
+/**
+ * BufferChannelBase implementation for ACodec.
+ */
+class ACodecBufferChannel : public BufferChannelBase {
+public:
+ struct BufferAndId {
+ sp<MediaCodecBuffer> mBuffer;
+ IOMX::buffer_id mBufferId;
+ };
+
+ struct BufferInfo {
+ BufferInfo(
+ const sp<MediaCodecBuffer> &buffer,
+ IOMX::buffer_id bufferId,
+ const sp<IMemory> &sharedEncryptedBuffer);
+
+ BufferInfo() = delete;
+
+ // Buffer facing MediaCodec and its clients.
+ const sp<MediaCodecBuffer> mClientBuffer;
+ // Buffer facing CodecBase.
+ const sp<MediaCodecBuffer> mCodecBuffer;
+ // OMX buffer ID.
+ const IOMX::buffer_id mBufferId;
+ // Encrypted buffer in case of secure input.
+ const sp<IMemory> mSharedEncryptedBuffer;
+ };
+
+ ACodecBufferChannel(
+ const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained);
+ virtual ~ACodecBufferChannel() = default;
+
+ // BufferChannelBase interface
+ virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
+ virtual status_t queueSecureInputBuffer(
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ AString *errorDetailMsg) override;
+ virtual status_t renderOutputBuffer(
+ const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
+ virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
+ virtual void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+ virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+
+ // Methods below are interface for ACodec to use.
+
+ /**
+ * Set input buffer array.
+ *
+ * @param array Newly allocated buffers. Empty if buffers are
+ * deallocated.
+ */
+ void setInputBufferArray(const std::vector<BufferAndId> &array);
+ /**
+ * Set output buffer array.
+ *
+ * @param array Newly allocated buffers. Empty if buffers are
+ * deallocated.
+ */
+ void setOutputBufferArray(const std::vector<BufferAndId> &array);
+ /**
+ * Request MediaCodec to fill the specified input buffer.
+ *
+ * @param bufferId ID of the buffer, assigned by underlying component.
+ */
+ void fillThisBuffer(IOMX::buffer_id bufferID);
+ /**
+ * Request MediaCodec to drain the specified output buffer.
+ *
+ * @param bufferId ID of the buffer, assigned by underlying component.
+ * @param omxFlags flags associated with this buffer (e.g. EOS).
+ */
+ void drainThisBuffer(IOMX::buffer_id bufferID, OMX_U32 omxFlags);
+
+private:
+ const sp<AMessage> mInputBufferFilled;
+ const sp<AMessage> mOutputBufferDrained;
+
+ sp<MemoryDealer> mDealer;
+
+ // These should only be accessed via std::atomic_* functions.
+ //
+ // Note on thread safety: since the vector and BufferInfo are const, it's
+ // safe to read them at any thread once the shared_ptr object is atomically
+ // obtained. Inside BufferInfo, mBufferId and mSharedEncryptedBuffer are
+ // immutable objects. We write internal states of mClient/CodecBuffer when
+ // the caller has given up the reference, so that access is also safe.
+ std::shared_ptr<const std::vector<const BufferInfo>> mInputBuffers;
+ std::shared_ptr<const std::vector<const BufferInfo>> mOutputBuffers;
+};
+
+} // namespace android
+
+#endif // A_BUFFER_CHANNEL_H_
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/include/OMX.h b/media/libstagefright/include/OMX.h
index 9edd0de..5b22a2f 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -21,12 +21,15 @@
#include <utils/threads.h>
#include <utils/KeyedVector.h>
+#include "OmxNodeOwner.h"
+
namespace android {
struct OMXMaster;
struct OMXNodeInstance;
class OMX : public BnOMX,
+ public OmxNodeOwner,
public IBinder::DeathRecipient {
public:
OMX();
@@ -43,7 +46,7 @@
virtual void binderDied(const wp<IBinder> &the_late_who);
- status_t freeNode(const sp<OMXNodeInstance>& instance);
+ virtual status_t freeNode(const sp<OMXNodeInstance>& instance);
protected:
virtual ~OMX();
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index ab12a86..ca24c2f 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -18,11 +18,12 @@
#define OMX_NODE_INSTANCE_H_
-#include "OMX.h"
-
+#include <media/IOMX.h>
#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <utils/KeyedVector.h>
+#include <utils/SortedVector.h>
+#include "OmxNodeOwner.h"
namespace android {
class IOMXBufferSource;
@@ -32,7 +33,7 @@
struct OMXNodeInstance : public BnOMXNode {
OMXNodeInstance(
- OMX *owner, const sp<IOMXObserver> &observer, const char *name);
+ OmxNodeOwner *owner, const sp<IOMXObserver> &observer, const char *name);
void setHandle(OMX_HANDLETYPE handle);
@@ -66,7 +67,7 @@
const sp<IOMXBufferSource> &bufferSource);
status_t allocateSecureBuffer(
- OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
+ OMX_U32 portIndex, size_t size, IOMX::buffer_id *buffer,
void **buffer_data, sp<NativeHandle> *native_handle);
status_t useBuffer(
@@ -106,7 +107,7 @@
Mutex mLock;
- OMX *mOwner;
+ OmxNodeOwner *mOwner;
OMX_HANDLETYPE mHandle;
sp<IOMXObserver> mObserver;
sp<CallbackDispatcher> mDispatcher;
@@ -125,14 +126,14 @@
struct ActiveBuffer {
OMX_U32 mPortIndex;
- OMX::buffer_id mID;
+ IOMX::buffer_id mID;
};
Vector<ActiveBuffer> mActiveBuffers;
// for buffer ptr to buffer id translation
Mutex mBufferIDLock;
uint32_t mBufferIDCount;
- KeyedVector<OMX::buffer_id, OMX_BUFFERHEADERTYPE *> mBufferIDToBufferHeader;
- KeyedVector<OMX_BUFFERHEADERTYPE *, OMX::buffer_id> mBufferHeaderToBufferID;
+ KeyedVector<IOMX::buffer_id, OMX_BUFFERHEADERTYPE *> mBufferIDToBufferHeader;
+ KeyedVector<OMX_BUFFERHEADERTYPE *, IOMX::buffer_id> mBufferHeaderToBufferID;
bool mLegacyAdaptiveExperiment;
IOMX::PortMode mPortMode[2];
@@ -167,45 +168,45 @@
~OMXNodeInstance();
- void addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id);
- void removeActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id);
+ void addActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id);
+ void removeActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id);
void freeActiveBuffers();
// For buffer id management
- OMX::buffer_id makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
- OMX_BUFFERHEADERTYPE *findBufferHeader(OMX::buffer_id buffer, OMX_U32 portIndex);
- OMX::buffer_id findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
- void invalidateBufferID(OMX::buffer_id buffer);
+ IOMX::buffer_id makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
+ OMX_BUFFERHEADERTYPE *findBufferHeader(IOMX::buffer_id buffer, OMX_U32 portIndex);
+ IOMX::buffer_id findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
+ void invalidateBufferID(IOMX::buffer_id buffer);
bool isProhibitedIndex_l(OMX_INDEXTYPE index);
status_t useBuffer_l(
OMX_U32 portIndex, const sp<IMemory> ¶ms,
- OMX::buffer_id *buffer);
+ IOMX::buffer_id *buffer);
status_t useGraphicBuffer_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id *buffer);
+ IOMX::buffer_id *buffer);
status_t useGraphicBufferWithMetadata_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id *buffer);
+ IOMX::buffer_id *buffer);
status_t useGraphicBuffer2_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id *buffer);
+ IOMX::buffer_id *buffer);
status_t emptyBuffer_l(
- OMX::buffer_id buffer,
+ IOMX::buffer_id buffer,
OMX_U32 rangeOffset, OMX_U32 rangeLength,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
status_t emptyGraphicBuffer_l(
- OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
+ IOMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
status_t emptyNativeHandleBuffer_l(
- OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+ IOMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
status_t emptyBuffer_l(
@@ -252,11 +253,11 @@
// buffer.)
status_t updateGraphicBufferInMeta_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+ IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
status_t updateNativeHandleInMeta_l(
OMX_U32 portIndex, const sp<NativeHandle> &nativeHandle,
- OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+ IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
sp<IOMXBufferSource> getBufferSource();
void setBufferSource(const sp<IOMXBufferSource> &bufferSource);
diff --git a/media/libstagefright/include/OmxNodeOwner.h b/media/libstagefright/include/OmxNodeOwner.h
new file mode 100644
index 0000000..64ec7f7
--- /dev/null
+++ b/media/libstagefright/include/OmxNodeOwner.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_NODE_OWNER_H_
+
+#define OMX_NODE_OWNER_H_
+
+namespace android {
+
+struct OMXNodeInstance;
+
+/**
+ * This struct is needed to separate OMX from OMXNodeInstance.
+ *
+ * TODO: This might not be needed after Treble transition is complete.
+ */
+struct OmxNodeOwner {
+ virtual status_t freeNode(const sp<OMXNodeInstance> &instance) = 0;
+ virtual ~OmxNodeOwner() {}
+};
+
+}
+
+#endif // OMX_NODE_OWNER_H_
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
deleted file mode 100644
index 65cb99a..0000000
--- a/media/libstagefright/include/WVMExtractor.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef WVM_EXTRACTOR_H_
-
-#define WVM_EXTRACTOR_H_
-
-#include <media/stagefright/MediaExtractor.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-struct AMessage;
-class String8;
-class DataSource;
-
-class WVMLoadableExtractor : public MediaExtractor {
-public:
- WVMLoadableExtractor() {}
- virtual ~WVMLoadableExtractor() {}
-
- virtual int64_t getCachedDurationUs(status_t *finalStatus) = 0;
- virtual status_t getError() = 0;
- virtual status_t getEstimatedBandwidthKbps(int32_t *kbps) = 0;
- virtual void setAdaptiveStreamingMode(bool adaptive) = 0;
- virtual void setCryptoPluginMode(bool cryptoPluginMode) = 0;
- virtual void setError(status_t err) = 0;
- virtual void setUID(uid_t uid) = 0;
-};
-
-class WVMExtractor : public MediaExtractor {
-public:
- explicit WVMExtractor(const sp<DataSource> &source);
-
- virtual size_t countTracks();
- virtual sp<IMediaSource> getTrack(size_t index);
- virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
- virtual sp<MetaData> getMetaData();
- virtual void setUID(uid_t uid);
-
- // Return the amount of data cached from the current
- // playback positiion (in us).
- // While more data is still being fetched *finalStatus == OK,
- // Once fetching is completed (no more data available), *finalStatus != OK
- // If fetching completed normally (i.e. reached EOS instead of IO error)
- // *finalStatus == ERROR_END_OF_STREAM
- int64_t getCachedDurationUs(status_t *finalStatus);
-
- // Return the current estimated bandwidth
- status_t getEstimatedBandwidthKbps(int32_t *kbps);
-
- // Set to use adaptive streaming mode by the WV component.
- // If adaptive == true, adaptive streaming mode will be used.
- // Default mode is non-adaptive streaming mode.
- // Should set to use adaptive streaming mode only if widevine:// protocol
- // is used.
- void setAdaptiveStreamingMode(bool adaptive);
-
- // setCryptoPluginMode(true) to select crypto plugin mode.
- // In this mode, the extractor returns encrypted data for use
- // with the MediaCodec model, which handles the decryption in the
- // codec.
- void setCryptoPluginMode(bool cryptoPluginMode);
-
- static bool getVendorLibHandle();
-
- status_t getError();
-
- void setError(status_t err);
-
-protected:
- virtual ~WVMExtractor();
-
-private:
- sp<DataSource> mDataSource;
- sp<WVMLoadableExtractor> mImpl;
-
- WVMExtractor(const WVMExtractor &);
- WVMExtractor &operator=(const WVMExtractor &);
-};
-
-bool SniffWVM(
- const sp<DataSource> &source, String8 *mimeType, float *confidence,
- sp<AMessage> *);
-
-} // namespace android
-
-#endif // DRM_EXTRACTOR_H_
-
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 fb5e079..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(
@@ -212,23 +215,22 @@
- mSeekSyncPoints->keyAt(0);
off64_t diffOffset = mSeekSyncPoints->valueAt(prevSyncSize - 1)
- mSeekSyncPoints->valueAt(0);
- durationUs = size * diffUs / diffOffset;
- durations.push_back(durationUs);
+ int64_t currentDurationUs = size * diffUs / diffOffset;
+ durations.push_back(currentDurationUs);
if (durations.size() > 5) {
durations.erase(durations.begin());
int64_t min = *durations.begin();
int64_t max = *durations.begin();
- for (List<int64_t>::iterator i = durations.begin();
- i != durations.end();
- ++i) {
- if (min > *i) {
- min = *i;
+ for (auto duration : durations) {
+ if (min > duration) {
+ min = duration;
}
- if (max < *i) {
- max = *i;
+ if (max < duration) {
+ max = duration;
}
}
if (max - min < 500 * 1000) {
+ durationUs = currentDurationUs;
break;
}
}
@@ -244,6 +246,8 @@
const sp<MetaData> meta = impl->getFormat();
meta->setInt64(kKeyDuration, durationUs);
impl->setFormat(meta);
+ } else {
+ estimateDurationsFromTimesUsAtEnd();
}
}
@@ -302,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/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index be4a932..c20e9fc 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -332,7 +332,7 @@
////////////////////////////////////////////////////////////////////////////////
OMXNodeInstance::OMXNodeInstance(
- OMX *owner, const sp<IOMXObserver> &observer, const char *name)
+ OmxNodeOwner *owner, const sp<IOMXObserver> &observer, const char *name)
: mOwner(owner),
mHandle(NULL),
mObserver(observer),
@@ -799,9 +799,7 @@
if (!graphic) {
// Extension not supported, check for manual override with system property
// This is a temporary workaround until partners support the OMX extension
- char value[PROPERTY_VALUE_MAX];
- if (property_get("media.mediadrmservice.enable", value, NULL)
- && (!strcmp("1", value) || !strcasecmp("true", value))) {
+ if (property_get_bool("media.mediadrmservice.enable", false)) {
CLOG_CONFIG(enableNativeBuffers, "system property override: using native-handles");
mSecureBufferType[portIndex] = kSecureBufferTypeNativeHandle;
} else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) {
@@ -1010,7 +1008,7 @@
}
status_t OMXNodeInstance::useBuffer(
- OMX_U32 portIndex, const OMXBuffer &omxBuffer, OMX::buffer_id *buffer) {
+ OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer) {
if (buffer == NULL) {
ALOGE("b/25884056");
return BAD_VALUE;
@@ -1040,7 +1038,7 @@
}
status_t OMXNodeInstance::useBuffer_l(
- OMX_U32 portIndex, const sp<IMemory> ¶ms, OMX::buffer_id *buffer) {
+ OMX_U32 portIndex, const sp<IMemory> ¶ms, IOMX::buffer_id *buffer) {
BufferMeta *buffer_meta;
OMX_BUFFERHEADERTYPE *header;
OMX_ERRORTYPE err = OMX_ErrorNone;
@@ -1147,7 +1145,7 @@
status_t OMXNodeInstance::useGraphicBuffer2_l(
OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
- OMX::buffer_id *buffer) {
+ IOMX::buffer_id *buffer) {
if (graphicBuffer == NULL || buffer == NULL) {
ALOGE("b/25884056");
return BAD_VALUE;
@@ -1203,7 +1201,7 @@
// can be renamed to useGraphicBuffer.
status_t OMXNodeInstance::useGraphicBuffer_l(
OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
- OMX::buffer_id *buffer) {
+ IOMX::buffer_id *buffer) {
if (graphicBuffer == NULL || buffer == NULL) {
ALOGE("b/25884056");
return BAD_VALUE;
@@ -1273,7 +1271,7 @@
status_t OMXNodeInstance::useGraphicBufferWithMetadata_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id *buffer) {
+ IOMX::buffer_id *buffer) {
if (portIndex != kPortIndexOutput) {
return BAD_VALUE;
}
@@ -1296,7 +1294,7 @@
status_t OMXNodeInstance::updateGraphicBufferInMeta_l(
OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
- OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
+ IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
// No need to check |graphicBuffer| since NULL is valid for it as below.
if (header == NULL) {
ALOGE("b/25884056");
@@ -1336,7 +1334,7 @@
status_t OMXNodeInstance::updateNativeHandleInMeta_l(
OMX_U32 portIndex, const sp<NativeHandle>& nativeHandle,
- OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
+ IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
// No need to check |nativeHandle| since NULL is valid for it as below.
if (header == NULL) {
ALOGE("b/25884056");
@@ -1423,7 +1421,7 @@
}
status_t OMXNodeInstance::allocateSecureBuffer(
- OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
+ OMX_U32 portIndex, size_t size, IOMX::buffer_id *buffer,
void **buffer_data, sp<NativeHandle> *native_handle) {
if (buffer == NULL || buffer_data == NULL || native_handle == NULL) {
ALOGE("b/25884056");
@@ -1481,7 +1479,7 @@
}
status_t OMXNodeInstance::freeBuffer(
- OMX_U32 portIndex, OMX::buffer_id buffer) {
+ OMX_U32 portIndex, IOMX::buffer_id buffer) {
Mutex::Autolock autoLock(mLock);
CLOG_BUFFER(freeBuffer, "%s:%u %#x", portString(portIndex), portIndex, buffer);
@@ -1505,7 +1503,7 @@
}
status_t OMXNodeInstance::fillBuffer(
- OMX::buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
+ IOMX::buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
Mutex::Autolock autoLock(mLock);
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexOutput);
@@ -1579,7 +1577,7 @@
}
status_t OMXNodeInstance::emptyBuffer_l(
- OMX::buffer_id buffer,
+ IOMX::buffer_id buffer,
OMX_U32 rangeOffset, OMX_U32 rangeLength,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
@@ -1731,7 +1729,7 @@
// like emptyBuffer, but the data is already in header->pBuffer
status_t OMXNodeInstance::emptyGraphicBuffer_l(
- OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
+ IOMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
if (header == NULL) {
@@ -1803,7 +1801,7 @@
}
status_t OMXNodeInstance::emptyNativeHandleBuffer_l(
- OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+ IOMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
if (header == NULL) {
@@ -2199,7 +2197,7 @@
return OMX_ErrorNone;
}
-void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) {
+void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id) {
ActiveBuffer active;
active.mPortIndex = portIndex;
active.mID = id;
@@ -2211,7 +2209,7 @@
}
void OMXNodeInstance::removeActiveBuffer(
- OMX_U32 portIndex, OMX::buffer_id id) {
+ OMX_U32 portIndex, IOMX::buffer_id id) {
for (size_t i = 0; i < mActiveBuffers.size(); ++i) {
if (mActiveBuffers[i].mPortIndex == portIndex
&& mActiveBuffers[i].mID == id) {
@@ -2236,17 +2234,17 @@
}
}
-OMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+IOMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
if (bufferHeader == NULL) {
return 0;
}
Mutex::Autolock autoLock(mBufferIDLock);
- OMX::buffer_id buffer;
+ IOMX::buffer_id buffer;
do { // handle the very unlikely case of ID overflow
if (++mBufferIDCount == 0) {
++mBufferIDCount;
}
- buffer = (OMX::buffer_id)mBufferIDCount;
+ buffer = (IOMX::buffer_id)mBufferIDCount;
} while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0);
mBufferIDToBufferHeader.add(buffer, bufferHeader);
mBufferHeaderToBufferID.add(bufferHeader, buffer);
@@ -2254,7 +2252,7 @@
}
OMX_BUFFERHEADERTYPE *OMXNodeInstance::findBufferHeader(
- OMX::buffer_id buffer, OMX_U32 portIndex) {
+ IOMX::buffer_id buffer, OMX_U32 portIndex) {
if (buffer == 0) {
return NULL;
}
@@ -2275,7 +2273,7 @@
return header;
}
-OMX::buffer_id OMXNodeInstance::findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+IOMX::buffer_id OMXNodeInstance::findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
if (bufferHeader == NULL) {
return 0;
}
@@ -2288,7 +2286,7 @@
return mBufferHeaderToBufferID.valueAt(index);
}
-void OMXNodeInstance::invalidateBufferID(OMX::buffer_id buffer) {
+void OMXNodeInstance::invalidateBufferID(IOMX::buffer_id buffer) {
if (buffer == 0) {
return;
}
diff --git a/media/libstagefright/omx/hal/1.0/Android.mk b/media/libstagefright/omx/hal/1.0/Android.mk
new file mode 100644
index 0000000..b84d74b
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.media.omx@1.0-impl
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+ GraphicBufferSource.cpp \
+ Omx.cpp \
+ OmxBufferSource.cpp \
+ OmxNode.cpp \
+ OmxObserver.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ libutils \
+ android.hardware.media.omx@1.0 \
+ android.hardware.graphics.common@1.0 \
+ android.hardware.media@1.0 \
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/omx/hal/1.0/GraphicBufferSource.cpp b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.cpp
new file mode 100644
index 0000000..6a43883
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.cpp
@@ -0,0 +1,66 @@
+#include "GraphicBufferSource.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IGraphicBufferSource follow.
+Return<Status> GraphicBufferSource::configure(const sp<IOmxNode>& omxNode, Dataspace dataspace) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setSuspend(bool suspend) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setMaxFps(float maxFps) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setStartTimeUs(int64_t startTimeUs) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setColorAspects(const ColorAspects& aspects) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setTimeOffsetUs(int64_t timeOffsetUs) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::signalEndOfInputStream() {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+
+IGraphicBufferSource* HIDL_FETCH_IGraphicBufferSource(const char* /* name */) {
+ return new GraphicBufferSource();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/GraphicBufferSource.h b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.h
new file mode 100644
index 0000000..fb8c89d
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.h
@@ -0,0 +1,50 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__GRAPHICBUFFERSOURCE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__GRAPHICBUFFERSOURCE_H
+
+#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+using ::android::hardware::media::omx::V1_0::ColorAspects;
+using ::android::hardware::media::omx::V1_0::IGraphicBufferSource;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct GraphicBufferSource : public IGraphicBufferSource {
+ // Methods from ::android::hardware::media::omx::V1_0::IGraphicBufferSource follow.
+ Return<Status> configure(const sp<IOmxNode>& omxNode, Dataspace dataspace) override;
+ Return<Status> setSuspend(bool suspend) override;
+ Return<Status> setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
+ Return<Status> setMaxFps(float maxFps) override;
+ Return<Status> setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ Return<Status> setStartTimeUs(int64_t startTimeUs) override;
+ Return<Status> setColorAspects(const ColorAspects& aspects) override;
+ Return<Status> setTimeOffsetUs(int64_t timeOffsetUs) override;
+ Return<Status> signalEndOfInputStream() override;
+
+};
+
+extern "C" IGraphicBufferSource* HIDL_FETCH_IGraphicBufferSource(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__GRAPHICBUFFERSOURCE_H
diff --git a/media/libstagefright/omx/hal/1.0/Omx.cpp b/media/libstagefright/omx/hal/1.0/Omx.cpp
new file mode 100644
index 0000000..68040eb
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/Omx.cpp
@@ -0,0 +1,31 @@
+#include "Omx.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmx follow.
+Return<void> Omx::listNodes(listNodes_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<void> Omx::allocateNode(const hidl_string& name, const sp<IOmxObserver>& observer, allocateNode_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+
+IOmx* HIDL_FETCH_IOmx(const char* /* name */) {
+ return new Omx();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/Omx.h b/media/libstagefright/omx/hal/1.0/Omx.h
new file mode 100644
index 0000000..02a0a01
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/Omx.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMX_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMX_H
+
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmx;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::IOmxObserver;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Omx : public IOmx {
+ // Methods from ::android::hardware::media::omx::V1_0::IOmx follow.
+ Return<void> listNodes(listNodes_cb _hidl_cb) override;
+ Return<void> allocateNode(const hidl_string& name, const sp<IOmxObserver>& observer, allocateNode_cb _hidl_cb) override;
+
+};
+
+extern "C" IOmx* HIDL_FETCH_IOmx(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMX_H
diff --git a/media/libstagefright/omx/hal/1.0/OmxBufferSource.cpp b/media/libstagefright/omx/hal/1.0/OmxBufferSource.cpp
new file mode 100644
index 0000000..2885d0d
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxBufferSource.cpp
@@ -0,0 +1,46 @@
+#include "OmxBufferSource.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmxBufferSource follow.
+Return<void> OmxBufferSource::onOmxExecuting() {
+ // TODO implement
+ return Void();
+}
+
+Return<void> OmxBufferSource::onOmxIdle() {
+ // TODO implement
+ return Void();
+}
+
+Return<void> OmxBufferSource::onOmxLoaded() {
+ // TODO implement
+ return Void();
+}
+
+Return<void> OmxBufferSource::onInputBufferAdded(uint32_t buffer) {
+ // TODO implement
+ return Void();
+}
+
+Return<void> OmxBufferSource::onInputBufferEmptied(uint32_t buffer, const hidl_handle& fence) {
+ // TODO implement
+ return Void();
+}
+
+
+IOmxBufferSource* HIDL_FETCH_IOmxBufferSource(const char* /* name */) {
+ return new OmxBufferSource();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/OmxBufferSource.h b/media/libstagefright/omx/hal/1.0/OmxBufferSource.h
new file mode 100644
index 0000000..5e4855b
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxBufferSource.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXBUFFERSOURCE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXBUFFERSOURCE_H
+
+#include <android/hardware/media/omx/1.0/IOmxBufferSource.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmxBufferSource;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct OmxBufferSource : public IOmxBufferSource {
+ // Methods from ::android::hardware::media::omx::V1_0::IOmxBufferSource follow.
+ Return<void> onOmxExecuting() override;
+ Return<void> onOmxIdle() override;
+ Return<void> onOmxLoaded() override;
+ Return<void> onInputBufferAdded(uint32_t buffer) override;
+ Return<void> onInputBufferEmptied(uint32_t buffer, const hidl_handle& fence) override;
+
+};
+
+extern "C" IOmxBufferSource* HIDL_FETCH_IOmxBufferSource(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXBUFFERSOURCE_H
diff --git a/media/libstagefright/omx/hal/1.0/OmxNode.cpp b/media/libstagefright/omx/hal/1.0/OmxNode.cpp
new file mode 100644
index 0000000..ba0e722
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxNode.cpp
@@ -0,0 +1,111 @@
+#include "OmxNode.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmxNode follow.
+Return<Status> OmxNode::freeNode() {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::sendCommand(uint32_t cmd, const hidl_vec<uint8_t>& info) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::getParameter(uint32_t index, const hidl_vec<uint8_t>& inParams, getParameter_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<Status> OmxNode::setParameter(uint32_t index, const hidl_vec<uint8_t>& params) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::getConfig(uint32_t index, const hidl_vec<uint8_t>& inConfig, getConfig_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<Status> OmxNode::setConfig(uint32_t index, const hidl_vec<uint8_t>& config) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::setPortMode(uint32_t portIndex, PortMode mode) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::prepareForAdaptivePlayback(uint32_t portIndex, bool enable, uint32_t maxFrameWidth, uint32_t maxFrameHeight) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::configureVideoTunnelMode(uint32_t portIndex, bool tunneled, uint32_t audioHwSync, configureVideoTunnelMode_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<void> OmxNode::getGraphicBufferUsage(uint32_t portIndex, getGraphicBufferUsage_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<Status> OmxNode::setInputSurface(const sp<IOmxBufferSource>& bufferSource) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::allocateSecureBuffer(uint32_t portIndex, uint64_t size, allocateSecureBuffer_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<void> OmxNode::useBuffer(uint32_t portIndex, const CodecBuffer& omxBuffer, useBuffer_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<Status> OmxNode::freeBuffer(uint32_t portIndex, uint32_t buffer) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::fillBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, const hidl_handle& fence) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::emptyBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, uint32_t flags, uint64_t timestampUs, const hidl_handle& fence) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::getExtensionIndex(const hidl_string& parameterName, getExtensionIndex_cb _hidl_cb) {
+ // TODO implement
+ return Void();
+}
+
+Return<Status> OmxNode::dispatchMessage(const Message& msg) {
+ // TODO implement
+ return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+
+IOmxNode* HIDL_FETCH_IOmxNode(const char* /* name */) {
+ return new OmxNode();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/OmxNode.h b/media/libstagefright/omx/hal/1.0/OmxNode.h
new file mode 100644
index 0000000..dd9e5b4
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxNode.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXNODE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXNODE_H
+
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::CodecBuffer;
+using ::android::hardware::media::omx::V1_0::IOmxBufferSource;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::Message;
+using ::android::hardware::media::omx::V1_0::PortMode;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct OmxNode : public IOmxNode {
+ // Methods from ::android::hardware::media::omx::V1_0::IOmxNode follow.
+ Return<Status> freeNode() override;
+ Return<Status> sendCommand(uint32_t cmd, const hidl_vec<uint8_t>& info) override;
+ Return<void> getParameter(uint32_t index, const hidl_vec<uint8_t>& inParams, getParameter_cb _hidl_cb) override;
+ Return<Status> setParameter(uint32_t index, const hidl_vec<uint8_t>& params) override;
+ Return<void> getConfig(uint32_t index, const hidl_vec<uint8_t>& inConfig, getConfig_cb _hidl_cb) override;
+ Return<Status> setConfig(uint32_t index, const hidl_vec<uint8_t>& config) override;
+ Return<Status> setPortMode(uint32_t portIndex, PortMode mode) override;
+ Return<Status> prepareForAdaptivePlayback(uint32_t portIndex, bool enable, uint32_t maxFrameWidth, uint32_t maxFrameHeight) override;
+ Return<void> configureVideoTunnelMode(uint32_t portIndex, bool tunneled, uint32_t audioHwSync, configureVideoTunnelMode_cb _hidl_cb) override;
+ Return<void> getGraphicBufferUsage(uint32_t portIndex, getGraphicBufferUsage_cb _hidl_cb) override;
+ Return<Status> setInputSurface(const sp<IOmxBufferSource>& bufferSource) override;
+ Return<void> allocateSecureBuffer(uint32_t portIndex, uint64_t size, allocateSecureBuffer_cb _hidl_cb) override;
+ Return<void> useBuffer(uint32_t portIndex, const CodecBuffer& omxBuffer, useBuffer_cb _hidl_cb) override;
+ Return<Status> freeBuffer(uint32_t portIndex, uint32_t buffer) override;
+ Return<Status> fillBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, const hidl_handle& fence) override;
+ Return<Status> emptyBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, uint32_t flags, uint64_t timestampUs, const hidl_handle& fence) override;
+ Return<void> getExtensionIndex(const hidl_string& parameterName, getExtensionIndex_cb _hidl_cb) override;
+ Return<Status> dispatchMessage(const Message& msg) override;
+
+};
+
+extern "C" IOmxNode* HIDL_FETCH_IOmxNode(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXNODE_H
diff --git a/media/libstagefright/omx/hal/1.0/OmxObserver.cpp b/media/libstagefright/omx/hal/1.0/OmxObserver.cpp
new file mode 100644
index 0000000..4e946cd
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxObserver.cpp
@@ -0,0 +1,26 @@
+#include "OmxObserver.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmxObserver follow.
+Return<void> OmxObserver::onMessages(const hidl_vec<Message>& messages) {
+ // TODO implement
+ return Void();
+}
+
+
+IOmxObserver* HIDL_FETCH_IOmxObserver(const char* /* name */) {
+ return new OmxObserver();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/OmxObserver.h b/media/libstagefright/omx/hal/1.0/OmxObserver.h
new file mode 100644
index 0000000..630cae3
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxObserver.h
@@ -0,0 +1,39 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXOBSERVER_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXOBSERVER_H
+
+#include <android/hardware/media/omx/1.0/IOmxObserver.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmxObserver;
+using ::android::hardware::media::omx::V1_0::Message;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct OmxObserver : public IOmxObserver {
+ // Methods from ::android::hardware::media::omx::V1_0::IOmxObserver follow.
+ Return<void> onMessages(const hidl_vec<Message>& messages) override;
+
+};
+
+extern "C" IOmxObserver* HIDL_FETCH_IOmxObserver(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXOBSERVER_H
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 935d7bf..1ce5d1a 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -800,7 +800,6 @@
using namespace android;
android::ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
const char *me = argv[0];
diff --git a/media/libstagefright/rtsp/rtp_test.cpp b/media/libstagefright/rtsp/rtp_test.cpp
index 24f529b..e612a8d 100644
--- a/media/libstagefright/rtsp/rtp_test.cpp
+++ b/media/libstagefright/rtsp/rtp_test.cpp
@@ -37,8 +37,6 @@
int main(int argc, char **argv) {
android::ProcessState::self()->startThreadPool();
- DataSource::RegisterDefaultSniffers();
-
const char *rtpFilename = NULL;
const char *rtcpFilename = NULL;
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 3587cb9..f1ecca0 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -36,7 +36,6 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/AudioSource.h>
-#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -748,8 +747,6 @@
status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer(
bool enableAudio, bool enableVideo) {
- DataSource::RegisterDefaultSniffers();
-
mExtractor = new NuMediaExtractor;
status_t err = mExtractor->setDataSource(
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index f0a4ded..9cda8dc 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -911,10 +911,8 @@
bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz
- char val[PROPERTY_VALUE_MAX];
if (supportsPCM
- && property_get("media.wfd.use-pcm-audio", val, NULL)
- && (!strcasecmp("true", val) || !strcmp("1", val))) {
+ && property_get_bool("media.wfd.use-pcm-audio", false)) {
ALOGI("Using PCM audio.");
mUsingPCMAudio = true;
} else if (supportsAAC) {
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index ffbfcbb..a4cb66d 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -17,7 +17,6 @@
LOCAL_SHARED_LIBRARIES := \
libresourcemanagerservice \
liblog \
- libcutils \
libmediaplayerservice \
libutils \
libbinder \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 4d2049e..0e70ad9 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1320,7 +1320,7 @@
ALOGV("%d died, releasing its sessions", pid);
size_t num = mAudioSessionRefs.size();
bool removed = false;
- for (size_t i = 0; i< num; ) {
+ for (size_t i = 0; i < num; ) {
AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
ALOGV(" pid %d @ %zu", ref->mPid, i);
if (ref->mPid == pid) {
@@ -2087,9 +2087,10 @@
sp<StreamInHalInterface> inStream;
status_t status = inHwHal->openInputStream(
*input, devices, &halconfig, flags, address.string(), source, &inStream);
- ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d"
+ ALOGV("openInput_l() openInputStream returned input %p, devices %x, SamplingRate %d"
", Format %#x, Channels %x, flags %#x, status %d addr %s",
inStream.get(),
+ devices,
halconfig.sample_rate,
halconfig.format,
halconfig.channel_mask,
@@ -2315,7 +2316,7 @@
}
size_t num = mAudioSessionRefs.size();
- for (size_t i = 0; i< num; i++) {
+ for (size_t i = 0; i < num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
if (ref->mSessionid == audioSession && ref->mPid == caller) {
ref->mCnt++;
@@ -2336,7 +2337,7 @@
caller = pid;
}
size_t num = mAudioSessionRefs.size();
- for (size_t i = 0; i< num; i++) {
+ for (size_t i = 0; i < num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
if (ref->mSessionid == audioSession && ref->mPid == caller) {
ref->mCnt--;
@@ -2354,6 +2355,18 @@
ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
}
+bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession)
+{
+ size_t num = mAudioSessionRefs.size();
+ for (size_t i = 0; i < num; i++) {
+ AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
+ if (ref->mSessionid == audioSession) {
+ return true;
+ }
+ }
+ return false;
+}
+
void AudioFlinger::purgeStaleEffects_l() {
ALOGV("purging stale effects");
@@ -2767,8 +2780,9 @@
sp<Client> client = registerPid(pid);
// create effect on selected output thread
+ bool pinned = (sessionId > AUDIO_SESSION_OUTPUT_MIX) && isSessionAcquired_l(sessionId);
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
- &desc, enabled, &lStatus);
+ &desc, enabled, &lStatus, pinned);
if (handle != 0 && id != NULL) {
*id = handle->id();
}
@@ -2965,7 +2979,7 @@
ALOGV("updateOrphanEffectChains session %d index %zd", session, index);
if (index >= 0) {
sp<EffectChain> chain = mOrphanEffectChains.valueAt(index);
- if (chain->removeEffect_l(effect) == 0) {
+ if (chain->removeEffect_l(effect, true) == 0) {
ALOGV("updateOrphanEffectChains removing effect chain at index %zd", index);
mOrphanEffectChains.removeItemsAt(index);
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e9c0f93..c3bf1f9 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -19,6 +19,7 @@
#define ANDROID_AUDIO_FLINGER_H
#include "Configuration.h"
+#include <deque>
#include <stdint.h>
#include <sys/types.h>
#include <limits.h>
@@ -589,6 +590,7 @@
void removeNotificationClient(pid_t pid);
bool isNonOffloadableGlobalEffectEnabled_l();
void onNonOffloadableGlobalEffectEnable();
+ bool isSessionAcquired_l(audio_session_t audioSession);
// Store an effect chain to mOrphanEffectChains keyed vector.
// Called when a thread exits and effects are still attached to it.
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 582a5e8..945f4b3 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -1611,8 +1611,13 @@
// in == NULL can happen if the track was flushed just after having
// been enabled for mixing.
if (in == NULL || (((uintptr_t)in) & 3)) {
- memset(out, 0, numFrames
- * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat));
+ if ( AUDIO_FORMAT_PCM_FLOAT == t.mMixerFormat ) {
+ memset((char*)fout, 0, numFrames
+ * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat));
+ } else {
+ memset((char*)out, 0, numFrames
+ * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat));
+ }
ALOGE_IF((((uintptr_t)in) & 3),
"process__OneTrack16BitsStereoNoResampling: misaligned buffer"
" %p track %d, channels %d, needs %08x, volume %08x vfl %f vfr %f",
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 3a04651..128eaba 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -60,8 +60,9 @@
const wp<AudioFlinger::EffectChain>& chain,
effect_descriptor_t *desc,
int id,
- audio_session_t sessionId)
- : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
+ audio_session_t sessionId,
+ bool pinned)
+ : mPinned(pinned),
mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
mDescriptor(*desc),
// mConfig is set by configure() and not used before then
@@ -71,7 +72,7 @@
mSuspended(false),
mAudioFlinger(thread->mAudioFlinger)
{
- ALOGV("Constructor %p", this);
+ ALOGV("Constructor %p pinned %d", this, pinned);
int lStatus;
// create effect engine from effect factory
@@ -94,7 +95,9 @@
goto Error;
}
+ setOffloaded(thread->type() == ThreadBase::OFFLOAD, thread->id());
ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
+
return;
Error:
mEffectInterface.clear();
@@ -105,10 +108,10 @@
{
ALOGV("Destructor %p", this);
if (mEffectInterface != 0) {
- remove_effect_from_hal_l();
- // release effect engine
- mEffectInterface.clear();
+ ALOGW("EffectModule %p destructor called with unreleased interface", this);
+ release_l();
}
+
}
status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
@@ -122,7 +125,7 @@
size_t i;
for (i = 0; i < size; i++) {
EffectHandle *h = mHandles[i];
- if (h == NULL || h->destroyed_l()) {
+ if (h == NULL || h->disconnected()) {
continue;
}
// first non destroyed handle is considered in control
@@ -150,9 +153,14 @@
return status;
}
-size_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
+ssize_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
{
Mutex::Autolock _l(mLock);
+ return removeHandle_l(handle);
+}
+
+ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
+{
size_t size = mHandles.size();
size_t i;
for (i = 0; i < size; i++) {
@@ -161,9 +169,10 @@
}
}
if (i == size) {
- return size;
+ ALOGW("%s %p handle not found %p", __FUNCTION__, this, handle);
+ return BAD_VALUE;
}
- ALOGV("removeHandle() %p removed handle %p in position %zu", this, handle, i);
+ ALOGV("removeHandle_l() %p removed handle %p in position %zu", this, handle, i);
mHandles.removeAt(i);
// if removed from first place, move effect control from this handle to next in line
@@ -190,7 +199,7 @@
// the first valid handle in the list has control over the module
for (size_t i = 0; i < mHandles.size(); i++) {
EffectHandle *h = mHandles[i];
- if (h != NULL && !h->destroyed_l()) {
+ if (h != NULL && !h->disconnected()) {
return h;
}
}
@@ -198,29 +207,22 @@
return NULL;
}
-size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast)
+// unsafe method called when the effect parent thread has been destroyed
+ssize_t AudioFlinger::EffectModule::disconnectHandle(EffectHandle *handle, bool unpinIfLast)
{
ALOGV("disconnect() %p handle %p", this, handle);
- // keep a strong reference on this EffectModule to avoid calling the
- // destructor before we exit
- sp<EffectModule> keep(this);
- {
- if (removeHandle(handle) == 0) {
- if (!isPinned() || unpinIfLast) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- thread->removeEffect_l(this);
- }
- sp<AudioFlinger> af = mAudioFlinger.promote();
- if (af != 0) {
- af->updateOrphanEffectChains(this);
- }
- AudioSystem::unregisterEffect(mId);
- }
+ Mutex::Autolock _l(mLock);
+ ssize_t numHandles = removeHandle_l(handle);
+ if ((numHandles == 0) && (!mPinned || unpinIfLast)) {
+ AudioSystem::unregisterEffect(mId);
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af != 0) {
+ mLock.unlock();
+ af->updateOrphanEffectChains(this);
+ mLock.lock();
}
}
- return mHandles.size();
+ return numHandles;
}
bool AudioFlinger::EffectModule::updateState() {
@@ -560,6 +562,16 @@
return status;
}
+// must be called with EffectChain::mLock held
+void AudioFlinger::EffectModule::release_l()
+{
+ if (mEffectInterface != 0) {
+ remove_effect_from_hal_l();
+ // release effect engine
+ mEffectInterface.clear();
+ }
+}
+
status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
@@ -599,16 +611,33 @@
return mStatus;
}
if (cmdCode == EFFECT_CMD_GET_PARAM &&
+ (sizeof(effect_param_t) > cmdSize ||
+ ((effect_param_t *)pCmdData)->psize > cmdSize
+ - sizeof(effect_param_t))) {
+ android_errorWriteLog(0x534e4554, "32438594");
+ android_errorWriteLog(0x534e4554, "33003822");
+ return -EINVAL;
+ }
+ if (cmdCode == EFFECT_CMD_GET_PARAM &&
(*replySize < sizeof(effect_param_t) ||
((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t))) {
android_errorWriteLog(0x534e4554, "29251553");
return -EINVAL;
}
if (cmdCode == EFFECT_CMD_GET_PARAM &&
- (sizeof(effect_param_t) > cmdSize ||
- ((effect_param_t *)pCmdData)->psize > cmdSize
- - sizeof(effect_param_t))) {
- android_errorWriteLog(0x534e4554, "32438594");
+ (sizeof(effect_param_t) > *replySize
+ || ((effect_param_t *)pCmdData)->psize > *replySize
+ - sizeof(effect_param_t)
+ || ((effect_param_t *)pCmdData)->vsize > *replySize
+ - sizeof(effect_param_t)
+ - ((effect_param_t *)pCmdData)->psize
+ || roundUpDelta(((effect_param_t *)pCmdData)->psize, (uint32_t)sizeof(int)) >
+ *replySize
+ - sizeof(effect_param_t)
+ - ((effect_param_t *)pCmdData)->psize
+ - ((effect_param_t *)pCmdData)->vsize)) {
+ ALOGV("\tLVM_ERROR : EFFECT_CMD_GET_PARAM: reply size inconsistent");
+ android_errorWriteLog(0x534e4554, "32705438");
return -EINVAL;
}
if ((cmdCode == EFFECT_CMD_SET_PARAM
@@ -636,7 +665,7 @@
uint32_t size = (replySize == NULL) ? 0 : *replySize;
for (size_t i = 1; i < mHandles.size(); i++) {
EffectHandle *h = mHandles[i];
- if (h != NULL && !h->destroyed_l()) {
+ if (h != NULL && !h->disconnected()) {
h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
}
}
@@ -689,7 +718,7 @@
}
for (size_t i = 1; i < mHandles.size(); i++) {
EffectHandle *h = mHandles[i];
- if (h != NULL && !h->destroyed_l()) {
+ if (h != NULL && !h->disconnected()) {
h->setEnabled(enabled);
}
}
@@ -849,8 +878,7 @@
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mHandles.size(); i++) {
EffectHandle *handle = mHandles[i];
- if (handle != NULL && !handle->destroyed_l()) {
- handle->effect().clear();
+ if (handle != NULL && !handle->disconnected()) {
if (handle->hasControl()) {
enabled = handle->enabled();
}
@@ -1076,7 +1104,7 @@
result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
for (size_t i = 0; i < mHandles.size(); ++i) {
EffectHandle *handle = mHandles[i];
- if (handle != NULL && !handle->destroyed_l()) {
+ if (handle != NULL && !handle->disconnected()) {
handle->dumpToBuffer(buffer, SIZE);
result.append(buffer);
}
@@ -1102,7 +1130,7 @@
int32_t priority)
: BnEffect(),
mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
- mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false)
+ mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false)
{
ALOGV("constructor %p", this);
@@ -1125,14 +1153,6 @@
AudioFlinger::EffectHandle::~EffectHandle()
{
ALOGV("Destructor %p", this);
-
- if (mEffect == 0) {
- mDestroyed = true;
- return;
- }
- mEffect->lock();
- mDestroyed = true;
- mEffect->unlock();
disconnect(false);
}
@@ -1143,13 +1163,15 @@
status_t AudioFlinger::EffectHandle::enable()
{
+ AutoMutex _l(mLock);
ALOGV("enable %p", this);
+ sp<EffectModule> effect = mEffect.promote();
+ if (effect == 0 || mDisconnected) {
+ return DEAD_OBJECT;
+ }
if (!mHasControl) {
return INVALID_OPERATION;
}
- if (mEffect == 0) {
- return DEAD_OBJECT;
- }
if (mEnabled) {
return NO_ERROR;
@@ -1157,20 +1179,20 @@
mEnabled = true;
- sp<ThreadBase> thread = mEffect->thread().promote();
+ sp<ThreadBase> thread = effect->thread().promote();
if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, true, mEffect->sessionId());
+ thread->checkSuspendOnEffectEnabled(effect, true, effect->sessionId());
}
// checkSuspendOnEffectEnabled() can suspend this same effect when enabled
- if (mEffect->suspended()) {
+ if (effect->suspended()) {
return NO_ERROR;
}
- status_t status = mEffect->setEnabled(true);
+ status_t status = effect->setEnabled(true);
if (status != NO_ERROR) {
if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
}
mEnabled = false;
} else {
@@ -1180,12 +1202,12 @@
Mutex::Autolock _l(t->mLock);
t->broadcast_l();
}
- if (!mEffect->isOffloadable()) {
+ if (!effect->isOffloadable()) {
if (thread->type() == ThreadBase::OFFLOAD) {
PlaybackThread *t = (PlaybackThread *)thread.get();
t->invalidateTracks(AUDIO_STREAM_MUSIC);
}
- if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
}
}
@@ -1197,27 +1219,29 @@
status_t AudioFlinger::EffectHandle::disable()
{
ALOGV("disable %p", this);
+ AutoMutex _l(mLock);
+ sp<EffectModule> effect = mEffect.promote();
+ if (effect == 0 || mDisconnected) {
+ return DEAD_OBJECT;
+ }
if (!mHasControl) {
return INVALID_OPERATION;
}
- if (mEffect == 0) {
- return DEAD_OBJECT;
- }
if (!mEnabled) {
return NO_ERROR;
}
mEnabled = false;
- if (mEffect->suspended()) {
+ if (effect->suspended()) {
return NO_ERROR;
}
- status_t status = mEffect->setEnabled(false);
+ status_t status = effect->setEnabled(false);
- sp<ThreadBase> thread = mEffect->thread().promote();
+ sp<ThreadBase> thread = effect->thread().promote();
if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
if (thread->type() == ThreadBase::OFFLOAD) {
PlaybackThread *t = (PlaybackThread *)thread.get();
Mutex::Autolock _l(t->mLock);
@@ -1230,25 +1254,39 @@
void AudioFlinger::EffectHandle::disconnect()
{
+ ALOGV("%s %p", __FUNCTION__, this);
disconnect(true);
}
void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
{
- ALOGV("disconnect(%s)", unpinIfLast ? "true" : "false");
- if (mEffect == 0) {
+ AutoMutex _l(mLock);
+ ALOGV("disconnect(%s) %p", unpinIfLast ? "true" : "false", this);
+ if (mDisconnected) {
+ if (unpinIfLast) {
+ android_errorWriteLog(0x534e4554, "32707507");
+ }
return;
}
- // restore suspended effects if the disconnected handle was enabled and the last one.
- if ((mEffect->disconnect(this, unpinIfLast) == 0) && mEnabled) {
- sp<ThreadBase> thread = mEffect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ mDisconnected = true;
+ sp<ThreadBase> thread;
+ {
+ sp<EffectModule> effect = mEffect.promote();
+ if (effect != 0) {
+ thread = effect->thread().promote();
+ }
+ }
+ if (thread != 0) {
+ thread->disconnectEffectHandle(this, unpinIfLast);
+ } else {
+ ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
+ // try to cleanup as much as we can
+ sp<EffectModule> effect = mEffect.promote();
+ if (effect != 0) {
+ effect->disconnectHandle(this, unpinIfLast);
}
}
- // release sp on module => module destructor can be called now
- mEffect.clear();
if (mClient != 0) {
if (mCblk != NULL) {
// unlike ~TrackBase(), mCblk is never a local new, so don't delete
@@ -1268,15 +1306,17 @@
void *pReplyData)
{
ALOGVV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
- cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+ cmdCode, mHasControl, mEffect.unsafe_get());
+ AutoMutex _l(mLock);
+ sp<EffectModule> effect = mEffect.promote();
+ if (effect == 0 || mDisconnected) {
+ return DEAD_OBJECT;
+ }
// only get parameter command is permitted for applications not controlling the effect
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
return INVALID_OPERATION;
}
- if (mEffect == 0) {
- return DEAD_OBJECT;
- }
if (mClient == 0) {
return INVALID_OPERATION;
}
@@ -1287,36 +1327,54 @@
// particular client process: no risk to block the whole media server process or mixer
// threads if we are stuck here
Mutex::Autolock _l(mCblk->lock);
- if (mCblk->clientIndex > EFFECT_PARAM_BUFFER_SIZE ||
- mCblk->serverIndex > EFFECT_PARAM_BUFFER_SIZE) {
+
+ // keep local copy of index in case of client corruption b/32220769
+ const uint32_t clientIndex = mCblk->clientIndex;
+ const uint32_t serverIndex = mCblk->serverIndex;
+ if (clientIndex > EFFECT_PARAM_BUFFER_SIZE ||
+ serverIndex > EFFECT_PARAM_BUFFER_SIZE) {
mCblk->serverIndex = 0;
mCblk->clientIndex = 0;
return BAD_VALUE;
}
status_t status = NO_ERROR;
- while (mCblk->serverIndex < mCblk->clientIndex) {
- int reply;
- uint32_t rsize = sizeof(int);
- int *p = (int *)(mBuffer + mCblk->serverIndex);
- int size = *p++;
- if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
+ effect_param_t *param = NULL;
+ for (uint32_t index = serverIndex; index < clientIndex;) {
+ int *p = (int *)(mBuffer + index);
+ const int size = *p++;
+ if (size < 0
+ || size > EFFECT_PARAM_BUFFER_SIZE
+ || ((uint8_t *)p + size) > mBuffer + clientIndex) {
ALOGW("command(): invalid parameter block size");
+ status = BAD_VALUE;
break;
}
- effect_param_t *param = (effect_param_t *)p;
- if (param->psize == 0 || param->vsize == 0) {
- ALOGW("command(): null parameter or value size");
- mCblk->serverIndex += size;
- continue;
+
+ // copy to local memory in case of client corruption b/32220769
+ param = (effect_param_t *)realloc(param, size);
+ if (param == NULL) {
+ ALOGW("command(): out of memory");
+ status = NO_MEMORY;
+ break;
}
- uint32_t psize = sizeof(effect_param_t) +
- ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) +
- param->vsize;
- status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM,
- psize,
- p,
+ memcpy(param, p, size);
+
+ int reply = 0;
+ uint32_t rsize = sizeof(reply);
+ status_t ret = effect->command(EFFECT_CMD_SET_PARAM,
+ size,
+ param,
&rsize,
&reply);
+
+ // verify shared memory: server index shouldn't change; client index can't go back.
+ if (serverIndex != mCblk->serverIndex
+ || clientIndex > mCblk->clientIndex) {
+ android_errorWriteLog(0x534e4554, "32220769");
+ status = BAD_VALUE;
+ break;
+ }
+
// stop at first error encountered
if (ret != NO_ERROR) {
status = ret;
@@ -1326,8 +1384,9 @@
*(int *)pReplyData = reply;
break;
}
- mCblk->serverIndex += size;
+ index += size;
}
+ free(param);
mCblk->serverIndex = 0;
mCblk->clientIndex = 0;
return status;
@@ -1339,7 +1398,7 @@
return disable();
}
- return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+ return effect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal, bool enabled)
@@ -1421,7 +1480,6 @@
if (mOwnInBuffer) {
delete mInBuffer;
}
-
}
// getEffectFromDesc_l() must be called with ThreadBase::mLock held
@@ -1537,13 +1595,38 @@
}
}
-// addEffect_l() must be called with PlaybackThread::mLock held
+// createEffect_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
+ ThreadBase *thread,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned)
+{
+ Mutex::Autolock _l(mLock);
+ effect = new EffectModule(thread, this, desc, id, sessionId, pinned);
+ status_t lStatus = effect->status();
+ if (lStatus == NO_ERROR) {
+ lStatus = addEffect_ll(effect);
+ }
+ if (lStatus != NO_ERROR) {
+ effect.clear();
+ }
+ return lStatus;
+}
+
+// addEffect_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
{
+ Mutex::Autolock _l(mLock);
+ return addEffect_ll(effect);
+}
+// addEffect_l() must be called with ThreadBase::mLock and EffectChain::mLock held
+status_t AudioFlinger::EffectChain::addEffect_ll(const sp<EffectModule>& effect)
+{
effect_descriptor_t desc = effect->desc();
uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
- Mutex::Autolock _l(mLock);
effect->setChain(this);
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
@@ -1653,8 +1736,9 @@
return NO_ERROR;
}
-// removeEffect_l() must be called with PlaybackThread::mLock held
-size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
+// removeEffect_l() must be called with ThreadBase::mLock held
+size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect,
+ bool release)
{
Mutex::Autolock _l(mLock);
size_t size = mEffects.size();
@@ -1669,6 +1753,10 @@
mEffects[i]->state() == EffectModule::STOPPING) {
mEffects[i]->stop();
}
+ if (release) {
+ mEffects[i]->release_l();
+ }
+
if (type == EFFECT_FLAG_TYPE_AUXILIARY) {
delete[] effect->inBuffer();
} else {
@@ -1680,6 +1768,7 @@
mEffects.removeAt(i);
ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %zu", effect.get(),
this, i);
+
break;
}
}
@@ -1687,7 +1776,7 @@
return mEffects.size();
}
-// setDevice_l() must be called with PlaybackThread::mLock held
+// setDevice_l() must be called with ThreadBase::mLock held
void AudioFlinger::EffectChain::setDevice_l(audio_devices_t device)
{
size_t size = mEffects.size();
@@ -1696,7 +1785,7 @@
}
}
-// setMode_l() must be called with PlaybackThread::mLock held
+// setMode_l() must be called with ThreadBase::mLock held
void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode)
{
size_t size = mEffects.size();
@@ -1705,7 +1794,7 @@
}
}
-// setAudioSource_l() must be called with PlaybackThread::mLock held
+// setAudioSource_l() must be called with ThreadBase::mLock held
void AudioFlinger::EffectChain::setAudioSource_l(audio_source_t source)
{
size_t size = mEffects.size();
@@ -1714,7 +1803,7 @@
}
}
-// setVolume_l() must be called with PlaybackThread::mLock or EffectChain::mLock held
+// setVolume_l() must be called with ThreadBase::mLock or EffectChain::mLock held
bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right, bool force)
{
uint32_t newLeft = *left;
@@ -1775,7 +1864,7 @@
return hasControl;
}
-// resetVolume_l() must be called with PlaybackThread::mLock or EffectChain::mLock held
+// resetVolume_l() must be called with ThreadBase::mLock or EffectChain::mLock held
void AudioFlinger::EffectChain::resetVolume_l()
{
if ((mLeftVolume != UINT_MAX) && (mRightVolume != UINT_MAX)) {
@@ -1876,7 +1965,7 @@
effect->setSuspended(false);
effect->lock();
EffectHandle *handle = effect->controlHandle_l();
- if (handle != NULL && !handle->destroyed_l()) {
+ if (handle != NULL && !handle->disconnected()) {
effect->setEnabled_l(handle->enabled());
}
effect->unlock();
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index a19b7fd..dc29ce0 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -25,10 +25,11 @@
// state changes or resource modifications. Always respect the following order
// if multiple mutexes must be acquired to avoid cross deadlock:
// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+// AudioHandle -> ThreadBase -> EffectChain -> EffectModule
// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
-// startOutput()...) should never be called with AudioFlinger or Threadbase mutex locked
-// to avoid cross deadlock with other clients calling AudioPolicyService methods that in turn
-// call AudioFlinger thus locking the same mutexes in the reverse order.
+// startOutput(), getInputForAttr(), releaseInput()...) should never be called with AudioFlinger or
+// Threadbase mutex locked to avoid cross deadlock with other clients calling AudioPolicyService
+// methods that in turn call AudioFlinger thus locking the same mutexes in the reverse order.
// The EffectModule class is a wrapper object controlling the effect engine implementation
// in the effect library. It prevents concurrent calls to process() and command() functions
@@ -45,7 +46,8 @@
const wp<AudioFlinger::EffectChain>& chain,
effect_descriptor_t *desc,
int id,
- audio_session_t sessionId);
+ audio_session_t sessionId,
+ bool pinned);
virtual ~EffectModule();
enum effect_state {
@@ -93,8 +95,9 @@
const wp<ThreadBase>& thread() { return mThread; }
status_t addHandle(EffectHandle *handle);
- size_t disconnect(EffectHandle *handle, bool unpinIfLast);
- size_t removeHandle(EffectHandle *handle);
+ ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
+ ssize_t removeHandle(EffectHandle *handle);
+ ssize_t removeHandle_l(EffectHandle *handle);
const effect_descriptor_t& desc() const { return mDescriptor; }
wp<EffectChain>& chain() { return mChain; }
@@ -124,6 +127,7 @@
status_t setOffloaded(bool offloaded, audio_io_handle_t io);
bool isOffloaded() const;
void addEffectToHal_l();
+ void release_l();
void dump(int fd, const Vector<String16>& args);
@@ -208,12 +212,17 @@
bool enabled() const { return mEnabled; }
// Getters
- int id() const { return mEffect->id(); }
+ wp<EffectModule> effect() const { return mEffect; }
+ int id() const {
+ sp<EffectModule> effect = mEffect.promote();
+ if (effect == 0) {
+ return 0;
+ }
+ return effect->id();
+ }
int priority() const { return mPriority; }
bool hasControl() const { return mHasControl; }
- sp<EffectModule> effect() const { return mEffect; }
- // destroyed_l() must be called with the associated EffectModule mLock held
- bool destroyed_l() const { return mDestroyed; }
+ bool disconnected() const { return mDisconnected; }
void dumpToBuffer(char* buffer, size_t size);
@@ -222,7 +231,8 @@
EffectHandle(const EffectHandle&);
EffectHandle& operator =(const EffectHandle&);
- sp<EffectModule> mEffect; // pointer to controlled EffectModule
+ Mutex mLock; // protects IEffect method calls
+ wp<EffectModule> mEffect; // pointer to controlled EffectModule
sp<IEffectClient> mEffectClient; // callback interface for client notifications
/*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect()
sp<IMemory> mCblkMemory; // shared memory for control block
@@ -233,8 +243,7 @@
bool mHasControl; // true if this handle is controlling the effect
bool mEnabled; // cached enable state: needed when the effect is
// restored after being suspended
- bool mDestroyed; // Set to true by destructor. Access with EffectModule
- // mLock held
+ bool mDisconnected; // Set to true by disconnect()
};
// the EffectChain class represents a group of effects associated to one audio session.
@@ -269,8 +278,15 @@
mLock.unlock();
}
+ status_t createEffect_l(sp<EffectModule>& effect,
+ ThreadBase *thread,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned);
status_t addEffect_l(const sp<EffectModule>& handle);
- size_t removeEffect_l(const sp<EffectModule>& handle);
+ status_t addEffect_ll(const sp<EffectModule>& handle);
+ size_t removeEffect_l(const sp<EffectModule>& handle, bool release = false);
audio_session_t sessionId() const { return mSessionId; }
void setSessionId(audio_session_t sessionId) { mSessionId = sessionId; }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 1d7b946..9966eeb 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1267,7 +1267,8 @@
audio_session_t sessionId,
effect_descriptor_t *desc,
int *enabled,
- status_t *status)
+ status_t *status,
+ bool pinned)
{
sp<EffectModule> effect;
sp<EffectHandle> handle;
@@ -1317,14 +1318,7 @@
}
effectRegistered = true;
// create a new effect module if none present in the chain
- effect = new EffectModule(this, chain, desc, id, sessionId);
- lStatus = effect->status();
- if (lStatus != NO_ERROR) {
- goto Exit;
- }
- effect->setOffloaded(mType == OFFLOAD, mId);
-
- lStatus = chain->addEffect_l(effect);
+ lStatus = chain->createEffect_l(effect, this, desc, id, sessionId, pinned);
if (lStatus != NO_ERROR) {
goto Exit;
}
@@ -1365,6 +1359,33 @@
return handle;
}
+void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle,
+ bool unpinIfLast)
+{
+ bool remove = false;
+ sp<EffectModule> effect;
+ {
+ Mutex::Autolock _l(mLock);
+
+ effect = handle->effect().promote();
+ if (effect == 0) {
+ return;
+ }
+ // restore suspended effects if the disconnected handle was enabled and the last one.
+ remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
+ if (remove) {
+ removeEffect_l(effect, true);
+ }
+ }
+ if (remove) {
+ mAudioFlinger->updateOrphanEffectChains(effect);
+ AudioSystem::unregisterEffect(effect->id());
+ if (handle->enabled()) {
+ checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
+ }
+ }
+}
+
sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(audio_session_t sessionId,
int effectId)
{
@@ -1425,9 +1446,9 @@
return NO_ERROR;
}
-void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect) {
+void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect, bool release) {
- ALOGV("removeEffect_l() %p effect %p", this, effect.get());
+ ALOGV("%s %p effect %p", __FUNCTION__, this, effect.get());
effect_descriptor_t desc = effect->desc();
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
detachAuxEffect_l(effect->id());
@@ -1436,7 +1457,7 @@
sp<EffectChain> chain = effect->chain().promote();
if (chain != 0) {
// remove effect chain if removing last effect
- if (chain->removeEffect_l(effect) == 0) {
+ if (chain->removeEffect_l(effect, release) == 0) {
removeEffectChain_l(chain);
}
} else {
@@ -1602,6 +1623,7 @@
dumpInternals(fd, args);
dumpTracks(fd, args);
dumpEffectChains(fd, args);
+ mLocalLog.dump(fd, args, " " /* prefix */);
}
void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args __unused)
@@ -2072,6 +2094,10 @@
chain->incActiveTrackCnt();
}
+ char buffer[256];
+ track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+ mLocalLog.log("addTrack_l (%p) %s", track.get(), buffer + 4); // log for analysis
+
status = NO_ERROR;
}
@@ -2097,6 +2123,11 @@
void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
{
track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+
+ char buffer[256];
+ track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+ mLocalLog.log("removeTrack_l (%p) %s", track.get(), buffer + 4); // log for analysis
+
mTracks.remove(track);
deleteTrackName_l(track->name());
// redundant as track is about to be destroyed, for dumpsys only
@@ -3264,6 +3295,10 @@
}
if (track->isTerminated()) {
removeTrack_l(track);
+ } else { // inactive but not terminated
+ char buffer[256];
+ track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+ mLocalLog.log("removeTracks_l(%p) %s", track.get(), buffer + 4);
}
}
}
@@ -3710,6 +3745,15 @@
FastMixerStateQueue *sq = mFastMixer->sq();
FastMixerState *state = sq->begin();
if (!(state->mCommand & FastMixerState::IDLE)) {
+ // Report any frames trapped in the Monopipe
+ MonoPipe *monoPipe = (MonoPipe *)mPipeSink.get();
+ const long long pipeFrames = monoPipe->maxFrames() - monoPipe->availableToWrite();
+ mLocalLog.log("threadLoop_standby: framesWritten:%lld suspendedFrames:%lld "
+ "monoPipeWritten:%lld monoPipeLeft:%lld",
+ (long long)mFramesWritten, (long long)mSuspendedFrames,
+ (long long)mPipeSink->framesWritten(), pipeFrames);
+ mLocalLog.log("threadLoop_standby: %s", mTimestamp.toString().c_str());
+
state->mCommand = FastMixerState::COLD_IDLE;
state->mColdFutexAddr = &mFastMixerFutex;
state->mColdGen++;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 5235cde..d261ea5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -295,7 +295,8 @@
audio_session_t sessionId,
effect_descriptor_t *desc,
int *enabled,
- status_t *status /*non-NULL*/);
+ status_t *status /*non-NULL*/,
+ bool pinned);
// return values for hasAudioSession (bit field)
enum effect_state {
@@ -334,7 +335,9 @@
status_t addEffect_l(const sp< EffectModule>& effect);
// remove and effect module. Also removes the effect chain is this was the last
// effect
- void removeEffect_l(const sp< EffectModule>& effect);
+ void removeEffect_l(const sp< EffectModule>& effect, bool release = false);
+ // disconnect an effect handle from module and destroy module if last handle
+ void disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast);
// detach all tracks connected to an auxiliary effect
virtual void detachAuxEffect_l(int effectId __unused) {}
// returns a combination of:
@@ -861,6 +864,78 @@
uint32_t mScreenState; // cached copy of gScreenState
static const size_t kFastMixerLogSize = 4 * 1024;
sp<NBLog::Writer> mFastMixerNBLogWriter;
+
+ // Do not call from a sched_fifo thread as it uses a system time call
+ // and obtains a local mutex.
+ class LocalLog {
+ public:
+ void log(const char *fmt, ...) {
+ va_list val;
+ va_start(val, fmt);
+
+ // format to buffer
+ char buffer[512];
+ int length = vsnprintf(buffer, sizeof(buffer), fmt, val);
+ if (length >= (signed)sizeof(buffer)) {
+ length = sizeof(buffer) - 1;
+ }
+
+ // strip out trailing newline
+ while (length > 0 && buffer[length - 1] == '\n') {
+ buffer[--length] = 0;
+ }
+
+ // store in circular array
+ AutoMutex _l(mLock);
+ mLog.emplace_back(
+ std::make_pair(systemTime(SYSTEM_TIME_REALTIME), std::string(buffer)));
+ if (mLog.size() > kLogSize) {
+ mLog.pop_front();
+ }
+
+ va_end(val);
+ }
+
+ void dump(int fd, const Vector<String16>& args, const char *prefix = "") {
+ if (!AudioFlinger::dumpTryLock(mLock)) return; // a local lock, shouldn't happen
+ if (mLog.size() > 0) {
+ bool dumpAll = false;
+ for (const auto &arg : args) {
+ if (arg == String16("--locallog")) {
+ dumpAll = true;
+ }
+ }
+
+ dprintf(fd, "Local Log:\n");
+ auto it = mLog.begin();
+ if (!dumpAll && mLog.size() > kLogPrint) {
+ it += (mLog.size() - kLogPrint);
+ }
+ for (; it != mLog.end(); ++it) {
+ const int64_t ns = it->first;
+ const int ns_per_sec = 1000000000;
+ const time_t sec = ns / ns_per_sec;
+ struct tm tm;
+ localtime_r(&sec, &tm);
+
+ dprintf(fd, "%s%02d-%02d %02d:%02d:%02d.%03d %s\n",
+ prefix,
+ tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (int)(ns % ns_per_sec / 1000000),
+ it->second.c_str());
+ }
+ }
+ mLock.unlock();
+ }
+
+ private:
+ Mutex mLock;
+ static const size_t kLogSize = 256; // full history
+ static const size_t kLogPrint = 32; // default print history
+ std::deque<std::pair<int64_t, std::string>> mLog;
+ } mLocalLog;
+
public:
virtual bool hasFastMixer() const = 0;
virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8f134c1..e8e27e4 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -459,7 +459,7 @@
/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
{
result.append(" Name Active Client Type Fmt Chn mask Session fCount S F SRate "
- "L dB R dB Server Main buf Aux Buf Flags UndFrmCnt\n");
+ "L dB R dB Server Main buf Aux buf Flags UndFrmCnt Flushed\n");
}
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active)
@@ -526,7 +526,7 @@
break;
}
snprintf(&buffer[8], size-8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g "
- "%08X %p %p 0x%03X %9u%c\n",
+ "%08X %08zX %08zX 0x%03X %9u%c %7u\n",
active ? "yes" : "no",
(mClient == 0) ? getpid_cached : mClient->pid(),
mStreamType,
@@ -540,11 +540,12 @@
20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))),
20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))),
mCblk->mServer,
- mMainBuffer,
- mAuxBuffer,
+ (size_t)mMainBuffer, // use %zX as %p appends 0x
+ (size_t)mAuxBuffer, // use %zX as %p appends 0x
mCblk->mFlags,
mAudioTrackServerProxy->getUnderrunFrames(),
- nowInUnderrun);
+ nowInUnderrun,
+ (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000); // 7 digits
}
uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index 3fb545d..9f3488d 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -4,19 +4,9 @@
LOCAL_SRC_FILES:= \
service/AudioPolicyService.cpp \
- service/AudioPolicyEffects.cpp
-
-ifeq ($(USE_LEGACY_AUDIO_POLICY), 1)
-LOCAL_SRC_FILES += \
- service/AudioPolicyInterfaceImplLegacy.cpp \
- service/AudioPolicyClientImplLegacy.cpp
-
- LOCAL_CFLAGS += -DUSE_LEGACY_AUDIO_POLICY
-else
-LOCAL_SRC_FILES += \
+ service/AudioPolicyEffects.cpp \
service/AudioPolicyInterfaceImpl.cpp \
service/AudioPolicyClientImpl.cpp
-endif
LOCAL_C_INCLUDES := \
$(TOPDIR)frameworks/av/services/audioflinger \
@@ -32,12 +22,8 @@
libbinder \
libaudioclient \
libhardware_legacy \
- libserviceutility
-
-ifneq ($(USE_LEGACY_AUDIO_POLICY), 1)
-LOCAL_SHARED_LIBRARIES += \
+ libserviceutility \
libaudiopolicymanager
-endif
LOCAL_STATIC_LIBRARIES := \
libmedia_helper \
@@ -52,8 +38,6 @@
include $(BUILD_SHARED_LIBRARY)
-ifneq ($(USE_LEGACY_AUDIO_POLICY), 1)
-
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= managerdefault/AudioPolicyManager.cpp
@@ -136,7 +120,6 @@
include $(BUILD_SHARED_LIBRARY)
endif
-endif
#######################################################################
# Recursive call sub-folder Android.mk
diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
index 818da72..a224004 100644
--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp
@@ -384,6 +384,7 @@
sp<AudioPort> source = ctx->findPortByTagName(String8(devTag));
if (source == NULL) {
ALOGE("%s: no source found with name=%s", __FUNCTION__, devTag);
+ free(sourcesLiteral);
return BAD_VALUE;
}
sources.add(source);
diff --git a/services/audiopolicy/config/a2dp_audio_policy_configuration.xml b/services/audiopolicy/config/a2dp_audio_policy_configuration.xml
index ced7463..7bcab5c 100644
--- a/services/audiopolicy/config/a2dp_audio_policy_configuration.xml
+++ b/services/audiopolicy/config/a2dp_audio_policy_configuration.xml
@@ -2,11 +2,7 @@
<!-- A2dp Audio HAL Audio Policy Configuration file -->
<module name="a2dp" halVersion="2.0">
<mixPorts>
- <mixPort name="a2dp output" role="source">
- <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
- samplingRates="44100"
- channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
- </mixPort>
+ <mixPort name="a2dp output" role="source"/>
<mixPort name="a2dp input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100,48000"
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 5ef0216..a714041 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4477,9 +4477,7 @@
bool AudioPolicyManager::streamsMatchForvolume(audio_stream_type_t stream1,
audio_stream_type_t stream2) {
- return ((stream1 == stream2) ||
- ((stream1 == AUDIO_STREAM_ACCESSIBILITY) && (stream2 == AUDIO_STREAM_MUSIC)) ||
- ((stream1 == AUDIO_STREAM_MUSIC) && (stream2 == AUDIO_STREAM_ACCESSIBILITY)));
+ return (stream1 == stream2);
}
uint32_t AudioPolicyManager::getStrategyForStream(audio_stream_type_t stream) {
diff --git a/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp
deleted file mode 100644
index aa228aa..0000000
--- a/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AudioPolicyService"
-//#define LOG_NDEBUG 0
-
-#include "Configuration.h"
-#undef __STRICT_ANSI__
-#define __STDINT_LIMITS
-#define __STDC_LIMIT_MACROS
-#include <stdint.h>
-
-#include <sys/time.h>
-#include <binder/IServiceManager.h>
-#include <utils/Log.h>
-#include <cutils/properties.h>
-#include <binder/IPCThreadState.h>
-#include <utils/String16.h>
-#include <utils/threads.h>
-#include "AudioPolicyService.h"
-#include "ServiceUtilities.h"
-#include <hardware_legacy/power.h>
-#include <media/AudioEffect.h>
-//#include <media/IAudioFlinger.h>
-
-#include <hardware/hardware.h>
-#include <system/audio.h>
-#include <system/audio_policy.h>
-#include <hardware/audio_policy.h>
-#include <audio_effects/audio_effects_conf.h>
-#include <media/AudioParameter.h>
-
-
-namespace android {
-
-/* implementation of the interface to the policy manager */
-extern "C" {
-
-audio_module_handle_t aps_load_hw_module(void *service __unused,
- const char *name)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- ALOGW("%s: could not get AudioFlinger", __func__);
- return AUDIO_MODULE_HANDLE_NONE;
- }
-
- return af->loadHwModule(name);
-}
-
-static audio_io_handle_t open_output(audio_module_handle_t module,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask,
- uint32_t *pLatencyMs,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- ALOGW("%s: could not get AudioFlinger", __func__);
- return AUDIO_IO_HANDLE_NONE;
- }
-
- if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL ||
- pDevices == NULL || pLatencyMs == NULL) {
- return AUDIO_IO_HANDLE_NONE;
- }
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = *pSamplingRate;
- config.format = *pFormat;
- config.channel_mask = *pChannelMask;
- if (offloadInfo != NULL) {
- config.offload_info = *offloadInfo;
- }
- audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = af->openOutput(module, &output, &config, pDevices,
- String8(""), pLatencyMs, flags);
- if (status == NO_ERROR) {
- *pSamplingRate = config.sample_rate;
- *pFormat = config.format;
- *pChannelMask = config.channel_mask;
- if (offloadInfo != NULL) {
- *((audio_offload_info_t *)offloadInfo) = config.offload_info;
- }
- }
- return output;
-}
-
-// deprecated: replaced by aps_open_output_on_module()
-audio_io_handle_t aps_open_output(void *service __unused,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask,
- uint32_t *pLatencyMs,
- audio_output_flags_t flags)
-{
- return open_output(AUDIO_MODULE_HANDLE_NONE, pDevices, pSamplingRate, pFormat, pChannelMask,
- pLatencyMs, flags, NULL);
-}
-
-audio_io_handle_t aps_open_output_on_module(void *service __unused,
- audio_module_handle_t module,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask,
- uint32_t *pLatencyMs,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo)
-{
- return open_output(module, pDevices, pSamplingRate, pFormat, pChannelMask,
- pLatencyMs, flags, offloadInfo);
-}
-
-audio_io_handle_t aps_open_dup_output(void *service __unused,
- audio_io_handle_t output1,
- audio_io_handle_t output2)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- ALOGW("%s: could not get AudioFlinger", __func__);
- return 0;
- }
- return af->openDuplicateOutput(output1, output2);
-}
-
-int aps_close_output(void *service __unused, audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- return PERMISSION_DENIED;
- }
-
- return af->closeOutput(output);
-}
-
-int aps_suspend_output(void *service __unused, audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- ALOGW("%s: could not get AudioFlinger", __func__);
- return PERMISSION_DENIED;
- }
-
- return af->suspendOutput(output);
-}
-
-int aps_restore_output(void *service __unused, audio_io_handle_t output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- ALOGW("%s: could not get AudioFlinger", __func__);
- return PERMISSION_DENIED;
- }
-
- return af->restoreOutput(output);
-}
-
-static audio_io_handle_t open_input(audio_module_handle_t module,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- ALOGW("%s: could not get AudioFlinger", __func__);
- return AUDIO_IO_HANDLE_NONE;
- }
-
- if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL || pDevices == NULL) {
- return AUDIO_IO_HANDLE_NONE;
- }
-
- if (((*pDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)
- && !captureAudioOutputAllowed(IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid())) {
- ALOGE("open_input() permission denied: capture not allowed");
- return AUDIO_IO_HANDLE_NONE;
- }
-
- audio_config_t config = AUDIO_CONFIG_INITIALIZER;;
- config.sample_rate = *pSamplingRate;
- config.format = *pFormat;
- config.channel_mask = *pChannelMask;
- audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = af->openInput(module, &input, &config, pDevices,
- String8(""), AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_FAST /*FIXME*/);
- if (status == NO_ERROR) {
- *pSamplingRate = config.sample_rate;
- *pFormat = config.format;
- *pChannelMask = config.channel_mask;
- }
- return input;
-}
-
-
-// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored
-audio_io_handle_t aps_open_input(void *service __unused,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask,
- audio_in_acoustics_t acoustics __unused)
-{
- return open_input(AUDIO_MODULE_HANDLE_NONE, pDevices, pSamplingRate, pFormat, pChannelMask);
-}
-
-audio_io_handle_t aps_open_input_on_module(void *service __unused,
- audio_module_handle_t module,
- audio_devices_t *pDevices,
- uint32_t *pSamplingRate,
- audio_format_t *pFormat,
- audio_channel_mask_t *pChannelMask)
-{
- return open_input(module, pDevices, pSamplingRate, pFormat, pChannelMask);
-}
-
-int aps_close_input(void *service __unused, audio_io_handle_t input)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- return PERMISSION_DENIED;
- }
-
- return af->closeInput(input);
-}
-
-int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- return PERMISSION_DENIED;
- }
-
- return af->invalidateStream(stream);
-}
-
-int aps_move_effects(void *service __unused, audio_session_t session,
- audio_io_handle_t src_output,
- audio_io_handle_t dst_output)
-{
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == 0) {
- return PERMISSION_DENIED;
- }
-
- return af->moveEffects(session, src_output, dst_output);
-}
-
-char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle,
- const char *keys)
-{
- String8 result = AudioSystem::getParameters(io_handle, String8(keys));
- return strdup(result.string());
-}
-
-void aps_set_parameters(void *service, audio_io_handle_t io_handle,
- const char *kv_pairs, int delay_ms)
-{
- AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
-
- audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms);
-}
-
-int aps_set_stream_volume(void *service, audio_stream_type_t stream,
- float volume, audio_io_handle_t output,
- int delay_ms)
-{
- AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
-
- return audioPolicyService->setStreamVolume(stream, volume, output,
- delay_ms);
-}
-
-int aps_start_tone(void *service, audio_policy_tone_t tone,
- audio_stream_type_t stream)
-{
- AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
-
- return audioPolicyService->startTone(tone, stream);
-}
-
-int aps_stop_tone(void *service)
-{
- AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
-
- return audioPolicyService->stopTone();
-}
-
-int aps_set_voice_volume(void *service, float volume, int delay_ms)
-{
- AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
-
- return audioPolicyService->setVoiceVolume(volume, delay_ms);
-}
-
-}; // extern "C"
-
-}; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 53c54bb..0c23080 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -400,7 +400,6 @@
sp<AudioPolicyEffects>audioPolicyEffects;
{
Mutex::Autolock _l(mLock);
- mAudioPolicyManager->releaseInput(input, session);
audioPolicyEffects = mAudioPolicyEffects;
}
if (audioPolicyEffects != 0) {
@@ -410,6 +409,10 @@
ALOGW("Failed to release effects on input %d", input);
}
}
+ {
+ Mutex::Autolock _l(mLock);
+ mAudioPolicyManager->releaseInput(input, session);
+ }
}
status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
deleted file mode 100644
index a5b96fe..0000000
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AudioPolicyService"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include "AudioPolicyService.h"
-#include "ServiceUtilities.h"
-
-#include <system/audio.h>
-#include <system/audio_policy.h>
-#include <hardware/audio_policy.h>
-#include <media/AudioPolicyHelper.h>
-
-namespace android {
-
-
-// ----------------------------------------------------------------------------
-
-status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address,
- const char *device_name __unused)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
- if (!audio_is_output_device(device) && !audio_is_input_device(device)) {
- return BAD_VALUE;
- }
- if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE &&
- state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) {
- return BAD_VALUE;
- }
-
- ALOGV("setDeviceConnectionState()");
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device,
- state, device_address);
-}
-
-audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState(
- audio_devices_t device,
- const char *device_address)
-{
- if (mpAudioPolicy == NULL) {
- return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
- }
- return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device,
- device_address);
-}
-
-status_t AudioPolicyService::setPhoneState(audio_mode_t state)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
- if (uint32_t(state) >= AUDIO_MODE_CNT) {
- return BAD_VALUE;
- }
-
- ALOGV("setPhoneState()");
-
- // TODO: check if it is more appropriate to do it in platform specific policy manager
- AudioSystem::setMode(state);
-
- Mutex::Autolock _l(mLock);
- mpAudioPolicy->set_phone_state(mpAudioPolicy, state);
- mPhoneState = state;
- return NO_ERROR;
-}
-
-audio_mode_t AudioPolicyService::getPhoneState()
-{
- Mutex::Autolock _l(mLock);
- return mPhoneState;
-}
-
-status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
- audio_policy_forced_cfg_t config)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
- if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
- return BAD_VALUE;
- }
- if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) {
- return BAD_VALUE;
- }
- ALOGV("setForceUse()");
- Mutex::Autolock _l(mLock);
- mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);
- return NO_ERROR;
-}
-
-audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage)
-{
- if (mpAudioPolicy == NULL) {
- return AUDIO_POLICY_FORCE_NONE;
- }
- if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) {
- return AUDIO_POLICY_FORCE_NONE;
- }
- return mpAudioPolicy->get_force_use(mpAudioPolicy, usage);
-}
-
-audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
- uint32_t samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- audio_output_flags_t flags,
- const audio_offload_info_t *offloadInfo)
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return AUDIO_IO_HANDLE_NONE;
- }
- if (mpAudioPolicy == NULL) {
- return AUDIO_IO_HANDLE_NONE;
- }
- ALOGV("getOutput()");
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate,
- format, channelMask, flags, offloadInfo);
-}
-
-status_t AudioPolicyService::startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
- }
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- ALOGV("startOutput()");
- // create audio processors according to stream
- sp<AudioPolicyEffects>audioPolicyEffects;
- {
- Mutex::Autolock _l(mLock);
- audioPolicyEffects = mAudioPolicyEffects;
- }
- if (audioPolicyEffects != 0) {
- status_t status = audioPolicyEffects->addOutputSessionEffects(output, stream, session);
- if (status != NO_ERROR && status != ALREADY_EXISTS) {
- ALOGW("Failed to add effects on session %d", session);
- }
- }
-
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);
-}
-
-status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
- }
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- ALOGV("stopOutput()");
- mOutputCommandThread->stopOutputCommand(output, stream, session);
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::doStopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
-{
- ALOGV("doStopOutput from tid %d", gettid());
- // release audio processors from the stream
- sp<AudioPolicyEffects>audioPolicyEffects;
- {
- Mutex::Autolock _l(mLock);
- audioPolicyEffects = mAudioPolicyEffects;
- }
- if (audioPolicyEffects != 0) {
- status_t status = audioPolicyEffects->releaseOutputSessionEffects(output, stream, session);
- if (status != NO_ERROR && status != ALREADY_EXISTS) {
- ALOGW("Failed to release effects on session %d", session);
- }
- }
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
-}
-
-void AudioPolicyService::releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
-{
- if (mpAudioPolicy == NULL) {
- return;
- }
- ALOGV("releaseOutput()");
- mOutputCommandThread->releaseOutputCommand(output, stream, session);
-}
-
-void AudioPolicyService::doReleaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream __unused,
- audio_session_t session __unused)
-{
- ALOGV("doReleaseOutput from tid %d", gettid());
- Mutex::Autolock _l(mLock);
- mpAudioPolicy->release_output(mpAudioPolicy, output);
-}
-
-status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *input,
- audio_session_t session,
- pid_t pid __unused,
- uid_t uid __unused,
- uint32_t samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- audio_input_flags_t flags __unused,
- audio_port_handle_t selectedDeviceId __unused)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
-
- audio_source_t inputSource = attr->source;
-
- // already checked by client, but double-check in case the client wrapper is bypassed
- if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD &&
- inputSource != AUDIO_SOURCE_FM_TUNER) {
- return BAD_VALUE;
- }
-
- if (inputSource == AUDIO_SOURCE_DEFAULT) {
- inputSource = AUDIO_SOURCE_MIC;
- }
-
- if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
- return BAD_VALUE;
- }
-
- sp<AudioPolicyEffects>audioPolicyEffects;
- {
- Mutex::Autolock _l(mLock);
- // the audio_in_acoustics_t parameter is ignored by get_input()
- *input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate,
- format, channelMask, (audio_in_acoustics_t) 0);
- audioPolicyEffects = mAudioPolicyEffects;
- }
- if (*input == AUDIO_IO_HANDLE_NONE) {
- return INVALID_OPERATION;
- }
-
- if (audioPolicyEffects != 0) {
- // create audio pre processors according to input source
- status_t status = audioPolicyEffects->addInputEffects(*input, inputSource, session);
- if (status != NO_ERROR && status != ALREADY_EXISTS) {
- ALOGW("Failed to add effects on input %d", input);
- }
- }
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::startInput(audio_io_handle_t input,
- audio_session_t session __unused)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- Mutex::Autolock _l(mLock);
-
- return mpAudioPolicy->start_input(mpAudioPolicy, input);
-}
-
-status_t AudioPolicyService::stopInput(audio_io_handle_t input,
- audio_session_t session __unused)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- Mutex::Autolock _l(mLock);
-
- return mpAudioPolicy->stop_input(mpAudioPolicy, input);
-}
-
-void AudioPolicyService::releaseInput(audio_io_handle_t input,
- audio_session_t session)
-{
- if (mpAudioPolicy == NULL) {
- return;
- }
-
- sp<AudioPolicyEffects>audioPolicyEffects;
- {
- Mutex::Autolock _l(mLock);
- mpAudioPolicy->release_input(mpAudioPolicy, input);
- audioPolicyEffects = mAudioPolicyEffects;
- }
- if (audioPolicyEffects != 0) {
- // release audio processors from the input
- status_t status = audioPolicyEffects->releaseInputEffects(input, session);
- if(status != NO_ERROR) {
- ALOGW("Failed to release effects on input %d", input);
- }
- }
-}
-
-status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
- int indexMin,
- int indexMax)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
- }
- Mutex::Autolock _l(mLock);
- mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax);
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
- int index,
- audio_devices_t device)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
- }
- Mutex::Autolock _l(mLock);
- if (mpAudioPolicy->set_stream_volume_index_for_device) {
- return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy,
- stream,
- index,
- device);
- } else {
- return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
- }
-}
-
-status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream,
- int *index,
- audio_devices_t device)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return BAD_VALUE;
- }
- Mutex::Autolock _l(mLock);
- if (mpAudioPolicy->get_stream_volume_index_for_device) {
- return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy,
- stream,
- index,
- device);
- } else {
- return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index);
- }
-}
-
-uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream)
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return 0;
- }
- if (mpAudioPolicy == NULL) {
- return 0;
- }
- return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream);
-}
-
-//audio policy: use audio_device_t appropriately
-
-audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream)
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return AUDIO_DEVICE_NONE;
- }
- if (mpAudioPolicy == NULL) {
- return AUDIO_DEVICE_NONE;
- }
- return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream);
-}
-
-audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc)
-{
- // FIXME change return type to status_t, and return NO_INIT here
- if (mpAudioPolicy == NULL) {
- return 0;
- }
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc);
-}
-
-status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc,
- audio_io_handle_t io,
- uint32_t strategy,
- audio_session_t session,
- int id)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- return mpAudioPolicy->register_effect(mpAudioPolicy, desc, io, strategy, session, id);
-}
-
-status_t AudioPolicyService::unregisterEffect(int id)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- return mpAudioPolicy->unregister_effect(mpAudioPolicy, id);
-}
-
-status_t AudioPolicyService::setEffectEnabled(int id, bool enabled)
-{
- if (mpAudioPolicy == NULL) {
- return NO_INIT;
- }
- return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled);
-}
-
-bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return false;
- }
- if (mpAudioPolicy == NULL) {
- return false;
- }
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs);
-}
-
-bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
-{
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) {
- return false;
- }
- if (mpAudioPolicy == NULL) {
- return false;
- }
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs);
-}
-
-bool AudioPolicyService::isSourceActive(audio_source_t source) const
-{
- if (mpAudioPolicy == NULL) {
- return false;
- }
- if (mpAudioPolicy->is_source_active == 0) {
- return false;
- }
- Mutex::Autolock _l(mLock);
- return mpAudioPolicy->is_source_active(mpAudioPolicy, source);
-}
-
-status_t AudioPolicyService::queryDefaultPreProcessing(audio_session_t audioSession,
- effect_descriptor_t *descriptors,
- uint32_t *count)
-{
- if (mpAudioPolicy == NULL) {
- *count = 0;
- return NO_INIT;
- }
- sp<AudioPolicyEffects>audioPolicyEffects;
- {
- Mutex::Autolock _l(mLock);
- audioPolicyEffects = mAudioPolicyEffects;
- }
- if (audioPolicyEffects == 0) {
- *count = 0;
- return NO_INIT;
- }
- return audioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count);
-}
-
-bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
-{
- if (mpAudioPolicy == NULL) {
- ALOGV("mpAudioPolicy == NULL");
- return false;
- }
-
- if (mpAudioPolicy->is_offload_supported == NULL) {
- ALOGV("HAL does not implement is_offload_supported");
- return false;
- }
-
- return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
-}
-
-status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused,
- audio_port_type_t type __unused,
- unsigned int *num_ports,
- struct audio_port *ports __unused,
- unsigned int *generation __unused)
-{
- *num_ports = 0;
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused,
- audio_patch_handle_t *handle __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches __unused,
- unsigned int *generation __unused)
-{
- *num_patches = 0;
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session __unused,
- audio_stream_type_t *stream,
- uid_t uid __unused,
- uint32_t samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- audio_output_flags_t flags,
- audio_port_handle_t selectedDeviceId __unused,
- const audio_offload_info_t *offloadInfo)
-{
- if (attr != NULL) {
- *stream = audio_attributes_to_stream_type(attr);
- } else {
- if (*stream == AUDIO_STREAM_DEFAULT) {
- return BAD_VALUE;
- }
- }
- *output = getOutput(*stream, samplingRate, format, channelMask,
- flags, offloadInfo);
- if (*output == AUDIO_IO_HANDLE_NONE) {
- return INVALID_OPERATION;
- }
- return NO_ERROR;
-}
-
-status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session __unused,
- audio_io_handle_t *ioHandle __unused,
- audio_devices_t *device __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::registerPolicyMixes(const Vector<AudioMix>& mixes __unused,
- bool registration __unused)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
- const audio_attributes_t *attributes,
- audio_patch_handle_t *handle)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::stopAudioSource(audio_patch_handle_t handle)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::setMasterMono(bool mono)
-{
- return INVALID_OPERATION;
-}
-
-status_t AudioPolicyService::getMasterMono(bool *mono)
-{
- return INVALID_OPERATION;
-}
-
-}; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index b6b6116..c4f6367 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -39,11 +39,6 @@
#include <system/audio.h>
#include <system/audio_policy.h>
-#ifdef USE_LEGACY_AUDIO_POLICY
-#include <hardware/hardware.h>
-#include <hardware/audio_policy.h>
-#endif
-
namespace android {
static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
@@ -54,11 +49,6 @@
static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
-#ifdef USE_LEGACY_AUDIO_POLICY
-namespace {
- extern struct audio_policy_service_ops aps_ops;
-};
-#endif
// ----------------------------------------------------------------------------
@@ -80,40 +70,8 @@
// start output activity command thread
mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
-#ifdef USE_LEGACY_AUDIO_POLICY
- ALOGI("AudioPolicyService CSTOR in legacy mode");
-
- /* instantiate the audio policy manager */
- const struct hw_module_t *module;
- int rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
- if (rc) {
- return;
- }
- rc = audio_policy_dev_open(module, &mpAudioPolicyDev);
- ALOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc));
- if (rc) {
- return;
- }
-
- rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
- &mpAudioPolicy);
- ALOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc));
- if (rc) {
- return;
- }
-
- rc = mpAudioPolicy->init_check(mpAudioPolicy);
- ALOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc));
- if (rc) {
- return;
- }
- ALOGI("Loaded audio policy from %s (%s)", module->name, module->id);
-#else
- ALOGI("AudioPolicyService CSTOR in new mode");
-
mAudioPolicyClient = new AudioPolicyClient(this);
mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
-#endif
}
// load audio processing modules
sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
@@ -129,17 +87,8 @@
mAudioCommandThread->exit();
mOutputCommandThread->exit();
-#ifdef USE_LEGACY_AUDIO_POLICY
- if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) {
- mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
- }
- if (mpAudioPolicyDev != NULL) {
- audio_policy_dev_close(mpAudioPolicyDev);
- }
-#else
destroyAudioPolicyManager(mAudioPolicyManager);
delete mAudioPolicyClient;
-#endif
mNotificationClients.clear();
mAudioPolicyEffects.clear();
@@ -187,14 +136,12 @@
Mutex::Autolock _l(mNotificationClientsLock);
mNotificationClients.removeItem(uid);
}
-#ifndef USE_LEGACY_AUDIO_POLICY
{
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager) {
mAudioPolicyManager->releaseResourcesForUid(uid);
}
}
-#endif
}
void AudioPolicyService::onAudioPortListUpdate()
@@ -360,11 +307,7 @@
char buffer[SIZE];
String8 result;
-#ifdef USE_LEGACY_AUDIO_POLICY
- snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy);
-#else
snprintf(buffer, SIZE, "AudioPolicyManager: %p\n", mAudioPolicyManager);
-#endif
result.append(buffer);
snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get());
result.append(buffer);
@@ -394,15 +337,9 @@
mTonePlaybackThread->dump(fd);
}
-#ifdef USE_LEGACY_AUDIO_POLICY
- if (mpAudioPolicy) {
- mpAudioPolicy->dump(mpAudioPolicy, fd);
- }
-#else
if (mAudioPolicyManager) {
mAudioPolicyManager->dump(fd);
}
-#endif
if (locked) mLock.unlock();
}
@@ -1210,29 +1147,4 @@
int aps_set_voice_volume(void *service, float volume, int delay_ms);
};
-#ifdef USE_LEGACY_AUDIO_POLICY
-namespace {
- struct audio_policy_service_ops aps_ops = {
- .open_output = aps_open_output,
- .open_duplicate_output = aps_open_dup_output,
- .close_output = aps_close_output,
- .suspend_output = aps_suspend_output,
- .restore_output = aps_restore_output,
- .open_input = aps_open_input,
- .close_input = aps_close_input,
- .set_stream_volume = aps_set_stream_volume,
- .invalidate_stream = aps_invalidate_stream,
- .set_parameters = aps_set_parameters,
- .get_parameters = aps_get_parameters,
- .start_tone = aps_start_tone,
- .stop_tone = aps_stop_tone,
- .set_voice_volume = aps_set_voice_volume,
- .move_effects = aps_move_effects,
- .load_hw_module = aps_load_hw_module,
- .open_output_on_module = aps_open_output_on_module,
- .open_input_on_module = aps_open_input_on_module,
- };
-}; // namespace <unnamed>
-#endif
-
}; // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index a310735..8eb4f2d 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -30,10 +30,6 @@
#include <media/ToneGenerator.h>
#include <media/AudioEffect.h>
#include <media/AudioPolicy.h>
-#ifdef USE_LEGACY_AUDIO_POLICY
-#include <hardware/audio_policy.h>
-#include <hardware_legacy/AudioPolicyInterface.h>
-#endif
#include "AudioPolicyEffects.h"
#include "managerdefault/AudioPolicyManager.h"
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index dd9029e..85faac6 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -470,8 +470,8 @@
// CameraInfo is for android.hardware.Camera which does not
// support external camera facing. The closest approximation would be
// front camera.
- if (cameraInfo->orientation == CAMERA_FACING_EXTERNAL) {
- cameraInfo->orientation = CAMERA_FACING_FRONT;
+ if (cameraInfo->facing == CAMERA_FACING_EXTERNAL) {
+ cameraInfo->facing = CAMERA_FACING_FRONT;
}
}
return rc;
@@ -2556,7 +2556,8 @@
write(fd, result.string(), result.size());
} else {
result.appendFormat(" Facing: %s\n",
- info.facing == CAMERA_FACING_BACK ? "BACK" : "FRONT");
+ info.facing == CAMERA_FACING_BACK ? "BACK" :
+ info.facing == CAMERA_FACING_FRONT ? "FRONT" : "EXTERNAL");
result.appendFormat(" Orientation: %d\n", info.orientation);
int deviceVersion;
if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_0) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index bcd62d6..bfbf640 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -993,7 +993,7 @@
}
status_t Camera2Client::startRecordingL(Parameters ¶ms, bool restart) {
- status_t res;
+ status_t res = OK;
ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 9a7839b..32b99ca 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -911,9 +911,7 @@
CameraParameters::FALSE);
}
- char value[PROPERTY_VALUE_MAX];
- property_get("camera.disable_zsl_mode", value, "0");
- if (!strcmp(value,"1") || slowJpegMode) {
+ if (slowJpegMode || property_get_bool("camera.disable_zsl_mode", false)) {
ALOGI("Camera %d: Disabling ZSL mode", cameraId);
allowZslMode = false;
} else {
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
new file mode 100644
index 0000000..76a5c1c
--- /dev/null
+++ b/services/mediaanalytics/Android.mk
@@ -0,0 +1,32 @@
+# Media Statistics service
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ main_mediaanalytics.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ liblog \
+ libmedia \
+ libmediaanalyticsservice \
+ libutils \
+ libbinder \
+ libicuuc
+
+LOCAL_STATIC_LIBRARIES := \
+ libicuandroid_utils \
+ libregistermsext
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/libmediaanalyticsservice
+
+LOCAL_MODULE:= mediaanalytics
+
+LOCAL_INIT_RC := mediaanalytics.rc
+
+LOCAL_CFLAGS := -Werror -Wall
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/mediaanalytics/main_mediaanalytics.cpp b/services/mediaanalytics/main_mediaanalytics.cpp
new file mode 100644
index 0000000..ba601ee
--- /dev/null
+++ b/services/mediaanalytics/main_mediaanalytics.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "mediaanalytics"
+//#define LOG_NDEBUG 0
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+//#include "RegisterExtensions.h"
+
+// from LOCAL_C_INCLUDES
+#include "IcuUtils.h"
+#include "MediaAnalyticsService.h"
+
+using namespace android;
+
+int main(int argc __unused, char **argv __unused)
+{
+ signal(SIGPIPE, SIG_IGN);
+
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm(defaultServiceManager());
+ ALOGI("ServiceManager: %p", sm.get());
+
+ InitializeIcuOrDie();
+ MediaAnalyticsService::instantiate();
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+}
diff --git a/services/mediaanalytics/mediaanalytics.rc b/services/mediaanalytics/mediaanalytics.rc
new file mode 100644
index 0000000..0af69f5
--- /dev/null
+++ b/services/mediaanalytics/mediaanalytics.rc
@@ -0,0 +1,5 @@
+service mediaanalytics /system/bin/mediaanalytics
+ class main
+ user media
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 38aa472..73109e1 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -23,7 +23,6 @@
LOCAL_SHARED_LIBRARIES:= \
libbinder \
- libcutils \
liblog \
libmediadrm \
libutils \
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
index b4cda19..4344506 100644
--- a/services/radio/Android.mk
+++ b/services/radio/Android.mk
@@ -17,9 +17,8 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- RadioService.cpp \
- RadioHalLegacy.cpp
+LOCAL_SRC_FILES:= \
+ RadioService.cpp
LOCAL_SHARED_LIBRARIES:= \
liblog \
@@ -31,6 +30,26 @@
libradio \
libradio_metadata
+ifeq ($(ENABLE_TREBLE),true)
+# Treble configuration
+LOCAL_CFLAGS += -DENABLE_TREBLE
+LOCAL_SRC_FILES += \
+ HidlUtils.cpp \
+ RadioHalHidl.cpp
+
+LOCAL_SHARED_LIBRARIES += \
+ libhwbinder \
+ libhidlbase \
+ libhidltransport \
+ libbase \
+ android.hardware.broadcastradio@1.0
+else
+# libhardware configuration
+LOCAL_SRC_FILES += \
+ RadioHalLegacy.cpp
+endif
+
+
LOCAL_CFLAGS += -Wall -Wextra -Werror
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
diff --git a/services/radio/HidlUtils.cpp b/services/radio/HidlUtils.cpp
new file mode 100644
index 0000000..bfced7a
--- /dev/null
+++ b/services/radio/HidlUtils.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "HidlUtils"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include <system/radio_metadata.h>
+
+#include "HidlUtils.h"
+
+namespace android {
+
+using android::hardware::broadcastradio::V1_0::MetadataType;
+using android::hardware::broadcastradio::V1_0::Band;
+using android::hardware::broadcastradio::V1_0::Deemphasis;
+using android::hardware::broadcastradio::V1_0::Rds;
+
+//static
+int HidlUtils::convertHalResult(Result result)
+{
+ switch (result) {
+ case Result::OK:
+ return 0;
+ case Result::INVALID_ARGUMENTS:
+ return -EINVAL;
+ case Result::INVALID_STATE:
+ return -ENOSYS;
+ case Result::TIMEOUT:
+ return -ETIMEDOUT;
+ case Result::NOT_INITIALIZED:
+ default:
+ return -ENODEV;
+ }
+}
+
+
+//static
+void HidlUtils::convertBandConfigToHal(BandConfig *halConfig,
+ const radio_hal_band_config_t *config)
+{
+ halConfig->type = static_cast<Band>(config->type);
+ halConfig->antennaConnected = config->antenna_connected;
+ halConfig->lowerLimit = config->lower_limit;
+ halConfig->upperLimit = config->upper_limit;
+ halConfig->spacings.setToExternal(const_cast<unsigned int *>(&config->spacings[0]),
+ config->num_spacings * sizeof(uint32_t));
+ // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+ halConfig->spacings.resize(config->num_spacings);
+
+ if (halConfig->type == Band::FM) {
+ halConfig->ext.fm.deemphasis = static_cast<Deemphasis>(config->fm.deemphasis);
+ halConfig->ext.fm.stereo = config->fm.stereo;
+ halConfig->ext.fm.rds = static_cast<Rds>(config->fm.rds);
+ halConfig->ext.fm.ta = config->fm.ta;
+ halConfig->ext.fm.af = config->fm.af;
+ halConfig->ext.fm.ea = config->fm.ea;
+ } else {
+ halConfig->ext.am.stereo = config->am.stereo;
+ }
+}
+
+//static
+void HidlUtils::convertPropertiesFromHal(radio_hal_properties_t *properties,
+ const Properties *halProperties)
+{
+ properties->class_id = static_cast<radio_class_t>(halProperties->classId);
+ strlcpy(properties->implementor, halProperties->implementor.c_str(), RADIO_STRING_LEN_MAX);
+ strlcpy(properties->product, halProperties->product.c_str(), RADIO_STRING_LEN_MAX);
+ strlcpy(properties->version, halProperties->version.c_str(), RADIO_STRING_LEN_MAX);
+ strlcpy(properties->serial, halProperties->serial.c_str(), RADIO_STRING_LEN_MAX);
+ properties->num_tuners = halProperties->numTuners;
+ properties->num_audio_sources = halProperties->numAudioSources;
+ properties->supports_capture = halProperties->supportsCapture;
+ properties->num_bands = halProperties->bands.size();
+
+ for (size_t i = 0; i < halProperties->bands.size(); i++) {
+ convertBandConfigFromHal(&properties->bands[i], &halProperties->bands[i]);
+ }
+}
+
+//static
+void HidlUtils::convertBandConfigFromHal(radio_hal_band_config_t *config,
+ const BandConfig *halConfig)
+{
+ config->type = static_cast<radio_band_t>(halConfig->type);
+ config->antenna_connected = halConfig->antennaConnected;
+ config->lower_limit = halConfig->lowerLimit;
+ config->upper_limit = halConfig->upperLimit;
+ config->num_spacings = halConfig->spacings.size();
+ if (config->num_spacings > RADIO_NUM_SPACINGS_MAX) {
+ config->num_spacings = RADIO_NUM_SPACINGS_MAX;
+ }
+ memcpy(config->spacings, halConfig->spacings.data(),
+ sizeof(uint32_t) * config->num_spacings);
+
+ if (halConfig->type == Band::FM) {
+ config->fm.deemphasis = static_cast<radio_deemphasis_t>(halConfig->ext.fm.deemphasis);
+ config->fm.stereo = halConfig->ext.fm.stereo;
+ config->fm.rds = static_cast<radio_rds_t>(halConfig->ext.fm.rds);
+ config->fm.ta = halConfig->ext.fm.ta;
+ config->fm.af = halConfig->ext.fm.af;
+ config->fm.ea = halConfig->ext.fm.ea;
+ } else {
+ config->am.stereo = halConfig->ext.am.stereo;
+ }
+}
+
+
+//static
+void HidlUtils::convertProgramInfoFromHal(radio_program_info_t *info,
+ const ProgramInfo *halInfo,
+ bool withMetadata)
+{
+ info->channel = halInfo->channel;
+ info->sub_channel = halInfo->subChannel;
+ info->tuned = halInfo->tuned;
+ info->stereo = halInfo->stereo;
+ info->digital = halInfo->digital;
+ info->signal_strength = halInfo->signalStrength;
+ if (withMetadata && halInfo->metadata.size() != 0) {
+ convertMetaDataFromHal(&info->metadata, halInfo->metadata,
+ halInfo->channel, halInfo->subChannel);
+ }
+}
+
+//static
+void HidlUtils::convertMetaDataFromHal(radio_metadata_t **metadata,
+ const hidl_vec<MetaData>& halMetadata,
+ uint32_t channel,
+ uint32_t subChannel)
+{
+
+ radio_metadata_allocate(metadata, channel, subChannel);
+ for (size_t i = 0; i < halMetadata.size(); i++) {
+ radio_metadata_key_t key = static_cast<radio_metadata_key_t>(halMetadata[i].key);
+ radio_metadata_type_t type = static_cast<radio_metadata_key_t>(halMetadata[i].type);
+ radio_metadata_clock_t clock;
+
+ switch (type) {
+ case RADIO_METADATA_TYPE_INT:
+ radio_metadata_add_int(metadata, key, halMetadata[i].intValue);
+ break;
+ case RADIO_METADATA_TYPE_TEXT:
+ radio_metadata_add_text(metadata, key, halMetadata[i].stringValue.c_str());
+ break;
+ case RADIO_METADATA_TYPE_RAW:
+ radio_metadata_add_raw(metadata, key,
+ halMetadata[i].rawValue.data(),
+ halMetadata[i].rawValue.size());
+ break;
+ case RADIO_METADATA_TYPE_CLOCK:
+ clock.utc_seconds_since_epoch =
+ halMetadata[i].clockValue.utcSecondsSinceEpoch;
+ clock.timezone_offset_in_minutes =
+ halMetadata[i].clockValue.timezoneOffsetInMinutes;
+ radio_metadata_add_clock(metadata, key, &clock);
+ break;
+ default:
+ ALOGW("%s invalid metadata type %u",__FUNCTION__, halMetadata[i].type);
+ break;
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/radio/HidlUtils.h b/services/radio/HidlUtils.h
new file mode 100644
index 0000000..091abb7
--- /dev/null
+++ b/services/radio/HidlUtils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_RADIO_HAL_HIDL_UTILS_H
+#define ANDROID_HARDWARE_RADIO_HAL_HIDL_UTILS_H
+
+#include <android/hardware/broadcastradio/1.0/types.h>
+#include <hardware/radio.h>
+
+namespace android {
+
+using android::hardware::hidl_vec;
+using android::hardware::broadcastradio::V1_0::Result;
+using android::hardware::broadcastradio::V1_0::Properties;
+using android::hardware::broadcastradio::V1_0::BandConfig;
+using android::hardware::broadcastradio::V1_0::ProgramInfo;
+using android::hardware::broadcastradio::V1_0::MetaData;
+
+class HidlUtils {
+public:
+ static int convertHalResult(Result result);
+ static void convertBandConfigFromHal(radio_hal_band_config_t *config,
+ const BandConfig *halConfig);
+ static void convertPropertiesFromHal(radio_hal_properties_t *properties,
+ const Properties *halProperties);
+ static void convertBandConfigToHal(BandConfig *halConfig,
+ const radio_hal_band_config_t *config);
+ static void convertProgramInfoFromHal(radio_program_info_t *info,
+ const ProgramInfo *halInfo,
+ bool withMetadata);
+ static void convertMetaDataFromHal(radio_metadata_t **metadata,
+ const hidl_vec<MetaData>& halMetadata,
+ uint32_t channel,
+ uint32_t subChannel);
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_HAL_HIDL_UTILS_H
diff --git a/services/radio/RadioHalHidl.cpp b/services/radio/RadioHalHidl.cpp
new file mode 100644
index 0000000..07cb4d5
--- /dev/null
+++ b/services/radio/RadioHalHidl.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RadioHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include <system/radio_metadata.h>
+#include <android/hardware/broadcastradio/1.0/IBroadcastRadioFactory.h>
+
+#include "RadioHalHidl.h"
+#include "HidlUtils.h"
+
+namespace android {
+
+using android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory;
+using android::hardware::broadcastradio::V1_0::Class;
+using android::hardware::broadcastradio::V1_0::Direction;
+using android::hardware::broadcastradio::V1_0::Properties;
+
+
+/* static */
+sp<RadioInterface> RadioInterface::connectModule(radio_class_t classId)
+{
+ return new RadioHalHidl(classId);
+}
+
+int RadioHalHidl::getProperties(radio_hal_properties_t *properties)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ sp<IBroadcastRadio> module = getService();
+ if (module == 0) {
+ return -ENODEV;
+ }
+ Properties halProperties;
+ Result halResult;
+ Return<void> hidlReturn =
+ module->getProperties([&](Result result, const Properties& properties) {
+ halResult = result;
+ if (result == Result::OK) {
+ halProperties = properties;
+ }
+ });
+
+ if (hidlReturn.getStatus().transactionError() == DEAD_OBJECT) {
+ clearService();
+ return -EPIPE;
+ }
+ if (halResult == Result::OK) {
+ HidlUtils::convertPropertiesFromHal(properties, &halProperties);
+ }
+ return HidlUtils::convertHalResult(halResult);
+}
+
+int RadioHalHidl::openTuner(const radio_hal_band_config_t *config,
+ bool audio,
+ sp<TunerCallbackInterface> callback,
+ sp<TunerInterface>& tuner)
+{
+ sp<IBroadcastRadio> module = getService();
+ if (module == 0) {
+ return -ENODEV;
+ }
+ sp<Tuner> tunerImpl = new Tuner(callback, this);
+
+ BandConfig halConfig;
+ Result halResult;
+ sp<ITuner> halTuner;
+
+ HidlUtils::convertBandConfigToHal(&halConfig, config);
+ Return<void> hidlReturn =
+ module->openTuner(halConfig, audio, tunerImpl,
+ [&](Result result, const sp<ITuner>& tuner) {
+ halResult = result;
+ if (result == Result::OK) {
+ halTuner = tuner;
+ }
+ });
+
+ if (hidlReturn.getStatus().transactionError() == DEAD_OBJECT) {
+ clearService();
+ return -EPIPE;
+ }
+ if (halResult == Result::OK) {
+ tunerImpl->setHalTuner(halTuner);
+ tuner = tunerImpl;
+ }
+
+ return HidlUtils::convertHalResult(halResult);
+}
+
+int RadioHalHidl::closeTuner(sp<TunerInterface>& tuner)
+{
+ sp<Tuner> tunerImpl = static_cast<Tuner *>(tuner.get());
+ sp<ITuner> clearTuner;
+ tunerImpl->setHalTuner(clearTuner);
+ return 0;
+}
+
+RadioHalHidl::RadioHalHidl(radio_class_t classId)
+ : mClassId(classId)
+{
+}
+
+RadioHalHidl::~RadioHalHidl()
+{
+}
+
+sp<IBroadcastRadio> RadioHalHidl::getService()
+{
+ if (mHalModule == 0) {
+ sp<IBroadcastRadioFactory> factory = IBroadcastRadioFactory::getService("broadcastradio");
+ if (factory != 0) {
+ factory->connectModule(static_cast<Class>(mClassId),
+ [&](Result retval, const ::android::sp<IBroadcastRadio>& result) {
+ if (retval == Result::OK) {
+ mHalModule = result;
+ }
+ });
+ }
+ }
+ ALOGV("%s OUT module %p", __FUNCTION__, mHalModule.get());
+ return mHalModule;
+}
+
+void RadioHalHidl::clearService()
+{
+ ALOGV("%s IN module %p", __FUNCTION__, mHalModule.get());
+ mHalModule.clear();
+}
+
+
+int RadioHalHidl::Tuner::setConfiguration(const radio_hal_band_config_t *config)
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ BandConfig halConfig;
+ HidlUtils::convertBandConfigToHal(&halConfig, config);
+
+ Return<Result> hidlResult = mHalTuner->setConfiguration(halConfig);
+ checkHidlStatus(hidlResult.getStatus());
+ return HidlUtils::convertHalResult(hidlResult);
+}
+
+int RadioHalHidl::Tuner::getConfiguration(radio_hal_band_config_t *config)
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ BandConfig halConfig;
+ Result halResult;
+ Return<void> hidlReturn =
+ mHalTuner->getConfiguration([&](Result result, const BandConfig& config) {
+ halResult = result;
+ if (result == Result::OK) {
+ halConfig = config;
+ }
+ });
+ status_t status = checkHidlStatus(hidlReturn.getStatus());
+ if (status == NO_ERROR && halResult == Result::OK) {
+ HidlUtils::convertBandConfigFromHal(config, &halConfig);
+ }
+ return HidlUtils::convertHalResult(halResult);
+}
+
+int RadioHalHidl::Tuner::scan(radio_direction_t direction, bool skip_sub_channel)
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ Return<Result> hidlResult =
+ mHalTuner->scan(static_cast<Direction>(direction), skip_sub_channel);
+ checkHidlStatus(hidlResult.getStatus());
+ return HidlUtils::convertHalResult(hidlResult);
+}
+
+int RadioHalHidl::Tuner::step(radio_direction_t direction, bool skip_sub_channel)
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ Return<Result> hidlResult =
+ mHalTuner->step(static_cast<Direction>(direction), skip_sub_channel);
+ checkHidlStatus(hidlResult.getStatus());
+ return HidlUtils::convertHalResult(hidlResult);
+}
+
+int RadioHalHidl::Tuner::tune(unsigned int channel, unsigned int sub_channel)
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ Return<Result> hidlResult =
+ mHalTuner->tune(channel, sub_channel);
+ checkHidlStatus(hidlResult.getStatus());
+ return HidlUtils::convertHalResult(hidlResult);
+}
+
+int RadioHalHidl::Tuner::cancel()
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ Return<Result> hidlResult = mHalTuner->cancel();
+ checkHidlStatus(hidlResult.getStatus());
+ return HidlUtils::convertHalResult(hidlResult);
+}
+
+int RadioHalHidl::Tuner::getProgramInformation(radio_program_info_t *info)
+{
+ ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
+ if (mHalTuner == 0) {
+ return -ENODEV;
+ }
+ ProgramInfo halInfo;
+ Result halResult;
+ bool withMetaData = (info->metadata != NULL);
+ Return<void> hidlReturn = mHalTuner->getProgramInformation(
+ withMetaData, [&](Result result, const ProgramInfo& info) {
+ halResult = result;
+ if (result == Result::OK) {
+ halInfo = info;
+ }
+ });
+ status_t status = checkHidlStatus(hidlReturn.getStatus());
+ if (status == NO_ERROR && halResult == Result::OK) {
+ HidlUtils::convertProgramInfoFromHal(info, &halInfo, withMetaData);
+ }
+ return HidlUtils::convertHalResult(halResult);
+}
+
+Return<void> RadioHalHidl::Tuner::hardwareFailure()
+{
+ ALOGV("%s IN", __FUNCTION__);
+ handleHwFailure();
+ return Return<void>();
+}
+
+Return<void> RadioHalHidl::Tuner::configChange(Result result, const BandConfig& config)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_CONFIG;
+ event.status = HidlUtils::convertHalResult(result);
+ HidlUtils::convertBandConfigFromHal(&event.config, &config);
+ onCallback(&event);
+ return Return<void>();
+}
+
+Return<void> RadioHalHidl::Tuner::tuneComplete(Result result, const ProgramInfo& info)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_TUNED;
+ event.status = HidlUtils::convertHalResult(result);
+ HidlUtils::convertProgramInfoFromHal(&event.info, &info, true);
+ onCallback(&event);
+ if (event.info.metadata != NULL) {
+ radio_metadata_deallocate(event.info.metadata);
+ }
+ return Return<void>();
+}
+
+Return<void> RadioHalHidl::Tuner::afSwitch(const ProgramInfo& info)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_AF_SWITCH;
+ HidlUtils::convertProgramInfoFromHal(&event.info, &info, true);
+ onCallback(&event);
+ if (event.info.metadata != NULL) {
+ radio_metadata_deallocate(event.info.metadata);
+ }
+ return Return<void>();
+}
+
+Return<void> RadioHalHidl::Tuner::antennaStateChange(bool connected)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_ANTENNA;
+ event.on = connected;
+ onCallback(&event);
+ return Return<void>();
+}
+Return<void> RadioHalHidl::Tuner::trafficAnnouncement(bool active)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_TA;
+ event.on = active;
+ onCallback(&event);
+ return Return<void>();
+}
+Return<void> RadioHalHidl::Tuner::emergencyAnnouncement(bool active)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_EA;
+ event.on = active;
+ onCallback(&event);
+ return Return<void>();
+}
+Return<void> RadioHalHidl::Tuner::newMetadata(uint32_t channel, uint32_t subChannel,
+ const ::android::hardware::hidl_vec<MetaData>& metadata)
+{
+ ALOGV("%s IN", __FUNCTION__);
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_METADATA;
+ HidlUtils::convertMetaDataFromHal(&event.metadata, metadata, channel, subChannel);
+ onCallback(&event);
+ if (event.metadata != NULL) {
+ radio_metadata_deallocate(event.info.metadata);
+ }
+ return Return<void>();
+}
+
+
+RadioHalHidl::Tuner::Tuner(sp<TunerCallbackInterface> callback, sp<RadioHalHidl> module)
+ : TunerInterface(), mHalTuner(NULL), mCallback(callback), mParentModule(module)
+{
+}
+
+
+RadioHalHidl::Tuner::~Tuner()
+{
+}
+
+void RadioHalHidl::Tuner::handleHwFailure()
+{
+ ALOGV("%s IN", __FUNCTION__);
+ sp<RadioHalHidl> parentModule = mParentModule.promote();
+ if (parentModule != 0) {
+ parentModule->clearService();
+ }
+ radio_hal_event_t event;
+ memset(&event, 0, sizeof(radio_hal_event_t));
+ event.type = RADIO_EVENT_HW_FAILURE;
+ onCallback(&event);
+ mHalTuner.clear();
+}
+
+status_t RadioHalHidl::Tuner::checkHidlStatus(Status hidlStatus)
+{
+ status_t status = hidlStatus.transactionError();
+ if (status == DEAD_OBJECT) {
+ handleHwFailure();
+ }
+ return status;
+}
+
+void RadioHalHidl::Tuner::onCallback(radio_hal_event_t *halEvent)
+{
+ if (mCallback != 0) {
+ mCallback->onEvent(halEvent);
+ }
+}
+
+} // namespace android
diff --git a/services/radio/RadioHalHidl.h b/services/radio/RadioHalHidl.h
new file mode 100644
index 0000000..5211ee2
--- /dev/null
+++ b/services/radio/RadioHalHidl.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_HAL_HIDL_H
+#define ANDROID_HARDWARE_RADIO_HAL_HIDL_H
+
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include "RadioInterface.h"
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
+#include <android/hardware/broadcastradio/1.0/types.h>
+#include <android/hardware/broadcastradio/1.0/IBroadcastRadio.h>
+#include <android/hardware/broadcastradio/1.0/ITuner.h>
+#include <android/hardware/broadcastradio/1.0/ITunerCallback.h>
+
+namespace android {
+
+using android::hardware::Status;
+using android::hardware::Return;
+using android::hardware::broadcastradio::V1_0::Result;
+using android::hardware::broadcastradio::V1_0::IBroadcastRadio;
+using android::hardware::broadcastradio::V1_0::ITuner;
+using android::hardware::broadcastradio::V1_0::ITunerCallback;
+
+using android::hardware::broadcastradio::V1_0::BandConfig;
+using android::hardware::broadcastradio::V1_0::ProgramInfo;
+using android::hardware::broadcastradio::V1_0::MetaData;
+
+class RadioHalHidl : public RadioInterface
+{
+public:
+ RadioHalHidl(radio_class_t classId);
+
+ // RadioInterface
+ virtual int getProperties(radio_hal_properties_t *properties);
+ virtual int openTuner(const radio_hal_band_config_t *config,
+ bool audio,
+ sp<TunerCallbackInterface> callback,
+ sp<TunerInterface>& tuner);
+ virtual int closeTuner(sp<TunerInterface>& tuner);
+
+ class Tuner : public TunerInterface, public virtual ITunerCallback
+ {
+ public:
+ Tuner(sp<TunerCallbackInterface> callback, sp<RadioHalHidl> module);
+
+ // TunerInterface
+ virtual int setConfiguration(const radio_hal_band_config_t *config);
+ virtual int getConfiguration(radio_hal_band_config_t *config);
+ virtual int scan(radio_direction_t direction, bool skip_sub_channel);
+ virtual int step(radio_direction_t direction, bool skip_sub_channel);
+ virtual int tune(unsigned int channel, unsigned int sub_channel);
+ virtual int cancel();
+ virtual int getProgramInformation(radio_program_info_t *info);
+
+ // ITunerCallback
+ virtual Return<void> hardwareFailure();
+ virtual Return<void> configChange(Result result, const BandConfig& config);
+ virtual Return<void> tuneComplete(Result result, const ProgramInfo& info);
+ virtual Return<void> afSwitch(const ProgramInfo& info);
+ virtual Return<void> antennaStateChange(bool connected);
+ virtual Return<void> trafficAnnouncement(bool active);
+ virtual Return<void> emergencyAnnouncement(bool active);
+ virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel,
+ const ::android::hardware::hidl_vec<MetaData>& metadata);
+
+ void setHalTuner(sp<ITuner>& halTuner) { mHalTuner = halTuner; }
+ sp<ITuner> getHalTuner() { return mHalTuner; }
+
+ private:
+ virtual ~Tuner();
+
+ void onCallback(radio_hal_event_t *halEvent);
+ void handleHwFailure();
+ status_t checkHidlStatus(Status hidlStatus);
+
+ sp<ITuner> mHalTuner;
+ sp<TunerCallbackInterface> mCallback;
+ wp<RadioHalHidl> mParentModule;
+ };
+
+ sp<IBroadcastRadio> getService();
+ void clearService();
+
+private:
+ virtual ~RadioHalHidl();
+
+ radio_class_t mClassId;
+ sp<IBroadcastRadio> mHalModule;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_HAL_HIDL_H
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);