Merge "CCodec: query api feature for block model" into rvc-dev
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 376d3e4..a04ab3f 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -17,6 +17,7 @@
manifest: "test_manifest.json",
file_contexts: ":com.android.media-file_contexts",
defaults: ["com.android.media-defaults"],
+ prebuilts: ["sdkinfo_45"],
installable: false,
}
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index 74e105e..7e9090f 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -460,8 +460,8 @@
const C2ConstGraphicBlock inBuffer =
inputBuffer->data().graphicBlocks().front();
- if (inBuffer.width() != mSize->width ||
- inBuffer.height() != mSize->height) {
+ if (inBuffer.width() < mSize->width ||
+ inBuffer.height() < mSize->height) {
ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
inBuffer.width(), mSize->width, inBuffer.height(),
mSize->height);
@@ -472,8 +472,8 @@
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
vpx_image_t raw_frame;
const C2PlanarLayout &layout = rView->layout();
- uint32_t width = rView->width();
- uint32_t height = rView->height();
+ uint32_t width = mSize->width;
+ uint32_t height = mSize->height;
if (width > 0x8000 || height > 0x8000) {
ALOGE("Image too big: %u x %u", width, height);
work->result = C2_BAD_VALUE;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 94cb04d..c07f3dc 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "CCodecBufferChannel"
#include <utils/Log.h>
+#include <algorithm>
+#include <list>
#include <numeric>
#include <C2AllocatorGralloc.h>
@@ -1255,62 +1257,98 @@
return UNKNOWN_ERROR;
}
size_t numInputSlots = mInput.lock()->numSlots;
- std::vector<sp<MediaCodecBuffer>> toBeQueued;
- for (size_t i = 0; i < numInputSlots; ++i) {
+
+ struct ClientInputBuffer {
size_t index;
sp<MediaCodecBuffer> buffer;
- {
- Mutexed<Input>::Locked input(mInput);
- if (!input->buffers->requestNewBuffer(&index, &buffer)) {
- if (i == 0) {
- ALOGW("[%s] start: cannot allocate memory at all", mName);
- return NO_MEMORY;
- } else {
- ALOGV("[%s] start: cannot allocate memory, only %zu buffers allocated",
- mName, i);
- }
+ size_t capacity;
+ };
+ std::list<ClientInputBuffer> clientInputBuffers;
+
+ {
+ Mutexed<Input>::Locked input(mInput);
+ while (clientInputBuffers.size() < numInputSlots) {
+ ClientInputBuffer clientInputBuffer;
+ if (!input->buffers->requestNewBuffer(&clientInputBuffer.index,
+ &clientInputBuffer.buffer)) {
break;
}
+ clientInputBuffer.capacity = clientInputBuffer.buffer->capacity();
+ clientInputBuffers.emplace_back(std::move(clientInputBuffer));
}
- if (buffer) {
- Mutexed<std::list<sp<ABuffer>>>::Locked configs(mFlushedConfigs);
- ALOGV("[%s] input buffer %zu available", mName, index);
- bool post = true;
- if (!configs->empty()) {
+ }
+ if (clientInputBuffers.empty()) {
+ ALOGW("[%s] start: cannot allocate memory at all", mName);
+ return NO_MEMORY;
+ } else if (clientInputBuffers.size() < numInputSlots) {
+ ALOGD("[%s] start: cannot allocate memory for all slots, "
+ "only %zu buffers allocated",
+ mName, clientInputBuffers.size());
+ } else {
+ ALOGV("[%s] %zu initial input buffers available",
+ mName, clientInputBuffers.size());
+ }
+ // Sort input buffers by their capacities in increasing order.
+ clientInputBuffers.sort(
+ [](const ClientInputBuffer& a, const ClientInputBuffer& b) {
+ return a.capacity < b.capacity;
+ });
+
+ {
+ Mutexed<std::list<sp<ABuffer>>>::Locked configs(mFlushedConfigs);
+ if (!configs->empty()) {
+ while (!configs->empty()) {
sp<ABuffer> config = configs->front();
configs->pop_front();
- if (buffer->capacity() >= config->size()) {
- memcpy(buffer->base(), config->data(), config->size());
- buffer->setRange(0, config->size());
- buffer->meta()->clear();
- buffer->meta()->setInt64("timeUs", 0);
- buffer->meta()->setInt32("csd", 1);
- post = false;
- } else {
- ALOGD("[%s] buffer capacity too small for the config (%zu < %zu)",
- mName, buffer->capacity(), config->size());
+ // Find the smallest input buffer that can fit the config.
+ auto i = std::find_if(
+ clientInputBuffers.begin(),
+ clientInputBuffers.end(),
+ [cfgSize = config->size()](const ClientInputBuffer& b) {
+ return b.capacity >= cfgSize;
+ });
+ if (i == clientInputBuffers.end()) {
+ ALOGW("[%s] no input buffer large enough for the config "
+ "(%zu bytes)",
+ mName, config->size());
+ return NO_MEMORY;
}
- } else if (oStreamFormat.value == C2BufferData::LINEAR && i == 0
- && (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
- // WORKAROUND: Some apps expect CSD available without queueing
- // any input. Queue an empty buffer to get the CSD.
- buffer->setRange(0, 0);
+ sp<MediaCodecBuffer> buffer = i->buffer;
+ memcpy(buffer->base(), config->data(), config->size());
+ buffer->setRange(0, config->size());
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", 0);
- post = false;
+ buffer->meta()->setInt32("csd", 1);
+ if (queueInputBufferInternal(buffer) != OK) {
+ ALOGW("[%s] Error while queueing a flushed config",
+ mName);
+ return UNKNOWN_ERROR;
+ }
+ clientInputBuffers.erase(i);
}
- if (post) {
- mCallback->onInputBufferAvailable(index, buffer);
- } else {
- toBeQueued.emplace_back(buffer);
+ } else if (oStreamFormat.value == C2BufferData::LINEAR &&
+ (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) {
+ sp<MediaCodecBuffer> buffer = clientInputBuffers.front().buffer;
+ // WORKAROUND: Some apps expect CSD available without queueing
+ // any input. Queue an empty buffer to get the CSD.
+ buffer->setRange(0, 0);
+ buffer->meta()->clear();
+ buffer->meta()->setInt64("timeUs", 0);
+ if (queueInputBufferInternal(buffer) != OK) {
+ ALOGW("[%s] Error while queueing an empty buffer to get CSD",
+ mName);
+ return UNKNOWN_ERROR;
}
+ clientInputBuffers.pop_front();
}
}
- for (const sp<MediaCodecBuffer> &buffer : toBeQueued) {
- if (queueInputBufferInternal(buffer) != OK) {
- ALOGV("[%s] Error while queueing initial buffers", mName);
- }
+
+ for (const ClientInputBuffer& clientInputBuffer: clientInputBuffers) {
+ mCallback->onInputBufferAvailable(
+ clientInputBuffer.index,
+ clientInputBuffer.buffer);
}
+
return OK;
}
@@ -1745,7 +1783,7 @@
realloc(c2Buffer);
output.unlock();
mCCodecCallback->onOutputBuffersChanged();
- return;
+ break;
case OutputBuffers::RETRY:
ALOGV("[%s] sendOutputBuffers: unable to register output buffer",
mName);
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
index e98934e..05c560d 100644
--- a/media/libaaudio/tests/test_steal_exclusive.cpp
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -116,7 +116,7 @@
do {
closeAudioStream();
if (mOpenDelayMillis) usleep(mOpenDelayMillis * 1000);
- openAudioStream(mDirection);
+ openAudioStream(mDirection, mRequestedSharingMode);
// It is possible for the stream to be disconnected, or stolen between the time
// it is opened and when it is started. If that happens then try again.
// If it was stolen then it should succeed the second time because there will already be
@@ -136,11 +136,13 @@
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
- aaudio_result_t openAudioStream(aaudio_direction_t direction) {
+ aaudio_result_t openAudioStream(aaudio_direction_t direction,
+ aaudio_sharing_mode_t requestedSharingMode) {
std::lock_guard<std::mutex> lock(mLock);
AAudioStreamBuilder *builder = nullptr;
mDirection = direction;
+ mRequestedSharingMode = requestedSharingMode;
// Use an AAudioStreamBuilder to contain requested parameters.
aaudio_result_t result = AAudio_createStreamBuilder(&builder);
@@ -153,7 +155,7 @@
// Request stream properties.
AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
- AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
+ AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
AAudioStreamBuilder_setDirection(builder, direction);
AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
@@ -280,6 +282,7 @@
AAudioStream *mStream = nullptr;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
+ aaudio_sharing_mode_t mRequestedSharingMode = AAUDIO_UNSPECIFIED;
std::mutex mLock;
std::string mName;
int mMaxRetries = 1;
@@ -316,10 +319,11 @@
}
static void s_usage() {
- printf("test_steal_exclusive [-i] [-r{maxRetries}] [-d{delay}]\n");
+ printf("test_steal_exclusive [-i] [-r{maxRetries}] [-d{delay}] -s\n");
printf(" -i direction INPUT, otherwise OUTPUT\n");
printf(" -d delay open by milliseconds, default = 0\n");
printf(" -r max retries in the error callback, default = 1\n");
+ printf(" -s try to open in SHARED mode\n");
}
int main(int argc, char ** argv) {
@@ -330,12 +334,13 @@
int errorCount = 0;
int maxRetries = 1;
int openDelayMillis = 0;
+ aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("Test Stealing an EXCLUSIVE stream V1.0\n");
+ printf("Test interaction between streams V1.1\n");
printf("\n");
for (int i = 1; i < argc; i++) {
@@ -352,6 +357,9 @@
case 'r':
maxRetries = atoi(&arg[2]);
break;
+ case 's':
+ requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ break;
default:
s_usage();
exit(EXIT_FAILURE);
@@ -369,17 +377,18 @@
victim.setMaxRetries(maxRetries);
thief.setMaxRetries(maxRetries);
- result = victim.openAudioStream(direction);
+ result = victim.openAudioStream(direction, requestedSharingMode);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream victim returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
- if (victim.sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
- printf("Victim modes is EXCLUSIVE => OK\n");
+ if (victim.sharingMode == requestedSharingMode) {
+ printf("Victim modes is %s => OK\n", s_sharingModeToText(requestedSharingMode));
} else {
- printf("Victim modes should be EXCLUSIVE => test not valid!\n");
+ printf("Victim modes should be %s => test not valid!\n",
+ s_sharingModeToText(requestedSharingMode));
goto onerror;
}
@@ -406,7 +415,7 @@
}
printf("Trying to start the THIEF stream, which may steal the VICTIM MMAP resource -----\n");
- result = thief.openAudioStream(direction);
+ result = thief.openAudioStream(direction, requestedSharingMode);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream victim returned %s\n",
AAudio_convertResultToText(result));
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 0eaa503..e6bb2e1 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1159,7 +1159,7 @@
readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, mode, &actualTimeUs);
if (mode != MediaPlayerSeekMode::SEEK_CLOSEST) {
- seekTimeUs = actualTimeUs;
+ seekTimeUs = std::max<int64_t>(0, actualTimeUs);
}
mVideoLastDequeueTimeUs = actualTimeUs;
}
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index fa13f32..88b15ae 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -22,11 +22,14 @@
#include <C2Buffer.h>
+#include <Codec2BufferUtils.h>
+
#include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android/hardware/drm/1.0/types.h>
#include <binder/MemoryDealer.h>
#include <hidlmemory/FrameworkUtils.h>
#include <media/openmax/OMX_Core.h>
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/MediaCodec.h>
@@ -91,15 +94,27 @@
}
status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
- if (mDealer != 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;
}
+ if (it->mClientBuffer != it->mCodecBuffer) {
+ // 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("queueInputBuffer #%d", it->mBufferId);
sp<AMessage> msg = mInputBufferFilled->dup();
msg->setObject("buffer", it->mCodecBuffer);
@@ -267,16 +282,30 @@
}
C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
C2ReadView view{block.map().get()};
- if (view.capacity() > buffer->capacity()) {
- return -ENOSYS;
- }
- memcpy(buffer->base(), view.data(), view.capacity());
- buffer->setRange(0, view.capacity());
+ size_t copyLength = std::min(size_t(view.capacity()), buffer->capacity());
+ ALOGV_IF(view.capacity() > buffer->capacity(),
+ "view.capacity() = %zu, buffer->capacity() = %zu",
+ view.capacity(), buffer->capacity());
+ memcpy(buffer->base(), view.data(), copyLength);
+ buffer->setRange(0, copyLength);
break;
}
case C2BufferData::GRAPHIC: {
- // TODO
- return -ENOSYS;
+ sp<ABuffer> imageData;
+ if (!buffer->format()->findBuffer("image-data", &imageData)) {
+ return -ENOSYS;
+ }
+ if (c2Buffer->data().graphicBlocks().size() != 1u) {
+ return -ENOSYS;
+ }
+ C2ConstGraphicBlock block{c2Buffer->data().graphicBlocks().front()};
+ const C2GraphicView view{block.map().get()};
+ status_t err = ImageCopy(
+ buffer->base(), (const MediaImage2 *)(imageData->base()), view);
+ if (err != OK) {
+ return err;
+ }
+ break;
}
case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 3e49bae..3bccb7b 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -273,6 +273,7 @@
"libutils",
"libmedia_helper",
"libsfplugin_ccodec",
+ "libsfplugin_ccodec_utils",
"libstagefright_codecbase",
"libstagefright_foundation",
"libstagefright_omx_utils",
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 4c4d228..39423c7 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -945,10 +945,30 @@
mInMemoryCache = NULL;
mInMemoryCacheOffset = 0;
+ status_t err = OK;
+ int32_t is4bitTrackId = false;
+ if (param && param->findInt32(kKey4BitTrackIds, &is4bitTrackId) && is4bitTrackId) {
+ err = validateAllTracksId(true);
+ } else {
+ err = validateAllTracksId(false);
+ }
+ if (err != OK) {
+ return err;
+ }
ALOGV("muxer starting: mHasMoovBox %d, mHasFileLevelMeta %d",
mHasMoovBox, mHasFileLevelMeta);
+ err = startWriterThread();
+ if (err != OK) {
+ return err;
+ }
+
+ err = setupAndStartLooper();
+ if (err != OK) {
+ return err;
+ }
+
writeFtypBox(param);
mFreeBoxOffset = mOffset;
@@ -980,22 +1000,22 @@
seekOrPostError(mFd, mMdatOffset, SEEK_SET);
write("\x00\x00\x00\x01mdat????????", 16);
- status_t err = startWriterThread();
- if (err != OK) {
- return err;
- }
-
- setupAndStartLooper();
-
- int32_t is4bitTrackId = false;
- if (param && param->findInt32(kKey4BitTrackIds, &is4bitTrackId) && is4bitTrackId) {
- err = validateAllTracksId(true);
- }
- else {
- err = validateAllTracksId(false);
- }
- if (err != OK) {
- return err;
+ /* Confirm whether the writing of the initial file atoms, ftyp and free,
+ * are written to the file properly by posting kWhatNoIOErrorSoFar to the
+ * MP4WtrCtrlHlpLooper that's handling write and seek errors also. If there
+ * was kWhatIOError, the following two scenarios should be handled.
+ * 1) If kWhatIOError was delivered and processed, MP4WtrCtrlHlpLooper
+ * would have stopped all threads gracefully already and posting
+ * kWhatNoIOErrorSoFar would fail.
+ * 2) If kWhatIOError wasn't delivered or getting processed,
+ * kWhatNoIOErrorSoFar should get posted successfully. Wait for
+ * response from MP4WtrCtrlHlpLooper.
+ */
+ sp<AMessage> msg = new AMessage(kWhatNoIOErrorSoFar, mReflector);
+ sp<AMessage> response;
+ err = msg->postAndAwaitResponse(&response);
+ if (err != OK || !response->findInt32("err", &err) || err != OK) {
+ return ERROR_IO;
}
err = startTracks(param);
@@ -1025,13 +1045,16 @@
}
void *dummy;
- status_t err = pthread_join(mThread, &dummy);
- WARN_UNLESS(err == 0, "stopWriterThread pthread_join err: %d", err);
-
- err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+ status_t err = OK;
+ int retVal = pthread_join(mThread, &dummy);
+ if (retVal == 0) {
+ err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+ ALOGD("WriterThread stopped. Status:%d", err);
+ } else {
+ ALOGE("stopWriterThread pthread_join status:%d", retVal);
+ err = UNKNOWN_ERROR;
+ }
mWriterThreadStarted = false;
- WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d", err);
- ALOGD("Writer thread stopped");
return err;
}
@@ -1089,23 +1112,26 @@
status_t MPEG4Writer::release() {
ALOGD("release()");
- if (mPreAllocationEnabled) {
- truncatePreAllocation();
+ status_t err = OK;
+ if (!truncatePreAllocation()) {
+ if (err == OK) { err = ERROR_IO; }
}
- int err = OK;
- int retVal = fsync(mFd);
- WARN_UNLESS(retVal == 0, "fsync err:%s(%d)", std::strerror(errno), errno);
- err |= retVal;
- retVal = close(mFd);
- WARN_UNLESS(retVal == 0, "close err:%s(%d)", std::strerror(errno), errno);
- err |= retVal;
+ if (fsync(mFd) != 0) {
+ ALOGW("(ignored)fsync err:%s(%d)", std::strerror(errno), errno);
+ // Don't bubble up fsync error, b/157291505.
+ // if (err == OK) { err = ERROR_IO; }
+ }
+ if (close(mFd) != 0) {
+ ALOGE("close err:%s(%d)", std::strerror(errno), errno);
+ if (err == OK) { err = ERROR_IO; }
+ }
mFd = -1;
if (mNextFd != -1) {
- retVal = close(mNextFd);
+ if (close(mNextFd) != 0) {
+ ALOGE("close(mNextFd) error:%s(%d)", std::strerror(errno), errno);
+ }
+ if (err == OK) { err = ERROR_IO; }
mNextFd = -1;
- WARN_UNLESS(retVal == 0, "close mNextFd error:%s(%d)",
- std::strerror(errno), errno);
- err |= retVal;
}
stopAndReleaseLooper();
mInitCheck = NO_INIT;
@@ -1165,7 +1191,7 @@
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
status_t trackErr = (*it)->stop(stopSource);
- WARN_UNLESS(trackErr == 0, "%s track stopped with an error",
+ WARN_UNLESS(trackErr == OK, "%s track stopped with an error",
(*it)->getTrackType());
if (err == OK && trackErr != OK) {
err = trackErr;
@@ -1254,7 +1280,11 @@
CHECK(mBoxes.empty());
- err = release();
+ status_t errRelease = release();
+ // Prioritize the error that occurred before release().
+ if (err == OK) {
+ err = errRelease;
+ }
return err;
}
@@ -1577,9 +1607,8 @@
// Can't guarantee that file is usable or write would succeed anymore, hence signal to stop.
sp<AMessage> msg = new AMessage(kWhatIOError, mReflector);
- msg->setInt32("errno", errno);
- status_t err = msg->post();
- ALOGE("writeOrPostError post:%d", err);
+ msg->setInt32("err", ERROR_IO);
+ WARN_UNLESS(msg->post() == OK, "writeOrPostError:error posting ERROR_IO");
}
void MPEG4Writer::seekOrPostError(int fd, off64_t offset, int whence) {
@@ -1597,9 +1626,8 @@
// Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop.
sp<AMessage> msg = new AMessage(kWhatIOError, mReflector);
- msg->setInt32("errno", errno);
- status_t err = msg->post();
- ALOGE("seekOrPostError post:%d", err);
+ msg->setInt32("err", ERROR_IO);
+ WARN_UNLESS(msg->post() == OK, "seekOrPostError:error posting ERROR_IO");
}
void MPEG4Writer::beginBox(uint32_t id) {
@@ -1838,7 +1866,7 @@
if (res == -1) {
ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
sp<AMessage> msg = new AMessage(kWhatFallocateError, mReflector);
- msg->setInt32("errno", errno);
+ msg->setInt32("err", ERROR_IO);
status_t err = msg->post();
mFallocateErr = true;
ALOGD("preAllocation post:%d", err);
@@ -1850,6 +1878,9 @@
}
bool MPEG4Writer::truncatePreAllocation() {
+ if (!mPreAllocationEnabled)
+ return true;
+
bool status = true;
off64_t endOffset = std::max(mMdatEndOffset, mOffset);
/* if mPreAllocateFileEndOffset >= endOffset, then preallocation logic works good. (diff >= 0).
@@ -1861,6 +1892,10 @@
if(ftruncate(mFd, endOffset) == -1) {
ALOGE("ftruncate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
status = false;
+ /* No need to post and handle(stop & notify client) error like it's done in preAllocate(),
+ * because ftruncate() is called during release() only and the error here would be
+ * reported from there as this function is returning false on any error in ftruncate().
+ */
}
return status;
}
@@ -2153,14 +2188,17 @@
mElstTableEntries->add(htonl((((uint32_t)mediaRate) << 16) | (uint32_t)mediaRateFraction));
}
-void MPEG4Writer::setupAndStartLooper() {
+status_t MPEG4Writer::setupAndStartLooper() {
+ status_t err = OK;
if (mLooper == nullptr) {
mLooper = new ALooper;
mLooper->setName("MP4WtrCtrlHlpLooper");
- mLooper->start();
+ err = mLooper->start();
mReflector = new AHandlerReflector<MPEG4Writer>(this);
mLooper->registerHandler(mReflector);
}
+ ALOGD("MP4WtrCtrlHlpLooper Started");
+ return err;
}
void MPEG4Writer::stopAndReleaseLooper() {
@@ -2399,23 +2437,35 @@
case kWhatIOError: {
ALOGE("kWhatIOError");
int32_t err;
- CHECK(msg->findInt32("errno", &err));
+ CHECK(msg->findInt32("err", &err));
// Stop tracks' threads and main writer thread.
stop();
notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
break;
}
- // fallocate() failed, hence notify app about it and stop().
+ // fallocate() failed, hence stop() and notify app.
case kWhatFallocateError: {
ALOGE("kWhatFallocateError");
int32_t err;
- CHECK(msg->findInt32("errno", &err));
+ CHECK(msg->findInt32("err", &err));
// Stop tracks' threads and main writer thread.
stop();
//TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN?
notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
break;
}
+ /* Response to kWhatNoIOErrorSoFar would be OK always as of now.
+ * Responding with other options could be added later if required.
+ */
+ case kWhatNoIOErrorSoFar: {
+ ALOGD("kWhatNoIOErrorSoFar");
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", OK);
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+ break;
+ }
default:
TRESPASS();
}
@@ -2778,11 +2828,16 @@
mDone = true;
void *dummy;
- status_t err = pthread_join(mThread, &dummy);
- WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err);
- status_t threadRetVal = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
- WARN_UNLESS(threadRetVal == 0, "%s track stopped. Status :%d. %s source",
- getTrackType(), err, stopSource ? "Stop" : "Not Stop");
+ status_t err = OK;
+ int retVal = pthread_join(mThread, &dummy);
+ if (retVal == 0) {
+ err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
+ ALOGD("%s track stopped. Status:%d. %s source",
+ getTrackType(), err, stopSource ? "Stop" : "Not Stop");
+ } else {
+ ALOGE("track::stop: pthread_join retVal:%d", retVal);
+ err = UNKNOWN_ERROR;
+ }
mStarted = false;
return err;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7f5e762..5d17f97 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1482,9 +1482,9 @@
return PostAndAwaitResponse(msg, &response);
}
-status_t MediaCodec::releaseAsync() {
+status_t MediaCodec::releaseAsync(const sp<AMessage> ¬ify) {
sp<AMessage> msg = new AMessage(kWhatRelease, this);
- msg->setInt32("async", 1);
+ msg->setMessage("async", notify);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
@@ -2496,6 +2496,18 @@
}
break;
}
+ if (!mLeftover.empty()) {
+ ssize_t index = dequeuePortBuffer(kPortIndexInput);
+ CHECK_GE(index, 0);
+
+ status_t err = handleLeftover(index);
+ if (err != OK) {
+ setStickyError(err);
+ postActivityNotificationIfPossible();
+ cancelPendingDequeueOperations();
+ }
+ break;
+ }
if (mFlags & kFlagIsAsync) {
if (!mHaveInputSurface) {
@@ -2683,6 +2695,11 @@
if (mReplyID != nullptr) {
(new AMessage)->postReply(mReplyID);
}
+ if (mAsyncReleaseCompleteNotification != nullptr) {
+ flushMediametrics();
+ mAsyncReleaseCompleteNotification->post();
+ mAsyncReleaseCompleteNotification.clear();
+ }
break;
}
@@ -3069,8 +3086,8 @@
break;
}
- int32_t async = 0;
- if (msg->findInt32("async", &async) && async) {
+ sp<AMessage> asyncNotify;
+ if (msg->findMessage("async", &asyncNotify) && asyncNotify != nullptr) {
if (mSurface != NULL) {
if (!mReleaseSurface) {
mReleaseSurface.reset(new ReleaseSurface);
@@ -3102,10 +3119,11 @@
pushBlankBuffersToNativeWindow(mSurface.get());
}
- if (async) {
+ if (asyncNotify != nullptr) {
mResourceManagerProxy->markClientForPendingRemoval();
(new AMessage)->postReply(mReplyID);
mReplyID = 0;
+ mAsyncReleaseCompleteNotification = asyncNotify;
}
break;
@@ -3185,7 +3203,15 @@
break;
}
- status_t err = onQueueInputBuffer(msg);
+ status_t err = UNKNOWN_ERROR;
+ if (!mLeftover.empty()) {
+ mLeftover.push_back(msg);
+ size_t index;
+ msg->findSize("index", &index);
+ err = handleLeftover(index);
+ } else {
+ err = onQueueInputBuffer(msg);
+ }
PostReplyWithError(replyID, err);
break;
@@ -3472,8 +3498,8 @@
sp<hardware::HidlMemory> memory;
size_t offset = 0;
- if ((mFlags & kFlagUseBlockModel) && mOwnerName.startsWith("codec2::")) {
- if (mCrypto) {
+ if (mFlags & kFlagUseBlockModel) {
+ if (hasCryptoOrDescrambler()) {
constexpr size_t kInitialDealerCapacity = 1048576; // 1MB
thread_local sp<MemoryDealer> sDealer = new MemoryDealer(
kInitialDealerCapacity, "CSD(1MB)");
@@ -3598,6 +3624,9 @@
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
Mutex::Autolock al(mBufferLock);
+ if (portIndex == kPortIndexInput) {
+ mLeftover.clear();
+ }
for (size_t i = 0; i < mPortBuffers[portIndex].size(); ++i) {
BufferInfo *info = &mPortBuffers[portIndex][i];
@@ -3728,7 +3757,26 @@
err = mBufferChannel->attachEncryptedBuffer(
memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
offset, subSamples, numSubSamples, buffer);
+ } else {
+ err = UNKNOWN_ERROR;
}
+
+ if (err == OK && !buffer->asC2Buffer()
+ && c2Buffer && c2Buffer->data().type() == C2BufferData::LINEAR) {
+ C2ConstLinearBlock block{c2Buffer->data().linearBlocks().front()};
+ if (block.size() > buffer->size()) {
+ C2ConstLinearBlock leftover = block.subBlock(
+ block.offset() + buffer->size(), block.size() - buffer->size());
+ sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
+ new WrapperObject<std::shared_ptr<C2Buffer>>{
+ C2Buffer::CreateLinearBuffer(leftover)}};
+ msg->setObject("c2buffer", obj);
+ mLeftover.push_front(msg);
+ // Not sending EOS if we have leftovers
+ flags &= ~BUFFER_FLAG_EOS;
+ }
+ }
+
offset = buffer->offset();
size = buffer->size();
if (err != OK) {
@@ -3793,6 +3841,16 @@
return err;
}
+status_t MediaCodec::handleLeftover(size_t index) {
+ if (mLeftover.empty()) {
+ return OK;
+ }
+ sp<AMessage> msg = mLeftover.front();
+ mLeftover.pop_front();
+ msg->setSize("index", index);
+ return onQueueInputBuffer(msg);
+}
+
//static
size_t MediaCodec::CreateFramesRenderedMessage(
const std::list<FrameRenderTracker::Info> &done, sp<AMessage> &msg) {
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index e048f07..a1fe57c 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -87,7 +87,8 @@
enum {
kWhatSwitch = 'swch',
kWhatIOError = 'ioer',
- kWhatFallocateError = 'faer'
+ kWhatFallocateError = 'faer',
+ kWhatNoIOErrorSoFar = 'noie'
};
int mFd;
@@ -231,7 +232,7 @@
status_t stopWriterThread();
static void *ThreadWrapper(void *me);
void threadFunc();
- void setupAndStartLooper();
+ status_t setupAndStartLooper();
void stopAndReleaseLooper();
// Buffer a single chunk to be written out later.
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 7f308c0..f7e6c27 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -139,7 +139,7 @@
// object.
status_t release();
- status_t releaseAsync();
+ status_t releaseAsync(const sp<AMessage> ¬ify);
status_t flush();
@@ -383,6 +383,7 @@
sp<AMessage> mInputFormat;
sp<AMessage> mCallback;
sp<AMessage> mOnFrameRenderedNotification;
+ sp<AMessage> mAsyncReleaseCompleteNotification;
sp<ResourceManagerServiceProxy> mResourceManagerProxy;
@@ -515,6 +516,9 @@
class ReleaseSurface;
std::unique_ptr<ReleaseSurface> mReleaseSurface;
+ std::list<sp<AMessage>> mLeftover;
+ status_t handleLeftover(size_t index);
+
sp<BatteryChecker> mBatteryChecker;
void statsBufferSent(int64_t presentationUs);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 849c050..024168b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7366,8 +7366,10 @@
// Update server timestamp with server stats
// systemTime() is optional if the hardware supports timestamps.
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
+ if (framesRead >= 0) {
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
+ }
// Update server timestamp with kernel stats
if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 0c5d1d0..923310c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -35,6 +35,7 @@
namespace android {
+class AudioPolicyMix;
class DeviceDescriptor;
class HwAudioOutputDescriptor;
class SwAudioOutputDescriptor;
@@ -90,11 +91,12 @@
product_strategy_t strategy, VolumeSource volumeSource,
audio_output_flags_t flags,
bool isPreferredDeviceForExclusiveUse,
- std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs) :
+ std::vector<wp<SwAudioOutputDescriptor>> secondaryOutputs,
+ wp<AudioPolicyMix> primaryMix) :
ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId,
isPreferredDeviceForExclusiveUse),
mStream(stream), mStrategy(strategy), mVolumeSource(volumeSource), mFlags(flags),
- mSecondaryOutputs(std::move(secondaryOutputs)) {}
+ mSecondaryOutputs(std::move(secondaryOutputs)), mPrimaryMix(primaryMix) {}
~TrackClientDescriptor() override = default;
using ClientDescriptor::dump;
@@ -108,6 +110,9 @@
return mSecondaryOutputs;
};
VolumeSource volumeSource() const { return mVolumeSource; }
+ const sp<AudioPolicyMix> getPrimaryMix() const {
+ return mPrimaryMix.promote();
+ };
void setActive(bool active) override
{
@@ -136,7 +141,7 @@
const VolumeSource mVolumeSource;
const audio_output_flags_t mFlags;
const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
-
+ const wp<AudioPolicyMix> mPrimaryMix;
/**
* required for duplicating thread, prevent from removing active client from an output
* involved in a duplication.
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index fc1a59f..b6de4be 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -28,7 +28,7 @@
void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const
{
- dst->appendFormat("%*sAudio Policy Mix %d:\n", spaces, "", index + 1);
+ dst->appendFormat("%*sAudio Policy Mix %d (%p):\n", spaces, "", index + 1, this);
std::string mixTypeLiteral;
if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) {
ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType);
@@ -44,6 +44,9 @@
dst->appendFormat("%*s- device address: %s\n", spaces, "", mDeviceAddress.string());
+ dst->appendFormat("%*s- output: %d\n", spaces, "",
+ mOutput == nullptr ? 0 : mOutput->mIoHandle);
+
int indexCriterion = 0;
for (const auto &criterion : mCriteria) {
dst->appendFormat("%*s- Criterion %d: ", spaces + 2, "", indexCriterion++);
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 95822b9..afc4d01 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -23,6 +23,7 @@
#include <TypeConverter.h>
#include "AudioOutputDescriptor.h"
#include "AudioPatch.h"
+#include "AudioPolicyMix.h"
#include "ClientDescriptor.h"
#include "DeviceDescriptor.h"
#include "HwModule.h"
@@ -55,6 +56,12 @@
ClientDescriptor::dump(dst, spaces, index);
dst->appendFormat("%*s- Stream: %d flags: %08x\n", spaces, "", mStream, mFlags);
dst->appendFormat("%*s- Refcount: %d\n", spaces, "", mActivityCount);
+ dst->appendFormat("%*s- DAP Primary Mix: %p\n", spaces, "", mPrimaryMix.promote().get());
+ dst->appendFormat("%*s- DAP Secondary Outputs:\n", spaces, "");
+ for (auto desc : mSecondaryOutputs) {
+ dst->appendFormat("%*s - %d\n", spaces, "",
+ desc.promote() == nullptr ? 0 : desc.promote()->mIoHandle);
+ }
}
std::string TrackClientDescriptor::toShortString() const
@@ -88,7 +95,7 @@
TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
{config.sample_rate, config.channel_mask, config.format}, AUDIO_PORT_HANDLE_NONE,
stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false,
- {} /* Sources do not support secondary outputs*/), mSrcDevice(srcDevice)
+ {} /* Sources do not support secondary outputs*/, nullptr), mSrcDevice(srcDevice)
{
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index f05be06..3730c34 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1102,14 +1102,15 @@
};
*portId = PolicyAudioPort::getNextUniqueId();
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
sp<TrackClientDescriptor> clientDesc =
new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
sanitizedRequestedPortId, *stream,
mEngine->getProductStrategyForAttributes(resultAttr),
toVolumeSource(resultAttr),
*flags, isRequestedDeviceForExclusiveUse,
- std::move(weakSecondaryOutputDescs));
- sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
+ std::move(weakSecondaryOutputDescs),
+ outputDesc->mPolicyMix);
outputDesc->addClient(clientDesc);
ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__,
@@ -2877,7 +2878,7 @@
{
ALOGV("registerPolicyMixes() %zu mix(es)", mixes.size());
status_t res = NO_ERROR;
-
+ bool checkOutputs = false;
sp<HwModule> rSubmixModule;
// examine each mix's route type
for (size_t i = 0; i < mixes.size(); i++) {
@@ -2996,11 +2997,16 @@
i, type, address.string());
res = INVALID_OPERATION;
break;
+ } else {
+ checkOutputs = true;
}
}
}
if (res != NO_ERROR) {
unregisterPolicyMixes(mixes);
+ } else if (checkOutputs) {
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
}
return res;
}
@@ -3009,6 +3015,7 @@
{
ALOGV("unregisterPolicyMixes() num mixes %zu", mixes.size());
status_t res = NO_ERROR;
+ bool checkOutputs = false;
sp<HwModule> rSubmixModule;
// examine each mix's route type
for (const auto& mix : mixes) {
@@ -3049,9 +3056,15 @@
if (mPolicyMixes.unregisterMix(mix) != NO_ERROR) {
res = INVALID_OPERATION;
continue;
+ } else {
+ checkOutputs = true;
}
}
}
+ if (res == NO_ERROR && checkOutputs) {
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+ }
return res;
}
@@ -5226,32 +5239,38 @@
SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevices(oldDevices, mPreviousOutputs);
SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevices(newDevices, mOutputs);
- // also take into account external policy-related changes: add all outputs which are
- // associated with policies in the "before" and "after" output vectors
- ALOGVV("%s(): policy related outputs", __func__);
- bool hasDynamicPolicy = false;
- for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
- const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
- if (desc != 0 && desc->mPolicyMix != NULL) {
- srcOutputs.add(desc->mIoHandle);
- hasDynamicPolicy = true;
- ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
+ uint32_t maxLatency = 0;
+ bool invalidate = false;
+ // take into account dynamic audio policies related changes: if a client is now associated
+ // to a different policy mix than at creation time, invalidate corresponding stream
+ for (size_t i = 0; i < mPreviousOutputs.size() && !invalidate; i++) {
+ const sp<SwAudioOutputDescriptor>& desc = mPreviousOutputs.valueAt(i);
+ if (desc->isDuplicated()) {
+ continue;
}
- }
- for (size_t i = 0 ; i < mOutputs.size() ; i++) {
- const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
- if (desc != 0 && desc->mPolicyMix != NULL) {
- dstOutputs.add(desc->mIoHandle);
- hasDynamicPolicy = true;
- ALOGVV(" new outputs: adding %d", desc->mIoHandle);
+ for (const sp<TrackClientDescriptor>& client : desc->getClientIterable()) {
+ if (mEngine->getProductStrategyForAttributes(client->attributes()) != psId) {
+ continue;
+ }
+ sp<AudioPolicyMix> primaryMix;
+ status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
+ client->flags(), primaryMix, nullptr);
+ if (status != OK) {
+ continue;
+ }
+ if (client->getPrimaryMix() != primaryMix) {
+ invalidate = true;
+ if (desc->isStrategyActive(psId)) {
+ maxLatency = desc->latency();
+ }
+ break;
+ }
}
}
- if (srcOutputs != dstOutputs) {
+ if (srcOutputs != dstOutputs || invalidate) {
// get maximum latency of all source outputs to determine the minimum mute time guaranteeing
// audio from invalidated tracks will be rendered when unmuting
- uint32_t maxLatency = 0;
- bool invalidate = hasDynamicPolicy;
for (audio_io_handle_t srcOut : srcOutputs) {
sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueFor(srcOut);
if (desc == nullptr) continue;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index bac9430..2dbe2e5 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1377,7 +1377,12 @@
Mutex::Autolock l(mLogLock);
mEventLog.add(msg);
- return -EBUSY;
+ auto current = mActiveClientManager.get(cameraId);
+ if (current != nullptr) {
+ return -EBUSY; // CAMERA_IN_USE
+ } else {
+ return -EUSERS; // MAX_CAMERAS_IN_USE
+ }
}
for (auto& i : evicted) {
@@ -1669,6 +1674,10 @@
return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
"Higher-priority client using camera, ID \"%s\" currently unavailable",
cameraId.string());
+ case -EUSERS:
+ return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
+ "Too many cameras already open, cannot open camera \"%s\"",
+ cameraId.string());
default:
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Unexpected error %s (%d) opening camera \"%s\"",
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b00a2d9..deeec7e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1777,13 +1777,6 @@
mStatusChanged.broadcast();
}
-void Camera3Device::pauseStateNotify(bool enable) {
- Mutex::Autolock il(mInterfaceLock);
- Mutex::Autolock l(mLock);
-
- mPauseStateNotify = enable;
-}
-
// Pause to reconfigure
status_t Camera3Device::internalPauseAndWaitLocked(nsecs_t maxExpectedDuration) {
if (mRequestThread.get() != nullptr) {
@@ -2359,7 +2352,7 @@
return false;
}
-bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
+bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams, int clientStatusId) {
ATRACE_CALL();
bool ret = false;
@@ -2373,7 +2366,16 @@
return true;
}
- auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
+ status_t rc = NO_ERROR;
+ bool markClientActive = false;
+ if (mStatus == STATUS_ACTIVE) {
+ markClientActive = true;
+ mPauseStateNotify = true;
+ mStatusTracker->markComponentIdle(clientStatusId, Fence::NO_FENCE);
+
+ rc = internalPauseAndWaitLocked(maxExpectedDuration);
+ }
+
if (rc == NO_ERROR) {
mNeedConfig = true;
rc = configureStreamsLocked(mOperatingMode, sessionParams, /*notifyRequestThread*/ false);
@@ -2401,6 +2403,10 @@
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
}
+ if (markClientActive) {
+ mStatusTracker->markComponentActive(clientStatusId);
+ }
+
return ret;
}
@@ -4277,22 +4283,11 @@
}
if (res == OK) {
- sp<StatusTracker> statusTracker = mStatusTracker.promote();
- if (statusTracker != 0) {
- sp<Camera3Device> parent = mParent.promote();
- if (parent != nullptr) {
- parent->pauseStateNotify(true);
- }
-
- statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
-
- if (parent != nullptr) {
- mReconfigured |= parent->reconfigureCamera(mLatestSessionParams);
- }
-
- statusTracker->markComponentActive(mStatusId);
- setPaused(false);
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != nullptr) {
+ mReconfigured |= parent->reconfigureCamera(mLatestSessionParams, mStatusId);
}
+ setPaused(false);
if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
mNextRequests[0].captureRequest->mInputStream->restoreConfiguredState();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index c059f55..2069841 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -638,17 +638,10 @@
const SurfaceMap &surfaceMap);
/**
- * Pause state updates to the client application. Needed to mask out idle/active
- * transitions during internal reconfigure
- */
- void pauseStateNotify(bool enable);
-
- /**
* Internally re-configure camera device using new session parameters.
- * This will get triggered by the request thread. Be sure to call
- * pauseStateNotify(true) before going idle in the requesting location.
+ * This will get triggered by the request thread.
*/
- bool reconfigureCamera(const CameraMetadata& sessionParams);
+ bool reconfigureCamera(const CameraMetadata& sessionParams, int clientStatusId);
/**
* Return true in case of any output or input abandoned streams,
diff --git a/services/camera/libcameraservice/tests/ClientManagerTest.cpp b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
new file mode 100644
index 0000000..6a38427
--- /dev/null
+++ b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 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 "ClientManagerTest"
+
+#include "../utils/ClientManager.h"
+#include <gtest/gtest.h>
+
+using namespace android::resource_policy;
+
+struct TestClient {
+ TestClient(int id, int32_t cost, const std::set<int>& conflictingKeys, int32_t ownerId,
+ int32_t score, int32_t state, bool isVendorClient) :
+ mId(id), mCost(cost), mConflictingKeys(conflictingKeys),
+ mOwnerId(ownerId), mScore(score), mState(state), mIsVendorClient(isVendorClient) {};
+ int mId;
+ int32_t mCost; // Int 0..100
+ std::set<int> mConflictingKeys;
+ int32_t mOwnerId; // PID
+ int32_t mScore; // Priority
+ int32_t mState; // Foreground/background etc
+ bool mIsVendorClient;
+};
+
+using TestClientDescriptor = ClientDescriptor<int, TestClient>;
+using TestDescriptorPtr = std::shared_ptr<TestClientDescriptor>;
+
+TestDescriptorPtr makeDescFromTestClient(const TestClient& tc) {
+ return std::make_shared<TestClientDescriptor>(/*ID*/tc.mId, tc, tc.mCost, tc.mConflictingKeys,
+ tc.mScore, tc.mOwnerId, tc.mState, tc.mIsVendorClient);
+}
+
+class TestClientManager : public ClientManager<int, TestClient> {
+public:
+ TestClientManager() {}
+ virtual ~TestClientManager() {}
+};
+
+
+// Test ClientMager behavior when there is only one single owner
+// The expected behavior is that if one owner (application or vendor) is trying
+// to open second camera, it may succeed or not, but the first opened camera
+// should never be evicted.
+TEST(ClientManagerTest, SingleOwnerMultipleCamera) {
+
+ TestClientManager cm;
+ TestClient cam0Client(/*ID*/0, /*cost*/100, /*conflicts*/{1},
+ /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ auto cam0Desc = makeDescFromTestClient(cam0Client);
+ auto evicted = cm.addAndEvict(cam0Desc);
+ ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+
+ TestClient cam1Client(/*ID*/1, /*cost*/100, /*conflicts*/{0},
+ /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ auto cam1Desc = makeDescFromTestClient(cam1Client);
+
+ // 1. Check with conflicting devices, new client would be evicted
+ auto wouldBeEvicted = cm.wouldEvict(cam1Desc);
+ ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
+ ASSERT_EQ(wouldBeEvicted[0]->getKey(), cam1Desc->getKey()) << "cam1 must be evicted";
+
+ cm.removeAll();
+
+ TestClient cam2Client(/*ID*/2, /*cost*/100, /*conflicts*/{},
+ /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ auto cam2Desc = makeDescFromTestClient(cam2Client);
+ evicted = cm.addAndEvict(cam2Desc);
+ ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+
+ TestClient cam3Client(/*ID*/3, /*cost*/100, /*conflicts*/{},
+ /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ auto cam3Desc = makeDescFromTestClient(cam3Client);
+
+ // 2. Check without conflicting devices, the pre-existing client won't be evicted
+ // In this case, the new client would be granted, but could later be rejected by HAL due to
+ // resource cost.
+ wouldBeEvicted = cm.wouldEvict(cam3Desc);
+ ASSERT_EQ(wouldBeEvicted.size(), 0u) << "Evicted list must be empty";
+
+ cm.removeAll();
+
+ evicted = cm.addAndEvict(cam0Desc);
+ ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+
+ TestClient cam0ClientNew(/*ID*/0, /*cost*/100, /*conflicts*/{1},
+ /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ auto cam0DescNew = makeDescFromTestClient(cam0ClientNew);
+ wouldBeEvicted = cm.wouldEvict(cam0DescNew);
+
+ // 3. Check opening the same camera twice will evict the older client
+ ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
+ ASSERT_EQ(wouldBeEvicted[0], cam0Desc) << "cam0 (old) must be evicted";
+}
+
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index 35d25bf..64be6c5 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -496,6 +496,20 @@
evictList.clear();
evictList.push_back(client);
return evictList;
+ } else if (conflicting && owner == curOwner) {
+ // Pre-existing conflicting client with the same client owner exists
+ // Open the same device twice -> most recent open wins
+ // Otherwise let the existing client wins to avoid behaviors difference
+ // due to how HAL advertising conflicting devices (which is hidden from
+ // application)
+ if (curKey == key) {
+ evictList.push_back(i);
+ totalCost -= curCost;
+ } else {
+ evictList.clear();
+ evictList.push_back(client);
+ return evictList;
+ }
} else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
(curPriority >= priority) &&
!(highestPriorityOwner == owner && owner == curOwner))) {
diff --git a/services/mediametrics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
index 89d6f8f..ac58929 100644
--- a/services/mediametrics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -50,30 +50,26 @@
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
- char *vendor = nullptr;
- (void) item->getCString("vendor", &vendor);
- char *description = nullptr;
- (void) item->getCString("description", &description);
- char *serialized_metrics = nullptr;
- (void) item->getCString("serialized_metrics", &serialized_metrics);
+ std::string vendor;
+ (void) item->getString("vendor", &vendor);
+ std::string description;
+ (void) item->getString("description", &description);
+ std::string serialized_metrics;
+ (void) item->getString("serialized_metrics", &serialized_metrics);
if (enabled_statsd) {
- android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : nullptr,
- serialized_metrics ? strlen(serialized_metrics)
- : 0);
+ android::util::BytesField bf_serialized(serialized_metrics.c_str(),
+ serialized_metrics.size());
android::util::stats_write(android::util::MEDIAMETRICS_MEDIADRM_REPORTED,
timestamp, pkgName.c_str(), pkgVersionCode,
mediaApexVersion,
- vendor, description,
+ vendor.c_str(),
+ description.c_str(),
bf_serialized);
} else {
- ALOGV("NOT sending: mediadrm private data (len=%zu)",
- serialized_metrics ? strlen(serialized_metrics) : 0);
+ ALOGV("NOT sending: mediadrm private data (len=%zu)", serialized_metrics.size());
}
- free(vendor);
- free(description);
- free(serialized_metrics);
return true;
}
@@ -87,23 +83,20 @@
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
- char *serialized_metrics = nullptr;
- (void) item->getCString("serialized_metrics", &serialized_metrics);
+ std::string serialized_metrics;
+ (void) item->getString("serialized_metrics", &serialized_metrics);
if (enabled_statsd) {
- android::util::BytesField bf_serialized(serialized_metrics ? serialized_metrics : nullptr,
- serialized_metrics ? strlen(serialized_metrics)
- : 0);
+ android::util::BytesField bf_serialized(serialized_metrics.c_str(),
+ serialized_metrics.size());
android::util::stats_write(android::util::MEDIAMETRICS_DRM_WIDEVINE_REPORTED,
timestamp, pkgName.c_str(), pkgVersionCode,
mediaApexVersion,
bf_serialized);
} else {
- ALOGV("NOT sending: widevine private data (len=%zu)",
- serialized_metrics ? strlen(serialized_metrics) : 0);
+ ALOGV("NOT sending: widevine private data (len=%zu)", serialized_metrics.size());
}
- free(serialized_metrics);
return true;
}
@@ -123,14 +116,14 @@
int64_t pkgVersionCode = item->getPkgVersionCode();
int64_t mediaApexVersion = 0;
- char *plugin_id = nullptr;
- (void) item->getCString("plugin_id", &plugin_id);
- char *description = nullptr;
- (void) item->getCString("description", &description);
+ std::string plugin_id;
+ (void) item->getString("plugin_id", &plugin_id);
+ std::string description;
+ (void) item->getString("description", &description);
int32_t method_id = -1;
(void) item->getInt32("method_id", &method_id);
- char *mime_types = nullptr;
- (void) item->getCString("mime_types", &mime_types);
+ std::string mime_types;
+ (void) item->getString("mime_types", &mime_types);
// Corresponds to the 13 APIs tracked in the MediametricsDrmManagerReported statsd proto
// Please see also DrmManager::kMethodIdMap
@@ -141,16 +134,15 @@
android::util::stats_write(android::util::MEDIAMETRICS_DRMMANAGER_REPORTED,
timestamp, pkgName.c_str(), pkgVersionCode, mediaApexVersion,
- plugin_id, description, method_id, mime_types,
+ plugin_id.c_str(), description.c_str(),
+ method_id, mime_types.c_str(),
methodCounts[0], methodCounts[1], methodCounts[2],
methodCounts[3], methodCounts[4], methodCounts[5],
methodCounts[6], methodCounts[7], methodCounts[8],
methodCounts[9], methodCounts[10], methodCounts[11],
methodCounts[12]);
- free(plugin_id);
- free(description);
- free(mime_types);
return true;
}
+
} // namespace android
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 15cbd82..ceefe93 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -95,9 +95,16 @@
mRegisteredStreams.swap(streamsDisconnected);
}
mConnected.store(false);
+ // We need to stop all the streams before we disconnect them.
+ // Otherwise there is a race condition where the first disconnected app
+ // tries to reopen a stream as MMAP but is blocked by the second stream,
+ // which hasn't stopped yet. Then the first app ends up with a Legacy stream.
for (const auto &stream : streamsDisconnected) {
- ALOGD("%s() - stop and disconnect port %d", __func__, stream->getPortHandle());
+ ALOGD("%s() - stop(), port = %d", __func__, stream->getPortHandle());
stream->stop();
+ }
+ for (const auto &stream : streamsDisconnected) {
+ ALOGD("%s() - disconnect(), port = %d", __func__, stream->getPortHandle());
stream->disconnect();
}
return streamsDisconnected;