Merge "point to new repo hooks for mainline uploads"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5cb5222..8fe48c2 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,3 +8,4 @@
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
media/libmediatranscoding/
+ services/mediatranscoding/
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/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
index 9cbfdb0..755051c 100644
--- a/camera/CaptureResult.cpp
+++ b/camera/CaptureResult.cpp
@@ -49,6 +49,9 @@
}
errorPhysicalCameraId = cameraId;
}
+ parcel->readInt64(&lastCompletedRegularFrameNumber);
+ parcel->readInt64(&lastCompletedReprocessFrameNumber);
+ parcel->readInt64(&lastCompletedZslFrameNumber);
return OK;
}
@@ -76,6 +79,9 @@
} else {
parcel->writeBool(false);
}
+ parcel->writeInt64(lastCompletedRegularFrameNumber);
+ parcel->writeInt64(lastCompletedReprocessFrameNumber);
+ parcel->writeInt64(lastCompletedZslFrameNumber);
return OK;
}
diff --git a/camera/include/camera/CaptureResult.h b/camera/include/camera/CaptureResult.h
index dc3d282..f163c1e 100644
--- a/camera/include/camera/CaptureResult.h
+++ b/camera/include/camera/CaptureResult.h
@@ -76,6 +76,34 @@
*/
String16 errorPhysicalCameraId;
+ // The last completed frame numbers shouldn't be checked in onResultReceived() and notifyError()
+ // because the output buffers could be arriving after onResultReceived() and
+ // notifyError(). Given this constraint, we check it for each
+ // onCaptureStarted, and if there is no further onCaptureStarted(),
+ // check for onDeviceIdle() to clear out all pending frame numbers.
+
+ /**
+ * The latest regular request frameNumber for which all buffers and capture result have been
+ * returned or notified as an BUFFER_ERROR/RESULT_ERROR/REQUEST_ERROR. -1 if
+ * none has completed.
+ */
+ int64_t lastCompletedRegularFrameNumber;
+
+ /**
+ * The latest reprocess request frameNumber for which all buffers and capture result have been
+ * returned or notified as an BUFFER_ERROR/RESULT_ERROR/REQUEST_ERROR. -1 if
+ * none has completed.
+ */
+ int64_t lastCompletedReprocessFrameNumber;
+
+ /**
+ * The latest Zsl request frameNumber for which all buffers and capture result have been
+ * returned or notified as an BUFFER_ERROR/RESULT_ERROR/REQUEST_ERROR. -1 if
+ * none has completed.
+ */
+ int64_t lastCompletedZslFrameNumber;
+
+
/**
* Constructor initializes object as invalid by setting requestId to be -1.
*/
@@ -87,7 +115,10 @@
frameNumber(0),
partialResultCount(0),
errorStreamId(-1),
- errorPhysicalCameraId() {
+ errorPhysicalCameraId(),
+ lastCompletedRegularFrameNumber(-1),
+ lastCompletedReprocessFrameNumber(-1),
+ lastCompletedZslFrameNumber(-1) {
}
/**
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 0d7180a..c15c5a5 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1336,56 +1336,97 @@
void
CameraDevice::checkAndFireSequenceCompleteLocked() {
int64_t completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
- //std::map<int, int64_t> mSequenceLastFrameNumberMap;
auto it = mSequenceLastFrameNumberMap.begin();
while (it != mSequenceLastFrameNumberMap.end()) {
int sequenceId = it->first;
- int64_t lastFrameNumber = it->second;
- bool seqCompleted = false;
- bool hasCallback = true;
+ int64_t lastFrameNumber = it->second.lastFrameNumber;
+ bool hasCallback = true;
+
+ if (mRemote == nullptr) {
+ ALOGW("Camera %s closed while checking sequence complete", getId());
+ return;
+ }
+ ALOGV("%s: seq %d's last frame number %" PRId64 ", completed %" PRId64,
+ __FUNCTION__, sequenceId, lastFrameNumber, completedFrameNumber);
+ if (!it->second.isSequenceCompleted) {
+ // Check if there is callback for this sequence
+ // This should not happen because we always register callback (with nullptr inside)
+ if (mSequenceCallbackMap.count(sequenceId) == 0) {
+ ALOGW("No callback found for sequenceId %d", sequenceId);
+ hasCallback = false;
+ }
+
+ if (lastFrameNumber <= completedFrameNumber) {
+ ALOGV("Mark sequenceId %d as sequence completed", sequenceId);
+ it->second.isSequenceCompleted = true;
+ }
+
+ if (it->second.isSequenceCompleted && hasCallback) {
+ auto cbIt = mSequenceCallbackMap.find(sequenceId);
+ CallbackHolder cbh = cbIt->second;
+
+ // send seq complete callback
+ sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
+ msg->setPointer(kContextKey, cbh.mContext);
+ msg->setObject(kSessionSpKey, cbh.mSession);
+ msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
+ msg->setInt32(kSequenceIdKey, sequenceId);
+ msg->setInt64(kFrameNumberKey, lastFrameNumber);
+
+ // Clear the session sp before we send out the message
+ // This will guarantee the rare case where the message is processed
+ // before cbh goes out of scope and causing we call the session
+ // destructor while holding device lock
+ cbh.mSession.clear();
+ postSessionMsgAndCleanup(msg);
+ }
+ }
+
+ if (it->second.isSequenceCompleted && it->second.isInflightCompleted) {
+ if (mSequenceCallbackMap.find(sequenceId) != mSequenceCallbackMap.end()) {
+ mSequenceCallbackMap.erase(sequenceId);
+ }
+ it = mSequenceLastFrameNumberMap.erase(it);
+ ALOGV("%s: Remove holder for sequenceId %d", __FUNCTION__, sequenceId);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void
+CameraDevice::removeCompletedCallbackHolderLocked(int64_t lastCompletedRegularFrameNumber) {
+ auto it = mSequenceLastFrameNumberMap.begin();
+ while (it != mSequenceLastFrameNumberMap.end()) {
+ int sequenceId = it->first;
+ int64_t lastFrameNumber = it->second.lastFrameNumber;
if (mRemote == nullptr) {
ALOGW("Camera %s closed while checking sequence complete", getId());
return;
}
- // Check if there is callback for this sequence
- // This should not happen because we always register callback (with nullptr inside)
- if (mSequenceCallbackMap.count(sequenceId) == 0) {
- ALOGW("No callback found for sequenceId %d", sequenceId);
- hasCallback = false;
- }
+ ALOGV("%s: seq %d's last frame number %" PRId64
+ ", completed inflight frame number %" PRId64,
+ __FUNCTION__, sequenceId, lastFrameNumber,
+ lastCompletedRegularFrameNumber);
+ if (lastFrameNumber <= lastCompletedRegularFrameNumber) {
+ if (it->second.isSequenceCompleted) {
+ // Check if there is callback for this sequence
+ // This should not happen because we always register callback (with nullptr inside)
+ if (mSequenceCallbackMap.count(sequenceId) == 0) {
+ ALOGW("No callback found for sequenceId %d", sequenceId);
+ } else {
+ mSequenceCallbackMap.erase(sequenceId);
+ }
- if (lastFrameNumber <= completedFrameNumber) {
- ALOGV("seq %d reached last frame %" PRId64 ", completed %" PRId64,
- sequenceId, lastFrameNumber, completedFrameNumber);
- seqCompleted = true;
- }
-
- if (seqCompleted && hasCallback) {
- // remove callback holder from callback map
- auto cbIt = mSequenceCallbackMap.find(sequenceId);
- CallbackHolder cbh = cbIt->second;
- mSequenceCallbackMap.erase(cbIt);
- // send seq complete callback
- sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
- msg->setPointer(kContextKey, cbh.mContext);
- msg->setObject(kSessionSpKey, cbh.mSession);
- msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
- msg->setInt32(kSequenceIdKey, sequenceId);
- msg->setInt64(kFrameNumberKey, lastFrameNumber);
-
- // Clear the session sp before we send out the message
- // This will guarantee the rare case where the message is processed
- // before cbh goes out of scope and causing we call the session
- // destructor while holding device lock
- cbh.mSession.clear();
- postSessionMsgAndCleanup(msg);
- }
-
- // No need to track sequence complete if there is no callback registered
- if (seqCompleted || !hasCallback) {
- it = mSequenceLastFrameNumberMap.erase(it);
+ it = mSequenceLastFrameNumberMap.erase(it);
+ ALOGV("%s: Remove holder for sequenceId %d", __FUNCTION__, sequenceId);
+ } else {
+ ALOGV("Mark sequenceId %d as inflight completed", sequenceId);
+ it->second.isInflightCompleted = true;
+ ++it;
+ }
} else {
++it;
}
@@ -1480,6 +1521,9 @@
return ret;
}
+ dev->removeCompletedCallbackHolderLocked(
+ std::numeric_limits<int64_t>::max()/*lastCompletedRegularFrameNumber*/);
+
if (dev->mIdle) {
// Already in idle state. Possibly other thread did waitUntilIdle
return ret;
@@ -1522,6 +1566,9 @@
return ret;
}
+ dev->removeCompletedCallbackHolderLocked(
+ resultExtras.lastCompletedRegularFrameNumber);
+
int sequenceId = resultExtras.requestId;
int32_t burstId = resultExtras.burstId;
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 6c2ceb3..2f2299f 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -267,8 +267,15 @@
static const int REQUEST_ID_NONE = -1;
int mRepeatingSequenceId = REQUEST_ID_NONE;
- // sequence id -> last frame number map
- std::map<int, int64_t> mSequenceLastFrameNumberMap;
+ // sequence id -> last frame number holder map
+ struct RequestLastFrameNumberHolder {
+ int64_t lastFrameNumber;
+ bool isSequenceCompleted = false;
+ bool isInflightCompleted = false;
+ RequestLastFrameNumberHolder(int64_t lastFN) :
+ lastFrameNumber(lastFN) {}
+ };
+ std::map<int, RequestLastFrameNumberHolder> mSequenceLastFrameNumberMap;
struct CallbackHolder {
CallbackHolder(sp<ACameraCaptureSession> session,
@@ -338,6 +345,7 @@
void checkRepeatingSequenceCompleteLocked(const int sequenceId, const int64_t lastFrameNumber);
void checkAndFireSequenceCompleteLocked();
+ void removeCompletedCallbackHolderLocked(int64_t lastCompletedRegularFrameNumber);
// Misc variables
int32_t mShadingMapSize[2]; // const after constructor
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 907aa39..4b158bd 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>
@@ -1242,62 +1244,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;
}
diff --git a/media/codecs/g711/decoder/Android.bp b/media/codecs/g711/decoder/Android.bp
index 377833f..efff60b 100644
--- a/media/codecs/g711/decoder/Android.bp
+++ b/media/codecs/g711/decoder/Android.bp
@@ -36,6 +36,7 @@
cfi: true,
},
apex_available: ["com.android.media.swcodec"],
+ min_sdk_version: "29",
target: {
darwin: {
diff --git a/media/extractors/Android.bp b/media/extractors/Android.bp
index 7c4e62f..f9abfe3 100644
--- a/media/extractors/Android.bp
+++ b/media/extractors/Android.bp
@@ -21,7 +21,6 @@
shared_libs: [
"liblog",
- "libmediandk#29",
],
// extractors are supposed to work on Q(29)
@@ -39,6 +38,21 @@
version_script: "exports.lds",
+ target: {
+ android: {
+ shared_libs: [
+ "libmediandk#29",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libutils",
+ "libmediandk_format",
+ "libmedia_ndkformatpriv",
+ ],
+ },
+ },
+
sanitize: {
cfi: true,
misc_undefined: [
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
index 818e466..0a70815 100644
--- a/media/extractors/fuzzers/Android.bp
+++ b/media/extractors/fuzzers/Android.bp
@@ -71,10 +71,15 @@
"frameworks/av/media/extractors/mp4",
],
+ header_libs: [
+ "libaudioclient_headers",
+ ],
+
static_libs: [
"liblog",
"libstagefright_foundation",
- "libmedia",
+ "libmediandk_format",
+ "libmedia_ndkformatpriv",
"libextractorfuzzerbase",
"libstagefright_id3",
"libstagefright_esds",
@@ -83,7 +88,6 @@
shared_libs: [
"libutils",
- "libmediandk",
"libbinder",
],
@@ -95,6 +99,8 @@
],
componentid: 155276,
},
+
+ host_supported: true,
}
cc_fuzz {
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index e48e1b7..afa055f 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -16,4 +16,12 @@
"libstagefright_id3",
"libutils",
],
+
+ host_supported: true,
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp
index 33d6b7a..a18b122 100644
--- a/media/extractors/tests/ExtractorUnitTest.cpp
+++ b/media/extractors/tests/ExtractorUnitTest.cpp
@@ -726,6 +726,9 @@
if (configParam.profile != kUndefined) {
if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_PROFILE, &profile)) {
ASSERT_EQ(configParam.profile, profile) << "profile not as expected";
+ } else if (mExtractorName == AAC &&
+ AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_AAC_PROFILE, &profile)) {
+ ASSERT_EQ(configParam.profile, profile) << "profile not as expected";
} else {
ASSERT_TRUE(false) << "profile not returned in extractor";
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 79fa5ed..4520823 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -288,7 +288,7 @@
requestStop();
}
- logBufferState();
+ logReleaseBufferState();
setState(AAUDIO_STREAM_STATE_CLOSING);
aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
@@ -783,6 +783,14 @@
adjustedFrames = std::min(actualFrames, adjustedFrames);
}
+ if (adjustedFrames != mBufferSizeInFrames) {
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, adjustedFrames)
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
+ .record();
+ }
+
mBufferSizeInFrames = adjustedFrames;
ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
return (aaudio_result_t) adjustedFrames;
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 0644368..bc973bd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -116,9 +116,10 @@
}
}
-void AudioStream::logBufferState() {
+void AudioStream::logReleaseBufferState() {
if (mMetricsId.size() > 0) {
android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RELEASE)
.set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSize())
.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
.record();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 613a092..fb71c36 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -115,7 +115,7 @@
// log to MediaMetrics
virtual void logOpen();
- void logBufferState();
+ void logReleaseBufferState();
/**
* Free any hardware or system resources from the open() call.
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 853c0db..6e5110f 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -292,7 +292,7 @@
// Then call it from here
if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
- logBufferState();
+ logReleaseBufferState();
mAudioRecord.clear();
mFixedBlockWriter.close();
return AudioStream::release_l();
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1120f05..ea08361 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -254,7 +254,7 @@
aaudio_result_t AudioStreamTrack::release_l() {
if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
- logBufferState();
+ logReleaseBufferState();
// TODO Investigate why clear() causes a hang in test_various.cpp
// if I call close() from a data callback.
// But the same thing in AudioRecord is OK!
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index a6e5f70..8935d57 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -233,6 +233,7 @@
srcs: ["test_steal_exclusive.cpp"],
shared_libs: [
"libaaudio",
+ "liblog",
"libbinder",
"libcutils",
"libutils",
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
index 2a05910..05c560d 100644
--- a/media/libaaudio/tests/test_steal_exclusive.cpp
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -47,137 +47,271 @@
*/
#include <atomic>
+#include <mutex>
#include <stdio.h>
#include <thread>
#include <unistd.h>
+#include <android/log.h>
+
#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
#define SOLO_DURATION_MSEC 2000
#define DUET_DURATION_MSEC 8000
#define SLEEP_DURATION_MSEC 500
+#define MODULE_NAME "stealAudio"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__)
+
static const char * s_sharingModeToText(aaudio_sharing_mode_t mode) {
return (mode == AAUDIO_SHARING_MODE_EXCLUSIVE) ? "EXCLUSIVE"
: ((mode == AAUDIO_SHARING_MODE_SHARED) ? "SHARED"
: AAudio_convertResultToText(mode));
}
+static const char * s_performanceModeToText(aaudio_performance_mode_t mode) {
+ return (mode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) ? "LOWLAT"
+ : ((mode == AAUDIO_PERFORMANCE_MODE_NONE) ? "NONE"
+ : AAudio_convertResultToText(mode));
+}
+
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream * /* stream */,
+ void *userData,
+ void *audioData,
+ int32_t numFrames);
+
static void s_myErrorCallbackProc(
AAudioStream *stream,
void *userData,
aaudio_result_t error);
-struct AudioEngine {
- AAudioStream *stream = nullptr;
- std::thread *thread = nullptr;
- aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+class AudioEngine {
+public:
+
+ AudioEngine(const char *name) {
+ mName = name;
+ }
// These counters are read and written by the callback and the main thread.
- std::atomic<int32_t> framesRead{};
std::atomic<int32_t> framesCalled{};
std::atomic<int32_t> callbackCount{};
+ std::atomic<aaudio_sharing_mode_t> sharingMode{};
+ std::atomic<aaudio_performance_mode_t> performanceMode{};
+ std::atomic<bool> isMMap{false};
+ void setMaxRetries(int maxRetries) {
+ mMaxRetries = maxRetries;
+ }
+
+ void setOpenDelayMillis(int openDelayMillis) {
+ mOpenDelayMillis = openDelayMillis;
+ }
+
+ void restartStream() {
+ int retriesLeft = mMaxRetries;
+ aaudio_result_t result;
+ do {
+ closeAudioStream();
+ if (mOpenDelayMillis) usleep(mOpenDelayMillis * 1000);
+ 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
+ // a SHARED stream, which will not get stolen.
+ result = AAudioStream_requestStart(mStream);
+ printf("%s: AAudioStream_requestStart() returns %s\n",
+ mName.c_str(),
+ AAudio_convertResultToText(result));
+ } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+ }
+
+ aaudio_data_callback_result_t onAudioReady(
+ void * /*audioData */,
+ int32_t numFrames) {
+ callbackCount++;
+ framesCalled += numFrames;
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+ }
+
+ 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);
+ if (result != AAUDIO_OK) {
+ printf("AAudio_createStreamBuilder returned %s",
+ AAudio_convertResultToText(result));
+ return result;
+ }
+
+ // Request stream properties.
+ AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+ AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+ AAudioStreamBuilder_delete(builder);
+ builder = nullptr;
+ if (result != AAUDIO_OK) {
+ printf("AAudioStreamBuilder_openStream returned %s",
+ AAudio_convertResultToText(result));
+ }
+
+ // See what kind of stream we actually opened.
+ int32_t deviceId = AAudioStream_getDeviceId(mStream);
+ sharingMode = AAudioStream_getSharingMode(mStream);
+ performanceMode = AAudioStream_getPerformanceMode(mStream);
+ isMMap = AAudioStream_isMMapUsed(mStream);
+ printf("%s: opened: deviceId = %3d, sharingMode = %s, perf = %s, %s --------\n",
+ mName.c_str(),
+ deviceId,
+ s_sharingModeToText(sharingMode),
+ s_performanceModeToText(performanceMode),
+ (isMMap ? "MMAP" : "Legacy")
+ );
+
+ return result;
+ }
+
+ aaudio_result_t closeAudioStream() {
+ std::lock_guard<std::mutex> lock(mLock);
+ aaudio_result_t result = AAUDIO_OK;
+ if (mStream != nullptr) {
+ result = AAudioStream_close(mStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_close returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+ mStream = nullptr;
+ }
+ return result;
+ }
+
+ /**
+ * @return 0 is OK, -1 for error
+ */
+ int checkEnginePositions() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStream == nullptr) return 0;
+
+ const int64_t framesRead = AAudioStream_getFramesRead(mStream);
+ const int64_t framesWritten = AAudioStream_getFramesWritten(mStream);
+ const int32_t delta = (int32_t)(framesWritten - framesRead);
+ printf("%s: playing framesRead = %7d, framesWritten = %7d"
+ ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+ mName.c_str(),
+ (int32_t) framesRead,
+ (int32_t) framesWritten,
+ delta,
+ framesCalled.load(),
+ callbackCount.load()
+ );
+ if (delta > AAudioStream_getBufferCapacityInFrames(mStream)) {
+ printf("ERROR - delta > capacity\n");
+ return -1;
+ }
+ return 0;
+ }
+
+ aaudio_result_t start() {
+ std::lock_guard<std::mutex> lock(mLock);
+ reset();
+ if (mStream == nullptr) return 0;
+ return AAudioStream_requestStart(mStream);
+ }
+
+ aaudio_result_t stop() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStream == nullptr) return 0;
+ return AAudioStream_requestStop(mStream);
+ }
+
+ bool hasAdvanced() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStream == nullptr) return 0;
+ if (mDirection == AAUDIO_DIRECTION_OUTPUT) {
+ return AAudioStream_getFramesRead(mStream) > 0;
+ } else {
+ return AAudioStream_getFramesWritten(mStream) > 0;
+ }
+ }
+
+ aaudio_result_t verify() {
+ int errorCount = 0;
+ if (hasAdvanced()) {
+ printf("%s: stream is running => PASS\n", mName.c_str());
+ } else {
+ errorCount++;
+ printf("%s: stream should be running => FAIL!!\n", mName.c_str());
+ }
+
+ if (isMMap) {
+ printf("%s: data path is MMAP => PASS\n", mName.c_str());
+ } else {
+ errorCount++;
+ printf("%s: data path is Legacy! => FAIL\n", mName.c_str());
+ }
+
+ // Check for PASS/FAIL
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
+ printf("%s: mode is SHARED => PASS\n", mName.c_str());
+ } else {
+ errorCount++;
+ printf("%s: modes is EXCLUSIVE => FAIL!!\n", mName.c_str());
+ }
+ return errorCount ? AAUDIO_ERROR_INVALID_FORMAT : AAUDIO_OK;
+ }
+
+private:
void reset() {
- framesRead.store(0);
framesCalled.store(0);
callbackCount.store(0);
}
+
+ 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;
+ int mOpenDelayMillis = 0;
};
// Callback function that fills the audio output buffer.
static aaudio_data_callback_result_t s_myDataCallbackProc(
- AAudioStream *stream,
+ AAudioStream * /* stream */,
void *userData,
void *audioData,
int32_t numFrames
) {
- (void) audioData;
- (void) numFrames;
- AudioEngine *engine = (struct AudioEngine *)userData;
- engine->callbackCount++;
-
- engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
- engine->framesCalled += numFrames;
- return AAUDIO_CALLBACK_RESULT_CONTINUE;
-}
-
-static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
- aaudio_direction_t direction) {
- AAudioStreamBuilder *builder = nullptr;
- engine->direction = direction;
-
- // Use an AAudioStreamBuilder to contain requested parameters.
- aaudio_result_t result = AAudio_createStreamBuilder(&builder);
- if (result != AAUDIO_OK) {
- printf("AAudio_createStreamBuilder returned %s",
- AAudio_convertResultToText(result));
- return result;
- }
-
- // 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_setDirection(builder, direction);
- AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, engine);
- AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, engine);
-
- // Create an AAudioStream using the Builder.
- result = AAudioStreamBuilder_openStream(builder, &engine->stream);
- AAudioStreamBuilder_delete(builder);
- builder = nullptr;
- if (result != AAUDIO_OK) {
- printf("AAudioStreamBuilder_openStream returned %s",
- AAudio_convertResultToText(result));
- }
-
- // See see what kind of stream we actually opened.
- int32_t deviceId = AAudioStream_getDeviceId(engine->stream);
- aaudio_sharing_mode_t actualSharingMode = AAudioStream_getSharingMode(engine->stream);
- printf("-------- opened: deviceId = %3d, actualSharingMode = %s\n",
- deviceId,
- s_sharingModeToText(actualSharingMode));
-
- return result;
-}
-
-static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
- aaudio_result_t result = AAUDIO_OK;
- if (engine->stream != nullptr) {
- result = AAudioStream_close(engine->stream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_close returned %s\n",
- AAudio_convertResultToText(result));
- }
- engine->stream = nullptr;
- }
- return result;
+ AudioEngine *engine = (AudioEngine *)userData;
+ return engine->onAudioReady(audioData, numFrames);
}
static void s_myRestartStreamProc(void *userData) {
+ LOGI("%s() called", __func__);
printf("%s() - restart in separate thread\n", __func__);
AudioEngine *engine = (AudioEngine *) userData;
- int retriesLeft = 1;
- aaudio_result_t result;
- do {
- s_CloseAudioStream(engine);
- s_OpenAudioStream(engine, engine->direction);
- // 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
- // a SHARED stream, which will not get stolen.
- result = AAudioStream_requestStart(engine->stream);
- printf("%s() - AAudioStream_requestStart() returns %s\n", __func__,
- AAudio_convertResultToText(result));
- } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+ engine->restartStream();
}
static void s_myErrorCallbackProc(
AAudioStream * /* stream */,
void *userData,
aaudio_result_t error) {
+ LOGI("%s() called", __func__);
printf("%s() - error = %s\n", __func__, AAudio_convertResultToText(error));
// Handle error on a separate thread.
std::thread t(s_myRestartStreamProc, userData);
@@ -185,48 +319,28 @@
}
static void s_usage() {
- printf("test_steal_exclusive [-i]\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");
}
-/**
- * @return 0 is OK, -1 for error
- */
-static int s_checkEnginePositions(AudioEngine *engine) {
- if (engine->stream == nullptr) return 0; // race condition with onError procs!
-
- const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
- const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
- const int32_t delta = (int32_t)(framesWritten - framesRead);
- printf("playing framesRead = %7d, framesWritten = %7d"
- ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
- (int32_t) framesRead,
- (int32_t) framesWritten,
- delta,
- engine->framesCalled.load(),
- engine->callbackCount.load()
- );
- if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
- printf("ERROR - delta > capacity\n");
- return -1;
- }
- return 0;
-}
-
-int main(int argc, char **argv) {
- (void) argc;
- (void) argv;
- struct AudioEngine victim;
- struct AudioEngine thief;
+int main(int argc, char ** argv) {
+ AudioEngine victim("victim");
+ AudioEngine thief("thief");
aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
aaudio_result_t result = AAUDIO_OK;
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++) {
@@ -234,9 +348,18 @@
if (arg[0] == '-') {
char option = arg[1];
switch (option) {
+ case 'd':
+ openDelayMillis = atoi(&arg[2]);
+ break;
case 'i':
direction = AAUDIO_DIRECTION_INPUT;
break;
+ case 'r':
+ maxRetries = atoi(&arg[2]);
+ break;
+ case 's':
+ requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ break;
default:
s_usage();
exit(EXIT_FAILURE);
@@ -249,16 +372,35 @@
}
}
- result = s_OpenAudioStream(&victim, direction);
+ victim.setOpenDelayMillis(openDelayMillis);
+ thief.setOpenDelayMillis(openDelayMillis);
+ victim.setMaxRetries(maxRetries);
+ thief.setMaxRetries(maxRetries);
+
+ result = victim.openAudioStream(direction, requestedSharingMode);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream victim returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
- victim.reset();
+
+ if (victim.sharingMode == requestedSharingMode) {
+ printf("Victim modes is %s => OK\n", s_sharingModeToText(requestedSharingMode));
+ } else {
+ printf("Victim modes should be %s => test not valid!\n",
+ s_sharingModeToText(requestedSharingMode));
+ goto onerror;
+ }
+
+ if (victim.isMMap) {
+ printf("Victim data path is MMAP => OK\n");
+ } else {
+ printf("Victim data path is Legacy! => test not valid\n");
+ goto onerror;
+ }
// Start stream.
- result = AAudioStream_requestStart(victim.stream);
+ result = victim.start();
printf("AAudioStream_requestStart(VICTIM) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
if (result != AAUDIO_OK) {
errorCount++;
@@ -267,77 +409,69 @@
if (result == AAUDIO_OK) {
const int watchLoops = SOLO_DURATION_MSEC / SLEEP_DURATION_MSEC;
for (int i = watchLoops; i > 0; i--) {
- errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+ errorCount += victim.checkEnginePositions() ? 1 : 0;
usleep(SLEEP_DURATION_MSEC * 1000);
}
}
- printf("Try to start the THIEF stream that may steal the VICTIM MMAP resource -----\n");
- result = s_OpenAudioStream(&thief, direction);
+ printf("Trying to start the THIEF stream, which may steal the VICTIM MMAP resource -----\n");
+ result = thief.openAudioStream(direction, requestedSharingMode);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream victim returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
- thief.reset();
// Start stream.
- result = AAudioStream_requestStart(thief.stream);
+ result = thief.start();
printf("AAudioStream_requestStart(THIEF) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
if (result != AAUDIO_OK) {
errorCount++;
}
- printf("You might enjoy plugging in a headset now to see what happens...\n");
+
+ // Give stream time to advance.
+ usleep(SLEEP_DURATION_MSEC * 1000);
+
+ if (victim.verify()) {
+ errorCount++;
+ goto onerror;
+ }
+ if (thief.verify()) {
+ errorCount++;
+ goto onerror;
+ }
+
+ LOGI("Both streams running. Ask user to plug in headset. ====");
+ printf("\n====\nPlease PLUG IN A HEADSET now!\n====\n\n");
if (result == AAUDIO_OK) {
const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC;
for (int i = watchLoops; i > 0; i--) {
- printf("victim: ");
- errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
- printf(" thief: ");
- errorCount += s_checkEnginePositions(&thief) ? 1 : 0;
+ errorCount += victim.checkEnginePositions() ? 1 : 0;
+ errorCount += thief.checkEnginePositions() ? 1 : 0;
usleep(SLEEP_DURATION_MSEC * 1000);
}
}
- // Check for PASS/FAIL
- aaudio_sharing_mode_t victimSharingMode = AAudioStream_getSharingMode(victim.stream);
- aaudio_sharing_mode_t thiefSharingMode = AAudioStream_getSharingMode(thief.stream);
- printf("victimSharingMode = %s, thiefSharingMode = %s, - ",
- s_sharingModeToText(victimSharingMode),
- s_sharingModeToText(thiefSharingMode));
- if ((victimSharingMode == AAUDIO_SHARING_MODE_SHARED)
- && (thiefSharingMode == AAUDIO_SHARING_MODE_SHARED)) {
- printf("Both modes are SHARED => PASS\n");
- } else {
- errorCount++;
- printf("Both modes should be SHARED => FAIL!!\n");
- }
+ errorCount += victim.verify() ? 1 : 0;
+ errorCount += thief.verify() ? 1 : 0;
- const int64_t victimFramesRead = AAudioStream_getFramesRead(victim.stream);
- const int64_t thiefFramesRead = AAudioStream_getFramesRead(thief.stream);
- printf("victimFramesRead = %d, thiefFramesRead = %d, - ",
- (int)victimFramesRead, (int)thiefFramesRead);
- if (victimFramesRead > 0 && thiefFramesRead > 0) {
- printf("Both streams are running => PASS\n");
- } else {
- errorCount++;
- printf("Both streams should be running => FAIL!!\n");
- }
-
- result = AAudioStream_requestStop(victim.stream);
+ result = victim.stop();
printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
if (result != AAUDIO_OK) {
+ printf("stop result = %d = %s\n", result, AAudio_convertResultToText(result));
errorCount++;
}
- result = AAudioStream_requestStop(thief.stream);
+ result = thief.stop();
printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
if (result != AAUDIO_OK) {
+ printf("stop result = %d = %s\n", result, AAudio_convertResultToText(result));
errorCount++;
}
- s_CloseAudioStream(&victim);
- s_CloseAudioStream(&thief);
+onerror:
+ victim.closeAudioStream();
+ thief.closeAudioStream();
printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
printf("test %s\n", errorCount ? "FAILED" : "PASSED");
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 604d182..005d358 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -787,8 +787,9 @@
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
.set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t)mProxy->getBufferSizeInFrames())
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount_l())
.record();
- logBufferSizeUnderruns();
});
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
@@ -1141,16 +1142,6 @@
return NO_ERROR;
}
-void AudioTrack::logBufferSizeUnderruns() {
- LOG_ALWAYS_FATAL_IF(mMetricsId.size() == 0, "mMetricsId is empty!");
- ALOGD("%s(), mMetricsId = %s", __func__, mMetricsId.c_str());
- // FIXME THis hangs! Why?
-// android::mediametrics::LogItem(mMetricsId)
-// .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSizeInFrames())
-// .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount())
-// .record();
-}
-
ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
{
AutoMutex lock(mLock);
@@ -1165,7 +1156,11 @@
ssize_t originalBufferSize = mProxy->getBufferSizeInFrames();
ssize_t finalBufferSize = mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames);
if (originalBufferSize != finalBufferSize) {
- logBufferSizeUnderruns();
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t)mProxy->getBufferSizeInFrames())
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)getUnderrunCount_l())
+ .record();
}
return finalBufferSize;
}
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 17af7d4..0dbd842 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1274,8 +1274,6 @@
std::string mMetricsId; // GUARDED_BY(mLock), could change in createTrack_l().
std::string mCallerName; // for example "aaudio"
- void logBufferSizeUnderruns();
-
private:
class AudioTrackCallback : public media::BnAudioTrackCallback {
public:
diff --git a/media/libaudioprocessing/AudioMixerBase.cpp b/media/libaudioprocessing/AudioMixerBase.cpp
index 64f91fe..a54e22f 100644
--- a/media/libaudioprocessing/AudioMixerBase.cpp
+++ b/media/libaudioprocessing/AudioMixerBase.cpp
@@ -1500,7 +1500,7 @@
ALOGVV("track__Resample\n");
mResampler->setSampleRate(sampleRate);
const bool ramp = needsRamp();
- if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND
+ if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND // custom volume handling
|| ramp || aux != NULL) {
// if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
// if aux != NULL: resample with unity gain to temp buffer then apply send level.
diff --git a/media/libaudioprocessing/AudioMixerOps.h b/media/libaudioprocessing/AudioMixerOps.h
index 2748182..80bd093 100644
--- a/media/libaudioprocessing/AudioMixerOps.h
+++ b/media/libaudioprocessing/AudioMixerOps.h
@@ -236,7 +236,8 @@
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
|| MIXTYPE == MIXTYPE_STEREOEXPAND);
auto proc = [](auto& a, const auto& b) {
- if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
+ if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND) {
a += b;
} else {
a = b;
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 5fab5be..c7afe2f 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -1240,9 +1240,9 @@
memcpy(outBuffer->s16,
session->outBuf,
fr * session->outChannelCount * sizeof(int16_t));
- memcpy(session->outBuf,
- session->outBuf + fr * session->outChannelCount,
- (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t));
+ memmove(session->outBuf,
+ session->outBuf + fr * session->outChannelCount,
+ (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t));
session->framesOut -= fr;
framesWr += fr;
}
@@ -1303,9 +1303,9 @@
session->procFrame->data_,
&frOut);
}
- memcpy(session->inBuf,
- session->inBuf + frIn * session->inChannelCount,
- (session->framesIn - frIn) * session->inChannelCount * sizeof(int16_t));
+ memmove(session->inBuf,
+ session->inBuf + frIn * session->inChannelCount,
+ (session->framesIn - frIn) * session->inChannelCount * sizeof(int16_t));
session->framesIn -= frIn;
} else {
size_t fr = session->frameCount - session->framesIn;
@@ -1381,9 +1381,9 @@
memcpy(outBuffer->s16 + framesWr * session->outChannelCount,
session->outBuf,
fr * session->outChannelCount * sizeof(int16_t));
- memcpy(session->outBuf,
- session->outBuf + fr * session->outChannelCount,
- (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t));
+ memmove(session->outBuf,
+ session->outBuf + fr * session->outChannelCount,
+ (session->framesOut - fr) * session->outChannelCount * sizeof(int16_t));
session->framesOut -= fr;
outBuffer->frameCount += fr;
@@ -1837,9 +1837,9 @@
session->revFrame->data_,
&frOut);
}
- memcpy(session->revBuf,
- session->revBuf + frIn * session->inChannelCount,
- (session->framesRev - frIn) * session->inChannelCount * sizeof(int16_t));
+ memmove(session->revBuf,
+ session->revBuf + frIn * session->inChannelCount,
+ (session->framesRev - frIn) * session->inChannelCount * sizeof(int16_t));
session->framesRev -= frIn;
} else {
size_t fr = session->frameCount - session->framesRev;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 9fbd4d9..818548d 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -30,6 +30,7 @@
"//apex_available:platform",
"com.android.media",
],
+ min_sdk_version: "29",
}
filegroup {
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index b916a78..0906411 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -175,10 +175,12 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN "open"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE "pause" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_RELEASE "release"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE "restore"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE "setMode" // AudioFlinger
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE "setBufferSize" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
-#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME "setVolume" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_START "start" // AudioTrack, AudioRecord
#define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP "stop" // AudioTrack, AudioRecord
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/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl b/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
index e810f1e..1a42809 100644
--- a/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
+++ b/media/libmediatranscoding/aidl/android/media/ITranscodingClientCallback.aidl
@@ -19,6 +19,7 @@
import android.media.TranscodingErrorCode;
import android.media.TranscodingJobParcel;
import android.media.TranscodingResultParcel;
+import android.os.ParcelFileDescriptor;
/**
* ITranscodingClientCallback
@@ -28,6 +29,15 @@
* {@hide}
*/
interface ITranscodingClientCallback {
+ /**
+ * Called to open a raw file descriptor to access data under a URI
+ *
+ * @param fileUri The path of the filename.
+ * @param mode The file mode to use. Must be one of ("r, "w", "rw")
+ * @return ParcelFileDescriptor if open the file successfully, null otherwise.
+ */
+ ParcelFileDescriptor openFileDescriptor(in @utf8InCpp String fileUri,
+ in @utf8InCpp String mode);
/**
* Called when the transcoding associated with the jobId finished.
diff --git a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
index 0ae8035..db171ce 100644
--- a/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingClientManager_tests.cpp
@@ -59,6 +59,11 @@
virtual ~TestClientCallback() { ALOGI("TestClientCallback destroyed"); };
+ Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
+ ::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
+ return Status::ok();
+ }
+
Status onTranscodingFinished(int32_t in_jobId,
const TranscodingResultParcel& in_result) override {
EXPECT_EQ(in_jobId, in_result.jobId);
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index 25321e3..d4fd483 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -162,6 +162,11 @@
ALOGD("TestClient Created");
}
+ Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
+ ::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
+ return Status::ok();
+ }
+
Status onTranscodingFinished(int32_t in_jobId,
const TranscodingResultParcel& in_result) override {
EXPECT_EQ(in_jobId, in_result.jobId);
diff --git a/media/libmediatranscoding/transcoder/Android.bp b/media/libmediatranscoding/transcoder/Android.bp
index 0b7ddbb..843d047 100644
--- a/media/libmediatranscoding/transcoder/Android.bp
+++ b/media/libmediatranscoding/transcoder/Android.bp
@@ -22,6 +22,7 @@
"MediaSampleReaderNDK.cpp",
"MediaSampleWriter.cpp",
"MediaTrackTranscoder.cpp",
+ "MediaTranscoder.cpp",
"PassthroughTrackTranscoder.cpp",
"VideoTrackTranscoder.cpp",
],
diff --git a/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp
index 1673b5b..10c0c6c 100644
--- a/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTrackTranscoder.cpp
@@ -101,4 +101,8 @@
return false;
}
-} // namespace android
\ No newline at end of file
+std::shared_ptr<MediaSampleQueue> MediaTrackTranscoder::getOutputQueue() const {
+ return mOutputQueue;
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
new file mode 100644
index 0000000..f2f7810
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -0,0 +1,350 @@
+/*
+ * 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 "MediaTranscoder"
+
+#include <android-base/logging.h>
+#include <fcntl.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaTranscoder.h>
+#include <media/PassthroughTrackTranscoder.h>
+#include <media/VideoTrackTranscoder.h>
+#include <unistd.h>
+
+namespace android {
+
+#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName) \
+ static void copy##_typeName(const char* key, AMediaFormat* to, AMediaFormat* from) { \
+ _type value; \
+ if (AMediaFormat_get##_typeName(from, key, &value)) { \
+ AMediaFormat_set##_typeName(to, key, value); \
+ } \
+ }
+
+DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
+DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
+DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);
+
+static AMediaFormat* mergeMediaFormats(AMediaFormat* base, AMediaFormat* overlay) {
+ if (base == nullptr || overlay == nullptr) {
+ LOG(ERROR) << "Cannot merge null formats";
+ return nullptr;
+ }
+
+ AMediaFormat* format = AMediaFormat_new();
+ if (AMediaFormat_copy(format, base) != AMEDIA_OK) {
+ AMediaFormat_delete(format);
+ return nullptr;
+ }
+
+ // Note: AMediaFormat does not expose a function for appending values from another format or for
+ // iterating over all values and keys in a format. Instead we define a static list of known keys
+ // along with their value types and copy the ones that are present. A better solution would be
+ // to either implement required functions in NDK or to parse the overlay format's string
+ // representation and copy all existing keys.
+ static const struct {
+ const char* key;
+ void (*copyValue)(const char* key, AMediaFormat* to, AMediaFormat* from);
+ } kSupportedConfigs[] = {
+ {AMEDIAFORMAT_KEY_MIME, copyString},
+ {AMEDIAFORMAT_KEY_DURATION, copyInt64},
+ {AMEDIAFORMAT_KEY_WIDTH, copyInt32},
+ {AMEDIAFORMAT_KEY_HEIGHT, copyInt32},
+ {AMEDIAFORMAT_KEY_BIT_RATE, copyInt32},
+ {AMEDIAFORMAT_KEY_PROFILE, copyInt32},
+ {AMEDIAFORMAT_KEY_LEVEL, copyInt32},
+ {AMEDIAFORMAT_KEY_COLOR_FORMAT, copyInt32},
+ {AMEDIAFORMAT_KEY_COLOR_RANGE, copyInt32},
+ {AMEDIAFORMAT_KEY_COLOR_STANDARD, copyInt32},
+ {AMEDIAFORMAT_KEY_COLOR_TRANSFER, copyInt32},
+ {AMEDIAFORMAT_KEY_FRAME_RATE, copyInt32},
+ {AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, copyInt32},
+ };
+
+ for (int i = 0; i < (sizeof(kSupportedConfigs) / sizeof(kSupportedConfigs[0])); ++i) {
+ kSupportedConfigs[i].copyValue(kSupportedConfigs[i].key, format, overlay);
+ }
+
+ return format;
+}
+
+void MediaTranscoder::sendCallback(media_status_t status) {
+ bool expected = false;
+ if (mCallbackSent.compare_exchange_strong(expected, true)) {
+ if (status == AMEDIA_OK) {
+ mCallbacks->onFinished(this);
+ } else {
+ mCallbacks->onError(this, status);
+ }
+
+ // Transcoding is done and the callback to the client has been sent, so tear down the
+ // pipeline but do it asynchronously to avoid deadlocks. If an error occurred then
+ // automatically delete the output file.
+ const bool deleteOutputFile = status != AMEDIA_OK;
+ std::thread asyncCancelThread{
+ [self = shared_from_this(), deleteOutputFile] { self->cancel(deleteOutputFile); }};
+ asyncCancelThread.detach();
+ }
+}
+
+void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
+ LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
+}
+
+void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
+ LOG(DEBUG) << "TrackTranscoder " << transcoder << " returned error " << status;
+ sendCallback(status);
+}
+
+void MediaTranscoder::onSampleWriterFinished(media_status_t status) {
+ LOG((status != AMEDIA_OK) ? ERROR : DEBUG) << "Sample writer finished with status " << status;
+ sendCallback(status);
+}
+
+std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
+ const std::shared_ptr<CallbackInterface>& callbacks,
+ const std::shared_ptr<Parcel>& pausedState) {
+ if (pausedState != nullptr) {
+ LOG(ERROR) << "Initializing from paused state is currently not supported.";
+ return nullptr;
+ } else if (callbacks == nullptr) {
+ LOG(ERROR) << "Callbacks cannot be null";
+ return nullptr;
+ }
+
+ return std::shared_ptr<MediaTranscoder>(new MediaTranscoder(callbacks));
+}
+
+media_status_t MediaTranscoder::configureSource(const char* path) {
+ if (path == nullptr) {
+ LOG(ERROR) << "Source path cannot be null";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ const int fd = open(path, O_RDONLY);
+ if (fd <= 0) {
+ LOG(ERROR) << "Unable to open source path: " << path;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ const size_t fileSize = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+
+ mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
+ close(fd);
+
+ if (mSampleReader == nullptr) {
+ LOG(ERROR) << "Unable to parse source file: " << path;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ const size_t trackCount = mSampleReader->getTrackCount();
+ for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
+ AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
+ if (trackFormat == nullptr) {
+ LOG(ERROR) << "Track #" << trackIndex << " has no format";
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
+ }
+
+ return AMEDIA_OK;
+}
+
+std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
+ // Return a deep copy of the formats to avoid the caller modifying our internal formats.
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
+ for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
+ AMediaFormat* copy = AMediaFormat_new();
+ AMediaFormat_copy(copy, sourceFormat.get());
+ trackFormats.emplace_back(copy, &AMediaFormat_delete);
+ }
+ return trackFormats;
+}
+
+media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex, AMediaFormat* trackFormat) {
+ if (mSampleReader == nullptr) {
+ LOG(ERROR) << "Source must be configured before tracks";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ } else if (trackIndex >= mSourceTrackFormats.size()) {
+ LOG(ERROR) << "Track index " << trackIndex
+ << " is out of bounds. Track count: " << mSourceTrackFormats.size();
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ std::unique_ptr<MediaTrackTranscoder> transcoder = nullptr;
+ std::shared_ptr<AMediaFormat> format = nullptr;
+
+ if (trackFormat == nullptr) {
+ transcoder = std::make_unique<PassthroughTrackTranscoder>(shared_from_this());
+ } else {
+ const char* srcMime = nullptr;
+ if (!AMediaFormat_getString(mSourceTrackFormats[trackIndex].get(), AMEDIAFORMAT_KEY_MIME,
+ &srcMime)) {
+ LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
+ return AMEDIA_ERROR_MALFORMED;
+ }
+
+ if (strncmp(srcMime, "video/", 6) != 0) {
+ LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
+ "track #"
+ << trackIndex << " with mime " << srcMime;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ const char* dstMime = nullptr;
+ if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
+ if (strncmp(dstMime, "video/", 6) != 0) {
+ LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
+ << srcMime << " to " << dstMime;
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+ }
+
+ transcoder = std::make_unique<VideoTrackTranscoder>(shared_from_this());
+
+ AMediaFormat* mergedFormat =
+ mergeMediaFormats(mSourceTrackFormats[trackIndex].get(), trackFormat);
+ if (mergedFormat == nullptr) {
+ LOG(ERROR) << "Unable to merge source and destination formats";
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ format = std::shared_ptr<AMediaFormat>(mergedFormat, &AMediaFormat_delete);
+ }
+
+ media_status_t status = transcoder->configure(mSampleReader, trackIndex, format);
+ if (status != AMEDIA_OK) {
+ LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
+ << status;
+ return status;
+ }
+
+ mTrackTranscoders.emplace_back(std::move(transcoder));
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::configureDestination(const char* path) {
+ if (path == nullptr || strlen(path) < 1) {
+ LOG(ERROR) << "Invalid destination path: " << path;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ } else if (mSampleWriter != nullptr) {
+ LOG(ERROR) << "Destination is already configured.";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ // Write-only, create file if non-existent, don't overwrite existing file.
+ static constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
+ // User R+W permission.
+ static constexpr int kFileMode = S_IRUSR | S_IWUSR;
+
+ const int fd = open(path, kOpenFlags, kFileMode);
+ if (fd < 0) {
+ LOG(ERROR) << "Unable to open destination file \"" << path << "\" for writing: " << fd;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ mDestinationPath = std::string(path);
+
+ mSampleWriter = std::make_unique<MediaSampleWriter>();
+ const bool initOk = mSampleWriter->init(
+ fd, std::bind(&MediaTranscoder::onSampleWriterFinished, this, std::placeholders::_1));
+ close(fd);
+
+ if (!initOk) {
+ LOG(ERROR) << "Unable to initialize sample writer with destination path " << path;
+ mSampleWriter.reset();
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::start() {
+ if (mTrackTranscoders.size() < 1) {
+ LOG(ERROR) << "Unable to start, no tracks are configured.";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ } else if (mSampleWriter == nullptr) {
+ LOG(ERROR) << "Unable to start, destination is not configured";
+ return AMEDIA_ERROR_INVALID_OPERATION;
+ }
+
+ // Add tracks to the writer.
+ for (auto& transcoder : mTrackTranscoders) {
+ const bool ok = mSampleWriter->addTrack(transcoder->getOutputQueue(),
+ transcoder->getOutputFormat());
+ if (!ok) {
+ LOG(ERROR) << "Unable to add track to sample writer.";
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ }
+
+ bool started = mSampleWriter->start();
+ if (!started) {
+ LOG(ERROR) << "Unable to start sample writer.";
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ // Start transcoders
+ for (auto& transcoder : mTrackTranscoders) {
+ started = transcoder->start();
+ if (!started) {
+ LOG(ERROR) << "Unable to start track transcoder.";
+ cancel(true);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ }
+ return AMEDIA_OK;
+}
+
+media_status_t MediaTranscoder::pause(std::shared_ptr<const Parcelable>* pausedState) {
+ (void)pausedState;
+ LOG(ERROR) << "Pause is not currently supported";
+ return AMEDIA_ERROR_UNSUPPORTED;
+}
+
+media_status_t MediaTranscoder::resume() {
+ LOG(ERROR) << "Resume is not currently supported";
+ return AMEDIA_ERROR_UNSUPPORTED;
+}
+
+media_status_t MediaTranscoder::cancel(bool deleteDestinationFile) {
+ bool expected = false;
+ if (!mCancelled.compare_exchange_strong(expected, true)) {
+ // Already cancelled.
+ return AMEDIA_OK;
+ }
+
+ mSampleWriter->stop();
+ for (auto& transcoder : mTrackTranscoders) {
+ transcoder->stop();
+ }
+
+ // TODO(chz): file deletion should be done by upper level from the content URI.
+ if (deleteDestinationFile && !mDestinationPath.empty()) {
+ int error = unlink(mDestinationPath.c_str());
+ if (error) {
+ LOG(ERROR) << "Unable to delete destination file " << mDestinationPath.c_str() << ": "
+ << error;
+ return AMEDIA_ERROR_IO;
+ }
+ }
+ return AMEDIA_OK;
+}
+
+} // namespace android
diff --git a/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp
index 4404bbb..7806208 100644
--- a/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/PassthroughTrackTranscoder.cpp
@@ -134,7 +134,7 @@
}
sample->info = info;
- if (mOutputQueue.enqueue(sample)) {
+ if (mOutputQueue->enqueue(sample)) {
LOG(ERROR) << "Output queue aborted";
return AMEDIA_ERROR_IO;
}
@@ -153,4 +153,7 @@
mBufferPool->abort();
}
+std::shared_ptr<AMediaFormat> PassthroughTrackTranscoder::getOutputFormat() const {
+ return mSourceFormat;
+}
} // namespace android
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 311e9be..3818545 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -30,6 +30,11 @@
static_assert(SAMPLE_FLAG_PARTIAL_FRAME == AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME,
"Sample flag mismatch: PARTIAL_FRAME");
+// Color format defined by surface. (See MediaCodecInfo.CodecCapabilities#COLOR_FormatSurface.)
+static constexpr int32_t kColorFormatSurface = 0x7f000789;
+// Default key frame interval in seconds.
+static constexpr float kDefaultKeyFrameIntervalSeconds = 1.0f;
+
template <typename T>
void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
{
@@ -113,11 +118,24 @@
media_status_t status = AMEDIA_OK;
if (destinationFormat == nullptr) {
- LOG(ERROR) << "Destination format is null";
+ LOG(ERROR) << "Destination format is null, use passthrough transcoder";
return AMEDIA_ERROR_INVALID_PARAMETER;
}
- mDestinationFormat = destinationFormat;
+ AMediaFormat* encoderFormat = AMediaFormat_new();
+ if (!encoderFormat || AMediaFormat_copy(encoderFormat, destinationFormat.get()) != AMEDIA_OK) {
+ LOG(ERROR) << "Unable to copy destination format";
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ float tmp;
+ if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &tmp)) {
+ AMediaFormat_setFloat(encoderFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
+ kDefaultKeyFrameIntervalSeconds);
+ }
+ AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
+
+ mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
// Create and configure the encoder.
const char* destinationMime = nullptr;
@@ -276,7 +294,7 @@
sample->info.flags = bufferInfo.flags;
sample->info.presentationTimeUs = bufferInfo.presentationTimeUs;
- const bool aborted = mOutputQueue.enqueue(sample);
+ const bool aborted = mOutputQueue->enqueue(sample);
if (aborted) {
LOG(ERROR) << "Output sample queue was aborted. Stopping transcode.";
mStatus = AMEDIA_ERROR_IO; // TODO: Define custom error codes?
@@ -321,7 +339,8 @@
}
AMediaCodec_stop(mDecoder);
- AMediaCodec_stop(mEncoder.get());
+ // TODO: Stop invalidates all buffers. Stop encoder when last buffer is released.
+ // AMediaCodec_stop(mEncoder.get());
return mStatus;
}
@@ -330,4 +349,8 @@
mCodecMessageQueue.push([this] { mStopRequested = true; }, true /* front */);
}
+std::shared_ptr<AMediaFormat> VideoTrackTranscoder::getOutputFormat() const {
+ return mDestinationFormat;
+}
+
} // namespace android
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h
index 235766c..a71db67 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTrackTranscoder.h
@@ -40,14 +40,14 @@
* successfully.
* @param transcoder The MediaTrackTranscoder that finished the transcoding.
*/
- virtual void onTrackFinished(MediaTrackTranscoder* transcoder);
+ virtual void onTrackFinished(const MediaTrackTranscoder* transcoder);
/**
* Called when the MediaTrackTranscoder instance encountered an error it could not recover from.
* @param transcoder The MediaTrackTranscoder that encountered the error.
* @param status The non-zero error code describing the encountered error.
*/
- virtual void onTrackError(MediaTrackTranscoder* transcoder, media_status_t status);
+ virtual void onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status);
protected:
virtual ~MediaTrackTranscoderCallback() = default;
@@ -96,15 +96,24 @@
bool stop();
/**
- * Sample output queue.
- * TODO(b/155918341) Move to protected.
+ * Retrieves the track transcoder's output sample queue.
+ * @return The output sample queue.
*/
- MediaSampleQueue mOutputQueue = {};
+ std::shared_ptr<MediaSampleQueue> getOutputQueue() const;
+
+ /**
+ * Retrieves the track transcoder's final output format. The output is available after the
+ * track transcoder has been successfully configured.
+ * @return The track output format.
+ */
+ virtual std::shared_ptr<AMediaFormat> getOutputFormat() const = 0;
+
+ virtual ~MediaTrackTranscoder() = default;
protected:
MediaTrackTranscoder(const std::weak_ptr<MediaTrackTranscoderCallback>& transcoderCallback)
- : mTranscoderCallback(transcoderCallback){};
- virtual ~MediaTrackTranscoder() = default;
+ : mOutputQueue(std::make_shared<MediaSampleQueue>()),
+ mTranscoderCallback(transcoderCallback){};
// configureDestinationFormat needs to be implemented by subclasses, and gets called on an
// external thread before start.
@@ -119,6 +128,7 @@
// be aborted as soon as possible. It should be safe to call abortTranscodeLoop multiple times.
virtual void abortTranscodeLoop() = 0;
+ std::shared_ptr<MediaSampleQueue> mOutputQueue;
std::shared_ptr<MediaSampleReader> mMediaSampleReader;
int mTrackIndex;
std::shared_ptr<AMediaFormat> mSourceFormat;
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
new file mode 100644
index 0000000..2d18eea
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/include/media/MediaTranscoder.h
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODER_H
+#define ANDROID_MEDIA_TRANSCODER_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <media/MediaSampleReader.h>
+#include <media/MediaSampleWriter.h>
+#include <media/MediaTrackTranscoder.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaFormat.h>
+
+#include <atomic>
+#include <memory>
+#include <unordered_set>
+
+namespace android {
+
+class MediaTranscoder : public std::enable_shared_from_this<MediaTranscoder>,
+ public MediaTrackTranscoderCallback {
+public:
+ /** Callbacks from transcoder to client. */
+ class CallbackInterface {
+ public:
+ /** Transcoder finished successfully. */
+ virtual void onFinished(const MediaTranscoder* transcoder) = 0;
+
+ /** Transcoder encountered an unrecoverable error. */
+ virtual void onError(const MediaTranscoder* transcoder, media_status_t error) = 0;
+
+ /** Transcoder progress update reported in percent from 0 to 100. */
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder, int32_t progress) = 0;
+
+ /**
+ * Transcoder lost codec resources and paused operations. The client can resume transcoding
+ * again when resources are available by either:
+ * 1) Calling resume on the same MediaTranscoder instance.
+ * 2) Creating a new MediaTranscoding instance with the paused state and then calling
+ * resume.
+ */
+ virtual void onCodecResourceLost(const MediaTranscoder* transcoder,
+ const std::shared_ptr<const Parcelable>& pausedState) = 0;
+
+ virtual ~CallbackInterface() = default;
+ };
+
+ /**
+ * Creates a new MediaTranscoder instance. If the supplied paused state is valid, the transcoder
+ * will be initialized with the paused state and be ready to be resumed right away. It is not
+ * possible to change any configurations on a paused transcoder.
+ */
+ static std::shared_ptr<MediaTranscoder> create(
+ const std::shared_ptr<CallbackInterface>& callbacks,
+ const std::shared_ptr<Parcel>& pausedState = nullptr);
+
+ /** Configures source from path. */
+ media_status_t configureSource(const char* path);
+
+ /** Gets the media formats of all tracks in the file. */
+ std::vector<std::shared_ptr<AMediaFormat>> getTrackFormats() const;
+
+ /**
+ * Configures transcoding of a track. Tracks that are not configured will not present in the
+ * final transcoded file, i.e. tracks will be dropped by default. Passing nullptr for
+ * trackFormat means the track will be copied unchanged ("passthrough") to the destination.
+ * Track configurations must be done after the source has been configured.
+ * Note: trackFormat is not modified but cannot be const.
+ */
+ media_status_t configureTrackFormat(size_t trackIndex, AMediaFormat* trackFormat);
+
+ /** Configures destination from path. */
+ media_status_t configureDestination(const char* path);
+
+ /** Starts transcoding. No configurations can be made once the transcoder has started. */
+ media_status_t start();
+
+ /**
+ * Pauses transcoding. The transcoder's paused state is returned through pausedState. The
+ * paused state is only needed for resuming transcoding with a new MediaTranscoder instance. The
+ * caller can resume transcoding with the current MediaTranscoder instance at any time by
+ * calling resume(). It is not required to cancel a paused transcoder. The paused state is
+ * independent and the caller can always initialize a new transcoder instance with the same
+ * paused state. If the caller wishes to abandon a paused transcoder's operation they can
+ * release the transcoder instance, clear the paused state and delete the partial destination
+ * file. The caller can optionally call cancel to let the transcoder clean up the partial
+ * destination file.
+ */
+ media_status_t pause(std::shared_ptr<const Parcelable>* pausedState);
+
+ /** Resumes a paused transcoding. */
+ media_status_t resume();
+
+ /** Cancels the transcoding. Once canceled the transcoding can not be restarted. returns error
+ * if file could not be deleted. */
+ media_status_t cancel(bool deleteDestinationFile = true);
+
+ virtual ~MediaTranscoder() = default;
+
+private:
+ MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks)
+ : mCallbacks(callbacks),
+ mSampleReader(nullptr),
+ mSampleWriter(nullptr),
+ mSourceTrackFormats(),
+ mTrackTranscoders() {}
+
+ // MediaTrackTranscoderCallback
+ virtual void onTrackFinished(const MediaTrackTranscoder* transcoder) override;
+ virtual void onTrackError(const MediaTrackTranscoder* transcoder,
+ media_status_t status) override;
+ // ~MediaTrackTranscoderCallback
+ void onSampleWriterFinished(media_status_t status);
+ void sendCallback(media_status_t status);
+
+ std::shared_ptr<CallbackInterface> mCallbacks;
+ std::shared_ptr<MediaSampleReader> mSampleReader;
+ std::unique_ptr<MediaSampleWriter> mSampleWriter;
+ std::vector<std::shared_ptr<AMediaFormat>> mSourceTrackFormats;
+ std::vector<std::unique_ptr<MediaTrackTranscoder>> mTrackTranscoders;
+
+ std::string mDestinationPath;
+ std::atomic_bool mCallbackSent = false;
+ std::atomic_bool mCancelled = false;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODER_H
diff --git a/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h
index 42feb85..b9491ed 100644
--- a/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/PassthroughTrackTranscoder.h
@@ -90,6 +90,7 @@
void abortTranscodeLoop() override;
media_status_t configureDestinationFormat(
const std::shared_ptr<AMediaFormat>& destinationFormat) override;
+ std::shared_ptr<AMediaFormat> getOutputFormat() const override;
// ~MediaTrackTranscoder
std::shared_ptr<BufferPool> mBufferPool;
diff --git a/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
index 7d93d60..c47e4b7 100644
--- a/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
@@ -61,6 +61,7 @@
void abortTranscodeLoop() override;
media_status_t configureDestinationFormat(
const std::shared_ptr<AMediaFormat>& destinationFormat) override;
+ std::shared_ptr<AMediaFormat> getOutputFormat() const override;
// ~MediaTrackTranscoder
// Enqueues an input sample with the decoder.
diff --git a/media/libmediatranscoding/transcoder/tests/Android.bp b/media/libmediatranscoding/transcoder/tests/Android.bp
index 926110885..4160c30 100644
--- a/media/libmediatranscoding/transcoder/tests/Android.bp
+++ b/media/libmediatranscoding/transcoder/tests/Android.bp
@@ -74,3 +74,10 @@
defaults: ["testdefaults"],
srcs: ["MediaSampleWriterTests.cpp"],
}
+
+// MediaTranscoder unit test
+cc_test {
+ name: "MediaTranscoderTests",
+ defaults: ["testdefaults"],
+ srcs: ["MediaTranscoderTests.cpp"],
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
index c5b181d..4d9386a 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
@@ -60,6 +60,7 @@
break;
}
ASSERT_NE(mTranscoder, nullptr);
+ mTranscoderOutputQueue = mTranscoder->getOutputQueue();
initSampleReader();
}
@@ -120,7 +121,7 @@
std::shared_ptr<MediaSample> sample;
bool aborted = false;
do {
- aborted = mTranscoder->mOutputQueue.dequeue(&sample);
+ aborted = mTranscoderOutputQueue->dequeue(&sample);
} while (!aborted && !(sample->info.flags & SAMPLE_FLAG_END_OF_STREAM));
mQueueWasAborted = aborted;
mGotEndOfStream =
@@ -142,6 +143,7 @@
protected:
std::shared_ptr<MediaTrackTranscoder> mTranscoder;
+ std::shared_ptr<MediaSampleQueue> mTranscoderOutputQueue;
std::shared_ptr<TestCallback> mCallback;
std::shared_ptr<MediaSampleReader> mMediaSampleReader;
@@ -242,7 +244,7 @@
EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
AMEDIA_OK);
ASSERT_TRUE(mTranscoder->start());
- mTranscoder->mOutputQueue.abort();
+ mTranscoderOutputQueue->abort();
drainOutputSampleQueue();
EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_ERROR_IO);
EXPECT_TRUE(mTranscoder->stop());
@@ -259,7 +261,7 @@
ASSERT_TRUE(mTranscoder->start());
std::shared_ptr<MediaSample> sample;
- EXPECT_FALSE(mTranscoder->mOutputQueue.dequeue(&sample));
+ EXPECT_FALSE(mTranscoderOutputQueue->dequeue(&sample));
drainOutputSampleQueue();
EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
@@ -269,6 +271,7 @@
EXPECT_TRUE(mGotEndOfStream);
mTranscoder.reset();
+ mTranscoderOutputQueue.reset();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
sample.reset();
}
@@ -280,7 +283,7 @@
ASSERT_TRUE(mTranscoder->start());
std::shared_ptr<MediaSample> sample;
- EXPECT_FALSE(mTranscoder->mOutputQueue.dequeue(&sample));
+ EXPECT_FALSE(mTranscoderOutputQueue->dequeue(&sample));
EXPECT_TRUE(mTranscoder->stop());
std::this_thread::sleep_for(std::chrono::milliseconds(20));
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
new file mode 100644
index 0000000..c4a67bb
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+// Unit Test for MediaTranscoder
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscoderTests"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <media/MediaTranscoder.h>
+
+namespace android {
+
+class TestCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+ virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ EXPECT_FALSE(mFinished);
+ mFinished = true;
+ mCondition.notify_all();
+ }
+
+ virtual void onError(const MediaTranscoder* transcoder __unused,
+ media_status_t error) override {
+ std::unique_lock<std::mutex> lock(mMutex);
+ EXPECT_NE(error, AMEDIA_OK);
+ EXPECT_FALSE(mFinished);
+ mFinished = true;
+ mStatus = error;
+ mCondition.notify_all();
+ }
+
+ virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
+ int32_t progress __unused) override {}
+
+ virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
+ const std::shared_ptr<const Parcelable>& pausedState
+ __unused) override {}
+
+ void waitForTranscodingFinished() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mFinished) {
+ mCondition.wait(lock);
+ }
+ }
+
+ media_status_t mStatus = AMEDIA_OK;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mFinished = false;
+};
+
+static const char* SOURCE_PATH =
+ "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+
+class MediaTranscoderTests : public ::testing::Test {
+public:
+ MediaTranscoderTests() { LOG(DEBUG) << "MediaTranscoderTests created"; }
+ ~MediaTranscoderTests() { LOG(DEBUG) << "MediaTranscoderTests destroyed"; }
+
+ void SetUp() override {
+ LOG(DEBUG) << "MediaTranscoderTests set up";
+ mCallbacks = std::make_shared<TestCallbacks>();
+ }
+
+ void TearDown() override {
+ LOG(DEBUG) << "MediaTranscoderTests tear down";
+ mCallbacks.reset();
+ }
+
+ void deleteFile(const char* path) { unlink(path); }
+
+ using FormatConfigurationCallback = std::function<AMediaFormat*(AMediaFormat*)>;
+ media_status_t transcodeHelper(const char* destPath,
+ FormatConfigurationCallback formatCallback) {
+ auto transcoder = MediaTranscoder::create(mCallbacks, nullptr);
+ EXPECT_NE(transcoder, nullptr);
+
+ EXPECT_EQ(transcoder->configureSource(SOURCE_PATH), AMEDIA_OK);
+
+ std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+ EXPECT_GT(trackFormats.size(), 0);
+
+ for (int i = 0; i < trackFormats.size(); ++i) {
+ AMediaFormat* format = formatCallback(trackFormats[i].get());
+ EXPECT_EQ(transcoder->configureTrackFormat(i, format), AMEDIA_OK);
+ if (format != nullptr) {
+ AMediaFormat_delete(format);
+ }
+ }
+ deleteFile(destPath);
+ EXPECT_EQ(transcoder->configureDestination(destPath), AMEDIA_OK);
+
+ media_status_t startStatus = transcoder->start();
+ EXPECT_EQ(startStatus, AMEDIA_OK);
+ if (startStatus == AMEDIA_OK) {
+ mCallbacks->waitForTranscodingFinished();
+ }
+
+ return mCallbacks->mStatus;
+ }
+
+ std::shared_ptr<TestCallbacks> mCallbacks;
+};
+
+TEST_F(MediaTranscoderTests, TestPassthrough) {
+ const char* destPath = "/data/local/tmp/MediaTranscoder_Passthrough.MP4";
+
+ EXPECT_EQ(transcodeHelper(destPath, [](AMediaFormat*) { return nullptr; }), AMEDIA_OK);
+
+ // TODO: Validate output file
+}
+
+TEST_F(MediaTranscoderTests, TestBasicVideoTranscode) {
+ const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode.MP4";
+
+ EXPECT_EQ(transcodeHelper(
+ destPath,
+ [](AMediaFormat* sourceFormat) {
+ AMediaFormat* format = nullptr;
+ const char* mime = nullptr;
+ AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
+ if (strncmp(mime, "video/", 6) == 0) {
+ const int32_t kBitRate = 8 * 1000 * 1000; // 8Mbs
+ format = AMediaFormat_new();
+ AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
+ }
+ return format;
+ }),
+ AMEDIA_OK);
+
+ // TODO: Validate output file
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
index 7a92a37..316793a 100644
--- a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
@@ -166,7 +166,8 @@
// Pull transcoder's output samples and compare against input checksums.
uint64_t sampleCount = 0;
std::shared_ptr<MediaSample> sample;
- while (!transcoder.mOutputQueue.dequeue(&sample)) {
+ std::shared_ptr<MediaSampleQueue> outputQueue = transcoder.getOutputQueue();
+ while (!outputQueue->dequeue(&sample)) {
ASSERT_NE(sample, nullptr);
if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
diff --git a/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h b/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h
index 6b9131c..79c227b 100644
--- a/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h
+++ b/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h
@@ -56,13 +56,13 @@
~TestCallback() = default;
// MediaTrackTranscoderCallback
- void onTrackFinished(MediaTrackTranscoder* transcoder __unused) {
+ void onTrackFinished(const MediaTrackTranscoder* transcoder __unused) {
std::unique_lock<std::mutex> lock(mMutex);
mTranscodingFinished = true;
mCv.notify_all();
}
- void onTrackError(MediaTrackTranscoder* transcoder __unused, media_status_t status) {
+ void onTrackError(const MediaTrackTranscoder* transcoder __unused, media_status_t status) {
std::unique_lock<std::mutex> lock(mMutex);
mTranscodingFinished = true;
mStatus = status;
diff --git a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
index 3cec1a1..6b1f640 100644
--- a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
@@ -100,10 +100,11 @@
EXPECT_EQ(transcoder.configure(mMediaSampleReader, mTrackIndex, mDestinationFormat), AMEDIA_OK);
ASSERT_TRUE(transcoder.start());
- std::thread sampleConsumerThread{[&transcoder] {
+ std::shared_ptr<MediaSampleQueue> outputQueue = transcoder.getOutputQueue();
+ std::thread sampleConsumerThread{[&outputQueue] {
uint64_t sampleCount = 0;
std::shared_ptr<MediaSample> sample;
- while (!transcoder.mOutputQueue.dequeue(&sample)) {
+ while (!outputQueue->dequeue(&sample)) {
ASSERT_NE(sample, nullptr);
const uint32_t flags = sample->info.flags;
diff --git a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
index 70e2111..01beeee 100755
--- a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
+++ b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
@@ -39,3 +39,6 @@
echo "testing MediaSampleWriter"
adb shell /data/nativetest64/MediaSampleWriterTests/MediaSampleWriterTests
+
+echo "testing MediaTranscoder"
+adb shell /data/nativetest64/MediaTranscoderTests/MediaTranscoderTests
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 890a939..fac6350 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -285,6 +285,7 @@
"libutils",
"libmedia_helper",
"libsfplugin_ccodec",
+ "libsfplugin_ccodec_utils",
"libstagefright_codecbase",
"libstagefright_foundation",
"libstagefright_omx_utils",
diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp
index 857380c..aac656a 100644
--- a/media/libstagefright/HevcUtils.cpp
+++ b/media/libstagefright/HevcUtils.cpp
@@ -30,6 +30,8 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
+#define UNUSED_PARAM __attribute__((unused))
+
namespace android {
static const uint8_t kHevcNalUnitTypes[8] = {
@@ -379,7 +381,7 @@
}
status_t HevcParameterSets::parsePps(
- const uint8_t* data __unused, size_t size __unused) {
+ const uint8_t* data UNUSED_PARAM, size_t size UNUSED_PARAM) {
return OK;
}
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 e944e21..6b264d5 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2497,6 +2497,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) {
@@ -3186,7 +3198,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;
@@ -3473,8 +3493,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)");
@@ -3599,6 +3619,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];
@@ -3729,7 +3752,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) {
@@ -3794,6 +3836,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/Utils.cpp b/media/libstagefright/Utils.cpp
index a1e4d43..ef69827 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1135,7 +1135,7 @@
// assertion, let's be lenient for now...
// CHECK((ptr[4] >> 2) == 0x3f); // reserved
- size_t lengthSize __unused = 1 + (ptr[4] & 3);
+ // we can get lengthSize value from 1 + (ptr[4] & 3)
// commented out check below as H264_QVGA_500_NO_AUDIO.3gp
// violates it...
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 d06e973..5f5d88a 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -522,6 +522,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/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 4bb21fa..e751a3e 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -172,29 +172,26 @@
return 0;
}
- unsigned bsmod __unused = bits.getBits(3);
+ bits.skipBits(3); // bsmod
unsigned acmod = bits.getBits(3);
- unsigned cmixlev __unused = 0;
- unsigned surmixlev __unused = 0;
- unsigned dsurmod __unused = 0;
if ((acmod & 1) > 0 && acmod != 1) {
if (bits.numBitsLeft() < 2) {
return 0;
}
- cmixlev = bits.getBits(2);
+ bits.skipBits(2); //cmixlev
}
if ((acmod & 4) > 0) {
if (bits.numBitsLeft() < 2) {
return 0;
}
- surmixlev = bits.getBits(2);
+ bits.skipBits(2); //surmixlev
}
if (acmod == 2) {
if (bits.numBitsLeft() < 2) {
return 0;
}
- dsurmod = bits.getBits(2);
+ bits.skipBits(2); //dsurmod
}
if (bits.numBitsLeft() < 1) {
@@ -269,7 +266,7 @@
samplingRate = samplingRateTable2[fscod2];
} else {
samplingRate = samplingRateTable[fscod];
- unsigned numblkscod __unused = bits.getBits(2);
+ bits.skipBits(2); // numblkscod
}
unsigned acmod = bits.getBits(3);
@@ -1087,7 +1084,7 @@
}
unsigned numAUs = bits.getBits(8);
bits.skipBits(8);
- unsigned quantization_word_length __unused = bits.getBits(2);
+ bits.skipBits(2); // quantization_word_length
unsigned audio_sampling_frequency = bits.getBits(3);
unsigned num_channels = bits.getBits(3);
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index 7d217eb..f7bf3ba 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -67,7 +67,7 @@
int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
Message tMsg;
tMsg.type = Message::Type::EVENT;
- tMsg.fence = native_handle_create(0, 0);
+ tMsg.fence.setTo(native_handle_create(0, 0), /* shouldOwn = */ true);
tMsg.data.eventData.event = uint32_t(OMX_EventDataSpaceChanged);
tMsg.data.eventData.data1 = dataSpace;
tMsg.data.eventData.data2 = aspects;
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index bb66f4c..c33bf3f 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -329,6 +329,7 @@
mPass.clear();
mAuthType = NONE;
mNonce.clear();
+ mRealm.clear();
mState = DISCONNECTED;
}
@@ -911,6 +912,14 @@
CHECK_GE(j, 0);
mNonce.setTo(value, i + 7, j - i - 7);
+
+ i = value.find("realm=");
+ CHECK_GE(i, 0);
+ CHECK_EQ(value.c_str()[i + 6], '\"');
+ j = value.find("\"", i + 7);
+ CHECK_GE(j, 0);
+
+ mRealm.setTo(value, i + 7, j - i - 7);
}
return true;
@@ -993,7 +1002,7 @@
AString A1;
A1.append(mUser);
A1.append(":");
- A1.append("Streaming Server");
+ A1.append(mRealm);
A1.append(":");
A1.append(mPass);
@@ -1029,6 +1038,9 @@
fragment.append("\", ");
fragment.append("response=\"");
fragment.append(digest);
+ fragment.append("\", ");
+ fragment.append("realm=\"");
+ fragment.append(mRealm);
fragment.append("\"");
fragment.append("\r\n");
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 56b604d..7cdd4c0 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -84,6 +84,7 @@
AString mUser, mPass;
AuthType mAuthType;
AString mNonce;
+ AString mRealm;
int mSocket;
int32_t mConnectionID;
int32_t mNextCSeq;
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 8fb6a87..80d5d50 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -114,12 +114,12 @@
int32_t actionCode,
const char *detail);
-struct AMediaCodecOnAsyncNotifyCallback {
+typedef struct AMediaCodecOnAsyncNotifyCallback {
AMediaCodecOnAsyncInputAvailable onAsyncInputAvailable;
AMediaCodecOnAsyncOutputAvailable onAsyncOutputAvailable;
AMediaCodecOnAsyncFormatChanged onAsyncFormatChanged;
AMediaCodecOnAsyncError onAsyncError;
-};
+} AMediaCodecOnAsyncNotifyCallback;
#if __ANDROID_API__ >= 21
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index dd84bf2..d6d6e25 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -154,7 +154,7 @@
mReadBufferState = -1;
dumpState->mFrameCount = frameCount;
}
-
+ dumpState->mSilenced = current->mSilenceCapture;
}
void FastCapture::onWork()
@@ -208,6 +208,9 @@
mReadBufferState = frameCount;
}
if (mReadBufferState > 0) {
+ if (current->mSilenceCapture) {
+ memset(mReadBuffer, 0, mReadBufferState * Format_frameSize(mFormat));
+ }
ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState);
audio_track_cblk_t* cblk = current->mCblk;
if (fastPatchRecordBufferProvider != 0) {
diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp
index 53eeba5..b8b3866 100644
--- a/services/audioflinger/FastCaptureDumpState.cpp
+++ b/services/audioflinger/FastCaptureDumpState.cpp
@@ -44,10 +44,11 @@
double periodSec = (double) mFrameCount / mSampleRate;
dprintf(fd, " FastCapture command=%s readSequence=%u framesRead=%u\n"
" readErrors=%u sampleRate=%u frameCount=%zu\n"
- " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n",
+ " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n"
+ " silenced: %s\n",
FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead,
mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
- periodSec * 1e3);
+ periodSec * 1e3, mSilenced ? "true" : "false");
}
} // android
diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h
index 6f9c4c3..a1b8706 100644
--- a/services/audioflinger/FastCaptureDumpState.h
+++ b/services/audioflinger/FastCaptureDumpState.h
@@ -35,6 +35,7 @@
uint32_t mReadErrors; // total number of read() errors
uint32_t mSampleRate;
size_t mFrameCount;
+ bool mSilenced = false; // capture is silenced
};
} // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
index d287232..f949275 100644
--- a/services/audioflinger/FastCaptureState.h
+++ b/services/audioflinger/FastCaptureState.h
@@ -41,6 +41,8 @@
audio_format_t mFastPatchRecordFormat = AUDIO_FORMAT_INVALID;
AudioBufferProvider* mFastPatchRecordBufferProvider = nullptr; // a reference to a patch
// record in fast mode
+ bool mSilenceCapture = false; // request to silence capture for fast track.
+ // note: this also silences the normal mixer pipe
// Extends FastThreadState::Command
static const Command
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 08df775..7e63851 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7088,6 +7088,8 @@
// reference to a fast track which is about to be removed
sp<RecordTrack> fastTrackToRemove;
+ bool silenceFastCapture = false;
+
{ // scope for mLock
Mutex::Autolock _l(mLock);
@@ -7175,14 +7177,33 @@
__func__, activeTrackState, activeTrack->id(), size);
}
- activeTracks.add(activeTrack);
- i++;
-
if (activeTrack->isFastTrack()) {
ALOG_ASSERT(!mFastTrackAvail);
ALOG_ASSERT(fastTrack == 0);
+ // if the active fast track is silenced either:
+ // 1) silence the whole capture from fast capture buffer if this is
+ // the only active track
+ // 2) invalidate this track: this will cause the client to reconnect and possibly
+ // be invalidated again until unsilenced
+ if (activeTrack->isSilenced()) {
+ if (size > 1) {
+ activeTrack->invalidate();
+ ALOG_ASSERT(fastTrackToRemove == 0);
+ fastTrackToRemove = activeTrack;
+ removeTrack_l(activeTrack);
+ mActiveTracks.remove(activeTrack);
+ size--;
+ continue;
+ } else {
+ silenceFastCapture = true;
+ }
+ }
fastTrack = activeTrack;
}
+
+ activeTracks.add(activeTrack);
+ i++;
+
}
mActiveTracks.updatePowerState(this);
@@ -7256,6 +7277,10 @@
AUDIO_FORMAT_INVALID : fastTrack->format();
didModify = true;
}
+ if (state->mSilenceCapture != silenceFastCapture) {
+ state->mSilenceCapture = silenceFastCapture;
+ didModify = true;
+ }
sq->end(didModify);
if (didModify) {
sq->push(block);
@@ -7340,8 +7365,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/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2a9a4c4..f05be06 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3820,7 +3820,11 @@
ALOGE("%s output not found for id %d", __func__, patch->sources[0].id);
return BAD_VALUE;
}
- // Reset handle so that setOutputDevice will force new AF patch to reach the sink
+ if (patchDesc->getHandle() != outputDesc->getPatchHandle()) {
+ // force SwOutput patch removal as AF counter part patch has already gone.
+ ALOGV("%s reset patch handle on Output as different from SWBridge", __func__);
+ removeAudioPatch(outputDesc->getPatchHandle());
+ }
outputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
setOutputDevices(outputDesc,
getNewOutputDevices(outputDesc, true /*fromCache*/),
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b00a2d9..01e70d8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -756,9 +756,10 @@
for (size_t i = 0; i < mInFlightMap.size(); i++) {
InFlightRequest r = mInFlightMap.valueAt(i);
lines.appendFormat(" Frame %d | Timestamp: %" PRId64 ", metadata"
- " arrived: %s, buffers left: %d\n", mInFlightMap.keyAt(i),
+ " arrived: %s, buffers left: %d, buffers returned with STATUS_ERROR: %d, "
+ " buffers notified with error: %d\n", mInFlightMap.keyAt(i),
r.shutterTimestamp, r.haveResultMetadata ? "true" : "false",
- r.numBuffersLeft);
+ r.numBuffersLeft, r.numErrorBuffersReturned, r.numErrorBuffersNotified);
}
}
write(fd, lines.string(), lines.size());
@@ -1043,8 +1044,9 @@
}
CaptureOutputStates states {
mId,
- mInFlightLock, mInFlightMap,
- mOutputLock, mResultQueue, mResultSignal,
+ mInFlightLock, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mInFlightMap, mOutputLock, mResultQueue, mResultSignal,
mNextShutterFrameNumber,
mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
mNextResultFrameNumber,
@@ -1100,8 +1102,9 @@
CaptureOutputStates states {
mId,
- mInFlightLock, mInFlightMap,
- mOutputLock, mResultQueue, mResultSignal,
+ mInFlightLock, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mInFlightMap, mOutputLock, mResultQueue, mResultSignal,
mNextShutterFrameNumber,
mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
mNextResultFrameNumber,
@@ -1139,8 +1142,9 @@
CaptureOutputStates states {
mId,
- mInFlightLock, mInFlightMap,
- mOutputLock, mResultQueue, mResultSignal,
+ mInFlightLock, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mInFlightMap, mOutputLock, mResultQueue, mResultSignal,
mNextShutterFrameNumber,
mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
mNextResultFrameNumber,
@@ -1777,13 +1781,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 +2356,7 @@
return false;
}
-bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
+bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams, int clientStatusId) {
ATRACE_CALL();
bool ret = false;
@@ -2373,7 +2370,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 +2407,10 @@
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
}
+ if (markClientActive) {
+ mStatusTracker->markComponentActive(clientStatusId);
+ }
+
return ret;
}
@@ -4277,22 +4287,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();
@@ -5906,11 +5905,13 @@
// though technically no other thread should be talking to Camera3Device at this point
Camera3OfflineStates offlineStates(
mTagMonitor, mVendorTagId, mUseHalBufManager, mNeedFixupMonochromeTags,
- mUsePartialResult, mNumPartialResults, mNextResultFrameNumber,
- mNextReprocessResultFrameNumber, mNextZslStillResultFrameNumber,
- mNextShutterFrameNumber, mNextReprocessShutterFrameNumber,
- mNextZslStillShutterFrameNumber, mDeviceInfo, mPhysicalDeviceInfoMap,
- mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers);
+ mUsePartialResult, mNumPartialResults, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mNextResultFrameNumber, mNextReprocessResultFrameNumber,
+ mNextZslStillResultFrameNumber, mNextShutterFrameNumber,
+ mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
+ mDeviceInfo, mPhysicalDeviceInfoMap, mDistortionMappers,
+ mZoomRatioMappers, mRotateAndCropMappers);
*session = new Camera3OfflineSession(mId, inputStream, offlineStreamSet,
std::move(bufferRecords), offlineReqs, offlineStates, offlineSession);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index c059f55..408f1f9 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,
@@ -1017,6 +1010,9 @@
std::mutex mInFlightLock;
camera3::InFlightRequestMap mInFlightMap;
nsecs_t mExpectedInflightDuration = 0;
+ int64_t mLastCompletedRegularFrameNumber = -1;
+ int64_t mLastCompletedReprocessFrameNumber = -1;
+ int64_t mLastCompletedZslFrameNumber = -1;
// End of mInFlightLock protection scope
int mInFlightStatusId; // const after initialize
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
index 5942868..95f9633 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.cpp
@@ -61,6 +61,9 @@
mNeedFixupMonochromeTags(offlineStates.mNeedFixupMonochromeTags),
mUsePartialResult(offlineStates.mUsePartialResult),
mNumPartialResults(offlineStates.mNumPartialResults),
+ mLastCompletedRegularFrameNumber(offlineStates.mLastCompletedRegularFrameNumber),
+ mLastCompletedReprocessFrameNumber(offlineStates.mLastCompletedReprocessFrameNumber),
+ mLastCompletedZslFrameNumber(offlineStates.mLastCompletedZslFrameNumber),
mNextResultFrameNumber(offlineStates.mNextResultFrameNumber),
mNextReprocessResultFrameNumber(offlineStates.mNextReprocessResultFrameNumber),
mNextZslStillResultFrameNumber(offlineStates.mNextZslStillResultFrameNumber),
@@ -247,8 +250,9 @@
CaptureOutputStates states {
mId,
- mOfflineReqsLock, mOfflineReqs,
- mOutputLock, mResultQueue, mResultSignal,
+ mOfflineReqsLock, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mOfflineReqs, mOutputLock, mResultQueue, mResultSignal,
mNextShutterFrameNumber,
mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
mNextResultFrameNumber,
@@ -285,8 +289,9 @@
CaptureOutputStates states {
mId,
- mOfflineReqsLock, mOfflineReqs,
- mOutputLock, mResultQueue, mResultSignal,
+ mOfflineReqsLock, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mOfflineReqs, mOutputLock, mResultQueue, mResultSignal,
mNextShutterFrameNumber,
mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
mNextResultFrameNumber,
@@ -318,8 +323,9 @@
CaptureOutputStates states {
mId,
- mOfflineReqsLock, mOfflineReqs,
- mOutputLock, mResultQueue, mResultSignal,
+ mOfflineReqsLock, mLastCompletedRegularFrameNumber,
+ mLastCompletedReprocessFrameNumber, mLastCompletedZslFrameNumber,
+ mOfflineReqs, mOutputLock, mResultQueue, mResultSignal,
mNextShutterFrameNumber,
mNextReprocessShutterFrameNumber, mNextZslStillShutterFrameNumber,
mNextResultFrameNumber,
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.h b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
index 208f70d..c4c7a85 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.h
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
@@ -57,10 +57,11 @@
const TagMonitor& tagMonitor, const metadata_vendor_id_t vendorTagId,
const bool useHalBufManager, const bool needFixupMonochromeTags,
const bool usePartialResult, const uint32_t numPartialResults,
- const uint32_t nextResultFN, const uint32_t nextReprocResultFN,
- const uint32_t nextZslResultFN, const uint32_t nextShutterFN,
- const uint32_t nextReprocShutterFN, const uint32_t nextZslShutterFN,
- const CameraMetadata& deviceInfo,
+ const int64_t lastCompletedRegularFN, const int64_t lastCompletedReprocessFN,
+ const int64_t lastCompletedZslFN, const uint32_t nextResultFN,
+ const uint32_t nextReprocResultFN, const uint32_t nextZslResultFN,
+ const uint32_t nextShutterFN, const uint32_t nextReprocShutterFN,
+ const uint32_t nextZslShutterFN, const CameraMetadata& deviceInfo,
const std::unordered_map<std::string, CameraMetadata>& physicalDeviceInfoMap,
const std::unordered_map<std::string, camera3::DistortionMapper>& distortionMappers,
const std::unordered_map<std::string, camera3::ZoomRatioMapper>& zoomRatioMappers,
@@ -69,6 +70,9 @@
mTagMonitor(tagMonitor), mVendorTagId(vendorTagId),
mUseHalBufManager(useHalBufManager), mNeedFixupMonochromeTags(needFixupMonochromeTags),
mUsePartialResult(usePartialResult), mNumPartialResults(numPartialResults),
+ mLastCompletedRegularFrameNumber(lastCompletedRegularFN),
+ mLastCompletedReprocessFrameNumber(lastCompletedReprocessFN),
+ mLastCompletedZslFrameNumber(lastCompletedZslFN),
mNextResultFrameNumber(nextResultFN),
mNextReprocessResultFrameNumber(nextReprocResultFN),
mNextZslStillResultFrameNumber(nextZslResultFN),
@@ -90,6 +94,15 @@
const bool mUsePartialResult;
const uint32_t mNumPartialResults;
+ // The last completed (buffers, result metadata, and error notify) regular
+ // request frame number
+ const int64_t mLastCompletedRegularFrameNumber;
+ // The last completed (buffers, result metadata, and error notify) reprocess
+ // request frame number
+ const int64_t mLastCompletedReprocessFrameNumber;
+ // The last completed (buffers, result metadata, and error notify) zsl
+ // request frame number
+ const int64_t mLastCompletedZslFrameNumber;
// the minimal frame number of the next non-reprocess result
const uint32_t mNextResultFrameNumber;
// the minimal frame number of the next reprocess result
@@ -214,6 +227,12 @@
std::mutex mOutputLock;
std::list<CaptureResult> mResultQueue;
std::condition_variable mResultSignal;
+ // the last completed frame number of regular requests
+ int64_t mLastCompletedRegularFrameNumber;
+ // the last completed frame number of reprocess requests
+ int64_t mLastCompletedReprocessFrameNumber;
+ // the last completed frame number of ZSL still capture requests
+ int64_t mLastCompletedZslFrameNumber;
// the minimal frame number of the next non-reprocess result
uint32_t mNextResultFrameNumber;
// the minimal frame number of the next reprocess result
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index 603f516..4994393 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -395,7 +395,7 @@
void removeInFlightRequestIfReadyLocked(CaptureOutputStates& states, int idx) {
InFlightRequestMap& inflightMap = states.inflightMap;
- const InFlightRequest &request = inflightMap.valueAt(idx);
+ InFlightRequest &request = inflightMap.editValueAt(idx);
const uint32_t frameNumber = inflightMap.keyAt(idx);
nsecs_t sensorTimestamp = request.sensorTimestamp;
@@ -405,8 +405,8 @@
// In the case of a successful request:
// all input and output buffers, all result metadata, shutter callback
// arrived.
- // In the case of a unsuccessful request:
- // all input and output buffers arrived.
+ // In the case of an unsuccessful request:
+ // all input and output buffers, as well as error notifications, arrived.
if (request.numBuffersLeft == 0 &&
(request.skipResultMetadata ||
(request.haveResultMetadata && shutterTimestamp != 0))) {
@@ -430,14 +430,26 @@
assert(request.requestStatus != OK ||
request.pendingOutputBuffers.size() == 0);
- returnOutputBuffers(
+ size_t bufferErrorCnt = returnOutputBuffers(
states.useHalBufManager, states.listener,
request.pendingOutputBuffers.array(),
request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
request.outputSurfaces, request.resultExtras);
- removeInFlightMapEntryLocked(states, idx);
- ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
+ request.numErrorBuffersReturned += bufferErrorCnt;
+ if (request.numErrorBuffersReturned == request.numErrorBuffersNotified) {
+ removeInFlightMapEntryLocked(states, idx);
+ ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
+
+ // Note down the just completed frame number
+ if (request.hasInputBuffer) {
+ states.lastCompletedReprocessFrameNumber = frameNumber;
+ } else if (request.zslCapture) {
+ states.lastCompletedZslFrameNumber = frameNumber;
+ } else {
+ states.lastCompletedRegularFrameNumber = frameNumber;
+ }
+ }
}
states.inflightIntf.checkInflightMapLengthLocked();
@@ -487,10 +499,10 @@
InFlightRequest &request = states.inflightMap.editValueAt(idx);
ALOGVV("%s: got InFlightRequest requestId = %" PRId32
", frameNumber = %" PRId64 ", burstId = %" PRId32
- ", partialResultCount = %d, hasCallback = %d",
+ ", partialResultCount = %d, hasCallback = %d, num_output_buffers %d",
__FUNCTION__, request.resultExtras.requestId,
request.resultExtras.frameNumber, request.resultExtras.burstId,
- result->partial_result, request.hasCallback);
+ result->partial_result, request.hasCallback, result->num_output_buffers);
// Always update the partial count to the latest one if it's not 0
// (buffers only). When framework aggregates adjacent partial results
// into one, the latest partial count will be used.
@@ -589,10 +601,11 @@
result->num_output_buffers);
} else {
bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
- returnOutputBuffers(states.useHalBufManager, states.listener,
+ auto numErrorBuffers = returnOutputBuffers(states.useHalBufManager, states.listener,
result->output_buffers, result->num_output_buffers,
shutterTimestamp, timestampIncreasing,
request.outputSurfaces, request.resultExtras);
+ request.numErrorBuffersReturned += numErrorBuffers;
}
if (result->result != NULL && !isPartialResult) {
@@ -630,6 +643,7 @@
" its stream:%s (%d)", __FUNCTION__,
frameNumber, strerror(-res), res);
}
+
} else {
ALOGW("%s: Input buffer should be NULL if there is no input"
" buffer sent in the request, skipping input buffer return.",
@@ -785,7 +799,7 @@
processCaptureResult(states, &r);
}
-void returnOutputBuffers(
+size_t returnOutputBuffers(
bool useHalBufManager,
sp<NotificationListener> listener,
const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
@@ -793,6 +807,7 @@
const SurfaceMap& outputSurfaces,
const CaptureResultExtras &inResultExtras) {
+ size_t numErrorBuffers = 0;
for (size_t i = 0; i < numBuffers; i++)
{
if (outputBuffers[i].buffer == nullptr) {
@@ -801,6 +816,10 @@
// has not got a output buffer handle filled yet. This is though illegal if HAL
// buffer management API is not being used.
ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
+ } else {
+ if (outputBuffers[i].status == CAMERA3_BUFFER_STATUS_ERROR) {
+ numErrorBuffers++;
+ }
}
continue;
}
@@ -844,8 +863,13 @@
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
extras);
}
+ } else {
+ if (outputBuffers[i].status == CAMERA3_BUFFER_STATUS_ERROR) {
+ numErrorBuffers++;
+ }
}
}
+ return numErrorBuffers;
}
void notifyShutter(CaptureOutputStates& states, const camera3_shutter_msg_t &msg) {
@@ -899,6 +923,12 @@
msg.frame_number, r.resultExtras.requestId, msg.timestamp);
// Call listener, if any
if (states.listener != nullptr) {
+ r.resultExtras.lastCompletedRegularFrameNumber =
+ states.lastCompletedRegularFrameNumber;
+ r.resultExtras.lastCompletedReprocessFrameNumber =
+ states.lastCompletedReprocessFrameNumber;
+ r.resultExtras.lastCompletedZslFrameNumber =
+ states.lastCompletedZslFrameNumber;
states.listener->notifyShutter(r.resultExtras, msg.timestamp);
}
// send pending result and buffers
@@ -909,11 +939,12 @@
r.rotateAndCropAuto, r.cameraIdsWithZoom, r.physicalMetadatas);
}
bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
- returnOutputBuffers(
+ size_t bufferErrorCnt = returnOutputBuffers(
states.useHalBufManager, states.listener,
r.pendingOutputBuffers.array(),
r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
r.outputSurfaces, r.resultExtras);
+ r.numErrorBuffersReturned += bufferErrorCnt;
r.pendingOutputBuffers.clear();
removeInFlightRequestIfReadyLocked(states, idx);
@@ -976,7 +1007,7 @@
InFlightRequest &r = states.inflightMap.editValueAt(idx);
r.requestStatus = msg.error_code;
resultExtras = r.resultExtras;
- bool logicalDeviceResultError = false;
+ bool physicalDeviceResultError = false;
if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT ==
errorCode) {
if (physicalCameraId.size() > 0) {
@@ -990,23 +1021,39 @@
}
r.physicalCameraIds.erase(iter);
resultExtras.errorPhysicalCameraId = physicalCameraId;
- } else {
- logicalDeviceResultError = true;
+ physicalDeviceResultError = true;
}
}
- if (logicalDeviceResultError
- || hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
- errorCode) {
- r.skipResultMetadata = true;
- }
- if (logicalDeviceResultError) {
- // In case of missing result check whether the buffers
- // returned. If they returned, then remove inflight
- // request.
- // TODO: should we call this for ERROR_CAMERA_REQUEST as well?
- // otherwise we are depending on HAL to send the buffers back after
- // calling notifyError. Not sure if that's in the spec.
+ if (!physicalDeviceResultError) {
+ if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT
+ == errorCode) {
+ r.skipResultMetadata = true;
+ } else if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER
+ == errorCode) {
+ r.numErrorBuffersNotified ++;
+ } else {
+ // errorCode is ERROR_CAMERA_REQUEST
+ if (!r.skipResultMetadata) {
+ // In case HAL calls multiples ERROR_REQUEST
+ // callback, only count the pending buffer
+ // notify error counter once. And also handle
+ // the case where ERROR_BUFFERs are sent before
+ // ERROR_REQUEST, even though it's not allowed
+ // by the HAL API.
+ if (r.numErrorBuffersNotified != 0) {
+ ALOGW("Camera %s: %s: HAL should not notify ERROR_REQUEST"
+ " and ERROR_BUFFER for the same request",
+ states.cameraId.string(), __FUNCTION__);
+ }
+ r.numErrorBuffersNotified =
+ r.numOutputBuffers - r.numErrorBuffersNotified;
+ r.skipResultMetadata = true;
+ }
+ }
+
+ // Check whether the buffers returned. If they returned,
+ // remove inflight request.
removeInFlightRequestIfReadyLocked(states, idx);
}
} else {
@@ -1332,14 +1379,23 @@
{ // First return buffers cached in mInFlightMap
std::lock_guard<std::mutex> l(states.inflightLock);
for (size_t idx = 0; idx < states.inflightMap.size(); idx++) {
- const InFlightRequest &request = states.inflightMap.valueAt(idx);
- returnOutputBuffers(
+ InFlightRequest &request = states.inflightMap.editValueAt(idx);
+ size_t bufferErrorCnt = returnOutputBuffers(
states.useHalBufManager, states.listener,
request.pendingOutputBuffers.array(),
request.pendingOutputBuffers.size(), 0,
/*timestampIncreasing*/true, request.outputSurfaces,
request.resultExtras);
+ request.numErrorBuffersReturned += bufferErrorCnt;
+ ALOGW("%s: Frame %d | Timestamp: %" PRId64 ", metadata"
+ " arrived: %s, buffers left: %d, buffers returned with STATUS_ERROR: %d, "
+ " buffers notified with error: %d\n", __FUNCTION__,
+ states.inflightMap.keyAt(idx), request.shutterTimestamp,
+ request.haveResultMetadata ? "true" : "false",
+ request.numBuffersLeft, request.numErrorBuffersReturned,
+ request.numErrorBuffersNotified);
}
+
states.inflightMap.clear();
states.inflightIntf.onInflightMapFlushedLocked();
}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
index fbb47f8..53f78f2 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -45,7 +45,8 @@
* Helper methods shared between Camera3Device/Camera3OfflineSession for HAL callbacks
*/
// helper function to return the output buffers to output streams.
- void returnOutputBuffers(
+ // returns the number of STATUS_ERROR buffers
+ size_t returnOutputBuffers(
bool useHalBufManager,
sp<NotificationListener> listener, // Only needed when outputSurfaces is not empty
const camera3_stream_buffer_t *outputBuffers,
@@ -60,6 +61,9 @@
struct CaptureOutputStates {
const String8& cameraId;
std::mutex& inflightLock;
+ int64_t& lastCompletedRegularFrameNumber;
+ int64_t& lastCompletedZslFrameNumber;
+ int64_t& lastCompletedReprocessFrameNumber;
InFlightRequestMap& inflightMap; // end of inflightLock scope
std::mutex& outputLock;
std::list<CaptureResult>& resultQueue;
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
index 424043b..3cb8324 100644
--- a/services/camera/libcameraservice/device3/InFlightRequest.h
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -43,6 +43,19 @@
// Decremented by calls to process_capture_result with valid output
// and input buffers
int numBuffersLeft;
+ // Total number of output buffers for this request
+ int numOutputBuffers;
+
+ // The inflight request is considered complete if all buffers are returned
+ // and numErrorBuffersReturned == numErrorBuffersNotified.
+
+ // The number of buffers returned with STATUS_ERROR;
+ int numErrorBuffersReturned;
+ // The number of buffers that are notified as error:
+ // +1 for each notifyError(ERROR_BUFFER), and
+ // +numOutputBuffers for notifyError(ERROR_REQUEST)
+ int numErrorBuffersNotified;
+
CaptureResultExtras resultExtras;
// If this request has any input buffer
bool hasInputBuffer;
@@ -110,6 +123,9 @@
requestStatus(OK),
haveResultMetadata(false),
numBuffersLeft(0),
+ numOutputBuffers(0),
+ numErrorBuffersReturned(0),
+ numErrorBuffersNotified(0),
hasInputBuffer(false),
hasCallback(true),
maxExpectedDuration(kDefaultExpectedDuration),
@@ -129,6 +145,9 @@
requestStatus(OK),
haveResultMetadata(false),
numBuffersLeft(numBuffers),
+ numOutputBuffers(hasInput ? numBuffers-1 : numBuffers),
+ numErrorBuffersReturned(0),
+ numErrorBuffersNotified(0),
resultExtras(extras),
hasInputBuffer(hasInput),
hasCallback(hasAppCallback),
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/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
index 56e34f5..91c14a5 100644
--- a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
@@ -20,7 +20,8 @@
android:versionName="1.0" >
<application android:label="TestAppA">
- <activity android:name="com.android.tests.transcoding.MainActivity">
+ <activity android:name="com.android.tests.transcoding.MainActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT"/>
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
index e1e7857..4baa35a 100644
--- a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppB.xml
@@ -20,7 +20,8 @@
android:versionName="1.0" >
<application android:label="TestAppB">
- <activity android:name="com.android.tests.transcoding.MainActivity">
+ <activity android:name="com.android.tests.transcoding.MainActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT"/>
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
index 55693a4..3dde3af 100644
--- a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppC.xml
@@ -20,7 +20,8 @@
android:versionName="1.0" >
<application android:label="TestAppC">
- <activity android:name="com.android.tests.transcoding.MainActivity">
+ <activity android:name="com.android.tests.transcoding.MainActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT"/>
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
index 7f7d509..babcfb5 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_tests.cpp
@@ -211,6 +211,11 @@
virtual ~TestClientCallback() { ALOGI("TestClientCallback %d destroyed", mClientId); }
+ Status openFileDescriptor(const std::string& /*in_fileUri*/, const std::string& /*in_mode*/,
+ ::ndk::ScopedFileDescriptor* /*_aidl_return*/) override {
+ return Status::ok();
+ }
+
Status onTranscodingFinished(
int32_t in_jobId,
const ::aidl::android::media::TranscodingResultParcel& /* in_result */) override {
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index 6e14434..9d9ca63 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -106,18 +106,9 @@
aaudio_result_t
AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
- aaudio_result_t result = AAUDIO_OK;
ALOGV("registerClientStream(%d,)\n", pid);
std::lock_guard<std::mutex> lock(mLock);
- sp<NotificationClient> notificationClient = mNotificationClients[pid];
- if (notificationClient == 0) {
- // This will get called the first time the audio server registers an internal stream.
- ALOGV("registerClientStream(%d,) unrecognized pid\n", pid);
- notificationClient = new NotificationClient(pid, nullptr);
- mNotificationClients[pid] = notificationClient;
- }
- notificationClient->registerClientStream(serviceStream);
- return result;
+ return getNotificationClient_l(pid)->registerClientStream(serviceStream);
}
// Find the tracker for this process and remove it.
@@ -136,6 +127,33 @@
return AAUDIO_OK;
}
+void AAudioClientTracker::setExclusiveEnabled(pid_t pid, bool enabled) {
+ ALOGD("%s(%d, %d)\n", __func__, pid, enabled);
+ std::lock_guard<std::mutex> lock(mLock);
+ getNotificationClient_l(pid)->setExclusiveEnabled(enabled);
+}
+
+bool AAudioClientTracker::isExclusiveEnabled(pid_t pid) {
+ std::lock_guard<std::mutex> lock(mLock);
+ return getNotificationClient_l(pid)->isExclusiveEnabled();
+}
+
+sp<AAudioClientTracker::NotificationClient>
+ AAudioClientTracker::getNotificationClient_l(pid_t pid) {
+ sp<NotificationClient> notificationClient = mNotificationClients[pid];
+ if (notificationClient == nullptr) {
+ // This will get called the first time the audio server uses this PID.
+ ALOGV("%s(%d,) unrecognized PID\n", __func__, pid);
+ notificationClient = new AAudioClientTracker::NotificationClient(pid, nullptr);
+ mNotificationClients[pid] = notificationClient;
+ }
+ return notificationClient;
+}
+
+// =======================================
+// AAudioClientTracker::NotificationClient
+// =======================================
+
AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid, const sp<IBinder>& binder)
: mProcessId(pid), mBinder(binder) {
}
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index 00ff467..943b809 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -58,6 +58,15 @@
aaudio_result_t unregisterClientStream(pid_t pid,
android::sp<AAudioServiceStreamBase> serviceStream);
+ /**
+ * Specify whether a process is allowed to create an EXCLUSIVE MMAP stream.
+ * @param pid
+ * @param enabled
+ */
+ void setExclusiveEnabled(pid_t pid, bool enabled);
+
+ bool isExclusiveEnabled(pid_t pid);
+
android::AAudioService *getAAudioService() const {
return mAAudioService;
}
@@ -84,17 +93,29 @@
aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
+ void setExclusiveEnabled(bool enabled) {
+ mExclusiveEnabled = enabled;
+ }
+
+ bool isExclusiveEnabled() {
+ return mExclusiveEnabled;
+ }
+
// IBinder::DeathRecipient
virtual void binderDied(const android::wp<IBinder>& who);
- protected:
+ private:
mutable std::mutex mLock;
const pid_t mProcessId;
std::set<android::sp<AAudioServiceStreamBase>> mStreams;
// hold onto binder to receive death notifications
android::sp<IBinder> mBinder;
+ bool mExclusiveEnabled = true;
};
+ // This must be called under mLock
+ android::sp<NotificationClient> getNotificationClient_l(pid_t pid);
+
mutable std::mutex mLock;
std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
android::AAudioService *mAAudioService = nullptr;
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index c9bf72f..9f34153 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -25,6 +25,7 @@
#include <sstream>
#include <utility/AAudioUtilities.h>
+#include "AAudioClientTracker.h"
#include "AAudioEndpointManager.h"
#include "AAudioServiceEndpointShared.h"
#include "AAudioServiceEndpointMMAP.h"
@@ -174,7 +175,15 @@
&& !request.isSharingModeMatchRequired()) { // app did not request a shared stream
ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
mExclusiveStolenCount++;
- endpointToSteal = endpoint;
+ // Prevent this process from getting another EXCLUSIVE stream.
+ // This will prevent two clients from colliding after a DISCONNECTION
+ // when they both try to open an exclusive stream at the same time.
+ // That can result in a stream getting disconnected between the OPEN
+ // and START calls. This will help preserve app compatibility.
+ // An app can avoid having this happen by closing their streams when
+ // the app is paused.
+ AAudioClientTracker::getInstance().setExclusiveEnabled(request.getProcessId(), false);
+ endpointToSteal = endpoint; // return it to caller
}
return nullptr;
} else {
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 82b12d6..22cdb35 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -117,7 +117,8 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
- if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
+ && AAudioClientTracker::getInstance().isExclusiveEnabled(request.getProcessId())) {
// only trust audioserver for in service indication
bool inService = false;
if (isCallerInService()) {
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;